diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..189b112f4b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'weekly' + - package-ecosystem: 'maven' + directory: '/' + schedule: + interval: 'weekly' diff --git a/.gitignore b/.gitignore index 655278e3e1..0bac28a65b 100644 --- a/.gitignore +++ b/.gitignore @@ -104,7 +104,9 @@ Utils/*implemented.txt .metadata .project .settings -.idea +.idea/ +.vscode/ +.factorypath syntax: regexp \.class \.jar diff --git a/Mage.Client/.gitignore b/Mage.Client/.gitignore new file mode 100644 index 0000000000..ae3c172604 --- /dev/null +++ b/Mage.Client/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Mage.Client/pom.xml b/Mage.Client/pom.xml index 05f3a8cc69..77c00dd529 100644 --- a/Mage.Client/pom.xml +++ b/Mage.Client/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 mage-client @@ -43,11 +43,14 @@ org.slf4j slf4j-log4j12 + + net.sf.trove4j trove4j 3.0.3 + com.mortennobel java-image-scaling @@ -70,7 +73,7 @@ com.amazonaws aws-java-sdk-s3 - 1.11.498 + 1.11.827 com.jgoodies @@ -107,7 +110,7 @@ org.jsoup jsoup - 1.11.3 + 1.13.1 truevfs-profile-base @@ -125,6 +128,8 @@ + + com.googlecode.soundlibs mp3spi @@ -140,12 +145,15 @@ tritonus_share 0.3.6 + + net.java.balloontip balloontip 1.2.4.1 - + + + + org.ocpsoft.prettytime prettytime - 4.0.2.Final + 4.0.5.Final + + + org.unbescape + unbescape + 1.1.6.RELEASE diff --git a/Mage.Client/release/sample-decks/Commander/Commander 2011/Devour for Power.dck b/Mage.Client/release/sample-decks/Commander/Commander 2011/Devour for Power.dck index 876f112e0f..c453b22df4 100644 --- a/Mage.Client/release/sample-decks/Commander/Commander 2011/Devour for Power.dck +++ b/Mage.Client/release/sample-decks/Commander/Commander 2011/Devour for Power.dck @@ -83,5 +83,5 @@ 1 [CMD:99] Shared Trauma 2 [CMD:318] Forest SB: 1 [CMD:210] The Mimeoplasm -LAYOUT MAIN:(2,10)(CMC,true,50)|()()([CMD:92],[CMD:227])([CMD:152],[CMD:83],[CMD:59],[CMD:176],[CMD:179])([CMD:145],[CMD:193],[CMD:44],[CMD:86],[CMD:165],[CMD:89],[CMD:98],[CMD:262],[CMD:71])([CMD:140],[CMD:52],[CMD:236])([CMD:76],[CMD:81],[CMD:97],[CMD:235],[CMD:239])([CMD:75],[CMD:191],[CMD:93],[CMD:62],[CMD:228],[CMD:263])([CMD:73])([CMD:1])([CMD:266],[CMD:269],[CMD:270],[CMD:271],[CMD:315],[CMD:315],[CMD:316],[CMD:316],[CMD:317],[CMD:317],[CMD:318],[CMD:318],[CMD:275],[CMD:303],[CMD:303],[CMD:304],[CMD:304],[CMD:305],[CMD:305],[CMD:306],[CMD:306],[CMD:279],[CMD:281],[CMD:285],[CMD:288],[CMD:289],[CMD:307],[CMD:307],[CMD:307],[CMD:308],[CMD:308],[CMD:308],[CMD:309],[CMD:309],[CMD:309],[CMD:310],[CMD:310],[CMD:290],[CMD:291],[CMD:292])([CMD:51],[CMD:99],[CMD:261])([CMD:101],[CMD:102],[CMD:175],[CMD:246],[CMD:249],[CMD:253],[CMD:259])([CMD:74],[CMD:148],[CMD:70],[CMD:63],[CMD:254],[CMD:50],[CMD:68],[CMD:107],[CMD:178])([CMD:104],[CMD:105],[CMD:45],[CMD:85])([CMD:88],[CMD:96],[CMD:103],[CMD:168])()()()() -LAYOUT SIDEBOARD:(1,1)(NONE,false,50)|([CMD:210]) +LAYOUT MAIN:(2,10)(CMC,true,50)|()()([CMD:92],[CMD:227])([CMD:152],[CMD:83],[CMD:59],[CMD:176],[CMD:179])([CMD:145],[CMD:193],[CMD:44],[CMD:86],[CMD:165],[CMD:89],[CMD:98],[CMD:262],[CMD:71])([CMD:140],[CMD:52],[CMD:236])([CMD:76],[CMD:81],[CMD:97],[CMD:235],[CMD:239])([CMD:75],[CMD:191],[CMD:93],[CMD:62],[CMD:228],[CMD:263])([CMD:73])([CMD:1])([CMD:266],[CMD:269],[CMD:270],[CMD:271],[CMD:315],[CMD:315],[CMD:316],[CMD:316],[CMD:317],[CMD:317],[CMD:318],[CMD:318],[CMD:275],[CMD:303],[CMD:303],[CMD:304],[CMD:304],[CMD:305],[CMD:305],[CMD:306],[CMD:306],[CMD:279],[CMD:281],[CMD:285],[CMD:288],[CMD:289],[CMD:307],[CMD:307],[CMD:307],[CMD:308],[CMD:308],[CMD:308],[CMD:309],[CMD:309],[CMD:309],[CMD:310],[CMD:310],[CMD:290],[CMD:291],[CMD:292])([CMD:51],[CMD:99],[CMD:261])([CMD:101],[CMD:102],[CMD:175],[CMD:246],[CMD:249],[CMD:253],[CMD:259])([CMD:74],[CMD:148],[CMD:70],[CMD:63],[CMD:254],[CMD:50],[CMD:68],[CMD:107],[CMD:178])([CMD:104],[CMD:105],[CMD:45],[CMD:85])([CMD:88],[CMD:96],[CMD:103],[CMD:168])()()()() +LAYOUT SIDEBOARD:(1,1)(NONE,false,50)|([CMD:210]) diff --git a/Mage.Client/release/sample-decks/Commander/Commander 2011/Heavenly Inferno.dck b/Mage.Client/release/sample-decks/Commander/Commander 2011/Heavenly Inferno.dck index 483818ee5f..75669c7a99 100644 --- a/Mage.Client/release/sample-decks/Commander/Commander 2011/Heavenly Inferno.dck +++ b/Mage.Client/release/sample-decks/Commander/Commander 2011/Heavenly Inferno.dck @@ -86,5 +86,5 @@ 1 [CMD:11] Congregate 1 [CMD:129] Mana-Charged Dragon SB: 1 [CMD:206] Kaalia of the Vast -LAYOUT MAIN:(2,10)(CMC,true,50)|()([CMD:21])([CMD:186],[CMD:217])([CMD:195],[CMD:202])([CMD:109],[CMD:120],[CMD:124],[CMD:18],[CMD:94],[CMD:35])([CMD:184],[CMD:82],[CMD:30],[CMD:31])([CMD:208],[CMD:129],[CMD:130],[CMD:216])([CMD:180],[CMD:5],[CMD:7],[CMD:185],[CMD:229])([CMD:108],[CMD:111],[CMD:95])([CMD:79])([CMD:264],[CMD:266],[CMD:267],[CMD:268],[CMD:269],[CMD:272],[CMD:273],[CMD:282],[CMD:311],[CMD:311],[CMD:312],[CMD:312],[CMD:313],[CMD:313],[CMD:314],[CMD:314],[CMD:283],[CMD:299],[CMD:299],[CMD:300],[CMD:300],[CMD:301],[CMD:301],[CMD:302],[CMD:302],[CMD:284],[CMD:285],[CMD:286],[CMD:307],[CMD:307],[CMD:308],[CMD:308],[CMD:309],[CMD:309],[CMD:310],[CMD:310],[CMD:297],[CMD:298])([CMD:121],[CMD:25],[CMD:261],[CMD:32])([CMD:242],[CMD:9],[CMD:243],[CMD:117],[CMD:253],[CMD:255],[CMD:257],[CMD:231])([CMD:245],[CMD:211],[CMD:24],[CMD:36],[CMD:138],[CMD:107])([CMD:11],[CMD:77],[CMD:80],[CMD:209],[CMD:132],[CMD:28],[CMD:136],[CMD:137],[CMD:104],[CMD:238])([CMD:116],[CMD:29],[CMD:103])([CMD:3],[CMD:118])()()() -LAYOUT SIDEBOARD:(1,1)(NONE,false,50)|([CMD:206]) +LAYOUT MAIN:(2,10)(CMC,true,50)|()([CMD:21])([CMD:186],[CMD:217])([CMD:195],[CMD:202])([CMD:109],[CMD:120],[CMD:124],[CMD:18],[CMD:94],[CMD:35])([CMD:184],[CMD:82],[CMD:30],[CMD:31])([CMD:208],[CMD:129],[CMD:130],[CMD:216])([CMD:180],[CMD:5],[CMD:7],[CMD:185],[CMD:229])([CMD:108],[CMD:111],[CMD:95])([CMD:79])([CMD:264],[CMD:266],[CMD:267],[CMD:268],[CMD:269],[CMD:272],[CMD:273],[CMD:282],[CMD:311],[CMD:311],[CMD:312],[CMD:312],[CMD:313],[CMD:313],[CMD:314],[CMD:314],[CMD:283],[CMD:299],[CMD:299],[CMD:300],[CMD:300],[CMD:301],[CMD:301],[CMD:302],[CMD:302],[CMD:284],[CMD:285],[CMD:286],[CMD:307],[CMD:307],[CMD:308],[CMD:308],[CMD:309],[CMD:309],[CMD:310],[CMD:310],[CMD:297],[CMD:298])([CMD:121],[CMD:25],[CMD:261],[CMD:32])([CMD:242],[CMD:9],[CMD:243],[CMD:117],[CMD:253],[CMD:255],[CMD:257],[CMD:231])([CMD:245],[CMD:211],[CMD:24],[CMD:36],[CMD:138],[CMD:107])([CMD:11],[CMD:77],[CMD:80],[CMD:209],[CMD:132],[CMD:28],[CMD:136],[CMD:137],[CMD:104],[CMD:238])([CMD:116],[CMD:29],[CMD:103])([CMD:3],[CMD:118])()()() +LAYOUT SIDEBOARD:(1,1)(NONE,false,50)|([CMD:206]) diff --git a/Mage.Client/release/sample-decks/Commander/Commander 2011/Political Puppets.dck b/Mage.Client/release/sample-decks/Commander/Commander 2011/Political Puppets.dck index 7ee8f0b492..6ffc7cb9d6 100644 --- a/Mage.Client/release/sample-decks/Commander/Commander 2011/Political Puppets.dck +++ b/Mage.Client/release/sample-decks/Commander/Commander 2011/Political Puppets.dck @@ -82,5 +82,5 @@ 1 [CMD:55] Propaganda 1 [CMD:12] Crescendo of War SB: 1 [CMD:240] Zedruu the Greathearted -LAYOUT MAIN:(2,9)(CMC,true,50)|()([CMD:125],[CMD:33])([CMD:183],[CMD:47],[CMD:16],[CMD:213],[CMD:37])([CMD:43],[CMD:48],[CMD:49],[CMD:218],[CMD:66],[CMD:237])([CMD:187],[CMD:13],[CMD:123],[CMD:221],[CMD:38])([CMD:41],[CMD:194],[CMD:205])([CMD:215],[CMD:133])([CMD:6])()([CMD:265],[CMD:268],[CMD:269],[CMD:272],[CMD:303],[CMD:303],[CMD:303],[CMD:304],[CMD:304],[CMD:304],[CMD:305],[CMD:305],[CMD:305],[CMD:306],[CMD:306],[CMD:306],[CMD:278],[CMD:311],[CMD:311],[CMD:312],[CMD:312],[CMD:313],[CMD:313],[CMD:314],[CMD:314],[CMD:299],[CMD:299],[CMD:300],[CMD:300],[CMD:301],[CMD:301],[CMD:302],[CMD:302],[CMD:291])([CMD:40],[CMD:46],[CMD:261],[CMD:32])([CMD:242],[CMD:112],[CMD:248],[CMD:251],[CMD:17],[CMD:127],[CMD:253],[CMD:54],[CMD:26],[CMD:256],[CMD:131],[CMD:61],[CMD:67],[CMD:69])([CMD:244],[CMD:114],[CMD:245],[CMD:14],[CMD:53],[CMD:22],[CMD:27],[CMD:55],[CMD:58],[CMD:63],[CMD:64],[CMD:36],[CMD:68],[CMD:138])([CMD:12],[CMD:57],[CMD:60],[CMD:139])()([CMD:8],[CMD:118],[CMD:247],[CMD:19])()([CMD:126]) -LAYOUT SIDEBOARD:(1,1)(NONE,false,50)|([CMD:240]) +LAYOUT MAIN:(2,9)(CMC,true,50)|()([CMD:125],[CMD:33])([CMD:183],[CMD:47],[CMD:16],[CMD:213],[CMD:37])([CMD:43],[CMD:48],[CMD:49],[CMD:218],[CMD:66],[CMD:237])([CMD:187],[CMD:13],[CMD:123],[CMD:221],[CMD:38])([CMD:41],[CMD:194],[CMD:205])([CMD:215],[CMD:133])([CMD:6])()([CMD:265],[CMD:268],[CMD:269],[CMD:272],[CMD:303],[CMD:303],[CMD:303],[CMD:304],[CMD:304],[CMD:304],[CMD:305],[CMD:305],[CMD:305],[CMD:306],[CMD:306],[CMD:306],[CMD:278],[CMD:311],[CMD:311],[CMD:312],[CMD:312],[CMD:313],[CMD:313],[CMD:314],[CMD:314],[CMD:299],[CMD:299],[CMD:300],[CMD:300],[CMD:301],[CMD:301],[CMD:302],[CMD:302],[CMD:291])([CMD:40],[CMD:46],[CMD:261],[CMD:32])([CMD:242],[CMD:112],[CMD:248],[CMD:251],[CMD:17],[CMD:127],[CMD:253],[CMD:54],[CMD:26],[CMD:256],[CMD:131],[CMD:61],[CMD:67],[CMD:69])([CMD:244],[CMD:114],[CMD:245],[CMD:14],[CMD:53],[CMD:22],[CMD:27],[CMD:55],[CMD:58],[CMD:63],[CMD:64],[CMD:36],[CMD:68],[CMD:138])([CMD:12],[CMD:57],[CMD:60],[CMD:139])()([CMD:8],[CMD:118],[CMD:247],[CMD:19])()([CMD:126]) +LAYOUT SIDEBOARD:(1,1)(NONE,false,50)|([CMD:240]) diff --git a/Mage.Client/release/sample-decks/Planeswalk Decks/Chandra, Bold Pyromancer.dck b/Mage.Client/release/sample-decks/Planeswalk Decks/Chandra, Bold Pyromancer.dck index 546e2d4634..acbe40635f 100644 --- a/Mage.Client/release/sample-decks/Planeswalk Decks/Chandra, Bold Pyromancer.dck +++ b/Mage.Client/release/sample-decks/Planeswalk Decks/Chandra, Bold Pyromancer.dck @@ -27,5 +27,5 @@ 3 [DOM:277] Karplusan Hound 2 [DOM:156] Baloth Gorger 4 [DOM:278] Pyromantic Pilgrim -LAYOUT MAIN:(2,7)(CMC,true,50)|()([DOM:168],[DOM:168])([DOM:159],[DOM:159],[DOM:159],[DOM:125])([DOM:172],[DOM:278],[DOM:278],[DOM:278],[DOM:278],[DOM:167])([DOM:277],[DOM:277],[DOM:277],[DOM:162],[DOM:156],[DOM:156])([DOM:120],[DOM:120],[DOM:120])([DOM:165],[DOM:177])([DOM:262],[DOM:262],[DOM:262],[DOM:263],[DOM:263],[DOM:263],[DOM:264],[DOM:264],[DOM:264],[DOM:265],[DOM:265],[DOM:266],[DOM:266],[DOM:266],[DOM:267],[DOM:267],[DOM:267],[DOM:268],[DOM:268],[DOM:268],[DOM:269],[DOM:269],[DOM:279],[DOM:279],[DOM:279],[DOM:279])([DOM:142],[DOM:142])([DOM:163],[DOM:163],[DOM:176])([DOM:119])()([DOM:276],[DOM:276],[DOM:215],[DOM:118])([DOM:275]) -LAYOUT SIDEBOARD:(0,0)(NONE,false,50)| +LAYOUT MAIN:(2,7)(CMC,true,50)|()([DOM:168],[DOM:168])([DOM:159],[DOM:159],[DOM:159],[DOM:125])([DOM:172],[DOM:278],[DOM:278],[DOM:278],[DOM:278],[DOM:167])([DOM:277],[DOM:277],[DOM:277],[DOM:162],[DOM:156],[DOM:156])([DOM:120],[DOM:120],[DOM:120])([DOM:165],[DOM:177])([DOM:262],[DOM:262],[DOM:262],[DOM:263],[DOM:263],[DOM:263],[DOM:264],[DOM:264],[DOM:264],[DOM:265],[DOM:265],[DOM:266],[DOM:266],[DOM:266],[DOM:267],[DOM:267],[DOM:267],[DOM:268],[DOM:268],[DOM:268],[DOM:269],[DOM:269],[DOM:279],[DOM:279],[DOM:279],[DOM:279])([DOM:142],[DOM:142])([DOM:163],[DOM:163],[DOM:176])([DOM:119])()([DOM:276],[DOM:276],[DOM:215],[DOM:118])([DOM:275]) +LAYOUT SIDEBOARD:(0,0)(NONE,false,50)| diff --git a/Mage.Client/release/sample-decks/Planeswalk Decks/Huatli, Dinosaur Knight.dck b/Mage.Client/release/sample-decks/Planeswalk Decks/Huatli, Dinosaur Knight.dck index 539358b8f6..8ccdfcc303 100644 --- a/Mage.Client/release/sample-decks/Planeswalk Decks/Huatli, Dinosaur Knight.dck +++ b/Mage.Client/release/sample-decks/Planeswalk Decks/Huatli, Dinosaur Knight.dck @@ -24,5 +24,5 @@ 3 [XLN:261] Plains 3 [XLN:260] Plains 2 [XLN:18] Kinjalli's Caller -LAYOUT MAIN:(1,4)(CARD_TYPE,false,50)|([XLN:13],[XLN:288],[XLN:288],[XLN:135],[XLN:18],[XLN:18],[XLN:286],[XLN:286],[XLN:286],[XLN:286],[XLN:31],[XLN:31],[XLN:169],[XLN:169],[XLN:146],[XLN:146],[XLN:41],[XLN:41],[XLN:133],[XLN:133],[XLN:28],[XLN:28],[XLN:36],[XLN:36])([XLN:287],[XLN:287],[XLN:287],[XLN:38],[XLN:38],[XLN:149],[XLN:149],[XLN:30],[XLN:30])([XLN:289],[XLN:289],[XLN:289],[XLN:289],[XLN:260],[XLN:260],[XLN:260],[XLN:261],[XLN:261],[XLN:261],[XLN:262],[XLN:262],[XLN:262],[XLN:263],[XLN:263],[XLN:272],[XLN:272],[XLN:272],[XLN:273],[XLN:273],[XLN:273],[XLN:274],[XLN:274],[XLN:274],[XLN:275],[XLN:275])([XLN:285]) -LAYOUT SIDEBOARD:(0,0)(NONE,false,50)| +LAYOUT MAIN:(1,4)(CARD_TYPE,false,50)|([XLN:13],[XLN:288],[XLN:288],[XLN:135],[XLN:18],[XLN:18],[XLN:286],[XLN:286],[XLN:286],[XLN:286],[XLN:31],[XLN:31],[XLN:169],[XLN:169],[XLN:146],[XLN:146],[XLN:41],[XLN:41],[XLN:133],[XLN:133],[XLN:28],[XLN:28],[XLN:36],[XLN:36])([XLN:287],[XLN:287],[XLN:287],[XLN:38],[XLN:38],[XLN:149],[XLN:149],[XLN:30],[XLN:30])([XLN:289],[XLN:289],[XLN:289],[XLN:289],[XLN:260],[XLN:260],[XLN:260],[XLN:261],[XLN:261],[XLN:261],[XLN:262],[XLN:262],[XLN:262],[XLN:263],[XLN:263],[XLN:272],[XLN:272],[XLN:272],[XLN:273],[XLN:273],[XLN:273],[XLN:274],[XLN:274],[XLN:274],[XLN:275],[XLN:275])([XLN:285]) +LAYOUT SIDEBOARD:(0,0)(NONE,false,50)| diff --git a/Mage.Client/release/sample-decks/Planeswalk Decks/Nissa, Genesis Mage.dck b/Mage.Client/release/sample-decks/Planeswalk Decks/Nissa, Genesis Mage.dck index c3ccd369b9..ed93833cce 100644 --- a/Mage.Client/release/sample-decks/Planeswalk Decks/Nissa, Genesis Mage.dck +++ b/Mage.Client/release/sample-decks/Planeswalk Decks/Nissa, Genesis Mage.dck @@ -19,5 +19,5 @@ 2 [HOU:143] River Hoopoe 2 [HOU:110] Ambuscade 7 [HOU:198] Forest -LAYOUT MAIN:(1,7)(CARD_TYPE,false,50)|([HOU:115],[HOU:115],[HOU:143],[HOU:143],[HOU:201],[HOU:201],[HOU:201],[AKH:179],[AKH:179],[AKH:179],[HOU:29],[HOU:29],[AKH:209],[AKH:209],[HOU:30],[HOU:30],[HOU:202],[HOU:202],[HOU:202],[HOU:202],[AKH:41],[AKH:41])([AKH:196])([AKH:219],[AKH:219])([HOU:54],[HOU:54],[HOU:54],[HOU:110],[HOU:110])([HOU:204],[HOU:204],[HOU:204],[HOU:204],[HOU:192],[HOU:192],[HOU:192],[HOU:192],[HOU:193],[HOU:193],[HOU:193],[HOU:193],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:199],[HOU:199],[HOU:199],[HOU:199],[HOU:199],[HOU:199],[HOU:199])([HOU:200])([HOU:154],[HOU:203],[HOU:203]) -LAYOUT SIDEBOARD:(0,0)(NONE,false,50)| +LAYOUT MAIN:(1,7)(CARD_TYPE,false,50)|([HOU:115],[HOU:115],[HOU:143],[HOU:143],[HOU:201],[HOU:201],[HOU:201],[AKH:179],[AKH:179],[AKH:179],[HOU:29],[HOU:29],[AKH:209],[AKH:209],[HOU:30],[HOU:30],[HOU:202],[HOU:202],[HOU:202],[HOU:202],[AKH:41],[AKH:41])([AKH:196])([AKH:219],[AKH:219])([HOU:54],[HOU:54],[HOU:54],[HOU:110],[HOU:110])([HOU:204],[HOU:204],[HOU:204],[HOU:204],[HOU:192],[HOU:192],[HOU:192],[HOU:192],[HOU:193],[HOU:193],[HOU:193],[HOU:193],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:199],[HOU:199],[HOU:199],[HOU:199],[HOU:199],[HOU:199],[HOU:199])([HOU:200])([HOU:154],[HOU:203],[HOU:203]) +LAYOUT SIDEBOARD:(0,0)(NONE,false,50)| diff --git a/Mage.Client/sounds/FeedbackNeeded.wav b/Mage.Client/sounds/FeedbackNeeded.wav new file mode 100644 index 0000000000..be64080145 Binary files /dev/null and b/Mage.Client/sounds/FeedbackNeeded.wav differ diff --git a/Mage.Client/src/main/assembly/distribution.xml b/Mage.Client/src/main/assembly/distribution.xml index 5f8c9aef15..2ce3f69aaf 100644 --- a/Mage.Client/src/main/assembly/distribution.xml +++ b/Mage.Client/src/main/assembly/distribution.xml @@ -69,6 +69,7 @@ / + false sounds/ diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 0117e7e325..6787ecb951 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -200,9 +200,17 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { TConfig config = TConfig.current(); config.setArchiveDetector(new TArchiveDetector("zip")); config.setAccessPreference(FsAccessOption.STORE, true); + try { UIManager.put("desktop", new Color(0, 0, 0, 0)); - UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); + UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); + + UIManager.put("nimbusBlueGrey", PreferencesDialog.getCurrentTheme().getNimbusBlueGrey()); // buttons, scrollbar background, disabled inputs + UIManager.put("control", PreferencesDialog.getCurrentTheme().getControl()); // window bg + UIManager.put("nimbusLightBackground", PreferencesDialog.getCurrentTheme().getNimbusLightBackground()); // inputs, table rows + UIManager.put("info", PreferencesDialog.getCurrentTheme().getInfo()); // tooltips + UIManager.put("nimbusBase", PreferencesDialog.getCurrentTheme().getNimbusBase()); // title bars, scrollbar foreground + //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); // stop JSplitPane from eating F6 and F8 or any other function keys { @@ -445,16 +453,19 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { } } + // Sets background for login screen private void setBackground() { if (liteMode || grayMode) { return; } - String filename = "/background.jpg"; + try { - if (Plugins.instance.isThemePluginLoaded()) { + // If user has custom background, use that, otherwise, use theme background + if (Plugins.instance.isThemePluginLoaded() && + !PreferencesDialog.getCachedValue(PreferencesDialog.KEY_BACKGROUND_IMAGE_DEFAULT, "true").equals("true")) { backgroundPane = (ImagePanel) Plugins.instance.updateTablePanel(new HashMap<>()); } else { - InputStream is = this.getClass().getResourceAsStream(filename); + InputStream is = this.getClass().getResourceAsStream(PreferencesDialog.getCurrentTheme().getLoginBackgroundPath()); BufferedImage background = ImageIO.read(is); backgroundPane = new ImagePanel(background, ImagePanelStyle.SCALED); } @@ -870,7 +881,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { popupDebug.add(menuDebugTestCardRenderModesDialog); setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); - setMinimumSize(new java.awt.Dimension(1024, 768)); + setMinimumSize(new java.awt.Dimension(1024, 500)); desktopPane.setBackground(new java.awt.Color(204, 204, 204)); @@ -1020,6 +1031,10 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { .addComponent(desktopPane, javax.swing.GroupLayout.DEFAULT_SIZE, 145, Short.MAX_VALUE)) ); + if (PreferencesDialog.getCurrentTheme().getMageToolbar() != null) { + mageToolbar.getParent().setBackground(PreferencesDialog.getCurrentTheme().getMageToolbar()); + } + pack(); }// //GEN-END:initComponents @@ -1624,6 +1639,13 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { whatsNewDialog.checkUpdatesAndShow(forceToShowPage); } } + + public boolean isGameFrameActive(UUID gameId) { + if (activeFrame != null && activeFrame instanceof GamePane) { + return ((GamePane) activeFrame).getGameId().equals(gameId); + } + return false; + } } class MagePaneMenuItem extends JCheckBoxMenuItem { diff --git a/Mage.Client/src/main/java/mage/client/cards/BigCard.form b/Mage.Client/src/main/java/mage/client/cards/BigCard.form index b80ecd4511..1927f746ae 100644 --- a/Mage.Client/src/main/java/mage/client/cards/BigCard.form +++ b/Mage.Client/src/main/java/mage/client/cards/BigCard.form @@ -6,7 +6,7 @@ - + @@ -30,11 +30,18 @@ + + + - + + + + + @@ -42,7 +49,7 @@ - + @@ -50,9 +57,7 @@ - - diff --git a/Mage.Client/src/main/java/mage/client/cards/BigCard.java b/Mage.Client/src/main/java/mage/client/cards/BigCard.java index 5bfe612dab..267bd68f1c 100644 --- a/Mage.Client/src/main/java/mage/client/cards/BigCard.java +++ b/Mage.Client/src/main/java/mage/client/cards/BigCard.java @@ -8,6 +8,7 @@ package mage.client.cards; +import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; @@ -15,9 +16,10 @@ import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.util.List; import java.util.UUID; +import javax.swing.BorderFactory; import javax.swing.JComponent; -import javax.swing.text.BadLocationException; -import javax.swing.text.StyledDocument; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; import static mage.client.constants.Constants.CONTENT_MAX_XOFFSET; import static mage.client.constants.Constants.FRAME_MAX_HEIGHT; import static mage.client.constants.Constants.FRAME_MAX_WIDTH; @@ -29,6 +31,7 @@ import mage.client.util.ImageHelper; import mage.constants.EnlargeMode; import org.jdesktop.swingx.JXPanel; import mage.client.util.TransformedImageCache; +import org.mage.card.arcane.UI; /** * Class for displaying big image of the card @@ -62,6 +65,9 @@ public class BigCard extends JComponent { setOpaque(true); this.scrollPane.setOpaque(true); this.scrollPane.setVisible(false); + + UI.setHTMLEditorKit(text); + text.setEditable(false); } private void initBounds(boolean rotated) { @@ -69,8 +75,8 @@ public class BigCard extends JComponent { if (rotated) { scrollPane.setBounds(50, 50, 100, 100); } else { - scrollPane.setBounds(this.getWidth()*1000/17777,this.getWidth()*1000/1100, - this.getWidth()*1000/1142,this.getWidth()*1000/2539); + scrollPane.setBounds(this.getWidth()*1000/17777,this.getWidth()*1000/1150, + this.getWidth()*1000/1130,this.getWidth()*1000/2100); } } @@ -95,8 +101,14 @@ public class BigCard extends JComponent { synchronized (this) { source = null; hue = 0.000f; - } - drawText(strings); + } + StringBuilder displayedText = new StringBuilder(); + for (String textLine: strings) { + if (textLine != null && !textLine.replace(".", "").trim().isEmpty()) { + displayedText.append("

").append(textLine).append("

"); + } + } + this.text.setText(displayedText.toString()); repaint(); } } @@ -109,19 +121,6 @@ public class BigCard extends JComponent { this.cardId = null; } - private void drawText(java.util.List strings) { - text.setText(""); - StyledDocument doc = text.getStyledDocument(); - - try { - for (String line : strings) { - doc.insertString(doc.getLength(), line + '\n', doc.getStyle("regular")); - } - } catch (BadLocationException ble) { - } - text.setCaretPosition(0); - } - @Override public void paintComponent(Graphics graphics) { if (bigImage != null) { @@ -169,21 +168,19 @@ public class BigCard extends JComponent { setFocusable(false); setMinimumSize(new Dimension(FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT)); setName("bigCardPanel"); // NOI18N - setOpaque(false); setPreferredSize(getMinimumSize()); setLayout(null); + scrollPane.setBackground(new java.awt.Color(220, 220, 220)); scrollPane.setBorder(null); scrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane.setOpaque(false); + scrollPane.setViewportBorder(javax.swing.BorderFactory.createEtchedBorder()); - text.setEditable(false); text.setFocusable(false); - text.setOpaque(false); scrollPane.setViewportView(text); add(scrollPane); - scrollPane.setBounds(20, 230, 210, 120); + scrollPane.setBounds(20, 220, 210, 130); scrollPane.setBounds(new Rectangle(CONTENT_MAX_XOFFSET, TEXT_MAX_YOFFSET, TEXT_MAX_WIDTH, TEXT_MAX_HEIGHT)); }// //GEN-END:initComponents diff --git a/Mage.Client/src/main/java/mage/client/cards/Card.java b/Mage.Client/src/main/java/mage/client/cards/Card.java index f9bce9e92e..05443a4d1e 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Card.java +++ b/Mage.Client/src/main/java/mage/client/cards/Card.java @@ -321,7 +321,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis @Override public void mouseMoved(MouseEvent arg0) { - this.bigCard.showTextComponent(); + // this.bigCard.showTextComponent(); this.bigCard.setCard(card.getId(), EnlargeMode.NORMAL, image, getRules(), false); } diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java index 80fbe14523..b98fadfe5e 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java @@ -73,8 +73,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } trimGrid(); layoutGrid(); - cardScroll.revalidate(); - cardScroll.repaint(); + repaintGrid(); } } @@ -315,8 +314,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Remove empty rows / cols / spaces in stacks trimGrid(); layoutGrid(); - cardScroll.revalidate(); - cardScroll.repaint(); + repaintGrid(); } else { // Add new cards to grid for (CardView card : cards) { @@ -325,14 +323,14 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg eventSource.fireEvent(card, ClientEventType.ADD_SPECIFIC_CARD); } layoutGrid(); - cardContent.repaint(); + repaintGrid(); } } public void changeGUISize() { layoutGrid(); cardScroll.getVerticalScrollBar().setUnitIncrement(CardRenderer.getCardTopHeight(getCardWidth())); - cardContent.repaint(); + repaintGrid(); } public void cleanUp() { @@ -386,7 +384,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } trimGrid(); layoutGrid(); - cardContent.repaint(); + repaintGrid(); } public DeckCardLayout getCardLayout() { @@ -562,6 +560,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg JButton selectByButton; JButton analyseButton; JButton blingButton; + JButton oldVersionButton; // Popup for toolbar final JPopupMenu filterPopup; @@ -709,60 +708,6 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Editting mode this.mode = Constants.DeckEditorMode.LIMITED_BUILDING; - // Toolbar - sortButton = new JButton("Sort"); - filterButton = new JButton("Filter"); - visibilityButton = new JButton("V"); // "Visibility" button - selectByButton = new JButton("Select By"); - analyseButton = new JButton("M"); // "Mana" button - blingButton = new JButton("B"); // "Bling" button - - // Name and count label - deckNameAndCountLabel = new JLabel(); - - // Count labels - landCountLabel = new JLabel("", new ImageIcon(getClass().getResource("/buttons/type_land.png")), SwingConstants.LEFT); - landCountLabel.setToolTipText("Number of lands in deck"); - creatureCountLabel = new JLabel("", new ImageIcon(getClass().getResource("/buttons/type_creatures.png")), SwingConstants.LEFT); - creatureCountLabel.setToolTipText("Number of creatures in deck"); - - JPanel toolbar = new JPanel(new BorderLayout()); - JPanel toolbarInner = new JPanel(); - toolbar.setBackground(new Color(250, 250, 250, 150)); - toolbar.setOpaque(true); - toolbarInner.setOpaque(false); - toolbarInner.add(deckNameAndCountLabel); - toolbarInner.add(landCountLabel); - toolbarInner.add(creatureCountLabel); - toolbarInner.add(sortButton); - toolbarInner.add(filterButton); - toolbarInner.add(selectByButton); - toolbarInner.add(visibilityButton); - toolbarInner.add(analyseButton); - toolbarInner.add(blingButton); - toolbar.add(toolbarInner, BorderLayout.WEST); - JPanel sliderPanel = new JPanel(new GridBagLayout()); - sliderPanel.setOpaque(false); - cardSizeSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 50); - cardSizeSlider.setOpaque(false); - cardSizeSlider.setPreferredSize(new Dimension(100, (int) cardSizeSlider.getPreferredSize().getHeight())); - cardSizeSlider.addChangeListener(e -> { - if (!cardSizeSlider.getValueIsAdjusting()) { - // Fraction in [-1, 1] - float sliderFrac = ((float) (cardSizeSlider.getValue() - 50)) / 50; - // Convert to frac in [0.5, 2.0] exponentially - cardSizeMod = (float) Math.pow(2, sliderFrac); - // Update grid - layoutGrid(); - cardContent.repaint(); - } - }); - cardSizeSliderLabel = new JLabel("Card Size:"); - sliderPanel.add(cardSizeSliderLabel); - sliderPanel.add(cardSizeSlider); - toolbar.add(sliderPanel, BorderLayout.EAST); - this.add(toolbar, BorderLayout.NORTH); - // Content cardContent = new JLayeredPane(); cardContent.setLayout(null); @@ -804,6 +749,62 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg cardScroll.getVerticalScrollBar().setUnitIncrement(CardRenderer.getCardTopHeight(getCardWidth())); this.add(cardScroll, BorderLayout.CENTER); + // Toolbar + sortButton = new JButton("Sort"); + filterButton = new JButton("Filter"); + visibilityButton = new JButton("V"); // "Visibility" button + selectByButton = new JButton("Select By"); + analyseButton = new JButton("M"); // "Mana" button + blingButton = new JButton("B"); // "Bling" button + oldVersionButton = new JButton("O"); // "Old version" button + + // Name and count label + deckNameAndCountLabel = new JLabel(); + + // Count labels + landCountLabel = new JLabel("", new ImageIcon(getClass().getResource("/buttons/type_land.png")), SwingConstants.LEFT); + landCountLabel.setToolTipText("Number of lands in deck"); + creatureCountLabel = new JLabel("", new ImageIcon(getClass().getResource("/buttons/type_creatures.png")), SwingConstants.LEFT); + creatureCountLabel.setToolTipText("Number of creatures in deck"); + + JPanel toolbar = new JPanel(new BorderLayout()); + JPanel toolbarInner = new JPanel(); + toolbar.setBackground(new Color(250, 250, 250, 150)); + toolbar.setOpaque(true); + toolbarInner.setOpaque(false); + toolbarInner.add(deckNameAndCountLabel); + toolbarInner.add(landCountLabel); + toolbarInner.add(creatureCountLabel); + toolbarInner.add(sortButton); + toolbarInner.add(filterButton); + toolbarInner.add(selectByButton); + toolbarInner.add(visibilityButton); + toolbarInner.add(analyseButton); + toolbarInner.add(blingButton); + toolbarInner.add(oldVersionButton); + toolbar.add(toolbarInner, BorderLayout.WEST); + JPanel sliderPanel = new JPanel(new GridBagLayout()); + sliderPanel.setOpaque(false); + cardSizeSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 50); + cardSizeSlider.setOpaque(false); + cardSizeSlider.setPreferredSize(new Dimension(100, (int) cardSizeSlider.getPreferredSize().getHeight())); + cardSizeSlider.addChangeListener(e -> { + if (!cardSizeSlider.getValueIsAdjusting()) { + // Fraction in [-1, 1] + float sliderFrac = ((float) (cardSizeSlider.getValue() - 50)) / 50; + // Convert to frac in [0.5, 2.0] exponentially + cardSizeMod = (float) Math.pow(2, sliderFrac); + // Update grid + layoutGrid(); + repaintGrid(); + } + }); + cardSizeSliderLabel = new JLabel("Card size:"); + sliderPanel.add(cardSizeSliderLabel); + sliderPanel.add(cardSizeSlider); + toolbar.add(sliderPanel, BorderLayout.EAST); + this.add(toolbar, BorderLayout.NORTH); + // Insert arrow insertArrow = new JLabel(); insertArrow.setSize(20, 20); @@ -982,6 +983,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg blingButton.addActionListener(evt -> blingDeck()); + // Old version button - Switch cards to the oldest non-promo printing. In case of multiples in a set, take the lowest card number. + oldVersionButton.setToolTipText("Switch cards to the oldest non-promo printing"); + + oldVersionButton.addActionListener(evt -> oldVersionDeck()); + // Filter popup filterPopup = new JPopupMenu(); filterPopup.setPreferredSize(new Dimension(300, 300)); @@ -1222,7 +1228,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // And finally rerender layoutGrid(); - repaint(); + repaintGrid(); } public void reselectBy() { @@ -1332,7 +1338,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // And finally rerender layoutGrid(); - repaint(); + repaintGrid(); } private static final Pattern pattern = Pattern.compile(".*Add(.*)(\\{[WUBRGXC]\\})"); @@ -1633,13 +1639,49 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } layoutGrid(); - cardScroll.revalidate(); - repaint(); + repaintGrid(); JOptionPane.showMessageDialog(null, "Added " + pimpedCards.size() + " cards. You can select them and the originals by choosing 'Multiples'"); } } } + private void oldVersionDeck() { + if (this.mode != Constants.DeckEditorMode.FREE_BUILDING) { + return; + } + + if (JOptionPane.showConfirmDialog(null, "Are you sure you want to switch your card versions to the oldest ones?", "WARNING", + JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { + return; + } + + List>> newCardGrid = new ArrayList<>(); + for (List> gridRow : cardGrid) { + List> newGridRow = new ArrayList<>(); + for (List stack : gridRow) { + List newStack = new ArrayList<>(); + for (CardView card : stack) { + CardInfo oldestCardInfo = CardRepository.instance.findOldestNonPromoVersionCard(card.getName()); + if (oldestCardInfo != null) { + CardView oldestCardView = new CardView(oldestCardInfo.getMockCard()); + this.removeCardView(card); + eventSource.fireEvent(card, ClientEventType.REMOVE_SPECIFIC_CARD); + this.addCardView(oldestCardView, false); + eventSource.fireEvent(oldestCardView, ClientEventType.ADD_SPECIFIC_CARD); + newStack.add(oldestCardView); + } else { + newStack.add(card); + } + } + newGridRow.add(newStack); + } + newCardGrid.add(newGridRow); + } + cardGrid = newCardGrid; + layoutGrid(); + repaintGrid(); + } + // Update the contents of the card grid public void setCards(CardsView cardsView, DeckCardLayout layout, BigCard bigCard) { if (bigCard != null) { @@ -1761,10 +1803,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg if (didModify) { // Update layout layoutGrid(); - - // Update draw - cardScroll.revalidate(); - repaint(); + repaintGrid(); } } @@ -1904,9 +1943,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg eventSource.fireEvent(card, ClientEventType.ADD_SPECIFIC_CARD); // Update layout layoutGrid(); - // Update draw - cardScroll.revalidate(); - repaint(); + repaintGrid(); } } @@ -2110,6 +2147,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg return (int) (1.4 * getCardWidth()); } + private void repaintGrid() { + cardScroll.revalidate(); + cardScroll.repaint(); + repaint(); + } + /** * Position all of the card views correctly */ diff --git a/Mage.Client/src/main/java/mage/client/combat/CombatManager.java b/Mage.Client/src/main/java/mage/client/combat/CombatManager.java index 6c80cbd74a..4e121976c9 100644 --- a/Mage.Client/src/main/java/mage/client/combat/CombatManager.java +++ b/Mage.Client/src/main/java/mage/client/combat/CombatManager.java @@ -27,6 +27,9 @@ public enum CombatManager { private final Map combatBlockers = new HashMap<>(); private int globalBlockersCount; // we need global counter as there are several combat groups + private static Color ARROW_COLOR_ATTACKER = Color.red; + private static Color ARROW_COLOR_BLOCKED_ATTACKER = Color.gray; + private Point parentPoint; @@ -79,15 +82,19 @@ public enum CombatManager { private void drawDefender(CombatGroupView group, MagePermanent attackerCard, UUID gameId) { UUID defenderId = group.getDefenderId(); if (defenderId != null) { + // if attacker was blocked then use another arrow color + Color attackColor = group.getBlockers().isEmpty() ? ARROW_COLOR_ATTACKER : ARROW_COLOR_BLOCKED_ATTACKER; parentPoint = getParentPoint(attackerCard); PlayAreaPanel p = MageFrame.getGamePlayers(gameId).get(defenderId); if (p != null) { + // attack to player Point target = p.getLocationOnScreen(); target.translate(-parentPoint.x, -parentPoint.y); Point attackerPoint = attackerCard.getLocationOnScreen(); attackerPoint.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.getBuilder().addArrow(gameId, (int) attackerPoint.getX() + 45, (int) attackerPoint.getY() + 25, (int) target.getX() + 40, (int) target.getY() - 20, Color.red, ArrowBuilder.Type.COMBAT); + ArrowBuilder.getBuilder().addArrow(gameId, (int) attackerPoint.getX() + 45, (int) attackerPoint.getY() + 25, (int) target.getX() + 40, (int) target.getY() - 20, attackColor, ArrowBuilder.Type.COMBAT); } else { + // attack to planeswalker for (PlayAreaPanel pa : MageFrame.getGamePlayers(gameId).values()) { MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(defenderId); if (permanent != null) { @@ -95,7 +102,7 @@ public enum CombatManager { target.translate(-parentPoint.x, -parentPoint.y); Point attackerPoint = attackerCard.getLocationOnScreen(); attackerPoint.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.getBuilder().addArrow(gameId, (int) attackerPoint.getX() + 45, (int) attackerPoint.getY() + 25, (int) target.getX() + 40, (int) target.getY() + 10, Color.red, ArrowBuilder.Type.COMBAT); + ArrowBuilder.getBuilder().addArrow(gameId, (int) attackerPoint.getX() + 45, (int) attackerPoint.getY() + 25, (int) target.getX() + 40, (int) target.getY() + 10, attackColor, ArrowBuilder.Type.COMBAT); } } } diff --git a/Mage.Client/src/main/java/mage/client/components/ColorPane.java b/Mage.Client/src/main/java/mage/client/components/ColorPane.java index 09996a47bd..0592e31e3d 100644 --- a/Mage.Client/src/main/java/mage/client/components/ColorPane.java +++ b/Mage.Client/src/main/java/mage/client/components/ColorPane.java @@ -6,9 +6,11 @@ import mage.client.MageFrame; import mage.client.dialog.PreferencesDialog; import mage.client.util.gui.GuiDisplayUtil; import mage.components.CardInfoPane; +import mage.game.command.Plane; import mage.util.CardUtil; import mage.utils.ThreadUtils; import mage.view.CardView; +import mage.view.PlaneView; import javax.swing.*; import javax.swing.event.HyperlinkEvent.EventType; @@ -75,17 +77,24 @@ public class ColorPane extends JEditorPane { if (!alternativeName.isEmpty()) { cardName = alternativeName; } - - CardInfo card = CardRepository.instance.findCard(cardName); try { final Component container = MageFrame.getUI().getComponent(MageComponents.POPUP_CONTAINER); if (e.getEventType() == EventType.EXITED) { setPopupVisibility(container, false); } - if (e.getEventType() == EventType.ENTERED && card != null) { + if (e.getEventType() == EventType.ENTERED) { CardInfoPane cardInfoPane = (CardInfoPane) MageFrame.getUI().getComponent(MageComponents.CARD_INFO_PANE); - cardInfoPane.setCard(new CardView(card.getMockCard()), container); - setPopupVisibility(container, true); + + // card + CardInfo card = CardRepository.instance.findCard(cardName); + Plane plane = Plane.createPlaneByFullName(cardName); + if (card != null) { + cardInfoPane.setCard(new CardView(card.getMockCard()), container); + setPopupVisibility(container, true); + } else if (plane != null) { + cardInfoPane.setCard(new CardView(new PlaneView(plane)), container); + setPopupVisibility(container, true); + } } } catch (InterruptedException e1) { e1.printStackTrace(); diff --git a/Mage.Client/src/main/java/mage/client/components/KeyboundButton.java b/Mage.Client/src/main/java/mage/client/components/KeyboundButton.java index c58ae3cad2..0054cb51d7 100644 --- a/Mage.Client/src/main/java/mage/client/components/KeyboundButton.java +++ b/Mage.Client/src/main/java/mage/client/components/KeyboundButton.java @@ -12,41 +12,48 @@ import mage.client.dialog.PreferencesDialog; */ public class KeyboundButton extends JButton { - private final String text; - private static final Font keyFont = new Font(Font.SANS_SERIF, Font.BOLD, 13); + private final String text; + private static final Font keyFont = new Font(Font.SANS_SERIF, Font.BOLD, 13); - private boolean tinting = false; + private boolean tinting = false; - public KeyboundButton(String key) { - text = PreferencesDialog.getCachedKeyText(key); - } + public KeyboundButton(String key, boolean drawText) { + if (drawText) { + text = PreferencesDialog.getCachedKeyText(key); + } else { + text = ""; + } + } - @Override - protected void paintComponent(Graphics g) { - if (ui != null && g != null) { - Graphics sg = g.create(); - try { - ui.update(sg, this); - if (tinting) { - sg.setColor(new Color(0, 0, 0, 32)); - sg.fillRoundRect(2, 2, getWidth() - 4 , getHeight() - 4, 6, 6); - } - sg.setColor(tinting ? Color.lightGray : Color.white); - sg.setFont(keyFont); + @Override + protected void paintComponent(Graphics g) { + if (ui != null && g != null) { + Graphics sg = g.create(); + try { + ui.update(sg, this); - int textWidth = sg.getFontMetrics(keyFont).stringWidth(text); - int centerX = (getWidth() - textWidth) / 2; + if (tinting) { + sg.setColor(new Color(0, 0, 0, 32)); + sg.fillRoundRect(2, 2, getWidth() - 4 , getHeight() - 4, 6, 6); + } + sg.setColor(tinting ? Color.lightGray : Color.white); - sg.drawString(text, centerX, 28); - } finally { - sg.dispose(); - } - } - } + if (!text.isEmpty()) { + sg.setFont(keyFont); - public void setTint(boolean tinting) { - this.tinting = tinting; - repaint(); - } + int textWidth = sg.getFontMetrics(keyFont).stringWidth(text); + int centerX = (getWidth() - textWidth) / 2; + sg.drawString(text, centerX, 28); + } + } finally { + sg.dispose(); + } + } + } + + public void setTint(boolean tinting) { + this.tinting = tinting; + repaint(); + } } diff --git a/Mage.Client/src/main/java/mage/client/components/LegalityLabel.java b/Mage.Client/src/main/java/mage/client/components/LegalityLabel.java new file mode 100644 index 0000000000..9d0a70b4bb --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/LegalityLabel.java @@ -0,0 +1,205 @@ +package mage.client.components; + +import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidator; +import mage.cards.decks.DeckValidatorError; +import mage.cards.decks.importer.DeckImporter; +import org.unbescape.html.HtmlEscape; +import org.unbescape.html.HtmlEscapeLevel; +import org.unbescape.html.HtmlEscapeType; + +import javax.swing.*; +import java.awt.*; +import java.io.File; + +/** + * @author Elandril + */ +public class LegalityLabel extends JLabel { + + protected static final Color COLOR_UNKNOWN = new Color(174, 174, 174); + protected static final Color COLOR_LEGAL = new Color(117, 152, 110); + protected static final Color COLOR_PARTLY_LEGAL = new Color(191, 176, 80); + protected static final Color COLOR_NOT_LEGAL = new Color(191, 84, 74); + protected static final Color COLOR_TEXT = new Color(255, 255, 255); + protected static final Dimension DIM_MINIMUM = new Dimension(75, 25); + protected static final Dimension DIM_MAXIMUM = new Dimension(150, 75); + protected static final Dimension DIM_PREFERRED = new Dimension(75, 25); + + protected Deck currentDeck; + protected String errorMessage; + protected DeckValidator validator; + + /** + * Creates a LegalityLabel instance with the specified text + * and the given DeckValidator. + * + * @param text The text to be displayed by the label. + * @param validator The DeckValidator to check against. + */ + public LegalityLabel(String text, DeckValidator validator) { + super(text); + this.validator = validator; + + setBackground(COLOR_UNKNOWN); + setForeground(COLOR_TEXT); + setHorizontalAlignment(SwingConstants.CENTER); + setMinimumSize(DIM_MINIMUM); + setMaximumSize(DIM_MAXIMUM); + setName(text); // NOI18N + setOpaque(true); + setPreferredSize(DIM_PREFERRED); + } + + /** + * Creates a LegalityLabel instance with the given DeckValidator and uses its + * shortName as the text. + * + * @param validator The DeckValidator to check against. + */ + public LegalityLabel(DeckValidator validator) { + this(validator.getShortName(), validator); + } + + /** + * Creates a LegalityLabel instance with no DeckValidator or text. + * This is used by the Netbeans GUI Editor. + */ + public LegalityLabel() { + super(); + + setBackground(COLOR_UNKNOWN); + setForeground(COLOR_TEXT); + setHorizontalAlignment(SwingConstants.CENTER); + setMinimumSize(DIM_MINIMUM); + setMaximumSize(DIM_MAXIMUM); + setOpaque(true); + setPreferredSize(DIM_PREFERRED); + } + + /** + * Creates hide button to close legality panel (must be same size as label) + */ + public static JButton createHideButton() { + JButton button = new JButton("Hide"); + button.setHorizontalAlignment(SwingConstants.CENTER); + button.setMinimumSize(DIM_MINIMUM); + button.setMaximumSize(DIM_MAXIMUM); + button.setPreferredSize(DIM_PREFERRED); + return button; + } + + public String getErrorMessage() { + return errorMessage; + } + + public DeckValidator getValidator() { + return validator; + } + + public void setValidator(DeckValidator validator) { + this.validator = validator; + revalidateDeck(); + } + + protected String escapeHtml(String string) { + return HtmlEscape.escapeHtml(string, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA, HtmlEscapeLevel.LEVEL_0_ONLY_MARKUP_SIGNIFICANT_EXCEPT_APOS); + } + + protected String formatInvalidTooltip(java.util.List sortedErrorsList) { + return sortedErrorsList.stream() + .reduce("

Deck is INVALID

The following problems have been found:
", + (str, error) -> String.format("%s", str, escapeHtml(error.getGroup()), escapeHtml(error.getMessage())), String::concat) + + "
%s%s
"; + } + + protected String formatPartlyValidTooltip(java.util.List sortedErrorsList) { + return sortedErrorsList.stream() + .reduce("

Deck is PARTLY VALID

The following problems have been found:
", + (str, error) -> String.format("%s", str, escapeHtml(error.getGroup()), escapeHtml(error.getMessage())), String::concat) + + "
%s%s
"; + } + + private String appendErrorMessage(String string) { + if (errorMessage.isEmpty()) + return string; + if (string.contains("")) { + return string.replaceFirst("(()?)", String.format("

The following errors occurred while loading the deck:
%s$1", escapeHtml(errorMessage))); + } + return string.concat("\n\nThe following errors occurred while loading the deck:\n" + errorMessage); + } + + public void showState(Color color) { + setBackground(color); + } + + public void showState(Color color, String tooltip) { + setBackground(color); + setToolTipText(appendErrorMessage(tooltip)); + } + + public void showStateUnknown(String tooltip) { + showState(COLOR_UNKNOWN, tooltip); + } + + public void showStateLegal(String tooltip) { + showState(COLOR_LEGAL, tooltip); + } + + public void showStatePartlyLegal(String tooltip) { + showState(COLOR_PARTLY_LEGAL, tooltip); + } + + public void showStateNotLegal(String tooltip) { + showState(COLOR_NOT_LEGAL, tooltip); + } + + public void validateDeck(Deck deck) { + errorMessage = ""; + currentDeck = deck; + if (deck == null) { + showStateUnknown("No deck loaded!"); + return; + } + if (validator == null) { + showStateUnknown("No deck type selected!"); + return; + } + try { + if (validator.validate(deck)) { + showStateLegal("Deck is VALID"); + } else if (validator.isPartlyValid()) { + showStatePartlyLegal(formatPartlyValidTooltip(validator.getErrorsListSorted())); + } else { + showStateNotLegal(formatInvalidTooltip(validator.getErrorsListSorted())); + } + } catch (Exception e) { + showStateUnknown(String.format("Deck could not be validated!
The following error occurred while validating this deck:
%s", escapeHtml(e.getMessage()))); + } + } + + public void validateDeck(File deckFile) { + deckFile = deckFile.getAbsoluteFile(); + if (!deckFile.exists()) { + errorMessage = String.format("Deck file '%s' does not exist.", deckFile.getAbsolutePath()); + showStateUnknown("No Deck loaded!"); + return; + } + try { + StringBuilder errorMessages = new StringBuilder(); + Deck deck = Deck.load(DeckImporter.importDeckFromFile(deckFile.getAbsolutePath(), errorMessages), true, true); + errorMessage = errorMessages.toString(); + validateDeck(deck); + } catch (Exception ex) { + errorMessage = String.format("Error importing deck from file '%s'!", deckFile.getAbsolutePath()); + } + } + + public void revalidateDeck() { + validateDeck(currentDeck); + } + + public void validateDeck(String deckFile) { + validateDeck(new File(deckFile)); + } +} diff --git a/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java b/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java index ca74f6196f..5d6ee1df71 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java +++ b/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java @@ -1,10 +1,5 @@ package mage.client.components; -//import com.sun.java.swing.Painter; - -import java.awt.Color; -import java.awt.Graphics2D; - import javax.swing.JDesktopPane; import javax.swing.UIDefaults; import javax.swing.UIManager; @@ -22,7 +17,7 @@ public class MageJDesktop extends JDesktopPane { public void updateUI() { if ("Nimbus".equals(UIManager.getLookAndFeel().getName())) { UIDefaults map = new UIDefaults(); - Painter painter = (g, c, w, h) -> { + Painter painter = (g, c, w, h) -> { g.setColor( UIManager.getDefaults().getColor("desktop") ); g.fillRect(0,0,w,h); }; diff --git a/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java b/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java index 7dc263ddc4..df03dc72fc 100644 --- a/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java +++ b/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java @@ -383,7 +383,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener { public static void main(String[] argv) { try { - UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); + UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); } catch (Exception ex) { } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java index 5cc741f495..2125c6be45 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java @@ -10,6 +10,7 @@ import mage.ObjectColor; import mage.cards.Card; import mage.cards.ExpansionSet; import mage.cards.Sets; +import mage.cards.decks.PennyDreadfulLegalityUtil; import mage.cards.repository.*; import mage.client.MageFrame; import mage.client.cards.*; @@ -37,8 +38,8 @@ import javax.swing.*; import javax.swing.table.DefaultTableCellRenderer; import java.awt.*; import java.awt.event.*; -import java.util.*; import java.util.List; +import java.util.*; import java.util.Map.Entry; import static mage.client.dialog.PreferencesDialog.*; @@ -299,6 +300,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene criteria.white(this.tbWhite.isSelected()); criteria.colorless(this.tbColorless.isSelected()); + // if you add new type filter then sync it with CardType if (this.tbLand.isSelected()) { criteria.types(CardType.LAND); } @@ -320,8 +322,6 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene if (this.tbPlaneswalkers.isSelected()) { criteria.types(CardType.PLANESWALKER); } - // criteria.types(CardType.TRIBAL); - // criteria.types(CardType.CONSPIRACY); if (this.tbCommon.isSelected()) { criteria.rarities(Rarity.COMMON); @@ -422,9 +422,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene try { java.util.List filteredCards = new ArrayList<>(); - boolean chkPD = chkPennyDreadful.isSelected(); - if (chkPD) { - generatePennyDreadfulHash(); + if (chkPennyDreadful.isSelected() && pdAllowed.isEmpty()) { + pdAllowed.putAll(PennyDreadfulLegalityUtil.getLegalCardList()); } if (limited) { @@ -438,7 +437,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene for (CardInfo cardInfo : foundCards) { Card card = cardInfo.getMockCard(); if (filter.match(card, null)) { - if (chkPD) { + if (chkPennyDreadful.isSelected()) { if (!pdAllowed.containsKey(card.getName())) { continue; } @@ -479,22 +478,6 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene } } - public void generatePennyDreadfulHash() { - if (pdAllowed.size() > 0) { - return; - } - - Properties properties = new Properties(); - try { - properties.load(CardSelector.class.getResourceAsStream("pennydreadful.properties")); - } catch (Exception e) { - e.printStackTrace(); - } - for (final Entry entry : properties.entrySet()) { - pdAllowed.put((String) entry.getKey(), 1); - } - } - private void reloadSetsCombobox() { DefaultComboBoxModel model = new DefaultComboBoxModel<>(ConstructedFormats.getTypes()); cbExpansionSet.setModel(model); diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.form b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.form index 8db77a1b8a..be4960f0eb 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.form +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.form @@ -11,39 +11,45 @@ + - - - - - - - - - - - - + - + + + + + + + - + + + + + + - + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java index d087803205..40a635038e 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java @@ -1,9 +1,3 @@ - - /* - * DeckArea.java - * - * Created on Feb 18, 2010, 3:10:39 PM - */ package mage.client.deckeditor; import mage.cards.Card; @@ -26,7 +20,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * * @author BetaSteward_at_googlemail.com */ public class DeckArea extends javax.swing.JPanel { @@ -38,7 +31,6 @@ public class DeckArea extends javax.swing.JPanel { private BigCard lastBigCard = null; private int dividerLocationNormal = 0; private int dividerLocationLimited = 0; - private static final boolean isLimitedBuildingOrientation = false; public DeckCardLayout getCardLayout() { return deckList.getCardLayout(); @@ -156,7 +148,7 @@ public class DeckArea extends javax.swing.JPanel { for (CardView card : cards) { CardView newCard = new CardView(card); sideboardList.addCardView(newCard, true); - } + } } @Override @@ -170,7 +162,7 @@ public class DeckArea extends javax.swing.JPanel { }); } - public Settings saveSettings() { + public Settings saveSettings(boolean isLimitedBuildingOrientation) { Settings settings = new Settings(); settings.maindeckSettings = deckList.saveSettings(); settings.sideboardSetings = sideboardList.saveSettings(); @@ -184,7 +176,7 @@ public class DeckArea extends javax.swing.JPanel { return settings; } - public void loadSettings(Settings s) { + public void loadSettings(Settings s, boolean isLimitedBuildingOrientation) { if (s != null) { deckList.loadSettings(s.maindeckSettings); sideboardList.loadSettings(s.sideboardSetings); @@ -215,8 +207,8 @@ public class DeckArea extends javax.swing.JPanel { private void setGUISize() { } - public void setOrientation(boolean limitedBuildingOrientation) { - if (limitedBuildingOrientation) { + public void setOrientation(boolean isLimitedBuildingOrientation) { + if (isLimitedBuildingOrientation) { deckAreaSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT); if (dividerLocationLimited != 0) { deckAreaSplitPane.setDividerLocation(dividerLocationLimited); @@ -298,21 +290,19 @@ public class DeckArea extends javax.swing.JPanel { deckList = new mage.client.cards.DragCardGrid(); sideboardList = new mage.client.cards.DragCardGrid(); + setLayout(new java.awt.BorderLayout()); + deckAreaSplitPane.setBorder(null); - deckAreaSplitPane.setResizeWeight(0.6); + deckAreaSplitPane.setDividerSize(10); + deckAreaSplitPane.setResizeWeight(0.5); + + deckList.setMinimumSize(new java.awt.Dimension(200, 56)); deckAreaSplitPane.setLeftComponent(deckList); + + sideboardList.setMinimumSize(new java.awt.Dimension(200, 56)); deckAreaSplitPane.setRightComponent(sideboardList); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(deckAreaSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 918, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(deckAreaSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 377, Short.MAX_VALUE) - ); + add(deckAreaSplitPane, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents public DragCardGrid getDeckList() { diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.form b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.form index 7428599da1..a743112f19 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.form +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.form @@ -20,6 +20,11 @@ + + + + + diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java index f3b65eb154..48a360f973 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java @@ -74,6 +74,7 @@ public class DeckEditorPane extends MagePane { deckEditorPanel1 = new mage.client.deckeditor.DeckEditorPanel(); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 10), new java.awt.Dimension(0, 32767)); + deckEditorPanel1.setMinimumSize(new java.awt.Dimension(1000, 500)); getContentPane().add(deckEditorPanel1, java.awt.BorderLayout.CENTER); getContentPane().add(filler1, java.awt.BorderLayout.NORTH); }// //GEN-END:initComponents diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form index 78ffbeab4d..98a309ce94 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form @@ -18,21 +18,22 @@ - - + + - + + @@ -47,6 +48,11 @@ + + + + + @@ -60,21 +66,16 @@ - - - - - - - + + - - + + @@ -89,8 +90,6 @@ - - @@ -112,7 +111,7 @@ - + @@ -158,7 +157,7 @@ - + @@ -215,7 +214,7 @@ - + @@ -272,7 +271,7 @@ - + @@ -329,7 +328,7 @@ - + @@ -392,15 +391,20 @@ - + + + - + - + + + + @@ -420,6 +424,16 @@ + + + + + + + + + + @@ -435,7 +449,7 @@ - + @@ -470,6 +484,72 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index e3487b15cf..be08f0511b 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -43,7 +43,7 @@ import static mage.cards.decks.DeckFormats.XMAGE; import static mage.cards.decks.DeckFormats.XMAGE_INFO; /** - * @author BetaSteward_at_googlemail.com, JayDi85 + * @author BetaSteward_at_googlemail.com, JayDi85, Elandril */ public class DeckEditorPanel extends javax.swing.JPanel { @@ -78,7 +78,6 @@ public class DeckEditorPanel extends javax.swing.JPanel { deckArea.setOpaque(false); panelLeft.setOpaque(false); panelRight.setOpaque(false); - restoreDividerLocationsAndDeckAreaSettings(); countdown = new javax.swing.Timer(1000, e -> { if (--timeout > 0) { @@ -93,11 +92,16 @@ public class DeckEditorPanel extends javax.swing.JPanel { } }); - // Set up tracking to save the deck editor settings when the deck editor is hidden. + // save editor settings dynamicly on hides (e.g. app close) addHierarchyListener((HierarchyEvent e) -> { if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) { if (!isShowing()) { - saveDividerLocationsAndDeckAreaSettings(); + // It's bugged and called on sideboarding creates too (before load). So: + // * for free mode - save here + // * for draft/sideboarding - save on cleanup call + if (mode == DeckEditorMode.FREE_BUILDING) { + saveDividerLocationsAndDeckAreaSettings(); + } } } }); @@ -127,21 +131,32 @@ public class DeckEditorPanel extends javax.swing.JPanel { } private void saveDividerLocationsAndDeckAreaSettings() { - PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION, Integer.toString(panelRight.getDividerLocation())); - PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_DECKAREA_SETTINGS, this.deckArea.saveSettings().toString()); + boolean isLimitedBuildingOrientation = (mode != DeckEditorMode.FREE_BUILDING); + if (isLimitedBuildingOrientation) { + PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION_LIMITED, Integer.toString(panelRight.getDividerLocation())); + } else { + PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION_NORMAL, Integer.toString(panelRight.getDividerLocation())); + } + + PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_DECKAREA_SETTINGS, this.deckArea.saveSettings(isLimitedBuildingOrientation).toString()); } private void restoreDividerLocationsAndDeckAreaSettings() { - // Load horizontal split position setting - String dividerLocation = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION, ""); + String dividerLocation = ""; + boolean isLimitedBuildingOrientation = (mode != DeckEditorMode.FREE_BUILDING); + if (isLimitedBuildingOrientation) { + dividerLocation = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION_LIMITED, ""); + } else { + dividerLocation = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION_NORMAL, ""); + } if (!dividerLocation.isEmpty()) { panelRight.setDividerLocation(Integer.parseInt(dividerLocation)); } // Load deck area settings this.deckArea.loadSettings( - DeckArea.Settings.parse( - PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_DECKAREA_SETTINGS, ""))); + DeckArea.Settings.parse(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_DECKAREA_SETTINGS, "")), + isLimitedBuildingOrientation); } public void changeGUISize() { @@ -157,11 +172,18 @@ public class DeckEditorPanel extends javax.swing.JPanel { this.mode = mode; this.btnAddLand.setVisible(false); + // workaround to enable real opaque in scrollbar pane (transparent panel) and remove scroll pane border + scrollPaneInfo.getViewport().setOpaque(false); + scrollPaneInfo.setBorder(BorderFactory.createEmptyBorder()); + scrollPaneInfo.setViewportBorder(BorderFactory.createEmptyBorder()); + + restoreDividerLocationsAndDeckAreaSettings(); switch (mode) { case LIMITED_BUILDING: this.btnAddLand.setVisible(true); this.txtTimeRemaining.setVisible(true); - // Fall through to sideboarding + this.btnLegality.setVisible(false); // legality check available only in free building mode + // Fall through to sideboarding (no break) case SIDEBOARDING: this.btnSubmit.setVisible(true); this.btnSubmitTimer.setVisible(true); @@ -194,6 +216,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { this.btnSubmit.setVisible(false); this.btnSubmitTimer.setVisible(false); this.btnAddLand.setVisible(true); + this.btnLegality.setVisible(true); this.cardSelector.setVisible(true); this.cardSelector.loadCards(this.bigCard); //this.cardTableSelector.loadCards(this.bigCard); @@ -839,7 +862,6 @@ public class DeckEditorPanel extends javax.swing.JPanel { cardInfoPane = new JLabel(); cardInfoPane.setVisible(false); } - bigCard = new mage.client.cards.BigCard(); panelDeck = new javax.swing.JPanel(); panelDeckName = new javax.swing.JPanel(); lblDeckName = new javax.swing.JLabel(); @@ -858,13 +880,21 @@ public class DeckEditorPanel extends javax.swing.JPanel { btnSubmitTimer = new javax.swing.JButton(); panelDeckLands = new javax.swing.JPanel(); btnAddLand = new javax.swing.JButton(); + btnLegality = new javax.swing.JButton(); panelDeckExit = new javax.swing.JPanel(); btnExit = new javax.swing.JButton(); txtTimeRemaining = new javax.swing.JTextField(); + scrollPaneInfo = new javax.swing.JScrollPane(); + panelInfo = new javax.swing.JPanel(); + deckLegalityDisplay = new mage.client.deckeditor.DeckLegalityPanel(); + bigCard = new mage.client.cards.BigCard(); + panelRight.setDividerSize(10); panelRight.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); panelRight.setResizeWeight(0.5); panelRight.setTopComponent(cardSelector); + + deckArea.setMinimumSize(new java.awt.Dimension(200, 56)); panelRight.setBottomComponent(deckArea); panelDeck.setOpaque(false); @@ -884,7 +914,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addContainerGap() .addComponent(lblDeckName) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(txtDeckName, javax.swing.GroupLayout.DEFAULT_SIZE, 175, Short.MAX_VALUE) + .addComponent(txtDeckName, javax.swing.GroupLayout.DEFAULT_SIZE, 205, Short.MAX_VALUE) .addContainerGap()) ); panelDeckNameLayout.setVerticalGroup( @@ -928,7 +958,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addComponent(btnNew, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnGenDeck, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(40, Short.MAX_VALUE)) + .addContainerGap(70, Short.MAX_VALUE)) ); panelDeckCreateLayout.setVerticalGroup( panelDeckCreateLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -971,7 +1001,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addComponent(btnLoad, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnImport, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(40, Short.MAX_VALUE)) + .addContainerGap(70, Short.MAX_VALUE)) ); panelDeckLoadLayout.setVerticalGroup( panelDeckLoadLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1014,7 +1044,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addComponent(btnSave, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnExport, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(40, Short.MAX_VALUE)) + .addContainerGap(70, Short.MAX_VALUE)) ); panelDeckSaveLayout.setVerticalGroup( panelDeckSaveLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1059,7 +1089,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addComponent(btnSubmit, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnSubmitTimer, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(40, Short.MAX_VALUE)) + .addContainerGap(70, Short.MAX_VALUE)) ); panelDeckDraftLayout.setVerticalGroup( panelDeckDraftLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1087,6 +1117,15 @@ public class DeckEditorPanel extends javax.swing.JPanel { } }); + btnLegality.setText("Validate"); + btnLegality.setIconTextGap(2); + btnLegality.setName("btnLegality"); // NOI18N + btnLegality.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnLegalityActionPerformed(evt); + } + }); + javax.swing.GroupLayout panelDeckLandsLayout = new javax.swing.GroupLayout(panelDeckLands); panelDeckLands.setLayout(panelDeckLandsLayout); panelDeckLandsLayout.setHorizontalGroup( @@ -1094,13 +1133,17 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addGroup(panelDeckLandsLayout.createSequentialGroup() .addContainerGap() .addComponent(btnAddLand, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(146, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnLegality, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(70, Short.MAX_VALUE)) ); panelDeckLandsLayout.setVerticalGroup( panelDeckLandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelDeckLandsLayout.createSequentialGroup() + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelDeckLandsLayout.createSequentialGroup() .addContainerGap() - .addComponent(btnAddLand, javax.swing.GroupLayout.DEFAULT_SIZE, 30, Short.MAX_VALUE) + .addGroup(panelDeckLandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(btnAddLand, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(btnLegality, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGap(0, 0, 0)) ); @@ -1131,7 +1174,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addComponent(btnExit, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(txtTimeRemaining, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(40, Short.MAX_VALUE)) + .addContainerGap(70, Short.MAX_VALUE)) ); panelDeckExitLayout.setVerticalGroup( panelDeckExitLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1144,22 +1187,56 @@ public class DeckEditorPanel extends javax.swing.JPanel { panelDeck.add(panelDeckExit); + scrollPaneInfo.setBorder(null); + scrollPaneInfo.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPaneInfo.setOpaque(false); + + panelInfo.setOpaque(false); + + deckLegalityDisplay.setMaximumSize(new java.awt.Dimension(245, 155)); + deckLegalityDisplay.setMinimumSize(new java.awt.Dimension(85, 155)); + deckLegalityDisplay.setOpaque(false); + deckLegalityDisplay.setVisible(false); + + javax.swing.GroupLayout panelInfoLayout = new javax.swing.GroupLayout(panelInfo); + panelInfo.setLayout(panelInfoLayout); + panelInfoLayout.setHorizontalGroup( + panelInfoLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelInfoLayout.createSequentialGroup() + .addGap(15, 15, 15) + .addGroup(panelInfoLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(bigCard, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelInfoLayout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(deckLegalityDisplay, javax.swing.GroupLayout.PREFERRED_SIZE, 245, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE))) + .addGap(15, 15, 15)) + ); + panelInfoLayout.setVerticalGroup( + panelInfoLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelInfoLayout.createSequentialGroup() + .addContainerGap() + .addComponent(deckLegalityDisplay, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + ); + + scrollPaneInfo.setViewportView(panelInfo); + javax.swing.GroupLayout panelLeftLayout = new javax.swing.GroupLayout(panelLeft); panelLeft.setLayout(panelLeftLayout); panelLeftLayout.setHorizontalGroup( panelLeftLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelLeftLayout.createSequentialGroup() - .addGap(0, 0, 0) - .addGroup(panelLeftLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(panelDeck, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(bigCard, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addComponent(panelDeck, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(scrollPaneInfo) ); panelLeftLayout.setVerticalGroup( panelLeftLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(panelLeftLayout.createSequentialGroup() .addComponent(panelDeck, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(scrollPaneInfo) .addContainerGap()) ); @@ -1169,13 +1246,13 @@ public class DeckEditorPanel extends javax.swing.JPanel { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(panelLeft, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) - .addComponent(panelRight, javax.swing.GroupLayout.DEFAULT_SIZE, 890, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(panelRight, javax.swing.GroupLayout.DEFAULT_SIZE, 883, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(panelLeft, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(panelRight, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 808, Short.MAX_VALUE) + .addComponent(panelRight, javax.swing.GroupLayout.Alignment.TRAILING) ); }// //GEN-END:initComponents @@ -1368,6 +1445,11 @@ public class DeckEditorPanel extends javax.swing.JPanel { exportChoose(evt); }//GEN-LAST:event_btnExportActionPerformed + private void btnLegalityActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLegalityActionPerformed + this.deckLegalityDisplay.setVisible(true); + this.deckLegalityDisplay.validateDeck(deck); + }//GEN-LAST:event_btnLegalityActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private mage.client.cards.BigCard bigCard; @@ -1376,6 +1458,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { private javax.swing.JButton btnExport; private javax.swing.JButton btnGenDeck; private javax.swing.JButton btnImport; + private javax.swing.JButton btnLegality; private javax.swing.JButton btnLoad; private javax.swing.JButton btnNew; private javax.swing.JButton btnSave; @@ -1387,6 +1470,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { */ private mage.client.deckeditor.CardSelector cardSelector; private mage.client.deckeditor.DeckArea deckArea; + private mage.client.deckeditor.DeckLegalityPanel deckLegalityDisplay; private javax.swing.JLabel lblDeckName; private javax.swing.JPanel panelDeck; private javax.swing.JPanel panelDeckCreate; @@ -1396,8 +1480,10 @@ public class DeckEditorPanel extends javax.swing.JPanel { private javax.swing.JPanel panelDeckLoad; private javax.swing.JPanel panelDeckName; private javax.swing.JPanel panelDeckSave; + private javax.swing.JPanel panelInfo; private javax.swing.JPanel panelLeft; private javax.swing.JSplitPane panelRight; + private javax.swing.JScrollPane scrollPaneInfo; private javax.swing.JTextField txtDeckName; private javax.swing.JTextField txtTimeRemaining; // End of variables declaration//GEN-END:variables @@ -1425,6 +1511,7 @@ class ImportFilter extends FileFilter { || ext.equalsIgnoreCase("dek") || ext.equalsIgnoreCase("cod") || ext.equalsIgnoreCase("o8d") + || ext.equalsIgnoreCase("json") || ext.equalsIgnoreCase("draft") || ext.equalsIgnoreCase("mtga"); } @@ -1433,7 +1520,7 @@ class ImportFilter extends FileFilter { @Override public String getDescription() { - return "All formats (*.dec; *.mwDeck; *.txt; *.dek; *.cod; *.o8d; *.draft; *.mtga)"; + return "All formats (*.dec; *.mwDeck; *.txt; *.dek; *.cod; *.o8d; *.json; *.draft; *.mtga;)"; } } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckLegalityPanel.form b/Mage.Client/src/main/java/mage/client/deckeditor/DeckLegalityPanel.form new file mode 100644 index 0000000000..4954e9b43d --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckLegalityPanel.form @@ -0,0 +1,97 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckLegalityPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckLegalityPanel.java new file mode 100644 index 0000000000..d253339dcd --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckLegalityPanel.java @@ -0,0 +1,123 @@ +package mage.client.deckeditor; + +import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidator; +import mage.client.components.LegalityLabel; +import mage.deck.*; + +import javax.swing.*; +import java.util.Arrays; +import java.util.stream.Stream; + + +/** + * @author Elandril + */ +public class DeckLegalityPanel extends javax.swing.JPanel { + /** + * Creates new form DeckLegalityPanel + */ + public DeckLegalityPanel() { + initComponents(); + initDeckLabels(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + previewUnknown = new javax.swing.JLabel(); + previewLegal = new javax.swing.JLabel(); + previewNotLegal = new javax.swing.JLabel(); + + setMinimumSize(new java.awt.Dimension(85, 35)); + setName("DeckLegalityPanel"); // NOI18N + setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEADING)); + + previewUnknown.setBackground(new java.awt.Color(174, 174, 174)); + previewUnknown.setForeground(new java.awt.Color(255, 255, 255)); + previewUnknown.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + previewUnknown.setText("Unknown"); + previewUnknown.setMaximumSize(new java.awt.Dimension(150, 50)); + previewUnknown.setMinimumSize(new java.awt.Dimension(75, 25)); + previewUnknown.setName("previewUnknown"); // NOI18N + previewUnknown.setOpaque(true); + previewUnknown.setPreferredSize(new java.awt.Dimension(75, 25)); + add(previewUnknown); + + previewLegal.setBackground(new java.awt.Color(117, 152, 110)); + previewLegal.setForeground(new java.awt.Color(255, 255, 255)); + previewLegal.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + previewLegal.setText("Legal"); + previewLegal.setMaximumSize(new java.awt.Dimension(150, 50)); + previewLegal.setMinimumSize(new java.awt.Dimension(75, 25)); + previewLegal.setName("previewLegal"); // NOI18N + previewLegal.setOpaque(true); + previewLegal.setPreferredSize(new java.awt.Dimension(75, 25)); + add(previewLegal); + + previewNotLegal.setBackground(new java.awt.Color(191, 84, 74)); + previewNotLegal.setForeground(new java.awt.Color(255, 255, 255)); + previewNotLegal.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + previewNotLegal.setText("Not Legal"); + previewNotLegal.setMaximumSize(new java.awt.Dimension(150, 50)); + previewNotLegal.setMinimumSize(new java.awt.Dimension(75, 25)); + previewNotLegal.setName("previewNotLegal"); // NOI18N + previewNotLegal.setOpaque(true); + previewNotLegal.setPreferredSize(new java.awt.Dimension(75, 25)); + add(previewNotLegal); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel previewLegal; + private javax.swing.JLabel previewNotLegal; + private javax.swing.JLabel previewUnknown; + // End of variables declaration//GEN-END:variables + + private void initDeckLabels() { + remove(previewUnknown); + remove(previewLegal); + remove(previewNotLegal); + + Stream.of( + // most popular + new Standard(), new Pioneer(), new Modern(), new Legacy(), new Vintage(), new Pauper(), + // common + new Commander(), new Oathbreaker(), new Brawl(), + // other + new Frontier(), new HistoricalType2(), new PennyDreadfulCommander() + // not used: new Eternal(), new Momir(), new TinyLeaders() + ).forEach(this::addLegalityLabel); + addHidePanelButton(); + + revalidate(); + repaint(); + } + + protected LegalityLabel addLegalityLabel(DeckValidator validator) { + LegalityLabel label = new LegalityLabel(validator); + add(label); + + return label; + } + + protected void addHidePanelButton() { + JButton button = LegalityLabel.createHideButton(); + button.addActionListener(e -> this.setVisible(false)); + add(button); + } + + public void validateDeck(Deck deck) { + Arrays.stream(getComponents()) + .filter(LegalityLabel.class::isInstance) + .map(LegalityLabel.class::cast) + .forEach(label -> label.validateDeck(deck)); + } + +} diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java index 01d5d729d2..ffab6f49d3 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java @@ -562,8 +562,8 @@ public class MageBook extends JComponent { Constructor cons = c.getConstructor(); Object newToken = cons.newInstance(); if (newToken instanceof Token) { - ((Token) newToken).setExpansionSetCodeForImage(set); ((Token) newToken).setOriginalExpansionSetCode(set); + ((Token) newToken).setExpansionSetCodeForImage(set); ((Token) newToken).setTokenType(token.getType()); tokens.add((Token) newToken); } @@ -634,6 +634,10 @@ public class MageBook extends JComponent { } } + if (emblems.size() == 0) { + return emblems; + } + int totalTokens = getTotalNumTokens(set); int start = 0; if (!(page * conf.CARDS_PER_PAGE <= totalTokens && (page + 1) * conf.CARDS_PER_PAGE >= totalTokens)) { @@ -917,8 +921,8 @@ public class MageBook extends JComponent { private int currentPage = 0; private String currentSet = "RTR"; - private int currentCardsInSet = 0; - private int currentCardsNotInSet = 0; + private final int currentCardsInSet = 0; + private final int currentCardsNotInSet = 0; private static CardDimensions cardDimensions = new CardDimensions(1.2d); private static final Logger log = Logger.getLogger(MageBook.class); diff --git a/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java b/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java index 8285714bbb..4a133d4fdc 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java @@ -1,5 +1,14 @@ package mage.client.dialog; +import java.awt.*; +import java.beans.PropertyVetoException; +import java.util.EnumSet; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; +import javax.swing.*; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; import mage.client.cards.BigCard; import mage.client.util.GUISizeHelper; import mage.client.util.ImageHelper; @@ -13,15 +22,6 @@ import mage.view.SimpleCardsView; import org.apache.log4j.Logger; import org.mage.plugins.card.utils.impl.ImageManagerImpl; -import javax.swing.*; -import javax.swing.event.InternalFrameAdapter; -import javax.swing.event.InternalFrameEvent; -import java.awt.*; -import java.beans.PropertyVetoException; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; - /** * @author BetaSteward_at_googlemail.com, JayDi85 */ @@ -30,7 +30,7 @@ public class CardInfoWindowDialog extends MageDialog { private static final Logger LOGGER = Logger.getLogger(CardInfoWindowDialog.class); public enum ShowType { - REVEAL, REVEAL_TOP_LIBRARY, LOOKED_AT, EXILE, GRAVEYARD, OTHER + REVEAL, REVEAL_TOP_LIBRARY, LOOKED_AT, EXILE, GRAVEYARD, COMPANION, OTHER } private final ShowType showType; @@ -72,8 +72,12 @@ public class CardInfoWindowDialog extends MageDialog { case EXILE: this.setFrameIcon(new ImageIcon(ImageManagerImpl.instance.getExileImage())); break; + case COMPANION: + this.setFrameIcon(new ImageIcon(ImageManagerImpl.instance.getTokenIconImage())); + this.setClosable(false); + break; default: - // no icon yet + // no icon yet } this.setTitelBarToolTip(name); setGUISize(); @@ -170,13 +174,17 @@ public class CardInfoWindowDialog extends MageDialog { Set cardTypesPresent = new LinkedHashSet() { }; for (CardView card : cardsView.values()) { - Set cardTypes = card.getCardTypes(); + Set cardTypes = EnumSet.noneOf(CardType.class); + cardTypes.addAll(card.getCardTypes()); for (CardType cardType : cardTypes) { cardTypesPresent.add(cardType.toString()); } } - if (cardTypesPresent.isEmpty()) return 0; - else return cardTypesPresent.size(); + if (cardTypesPresent.isEmpty()) { + return 0; + } else { + return cardTypesPresent.size(); + } } /** diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form index 4b0dae2a96..3b146fe8bc 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form @@ -324,10 +324,12 @@ - - - - + + + + + + @@ -347,6 +349,7 @@ + @@ -360,8 +363,7 @@
- - + @@ -372,32 +374,29 @@ - - + - - - + @@ -414,7 +413,7 @@ - + @@ -424,29 +423,51 @@ - - - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -463,7 +484,7 @@ - + diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index 3fb22126d2..7e5203a75e 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -92,6 +92,118 @@ public class ConnectDialog extends MageDialog { // last settings for reconnect MagePreferences.saveLastServer(); } + + private void setServerSettings(String address, String port, boolean needRegistration) { + this.txtServer.setText(address); + this.txtPort.setText(port); + this.txtUserName.setText(MagePreferences.getUserName(address)); + if (needRegistration) { + this.txtPassword.setText(MagePreferences.getPassword(address)); + } else { + this.txtPassword.setText(""); + } + } + + private void chooseAndSetServerSettingsFromOther() { + BufferedReader in = null; + Writer output = null; + try { + String serverUrl = PreferencesDialog.getCachedValue(KEY_CONNECTION_URL_SERVER_LIST, "http://xmage.de/files/server-list.txt"); + if (serverUrl.contains("xmage.info/files/")) { + serverUrl = serverUrl.replace("xmage.info/files/", "xmage.de/files/"); // replace old URL if still saved + PreferencesDialog.saveValue(KEY_CONNECTION_URL_SERVER_LIST, serverUrl); + } + URL serverListURL = new URL(serverUrl); + + Connection.ProxyType configProxyType = Connection.ProxyType.valueByText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_TYPE, "None")); + Proxy p = null; + Proxy.Type type = Proxy.Type.DIRECT; + switch (configProxyType) { + case HTTP: + type = Proxy.Type.HTTP; + break; + case SOCKS: + type = Proxy.Type.SOCKS; + break; + case NONE: + default: + p = Proxy.NO_PROXY; + break; + } + + if (p == null || !p.equals(Proxy.NO_PROXY)) { + try { + String address = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_ADDRESS, ""); + Integer port = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_PORT, "80")); + p = new Proxy(type, new InetSocketAddress(address, port)); + } catch (Exception ex) { + throw new RuntimeException("Gui_DownloadPictures : error 1 - " + ex); + } + } + + if (p == null) { + JOptionPane.showMessageDialog(null, "Couldn't configure Proxy object!", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + + boolean URLNotFound = false; + try { + in = new BufferedReader(new InputStreamReader(serverListURL.openConnection(p).getInputStream())); + } catch (SocketTimeoutException | FileNotFoundException | UnknownHostException ex) { + logger.warn("Could not read serverlist from: " + serverListURL.toString()); + File f = new File("serverlist.txt"); + if (f.exists() && !f.isDirectory()) { + logger.info("Using buffered serverlist: serverlist.txt"); + URLNotFound = true; + in = new BufferedReader(new FileReader("serverlist.txt")); + } + } + List servers = new ArrayList<>(); + if (in != null) { + + if (!URLNotFound) { + // write serverlist to be able to read if URL is not available + File file = new File("serverlist.txt"); + if (file.exists() && !file.isDirectory()) { + file.delete(); + } + output = new BufferedWriter(new FileWriter(file)); + } + + String inputLine; + while ((inputLine = in.readLine()) != null) { + logger.debug("Found server: " + inputLine); + servers.add(inputLine); + if (output != null) { + output.append(inputLine).append('\n'); + + } + } + } + if (servers.isEmpty()) { + JOptionPane.showMessageDialog(null, "Couldn't find any server."); + return; + } + + String selectedServer = (String) JOptionPane.showInputDialog(null, + "Choose XMage Public Server:", "Input", + JOptionPane.INFORMATION_MESSAGE, null, servers.toArray(), + servers.get(0)); + if (selectedServer != null) { + String[] params = selectedServer.split(":"); + if (params.length == 3) { + setServerSettings(params[1], params[2], true); + } else { + JOptionPane.showMessageDialog(null, "Wrong server data format."); + } + } + } catch (Exception ex) { + logger.error(ex, ex); + } finally { + StreamUtils.closeQuietly(in); + StreamUtils.closeQuietly(output); + } + } /** * This method is called from within the constructor to initialize the form. @@ -127,6 +239,7 @@ public class ConnectDialog extends MageDialog { btnFindBeta = new javax.swing.JButton(); btnFindUs = new javax.swing.JButton(); btnFindOther = new javax.swing.JButton(); + btnFindEU = new javax.swing.JButton(); panelServer = new javax.swing.JPanel(); txtServer = new javax.swing.JTextField(); txtPort = new javax.swing.JTextField(); @@ -242,31 +355,27 @@ public class ConnectDialog extends MageDialog { btnFindMain.setIcon(new javax.swing.ImageIcon(getClass().getResource("/flags/de.png"))); // NOI18N btnFindMain.setText("X"); - btnFindMain.setToolTipText("Connect to xmage.de (Europe, most popular, registration needs)"); - btnFindMain.setActionCommand("connectXmageDe"); + btnFindMain.setToolTipText("Connect to xmage.de (first Europe server, most popular, registration needs)"); btnFindMain.setAlignmentY(0.0F); btnFindMain.setMargin(new java.awt.Insets(2, 2, 2, 2)); btnFindMain.setMaximumSize(new java.awt.Dimension(42, 23)); btnFindMain.setMinimumSize(new java.awt.Dimension(42, 23)); - btnFindMain.setName("connectXmageDeBtn"); // NOI18N btnFindMain.setPreferredSize(new java.awt.Dimension(23, 23)); btnFindMain.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - connectXmageDe(evt); + btnFindMainActionPerformed(evt); } }); btnFindLocal.setText("LOCAL, AI"); btnFindLocal.setToolTipText("Connect to localhost, AI enabled (run local server from launcher)"); - btnFindLocal.setActionCommand("connectLocalhost"); btnFindLocal.setAlignmentY(0.0F); btnFindLocal.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); btnFindLocal.setMargin(new java.awt.Insets(2, 2, 2, 2)); - btnFindLocal.setName("connectLocalhostBtn"); // NOI18N btnFindLocal.setPreferredSize(new java.awt.Dimension(23, 23)); btnFindLocal.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - connectLocalhost(evt); + btnFindLocalActionPerformed(evt); } }); @@ -278,31 +387,42 @@ public class ConnectDialog extends MageDialog { btnFindBeta.setPreferredSize(new java.awt.Dimension(23, 23)); btnFindBeta.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - btnFindBetaconnectLocalhost(evt); + btnFindBetaActionPerformed(evt); } }); btnFindUs.setIcon(new javax.swing.ImageIcon(getClass().getResource("/flags/us.png"))); // NOI18N btnFindUs.setText("US"); btnFindUs.setToolTipText("Connect to us.xmage.today (USA, use any username without registration)"); - btnFindUs.setActionCommand("connectXmageus"); btnFindUs.setAlignmentY(0.0F); btnFindUs.setMargin(new java.awt.Insets(2, 2, 2, 2)); - btnFindUs.setName("connectXmageusBtn"); // NOI18N btnFindUs.setPreferredSize(new java.awt.Dimension(23, 23)); btnFindUs.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - connectXmageus(evt); + btnFindUsActionPerformed(evt); } }); - btnFindOther.setText("Other servers..."); + btnFindOther.setText("Other..."); btnFindOther.setToolTipText("Choose server from full servers list"); btnFindOther.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnFindOther.setName("findServerBtn"); // NOI18N btnFindOther.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - findPublicServerActionPerformed(evt); + btnFindOtherActionPerformed(evt); + } + }); + + btnFindEU.setIcon(new javax.swing.ImageIcon(getClass().getResource("/flags/europeanunion.png"))); // NOI18N + btnFindEU.setText("EU"); + btnFindEU.setToolTipText("Connect to eu.xmage.today (second Europe server, use any username without registration)"); + btnFindEU.setAlignmentY(0.0F); + btnFindEU.setMargin(new java.awt.Insets(2, 2, 2, 2)); + btnFindEU.setMaximumSize(new java.awt.Dimension(42, 23)); + btnFindEU.setMinimumSize(new java.awt.Dimension(42, 23)); + btnFindEU.setPreferredSize(new java.awt.Dimension(23, 23)); + btnFindEU.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnFindEUActionPerformed(evt); } }); @@ -312,9 +432,11 @@ public class ConnectDialog extends MageDialog { panelFastLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(panelFastLayout.createSequentialGroup() .addGap(0, 0, 0) - .addComponent(btnFindMain, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(btnFindMain, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnFindUs, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(btnFindEU, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnFindUs, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnFindBeta, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -332,7 +454,8 @@ public class ConnectDialog extends MageDialog { .addComponent(btnFindLocal, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(btnFindUs, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(btnFindBeta, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(btnFindOther, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(btnFindOther, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(btnFindEU, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(0, 0, 0)) ); @@ -368,7 +491,7 @@ public class ConnectDialog extends MageDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(txtPort, javax.swing.GroupLayout.PREFERRED_SIZE, 75, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnCheckStatus, javax.swing.GroupLayout.DEFAULT_SIZE, 205, Short.MAX_VALUE) + .addComponent(btnCheckStatus, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGap(0, 0, 0)) ); panelServerLayout.setVerticalGroup( @@ -615,110 +738,7 @@ public class ConnectDialog extends MageDialog { }//GEN-LAST:event_chkAutoConnectActionPerformed private void findPublicServerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - BufferedReader in = null; - Writer output = null; - try { - String serverUrl = PreferencesDialog.getCachedValue(KEY_CONNECTION_URL_SERVER_LIST, "http://xmage.de/files/server-list.txt"); - if (serverUrl.contains("xmage.info/files/")) { - serverUrl = serverUrl.replace("xmage.info/files/", "xmage.de/files/"); // replace old URL if still saved - PreferencesDialog.saveValue(KEY_CONNECTION_URL_SERVER_LIST, serverUrl); - } - URL serverListURL = new URL(serverUrl); - - Connection.ProxyType configProxyType = Connection.ProxyType.valueByText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_TYPE, "None")); - Proxy p = null; - Proxy.Type type = Proxy.Type.DIRECT; - switch (configProxyType) { - case HTTP: - type = Proxy.Type.HTTP; - break; - case SOCKS: - type = Proxy.Type.SOCKS; - break; - case NONE: - default: - p = Proxy.NO_PROXY; - break; - } - - if (p == null || !p.equals(Proxy.NO_PROXY)) { - try { - String address = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_ADDRESS, ""); - Integer port = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_PORT, "80")); - p = new Proxy(type, new InetSocketAddress(address, port)); - } catch (Exception ex) { - throw new RuntimeException("Gui_DownloadPictures : error 1 - " + ex); - } - } - - if (p == null) { - JOptionPane.showMessageDialog(null, "Couldn't configure Proxy object!", "Error", JOptionPane.ERROR_MESSAGE); - return; - } - - boolean URLNotFound = false; - try { - in = new BufferedReader(new InputStreamReader(serverListURL.openConnection(p).getInputStream())); - } catch (SocketTimeoutException | FileNotFoundException | UnknownHostException ex) { - logger.warn("Could not read serverlist from: " + serverListURL.toString()); - File f = new File("serverlist.txt"); - if (f.exists() && !f.isDirectory()) { - logger.info("Using buffered serverlist: serverlist.txt"); - URLNotFound = true; - in = new BufferedReader(new FileReader("serverlist.txt")); - } - } - List servers = new ArrayList<>(); - if (in != null) { - - if (!URLNotFound) { - // write serverlist to be able to read if URL is not available - File file = new File("serverlist.txt"); - if (file.exists() && !file.isDirectory()) { - file.delete(); - } - output = new BufferedWriter(new FileWriter(file)); - } - - String inputLine; - while ((inputLine = in.readLine()) != null) { - logger.debug("Found server: " + inputLine); - servers.add(inputLine); - if (output != null) { - output.append(inputLine).append('\n'); - - } - } - } - if (servers.isEmpty()) { - JOptionPane.showMessageDialog(null, "Couldn't find any server."); - return; - } - - String selectedServer = (String) JOptionPane.showInputDialog(null, - "Choose XMage Public Server:", "Input", - JOptionPane.INFORMATION_MESSAGE, null, servers.toArray(), - servers.get(0)); - if (selectedServer != null) { - String[] params = selectedServer.split(":"); - if (params.length == 3) { - String serverAddress = params[1]; - this.txtServer.setText(serverAddress); - this.txtPort.setText(params[2]); - // Update userName and password according to the chosen server. - this.txtUserName.setText(MagePreferences.getUserName(serverAddress)); - this.txtPassword.setText(MagePreferences.getPassword(serverAddress)); - } else { - JOptionPane.showMessageDialog(null, "Wrong server data format."); - } - } - - } catch (Exception ex) { - logger.error(ex, ex); - } finally { - StreamUtils.closeQuietly(in); - StreamUtils.closeQuietly(output); - } + }//GEN-LAST:event_jButton1ActionPerformed private void jProxySettingsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jProxySettingsButtonActionPerformed @@ -786,10 +806,6 @@ public class ConnectDialog extends MageDialog { doFastFlagSearch(); }//GEN-LAST:event_btnFlagSearchActionPerformed - private void btnFindBetaconnectLocalhost(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFindBetaconnectLocalhost - connectBeta(evt); - }//GEN-LAST:event_btnFindBetaconnectLocalhost - private void btnCheckStatusActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCheckStatusActionPerformed if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { try { @@ -804,6 +820,30 @@ public class ConnectDialog extends MageDialog { MageFrame.getInstance().showWhatsNewDialog(true); }//GEN-LAST:event_btnWhatsNewActionPerformed + private void btnFindMainActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFindMainActionPerformed + setServerSettings("xmage.de", "17171", true); + }//GEN-LAST:event_btnFindMainActionPerformed + + private void btnFindEUActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFindEUActionPerformed + setServerSettings("eu.xmage.today", "17171", false); + }//GEN-LAST:event_btnFindEUActionPerformed + + private void btnFindUsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFindUsActionPerformed + setServerSettings("us.xmage.today", "17171", false); + }//GEN-LAST:event_btnFindUsActionPerformed + + private void btnFindBetaActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFindBetaActionPerformed + setServerSettings("beta.xmage.today", "17171", false); + }//GEN-LAST:event_btnFindBetaActionPerformed + + private void btnFindLocalActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFindLocalActionPerformed + setServerSettings("localhost", "17171", false); + }//GEN-LAST:event_btnFindLocalActionPerformed + + private void btnFindOtherActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFindOtherActionPerformed + chooseAndSetServerSettingsFromOther(); + }//GEN-LAST:event_btnFindOtherActionPerformed + private void doFastFlagSearch() { Choice choice = new ChoiceImpl(false); @@ -852,6 +892,7 @@ public class ConnectDialog extends MageDialog { private javax.swing.JButton btnCheckStatus; private javax.swing.JButton btnConnect; private javax.swing.JButton btnFindBeta; + private javax.swing.JButton btnFindEU; private javax.swing.JButton btnFindLocal; private javax.swing.JButton btnFindMain; private javax.swing.JButton btnFindOther; diff --git a/Mage.Client/src/main/java/mage/client/dialog/GameEndDialog.java b/Mage.Client/src/main/java/mage/client/dialog/GameEndDialog.java index 94920b36ef..ad7f4abc7a 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/GameEndDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/GameEndDialog.java @@ -31,15 +31,12 @@ */ public class GameEndDialog extends MageDialog { - private final DateFormat df = DateFormat.getDateTimeInstance(); - /** * Creates new form GameEndDialog * * @param gameEndView */ public GameEndDialog(GameEndView gameEndView) { - initComponents(); this.modal = true; @@ -47,7 +44,14 @@ pnlText.setBackground(new Color(240, 240, 240, 140)); Rectangle r = new Rectangle(610, 250); - Image image = ImageHelper.getImageFromResources(gameEndView.hasWon() ? "/game_won.jpg" : "/game_lost.jpg"); + + Image image; + if (gameEndView.hasWon()) { + image = ImageHelper.getImageFromResources(PreferencesDialog.getCurrentTheme().getWinlossPath("game_won.jpg")); + } else { + image = ImageHelper.getImageFromResources(PreferencesDialog.getCurrentTheme().getWinlossPath("game_lost.jpg")); + } + BufferedImage imageResult = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r); ImageIcon icon = new ImageIcon(imageResult); lblResultImage.setIcon(icon); @@ -61,6 +65,8 @@ this.saveGameLog(gameEndView); } + DateFormat df = DateFormat.getDateTimeInstance(); + // game duration txtDurationGame.setText(" " + Format.getDuration(gameEndView.getStartTime(), gameEndView.getEndTime())); txtDurationGame.setToolTipText(new StringBuilder(df.format(gameEndView.getStartTime())).append(" - ").append(df.format(gameEndView.getEndTime())).toString()); diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index ea34892d98..a905c84926 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -890,7 +890,7 @@ public class NewTableDialog extends MageDialog { this.spnFreeMulligans.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS + versionStr, "0"))); this.cbMulligan.setSelectedItem(MulliganType.valueByName(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_MULLIGAN_TYPE + versionStr, MulliganType.GAME_DEFAULT.toString()))); - int range = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RANGE + versionStr, "1")); + int range = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RANGE + versionStr, "0")); for (RangeOfInfluence roi : RangeOfInfluence.values()) { if (roi.getRange() == range) { this.cbRange.setSelectedItem(roi); diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java index 648a13651a..2e4b980870 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -1203,6 +1203,7 @@ public class NewTournamentDialog extends MageDialog { tOptions.getLimitedOptions().setConstructionTime((Integer) this.spnConstructTime.getValue() * 60); tOptions.getLimitedOptions().setIsRandom(tournamentType.isRandom()); tOptions.getLimitedOptions().setIsRichMan(tournamentType.isRichMan()); + tOptions.getLimitedOptions().setIsJumpstart(tournamentType.isJumpstart()); if (tournamentType.isCubeBooster()) { tOptions.getLimitedOptions().setDraftCubeName(this.cbDraftCube.getSelectedItem().toString()); if (!(cubeFromDeckFilename.isEmpty())) { diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickCheckBoxDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PickCheckBoxDialog.java index 4f41afc930..584d5b5aa3 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PickCheckBoxDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PickCheckBoxDialog.java @@ -230,12 +230,12 @@ public class PickCheckBoxDialog extends MageDialog { if (filter == null) { filter = ""; } - filter = filter.toLowerCase(); + filter = filter.toLowerCase(Locale.ENGLISH); this.dataModel.clear(); this.m_dataModel.clear(); for (KeyValueItem item : this.allItems) { - if (!choice.isSearchEnabled() || item.Value.toLowerCase().contains(filter)) { + if (!choice.isSearchEnabled() || item.Value.toLowerCase(Locale.ENGLISH).contains(filter)) { this.dataModel.addElement(item); this.m_dataModel.addElement(item.getObjectValue()); } diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.form b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.form index 8f4d4356cd..a6cffbba21 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.form @@ -1,6 +1,9 @@
+ + + @@ -39,7 +42,7 @@ - + @@ -55,8 +58,8 @@ - - + + @@ -203,4 +206,4 @@ - \ No newline at end of file + diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java index 2c992a715f..92ba6cac31 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java @@ -1,25 +1,22 @@ package mage.client.dialog; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import mage.choices.Choice; import mage.client.MageFrame; import mage.client.util.gui.MageDialogState; -import javax.swing.*; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import java.awt.*; -import java.awt.event.*; -import java.util.*; -import java.util.List; - /** * @author JayDi85 */ - public class PickChoiceDialog extends MageDialog { Choice choice; - List allItems = new ArrayList<>(); + java.util.List allItems = new ArrayList<>(); DefaultListModel dataModel = new DefaultListModel<>(); final private static String HTML_TEMPLATE = "
%s
"; @@ -79,7 +76,7 @@ public class PickChoiceDialog extends MageDialog { this.editSearch.setText(""); } - // listeners for inremental filtering + // listeners for inremental filtering editSearch.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { @@ -104,7 +101,7 @@ public class PickChoiceDialog extends MageDialog { editSearch.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { - //System.out.println("types"); + //System.out.println("types"); } @Override @@ -152,8 +149,11 @@ public class PickChoiceDialog extends MageDialog { } else { MageFrame.getDesktop().add(this, JLayeredPane.PALETTE_LAYER); } - if (mageDialogState != null) mageDialogState.setStateToDialog(this); - else this.makeWindowCentered(); + if (mageDialogState != null) { + mageDialogState.setStateToDialog(this); + } else { + this.makeWindowCentered(); + } // final load loadData(); @@ -269,6 +269,7 @@ public class PickChoiceDialog extends MageDialog { } class KeyValueItem { + private final String Key; private final String Value; @@ -312,6 +313,8 @@ public class PickChoiceDialog extends MageDialog { btOK = new javax.swing.JButton(); btCancel = new javax.swing.JButton(); + setResizable(true); + labelMessage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); labelMessage.setText("
example long message example long message example long message example long message example long message
"); @@ -322,20 +325,20 @@ public class PickChoiceDialog extends MageDialog { javax.swing.GroupLayout panelHeaderLayout = new javax.swing.GroupLayout(panelHeader); panelHeader.setLayout(panelHeaderLayout); panelHeaderLayout.setHorizontalGroup( - panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelHeaderLayout.createSequentialGroup() - .addGroup(panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(labelMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 210, Short.MAX_VALUE) - .addComponent(labelSubMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 210, Short.MAX_VALUE)) - .addGap(0, 0, 0)) + panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelHeaderLayout.createSequentialGroup() + .addGroup(panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(labelMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 337, Short.MAX_VALUE) + .addComponent(labelSubMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 337, Short.MAX_VALUE)) + .addGap(0, 0, 0)) ); panelHeaderLayout.setVerticalGroup( - panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelHeaderLayout.createSequentialGroup() - .addGap(0, 0, 0) - .addComponent(labelMessage) - .addGap(0, 0, 0) - .addComponent(labelSubMessage)) + panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelHeaderLayout.createSequentialGroup() + .addGap(0, 0, 0) + .addComponent(labelMessage) + .addGap(0, 0, 0) + .addComponent(labelSubMessage)) ); labelSearch.setText("Search:"); @@ -345,34 +348,28 @@ public class PickChoiceDialog extends MageDialog { javax.swing.GroupLayout panelSearchLayout = new javax.swing.GroupLayout(panelSearch); panelSearch.setLayout(panelSearchLayout); panelSearchLayout.setHorizontalGroup( - panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelSearchLayout.createSequentialGroup() - .addGap(0, 0, 0) - .addComponent(labelSearch) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(editSearch) - .addGap(0, 0, 0)) + panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelSearchLayout.createSequentialGroup() + .addGap(0, 0, 0) + .addComponent(labelSearch) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(editSearch) + .addGap(0, 0, 0)) ); panelSearchLayout.setVerticalGroup( - panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelSearchLayout.createSequentialGroup() - .addGap(3, 3, 3) - .addGroup(panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelSearch) - .addComponent(editSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(3, 3, 3)) + panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelSearchLayout.createSequentialGroup() + .addGap(3, 3, 3) + .addGroup(panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(labelSearch) + .addComponent(editSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(3, 3, 3)) ); listChoices.setModel(new javax.swing.AbstractListModel() { - String[] strings = {"item1", "item2", "item3"}; - - public int getSize() { - return strings.length; - } - - public Object getElementAt(int i) { - return strings[i]; - } + String[] strings = { "item1", "item2", "item3" }; + public int getSize() { return strings.length; } + public Object getElementAt(int i) { return strings[i]; } }); scrollList.setViewportView(listChoices); @@ -393,25 +390,25 @@ public class PickChoiceDialog extends MageDialog { javax.swing.GroupLayout panelCommandsLayout = new javax.swing.GroupLayout(panelCommands); panelCommands.setLayout(panelCommandsLayout); panelCommandsLayout.setHorizontalGroup( - panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelCommandsLayout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(btOK) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelCommandsLayout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(btOK) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) ); - panelCommandsLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, btCancel, btOK); + panelCommandsLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {btCancel, btOK}); panelCommandsLayout.setVerticalGroup( - panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelCommandsLayout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(btCancel) - .addComponent(btOK)) - .addContainerGap()) + panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelCommandsLayout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(btCancel) + .addComponent(btOK)) + .addContainerGap()) ); getRootPane().setDefaultButton(btOK); @@ -419,28 +416,28 @@ public class PickChoiceDialog extends MageDialog { javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollList, javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(panelCommands, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(panelHeader, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(panelSearch, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addContainerGap()) + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(scrollList, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(panelCommands, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(panelHeader, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(panelSearch, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) ); layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addComponent(panelHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(panelSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(scrollList, javax.swing.GroupLayout.DEFAULT_SIZE, 246, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(panelCommands, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(panelHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(panelSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(scrollList, javax.swing.GroupLayout.DEFAULT_SIZE, 282, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(panelCommands, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) ); pack(); @@ -474,4 +471,4 @@ public class PickChoiceDialog extends MageDialog { private javax.swing.JPanel panelSearch; private javax.swing.JScrollPane scrollList; // End of variables declaration//GEN-END:variables -} \ No newline at end of file +} diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form index 559770d732..1e52f05391 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -6236,6 +6236,114 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 47a8fd897a..1b9e9881ba 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -3,6 +3,7 @@ package mage.client.dialog; import mage.client.MageFrame; import mage.client.SessionHandler; import mage.client.components.KeyBindButton; +import mage.client.themes.ThemeType; import mage.client.util.CardLanguage; import mage.client.util.ClientDefaultSettings; import mage.client.util.GUISizeHelper; @@ -100,6 +101,9 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_BIG_CARD_TOGGLED = "bigCardToggled"; + // Themes + public static final String KEY_THEME = "themeSelection"; + // Phases public static final String UPKEEP_YOU = "upkeepYou"; public static final String DRAW_YOU = "drawYou"; @@ -156,7 +160,8 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_TABLES_DIVIDER_LOCATION_4 = "tablePanelDividerLocation4"; // Positions of deck editor divider bars - public static final String KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION = "editorHorizontalDividerLocation"; + public static final String KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION_NORMAL = "editorHorizontalDividerLocationNormal"; + public static final String KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION_LIMITED = "editorHorizontalDividerLocationLimited"; public static final String KEY_EDITOR_DECKAREA_SETTINGS = "editorDeckAreaSettings"; // user list @@ -310,6 +315,17 @@ public class PreferencesDialog extends javax.swing.JDialog { private static int selectedAvatarId; + private static ThemeType currentTheme = null; + + public static ThemeType getCurrentTheme() { + if (currentTheme == null) { + currentTheme = ThemeType.valueByName(getCachedValue(KEY_THEME, "Default")); + logger.info("Using GUI theme: " + currentTheme.getName()); + } + + return currentTheme; + } + private final JFileChooser fc = new JFileChooser(); { @@ -359,6 +375,7 @@ public class PreferencesDialog extends javax.swing.JDialog { initComponents(); txtImageFolderPath.setEditable(false); cbProxyType.setModel(new DefaultComboBoxModel<>(Connection.ProxyType.values())); + cbTheme.setModel(new DefaultComboBoxModel<>(ThemeType.values())); addAvatars(); cbPreferedImageLanguage.setModel(new DefaultComboBoxModel<>(CardLanguage.toList())); @@ -469,7 +486,7 @@ public class PreferencesDialog extends javax.swing.JDialog { txtImageFolderPath = new javax.swing.JTextField(); btnBrowseImageLocation = new javax.swing.JButton(); cbSaveToZipFiles = new javax.swing.JCheckBox(); - cbPreferedImageLanguage = new javax.swing.JComboBox<>(); + cbPreferedImageLanguage = new javax.swing.JComboBox(); labelPreferedImageLanguage = new javax.swing.JLabel(); labelNumberOfDownloadThreads = new javax.swing.JLabel(); cbNumberOfDownloadThreads = new javax.swing.JComboBox(); @@ -530,7 +547,7 @@ public class PreferencesDialog extends javax.swing.JDialog { txtURLServerList = new javax.swing.JTextField(); jLabel17 = new javax.swing.JLabel(); lblProxyType = new javax.swing.JLabel(); - cbProxyType = new javax.swing.JComboBox<>(); + cbProxyType = new javax.swing.JComboBox(); pnlProxySettings = new javax.swing.JPanel(); pnlProxy = new javax.swing.JPanel(); lblProxyServer = new javax.swing.JLabel(); @@ -568,6 +585,11 @@ public class PreferencesDialog extends javax.swing.JDialog { keyToggleRecordMacro = new KeyBindButton(this, KEY_CONTROL_TOGGLE_MACRO); labelSwitchChat = new javax.swing.JLabel(); keySwitchChat = new KeyBindButton(this, KEY_CONTROL_SWITCH_CHAT); + tabThemes = new javax.swing.JPanel(); + themesCategory = new javax.swing.JPanel(); + lbSelectLabel = new javax.swing.JLabel(); + cbTheme = new javax.swing.JComboBox(); + lbThemeHint = new javax.swing.JLabel(); saveButton = new javax.swing.JButton(); exitButton = new javax.swing.JButton(); @@ -1534,7 +1556,7 @@ public class PreferencesDialog extends javax.swing.JDialog { }); cbPreferedImageLanguage.setMaximumRowCount(20); - cbPreferedImageLanguage.setModel(new javax.swing.DefaultComboBoxModel<>(new String[]{"Item 1", "Item 2", "Item 3", "Item 4"})); + cbPreferedImageLanguage.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"Item 1", "Item 2", "Item 3", "Item 4"})); labelPreferedImageLanguage.setText("Default images language:"); labelPreferedImageLanguage.setFocusable(false); @@ -2663,6 +2685,70 @@ public class PreferencesDialog extends javax.swing.JDialog { tabsPanel.addTab("Controls", tabControls); + themesCategory.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Themes")); + + lbSelectLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lbSelectLabel.setText("Select a theme:"); + lbSelectLabel.setToolTipText(""); + lbSelectLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); + lbSelectLabel.setPreferredSize(new java.awt.Dimension(110, 16)); + lbSelectLabel.setVerticalTextPosition(javax.swing.SwingConstants.TOP); + + cbTheme.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cbThemeActionPerformed(evt); + } + }); + + lbThemeHint.setText("Requires a restart to apply new theme."); + + org.jdesktop.layout.GroupLayout themesCategoryLayout = new org.jdesktop.layout.GroupLayout(themesCategory); + themesCategory.setLayout(themesCategoryLayout); + themesCategoryLayout.setHorizontalGroup( + themesCategoryLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(themesCategoryLayout.createSequentialGroup() + .addContainerGap() + .add(lbSelectLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 96, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(themesCategoryLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(lbThemeHint) + .add(cbTheme, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 303, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(303, Short.MAX_VALUE)) + ); + themesCategoryLayout.setVerticalGroup( + themesCategoryLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(themesCategoryLayout.createSequentialGroup() + .add(themesCategoryLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING) + .add(cbTheme, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(lbSelectLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 22, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(lbThemeHint) + .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + org.jdesktop.layout.GroupLayout tabThemesLayout = new org.jdesktop.layout.GroupLayout(tabThemes); + tabThemes.setLayout(tabThemesLayout); + tabThemesLayout.setHorizontalGroup( + tabThemesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(0, 750, Short.MAX_VALUE) + .add(tabThemesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(tabThemesLayout.createSequentialGroup() + .addContainerGap() + .add(themesCategory, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap())) + ); + tabThemesLayout.setVerticalGroup( + tabThemesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(0, 526, Short.MAX_VALUE) + .add(tabThemesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(tabThemesLayout.createSequentialGroup() + .add(21, 21, 21) + .add(themesCategory, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addContainerGap(430, Short.MAX_VALUE))) + ); + + tabsPanel.addTab("Themes", tabThemes); + saveButton.setLabel("Save"); saveButton.setMaximumSize(new java.awt.Dimension(100, 30)); saveButton.setMinimumSize(new java.awt.Dimension(100, 30)); @@ -2879,6 +2965,9 @@ public class PreferencesDialog extends javax.swing.JDialog { save(prefs, dialog.keyToggleRecordMacro); save(prefs, dialog.keySwitchChat); + // Themes + save(prefs, dialog.cbTheme, KEY_THEME); + // Avatar if (selectedAvatarId < MIN_AVATAR_ID || selectedAvatarId > MAX_AVATAR_ID) { selectedAvatarId = DEFAULT_AVATAR_ID; @@ -3184,6 +3273,10 @@ public class PreferencesDialog extends javax.swing.JDialog { } }//GEN-LAST:event_cbUseDefaultImageFolderActionPerformed + private void cbThemeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbThemeActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_cbThemeActionPerformed + private void showProxySettings() { Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem(); switch (proxyType) { @@ -3268,6 +3361,9 @@ public class PreferencesDialog extends javax.swing.JDialog { // Controls loadControlSettings(prefs); + // Themes + loadThemeSettings(prefs); + // Selected avatar loadSelectedAvatar(prefs); @@ -3457,6 +3553,10 @@ public class PreferencesDialog extends javax.swing.JDialog { load(prefs, dialog.keySwitchChat); } + private static void loadThemeSettings(Preferences prefs) { + dialog.cbTheme.setSelectedItem(PreferencesDialog.getCurrentTheme()); + } + private static void loadSelectedAvatar(Preferences prefs) { getSelectedAvatar(); dialog.setSelectedId(selectedAvatarId); @@ -3916,6 +4016,7 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JCheckBox cbStopOnAllEnd; private javax.swing.JCheckBox cbStopOnAllMain; private javax.swing.JCheckBox cbStopOnNewStackObjects; + private javax.swing.JComboBox cbTheme; private javax.swing.JCheckBox cbUseDefaultBackground; private javax.swing.JCheckBox cbUseDefaultBattleImage; private javax.swing.JCheckBox cbUseDefaultImageFolder; @@ -4015,6 +4116,8 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JLabel labelToggleRecordMacro; private javax.swing.JLabel labelTooltipSize; private javax.swing.JLabel labelYourTurn; + private javax.swing.JLabel lbSelectLabel; + private javax.swing.JLabel lbThemeHint; private javax.swing.JLabel lblBattlefieldFeedbackColorizingMode; private javax.swing.JLabel lblProxyPassword; private javax.swing.JLabel lblProxyPort; @@ -4062,7 +4165,9 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JPanel tabMain; private javax.swing.JPanel tabPhases; private javax.swing.JPanel tabSounds; + private javax.swing.JPanel tabThemes; private javax.swing.JTabbedPane tabsPanel; + private javax.swing.JPanel themesCategory; private javax.swing.JSlider tooltipDelay; private javax.swing.JLabel tooltipDelayLabel; private javax.swing.JTextField txtBackgroundImagePath; diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index 9e598ed5f5..25a5da5b83 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -35,6 +35,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.UUID; @@ -202,7 +203,7 @@ public class TestCardRenderDialog extends MageDialog { Player playerOpponent = new StubPlayer("player2", RangeOfInfluence.ALL); game.addPlayer(playerOpponent, deck); - java.util.List cardViews = new ArrayList<>(); + List cardViews = new ArrayList<>(); ///* cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "263", 0, 0, 0, false)); // mountain cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true)); // Judith, the Scourge Diva @@ -399,7 +400,7 @@ class TestGame extends GameImpl { private int numPlayers; public TestGame(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 60); } public TestGame(final TestGame game) { diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java index d5b41eb9e0..f63d0853e6 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java @@ -1,864 +1,863 @@ /* - * DraftPanel.java - * - * Created on Jan 7, 2011, 2:15:48 PM - */ -package mage.client.draft; + * DraftPanel.java + * + * Created on Jan 7, 2011, 2:15:48 PM + */ + package mage.client.draft; -import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; -import mage.client.MageFrame; -import mage.client.SessionHandler; -import mage.client.components.tray.MageTray; -import mage.client.deckeditor.SortSettingDraft; -import mage.client.dialog.PreferencesDialog; -import mage.client.plugins.impl.Plugins; -import mage.client.util.*; -import mage.client.util.Event; -import mage.client.util.audio.AudioManager; -import mage.client.util.gui.BufferedImageBuilder; -import mage.constants.PlayerAction; -import mage.view.*; + import mage.cards.repository.CardInfo; + import mage.cards.repository.CardRepository; + import mage.client.MageFrame; + import mage.client.SessionHandler; + import mage.client.components.tray.MageTray; + import mage.client.deckeditor.SortSettingDraft; + import mage.client.dialog.PreferencesDialog; + import mage.client.plugins.impl.Plugins; + import mage.client.util.Event; + import mage.client.util.*; + import mage.client.util.audio.AudioManager; + import mage.client.util.gui.BufferedImageBuilder; + import mage.constants.PlayerAction; + import mage.view.*; -import javax.swing.*; -import javax.swing.Timer; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.image.BufferedImage; -import java.io.File; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.List; + import javax.swing.Timer; + import javax.swing.*; + import java.awt.*; + import java.awt.event.ActionEvent; + import java.awt.event.ActionListener; + import java.awt.event.KeyEvent; + import java.awt.image.BufferedImage; + import java.io.File; + import java.text.SimpleDateFormat; + import java.util.List; + import java.util.*; /** - * - * @author BetaSteward_at_googlemail.com - */ -public class DraftPanel extends javax.swing.JPanel { + * @author BetaSteward_at_googlemail.com + */ + public class DraftPanel extends javax.swing.JPanel { - private UUID draftId; - private Timer countdown; - private int timeout; + private UUID draftId; + private Timer countdown; + private int timeout; - // popup menu area picked cards - private final JPopupMenu popupMenuPickedArea; - // popup menu for a card - private final JPopupMenu popupMenuCardPanel; - // cards hidden in the picked cards area - private final Set cardsHidden = new HashSet<>(); - // all cards picked - protected SimpleCardsView pickedCards; - // all cards picked - protected final SimpleCardsView pickedCardsShown = new SimpleCardsView(); - // id of card with popup menu - protected UUID cardIdPopupMenu; + // popup menu area picked cards + private final JPopupMenu popupMenuPickedArea; + // popup menu for a card + private final JPopupMenu popupMenuCardPanel; + // cards hidden in the picked cards area + private final Set cardsHidden = new HashSet<>(); + // all cards picked + protected SimpleCardsView pickedCards; + // all cards picked + protected final SimpleCardsView pickedCardsShown = new SimpleCardsView(); + // id of card with popup menu + protected UUID cardIdPopupMenu; - // Helper for writing the draft log. - private DraftPickLogger draftLogger; + // Helper for writing the draft log. + private DraftPickLogger draftLogger; - // List of set codes (for draft log writing). - private List setCodes; + // List of set codes (for draft log writing). + private List setCodes; - // Number of the current booster (for draft log writing). - private int packNo; + // Number of the current booster (for draft log writing). + private int packNo; - // Number of the current card pick (for draft log writing). - private int pickNo; + // Number of the current card pick (for draft log writing). + private int pickNo; - // Cached booster data to be written into the log (see logLastPick). - private String[] currentBooster; + // Cached booster data to be written into the log (see logLastPick). + private String[] currentBooster; - private static final CardsView EMPTY_VIEW = new CardsView(); + private static final CardsView EMPTY_VIEW = new CardsView(); - /** - * Creates new form DraftPanel - */ - public DraftPanel() { - initComponents(); + /** + * Creates new form DraftPanel + */ + public DraftPanel() { + initComponents(); - draftBooster.setOpaque(false); - draftPicks.setSortSetting(SortSettingDraft.getInstance()); - draftPicks.setOpaque(false); + draftBooster.setOpaque(false); + draftPicks.setSortSetting(SortSettingDraft.getInstance()); + draftPicks.setOpaque(false); - popupMenuPickedArea = new JPopupMenu(); - addPopupMenuPickArea(); - this.add(popupMenuPickedArea); + popupMenuPickedArea = new JPopupMenu(); + addPopupMenuPickArea(); + this.add(popupMenuPickedArea); - popupMenuCardPanel = new JPopupMenu(); - addPopupMenuCardPanel(); - this.add(popupMenuCardPanel); + popupMenuCardPanel = new JPopupMenu(); + addPopupMenuCardPanel(); + this.add(popupMenuCardPanel); - draftLeftPane.setOpaque(false); + draftLeftPane.setOpaque(false); - countdown = new Timer(1000, - e -> { - if (--timeout > 0) { - setTimeout(timeout); - } else { - setTimeout(0); - countdown.stop(); - } - } - ); - } + countdown = new Timer(1000, + e -> { + if (--timeout > 0) { + setTimeout(timeout); + } else { + setTimeout(0); + countdown.stop(); + } + } + ); + } - public void cleanUp() { - draftPicks.cleanUp(); - draftBooster.clear(); + public void cleanUp() { + draftPicks.cleanUp(); + draftBooster.clear(); - if (countdown != null) { - countdown.stop(); - for (ActionListener al : countdown.getActionListeners()) { - countdown.removeActionListener(al); - } - } - } + if (countdown != null) { + countdown.stop(); + for (ActionListener al : countdown.getActionListeners()) { + countdown.removeActionListener(al); + } + } + } - public void changeGUISize() { - draftPicks.changeGUISize(); - setGUISize(); - } + public void changeGUISize() { + draftPicks.changeGUISize(); + setGUISize(); + } - private void setGUISize() { - GUISizeHelper.changePopupMenuFont(popupMenuPickedArea); - } + private void setGUISize() { + GUISizeHelper.changePopupMenuFont(popupMenuPickedArea); + } - public synchronized void showDraft(UUID draftId) { - this.draftId = draftId; - MageFrame.addDraft(draftId, this); - if (!SessionHandler.joinDraft(draftId)) { - hideDraft(); - } + public synchronized void showDraft(UUID draftId) { + this.draftId = draftId; + MageFrame.addDraft(draftId, this); + if (!SessionHandler.joinDraft(draftId)) { + hideDraft(); + } - if (isLogging()) { - SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); - String logFilename = "Draft_" + sdf.format(new Date()) + '_' + draftId + ".draft"; - draftLogger = new DraftPickLogger(new File("gamelogs"), logFilename); - } else { - draftLogger = new DraftPickLogger(); - } - } + if (isLogging()) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); + String logFilename = "Draft_" + sdf.format(new Date()) + '_' + draftId + ".draft"; + draftLogger = new DraftPickLogger(new File("gamelogs"), logFilename); + } else { + draftLogger = new DraftPickLogger(); + } + } - public void updateDraft(DraftView draftView) { - if (draftView.getSets().size() != 3) { - // Random draft - this.txtPack1.setText("Random Boosters"); - this.txtPack2.setText("Random Boosters"); - this.txtPack3.setText("Random Boosters"); - } else { - this.txtPack1.setText(draftView.getSets().get(0)); - this.txtPack2.setText(draftView.getSets().get(1)); - this.txtPack3.setText(draftView.getSets().get(2)); - } - this.chkPack1.setSelected(draftView.getBoosterNum() > 0); - this.chkPack2.setSelected(draftView.getBoosterNum() > 1); - this.chkPack3.setSelected(draftView.getBoosterNum() > 2); - this.txtCardNo.setText(Integer.toString(draftView.getCardNum())); + public void updateDraft(DraftView draftView) { + if (draftView.getSets().size() != 3) { + // Random draft + this.txtPack1.setText("Random Boosters"); + this.txtPack2.setText("Random Boosters"); + this.txtPack3.setText("Random Boosters"); + } else { + this.txtPack1.setText(draftView.getSets().get(0)); + this.txtPack2.setText(draftView.getSets().get(1)); + this.txtPack3.setText(draftView.getSets().get(2)); + } + this.chkPack1.setSelected(draftView.getBoosterNum() > 0); + this.chkPack2.setSelected(draftView.getBoosterNum() > 1); + this.chkPack3.setSelected(draftView.getBoosterNum() > 2); + this.txtCardNo.setText(Integer.toString(draftView.getCardNum())); - packNo = draftView.getBoosterNum(); - pickNo = draftView.getCardNum(); - setCodes = draftView.getSetCodes(); - draftLogger.updateDraft(draftId, draftView); + packNo = draftView.getBoosterNum(); + pickNo = draftView.getCardNum(); + setCodes = draftView.getSetCodes(); + draftLogger.updateDraft(draftId, draftView); - int right = draftView.getPlayers().size() / 2; - int left = draftView.getPlayers().size() - right; - int height = left * 18; - lblTableImage.setSize(new Dimension(lblTableImage.getWidth(), height)); - Image tableImage = ImageHelper.getImageFromResources(draftView.getBoosterNum() == 2 ? "/draft/table_left.png" : "/draft/table_right.png"); - BufferedImage resizedTable = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(tableImage, BufferedImage.TYPE_INT_ARGB), lblTableImage.getWidth(), lblTableImage.getHeight()); - lblTableImage.setIcon(new ImageIcon(resizedTable)); + int right = draftView.getPlayers().size() / 2; + int left = draftView.getPlayers().size() - right; + int height = left * 18; + lblTableImage.setSize(new Dimension(lblTableImage.getWidth(), height)); + Image tableImage = ImageHelper.getImageFromResources(draftView.getBoosterNum() == 2 ? "/draft/table_left.png" : "/draft/table_right.png"); + BufferedImage resizedTable = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(tableImage, BufferedImage.TYPE_INT_ARGB), lblTableImage.getWidth(), lblTableImage.getHeight()); + lblTableImage.setIcon(new ImageIcon(resizedTable)); - int count = 0; - int numberPlayers = draftView.getPlayers().size(); - for (String playerName : draftView.getPlayers()) { - count++; - setPlayerNameToLabel(playerName, count, numberPlayers); - } - } + int count = 0; + int numberPlayers = draftView.getPlayers().size(); + for (String playerName : draftView.getPlayers()) { + count++; + setPlayerNameToLabel(playerName, count, numberPlayers); + } + } - private void setPlayerNameToLabel(String name, int index, int players) { - int tablePosition; - int right = players / 2; - int left = players - right; - if (index <= left) { - // left side down (1 - 8) - tablePosition = index; - } else { - // right side up (16 - 9) - tablePosition = 9 + right - (index - left); - } - switch (tablePosition) { - case 1: - lblPlayer01.setText(name); - break; - case 2: - lblPlayer02.setText(name); - break; - case 3: - lblPlayer03.setText(name); - break; - case 4: - lblPlayer04.setText(name); - break; - case 5: - lblPlayer05.setText(name); - break; - case 6: - lblPlayer06.setText(name); - break; - case 7: - lblPlayer07.setText(name); - break; - case 8: - lblPlayer08.setText(name); - break; - case 9: - lblPlayer09.setText(name); - break; - case 10: - lblPlayer10.setText(name); - break; - case 11: - lblPlayer11.setText(name); - break; - case 12: - lblPlayer12.setText(name); - break; - case 13: - lblPlayer13.setText(name); - break; - case 14: - lblPlayer14.setText(name); - break; - case 15: - lblPlayer15.setText(name); - break; - case 16: - lblPlayer16.setText(name); - break; - } - } + private void setPlayerNameToLabel(String name, int index, int players) { + int tablePosition; + int right = players / 2; + int left = players - right; + if (index <= left) { + // left side down (1 - 8) + tablePosition = index; + } else { + // right side up (16 - 9) + tablePosition = 9 + right - (index - left); + } + switch (tablePosition) { + case 1: + lblPlayer01.setText(name); + break; + case 2: + lblPlayer02.setText(name); + break; + case 3: + lblPlayer03.setText(name); + break; + case 4: + lblPlayer04.setText(name); + break; + case 5: + lblPlayer05.setText(name); + break; + case 6: + lblPlayer06.setText(name); + break; + case 7: + lblPlayer07.setText(name); + break; + case 8: + lblPlayer08.setText(name); + break; + case 9: + lblPlayer09.setText(name); + break; + case 10: + lblPlayer10.setText(name); + break; + case 11: + lblPlayer11.setText(name); + break; + case 12: + lblPlayer12.setText(name); + break; + case 13: + lblPlayer13.setText(name); + break; + case 14: + lblPlayer14.setText(name); + break; + case 15: + lblPlayer15.setText(name); + break; + case 16: + lblPlayer16.setText(name); + break; + } + } - public void loadBooster(DraftPickView draftPickView) { - logLastPick(draftPickView); - // upper area that shows the picks - loadCardsToPickedCardsArea(draftPickView.getPicks()); + public void loadBooster(DraftPickView draftPickView) { + logLastPick(draftPickView); + // upper area that shows the picks + loadCardsToPickedCardsArea(draftPickView.getPicks()); - this.draftPicks.clearCardEventListeners(); - this.draftPicks.addCardEventListener((Listener) event -> { - if (event.getEventType() == ClientEventType.SHOW_POP_UP_MENU) { - if (event.getSource() != null) { - // Popup Menu Card - cardIdPopupMenu = ((SimpleCardView) event.getSource()).getId(); - popupMenuCardPanel.show(event.getComponent(), event.getxPos(), event.getyPos()); - } else { - // Popup Menu area - popupMenuPickedArea.show(event.getComponent(), event.getxPos(), event.getyPos()); - } - } - } - ); + this.draftPicks.clearCardEventListeners(); + this.draftPicks.addCardEventListener((Listener) event -> { + if (event.getEventType() == ClientEventType.SHOW_POP_UP_MENU) { + if (event.getSource() != null) { + // Popup Menu Card + cardIdPopupMenu = ((SimpleCardView) event.getSource()).getId(); + popupMenuCardPanel.show(event.getComponent(), event.getxPos(), event.getyPos()); + } else { + // Popup Menu area + popupMenuPickedArea.show(event.getComponent(), event.getxPos(), event.getyPos()); + } + } + } + ); - // lower area that shows the booster - draftBooster.loadBooster(CardsViewUtil.convertSimple(draftPickView.getBooster()), bigCard); - this.draftBooster.clearCardEventListeners(); - this.draftBooster.addCardEventListener( - (Listener) event -> { - if (event.getEventType() == ClientEventType.PICK_A_CARD) { - SimpleCardView source = (SimpleCardView) event.getSource(); - DraftPickView view = SessionHandler.sendCardPick(draftId, source.getId(), cardsHidden); - if (view != null) { - loadCardsToPickedCardsArea(view.getPicks()); - draftBooster.loadBooster(EMPTY_VIEW, bigCard); - Plugins.instance.getActionCallback().hideOpenComponents(); - setMessage("Waiting for other players"); - } - } - if (event.getEventType() == ClientEventType.MARK_A_CARD) { - SimpleCardView source = (SimpleCardView) event.getSource(); - SessionHandler.sendCardMark(draftId, source.getId()); - } - } - ); - setMessage("Pick a card"); - if (!MageFrame.getInstance().isActive()) { - MageTray.instance.displayMessage("Pick the next card."); - MageTray.instance.blink(); - } - countdown.stop(); - this.timeout = draftPickView.getTimeout(); - setTimeout(timeout); - if (timeout != 0) { - countdown.start(); - } - } + // lower area that shows the booster + draftBooster.loadBooster(CardsViewUtil.convertSimple(draftPickView.getBooster()), bigCard); + this.draftBooster.clearCardEventListeners(); + this.draftBooster.addCardEventListener( + (Listener) event -> { + if (event.getEventType() == ClientEventType.PICK_A_CARD) { + SimpleCardView source = (SimpleCardView) event.getSource(); + DraftPickView view = SessionHandler.sendCardPick(draftId, source.getId(), cardsHidden); + if (view != null) { + loadCardsToPickedCardsArea(view.getPicks()); + draftBooster.loadBooster(EMPTY_VIEW, bigCard); + Plugins.instance.getActionCallback().hideOpenComponents(); + setMessage("Waiting for other players"); + } + } + if (event.getEventType() == ClientEventType.MARK_A_CARD) { + SimpleCardView source = (SimpleCardView) event.getSource(); + SessionHandler.sendCardMark(draftId, source.getId()); + } + } + ); + setMessage("Pick a card"); + if (!AppUtil.isAppActive()) { + MageTray.instance.displayMessage("Pick the next card."); + MageTray.instance.blink(); + } + countdown.stop(); + this.timeout = draftPickView.getTimeout(); + setTimeout(timeout); + if (timeout != 0) { + countdown.start(); + } + } - private void loadCardsToPickedCardsArea(SimpleCardsView pickedCards) { - this.pickedCards = pickedCards; - for (Map.Entry entry : pickedCards.entrySet()) { - if (!cardsHidden.contains(entry.getKey())) { - pickedCardsShown.put(entry.getKey(), entry.getValue()); - } - } - draftPicks.loadCards(CardsViewUtil.convertSimple(pickedCardsShown), bigCard, null); - } + private void loadCardsToPickedCardsArea(SimpleCardsView pickedCards) { + this.pickedCards = pickedCards; + for (Map.Entry entry : pickedCards.entrySet()) { + if (!cardsHidden.contains(entry.getKey())) { + pickedCardsShown.put(entry.getKey(), entry.getValue()); + } + } + draftPicks.loadCards(CardsViewUtil.convertSimple(pickedCardsShown), bigCard, null); + } - private void setTimeout(int s) { - int minute = s / 60; - int second = s - (minute * 60); - String text; - if (minute < 10) { - text = '0' + Integer.toString(minute) + ':'; - } else { - text = Integer.toString(minute) + ':'; - } - if (second < 10) { - text = text + '0' + Integer.toString(second); - } else { - text = text + Integer.toString(second); - } - this.txtTimeRemaining.setText(text); - if (s == 6 && !draftBooster.isEmptyGrid()) { - AudioManager.playOnCountdown1(); - } - } + private void setTimeout(int s) { + int minute = s / 60; + int second = s - (minute * 60); + String text; + if (minute < 10) { + text = '0' + Integer.toString(minute) + ':'; + } else { + text = Integer.toString(minute) + ':'; + } + if (second < 10) { + text = text + '0' + Integer.toString(second); + } else { + text = text + Integer.toString(second); + } + this.txtTimeRemaining.setText(text); + if (s == 6 && !draftBooster.isEmptyGrid()) { + AudioManager.playOnCountdown1(); + } + } - public void hideDraft() { - Component c = this.getParent(); - while (c != null && !(c instanceof DraftPane)) { - c = c.getParent(); - } - if (c != null) { - ((DraftPane) c).removeDraft(); - } - } + public void hideDraft() { + Component c = this.getParent(); + while (c != null && !(c instanceof DraftPane)) { + c = c.getParent(); + } + if (c != null) { + ((DraftPane) c).removeDraft(); + } + } - protected void setMessage(String message) { - this.lblMessage.setText(message); - } + protected void setMessage(String message) { + this.lblMessage.setText(message); + } - private void addPopupMenuPickArea() { - int c = JComponent.WHEN_IN_FOCUSED_WINDOW; + private void addPopupMenuPickArea() { + int c = JComponent.WHEN_IN_FOCUSED_WINDOW; - KeyStroke ks9 = KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0); - this.getInputMap(c).put(ks9, "F9_PRESS"); - this.getActionMap().put("F9_PRESS", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - showAgainAllHiddenCards(); - } - }); + KeyStroke ks9 = KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0); + this.getInputMap(c).put(ks9, "F9_PRESS"); + this.getActionMap().put("F9_PRESS", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + showAgainAllHiddenCards(); + } + }); - JMenuItem menuItem; + JMenuItem menuItem; - menuItem = new JMenuItem("F9 - Show all hidden cards"); - popupMenuPickedArea.add(menuItem); + menuItem = new JMenuItem("F9 - Show all hidden cards"); + popupMenuPickedArea.add(menuItem); - // Confirm (F9) - menuItem.addActionListener(e -> showAgainAllHiddenCards()); + // Confirm (F9) + menuItem.addActionListener(e -> showAgainAllHiddenCards()); - // popupMenuPickedArea.addSeparator(); - } + // popupMenuPickedArea.addSeparator(); + } - private void addPopupMenuCardPanel() { + private void addPopupMenuCardPanel() { - JMenuItem menuItem; + JMenuItem menuItem; - menuItem = new JMenuItem("Hide this card"); - popupMenuCardPanel.add(menuItem); + menuItem = new JMenuItem("Hide this card"); + popupMenuCardPanel.add(menuItem); - // Hide Card - menuItem.addActionListener(e -> hideThisCard(cardIdPopupMenu)); + // Hide Card + menuItem.addActionListener(e -> hideThisCard(cardIdPopupMenu)); - // popupMenuCardPanel.addSeparator(); - } + // popupMenuCardPanel.addSeparator(); + } - private void hideThisCard(UUID card) { - // Add the card to the hidden cards - cardsHidden.add(card); - pickedCardsShown.remove(card); - draftPicks.loadCards(CardsViewUtil.convertSimple(pickedCardsShown), bigCard, null); - } + private void hideThisCard(UUID card) { + // Add the card to the hidden cards + cardsHidden.add(card); + pickedCardsShown.remove(card); + draftPicks.loadCards(CardsViewUtil.convertSimple(pickedCardsShown), bigCard, null); + } - private void showAgainAllHiddenCards() { - // Add back the hidden cards to the shown set - for (UUID card : cardsHidden) { - pickedCardsShown.put(card, pickedCards.get(card)); - } - cardsHidden.clear(); - draftPicks.loadCards(CardsViewUtil.convertSimple(pickedCardsShown), bigCard, null); - } + private void showAgainAllHiddenCards() { + // Add back the hidden cards to the shown set + for (UUID card : cardsHidden) { + pickedCardsShown.put(card, pickedCards.get(card)); + } + cardsHidden.clear(); + draftPicks.loadCards(CardsViewUtil.convertSimple(pickedCardsShown), bigCard, null); + } - // Log the last card picked into the draft log together with booster - // contents. - // We don't get any event when the card is selected due to timeout - // that's why instead of proactively logging our pick we instead - // log *last* pick from the list of picks. - // To make this possible we cache the list of cards from the - // previous booster and it's sequence number (pack number / pick number) - // in fields currentBooster and currentBoosterHeader. - private void logLastPick(DraftPickView pickView) { - if (!isLogging()) { - return; - } - if (currentBooster != null) { - String lastPick = getCardName(getLastPick(pickView.getPicks().values())); - if (lastPick != null && currentBooster.length > 1) { - draftLogger.logPick(getCurrentSetCode(), packNo, pickNo-1, lastPick, currentBooster); - } - currentBooster = null; - } - setCurrentBoosterForLog(pickView.getBooster()); - if (currentBooster.length == 1) { - draftLogger.logPick(getCurrentSetCode(), packNo, pickNo, currentBooster[0], currentBooster); - } - } + // Log the last card picked into the draft log together with booster + // contents. + // We don't get any event when the card is selected due to timeout + // that's why instead of proactively logging our pick we instead + // log *last* pick from the list of picks. + // To make this possible we cache the list of cards from the + // previous booster and it's sequence number (pack number / pick number) + // in fields currentBooster and currentBoosterHeader. + private void logLastPick(DraftPickView pickView) { + if (!isLogging()) { + return; + } + if (currentBooster != null) { + String lastPick = getCardName(getLastPick(pickView.getPicks().values())); + if (lastPick != null && currentBooster.length > 1) { + draftLogger.logPick(getCurrentSetCode(), packNo, pickNo - 1, lastPick, currentBooster); + } + currentBooster = null; + } + setCurrentBoosterForLog(pickView.getBooster()); + if (currentBooster.length == 1) { + draftLogger.logPick(getCurrentSetCode(), packNo, pickNo, currentBooster[0], currentBooster); + } + } - private String getCurrentSetCode() { - if (!setCodes.isEmpty()) { - return setCodes.get(packNo-1); - } else { - return ""; - } - } + private String getCurrentSetCode() { + if (!setCodes.isEmpty()) { + return setCodes.get(packNo - 1); + } else { + return ""; + } + } - private static boolean isLogging() { - String autoSave = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_DRAFT_LOG_AUTO_SAVE, "true"); - return autoSave.equals("true"); - } + private static boolean isLogging() { + String autoSave = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_DRAFT_LOG_AUTO_SAVE, "true"); + return autoSave.equals("true"); + } - private void setCurrentBoosterForLog(SimpleCardsView booster) { - LinkedList cards = new LinkedList<>(); - for (SimpleCardView simple : booster.values()) { - String cardName = getCardName(simple); - if (cardName != null) { - cards.add(cardName); - } - } + private void setCurrentBoosterForLog(SimpleCardsView booster) { + LinkedList cards = new LinkedList<>(); + for (SimpleCardView simple : booster.values()) { + String cardName = getCardName(simple); + if (cardName != null) { + cards.add(cardName); + } + } - currentBooster = cards.toArray(new String[cards.size()]); - } + currentBooster = cards.toArray(new String[cards.size()]); + } private static SimpleCardView getLastPick(Collection picks) { - SimpleCardView last = null; - for (SimpleCardView pick : picks) { - last = pick; - } - return last; - } + SimpleCardView last = null; + for (SimpleCardView pick : picks) { + last = pick; + } + return last; + } - private static String getCardName(SimpleCardView card) { - if (card == null) { - return null; - } - CardInfo cardInfo = CardRepository.instance.findCard(card.getExpansionSetCode(), card.getCardNumber()); - return cardInfo != null ? cardInfo.getName() : null; - } + private static String getCardName(SimpleCardView card) { + if (card == null) { + return null; + } + CardInfo cardInfo = CardRepository.instance.findCard(card.getExpansionSetCode(), card.getCardNumber()); + return cardInfo != null ? cardInfo.getName() : null; + } - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { - jSeparator1 = new javax.swing.JSeparator(); - draftLeftPane = new javax.swing.JPanel(); - btnQuitTournament = new javax.swing.JButton(); - lblPack1 = new javax.swing.JLabel(); - txtPack1 = new javax.swing.JTextField(); - chkPack1 = new javax.swing.JCheckBox(); - lblPack2 = new javax.swing.JLabel(); - txtPack2 = new javax.swing.JTextField(); - chkPack2 = new javax.swing.JCheckBox(); - lblPack3 = new javax.swing.JLabel(); - txtPack3 = new javax.swing.JTextField(); - chkPack3 = new javax.swing.JCheckBox(); - lblCardNo = new javax.swing.JLabel(); - txtCardNo = new javax.swing.JTextField(); - txtTimeRemaining = new javax.swing.JTextField(); - lblMessage = new javax.swing.JLabel(); - bigCard = new mage.client.cards.BigCard(); - jPanel1 = new javax.swing.JPanel(); - pnlLeft = new javax.swing.JPanel(); - lblPlayer01 = new javax.swing.JLabel(); - lblPlayer02 = new javax.swing.JLabel(); - lblPlayer03 = new javax.swing.JLabel(); - lblPlayer04 = new javax.swing.JLabel(); - lblPlayer05 = new javax.swing.JLabel(); - lblPlayer06 = new javax.swing.JLabel(); - lblPlayer07 = new javax.swing.JLabel(); - lblPlayer08 = new javax.swing.JLabel(); - lblTableImage = new javax.swing.JLabel(); - pnlRight = new javax.swing.JPanel(); - lblPlayer09 = new javax.swing.JLabel(); - lblPlayer10 = new javax.swing.JLabel(); - lblPlayer11 = new javax.swing.JLabel(); - lblPlayer12 = new javax.swing.JLabel(); - lblPlayer13 = new javax.swing.JLabel(); - lblPlayer14 = new javax.swing.JLabel(); - lblPlayer15 = new javax.swing.JLabel(); - lblPlayer16 = new javax.swing.JLabel(); - draftPicks = new mage.client.cards.CardsList(); - draftBooster = new mage.client.cards.DraftGrid(); + jSeparator1 = new javax.swing.JSeparator(); + draftLeftPane = new javax.swing.JPanel(); + btnQuitTournament = new javax.swing.JButton(); + lblPack1 = new javax.swing.JLabel(); + txtPack1 = new javax.swing.JTextField(); + chkPack1 = new javax.swing.JCheckBox(); + lblPack2 = new javax.swing.JLabel(); + txtPack2 = new javax.swing.JTextField(); + chkPack2 = new javax.swing.JCheckBox(); + lblPack3 = new javax.swing.JLabel(); + txtPack3 = new javax.swing.JTextField(); + chkPack3 = new javax.swing.JCheckBox(); + lblCardNo = new javax.swing.JLabel(); + txtCardNo = new javax.swing.JTextField(); + txtTimeRemaining = new javax.swing.JTextField(); + lblMessage = new javax.swing.JLabel(); + bigCard = new mage.client.cards.BigCard(); + jPanel1 = new javax.swing.JPanel(); + pnlLeft = new javax.swing.JPanel(); + lblPlayer01 = new javax.swing.JLabel(); + lblPlayer02 = new javax.swing.JLabel(); + lblPlayer03 = new javax.swing.JLabel(); + lblPlayer04 = new javax.swing.JLabel(); + lblPlayer05 = new javax.swing.JLabel(); + lblPlayer06 = new javax.swing.JLabel(); + lblPlayer07 = new javax.swing.JLabel(); + lblPlayer08 = new javax.swing.JLabel(); + lblTableImage = new javax.swing.JLabel(); + pnlRight = new javax.swing.JPanel(); + lblPlayer09 = new javax.swing.JLabel(); + lblPlayer10 = new javax.swing.JLabel(); + lblPlayer11 = new javax.swing.JLabel(); + lblPlayer12 = new javax.swing.JLabel(); + lblPlayer13 = new javax.swing.JLabel(); + lblPlayer14 = new javax.swing.JLabel(); + lblPlayer15 = new javax.swing.JLabel(); + lblPlayer16 = new javax.swing.JLabel(); + draftPicks = new mage.client.cards.CardsList(); + draftBooster = new mage.client.cards.DraftGrid(); - draftLeftPane.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); - draftLeftPane.setFocusable(false); - draftLeftPane.setRequestFocusEnabled(false); - draftLeftPane.setVerifyInputWhenFocusTarget(false); + draftLeftPane.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); + draftLeftPane.setFocusable(false); + draftLeftPane.setRequestFocusEnabled(false); + draftLeftPane.setVerifyInputWhenFocusTarget(false); - btnQuitTournament.setText("Quit Tournament"); - btnQuitTournament.addActionListener(evt -> btnQuitTournamentActionPerformed(evt)); + btnQuitTournament.setText("Quit Tournament"); + btnQuitTournament.addActionListener(evt -> btnQuitTournamentActionPerformed(evt)); - lblPack1.setText("Pack 1:"); + lblPack1.setText("Pack 1:"); - txtPack1.setEditable(false); - txtPack1.setEnabled(false); - txtPack1.setPreferredSize(new java.awt.Dimension(130, 22)); + txtPack1.setEditable(false); + txtPack1.setEnabled(false); + txtPack1.setPreferredSize(new java.awt.Dimension(130, 22)); - lblPack2.setText("Pack 2:"); + lblPack2.setText("Pack 2:"); - txtPack2.setEditable(false); - txtPack2.setEnabled(false); - txtPack2.setPreferredSize(new java.awt.Dimension(130, 22)); + txtPack2.setEditable(false); + txtPack2.setEnabled(false); + txtPack2.setPreferredSize(new java.awt.Dimension(130, 22)); - lblPack3.setText("Pack 3:"); + lblPack3.setText("Pack 3:"); - txtPack3.setEditable(false); - txtPack3.setEnabled(false); - txtPack3.setPreferredSize(new java.awt.Dimension(130, 22)); + txtPack3.setEditable(false); + txtPack3.setEnabled(false); + txtPack3.setPreferredSize(new java.awt.Dimension(130, 22)); - lblCardNo.setText("Card #:"); + lblCardNo.setText("Card #:"); - txtCardNo.setEditable(false); - txtCardNo.setEnabled(false); + txtCardNo.setEditable(false); + txtCardNo.setEnabled(false); - txtTimeRemaining.setEditable(false); - txtTimeRemaining.setForeground(java.awt.Color.red); - txtTimeRemaining.setHorizontalAlignment(javax.swing.JTextField.CENTER); - txtTimeRemaining.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); + txtTimeRemaining.setEditable(false); + txtTimeRemaining.setForeground(java.awt.Color.red); + txtTimeRemaining.setHorizontalAlignment(javax.swing.JTextField.CENTER); + txtTimeRemaining.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); - lblMessage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - lblMessage.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); - lblMessage.setOpaque(true); + lblMessage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + lblMessage.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); + lblMessage.setOpaque(true); - bigCard.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); + bigCard.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); - jPanel1.setOpaque(false); - jPanel1.setLayout(null); + jPanel1.setOpaque(false); + jPanel1.setLayout(null); - pnlLeft.setFocusable(false); - pnlLeft.setMaximumSize(new java.awt.Dimension(80, 132)); - pnlLeft.setMinimumSize(new java.awt.Dimension(80, 132)); - pnlLeft.setOpaque(false); - pnlLeft.setPreferredSize(new java.awt.Dimension(80, 132)); - pnlLeft.setRequestFocusEnabled(false); - pnlLeft.setVerifyInputWhenFocusTarget(false); - pnlLeft.setLayout(new java.awt.GridLayout(8, 1)); + pnlLeft.setFocusable(false); + pnlLeft.setMaximumSize(new java.awt.Dimension(80, 132)); + pnlLeft.setMinimumSize(new java.awt.Dimension(80, 132)); + pnlLeft.setOpaque(false); + pnlLeft.setPreferredSize(new java.awt.Dimension(80, 132)); + pnlLeft.setRequestFocusEnabled(false); + pnlLeft.setVerifyInputWhenFocusTarget(false); + pnlLeft.setLayout(new java.awt.GridLayout(8, 1)); - lblPlayer01.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer01.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - lblPlayer01.setFocusable(false); - lblPlayer01.setRequestFocusEnabled(false); - lblPlayer01.setVerifyInputWhenFocusTarget(false); - pnlLeft.add(lblPlayer01); - lblPlayer01.getAccessibleContext().setAccessibleName(""); + lblPlayer01.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer01.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); + lblPlayer01.setFocusable(false); + lblPlayer01.setRequestFocusEnabled(false); + lblPlayer01.setVerifyInputWhenFocusTarget(false); + pnlLeft.add(lblPlayer01); + lblPlayer01.getAccessibleContext().setAccessibleName(""); - lblPlayer02.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer02.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - lblPlayer02.setFocusable(false); - lblPlayer02.setRequestFocusEnabled(false); - lblPlayer02.setVerifyInputWhenFocusTarget(false); - pnlLeft.add(lblPlayer02); + lblPlayer02.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer02.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); + lblPlayer02.setFocusable(false); + lblPlayer02.setRequestFocusEnabled(false); + lblPlayer02.setVerifyInputWhenFocusTarget(false); + pnlLeft.add(lblPlayer02); - lblPlayer03.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer03.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - lblPlayer03.setFocusable(false); - lblPlayer03.setRequestFocusEnabled(false); - lblPlayer03.setVerifyInputWhenFocusTarget(false); - pnlLeft.add(lblPlayer03); + lblPlayer03.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer03.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); + lblPlayer03.setFocusable(false); + lblPlayer03.setRequestFocusEnabled(false); + lblPlayer03.setVerifyInputWhenFocusTarget(false); + pnlLeft.add(lblPlayer03); - lblPlayer04.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer04.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - lblPlayer04.setFocusable(false); - lblPlayer04.setRequestFocusEnabled(false); - lblPlayer04.setVerifyInputWhenFocusTarget(false); - pnlLeft.add(lblPlayer04); + lblPlayer04.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer04.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); + lblPlayer04.setFocusable(false); + lblPlayer04.setRequestFocusEnabled(false); + lblPlayer04.setVerifyInputWhenFocusTarget(false); + pnlLeft.add(lblPlayer04); - lblPlayer05.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer05.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - lblPlayer05.setFocusable(false); - lblPlayer05.setRequestFocusEnabled(false); - lblPlayer05.setVerifyInputWhenFocusTarget(false); - pnlLeft.add(lblPlayer05); + lblPlayer05.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer05.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); + lblPlayer05.setFocusable(false); + lblPlayer05.setRequestFocusEnabled(false); + lblPlayer05.setVerifyInputWhenFocusTarget(false); + pnlLeft.add(lblPlayer05); - lblPlayer06.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer06.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - lblPlayer06.setFocusable(false); - lblPlayer06.setRequestFocusEnabled(false); - lblPlayer06.setVerifyInputWhenFocusTarget(false); - pnlLeft.add(lblPlayer06); + lblPlayer06.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer06.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); + lblPlayer06.setFocusable(false); + lblPlayer06.setRequestFocusEnabled(false); + lblPlayer06.setVerifyInputWhenFocusTarget(false); + pnlLeft.add(lblPlayer06); - lblPlayer07.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer07.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - lblPlayer07.setFocusable(false); - lblPlayer07.setRequestFocusEnabled(false); - lblPlayer07.setVerifyInputWhenFocusTarget(false); - pnlLeft.add(lblPlayer07); + lblPlayer07.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer07.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); + lblPlayer07.setFocusable(false); + lblPlayer07.setRequestFocusEnabled(false); + lblPlayer07.setVerifyInputWhenFocusTarget(false); + pnlLeft.add(lblPlayer07); - lblPlayer08.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer08.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - lblPlayer08.setFocusable(false); - lblPlayer08.setRequestFocusEnabled(false); - lblPlayer08.setVerifyInputWhenFocusTarget(false); - pnlLeft.add(lblPlayer08); + lblPlayer08.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer08.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); + lblPlayer08.setFocusable(false); + lblPlayer08.setRequestFocusEnabled(false); + lblPlayer08.setVerifyInputWhenFocusTarget(false); + pnlLeft.add(lblPlayer08); - jPanel1.add(pnlLeft); - pnlLeft.setBounds(0, 5, 90, 136); + jPanel1.add(pnlLeft); + pnlLeft.setBounds(0, 5, 90, 136); - lblTableImage.setBackground(new java.awt.Color(51, 102, 255)); - lblTableImage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - lblTableImage.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); - lblTableImage.setFocusable(false); - lblTableImage.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - lblTableImage.setOpaque(true); - lblTableImage.setRequestFocusEnabled(false); - lblTableImage.setVerifyInputWhenFocusTarget(false); - jPanel1.add(lblTableImage); - lblTableImage.setBounds(95, 5, 40, 136); + lblTableImage.setBackground(new java.awt.Color(51, 102, 255)); + lblTableImage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + lblTableImage.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); + lblTableImage.setFocusable(false); + lblTableImage.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + lblTableImage.setOpaque(true); + lblTableImage.setRequestFocusEnabled(false); + lblTableImage.setVerifyInputWhenFocusTarget(false); + jPanel1.add(lblTableImage); + lblTableImage.setBounds(95, 5, 40, 136); - pnlRight.setFocusable(false); - pnlRight.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - pnlRight.setMaximumSize(new java.awt.Dimension(80, 132)); - pnlRight.setMinimumSize(new java.awt.Dimension(80, 132)); - pnlRight.setOpaque(false); - pnlRight.setPreferredSize(new java.awt.Dimension(80, 132)); - pnlRight.setRequestFocusEnabled(false); - pnlRight.setVerifyInputWhenFocusTarget(false); - pnlRight.setLayout(new java.awt.GridLayout(8, 1)); + pnlRight.setFocusable(false); + pnlRight.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + pnlRight.setMaximumSize(new java.awt.Dimension(80, 132)); + pnlRight.setMinimumSize(new java.awt.Dimension(80, 132)); + pnlRight.setOpaque(false); + pnlRight.setPreferredSize(new java.awt.Dimension(80, 132)); + pnlRight.setRequestFocusEnabled(false); + pnlRight.setVerifyInputWhenFocusTarget(false); + pnlRight.setLayout(new java.awt.GridLayout(8, 1)); - lblPlayer09.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer09.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - lblPlayer09.setFocusable(false); - lblPlayer09.setRequestFocusEnabled(false); - lblPlayer09.setVerifyInputWhenFocusTarget(false); - pnlRight.add(lblPlayer09); + lblPlayer09.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer09.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lblPlayer09.setFocusable(false); + lblPlayer09.setRequestFocusEnabled(false); + lblPlayer09.setVerifyInputWhenFocusTarget(false); + pnlRight.add(lblPlayer09); - lblPlayer10.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer10.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - lblPlayer10.setFocusable(false); - lblPlayer10.setRequestFocusEnabled(false); - lblPlayer10.setVerifyInputWhenFocusTarget(false); - pnlRight.add(lblPlayer10); + lblPlayer10.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer10.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lblPlayer10.setFocusable(false); + lblPlayer10.setRequestFocusEnabled(false); + lblPlayer10.setVerifyInputWhenFocusTarget(false); + pnlRight.add(lblPlayer10); - lblPlayer11.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer11.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - lblPlayer11.setFocusable(false); - lblPlayer11.setRequestFocusEnabled(false); - lblPlayer11.setVerifyInputWhenFocusTarget(false); - pnlRight.add(lblPlayer11); + lblPlayer11.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer11.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lblPlayer11.setFocusable(false); + lblPlayer11.setRequestFocusEnabled(false); + lblPlayer11.setVerifyInputWhenFocusTarget(false); + pnlRight.add(lblPlayer11); - lblPlayer12.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer12.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - lblPlayer12.setFocusable(false); - lblPlayer12.setRequestFocusEnabled(false); - lblPlayer12.setVerifyInputWhenFocusTarget(false); - pnlRight.add(lblPlayer12); + lblPlayer12.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer12.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lblPlayer12.setFocusable(false); + lblPlayer12.setRequestFocusEnabled(false); + lblPlayer12.setVerifyInputWhenFocusTarget(false); + pnlRight.add(lblPlayer12); - lblPlayer13.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer13.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - lblPlayer13.setFocusable(false); - lblPlayer13.setRequestFocusEnabled(false); - lblPlayer13.setVerifyInputWhenFocusTarget(false); - pnlRight.add(lblPlayer13); + lblPlayer13.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer13.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lblPlayer13.setFocusable(false); + lblPlayer13.setRequestFocusEnabled(false); + lblPlayer13.setVerifyInputWhenFocusTarget(false); + pnlRight.add(lblPlayer13); - lblPlayer14.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer14.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - lblPlayer14.setFocusable(false); - lblPlayer14.setRequestFocusEnabled(false); - lblPlayer14.setVerifyInputWhenFocusTarget(false); - pnlRight.add(lblPlayer14); + lblPlayer14.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer14.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lblPlayer14.setFocusable(false); + lblPlayer14.setRequestFocusEnabled(false); + lblPlayer14.setVerifyInputWhenFocusTarget(false); + pnlRight.add(lblPlayer14); - lblPlayer15.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer15.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - lblPlayer15.setFocusable(false); - lblPlayer15.setRequestFocusEnabled(false); - lblPlayer15.setVerifyInputWhenFocusTarget(false); - pnlRight.add(lblPlayer15); + lblPlayer15.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer15.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lblPlayer15.setFocusable(false); + lblPlayer15.setRequestFocusEnabled(false); + lblPlayer15.setVerifyInputWhenFocusTarget(false); + pnlRight.add(lblPlayer15); - lblPlayer16.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N - lblPlayer16.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - lblPlayer16.setFocusable(false); - lblPlayer16.setRequestFocusEnabled(false); - lblPlayer16.setVerifyInputWhenFocusTarget(false); - pnlRight.add(lblPlayer16); + lblPlayer16.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + lblPlayer16.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lblPlayer16.setFocusable(false); + lblPlayer16.setRequestFocusEnabled(false); + lblPlayer16.setVerifyInputWhenFocusTarget(false); + pnlRight.add(lblPlayer16); - jPanel1.add(pnlRight); - pnlRight.setBounds(140, 5, 90, 136); + jPanel1.add(pnlRight); + pnlRight.setBounds(140, 5, 90, 136); - javax.swing.GroupLayout draftLeftPaneLayout = new javax.swing.GroupLayout(draftLeftPane); - draftLeftPane.setLayout(draftLeftPaneLayout); - draftLeftPaneLayout.setHorizontalGroup( - draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(draftLeftPaneLayout.createSequentialGroup() - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(draftLeftPaneLayout.createSequentialGroup() - .addContainerGap() - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lblCardNo) - .addGroup(draftLeftPaneLayout.createSequentialGroup() - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, draftLeftPaneLayout.createSequentialGroup() - .addComponent(lblPack2) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(txtPack2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, draftLeftPaneLayout.createSequentialGroup() - .addComponent(lblPack1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(txtPack1, javax.swing.GroupLayout.PREFERRED_SIZE, 165, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, draftLeftPaneLayout.createSequentialGroup() - .addComponent(lblPack3) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(txtCardNo) - .addComponent(txtPack3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(chkPack3) - .addComponent(chkPack2) - .addComponent(chkPack1))) - .addGroup(draftLeftPaneLayout.createSequentialGroup() - .addComponent(btnQuitTournament) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(txtTimeRemaining, javax.swing.GroupLayout.PREFERRED_SIZE, 94, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(lblMessage, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 231, Short.MAX_VALUE)))) - .addComponent(bigCard, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(0, 0, Short.MAX_VALUE)) - ); - draftLeftPaneLayout.setVerticalGroup( - draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, draftLeftPaneLayout.createSequentialGroup() - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(btnQuitTournament) - .addComponent(txtTimeRemaining, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(chkPack1) - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblPack1) - .addComponent(txtPack1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblPack2) - .addComponent(txtPack2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(chkPack2)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblPack3) - .addComponent(txtPack3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(chkPack3)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblCardNo) - .addComponent(txtCardNo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lblMessage, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, 148, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 55, Short.MAX_VALUE) - .addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - ); + javax.swing.GroupLayout draftLeftPaneLayout = new javax.swing.GroupLayout(draftLeftPane); + draftLeftPane.setLayout(draftLeftPaneLayout); + draftLeftPaneLayout.setHorizontalGroup( + draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(draftLeftPaneLayout.createSequentialGroup() + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(draftLeftPaneLayout.createSequentialGroup() + .addContainerGap() + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lblCardNo) + .addGroup(draftLeftPaneLayout.createSequentialGroup() + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, draftLeftPaneLayout.createSequentialGroup() + .addComponent(lblPack2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(txtPack2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, draftLeftPaneLayout.createSequentialGroup() + .addComponent(lblPack1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(txtPack1, javax.swing.GroupLayout.PREFERRED_SIZE, 165, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, draftLeftPaneLayout.createSequentialGroup() + .addComponent(lblPack3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(txtCardNo) + .addComponent(txtPack3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(chkPack3) + .addComponent(chkPack2) + .addComponent(chkPack1))) + .addGroup(draftLeftPaneLayout.createSequentialGroup() + .addComponent(btnQuitTournament) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(txtTimeRemaining, javax.swing.GroupLayout.PREFERRED_SIZE, 94, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lblMessage, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 231, Short.MAX_VALUE)))) + .addComponent(bigCard, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, Short.MAX_VALUE)) + ); + draftLeftPaneLayout.setVerticalGroup( + draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, draftLeftPaneLayout.createSequentialGroup() + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(btnQuitTournament) + .addComponent(txtTimeRemaining, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(chkPack1) + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblPack1) + .addComponent(txtPack1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblPack2) + .addComponent(txtPack2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(chkPack2)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblPack3) + .addComponent(txtPack3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(chkPack3)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(draftLeftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblCardNo) + .addComponent(txtCardNo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblMessage, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, 148, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 55, Short.MAX_VALUE) + .addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); - draftBooster.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + draftBooster.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); - javax.swing.GroupLayout draftBoosterLayout = new javax.swing.GroupLayout(draftBooster); - draftBooster.setLayout(draftBoosterLayout); - draftBoosterLayout.setHorizontalGroup( - draftBoosterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 738, Short.MAX_VALUE) - ); - draftBoosterLayout.setVerticalGroup( - draftBoosterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 439, Short.MAX_VALUE) - ); + javax.swing.GroupLayout draftBoosterLayout = new javax.swing.GroupLayout(draftBooster); + draftBooster.setLayout(draftBoosterLayout); + draftBoosterLayout.setHorizontalGroup( + draftBoosterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 738, Short.MAX_VALUE) + ); + draftBoosterLayout.setVerticalGroup( + draftBoosterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 439, Short.MAX_VALUE) + ); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(draftLeftPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(draftPicks, javax.swing.GroupLayout.DEFAULT_SIZE, 740, Short.MAX_VALUE) - .addComponent(draftBooster, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(draftLeftPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(draftPicks, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(draftBooster, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - ); - }// //GEN-END:initComponents + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(draftLeftPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, 0) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(draftPicks, javax.swing.GroupLayout.DEFAULT_SIZE, 740, Short.MAX_VALUE) + .addComponent(draftBooster, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(draftLeftPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(draftPicks, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(draftBooster, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); + }// //GEN-END:initComponents - private void btnQuitTournamentActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuitTournamentActionPerformed - UserRequestMessage message = new UserRequestMessage("Confirm quit tournament", "Are you sure you want to quit the draft tournament?"); - message.setButton1("No", null); - message.setButton2("Yes", PlayerAction.CLIENT_QUIT_DRAFT_TOURNAMENT); - message.setTournamentId(draftId); - MageFrame.getInstance().showUserRequestDialog(message); - }//GEN-LAST:event_btnQuitTournamentActionPerformed + private void btnQuitTournamentActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuitTournamentActionPerformed + UserRequestMessage message = new UserRequestMessage("Confirm quit tournament", "Are you sure you want to quit the draft tournament?"); + message.setButton1("No", null); + message.setButton2("Yes", PlayerAction.CLIENT_QUIT_DRAFT_TOURNAMENT); + message.setTournamentId(draftId); + MageFrame.getInstance().showUserRequestDialog(message); + }//GEN-LAST:event_btnQuitTournamentActionPerformed - // Variables declaration - do not modify//GEN-BEGIN:variables - private mage.client.cards.BigCard bigCard; - private javax.swing.JButton btnQuitTournament; - private javax.swing.JCheckBox chkPack1; - private javax.swing.JCheckBox chkPack2; - private javax.swing.JCheckBox chkPack3; - private mage.client.cards.DraftGrid draftBooster; - private javax.swing.JPanel draftLeftPane; - private mage.client.cards.CardsList draftPicks; - private javax.swing.JPanel jPanel1; - private javax.swing.JSeparator jSeparator1; - private javax.swing.JLabel lblCardNo; - private javax.swing.JLabel lblMessage; - private javax.swing.JLabel lblPack1; - private javax.swing.JLabel lblPack2; - private javax.swing.JLabel lblPack3; - private javax.swing.JLabel lblPlayer01; - private javax.swing.JLabel lblPlayer02; - private javax.swing.JLabel lblPlayer03; - private javax.swing.JLabel lblPlayer04; - private javax.swing.JLabel lblPlayer05; - private javax.swing.JLabel lblPlayer06; - private javax.swing.JLabel lblPlayer07; - private javax.swing.JLabel lblPlayer08; - private javax.swing.JLabel lblPlayer09; - private javax.swing.JLabel lblPlayer10; - private javax.swing.JLabel lblPlayer11; - private javax.swing.JLabel lblPlayer12; - private javax.swing.JLabel lblPlayer13; - private javax.swing.JLabel lblPlayer14; - private javax.swing.JLabel lblPlayer15; - private javax.swing.JLabel lblPlayer16; - private javax.swing.JLabel lblTableImage; - private javax.swing.JPanel pnlLeft; - private javax.swing.JPanel pnlRight; - private javax.swing.JTextField txtCardNo; - private javax.swing.JTextField txtPack1; - private javax.swing.JTextField txtPack2; - private javax.swing.JTextField txtPack3; - private javax.swing.JTextField txtTimeRemaining; - // End of variables declaration//GEN-END:variables + // Variables declaration - do not modify//GEN-BEGIN:variables + private mage.client.cards.BigCard bigCard; + private javax.swing.JButton btnQuitTournament; + private javax.swing.JCheckBox chkPack1; + private javax.swing.JCheckBox chkPack2; + private javax.swing.JCheckBox chkPack3; + private mage.client.cards.DraftGrid draftBooster; + private javax.swing.JPanel draftLeftPane; + private mage.client.cards.CardsList draftPicks; + private javax.swing.JPanel jPanel1; + private javax.swing.JSeparator jSeparator1; + private javax.swing.JLabel lblCardNo; + private javax.swing.JLabel lblMessage; + private javax.swing.JLabel lblPack1; + private javax.swing.JLabel lblPack2; + private javax.swing.JLabel lblPack3; + private javax.swing.JLabel lblPlayer01; + private javax.swing.JLabel lblPlayer02; + private javax.swing.JLabel lblPlayer03; + private javax.swing.JLabel lblPlayer04; + private javax.swing.JLabel lblPlayer05; + private javax.swing.JLabel lblPlayer06; + private javax.swing.JLabel lblPlayer07; + private javax.swing.JLabel lblPlayer08; + private javax.swing.JLabel lblPlayer09; + private javax.swing.JLabel lblPlayer10; + private javax.swing.JLabel lblPlayer11; + private javax.swing.JLabel lblPlayer12; + private javax.swing.JLabel lblPlayer13; + private javax.swing.JLabel lblPlayer14; + private javax.swing.JLabel lblPlayer15; + private javax.swing.JLabel lblPlayer16; + private javax.swing.JLabel lblTableImage; + private javax.swing.JPanel pnlLeft; + private javax.swing.JPanel pnlRight; + private javax.swing.JTextField txtCardNo; + private javax.swing.JTextField txtPack1; + private javax.swing.JTextField txtPack2; + private javax.swing.JTextField txtPack3; + private javax.swing.JTextField txtTimeRemaining; + // End of variables declaration//GEN-END:variables -} + } diff --git a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java index af839050f0..1a86bde7e5 100644 --- a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java @@ -1,245 +1,238 @@ +package mage.client.game; +import mage.cards.MagePermanent; +import mage.client.cards.BigCard; +import mage.client.cards.Permanent; +import mage.client.dialog.PreferencesDialog; +import mage.client.plugins.impl.Plugins; +import mage.client.util.ClientDefaultSettings; +import mage.client.util.GUISizeHelper; +import mage.client.util.audio.AudioManager; +import mage.client.util.layout.CardLayoutStrategy; +import mage.client.util.layout.impl.OldCardLayoutStrategy; +import mage.view.CounterView; +import mage.view.PermanentView; - /* - * BattlefieldPanel.java - * - * Created on 10-Jan-2010, 10:43:14 PM - */ - package mage.client.game; +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.util.List; +import java.util.*; +import java.util.Map.Entry; - import mage.cards.MagePermanent; - import mage.client.cards.BigCard; - import mage.client.cards.Permanent; - import mage.client.dialog.PreferencesDialog; - import mage.client.plugins.impl.Plugins; - import mage.client.util.ClientDefaultSettings; - import mage.client.util.GUISizeHelper; - import mage.client.util.audio.AudioManager; - import mage.client.util.layout.CardLayoutStrategy; - import mage.client.util.layout.impl.OldCardLayoutStrategy; - import mage.view.CounterView; - import mage.view.PermanentView; +/** + * @author BetaSteward_at_googlemail.com + */ +public class BattlefieldPanel extends javax.swing.JLayeredPane { - import javax.swing.*; - import javax.swing.border.Border; - import javax.swing.border.EmptyBorder; - import java.awt.*; - import java.awt.event.ComponentAdapter; - import java.awt.event.ComponentEvent; - import java.util.List; - import java.util.*; - import java.util.Map.Entry; + private final Map permanents = new LinkedHashMap<>(); + private UUID gameId; + private BigCard bigCard; + private final Map uiComponentsList = new HashMap<>(); - /** - * @author BetaSteward_at_googlemail.com - */ - public class BattlefieldPanel extends javax.swing.JLayeredPane { + protected Map battlefield; + private Dimension cardDimension; - private final Map permanents = new LinkedHashMap<>(); - private UUID gameId; - private BigCard bigCard; - private final Map uiComponentsList = new HashMap<>(); + private JLayeredPane jPanel; + private JScrollPane jScrollPane; - protected Map battlefield; - private Dimension cardDimension; + private final CardLayoutStrategy layoutStrategy = new OldCardLayoutStrategy(); - private JLayeredPane jPanel; - private JScrollPane jScrollPane; - private int width; + //private static int iCounter = 0; + private boolean addedPermanent; + private boolean addedArtifact; + private boolean addedCreature; - private final CardLayoutStrategy layoutStrategy = new OldCardLayoutStrategy(); + private boolean removedCreature; + // defines if the battlefield is within a top (means top row of player panels) or a bottom player panel + private boolean topPanelBattlefield; - //private static int iCounter = 0; - private boolean addedPermanent; - private boolean addedArtifact; - private boolean addedCreature; + /** + * Creates new form BattlefieldPanel + */ + public BattlefieldPanel() { + uiComponentsList.put("battlefieldPanel", this); + initComponents(); + uiComponentsList.put("jPanel", jPanel); + setGUISize(); - private boolean removedCreature; - // defines if the battlefield is within a top (means top row of player panels) or a bottom player panel - private boolean topPanelBattlefield; + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + updateSize(); + } + }); + } - /** - * Creates new form BattlefieldPanel - */ - public BattlefieldPanel() { - uiComponentsList.put("battlefieldPanel", this); - initComponents(); - uiComponentsList.put("jPanel", jPanel); - setGUISize(); + public void updateSize() { + this.jScrollPane.setSize(this.getWidth(), this.getHeight()); + sortLayout(); + } - addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - int width = e.getComponent().getWidth(); - int height = e.getComponent().getHeight(); - BattlefieldPanel.this.jScrollPane.setSize(width, height); - BattlefieldPanel.this.width = width; - sortLayout(); - } - }); - } + public void init(UUID gameId, BigCard bigCard) { + this.gameId = gameId; + this.bigCard = bigCard; + } - public void init(UUID gameId, BigCard bigCard) { - this.gameId = gameId; - this.bigCard = bigCard; - } + public void cleanUp() { + for (Component c : this.jPanel.getComponents()) { + if (c instanceof Permanent || c instanceof MagePermanent) { + this.jPanel.remove(c); + } + } + permanents.clear(); + // Plugins.getInstance().sortPermanents(uiComponentsList, permanents.values()); + this.bigCard = null; + } - public void cleanUp() { - for (Component c : this.jPanel.getComponents()) { - if (c instanceof Permanent || c instanceof MagePermanent) { - this.jPanel.remove(c); - } - } - permanents.clear(); - // Plugins.getInstance().sortPermanents(uiComponentsList, permanents.values()); - this.bigCard = null; - } + public void changeGUISize() { + setGUISize(); + sortLayout(); + } - public void changeGUISize() { - setGUISize(); - sortLayout(); - } + private void setGUISize() { + jScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(GUISizeHelper.scrollBarSize, 0)); + jScrollPane.getHorizontalScrollBar().setPreferredSize(new Dimension(0, GUISizeHelper.scrollBarSize)); + cardDimension = GUISizeHelper.battlefieldCardMaxDimension; + } - private void setGUISize() { - jScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(GUISizeHelper.scrollBarSize, 0)); - jScrollPane.getHorizontalScrollBar().setPreferredSize(new Dimension(0, GUISizeHelper.scrollBarSize)); - cardDimension = GUISizeHelper.battlefieldCardMaxDimension; - } + public boolean isTopPanelBattlefield() { + return topPanelBattlefield; + } - public boolean isTopPanelBattlefield() { - return topPanelBattlefield; - } + public void setTopPanelBattlefield(boolean topPanelBattlefield) { + this.topPanelBattlefield = topPanelBattlefield; + } - public void setTopPanelBattlefield(boolean topPanelBattlefield) { - this.topPanelBattlefield = topPanelBattlefield; - } + public void update(Map battlefield) { + boolean changed = false; - public void update(Map battlefield) { - boolean changed = false; + List permanentsToAdd = new ArrayList<>(); + for (PermanentView permanent : battlefield.values()) { + if (!permanent.isPhasedIn()) { + continue; + } + MagePermanent oldMagePermanent = permanents.get(permanent.getId()); + if (oldMagePermanent == null) { + permanentsToAdd.add(permanent); + changed = true; + } else { + if (!changed) { + changed = oldMagePermanent.getOriginalPermanent().isCreature() != permanent.isCreature(); + // Check if there was a chnage in the permanets that are the permanent attached to + if (!changed) { + int attachments = permanent.getAttachments() == null ? 0 : permanent.getAttachments().size(); + int attachmentsBefore = oldMagePermanent.getLinks().size(); + if (attachments != attachmentsBefore) { + changed = true; + } else if (attachments > 0) { + Set attachmentIds = new HashSet<>(permanent.getAttachments()); + for (MagePermanent magePermanent : oldMagePermanent.getLinks()) { + if (!attachmentIds.contains(magePermanent.getOriginalPermanent().getId())) { + // that means that the amount of attachments is the same + // but they are different: + // we've just found an attachment on previous view + // that doesn't exist anymore on current view + changed = true; + break; + } + } + } + } + // Check if permanents it now attached to another or no permanent + if (!changed) { + UUID attachedToIdBefore = oldMagePermanent.getOriginalPermanent().getAttachedTo(); + UUID attachedToId = permanent.getAttachedTo(); + if (attachedToIdBefore == null && attachedToId != null || attachedToId == null && attachedToIdBefore != null + || (attachedToIdBefore != null && !attachedToIdBefore.equals(attachedToId))) { + changed = true; + } + } + // Check for changes in the counters of the permanent + if (!changed) { + List counters1 = oldMagePermanent.getOriginalPermanent().getCounters(); + List counters2 = permanent.getCounters(); + if (counters1 == null && counters2 != null || counters1 != null && counters2 == null) { + changed = true; + } else if (counters1 != null && counters2 != null && counters1.size() != counters2.size()) { + changed = true; + } + } - List permanentsToAdd = new ArrayList<>(); - for (PermanentView permanent : battlefield.values()) { - if (!permanent.isPhasedIn()) { - continue; - } - MagePermanent oldMagePermanent = permanents.get(permanent.getId()); - if (oldMagePermanent == null) { - permanentsToAdd.add(permanent); - changed = true; - } else { - if (!changed) { - changed = oldMagePermanent.getOriginalPermanent().isCreature() != permanent.isCreature(); - // Check if there was a chnage in the permanets that are the permanent attached to - if (!changed) { - int attachments = permanent.getAttachments() == null ? 0 : permanent.getAttachments().size(); - int attachmentsBefore = oldMagePermanent.getLinks().size(); - if (attachments != attachmentsBefore) { - changed = true; - } else if (attachments > 0) { - Set attachmentIds = new HashSet<>(permanent.getAttachments()); - for (MagePermanent magePermanent : oldMagePermanent.getLinks()) { - if (!attachmentIds.contains(magePermanent.getOriginalPermanent().getId())) { - // that means that the amount of attachments is the same - // but they are different: - // we've just found an attachment on previous view - // that doesn't exist anymore on current view - changed = true; - break; - } - } - } - } - // Check if permanents it now attached to another or no permanent - if (!changed) { - UUID attachedToIdBefore = oldMagePermanent.getOriginalPermanent().getAttachedTo(); - UUID attachedToId = permanent.getAttachedTo(); - if (attachedToIdBefore == null && attachedToId != null || attachedToId == null && attachedToIdBefore != null - || (attachedToIdBefore != null && !attachedToIdBefore.equals(attachedToId))) { - changed = true; - } - } - // Check for changes in the counters of the permanent - if (!changed) { - List counters1 = oldMagePermanent.getOriginalPermanent().getCounters(); - List counters2 = permanent.getCounters(); - if (counters1 == null && counters2 != null || counters1 != null && counters2 == null) { - changed = true; - } else if (counters1 != null && counters2 != null && counters1.size() != counters2.size()) { - changed = true; - } - } + } + oldMagePermanent.update(permanent); + } + } - } - oldMagePermanent.update(permanent); - } - } + addedArtifact = addedCreature = addedPermanent = false; - addedArtifact = addedCreature = addedPermanent = false; + int count = permanentsToAdd.size(); + for (PermanentView permanent : permanentsToAdd) { + addPermanent(permanent, count); + } - int count = permanentsToAdd.size(); - for (PermanentView permanent : permanentsToAdd) { - addPermanent(permanent, count); - } + if (addedArtifact) { + AudioManager.playAddArtifact(); + } else if (addedCreature) { + AudioManager.playSummon(); + } else if (addedPermanent) { + AudioManager.playAddPermanent(); + } - if (addedArtifact) { - AudioManager.playAddArtifact(); - } else if (addedCreature) { - AudioManager.playSummon(); - } else if (addedPermanent) { - AudioManager.playAddPermanent(); - } + removedCreature = false; - removedCreature = false; + for (Iterator> iterator = permanents.entrySet().iterator(); iterator.hasNext(); ) { + Entry entry = iterator.next(); + if (!battlefield.containsKey(entry.getKey()) || !battlefield.get(entry.getKey()).isPhasedIn()) { + removePermanent(entry.getKey(), 1); + iterator.remove(); + changed = true; + } + } - for (Iterator> iterator = permanents.entrySet().iterator(); iterator.hasNext(); ) { - Entry entry = iterator.next(); - if (!battlefield.containsKey(entry.getKey()) || !battlefield.get(entry.getKey()).isPhasedIn()) { - removePermanent(entry.getKey(), 1); - iterator.remove(); - changed = true; - } - } + if (removedCreature) { + AudioManager.playDiedCreature(); + } - if (removedCreature) { - AudioManager.playDiedCreature(); - } + if (changed) { + this.battlefield = battlefield; + sortLayout(); + } + } - if (changed) { - this.battlefield = battlefield; - sortLayout(); - } - } + public void sortLayout() { + if (battlefield == null || this.getWidth() < 1) { // Can't do layout when panel is not sized yet + return; + } - public void sortLayout() { - if (battlefield == null || this.getWidth() < 1) { // Can't do layout when panel is not sized yet - return; - } + layoutStrategy.doLayout(this, this.getWidth()); - layoutStrategy.doLayout(this, width); + this.jScrollPane.repaint(); + this.jScrollPane.revalidate(); - this.jScrollPane.repaint(); - this.jScrollPane.revalidate(); + invalidate(); + repaint(); + } - invalidate(); - repaint(); - } + private void addPermanent(PermanentView permanent, final int count) { + if (cardDimension == null) { + cardDimension = new Dimension(ClientDefaultSettings.dimensions.getFrameWidth(), ClientDefaultSettings.dimensions.getFrameHeight()); + } + final MagePermanent perm = Plugins.instance.getMagePermanent(permanent, bigCard, cardDimension, gameId, true, PreferencesDialog.getRenderMode(), true); - private void addPermanent(PermanentView permanent, final int count) { - if (cardDimension == null) { - cardDimension = new Dimension(ClientDefaultSettings.dimensions.getFrameWidth(), ClientDefaultSettings.dimensions.getFrameHeight()); - } - final MagePermanent perm = Plugins.instance.getMagePermanent(permanent, bigCard, cardDimension, gameId, true, PreferencesDialog.getRenderMode(), true); + permanents.put(permanent.getId(), perm); - permanents.put(permanent.getId(), perm); - - BattlefieldPanel.this.jPanel.add(perm, 10); - //this.jPanel.add(perm); - if (!Plugins.instance.isCardPluginLoaded()) { - moveToFront(perm); - perm.update(permanent); - } else { - moveToFront(jPanel); - Plugins.instance.onAddCard(perm, 1); + BattlefieldPanel.this.jPanel.add(perm, 10); + //this.jPanel.add(perm); + if (!Plugins.instance.isCardPluginLoaded()) { + moveToFront(perm); + perm.update(permanent); + } else { + moveToFront(jPanel); + Plugins.instance.onAddCard(perm, 1); /*Thread t = new Thread(new Runnable() { @Override public void run() { @@ -249,76 +242,76 @@ synchronized (this) { threads.add(t); }*/ - } + } - if (permanent.isArtifact()) { - addedArtifact = true; - } else if (permanent.isCreature()) { - addedCreature = true; - } else { - addedPermanent = true; - } - } + if (permanent.isArtifact()) { + addedArtifact = true; + } else if (permanent.isCreature()) { + addedCreature = true; + } else { + addedPermanent = true; + } + } - private void removePermanent(UUID permanentId, final int count) { - for (Component c : this.jPanel.getComponents()) { - final Component comp = c; - if (comp instanceof Permanent) { - if (((Permanent) comp).getPermanentId().equals(permanentId)) { - comp.setVisible(false); - this.jPanel.remove(comp); - } - } else if (comp instanceof MagePermanent) { - if (((MagePermanent) comp).getOriginal().getId().equals(permanentId)) { - Thread t = new Thread(() -> { - Plugins.instance.onRemoveCard((MagePermanent) comp, count); - comp.setVisible(false); - BattlefieldPanel.this.jPanel.remove(comp); - }); - t.start(); - } - if (((MagePermanent) comp).getOriginal().isCreature()) { - removedCreature = true; - } - } - } - } + private void removePermanent(UUID permanentId, final int count) { + for (Component c : this.jPanel.getComponents()) { + final Component comp = c; + if (comp instanceof Permanent) { + if (((Permanent) comp).getPermanentId().equals(permanentId)) { + comp.setVisible(false); + this.jPanel.remove(comp); + } + } else if (comp instanceof MagePermanent) { + if (((MagePermanent) comp).getOriginal().getId().equals(permanentId)) { + Thread t = new Thread(() -> { + Plugins.instance.onRemoveCard((MagePermanent) comp, count); + comp.setVisible(false); + BattlefieldPanel.this.jPanel.remove(comp); + }); + t.start(); + } + if (((MagePermanent) comp).getOriginal().isCreature()) { + removedCreature = true; + } + } + } + } - @Override - public boolean isOptimizedDrawingEnabled() { - return false; - } + @Override + public boolean isOptimizedDrawingEnabled() { + return false; + } - public Map getPermanents() { - return permanents; - } + public Map getPermanents() { + return permanents; + } - private void initComponents() { - setOpaque(false); + private void initComponents() { + setOpaque(false); - jPanel = new JLayeredPane(); - jPanel.setLayout(null); - jPanel.setOpaque(false); - jScrollPane = new JScrollPane(jPanel); + jPanel = new JLayeredPane(); + jPanel.setLayout(null); + jPanel.setOpaque(false); + jScrollPane = new JScrollPane(jPanel); - Border empty = new EmptyBorder(0, 0, 0, 0); - jScrollPane.setBorder(empty); - jScrollPane.setViewportBorder(empty); - jScrollPane.setOpaque(false); - jScrollPane.getViewport().setOpaque(false); + Border empty = new EmptyBorder(0, 0, 0, 0); + jScrollPane.setBorder(empty); + jScrollPane.setViewportBorder(empty); + jScrollPane.setOpaque(false); + jScrollPane.getViewport().setOpaque(false); - this.add(jScrollPane); - } + this.add(jScrollPane); + } - public JLayeredPane getMainPanel() { - return jPanel; - } + public JLayeredPane getMainPanel() { + return jPanel; + } - public Map getBattlefield() { - return battlefield; - } + public Map getBattlefield() { + return battlefield; + } - public Map getUiComponentsList() { - return uiComponentsList; - } - } + public Map getUiComponentsList() { + return uiComponentsList; + } +} diff --git a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java index 4d1c9c4ec4..aac7f62995 100644 --- a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java @@ -1,20 +1,5 @@ - - - /* - * FeedbackPanel.java - * - * Created on 23-Dec-2009, 9:54:01 PM - */ package mage.client.game; -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.io.Serializable; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import mage.client.MageFrame; import mage.client.SessionHandler; import mage.client.chat.ChatPanelBasic; @@ -22,15 +7,22 @@ import mage.client.dialog.MageDialog; import mage.client.util.GUISizeHelper; import mage.client.util.audio.AudioManager; import mage.client.util.gui.ArrowBuilder; -import static mage.constants.Constants.Option.ORIGINAL_ID; -import static mage.constants.Constants.Option.SECOND_MESSAGE; -import static mage.constants.Constants.Option.SPECIAL_BUTTON; import mage.constants.PlayerAction; import mage.constants.TurnPhase; import org.apache.log4j.Logger; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.io.Serializable; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import static mage.constants.Constants.Option.*; + /** - * * @author BetaSteward_at_googlemail.com */ public class FeedbackPanel extends javax.swing.JPanel { @@ -85,6 +77,7 @@ public class FeedbackPanel extends javax.swing.JPanel { String lblText = addAdditionalText(message, options); this.helper.setTextArea(lblText); //this.lblMessage.setText(lblText); + this.mode = mode; switch (this.mode) { case INFORM: @@ -178,12 +171,12 @@ public class FeedbackPanel extends javax.swing.JPanel { if (options.containsKey("UI.left.btn.text")) { String text = (String) options.get("UI.left.btn.text"); this.btnLeft.setText(text); - this.helper.setLeft(text, true); + this.helper.setLeft(text, !text.isEmpty()); } if (options.containsKey("UI.right.btn.text")) { String text = (String) options.get("UI.right.btn.text"); this.btnRight.setText(text); - this.helper.setRight(text, true); + this.helper.setRight(text, !text.isEmpty()); } if (options.containsKey("dialog")) { connectedDialog = (MageDialog) options.get("dialog"); diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 7078b93a2b..ce23d6c48a 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -40,6 +40,7 @@ import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.plaf.basic.BasicSplitPaneUI; import java.awt.*; import java.awt.event.*; +import java.beans.PropertyVetoException; import java.io.Serializable; import java.util.List; import java.util.*; @@ -74,6 +75,7 @@ public final class GamePanel extends javax.swing.JPanel { private final Map revealed = new HashMap<>(); private final Map lookedAt = new HashMap<>(); private final Map graveyardWindows = new HashMap<>(); + private final Map companion = new HashMap<>(); private final Map graveyards = new HashMap<>(); private final ArrayList pickTarget = new ArrayList<>(); @@ -241,6 +243,10 @@ public final class GamePanel extends javax.swing.JPanel { lookedAtDialog.cleanUp(); lookedAtDialog.removeDialog(); } + for (CardInfoWindowDialog companionDialog : companion.values()) { + companionDialog.cleanUp(); + companionDialog.removeDialog(); + } for (ShowCardsDialog pickTargetDialog : pickTarget) { pickTargetDialog.cleanUp(); pickTargetDialog.removeDialog(); @@ -275,6 +281,9 @@ public final class GamePanel extends javax.swing.JPanel { for (CardInfoWindowDialog cardInfoWindowDialog : lookedAt.values()) { cardInfoWindowDialog.changeGUISize(); } + for (CardInfoWindowDialog cardInfoWindowDialog : companion.values()) { + cardInfoWindowDialog.changeGUISize(); + } for (CardInfoWindowDialog cardInfoWindowDialog : graveyardWindows.values()) { cardInfoWindowDialog.changeGUISize(); } @@ -602,6 +611,21 @@ public final class GamePanel extends javax.swing.JPanel { this.pnlBattlefield.add(topPanel, panelC); panelC.gridy = 1; this.pnlBattlefield.add(bottomPanel, panelC); + + // TODO: combat arrows aren't visible on re-connect, must click on avatar to update correctrly + // reason: panels aren't visible/located here, so battlefieldpanel see wrong sizes + // recalc all component sizes and update permanents/arrows positions + // if you don't do it here then will catch wrong arrows drawing on re-connect (no sortLayout calls) + /* + this.validate(); + for (Map.Entry p : players.entrySet()) { + PlayerView playerView = game.getPlayers().stream().filter(view -> view.getPlayerId().equals(p.getKey())).findFirst().orElse(null); + if (playerView != null) { + p.getValue().getBattlefieldPanel().updateSize(); + p.getValue().update(null, playerView, null); + } + } + */ } public synchronized void updateGame(GameView game) { @@ -781,6 +805,7 @@ public final class GamePanel extends javax.swing.JPanel { showRevealed(game); showLookedAt(game); + showCompanion(game); if (!game.getCombat().isEmpty()) { CombatManager.instance.showCombat(game.getCombat(), gameId); } else { @@ -1085,6 +1110,9 @@ public final class GamePanel extends javax.swing.JPanel { for (CardInfoWindowDialog lookedAtDialog : lookedAt.values()) { lookedAtDialog.hideDialog(); } + for (CardInfoWindowDialog companionDialog : companion.values()) { + companionDialog.hideDialog(); + } } // Called if the game frame comes to front again @@ -1102,6 +1130,9 @@ public final class GamePanel extends javax.swing.JPanel { for (CardInfoWindowDialog lookedAtDialog : lookedAt.values()) { lookedAtDialog.show(); } + for (CardInfoWindowDialog companionDialog : companion.values()) { + companionDialog.show(); + } } public void openGraveyardWindow(String playerName) { @@ -1146,6 +1177,23 @@ public final class GamePanel extends javax.swing.JPanel { removeClosedCardInfoWindows(lookedAt); } + private void showCompanion(GameView game) { + for (RevealedView revealView : game.getCompanion()) { + handleGameInfoWindow(companion, ShowType.COMPANION, revealView.getName(), revealView.getCards()); + } + // Close the companion view if not in the game view + companion.forEach((name, companionDialog) -> { + if (game.getCompanion().stream().noneMatch(revealedView -> revealedView.getName().equals(name))) { + try { + companionDialog.setClosed(true); + } catch (PropertyVetoException e) { + logger.error("Couldn't close companion dialog", e); + } + } + }); + removeClosedCardInfoWindows(companion); + } + private void handleGameInfoWindow(Map windowMap, ShowType showType, String name, LinkedHashMap cardsView) { CardInfoWindowDialog cardInfoWindowDialog; if (!windowMap.containsKey(name)) { @@ -1160,6 +1208,7 @@ public final class GamePanel extends javax.swing.JPanel { switch (showType) { case REVEAL: case REVEAL_TOP_LIBRARY: + case COMPANION: cardInfoWindowDialog.loadCards((CardsView) cardsView, bigCard, gameId); break; case LOOKED_AT: @@ -1332,6 +1381,22 @@ public final class GamePanel extends javax.swing.JPanel { } } + // companion + for (RevealedView rev : gameView.getCompanion()) { + for (Map.Entry card : rev.getCards().entrySet()) { + if (needSelectable.contains(card.getKey())) { + card.getValue().setChoosable(true); + } + if (needChoosen.contains(card.getKey())) { + card.getValue().setSelected(true); + } + if (needPlayable.containsKey(card.getKey())) { + card.getValue().setPlayable(true); + card.getValue().setPlayableAmount(needPlayable.get(card.getKey())); + } + } + } + // looked at for (LookedAtView look : gameView.getLookedAt()) { for (Map.Entry card : look.getCards().entrySet()) { @@ -1525,7 +1590,6 @@ public final class GamePanel extends javax.swing.JPanel { @SuppressWarnings("unchecked") private void initComponents() { - abilityPicker = new mage.client.components.ability.AbilityPicker(); jSplitPane1 = new javax.swing.JSplitPane(); jSplitPane0 = new javax.swing.JSplitPane(); @@ -1560,14 +1624,16 @@ public final class GamePanel extends javax.swing.JPanel { txtHoldPriority.setToolTipText("Holding priority after the next spell cast or ability activation"); txtHoldPriority.setVisible(false); - btnToggleMacro = new KeyboundButton(KEY_CONTROL_TOGGLE_MACRO); - btnCancelSkip = new KeyboundButton(KEY_CONTROL_CANCEL_SKIP); // F3 - btnSkipToNextTurn = new KeyboundButton(KEY_CONTROL_NEXT_TURN); // F4 - btnSkipToEndTurn = new KeyboundButton(KEY_CONTROL_END_STEP); // F5 - btnSkipToNextMain = new KeyboundButton(KEY_CONTROL_MAIN_STEP); // F7 - btnSkipStack = new KeyboundButton(KEY_CONTROL_SKIP_STACK); // F10 - btnSkipToYourTurn = new KeyboundButton(KEY_CONTROL_YOUR_TURN); // F9 - btnSkipToEndStepBeforeYourTurn = new KeyboundButton(KEY_CONTROL_PRIOR_END); // F11 + boolean displayButtonText = PreferencesDialog.getCurrentTheme().isShortcutsVisibleForSkipButtons(); + + btnToggleMacro = new KeyboundButton(KEY_CONTROL_TOGGLE_MACRO, displayButtonText); + btnCancelSkip = new KeyboundButton(KEY_CONTROL_CANCEL_SKIP, displayButtonText); // F3 + btnSkipToNextTurn = new KeyboundButton(KEY_CONTROL_NEXT_TURN, displayButtonText); // F4 + btnSkipToEndTurn = new KeyboundButton(KEY_CONTROL_END_STEP, displayButtonText); // F5 + btnSkipToNextMain = new KeyboundButton(KEY_CONTROL_MAIN_STEP, displayButtonText); // F7 + btnSkipStack = new KeyboundButton(KEY_CONTROL_SKIP_STACK, displayButtonText); // F10 + btnSkipToYourTurn = new KeyboundButton(KEY_CONTROL_YOUR_TURN, displayButtonText); // F9 + btnSkipToEndStepBeforeYourTurn = new KeyboundButton(KEY_CONTROL_PRIOR_END, displayButtonText); // F11 btnConcede = new javax.swing.JButton(); btnSwitchHands = new javax.swing.JButton(); diff --git a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java index 20650740b3..0a94eab63a 100644 --- a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java @@ -1,4 +1,3 @@ - package mage.client.game; import mage.client.SessionHandler; @@ -6,7 +5,9 @@ import mage.client.components.MageTextArea; import mage.client.constants.Constants; import mage.client.dialog.PreferencesDialog; import mage.client.game.FeedbackPanel.FeedbackMode; +import mage.client.util.AppUtil; import mage.client.util.GUISizeHelper; +import mage.client.util.audio.AudioManager; import mage.constants.TurnPhase; import javax.swing.*; @@ -14,6 +15,7 @@ import javax.swing.border.EmptyBorder; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; +import java.util.List; import java.util.UUID; import static mage.client.game.FeedbackPanel.FeedbackMode.QUESTION; @@ -63,6 +65,22 @@ public class HelperPanel extends JPanel { private boolean gameNeedFeedback = false; private TurnPhase gameTurnPhase = null; + private Timer needFeedbackTimer; + + { + // start timer to inform user about needed feedback (example: inform by sound play) + needFeedbackTimer = new Timer(100, evt -> SwingUtilities.invokeLater(() -> { + needFeedbackTimer.stop(); + if (!AppUtil.isAppActive() || !AppUtil.isGameActive(this.gameId)) { + // sound notification + AudioManager.playFeedbackNeeded(); + // tray notification (baloon + icon blinking) + //MageTray.instance.displayMessage("Game needs your action."); + //MageTray.instance.blink(); + } + })); + } + public HelperPanel() { initComponents(); } @@ -296,7 +314,7 @@ public class HelperPanel extends JPanel { public void autoSizeButtonsAndFeedbackState() { // two mode: same size for small texts (flow), different size for long texts (grid) - // plus colorize feedback panel on player's priority + // also colorize feedback panel on player's priority and enable sound notification int BUTTONS_H_GAP = 15; Color ACTIVE_FEEDBACK_BACKGROUND_COLOR_MAIN = new Color(0, 0, 255, 50); @@ -308,7 +326,7 @@ public class HelperPanel extends JPanel { this.buttonGrid.setLayout(new FlowLayout(FlowLayout.CENTER, BUTTONS_H_GAP, 0)); this.buttonGrid.setPreferredSize(null); - java.util.List buttons = new ArrayList<>(); + List buttons = new ArrayList<>(); if (this.btnSpecial.isVisible()) { buttons.add(this.btnSpecial); } @@ -324,6 +342,10 @@ public class HelperPanel extends JPanel { // color panel on player's feedback waiting if (this.gameNeedFeedback) { + + // start notification sound timer + this.needFeedbackTimer.restart(); + // wait player's action switch (FEEDBACK_COLORIZING_MODE) { case Constants.BATTLEFIELD_FEEDBACK_COLORIZING_MODE_DISABLE: @@ -361,6 +383,9 @@ public class HelperPanel extends JPanel { break; } } else { + // stop notification sound timer + this.needFeedbackTimer.stop(); + // inform about other players this.mainPanel.setOpaque(false); } diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index a3e49b9c81..1f04a72fd2 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -494,7 +494,7 @@ public class PlayAreaPanel extends javax.swing.JPanel { } public final void init(PlayerView player, BigCard bigCard, UUID gameId, int priorityTime) { - this.playerPanel.init(gameId, player.getPlayerId(), bigCard, priorityTime); + this.playerPanel.init(gameId, player.getPlayerId(), player.getControlled(), bigCard, priorityTime); this.battlefieldPanel.init(gameId, bigCard); this.gameId = gameId; this.playerId = player.getPlayerId(); diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java index f0c8085cc6..f66394ce70 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java @@ -1,5 +1,13 @@ package mage.client.game; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.*; +import javax.swing.*; +import javax.swing.GroupLayout.Alignment; +import javax.swing.LayoutStyle.ComponentPlacement; +import javax.swing.border.Border; +import javax.swing.border.LineBorder; import mage.cards.decks.importer.DckDeckImporter; import mage.client.MageFrame; import mage.client.SessionHandler; @@ -8,6 +16,7 @@ import mage.client.components.HoverButton; import mage.client.components.MageRoundPane; import mage.client.components.ext.dlg.DialogManager; import mage.client.dialog.PreferencesDialog; +import mage.client.themes.ThemeType; import mage.client.util.CardsViewUtil; import mage.client.util.ImageHelper; import mage.client.util.gui.BufferedImageBuilder; @@ -15,6 +24,7 @@ import mage.client.util.gui.countryBox.CountryUtil; import mage.components.ImagePanel; import mage.components.ImagePanelStyle; import mage.constants.CardType; +import static mage.constants.Constants.*; import mage.constants.ManaType; import mage.counters.Counter; import mage.counters.CounterType; @@ -23,17 +33,6 @@ import mage.utils.timer.PriorityTimer; import mage.view.*; import org.mage.card.arcane.ManaSymbols; -import javax.swing.*; -import javax.swing.GroupLayout.Alignment; -import javax.swing.LayoutStyle.ComponentPlacement; -import javax.swing.border.Border; -import javax.swing.border.LineBorder; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.util.*; - -import static mage.constants.Constants.*; - /** * Enhanced player pane. * @@ -42,7 +41,6 @@ import static mage.constants.Constants.*; public class PlayerPanelExt extends javax.swing.JPanel { // TODO: *.form file was lost, panel must be reworks in designer - private UUID playerId; private UUID gameId; private PlayerView player; @@ -59,9 +57,9 @@ public class PlayerPanelExt extends javax.swing.JPanel { private static final Border GREEN_BORDER = new LineBorder(Color.green, 3); private static final Border RED_BORDER = new LineBorder(Color.red, 2); private static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(0, 0, 0, 0); - private final Color inactiveBackgroundColor = new Color(200, 200, 180, 200); - private final Color activeBackgroundColor = new Color(200, 255, 200, 200); - private final Color deadBackgroundColor = new Color(131, 94, 83, 200); + private final Color inactiveBackgroundColor; + private final Color activeBackgroundColor; + private final Color deadBackgroundColor; private final Color activeValueColor = new Color(244, 9, 47); private final Font fontValuesZero = this.getFont().deriveFont(Font.PLAIN); @@ -81,13 +79,18 @@ public class PlayerPanelExt extends javax.swing.JPanel { setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT)); initComponents(); setGUISize(); + + ThemeType currentTheme = PreferencesDialog.getCurrentTheme(); + inactiveBackgroundColor = currentTheme.getPlayerPanel_inactiveBackgroundColor(); + activeBackgroundColor = currentTheme.getPlayerPanel_activeBackgroundColor(); + deadBackgroundColor = currentTheme.getPlayerPanel_deadBackgroundColor(); } - public void init(UUID gameId, UUID playerId, BigCard bigCard, int priorityTime) { + public void init(UUID gameId, UUID playerId, boolean controlled, BigCard bigCard, int priorityTime) { this.gameId = gameId; this.playerId = playerId; this.bigCard = bigCard; - cheat.setVisible(SessionHandler.isTestMode()); + cheat.setVisible(SessionHandler.isTestMode() && controlled); cheat.setFocusable(false); flagName = null; if (priorityTime > 0) { @@ -612,7 +615,6 @@ public class PlayerPanelExt extends javax.swing.JPanel { manaCountLabelW.addMouseListener(manaMouseAdapter); manaLabels.put(manaCountLabelW, ManaType.WHITE);l //*/ - ///* JLabel manaCountLabelW = new JLabel(); manaCountLabelW.setToolTipText("White mana"); @@ -944,7 +946,8 @@ public class PlayerPanelExt extends javax.swing.JPanel { Set cardTypesPresent = new LinkedHashSet() { }; for (CardView card : cardsView.values()) { - Set cardTypes = card.getCardTypes(); + Set cardTypes = EnumSet.noneOf(CardType.class); + cardTypes.addAll(card.getCardTypes()); for (CardType cardType : cardTypes) { cardTypesPresent.add(cardType.toString()); } diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java index cf5a65eee0..50665578df 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java @@ -242,7 +242,7 @@ public class MageActionCallback implements ActionCallback { if (this.startedDragging && prevCardPanel != null && card != null) { for (Component component : card.getCardArea().getComponents()) { if (component instanceof CardPanel) { - if (cardPanels.contains(component)) { + if (cardPanels.contains((CardPanel) component)) { component.setLocation(component.getLocation().x, component.getLocation().y - GO_DOWN_ON_DRAG_Y_OFFSET); } } @@ -316,7 +316,7 @@ public class MageActionCallback implements ActionCallback { for (Component component : container.getComponents()) { if (component instanceof CardPanel) { if (!component.equals(card)) { - if (!cardPanels.contains(component)) { + if (!cardPanels.contains((CardPanel) component)) { component.setLocation(component.getLocation().x, component.getLocation().y + GO_DOWN_ON_DRAG_Y_OFFSET); } cardPanels.add((CardPanel) component); @@ -539,7 +539,7 @@ public class MageActionCallback implements ActionCallback { if (enlargedWindowState == EnlargedWindowState.CLOSED) { return; } - + MageComponents mageComponentCardPreviewContainer; MageComponents mageComponentCardPreviewPane; if (cardView.isToRotate()) { @@ -568,7 +568,7 @@ public class MageActionCallback implements ActionCallback { location.translate(-parentPoint.x, -parentPoint.y); popupContainer.setLocation(location); popupContainer.setVisible(true); - + MageCard mageCard = (MageCard) transferData.getComponent(); Image image = null; switch (enlargeMode) { @@ -593,13 +593,12 @@ public class MageActionCallback implements ActionCallback { image = mageCard.getImage(); } // shows the card in the popup Container - BigCard bigCard = (BigCard) cardPreviewPane; - displayCardInfo(mageCard, image, bigCard); - + displayCardInfo(mageCard, image, (BigCard) cardPreviewPane); + } else { LOGGER.warn("No Card preview Pane in Mage Frame defined. Card: " + cardView.getName()); } - + } catch (Exception e) { LOGGER.warn("Problem dring display of enlarged card", e); } diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index d93e9f67a4..ae6e378bb5 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -429,7 +429,7 @@ public class CallbackClientImpl implements CallbackClient { .append(" - Undo F4/F5/F7/F9/F11") .append("
") .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_SWITCH_CHAT))) - .append(" - Switth in/out to chat text field") + .append(" - Switch in/out to chat text field") /* .append("
") .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_TOGGLE_MACRO))) diff --git a/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java index 6645b4ab16..eae1b08bb8 100644 --- a/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java @@ -1,10 +1,3 @@ - - - /* - * NewPlayerPanel.java - * - * Created on 15-Dec-2009, 10:09:46 PM - */ package mage.client.table; import java.io.File; @@ -45,7 +38,7 @@ public class NewPlayerPanel extends javax.swing.JPanel { this.txtPlayerName.setEditable(false); this.txtPlayerName.setEnabled(false); } - + protected void playerLoadDeck() { String lastFolder = MageFrame.getPreferences().get("lastDeckFolder", ""); if (!lastFolder.isEmpty()) { diff --git a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java index ae68ccddd6..3a7a6dd8f3 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java @@ -126,7 +126,9 @@ public class TablePlayerPanel extends javax.swing.JPanel { private void cbPlayerTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPlayerTypeActionPerformed if (getPlayerType() != PlayerType.HUMAN) { this.newPlayerPanel.setVisible(true); - } else { + this.newPlayerPanel.setPlayerName(ClientDefaultSettings.computerName + " " + this.lblPlayerNum.getText().charAt(this.lblPlayerNum.getText().length() - 1)); + } + else { this.newPlayerPanel.setVisible(false); } this.revalidate(); diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.form b/Mage.Client/src/main/java/mage/client/table/TablesPanel.form index 14518523fd..3a017d100b 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.form +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.form @@ -44,10 +44,14 @@ - + + + + + - + @@ -63,7 +67,13 @@ - + + + + + + + @@ -367,7 +377,7 @@
- + @@ -567,6 +577,14 @@ + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index e7a260cd91..c8af0daf78 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -582,6 +582,7 @@ public class TablesPanel extends javax.swing.JPanel { Dimension newDimension = new Dimension((int) jPanelBottom.getPreferredSize().getWidth(), GUISizeHelper.menuFont.getSize() + 28); jPanelBottom.setMinimumSize(newDimension); jPanelBottom.setPreferredSize(newDimension); + buttonWhatsNew.setFont(GUISizeHelper.menuFont); buttonNextMessage.setFont(GUISizeHelper.menuFont); labelMessageHeader.setFont(new Font(GUISizeHelper.menuFont.getName(), Font.BOLD, GUISizeHelper.menuFont.getSize())); labelMessageText.setFont(GUISizeHelper.menuFont); @@ -693,6 +694,7 @@ public class TablesPanel extends javax.swing.JPanel { if (SessionHandler.getSession() != null) { btnQuickStartDuel.setVisible(SessionHandler.isTestMode()); btnQuickStartCommander.setVisible(SessionHandler.isTestMode()); + btnQuickStartMCTS.setVisible(SessionHandler.isTestMode()); gameChooser.init(); chatRoomId = SessionHandler.getRoomChatId(roomId).orElse(null); } @@ -791,7 +793,7 @@ public class TablesPanel extends javax.swing.JPanel { typeFilterList.add(RowFilter.regexFilter("Constructed", TablesTableModel.COLUMN_GAME_TYPE)); } if (btnTypeTourneyLimited.isSelected()) { - typeFilterList.add(RowFilter.regexFilter("Booster|Sealed", TablesTableModel.COLUMN_GAME_TYPE)); + typeFilterList.add(RowFilter.regexFilter("Booster|Sealed|Jumpstart", TablesTableModel.COLUMN_GAME_TYPE)); } // format @@ -987,6 +989,7 @@ public class TablesPanel extends javax.swing.JPanel { btnPassword = new javax.swing.JToggleButton(); btnQuickStartDuel = new javax.swing.JButton(); btnQuickStartCommander = new javax.swing.JButton(); + btnQuickStartMCTS = new javax.swing.JButton(); jSplitPane1 = new javax.swing.JSplitPane(); jPanelTables = new javax.swing.JPanel(); jSplitPaneTables = new javax.swing.JSplitPane(); @@ -1456,44 +1459,58 @@ public class TablesPanel extends javax.swing.JPanel { } }); + btnQuickStartMCTS.setText("Quick start MCTS"); + btnQuickStartMCTS.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnQuickStartMCTSActionPerformed(evt); + } + }); + javax.swing.GroupLayout jPanelTopLayout = new javax.swing.GroupLayout(jPanelTop); jPanelTop.setLayout(jPanelTopLayout); jPanelTopLayout.setHorizontalGroup( - jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanelTopLayout.createSequentialGroup() - .addContainerGap() - .addComponent(btnNewTable) - .addGap(6, 6, 6) - .addComponent(btnNewTournament) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(filterBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(btnQuickStartDuel) - .addComponent(btnQuickStartCommander)) - .addContainerGap(667, Short.MAX_VALUE)) + jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanelTopLayout.createSequentialGroup() + .addContainerGap() + .addComponent(btnNewTable) + .addGap(6, 6, 6) + .addComponent(btnNewTournament) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(filterBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanelTopLayout.createSequentialGroup() + .addComponent(btnQuickStartDuel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnQuickStartMCTS)) + .addComponent(btnQuickStartCommander)) + .addContainerGap(540, Short.MAX_VALUE)) ); jPanelTopLayout.setVerticalGroup( - jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanelTopLayout.createSequentialGroup() - .addContainerGap() + jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanelTopLayout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(btnNewTable) + .addComponent(btnNewTournament)) + .addGroup(jPanelTopLayout.createSequentialGroup() + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(filterBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jPanelTopLayout.createSequentialGroup() .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(btnNewTable) - .addComponent(btnNewTournament)) - .addGroup(jPanelTopLayout.createSequentialGroup() - .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(filterBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(btnQuickStartDuel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(jPanelTopLayout.createSequentialGroup() - .addComponent(btnQuickStartCommander) - .addGap(0, 0, Short.MAX_VALUE))))) - .addContainerGap()) + .addComponent(btnQuickStartDuel) + .addComponent(btnQuickStartMCTS)) + .addGap(0, 0, Short.MAX_VALUE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jPanelTopLayout.createSequentialGroup() + .addComponent(btnQuickStartCommander) + .addGap(0, 0, Short.MAX_VALUE))))) + .addContainerGap()) ); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -1530,12 +1547,12 @@ public class TablesPanel extends javax.swing.JPanel { javax.swing.GroupLayout jPanelTablesLayout = new javax.swing.GroupLayout(jPanelTables); jPanelTables.setLayout(jPanelTablesLayout); jPanelTablesLayout.setHorizontalGroup( - jPanelTablesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jSplitPaneTables, javax.swing.GroupLayout.DEFAULT_SIZE, 23, Short.MAX_VALUE) + jPanelTablesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jSplitPaneTables, javax.swing.GroupLayout.DEFAULT_SIZE, 23, Short.MAX_VALUE) ); jPanelTablesLayout.setVerticalGroup( - jPanelTablesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jSplitPaneTables, javax.swing.GroupLayout.DEFAULT_SIZE, 672, Short.MAX_VALUE) + jPanelTablesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jSplitPaneTables, javax.swing.GroupLayout.DEFAULT_SIZE, 672, Short.MAX_VALUE) ); jSplitPane1.setLeftComponent(jPanelTables); @@ -1553,7 +1570,7 @@ public class TablesPanel extends javax.swing.JPanel { jPanelBottom.setPreferredSize(new java.awt.Dimension(516, 37)); jPanelBottom.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT)); - buttonWhatsNew.setText("Show that's new"); + buttonWhatsNew.setText("Show what's new"); buttonWhatsNew.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); buttonWhatsNew.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); buttonWhatsNew.setOpaque(false); @@ -1596,7 +1613,7 @@ public class TablesPanel extends javax.swing.JPanel { newTournamentDialog.showDialog(roomId); }//GEN-LAST:event_btnNewTournamentActionPerformed - private void createTestGame(String gameName, String gameType) { + private void createTestGame(String gameName, String gameType, boolean useMonteCarloAI) { TableView table; try { String testDeckFile = "test.dck"; @@ -1612,9 +1629,10 @@ public class TablesPanel extends javax.swing.JPanel { } DeckCardLists testDeck = DeckImporter.importDeckFromFile(testDeckFile); + PlayerType aiType = useMonteCarloAI ? PlayerType.COMPUTER_MONTE_CARLO : PlayerType.COMPUTER_MAD; MatchOptions options = new MatchOptions(gameName, gameType, false, 2); options.getPlayerTypes().add(PlayerType.HUMAN); - options.getPlayerTypes().add(PlayerType.COMPUTER_MAD); + options.getPlayerTypes().add(aiType); options.setDeckType("Limited"); options.setAttackOption(MultiplayerAttackOption.LEFT); options.setRange(RangeOfInfluence.ALL); @@ -1630,7 +1648,7 @@ public class TablesPanel extends javax.swing.JPanel { table = SessionHandler.createTable(roomId, options); SessionHandler.joinTable(roomId, table.getTableId(), "Human", PlayerType.HUMAN, 1, testDeck, ""); - SessionHandler.joinTable(roomId, table.getTableId(), "Computer", PlayerType.COMPUTER_MAD, 5, testDeck, ""); + SessionHandler.joinTable(roomId, table.getTableId(), "Computer", aiType, 5, testDeck, ""); SessionHandler.startMatch(roomId, table.getTableId()); } catch (HeadlessException ex) { handleError(ex); @@ -1638,7 +1656,7 @@ public class TablesPanel extends javax.swing.JPanel { } private void btnQuickStartDuelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartDuelActionPerformed - createTestGame("Test duel", "Two Player Duel"); + createTestGame("Test duel", "Two Player Duel", false); }//GEN-LAST:event_btnQuickStartDuelActionPerformed private void btnNewTableActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNewTableActionPerformed @@ -1677,9 +1695,13 @@ public class TablesPanel extends javax.swing.JPanel { }//GEN-LAST:event_buttonWhatsNewActionPerformed private void btnQuickStartCommanderActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartCommanderActionPerformed - createTestGame("Test commander", "Commander Two Player Duel"); + createTestGame("Test commander", "Commander Two Player Duel", false); }//GEN-LAST:event_btnQuickStartCommanderActionPerformed + private void btnQuickStartMCTSActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartMCTSActionPerformed + createTestGame("Test Monte Carlo AI", "Two Player Duel", true); + }//GEN-LAST:event_btnQuickStartMCTSActionPerformed + private void handleError(Exception ex) { LOGGER.fatal("Error loading deck: ", ex); JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Error loading deck.", "Error", JOptionPane.ERROR_MESSAGE); @@ -1691,9 +1713,9 @@ public class TablesPanel extends javax.swing.JPanel { private javax.swing.JToggleButton btnFormatLegacy; private javax.swing.JToggleButton btnFormatLimited; private javax.swing.JToggleButton btnFormatModern; - private javax.swing.JToggleButton btnFormatPioneer; private javax.swing.JToggleButton btnFormatOathbreaker; private javax.swing.JToggleButton btnFormatOther; + private javax.swing.JToggleButton btnFormatPioneer; private javax.swing.JToggleButton btnFormatPremodern; private javax.swing.JToggleButton btnFormatStandard; private javax.swing.JToggleButton btnFormatTinyLeader; @@ -1704,6 +1726,7 @@ public class TablesPanel extends javax.swing.JPanel { private javax.swing.JToggleButton btnPassword; private javax.swing.JButton btnQuickStartCommander; private javax.swing.JButton btnQuickStartDuel; + private javax.swing.JButton btnQuickStartMCTS; private javax.swing.JToggleButton btnRated; private javax.swing.JToggleButton btnSkillBeginner; private javax.swing.JToggleButton btnSkillCasual; diff --git a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java index 33e462727d..a7e2d7b4e2 100644 --- a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java @@ -10,6 +10,7 @@ package mage.client.table; import mage.cards.decks.DeckCardLists; import mage.client.SessionHandler; +import mage.client.util.ClientDefaultSettings; import mage.players.PlayerType; import javax.swing.*; @@ -136,9 +137,9 @@ public class TournamentPlayerPanel extends javax.swing.JPanel { private void cbPlayerTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPlayerTypeActionPerformed if (this.cbPlayerType.getSelectedItem() != PlayerType.HUMAN) { this.pnlPlayerName.setVisible(true); - if (this.txtPlayerName.getText().isEmpty()) { - this.txtPlayerName.setText("Computer " + this.lblPlayerNum.getText()); - } + this.txtPlayerName.setText(ClientDefaultSettings.computerName + " " + this.lblPlayerNum.getText().charAt(this.lblPlayerNum.getText().length() - 1)); + this.txtPlayerName.setEditable(false); + this.txtPlayerName.setEnabled(false); } else { this.pnlPlayerName.setVisible(false); } diff --git a/Mage.Client/src/main/java/mage/client/themes/ThemeType.java b/Mage.Client/src/main/java/mage/client/themes/ThemeType.java new file mode 100644 index 0000000000..56b57493bd --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/themes/ThemeType.java @@ -0,0 +1,270 @@ +package mage.client.themes; + +import java.awt.*; + +public enum ThemeType { + // https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/_nimbusDefaults.html + DEFAULT("Default", + "", + true, + false, + true, + true, + true, + true, + true, + new Color(169, 176, 190), // nimbusBlueGrey + new Color(214, 217, 223), // control + new Color(255, 255, 255), // nimbusLightBackground + new Color(242, 242, 189), // info + new Color(51, 98, 140), // nimbusBase + null, // mageToolbar + new Color(200, 200, 180, 200), // playerPanel_inactiveBackgroundColor + new Color(200, 255, 200, 200), // playerPanel_activeBackgroundColor + new Color(131, 94, 83, 200) // playerPanel_deadBackgroundColor + ), + GREY("Grey", + "grey-theme/", + false, + false, + false, + false, + false, + true, + true, + new Color(158, 158, 158), // nimbusBlueGrey + new Color(212, 212, 212), // control + new Color(215, 215, 215), // nimbusLightBackground + new Color(189, 189, 164), // info + new Color(102, 102, 102), // nimbusBase + null, // mageToolbar + new Color(172, 172, 172, 200), // playerPanel_inactiveBackgroundColor + new Color(180, 234, 180, 200), // playerPanel_activeBackgroundColor + new Color(99, 99, 99, 200) // playerPanel_deadBackgroundColor + ), + SUNSET_VAPORWAVE("Vaporwave Sunset", + "16bit-theme/", + true, + true, + false, + true, + true, + true, + false, + new Color(246, 136, 158), + new Color(243, 233, 164), + new Color(204, 236, 201), + new Color(117, 174, 238), + new Color(106, 0, 255), + new Color(192, 166, 232), + new Color(243, 233, 164), + new Color(204, 236, 201), + new Color(106, 0, 255) + ), + COFFEE("Coffee", + "coffee-theme/", + true, + true, + true, + true, + true, + true, + false, + new Color(219, 193, 172), // nimbusBlueGrey + new Color(182, 157, 135), // control + new Color(219, 193, 172), // nimbusLightBackground + new Color(219, 197, 182), // info + new Color(97, 27, 0), // nimbusBase + new Color(219, 193, 172), // mageToolbar + new Color(219, 193, 172), + new Color(204, 236, 201), + new Color(99, 72, 50, 255) + ), + ISLAND("Island", + "island-theme/", + true, + true, + false, + true, + true, + true, + false, + new Color(172, 197, 219), // nimbusBlueGrey + new Color(135, 158, 182), // control + new Color(172, 197, 219), // nimbusLightBackground + new Color(182, 200, 219), // info + new Color(0, 78, 97), // nimbusBase + new Color(172, 195, 219), // mageToolbar + new Color(172, 195, 219), + new Color(204, 236, 201), + new Color(50, 68, 99, 255) + ); + + private final String name; + private final String path; + private final boolean hasBackground; + private final boolean hasLoginBackground; + private final boolean hasBattleBackground; + private final boolean hasSkipButtons; + private final boolean hasPhaseIcons; + private final boolean hasWinLossImages; + private final boolean shortcutsVisibleForSkipButtons; // Whether or not to display skip button shortcuts + private final Color nimbusBlueGrey; // buttons, scrollbar background, disabled inputs + private final Color control; // window bg + private final Color nimbusLightBackground; // inputs, table rows + private final Color info;// tooltips + private final Color nimbusBase;// title bars, scrollbar foreground + private final Color mageToolbar; + private final Color playerPanel_inactiveBackgroundColor; + private final Color playerPanel_activeBackgroundColor; + private final Color playerPanel_deadBackgroundColor; + + ThemeType(String name, + String path, + boolean hasBackground, + boolean hasLoginBackground, + boolean hasBattleBackground, + boolean hasSkipButtons, + boolean hasPhaseIcons, + boolean hasWinLossImages, + boolean shortcutsVisibleForSkipButtons, + Color nimbusBlueGrey, + Color control, + Color nimbusLightBackground, + Color info, + Color nimbusBase, + Color mageToolbar, + Color playerPanel_inactiveBackgroundColor, + Color playerPanel_activeBackgroundColor, + Color playerPanel_deadBackgroundColor + ) { + this.name = name; + this.path = path; + this.hasBackground = hasBackground; + this.hasLoginBackground = hasLoginBackground; + this.hasBattleBackground = hasBattleBackground; + this.hasSkipButtons = hasSkipButtons; + this.hasPhaseIcons = hasPhaseIcons; + this.hasWinLossImages = hasWinLossImages; + this.shortcutsVisibleForSkipButtons = shortcutsVisibleForSkipButtons; + this.nimbusBlueGrey = nimbusBlueGrey; + this.control = control; + this.nimbusLightBackground = nimbusLightBackground; + this.info = info; + this.nimbusBase = nimbusBase; + this.mageToolbar = mageToolbar; + this.playerPanel_activeBackgroundColor = playerPanel_activeBackgroundColor; + this.playerPanel_deadBackgroundColor = playerPanel_deadBackgroundColor; + this.playerPanel_inactiveBackgroundColor = playerPanel_inactiveBackgroundColor; + } + + @Override + public String toString() { + return name; + } + + public static ThemeType valueByName(String value) { + for (ThemeType themeType : values()) { + if (themeType.name.equals(value)) { + return themeType; + } + } + return DEFAULT; + } + + public String getName() { + return name; + } + + public boolean isShortcutsVisibleForSkipButtons() { + return shortcutsVisibleForSkipButtons; + } + + public Color getNimbusBlueGrey() { + return nimbusBlueGrey; + } + + public Color getControl() { + return control; + } + + public Color getNimbusLightBackground() { + return nimbusLightBackground; + } + + public Color getInfo() { + return info; + } + + public Color getNimbusBase() { + return nimbusBase; + } + + public Color getMageToolbar() { + return mageToolbar; + } + + public Color getPlayerPanel_inactiveBackgroundColor() { + return playerPanel_inactiveBackgroundColor; + } + + public Color getPlayerPanel_activeBackgroundColor() { + return playerPanel_activeBackgroundColor; + } + + public Color getPlayerPanel_deadBackgroundColor() { + return playerPanel_deadBackgroundColor; + } + + private String getImagePath(String imageType, String name) { + return "/" + imageType + "/" + path + name; + } + + public String getButtonPath(String name) { + if (hasSkipButtons) { + return getImagePath("buttons", name); + } else { + return "/buttons/" + name; + } + } + + public String getPhasePath(String name) { + if (hasPhaseIcons) { + return getImagePath("phases", name); + } else { + return "/phases/" + name; + } + } + + public String getWinlossPath(String name) { + if (hasWinLossImages) { + return getImagePath("winloss", name); + } else { + return "/winloss/" + name; + } + } + + public String getBackgroundPath() { + if (hasBackground) { + return getImagePath("background", "background.png"); + } else { + return "/background/background.png"; + } + } + + public String getLoginBackgroundPath() { + if (hasLoginBackground) { + return getImagePath("background", "login-background.png"); + } else { + return getBackgroundPath(); + } + } + + public String getBattleBackgroundPath() { + if (hasBattleBackground) { + return getImagePath("background", "battle-background.png"); + } else { + return getBackgroundPath(); + } + } +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/AppUtil.java b/Mage.Client/src/main/java/mage/client/util/AppUtil.java new file mode 100644 index 0000000000..951341a22c --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/AppUtil.java @@ -0,0 +1,28 @@ +package mage.client.util; + +import mage.client.MageFrame; + +import java.util.UUID; + +/** + * @author JayDi85 + */ +public class AppUtil { + + /** + * Application is active in operation system (got user focus) + */ + public static boolean isAppActive() { + return MageFrame.getInstance().isActive(); + } + + /** + * Current active panel is game panel (e.g. the user sees the checking game) + * + * @param gameId game to check + * @return + */ + public static boolean isGameActive(UUID gameId) { + return MageFrame.getInstance().isGameFrameActive(gameId); + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewCardTypeComparator.java b/Mage.Client/src/main/java/mage/client/util/CardViewCardTypeComparator.java index ccec924765..70b7bc1ae3 100644 --- a/Mage.Client/src/main/java/mage/client/util/CardViewCardTypeComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/CardViewCardTypeComparator.java @@ -1,22 +1,22 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.client.util; - -import java.util.Comparator; -import mage.view.CardView; - -/** - * - * @author LevelX2 - */ -public class CardViewCardTypeComparator implements Comparator { - - @Override - public int compare(CardView o1, CardView o2) { - return o1.getCardTypes().toString().compareTo(o2.getCardTypes().toString()); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.client.util; + +import java.util.Comparator; +import mage.view.CardView; + +/** + * + * @author LevelX2 + */ +public class CardViewCardTypeComparator implements Comparator { + + @Override + public int compare(CardView o1, CardView o2) { + return o1.getCardTypes().toString().compareTo(o2.getCardTypes().toString()); + } + +} diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java b/Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java index 82f0edeb50..77bd9ff23f 100644 --- a/Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java @@ -15,16 +15,10 @@ public class CardViewRarityComparator implements Comparator { Rarity r1 = o1.getRarity(); Rarity r2 = o2.getRarity(); - int val = Integer.compare( + return Integer.compare( r1 == null ? 0 : r1.getSorting(), r2 == null ? 0 : r2.getSorting() ); - - if (val == 0) { - return o1.getName().compareTo(o2.getName()); - } else { - return val; - } } } \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/ClientDefaultSettings.java b/Mage.Client/src/main/java/mage/client/util/ClientDefaultSettings.java index 7d4d529516..528e2b85b4 100644 --- a/Mage.Client/src/main/java/mage/client/util/ClientDefaultSettings.java +++ b/Mage.Client/src/main/java/mage/client/util/ClientDefaultSettings.java @@ -28,7 +28,7 @@ public final class ClientDefaultSettings { handScalingFactor = 1.3; deckPath = ""; otherPlayerIndex = "1"; // combobox default, example: 0: Human, 1: Computer - mad, 2: Computer - Draft Bot - computerName = "computer"; + computerName = "Computer"; dimensions = new CardDimensions(cardScalingFactor); dimensionsEnlarged = new CardDimensions(cardScalingFactorEnlarged); } diff --git a/Mage.Client/src/main/java/mage/client/util/audio/AudioManager.java b/Mage.Client/src/main/java/mage/client/util/audio/AudioManager.java index 5e310ddbf1..a303282d47 100644 --- a/Mage.Client/src/main/java/mage/client/util/audio/AudioManager.java +++ b/Mage.Client/src/main/java/mage/client/util/audio/AudioManager.java @@ -47,6 +47,7 @@ public class AudioManager { private MageClip playerQuitTournament = null; private MageClip playerWon = null; private MageClip playerLost = null; + private MageClip feedbackNeeded = null; /** * AudioManager singleton. */ @@ -284,6 +285,13 @@ public class AudioManager { } checkAndPlayClip(audioManager.playerWon); } + + public static void playFeedbackNeeded() { + if (audioManager.feedbackNeeded == null) { + audioManager.feedbackNeeded = new MageClip(Constants.BASE_SOUND_PATH + "FeedbackNeeded.wav", AudioGroup.GameSounds); + } + checkAndPlayClip(audioManager.feedbackNeeded); + } private static boolean audioGroupEnabled(AudioGroup audioGroup) { switch (audioGroup) { diff --git a/Mage.Client/src/main/java/mage/client/util/gui/Arrow.java b/Mage.Client/src/main/java/mage/client/util/gui/Arrow.java index 6dfcc49c23..6d0d8b54ba 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/Arrow.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/Arrow.java @@ -1,18 +1,11 @@ package mage.client.util.gui; -import java.awt.AlphaComposite; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Composite; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.RenderingHints; +import javax.swing.*; +import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.awt.geom.Area; import java.awt.geom.GeneralPath; -import javax.swing.JFrame; -import javax.swing.JPanel; public class Arrow extends JPanel { @@ -26,6 +19,7 @@ public class Arrow extends JPanel { private float headSize = 17; private Composite composite; private Color color = Color.red; + private Color strokeColor = Color.black; public Arrow() { setOpaque(false); @@ -53,8 +47,11 @@ public class Arrow extends JPanel { g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setComposite(composite); g2d.setColor(this.color); + //debug stroke draw + //float[] dash2 = {10, 5}; + //g2d.setStroke(new BasicStroke(3.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10.0f, dash2, 0)); g2d.fill(arrow); - g2d.setColor(Color.BLACK); + g2d.setColor(this.strokeColor); g2d.draw(arrow); } diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index 6717d05451..eea8d87ac3 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -14,8 +14,8 @@ import org.mage.card.arcane.UI; import javax.swing.*; import java.awt.*; -import java.util.ArrayList; -import java.util.Locale; +import java.util.List; +import java.util.*; import static mage.client.dialog.PreferencesDialog.KEY_MAGE_PANEL_LAST_SIZE; @@ -38,7 +38,7 @@ public final class GuiDisplayUtil { this.basicTextLength = basicTextLength; } - public java.util.List getLines() { + public List getLines() { return lines; } @@ -343,6 +343,9 @@ public final class GuiDisplayUtil { } buffer.append(""); + // split card rules shows up by parts, so no needs to duplicate it later (only dynamic abilities must be shown) + Set duplicatedRules = new HashSet<>(); + StringBuilder rule = new StringBuilder("
"); if (card.isSplitCard()) { rule.append(""); @@ -355,7 +358,9 @@ public final class GuiDisplayUtil { rule.append("
"); for (String ruling : card.getLeftSplitRules()) { if (ruling != null && !ruling.replace(".", "").trim().isEmpty()) { - rule.append("

").append(ruling).append("

"); + // split names must be replaced + duplicatedRules.add(ruling); + rule.append("

").append(replaceNamesInRule(ruling, card.getLeftSplitName())).append("

"); } } rule.append(""); @@ -368,13 +373,18 @@ public final class GuiDisplayUtil { rule.append("
"); for (String ruling : card.getRightSplitRules()) { if (ruling != null && !ruling.replace(".", "").trim().isEmpty()) { - rule.append("

").append(ruling).append("

"); + // split names must be replaced + duplicatedRules.add(ruling); + rule.append("

").append(replaceNamesInRule(ruling, card.getRightSplitName())).append("

"); } } } if (!textLines.getLines().isEmpty()) { for (String textLine : textLines.getLines()) { if (textLine != null && !textLine.replace(".", "").trim().isEmpty()) { + if (duplicatedRules.contains(textLine)) { + continue; + } rule.append("

").append(textLine).append("

"); } } @@ -382,8 +392,7 @@ public final class GuiDisplayUtil { String legal = rule.toString(); if (!legal.isEmpty()) { - legal = legal.replaceAll("\\{this\\}", card.getName().isEmpty() ? "this" : card.getName()); - legal = legal.replaceAll("\\{source\\}", card.getName().isEmpty() ? "this" : card.getName()); + legal = replaceNamesInRule(legal, card.getDisplayName()); // must show real display name (e.g. split part, not original card) buffer.append(ManaSymbols.replaceSymbolsWithHTML(legal, ManaSymbols.Type.TOOLTIP)); } @@ -396,6 +405,12 @@ public final class GuiDisplayUtil { return buffer; } + private static String replaceNamesInRule(String rule, String cardName) { + String res = rule.replaceAll("\\{this\\}", cardName.isEmpty() ? "this" : cardName); + res = res.replaceAll("\\{source\\}", cardName.isEmpty() ? "this" : cardName); + return res; + } + private static String getResourcePath(String image) { return GuiDisplayUtil.class.getClassLoader().getResource(image).toString(); } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java index e4c84b3ac3..f298ffb70d 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java @@ -154,6 +154,7 @@ public class CardPanelRenderImpl extends CardPanel { sb.append(this.view.getToughness()); sb.append(this.view.getLoyalty()); sb.append(this.view.getColor().toString()); + sb.append(this.view.getType()); sb.append(this.view.getExpansionSetCode()); for (CardType type : this.view.getCardTypes()) { sb.append((char) type.ordinal()); @@ -227,7 +228,7 @@ public class CardPanelRenderImpl extends CardPanel { private BufferedImage faceArtImage; // Factory to generate card appropriate views - private CardRendererFactory cardRendererFactory = new CardRendererFactory(); + private final CardRendererFactory cardRendererFactory = new CardRendererFactory(); // The rendered card image, with or without the art image loaded yet // = null while invalid diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java index 048d4d42db..bdb099bac9 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java @@ -140,13 +140,30 @@ public abstract class CardRenderer { break; } + // workaround to use real split card names + String realCardName = cardView.getDisplayName(); + if (cardView.isSplitCard()) { + for (String partRule : cardView.getLeftSplitRules()) { + if (partRule.equals(rule)) { + realCardName = cardView.getLeftSplitName(); + break; + } + } + for (String partRule : cardView.getRightSplitRules()) { + if (partRule.equals(rule)) { + realCardName = cardView.getRightSplitName(); + break; + } + } + } + // Kill reminder text if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_REMINDER_TEXT, "false").equals("false")) { rule = CardRendererUtils.killReminderText(rule).trim(); } if (!rule.isEmpty()) { - TextboxRule tbRule = TextboxRuleParser.parse(cardView, rule); + TextboxRule tbRule = TextboxRuleParser.parse(cardView, rule, realCardName); if (tbRule.type == TextboxRuleType.SIMPLE_KEYWORD) { keywords.add(tbRule); } else if (tbRule.text.isEmpty()) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index 42caffce72..632f9be42f 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -1,256 +1,256 @@ -package org.mage.card.arcane; - -import mage.MageInt; -import mage.view.CardView; -import mage.view.PermanentView; - -import java.awt.*; -import java.awt.image.BufferedImage; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.StringTokenizer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @author stravant@gmail.com, JayDi85 - *

- * Various static utilities for use in the card renderer - */ -public final class CardRendererUtils { - - // text colors for PT (mtgo and image render modes) - private static final Color CARD_TEXT_COLOR_GOOD_LIGHT = new Color(182, 235, 168); - private static final Color CARD_TEXT_COLOR_GOOD_DARK = new Color(52, 135, 88); - private static final Color CARD_TEXT_COLOR_BAD_LIGHT = new Color(234, 153, 153); - private static final Color CARD_TEXT_COLOR_BAD_DARK = new Color(200, 33, 33); - - /** - * Convert an abstract image, whose underlying implementation may or may not - * be a BufferedImage into a BufferedImage by creating one and coping the - * contents if it is not, and simply up-casting if it is. - * - * @param img The image to convert - * @return The converted image - */ - public static BufferedImage toBufferedImage(Image img) { - // Null? No conversion to do - if (img == null) { - return null; - } - - // Already a buffered image? - if (img instanceof BufferedImage) { - return (BufferedImage) img; - } - - // Create a buffered image with transparency - BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); - - // Draw the image on to the buffered image - Graphics2D bGr = bimage.createGraphics(); - bGr.drawImage(img, 0, 0, null); - bGr.dispose(); - - // Return the buffered image - return bimage; - } - - public static Color abitbrighter(Color c) { - int r = c.getRed(); - int g = c.getGreen(); - int b = c.getBlue(); - int alpha = c.getAlpha(); - - int plus_r = (255 - r) / 2; - int plus_g = (255 - g) / 2; - int plus_b = (255 - b) / 2; - - return new Color(r + plus_r, - g + plus_g, - b + plus_b, - alpha); - } - - public static Color abitdarker(Color c) { - int r = c.getRed(); - int g = c.getGreen(); - int b = c.getBlue(); - int alpha = c.getAlpha(); - - int plus_r = Math.min(255 - r, r) / 2; - int plus_g = Math.min(255 - g, g) / 2; - int plus_b = Math.min(255 - b, b) / 2; - - return new Color(r - plus_r, - g - plus_g, - b - plus_b, - alpha); - } - - // Draw a rounded box with a 2-pixel border - // Used on various card parts. - public static void drawRoundedBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Paint fill) { - g.setColor(new Color(0, 0, 0, 150)); - g.drawOval(x - 1, y - 1, bevel * 2, h); - g.setPaint(border); - g.drawOval(x, y, bevel * 2 - 1, h - 1); - g.drawOval(x + w - bevel * 2, y, bevel * 2 - 1, h - 1); - g.drawOval(x + 1, y + 1, bevel * 2 - 3, h - 3); - g.drawOval(x + 1 + w - bevel * 2, y + 1, bevel * 2 - 3, h - 3); - g.drawRect(x + bevel, y, w - 2 * bevel, h - 1); - g.drawRect(x + 1 + bevel, y + 1, w - 2 * bevel - 2, h - 3); - g.setPaint(fill); - g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4); - g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4); - g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4); - g.setPaint(fill); - g.setColor(abitbrighter(g.getColor())); - g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1); - g.setPaint(fill); - g.setColor(abitdarker(g.getColor())); - g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); - } - - public static void drawZendikarLandBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Paint fill) { - g.setColor(new Color(0, 0, 0, 150)); - - g.drawOval(x - 1, y, bevel * 2, h); - g.setPaint(border); - g.drawOval(x, y, bevel * 2 - 1, h - 1); - g.drawOval(x + w - bevel * 2, y, bevel * 2 - 1, h - 1); - g.drawOval(x + 1, y + 1, bevel * 2 - 3, h - 3); - g.drawOval(x + 1 + w - bevel * 2, y + 1, bevel * 2 - 3, h - 3); - - // The big circle in the middle.. (diameter=2+1/4 of height) - 3/4 above line, 1/2 below 0.75 + .5 + 1= 2.25 = 9/4 - g.drawOval(x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4); - - g.drawRect(x + bevel, y, w - 2 * bevel, h - 1); - g.drawRect(x + 1 + bevel, y + 1, w - 2 * bevel - 2, h - 3); - g.setPaint(fill); - g.setColor(abitbrighter(g.getColor())); - g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1); - g.setPaint(fill); - g.setColor(abitdarker(g.getColor())); - g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); - - g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4); - g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4); - g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4); - - g.fillOval(x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4); - } - - // Get the width of a mana cost rendered with ManaSymbols.draw - public static int getManaCostWidth(String manaCost, int symbolSize) { - int width = 0; - manaCost = manaCost.replace("\\", ""); - StringTokenizer tok = new StringTokenizer(manaCost, " "); - while (tok.hasMoreTokens()) { - tok.nextToken(); - width += symbolSize; - } - return width; - } - - // Abbreviate a piece of rules text, making substitutions to decrease its - // length. Also abbreviate reminder text. - private static final Pattern abbreviationPattern; - private static final Map abbreviations = new HashMap<>(); - private static final Pattern killReminderTextPattern; - - static { - // Available abbreviations - abbreviations.put("enters the battlefield", "ETB"); - abbreviations.put("less than", "<"); - abbreviations.put("greater than", ">"); - - // Compile into regex - String patternString = "("; - Iterator it = abbreviations.keySet().iterator(); - while (it.hasNext()) { - patternString += it.next(); - if (it.hasNext()) { - patternString += "|"; - } - } - patternString += ")"; - abbreviationPattern = Pattern.compile(patternString); - - // Reminder text killing - killReminderTextPattern = Pattern.compile("\\([^\\)]*\\)"); - } - - public static String abbreviateRule(String rule) { - StringBuffer build = new StringBuffer(); - Matcher match = abbreviationPattern.matcher(rule); - while (match.find()) { - match.appendReplacement(build, abbreviations.get(match.group(1))); - } - match.appendTail(build); - return build.toString(); - } - - public static String killReminderText(String rule) { - return killReminderTextPattern.matcher(rule).replaceAll("") - .replaceAll("", "") - .replaceAll("", ""); - } - - public static Color copyColor(Color color) { - if (color != null) { - return new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); - } else { - return null; - } - } - - public static String getCardLifeWithDamage(CardView cardView) { - // life with damage - String originLife = cardView.getToughness(); - if (cardView instanceof PermanentView) { - int damage = ((PermanentView) cardView).getDamage(); - int life; - try { - life = Integer.parseInt(originLife); - originLife = String.valueOf(Math.max(0, life - damage)); - } catch (NumberFormatException e) { - // - } - } - return originLife; - } - - public static boolean isCardWithDamage(CardView cardView) { - boolean haveDamage = false; - if (cardView instanceof PermanentView) { - haveDamage = ((PermanentView) cardView).getDamage() > 0; - } - return haveDamage; - } - - public static Color getCardTextColor(MageInt value, boolean drawAsDamaged, Color defaultColor, boolean textLight) { - if (drawAsDamaged) { - return textLight ? CARD_TEXT_COLOR_BAD_LIGHT : CARD_TEXT_COLOR_BAD_DARK; - } - - // boost colorizing - if (value != null) { - int current = value.getValue(); - int origin = value.getBaseValue(); - if (origin != 0) { - if (current < origin) { - return textLight ? CARD_TEXT_COLOR_BAD_LIGHT : CARD_TEXT_COLOR_BAD_DARK; - } else if (current > origin) { - return textLight ? CARD_TEXT_COLOR_GOOD_LIGHT : CARD_TEXT_COLOR_GOOD_DARK; - } else { - return defaultColor; - } - } - } - - return defaultColor; - } - -} +package org.mage.card.arcane; + +import mage.MageInt; +import mage.view.CardView; +import mage.view.PermanentView; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author stravant@gmail.com, JayDi85 + *

+ * Various static utilities for use in the card renderer + */ +public final class CardRendererUtils { + + // text colors for PT (mtgo and image render modes) + private static final Color CARD_TEXT_COLOR_GOOD_LIGHT = new Color(182, 235, 168); + private static final Color CARD_TEXT_COLOR_GOOD_DARK = new Color(52, 135, 88); + private static final Color CARD_TEXT_COLOR_BAD_LIGHT = new Color(234, 153, 153); + private static final Color CARD_TEXT_COLOR_BAD_DARK = new Color(200, 33, 33); + + /** + * Convert an abstract image, whose underlying implementation may or may not + * be a BufferedImage into a BufferedImage by creating one and coping the + * contents if it is not, and simply up-casting if it is. + * + * @param img The image to convert + * @return The converted image + */ + public static BufferedImage toBufferedImage(Image img) { + // Null? No conversion to do + if (img == null) { + return null; + } + + // Already a buffered image? + if (img instanceof BufferedImage) { + return (BufferedImage) img; + } + + // Create a buffered image with transparency + BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); + + // Draw the image on to the buffered image + Graphics2D bGr = bimage.createGraphics(); + bGr.drawImage(img, 0, 0, null); + bGr.dispose(); + + // Return the buffered image + return bimage; + } + + public static Color abitbrighter(Color c) { + int r = c.getRed(); + int g = c.getGreen(); + int b = c.getBlue(); + int alpha = c.getAlpha(); + + int plus_r = (255 - r) / 2; + int plus_g = (255 - g) / 2; + int plus_b = (255 - b) / 2; + + return new Color(r + plus_r, + g + plus_g, + b + plus_b, + alpha); + } + + public static Color abitdarker(Color c) { + int r = c.getRed(); + int g = c.getGreen(); + int b = c.getBlue(); + int alpha = c.getAlpha(); + + int plus_r = Math.min(255 - r, r) / 2; + int plus_g = Math.min(255 - g, g) / 2; + int plus_b = Math.min(255 - b, b) / 2; + + return new Color(r - plus_r, + g - plus_g, + b - plus_b, + alpha); + } + + // Draw a rounded box with a 2-pixel border + // Used on various card parts. + public static void drawRoundedBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Paint fill) { + g.setColor(new Color(0, 0, 0, 150)); + g.drawOval(x - 1, y - 1, bevel * 2, h); + g.setPaint(border); + g.drawOval(x, y, bevel * 2 - 1, h - 1); + g.drawOval(x + w - bevel * 2, y, bevel * 2 - 1, h - 1); + g.drawOval(x + 1, y + 1, bevel * 2 - 3, h - 3); + g.drawOval(x + 1 + w - bevel * 2, y + 1, bevel * 2 - 3, h - 3); + g.drawRect(x + bevel, y, w - 2 * bevel, h - 1); + g.drawRect(x + 1 + bevel, y + 1, w - 2 * bevel - 2, h - 3); + g.setPaint(fill); + g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4); + g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4); + g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4); + g.setPaint(fill); + g.setColor(abitbrighter(g.getColor())); + g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1); + g.setPaint(fill); + g.setColor(abitdarker(g.getColor())); + g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); + } + + public static void drawZendikarLandBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Paint fill) { + g.setColor(new Color(0, 0, 0, 150)); + + g.drawOval(x - 1, y, bevel * 2, h); + g.setPaint(border); + g.drawOval(x, y, bevel * 2 - 1, h - 1); + g.drawOval(x + w - bevel * 2, y, bevel * 2 - 1, h - 1); + g.drawOval(x + 1, y + 1, bevel * 2 - 3, h - 3); + g.drawOval(x + 1 + w - bevel * 2, y + 1, bevel * 2 - 3, h - 3); + + // The big circle in the middle.. (diameter=2+1/4 of height) - 3/4 above line, 1/2 below 0.75 + .5 + 1= 2.25 = 9/4 + g.drawOval(x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4); + + g.drawRect(x + bevel, y, w - 2 * bevel, h - 1); + g.drawRect(x + 1 + bevel, y + 1, w - 2 * bevel - 2, h - 3); + g.setPaint(fill); + g.setColor(abitbrighter(g.getColor())); + g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1); + g.setPaint(fill); + g.setColor(abitdarker(g.getColor())); + g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); + + g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4); + g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4); + g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4); + + g.fillOval(x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4); + } + + // Get the width of a mana cost rendered with ManaSymbols.draw + public static int getManaCostWidth(String manaCost, int symbolSize) { + int width = 0; + manaCost = manaCost.replace("\\", ""); + StringTokenizer tok = new StringTokenizer(manaCost, " "); + while (tok.hasMoreTokens()) { + tok.nextToken(); + width += symbolSize; + } + return width; + } + + // Abbreviate a piece of rules text, making substitutions to decrease its + // length. Also abbreviate reminder text. + private static final Pattern abbreviationPattern; + private static final Map abbreviations = new HashMap<>(); + private static final Pattern killReminderTextPattern; + + static { + // Available abbreviations + abbreviations.put("enters the battlefield", "ETB"); + abbreviations.put("less than", "<"); + abbreviations.put("greater than", ">"); + + // Compile into regex + String patternString = "("; + Iterator it = abbreviations.keySet().iterator(); + while (it.hasNext()) { + patternString += it.next(); + if (it.hasNext()) { + patternString += "|"; + } + } + patternString += ")"; + abbreviationPattern = Pattern.compile(patternString); + + // Reminder text killing + killReminderTextPattern = Pattern.compile("\\([^\\)]*\\)"); + } + + public static String abbreviateRule(String rule) { + StringBuffer build = new StringBuffer(); + Matcher match = abbreviationPattern.matcher(rule); + while (match.find()) { + match.appendReplacement(build, abbreviations.get(match.group(1))); + } + match.appendTail(build); + return build.toString(); + } + + public static String killReminderText(String rule) { + return killReminderTextPattern.matcher(rule).replaceAll("") + .replaceAll("", "") + .replaceAll("", ""); + } + + public static Color copyColor(Color color) { + if (color != null) { + return new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); + } else { + return null; + } + } + + public static String getCardLifeWithDamage(CardView cardView) { + // life with damage + String originLife = cardView.getToughness(); + if (cardView instanceof PermanentView) { + int damage = ((PermanentView) cardView).getDamage(); + int life; + try { + life = Integer.parseInt(originLife); + originLife = String.valueOf(Math.max(0, life - damage)); + } catch (NumberFormatException e) { + // + } + } + return originLife; + } + + public static boolean isCardWithDamage(CardView cardView) { + boolean haveDamage = false; + if (cardView instanceof PermanentView) { + haveDamage = ((PermanentView) cardView).getDamage() > 0; + } + return haveDamage; + } + + public static Color getCardTextColor(MageInt value, boolean drawAsDamaged, Color defaultColor, boolean textLight) { + if (drawAsDamaged) { + return textLight ? CARD_TEXT_COLOR_BAD_LIGHT : CARD_TEXT_COLOR_BAD_DARK; + } + + // boost colorizing + if (value != null) { + int current = value.getValue(); + int origin = value.getBaseValue(); + if (origin != 0) { + if (current < origin) { + return textLight ? CARD_TEXT_COLOR_BAD_LIGHT : CARD_TEXT_COLOR_BAD_DARK; + } else if (current > origin) { + return textLight ? CARD_TEXT_COLOR_GOOD_LIGHT : CARD_TEXT_COLOR_GOOD_DARK; + } else { + return defaultColor; + } + } + } + + return defaultColor; + } + +} diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxKeywordRule.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxKeywordRule.java index 50e15b04aa..30ce82323a 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxKeywordRule.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxKeywordRule.java @@ -1,17 +1,17 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.mage.card.arcane; - -import java.util.List; - -/** - * @author stravant@gmail.com - */ -public class TextboxKeywordRule extends TextboxRule { - public TextboxKeywordRule(String text, List regions) { - super(text, regions, TextboxRuleType.SIMPLE_KEYWORD); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.card.arcane; + +import java.util.List; + +/** + * @author stravant@gmail.com + */ +public class TextboxKeywordRule extends TextboxRule { + public TextboxKeywordRule(String text, List regions) { + super(text, regions, TextboxRuleType.SIMPLE_KEYWORD); + } +} diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java index 9dafbcb32b..371c8158e2 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java @@ -7,6 +7,7 @@ package org.mage.card.arcane; import java.awt.Font; import java.awt.Image; +import java.awt.Paint; import java.awt.font.GraphicAttribute; import java.awt.font.ImageGraphicAttribute; import java.awt.font.TextAttribute; @@ -47,6 +48,25 @@ public class TextboxRule { } } + public static class ColorRegion implements AttributeRegion { + + ColorRegion(int start, int end, Paint color) { + this.start = start; + this.end = end; + this.color = color; + } + private final int start; + private final int end; + private final Paint color; + + @Override + public void applyToAttributedString(AttributedString str, Font normal, Font italic) { + if (end > start + 1) { + str.addAttribute(TextAttribute.FOREGROUND, color, start, end); + } + } + } + // A special symbol embedded at some point in a string public static class EmbeddedSymbol implements AttributeRegion { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java index 4a36062f52..27fc01cd62 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java @@ -8,6 +8,7 @@ import java.awt.*; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -22,15 +23,16 @@ public final class TextboxRuleParser { private static final Pattern LevelAbilityPattern = Pattern.compile("Level (\\d+)-?(\\d*)(\\+?)"); private static final Pattern LoyaltyAbilityPattern = Pattern.compile("^(\\+|\\-)(\\d+|X): "); private static final Pattern SimpleKeywordPattern = Pattern.compile("^(\\w+( \\w+)?)\\s*(\\([^\\)]*\\))?\\s*$"); + private static final Pattern FontColorValuePattern = Pattern.compile("color\\s*=\\s*[\"'](\\w+)[\"']"); // Parse a given rule (given as a string) into a TextboxRule, replacing // symbol annotations, italics, etc, parsing out information such as // if the ability is a loyalty ability, and returning an TextboxRule // representing that information, which can be used to render the rule in // the textbox of a card. - public static TextboxRule parse(CardView source, String rule) { + public static TextboxRule parse(CardView source, String rule, String cardNameToUse) { // List of regions to apply - java.util.List regions = new ArrayList<>(); + List regions = new ArrayList<>(); // Leveler / loyalty / basic boolean isLeveler = false; @@ -102,10 +104,9 @@ public final class TextboxRuleParser { String contents = rule.substring(index + 1, closeIndex); if (contents.equals("this") || contents.equals("source")) { // Replace {this} with the card's name - String cardName = source.getName(); - build.append(cardName); + build.append(cardNameToUse); index += contents.length() + 2; - outputIndex += cardName.length(); + outputIndex += cardNameToUse.length(); } else { Image symbol = ManaSymbols.getSizedManaSymbol(contents.replace("/", ""), 10); if (symbol != null) { @@ -199,6 +200,11 @@ public final class TextboxRuleParser { } } break; + case "/font": + // Font it is an additional info of a card + // lets make it blue like it is in tooltip + regions.add(new TextboxRule.ColorRegion(openingIndex, outputIndex, Color.BLUE)); + break; default: // Unknown build.append('<').append(tag).append('>'); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleType.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleType.java index 76538a86a9..671fe25e70 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleType.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleType.java @@ -1,28 +1,28 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.mage.card.arcane; - -/** - * @author stravant@gmail.com - */ -public enum TextboxRuleType { - /* Normal abilities, just rendered as lines of text with embedded symbols - * replaced to the relevant images. */ - NORMAL, - - /* Keyword ability. To be displayed in the comma separated list at the - * very top of the rules box */ - SIMPLE_KEYWORD, - - /* Loyalty abilities on planeswalkers */ - LOYALTY, - - /* Basic mana ability */ - BASIC_MANA, - - /* Levelup creature - static ability at a given level */ - LEVEL -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.card.arcane; + +/** + * @author stravant@gmail.com + */ +public enum TextboxRuleType { + /* Normal abilities, just rendered as lines of text with embedded symbols + * replaced to the relevant images. */ + NORMAL, + + /* Keyword ability. To be displayed in the comma separated list at the + * very top of the rules box */ + SIMPLE_KEYWORD, + + /* Loyalty abilities on planeswalkers */ + LOYALTY, + + /* Basic mana ability */ + BASIC_MANA, + + /* Levelup creature - static ability at a given level */ + LEVEL +} diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CardImageUrls.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CardImageUrls.java index 1176ad3fed..e27a002cee 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CardImageUrls.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CardImageUrls.java @@ -2,6 +2,7 @@ package org.mage.plugins.card.dl.sources; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * @author JayDi85 @@ -25,7 +26,10 @@ public class CardImageUrls { this.baseUrl = baseUrl; - if (alternativeUrl != null && !alternativeUrl.isEmpty()) { + if (alternativeUrl != null + && alternativeUrl != null + && !alternativeUrl.isEmpty() + && !Objects.equals(baseUrl, alternativeUrl)) { this.alternativeUrls.add(alternativeUrl); } } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java index 324b4bc736..24c4980db1 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java @@ -24,7 +24,7 @@ public enum GrabbagImageSource implements CardImageSource { @Override public String getSourceName() { - return ""; + return "Grabbag"; } @Override @@ -75,7 +75,6 @@ public enum GrabbagImageSource implements CardImageSource { return; } singleLinks = new HashMap<>(); - singleLinks.put("SWS/AAT-1", "CqmDY8V.jpg"); singleLinks.put("SWS/Acklay of the Arena", "ESVRm6F.jpg"); singleLinks.put("SWS/Acquire Target", "FOskB4q.jpg"); @@ -468,7 +467,7 @@ public enum GrabbagImageSource implements CardImageSource { if (card.getSet().equals("MTG")) { return "http://static.starcitygames.com/sales/cardscans/"; } else if (card.getSet().equals("SWS")) { - return "http://i.imgur.com/"; + return "https://i.imgur.com/"; } else { return "http://magiccards.info/scans/en/"; } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java index c8fff3c370..255aac82c0 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java @@ -114,6 +114,15 @@ public enum ScryfallImageSource implements CardImageSource { + card.getCollectorId() + "/" + localizedCode + "?format=image"; alternativeUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/" + card.getCollectorId() + "/" + defaultCode + "?format=image"; + + // workaround to use cards without english images (some promos or special cards) + // bug: https://github.com/magefree/mage/issues/6829 + // example: Mysterious Egg from IKO https://api.scryfall.com/cards/iko/385/?format=image + if (Objects.equals(baseUrl, alternativeUrl)) { + // without loc code scryfall must return first available image + alternativeUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/" + + card.getCollectorId() + "/?format=image"; + } } return new CardImageUrls(baseUrl, alternativeUrl); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java index 7f52346d10..99cfdb2b2b 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java @@ -473,6 +473,141 @@ public class ScryfallImageSupportCards { // TODO: DuelsOfThePlaneswalkersPromos add("DPAP"); add("GRC"); + // + add("ARC"); + add("M11"); + add("V10"); + add("DDF"); + add("SOM"); + // add("TD0"); // Commander Theme Decks + add("PD2"); + add("ME4"); + add("MBS"); + add("DDG"); + add("NPH"); + add("CMD"); + add("M12"); + add("V11"); + add("DDH"); + add("ISD"); + add("PD3"); + add("DKA"); + add("DDI"); + add("AVR"); + add("PC2"); + add("M13"); + add("V12"); + add("DDJ"); + add("RTR"); + add("CM1"); + // add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia + add("GTC"); + add("DDK"); + add("DGM"); + add("MMA"); + add("M14"); + add("V13"); + add("DDL"); + add("THS"); + add("C13"); + add("BNG"); + add("DDM"); + add("JOU"); + // add("MD1"); // Modern Event Deck + add("CNS"); + add("VMA"); + add("M15"); + add("V14"); + add("DDN"); + add("KTK"); + add("C14"); + // add("DD3"); // Duel Decks Anthology + add("FRF"); + add("DDO"); + add("DTK"); + add("TPR"); + add("MM2"); + add("ORI"); + add("V15"); + add("DDP"); + add("BFZ"); + add("EXP"); + add("C15"); + // add("PZ1"); // Legendary Cube + add("OGW"); + add("DDQ"); + add("W16"); + add("SOI"); + add("EMA"); + add("EMN"); + add("V16"); + add("CN2"); + add("DDR"); + add("KLD"); + add("MPS"); + // add("PZ2"); + add("C16"); + add("PCA"); + add("AER"); + add("MM3"); + add("DDS"); + add("W17"); + add("AKH"); + add("CMA"); + add("E01"); + add("HOU"); + add("C17"); + add("XLN"); + add("DDT"); + add("IMA"); + add("E02"); + add("V17"); + add("UST"); + add("DDU"); + add("RIX"); + add("WMCQ"); + add("PPRO"); + add("A25"); + add("DOM"); + add("BBD"); + add("C18"); + add("CM2"); + add("M19"); + add("GS1"); + add("GRN"); + add("GK1"); + add("GNT"); + add("UMA"); + add("PUMA"); + add("RNA"); + add("MEDM"); + add("GK2"); + add("MH1"); + add("WAR"); + add("M20"); + add("C19"); + add("ELD"); + add("THB"); + add("IKO"); + add("C20"); + add("MB1"); + add("FMB1"); + // + add("EURO"); + add("GPX"); + add("ATH"); + add("GRC"); + add("ANA"); + add("G18"); + add("PM20"); + add("PS19"); + add("SS1"); + add("SS2"); + add("PPP1"); + add("PF19"); + add("MPS-AKH"); + add("M21"); + add("JMP"); } }; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java index 610370bb7e..627c761e2c 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java @@ -94,16 +94,33 @@ public class ScryfallImageSupportTokens { put("HOU/Insect", "https://api.scryfall.com/cards/thou/12/en?format=image"); put("HOU/Snake", "https://api.scryfall.com/cards/thou/11/en?format=image"); - //AKH + //AKH - tokens put("AKH/Beast", "https://api.scryfall.com/cards/takh/21/en?format=image"); put("AKH/Cat", "https://api.scryfall.com/cards/takh/16/en?format=image"); put("AKH/Drake", "https://api.scryfall.com/cards/takh/18/en?format=image"); put("AKH/Emblem Gideon", "https://api.scryfall.com/cards/takh/25/en?format=image"); put("AKH/Hippo", "https://api.scryfall.com/cards/takh/22/en?format=image"); + put("AKH/Insect", "https://api.scryfall.com/cards/takh/19/en?format=image"); put("AKH/Snake", "https://api.scryfall.com/cards/takh/23/en?format=image"); put("AKH/Warrior", "https://api.scryfall.com/cards/takh/17/en?format=image"); put("AKH/Wurm", "https://api.scryfall.com/cards/takh/24/en?format=image"); put("AKH/Zombie", "https://api.scryfall.com/cards/takh/20/en?format=image"); + //AKH - embalm ability (token from card) + put("AKH/Angel of Sanctions", "https://api.scryfall.com/cards/takh/1/en?format=image"); + put("AKH/Anointer Priest", "https://api.scryfall.com/cards/takh/2/en?format=image"); + put("AKH/Aven Initiate", "https://api.scryfall.com/cards/takh/3/en?format=image"); + put("AKH/Aven Wind Guide", "https://api.scryfall.com/cards/takh/4/en?format=image"); + put("AKH/Glyph Keeper", "https://api.scryfall.com/cards/takh/5/en?format=image"); + put("AKH/Heart-Piercer Manticore", "https://api.scryfall.com/cards/takh/6/en?format=image"); + put("AKH/Honored Hydra", "https://api.scryfall.com/cards/takh/7/en?format=image"); + put("AKH/Labyrinth Guardian", "https://api.scryfall.com/cards/takh/8/en?format=image"); + put("AKH/Oketra's Attendant", "https://api.scryfall.com/cards/takh/9/en?format=image"); + put("AKH/Sacred Cat", "https://api.scryfall.com/cards/takh/10/en?format=image"); + put("AKH/Tah-Crop Skirmisher", "https://api.scryfall.com/cards/takh/11/en?format=image"); + put("AKH/Temmet, Vizier of Naktamun", "https://api.scryfall.com/cards/takh/12/en?format=image"); + put("AKH/Trueheart Duelist", "https://api.scryfall.com/cards/takh/13/en?format=image"); + put("AKH/Unwavering Initiate", "https://api.scryfall.com/cards/takh/14/en?format=image"); + put("AKH/Vizier of Many Faces", "https://api.scryfall.com/cards/takh/15/en?format=image"); //AER put("AER/Etherium Cell", "https://api.scryfall.com/cards/taer/3/en?format=image"); @@ -350,6 +367,88 @@ public class ScryfallImageSupportTokens { put("THB/Wolf", "https://api.scryfall.com/cards/tthb/11/en?format=image"); put("THB/Zombie", "https://api.scryfall.com/cards/tthb/7/en?format=image"); + // IKO + put("IKO/Emblem Narset Of The Ancient Way", "https://api.scryfall.com/cards/tiko/12/en?format=image"); + put("IKO/Beast", "https://api.scryfall.com/cards/tiko/10/en?format=image"); + put("IKO/Cat Bird", "https://api.scryfall.com/cards/tiko/2/en?format=image"); + put("IKO/Cat", "https://api.scryfall.com/cards/tiko/1/en?format=image"); + put("IKO/Dinosaur Beast", "https://api.scryfall.com/cards/tiko/11/en?format=image"); + put("IKO/Dinosaur", "https://api.scryfall.com/cards/tiko/8/en?format=image"); + put("IKO/Feather", "https://api.scryfall.com/cards/tiko/9/en?format=image"); + put("IKO/Human Soldier/1", "https://api.scryfall.com/cards/tiko/3/en?format=image"); + put("IKO/Human Soldier/2", "https://api.scryfall.com/cards/tiko/4/en?format=image"); + put("IKO/Human Soldier/3", "https://api.scryfall.com/cards/tiko/5/en?format=image"); + put("IKO/Kraken", "https://api.scryfall.com/cards/tiko/6/en?format=image"); + put("IKO/Shark", "https://api.scryfall.com/cards/tiko/7/en?format=image"); + + // PCA (planes) + put("PCA/Eldrazi", "https://api.scryfall.com/cards/tpca/1/en?format=image"); + put("PCA/Plane - Academy at Tolaria West", "https://api.scryfall.com/cards/opca/9/en?format=image"); + put("PCA/Plane - Agyrem", "https://api.scryfall.com/cards/opca/11/en?format=image"); + put("PCA/Plane - Akoum", "https://api.scryfall.com/cards/opca/12/en?format=image"); + put("PCA/Plane - Astral Arena", "https://api.scryfall.com/cards/opca/14/en?format=image"); + put("PCA/Plane - Bant", "https://api.scryfall.com/cards/opca/15/en?format=image"); + put("PCA/Plane - Edge of Malacol", "https://api.scryfall.com/cards/opca/20/en?format=image"); + put("PCA/Plane - Feeding Grounds", "https://api.scryfall.com/cards/opca/23/en?format=image"); + put("PCA/Plane - Fields of Summer", "https://api.scryfall.com/cards/opca/24/en?format=image"); + put("PCA/Plane - Hedron Fields of Agadeem", "https://api.scryfall.com/cards/opca/35/en?format=image"); + put("PCA/Plane - Lethe Lake", "https://api.scryfall.com/cards/opca/47/en?format=image"); + put("PCA/Plane - Naya", "https://api.scryfall.com/cards/opca/55/en?format=image"); + put("PCA/Plane - Panopticon", "https://api.scryfall.com/cards/opca/62/en?format=image"); + put("PCA/Plane - Tazeem", "https://api.scryfall.com/cards/opca/78/en?format=image"); + put("PCA/Plane - The Dark Barony", "https://api.scryfall.com/cards/opca/19/en?format=image"); + put("PCA/Plane - The Eon Fog", "https://api.scryfall.com/cards/opca/22/en?format=image"); + put("PCA/Plane - The Great Forest", "https://api.scryfall.com/cards/opca/32/en?format=image"); + put("PCA/Plane - The Zephyr Maze", "https://api.scryfall.com/cards/opca/86/en?format=image"); + put("PCA/Plane - Truga Jungle", "https://api.scryfall.com/cards/opca/81/en?format=image"); + put("PCA/Plane - Trail of the Mage-Rings", "https://api.scryfall.com/cards/opca/80/en?format=image"); + put("PCA/Plane - Turri Island", "https://api.scryfall.com/cards/opca/82/en?format=image"); + put("PCA/Plane - Undercity Reaches", "https://api.scryfall.com/cards/opca/83/en?format=image"); + + // C20 + put("C20/Angel", "https://api.scryfall.com/cards/tc20/1/en?format=image"); + put("C20/Beast", "https://api.scryfall.com/cards/tc20/11/en?format=image"); + put("C20/Bird Illusion", "https://api.scryfall.com/cards/tc20/7/en?format=image"); + put("C20/Bird", "https://api.scryfall.com/cards/tc20/2/en?format=image"); + put("C20/Dinosaur Cat", "https://api.scryfall.com/cards/tc20/16/en?format=image"); + put("C20/Drake", "https://api.scryfall.com/cards/tc20/8/en?format=image"); + put("C20/Elemental/1", "https://api.scryfall.com/cards/tc20/10/en?format=image"); // 3/1 + put("C20/Elemental/2", "https://api.scryfall.com/cards/tc20/3/en?format=image"); // 4/4 + put("C20/Goblin Warrior", "https://api.scryfall.com/cards/tc20/17/en?format=image"); + put("C20/Human", "https://api.scryfall.com/cards/tc20/4/en?format=image"); + put("C20/Hydra", "https://api.scryfall.com/cards/tc20/12/en?format=image"); + put("C20/Insect/1", "https://api.scryfall.com/cards/tc20/13/en?format=image"); // deathtouch + put("C20/Insect/2", "https://api.scryfall.com/cards/tc20/18/en?format=image"); // haste + put("C20/Saproling", "https://api.scryfall.com/cards/tc20/14/en?format=image"); + put("C20/Snake", "https://api.scryfall.com/cards/tc20/15/en?format=image"); + put("C20/Soldier", "https://api.scryfall.com/cards/tc20/5/en?format=image"); + put("C20/Spirit", "https://api.scryfall.com/cards/tc20/6/en?format=image"); + put("C20/Treasure", "https://api.scryfall.com/cards/tc20/19/en?format=image"); + put("C20/Zombie", "https://api.scryfall.com/cards/tc20/9/en?format=image"); + + // M21 + put("M21/Angel", "https://api.scryfall.com/cards/tm21/1/en?format=image"); + put("M21/Emblem Basri Ket", "https://api.scryfall.com/cards/tm21/16/en?format=image"); + put("M21/Beast", "https://api.scryfall.com/cards/tm21/10/en?format=image"); + put("M21/Bird", "https://api.scryfall.com/cards/tm21/2/en?format=image"); + put("M21/Cat/1", "https://api.scryfall.com/cards/tm21/20/en?format=image"); // 1/1 + put("M21/Cat/2", "https://api.scryfall.com/cards/tm21/11/en?format=image"); // 2/2 + put("M21/Construct", "https://api.scryfall.com/cards/tm21/14/en?format=image"); + put("M21/Demon", "https://api.scryfall.com/cards/tm21/6/en?format=image"); + put("M21/Dog", "https://api.scryfall.com/cards/tm21/19/en?format=image"); + put("M21/Emblem Garruk, Unleashed", "https://api.scryfall.com/cards/tm21/17/en?format=image"); + put("M21/Goblin Wizard", "https://api.scryfall.com/cards/tm21/8/en?format=image"); + put("M21/Griffin", "https://api.scryfall.com/cards/tm21/3/en?format=image"); + put("M21/Knight", "https://api.scryfall.com/cards/tm21/4/en?format=image"); + put("M21/Emblem Liliana, Waker of the Dead", "https://api.scryfall.com/cards/tm21/18/en?format=image"); + put("M21/Pirate", "https://api.scryfall.com/cards/tm21/9/en?format=image"); + put("M21/Saproling", "https://api.scryfall.com/cards/tm21/12/en?format=image"); + put("M21/Soldier", "https://api.scryfall.com/cards/tm21/5/en?format=image"); + put("M21/Treasure", "https://api.scryfall.com/cards/tm21/15/en?format=image"); + put("M21/Weird", "https://api.scryfall.com/cards/tm21/13/en?format=image"); + put("M21/Zombie", "https://api.scryfall.com/cards/tm21/7/en?format=image"); + + // generate supported sets supportedSets.clear(); for (String cardName : this.keySet()) { diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java index a077bdf967..0b3b528436 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java @@ -47,7 +47,7 @@ public final class ImageCache { private static final SoftValuesLoadingCache FACE_IMAGE_CACHE; /** - * Common pattern for keys. Format: "##" + * Common pattern for keys. See ImageCache.getKey for structure info */ private static final Pattern KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)#(.*)#(.*)#(.*)"); @@ -84,13 +84,21 @@ public final class ImageCache { boolean cardback = false; String path; - if (collectorId.isEmpty() || "0".equals(collectorId)) { + if (collectorId.isEmpty() || "0".equals(collectorId) || !tokenDescriptor.isEmpty()) { // tokenDescriptor for embalm ability info.setToken(true); path = CardImageUtils.generateTokenImagePath(info); if (path == null) { cardback = true; - // TODO: replace empty token by other default card, not cardback - path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename); + // try token image from card + CardDownloadData newInfo = new CardDownloadData(info); + newInfo.setToken(false); + path = CardImageUtils.buildImagePathToCard(newInfo); + TFile tokenFile = getTFile(path); + if (tokenFile == null || !tokenFile.exists()) { + // token empty token image + // TODO: replace empty token by other default card, not cardback + path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename); + } } } else { path = CardImageUtils.buildImagePathToCard(info); @@ -245,12 +253,20 @@ public final class ImageCache { CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor); String path; - if (collectorId.isEmpty() || "0".equals(collectorId)) { + if (collectorId.isEmpty() || "0".equals(collectorId) || !tokenDescriptor.isEmpty()) { // tokenDescriptor for embalm ability info.setToken(true); - path = CardImageUtils.generateFullTokenImagePath(info); + path = CardImageUtils.generateTokenImagePath(info); if (path == null) { - // TODO: replace empty token by other default card, not cardback - path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename); + // try token image from card + CardDownloadData newInfo = new CardDownloadData(info); + newInfo.setToken(false); + path = CardImageUtils.buildImagePathToCard(newInfo); + TFile tokenFile = getTFile(path); + if (tokenFile == null || !tokenFile.exists()) { + // token empty token image + // TODO: replace empty token by other default card, not cardback + path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename); + } } } else { path = CardImageUtils.buildImagePathToCard(info); @@ -428,11 +444,14 @@ public final class ImageCache { * Returns the map key for a card, without any suffixes for the image size. */ private static String getKey(CardView card, String name, String suffix) { - return name + '#' + card.getExpansionSetCode() + '#' + card.getType() + '#' + card.getCardNumber() + '#' - + (card.getTokenSetCode() == null ? "" : card.getTokenSetCode()) + return name + + '#' + card.getExpansionSetCode() + + '#' + card.getType() + + '#' + card.getCardNumber() + + '#' + (card.getTokenSetCode() == null ? "" : card.getTokenSetCode()) + suffix + (card.getUsesVariousArt() ? "#usesVariousArt" : "") - + (card.getTokenDescriptor() != null ? '#' + card.getTokenDescriptor() : "#"); + + '#' + (card.getTokenDescriptor() != null ? card.getTokenDescriptor() : ""); } /** diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java index e37eb827c1..0aff4722d9 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/impl/ImageManagerImpl.java @@ -14,6 +14,8 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; import javax.imageio.ImageIO; + +import mage.client.dialog.PreferencesDialog; import mage.client.util.gui.BufferedImageBuilder; import org.mage.plugins.card.utils.ImageManager; import org.mage.plugins.card.utils.Transparency; @@ -31,7 +33,9 @@ public enum ImageManagerImpl implements ImageManager { "Main2", "Cleanup", "Next_Turn"}; phasesImages = new HashMap<>(); for (String name : phases) { - Image image = getImageFromResource("/phases/phase_" + name.toLowerCase(Locale.ENGLISH) + ".png", new Rectangle(36, 36)); + Image image = getImageFromResource( + PreferencesDialog.getCurrentTheme().getPhasePath("phase_" + name.toLowerCase(Locale.ENGLISH) + ".png"), + new Rectangle(36, 36)); phasesImages.put(name, image); } } @@ -263,7 +267,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getConcedeButtonImage() { if (imageConcedeButton == null) { - imageConcedeButton = getBufferedImageFromResource("/buttons/concede.png"); + imageConcedeButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("concede.png")); } return imageConcedeButton; } @@ -271,7 +276,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getSwitchHandsButtonImage() { if (imageSwitchHandsButton == null) { - imageSwitchHandsButton = getBufferedImageFromResource("/buttons/switch_hands.png"); + imageSwitchHandsButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("switch_hands.png")); } return imageSwitchHandsButton; } @@ -279,7 +285,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getStopWatchButtonImage() { if (imageStopWatchingButton == null) { - imageStopWatchingButton = getBufferedImageFromResource("/buttons/stop_watching.png"); + imageStopWatchingButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("stop_watching.png")); } return imageStopWatchingButton; } @@ -287,7 +294,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getCancelSkipButtonImage() { if (imageCancelSkipButton == null) { - imageCancelSkipButton = getBufferedImageFromResource("/buttons/cancel_skip.png"); + imageCancelSkipButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("cancel_skip.png")); } return imageCancelSkipButton; } @@ -295,7 +303,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getSkipNextTurnButtonImage() { if (imageSkipNextTurnButton == null) { - imageSkipNextTurnButton = getBufferedImageFromResource("/buttons/skip_turn.png"); + imageSkipNextTurnButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("skip_turn.png")); } return imageSkipNextTurnButton; } @@ -303,7 +312,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getSkipEndTurnButtonImage() { if (imageSkipToEndTurnButton == null) { - imageSkipToEndTurnButton = getBufferedImageFromResource("/buttons/skip_to_end.png"); + imageSkipToEndTurnButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("skip_to_end.png")); } return imageSkipToEndTurnButton; } @@ -311,7 +321,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getSkipMainButtonImage() { if (imageSkipToMainButton == null) { - imageSkipToMainButton = getBufferedImageFromResource("/buttons/skip_to_main.png"); + imageSkipToMainButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("skip_to_main.png")); } return imageSkipToMainButton; } @@ -319,7 +330,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getSkipStackButtonImage() { if (imageSkipStackButton == null) { - imageSkipStackButton = getBufferedImageFromResource("/buttons/skip_stack.png"); + imageSkipStackButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("skip_stack.png")); } return imageSkipStackButton; } @@ -327,7 +339,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getSkipEndStepBeforeYourTurnButtonImage() { if (imageSkipUntilEndStepBeforeYourTurnButton == null) { - imageSkipUntilEndStepBeforeYourTurnButton = getBufferedImageFromResource("/buttons/skip_to_previous_end.png"); + imageSkipUntilEndStepBeforeYourTurnButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("skip_to_previous_end.png")); } return imageSkipUntilEndStepBeforeYourTurnButton; } @@ -335,7 +348,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getSkipYourNextTurnButtonImage() { if (imageSkipYourNextTurnButton == null) { - imageSkipYourNextTurnButton = getBufferedImageFromResource("/buttons/skip_all.png"); + imageSkipYourNextTurnButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("skip_all.png")); } return imageSkipYourNextTurnButton; } @@ -343,7 +357,8 @@ public enum ImageManagerImpl implements ImageManager { @Override public Image getToggleRecordMacroButtonImage() { if (imageToggleRecordMacroButton == null) { - imageToggleRecordMacroButton = getBufferedImageFromResource("/buttons/toggle_macro.png"); + imageToggleRecordMacroButton = getBufferedImageFromResource( + PreferencesDialog.getCurrentTheme().getButtonPath("toggle_macro.png")); } return imageToggleRecordMacroButton; } diff --git a/Mage.Client/src/main/java/org/mage/plugins/theme/ThemePluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/theme/ThemePluginImpl.java index 6f9b00879f..7d12660d5a 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/theme/ThemePluginImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/theme/ThemePluginImpl.java @@ -12,7 +12,6 @@ import mage.components.ImagePanel; import mage.components.ImagePanelStyle; import mage.interfaces.plugin.ThemePlugin; import net.xeoh.plugins.base.annotations.PluginImplementation; -import net.xeoh.plugins.base.annotations.events.Init; import net.xeoh.plugins.base.annotations.events.PluginLoaded; import net.xeoh.plugins.base.annotations.meta.Author; import org.apache.log4j.Logger; @@ -27,10 +26,6 @@ public class ThemePluginImpl implements ThemePlugin { private final List flist = new List(); private final String BackgroundDir = "backgrounds" + File.separator; - @Init - public void init() { - } - @PluginLoaded public void newPlugin(ThemePlugin plugin) { log.info(plugin.toString() + " has been loaded."); @@ -104,10 +99,11 @@ public class ThemePluginImpl implements ThemePlugin { } } + // Sets background for in-battle + // loadbuffer_default - Only apply theme background if no custom user background set private BufferedImage loadbuffer_default() throws IOException { - String filename = "/dragon.png"; BufferedImage res; - InputStream is = this.getClass().getResourceAsStream(filename); + InputStream is = this.getClass().getResourceAsStream(PreferencesDialog.getCurrentTheme().getBattleBackgroundPath()); res = ImageIO.read(is); return res; } @@ -150,43 +146,44 @@ public class ThemePluginImpl implements ThemePlugin { return bgPanel; } + // Sets background for logged in user for tables/deck editor/card viewer/etc private synchronized ImagePanel createImagePanelInstance() { if (background == null) { - String filename = "/background.png"; - try { - if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_BACKGROUND_IMAGE_DEFAULT, "true").equals("true")) { - InputStream is = this.getClass().getResourceAsStream(filename); - if (is == null) { - throw new FileNotFoundException("Couldn't find " + filename + " in resources."); - } - background = ImageIO.read(is); - } else { - String path = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_BACKGROUND_IMAGE, ""); - if (path != null && !path.isEmpty()) { - try { - File f = new File(path); - if (f != null) { - background = ImageIO.read(f); - } - } catch (Exception e) { - background = null; + try { + if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_BACKGROUND_IMAGE_DEFAULT, "true").equals("true")) { + InputStream is = this.getClass().getResourceAsStream(PreferencesDialog.getCurrentTheme().getBackgroundPath()); + if (is == null) { + throw new FileNotFoundException("Couldn't find " + PreferencesDialog.getCurrentTheme().getBackgroundPath() + " in resources."); + } + background = ImageIO.read(is); + } else { + String path = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_BACKGROUND_IMAGE, ""); + if (path != null && !path.isEmpty()) { + try { + File f = new File(path); + if (f != null) { + background = ImageIO.read(f); } + } catch (Exception e) { + background = null; } } - if (background == null) { - InputStream is = this.getClass().getResourceAsStream(filename); - if (is == null) { - throw new FileNotFoundException("Couldn't find " + filename + " in resources."); - } - background = ImageIO.read(is); + } + if (background == null) { + String filename = "/background/background.png"; + InputStream is = this.getClass().getResourceAsStream(filename); + if (is == null) { + throw new FileNotFoundException("Couldn't find " + filename + " in resources."); } + background = ImageIO.read(is); if (background == null) { throw new FileNotFoundException("Couldn't find " + filename + " in resources."); } - } catch (Exception e) { - log.error(e.getMessage(), e); - return null; } + } catch (Exception e) { + log.error(e.getMessage(), e); + return null; + } } return new ImagePanel(background, ImagePanelStyle.SCALED); } diff --git a/Mage.Client/src/main/resources/background.jpg b/Mage.Client/src/main/resources/background.jpg deleted file mode 100644 index 75c7980b63..0000000000 Binary files a/Mage.Client/src/main/resources/background.jpg and /dev/null differ diff --git a/Mage.Client/src/main/resources/background/16bit-theme/background.png b/Mage.Client/src/main/resources/background/16bit-theme/background.png new file mode 100644 index 0000000000..53260054e5 Binary files /dev/null and b/Mage.Client/src/main/resources/background/16bit-theme/background.png differ diff --git a/Mage.Client/src/main/resources/background/16bit-theme/login-background.png b/Mage.Client/src/main/resources/background/16bit-theme/login-background.png new file mode 100644 index 0000000000..a1c69400c5 Binary files /dev/null and b/Mage.Client/src/main/resources/background/16bit-theme/login-background.png differ diff --git a/Mage.Client/src/main/resources/background.png b/Mage.Client/src/main/resources/background/background.png similarity index 100% rename from Mage.Client/src/main/resources/background.png rename to Mage.Client/src/main/resources/background/background.png diff --git a/Mage.Client/src/main/resources/dragon.png b/Mage.Client/src/main/resources/background/battle-background.png similarity index 100% rename from Mage.Client/src/main/resources/dragon.png rename to Mage.Client/src/main/resources/background/battle-background.png diff --git a/Mage.Client/src/main/resources/background/coffee-theme/background.png b/Mage.Client/src/main/resources/background/coffee-theme/background.png new file mode 100644 index 0000000000..d027873990 Binary files /dev/null and b/Mage.Client/src/main/resources/background/coffee-theme/background.png differ diff --git a/Mage.Client/src/main/resources/background/coffee-theme/battle-background.png b/Mage.Client/src/main/resources/background/coffee-theme/battle-background.png new file mode 100644 index 0000000000..08aacd45d0 Binary files /dev/null and b/Mage.Client/src/main/resources/background/coffee-theme/battle-background.png differ diff --git a/Mage.Client/src/main/resources/background/coffee-theme/login-background.png b/Mage.Client/src/main/resources/background/coffee-theme/login-background.png new file mode 100644 index 0000000000..c6e272ccd2 Binary files /dev/null and b/Mage.Client/src/main/resources/background/coffee-theme/login-background.png differ diff --git a/Mage.Client/src/main/resources/background/island-theme/background.png b/Mage.Client/src/main/resources/background/island-theme/background.png new file mode 100644 index 0000000000..ee53a5c95b Binary files /dev/null and b/Mage.Client/src/main/resources/background/island-theme/background.png differ diff --git a/Mage.Client/src/main/resources/background/island-theme/login-background.png b/Mage.Client/src/main/resources/background/island-theme/login-background.png new file mode 100644 index 0000000000..1e1c644f5a Binary files /dev/null and b/Mage.Client/src/main/resources/background/island-theme/login-background.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/cancel_skip.png b/Mage.Client/src/main/resources/buttons/16bit-theme/cancel_skip.png new file mode 100644 index 0000000000..ccb15fa5f6 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/cancel_skip.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/concede.png b/Mage.Client/src/main/resources/buttons/16bit-theme/concede.png new file mode 100644 index 0000000000..1ce98fb19a Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/concede.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/skip_all.png b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_all.png new file mode 100644 index 0000000000..52fa4050a0 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_all.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/skip_stack.png b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_stack.png new file mode 100644 index 0000000000..e2ef22ee23 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_stack.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/skip_to_end.png b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_to_end.png new file mode 100644 index 0000000000..9a80561dd5 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_to_end.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/skip_to_main.png b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_to_main.png new file mode 100644 index 0000000000..7ca3ea3f8c Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_to_main.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/skip_to_previous_end.png b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_to_previous_end.png new file mode 100644 index 0000000000..38792e1bd0 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_to_previous_end.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/skip_turn.png b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_turn.png new file mode 100644 index 0000000000..db01fefadc Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/skip_turn.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/stop_watching.png b/Mage.Client/src/main/resources/buttons/16bit-theme/stop_watching.png new file mode 100644 index 0000000000..5a9ebb7b7b Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/stop_watching.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/switch_hands.png b/Mage.Client/src/main/resources/buttons/16bit-theme/switch_hands.png new file mode 100644 index 0000000000..8ee4fffdde Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/switch_hands.png differ diff --git a/Mage.Client/src/main/resources/buttons/16bit-theme/toggle_macro.png b/Mage.Client/src/main/resources/buttons/16bit-theme/toggle_macro.png new file mode 100644 index 0000000000..24dafbb4e5 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/16bit-theme/toggle_macro.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/cancel_skip.png b/Mage.Client/src/main/resources/buttons/coffee-theme/cancel_skip.png new file mode 100644 index 0000000000..a65476db20 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/cancel_skip.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/concede.png b/Mage.Client/src/main/resources/buttons/coffee-theme/concede.png new file mode 100644 index 0000000000..33ff828cbf Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/concede.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/skip_all.png b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_all.png new file mode 100644 index 0000000000..a9e7e90517 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_all.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/skip_stack.png b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_stack.png new file mode 100644 index 0000000000..ffb27fdc1d Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_stack.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/skip_to_end.png b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_to_end.png new file mode 100644 index 0000000000..1d89223f13 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_to_end.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/skip_to_main.png b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_to_main.png new file mode 100644 index 0000000000..68171b6302 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_to_main.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/skip_to_previous_end.png b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_to_previous_end.png new file mode 100644 index 0000000000..a6e23b98d3 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_to_previous_end.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/skip_turn.png b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_turn.png new file mode 100644 index 0000000000..a9ba0dde7e Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/skip_turn.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/stop_watching.png b/Mage.Client/src/main/resources/buttons/coffee-theme/stop_watching.png new file mode 100644 index 0000000000..baf9868776 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/stop_watching.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/switch_hands.png b/Mage.Client/src/main/resources/buttons/coffee-theme/switch_hands.png new file mode 100644 index 0000000000..c248dacdcf Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/switch_hands.png differ diff --git a/Mage.Client/src/main/resources/buttons/coffee-theme/toggle_macro.png b/Mage.Client/src/main/resources/buttons/coffee-theme/toggle_macro.png new file mode 100644 index 0000000000..71cf8e1454 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/coffee-theme/toggle_macro.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/cancel_skip.png b/Mage.Client/src/main/resources/buttons/island-theme/cancel_skip.png new file mode 100644 index 0000000000..013792f806 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/cancel_skip.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/concede.png b/Mage.Client/src/main/resources/buttons/island-theme/concede.png new file mode 100644 index 0000000000..dcf96b5ec6 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/concede.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/skip_all.png b/Mage.Client/src/main/resources/buttons/island-theme/skip_all.png new file mode 100644 index 0000000000..18576182d4 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/skip_all.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/skip_stack.png b/Mage.Client/src/main/resources/buttons/island-theme/skip_stack.png new file mode 100644 index 0000000000..7e8a313e03 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/skip_stack.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/skip_to_end.png b/Mage.Client/src/main/resources/buttons/island-theme/skip_to_end.png new file mode 100644 index 0000000000..ce9c883c12 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/skip_to_end.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/skip_to_main.png b/Mage.Client/src/main/resources/buttons/island-theme/skip_to_main.png new file mode 100644 index 0000000000..a2d33d3c71 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/skip_to_main.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/skip_to_previous_end.png b/Mage.Client/src/main/resources/buttons/island-theme/skip_to_previous_end.png new file mode 100644 index 0000000000..9eec76991d Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/skip_to_previous_end.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/skip_turn.png b/Mage.Client/src/main/resources/buttons/island-theme/skip_turn.png new file mode 100644 index 0000000000..c19196777c Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/skip_turn.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/stop_watching.png b/Mage.Client/src/main/resources/buttons/island-theme/stop_watching.png new file mode 100644 index 0000000000..6f8934b8be Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/stop_watching.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/switch_hands.png b/Mage.Client/src/main/resources/buttons/island-theme/switch_hands.png new file mode 100644 index 0000000000..f7a39488a6 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/switch_hands.png differ diff --git a/Mage.Client/src/main/resources/buttons/island-theme/toggle_macro.png b/Mage.Client/src/main/resources/buttons/island-theme/toggle_macro.png new file mode 100644 index 0000000000..2c2fd6c659 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/island-theme/toggle_macro.png differ diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt index 712f9912df..7e055342ce 100644 --- a/Mage.Client/src/main/resources/card-pictures-tok.txt +++ b/Mage.Client/src/main/resources/card-pictures-tok.txt @@ -45,6 +45,8 @@ #|Generate|TOK:PPRE|Wolf|| #|Generate|TOK:PPRE|Wurm|| #|Generate|TOK:WMCQ|Angel|| + +# Emblems |Generate|EMBLEM!:BFZ|Emblem Gideon|||GideonAllyOfZendikarEmblem| |Generate|EMBLEM!:BFZ|Emblem Kiora|||KioraMasterOfTheDepthsEmblem| |Generate|EMBLEM!:BFZ|Emblem Nixilis|||ObNixilisReignitedEmblem| @@ -101,12 +103,18 @@ |Generate|EMBLEM:DOM|Teferi, Hero of Dominaria||Emblem Teferi|TeferiHeroOfDominariaEmblem| |Generate|EMBLEM:AER|Tezzeret the Schemer||Emblem Tezzeret|TezzeretTheSchemerEmblem| |Generate|EMBLEM:ELD|Garruk, Cursed Huntsman||Emblem Garruk|GarrukCursedHuntsmanEmblem| -|Generate|PLANE:PCA|Plane - Academy At Tolaria West|||AcademyAtTolariaWestPlane| +|Generate|EMBLEM:IKO|Narset Of The Ancient Way||Emblem Narset|NarsetOfTheAncientWayEmblem| +|Generate|EMBLEM:M21|Basri Ket||Emblem Basri|BasriKetEmblem| +|Generate|EMBLEM:M21|Garruk, Unleashed||Emblem Garruk|GarrukUnleashedEmblem| +|Generate|EMBLEM:M21|Liliana, Waker of the Dead||Emblem Liliana|LilianaWakerOfTheDeadEmblem| + +# Planes +|Generate|PLANE:PCA|Plane - Academy at Tolaria West|||AcademyAtTolariaWestPlane| |Generate|PLANE:PCA|Plane - Agyrem|||AgyremPlane| |Generate|PLANE:PCA|Plane - Akoum|||AkoumPlane| |Generate|PLANE:PCA|Plane - Astral Arena|||AstralArenaPlane| |Generate|PLANE:PCA|Plane - Bant|||BantPlane| -|Generate|PLANE:PCA|Plane - Edge Of Malacol|||EdgeOfMalacolPlane| +|Generate|PLANE:PCA|Plane - Edge of Malacol|||EdgeOfMalacolPlane| |Generate|PLANE:PCA|Plane - Feeding Grounds|||FeedingGroundsPlane| |Generate|PLANE:PCA|Plane - Fields of Summer|||FieldsOfSummerPlane| |Generate|PLANE:PCA|Plane - Hedron Fields of Agadeem|||HedronFieldsOfAgadeemPlane| @@ -120,6 +128,8 @@ |Generate|PLANE:PCA|Plane - Truga Jungle|||TrugaJunglePlane| |Generate|PLANE:PCA|Plane - Turri Island|||TurriIslandPlane| |Generate|PLANE:PCA|Plane - Undercity Reaches|||UndercityReachesPlane| +|Generate|PLANE:PCA|Plane - The Zephyr Maze|||TheZephyrMazePlane| +|Generate|PLANE:PCA|Plane - Trail of the Mage-Rings|||TrailOfTheMageRingsPlane| |Generate|TOK:ANA|Goblin|||GoblinToken| |Generate|TOK:ANA|Spirit|||SpiritWhiteToken| |Generate|TOK:PCA|Eldrazi|||EldraziAnnihilatorToken| @@ -151,7 +161,7 @@ |Generate|TOK:5ED|Serf|||SerfToken| |Generate|TOK:5ED|Snake|||SerpentGeneratorSnakeToken| |Generate|TOK:5ED|Thrull|||BreedingPitBlackInsectToken| -|Generate|TOK:6ED|Cat|||WaitingInTheWeedsCatToken| +|Generate|TOK:6ED|Cat|||GreenCatToken| |Generate|TOK:6ED|Citizen|||CitizenToken| |Generate|TOK:6ED|Djinn|||DjinnToken| |Generate|TOK:6ED|Goblin|||GoblinToken| @@ -165,30 +175,34 @@ |Generate|TOK:AER|Etherium Cell|||EtheriumCellToken| |Generate|TOK:AER|Gremlin|||GremlinToken| |Generate|TOK:AER|Ragavan|||RagavanToken| + +# AKH |Generate|TOK:AKH|Beast|||BeastToken3| |Generate|TOK:AKH|Cat|||CatToken2| |Generate|TOK:AKH|Drake|||DrakeToken| |Generate|TOK:AKH|Hippo|||HippoToken2| +|Generate|TOK:AKH|Insect|||NestOfScarabsBlackInsectToken| |Generate|TOK:AKH|Snake|||DeathtouchSnakeToken| |Generate|TOK:AKH|Warrior|||WarriorVigilantToken| -|Generate|TOK:AKH|Wurm|||WurmToken3| -|Generate|TOK:AKH|Zombie|||ZombieToken +|Generate|TOK:AKH|Wurm|||Wurm55Token| +|Generate|TOK:AKH|Zombie|||ZombieToken| #TOK:AKH - some tokens from real cards (see Embalm ability) -#|Generate|TOK:AKH|Angel of Sanctions|| -#|Generate|TOK:AKH|Anointer Priest|| -#|Generate|TOK:AKH|Aven Initiate|| -#|Generate|TOK:AKH|Aven Wind Guide|| -#|Generate|TOK:AKH|Glyph Keeper|| -#|Generate|TOK:AKH|Heart-Piercer Manticore|| -#|Generate|TOK:AKH|Honored Hydra|| -#|Generate|TOK:AKH|Labyrinth Guardian|| -#|Generate|TOK:AKH|Oketra's Attendant|| -#|Generate|TOK:AKH|Sacred Cat|| -#|Generate|TOK:AKH|Tah-Crop Skirmisher|| -#|Generate|TOK:AKH|Temmet, Vizier of Naktamun|| -#|Generate|TOK:AKH|Trueheart Duelist|| -#|Generate|TOK:AKH|Unwavering Initiate|| -#|Generate|TOK:AKH|Vizier of Many Faces|| +|Generate|TOK:AKH|Angel of Sanctions|| +|Generate|TOK:AKH|Anointer Priest|| +|Generate|TOK:AKH|Aven Initiate|| +|Generate|TOK:AKH|Aven Wind Guide|| +|Generate|TOK:AKH|Glyph Keeper|| +|Generate|TOK:AKH|Heart-Piercer Manticore|| +|Generate|TOK:AKH|Honored Hydra|| +|Generate|TOK:AKH|Labyrinth Guardian|| +|Generate|TOK:AKH|Oketra's Attendant|| +|Generate|TOK:AKH|Sacred Cat|| +|Generate|TOK:AKH|Tah-Crop Skirmisher|| +|Generate|TOK:AKH|Temmet, Vizier of Naktamun|| +|Generate|TOK:AKH|Trueheart Duelist|| +|Generate|TOK:AKH|Unwavering Initiate|| +|Generate|TOK:AKH|Vizier of Many Faces|| + |Generate|TOK:ALA|Beast|||GodSireBeastToken| |Generate|TOK:ALA|Dragon|||DragonToken| |Generate|TOK:ALA|Goblin|||GoblinTokenWithHaste| @@ -230,7 +244,6 @@ |Generate|TOK:ATQ|Assembly-Worker|||AssemblyWorkerToken| |Generate|TOK:ATQ|Tetravite|||TetraviteToken| |Generate|TOK:AVR|Angel|||AngelToken| -|Generate|TOK:AVR|Angel|||AngelTombToken| |Generate|TOK:AVR|Demon|||DemonToken| |Generate|TOK:AVR|Human|1||HumanToken| |Generate|TOK:AVR|Human|2||RedHumanToken| @@ -267,9 +280,9 @@ |Generate|TOK:C13|Beast|||OneDozenEyesBeastToken| |Generate|TOK:C13|Beast|||SpawningGroundsBeastToken| |Generate|TOK:C13|Drake|||LeafdrakeRoostDrakeToken| -|Generate|TOK:C13|Elemental|||SeedGuardianToken| -|Generate|TOK:C13|Elemental|||WalkerOfTheGroveToken| -|Generate|TOK:C13|Elemental|||YoungPyromancerElementalToken| +|Generate|TOK:C13|Elemental|1||SeedGuardianToken| +|Generate|TOK:C13|Elemental|2||WalkerOfTheGroveToken| +|Generate|TOK:C13|Elemental|3||RedElementalToken| |Generate|TOK:C13|Elephant|||ElephantToken| |Generate|TOK:C13|Elf Warrior|||ElfToken| |Generate|TOK:C13|Goat|||GoatToken| @@ -316,8 +329,8 @@ |Generate|TOK:C14|Treefolk|||SylvanOfferingTreefolkToken| |Generate|TOK:C14|Whale|||ReefWormWhaleToken| |Generate|TOK:C14|Wolf|||WolfToken| -|Generate|TOK:C14|Wurm|1||Wurm1Token| -|Generate|TOK:C14|Wurm|2||Wurm2Token| +|Generate|TOK:C14|Wurm|1||WurmWithDeathtouchToken| +|Generate|TOK:C14|Wurm|2||WurmWithLifelinkToken| |Generate|TOK:C14|Zombie|1||ZombieToken| |Generate|TOK:C14|Zombie|2||StitcherGeralfZombieToken| |Generate|TOK:C15|Angel|||AngelToken| @@ -348,7 +361,7 @@ |Generate|TOK:C16|Beast|| |Generate|TOK:C16|Bird|1| |Generate|TOK:C16|Bird|2| -|Generate|TOK:C16|Elemental|| +|Generate|TOK:C16|Elemental|||WhiteElementalToken| |Generate|TOK:C16|Elf Warrior|| |Generate|TOK:C16|Germ|| |Generate|TOK:C16|Goat|| @@ -366,7 +379,7 @@ |Generate|TOK:C16|Worm|| |Generate|TOK:C16|Zombie|| |Generate|TOK:C17|Bat|| -|Generate|TOK:C17|Cat|| +|Generate|TOK:C17|Cat|||CatToken| |Generate|TOK:C17|Cat Dragon|||WasitoraCatDragonToken| |Generate|TOK:C17|Cat Warrior|| |Generate|TOK:C17|Dragon|1||DragonToken| @@ -381,7 +394,7 @@ |Generate|TOK:C18|Cat|||CatToken| |Generate|TOK:C18|Cat Warrior|||CatWarriorToken| |Generate|TOK:C18|Clue|||ClueArtifactToken| -|Generate|TOK:C18|Construct|1||RetrofitterFoundryToken| +|Generate|TOK:C18|Construct|1||Construct4Token| |Generate|TOK:C18|Construct|2||StoneTrapIdolToken| |Generate|TOK:C18|Dragon|||DragonEggDragonToken| |Generate|TOK:C18|Dragon Egg|||NestingDragonToken| @@ -444,8 +457,8 @@ |Generate|TOK:CMA|Treefolk|||SylvanOfferingTreefolkToken| |Generate|TOK:CMA|Wolf|||WolfToken| |Generate|TOK:CMA|Zombie|||ZombieToken| -|Generate|TOK:CMD|Beast|||BeastToken2| -|Generate|TOK:CMD|Beast|||BeastToken| +|Generate|TOK:CMD|Beast|1||BeastToken2| +|Generate|TOK:CMD|Beast|2||BeastToken| |Generate|TOK:CMD|Dragon|||DragonToken2| |Generate|TOK:CMD|Eldrazi Spawn|||EldraziSpawnToken| |Generate|TOK:CMD|Elemental|||CallTheSkyBreakerElementalToken| @@ -564,11 +577,11 @@ |Generate|TOK:DGM|Rhino|||RhinoToken| |Generate|TOK:DGM|Soldier|||SoldierTokenWithHaste| |Generate|TOK:DGM|Spirit|||TeysaEnvoyOfGhostsToken| -|Generate|TOK:DGM|Wurm|||WurmToken2| -|Generate|TOK:DGM|Wurm|||WurmToken3| +|Generate|TOK:DGM|Wurm|1||WurmWithTrampleToken| +|Generate|TOK:DGM|Wurm|2||Wurm55Token| |Generate|TOK:DIS|Bird|||DovescapeToken| |Generate|TOK:DIS|Drake|||LeafdrakeRoostDrakeToken| -|Generate|TOK:DIS|Elemental|||ElementalToken| +|Generate|TOK:DIS|Elemental|||ResearchDevelopmentToken| |Generate|TOK:DIS|Goblin|||RakdosGuildmageGoblinToken| |Generate|TOK:DIS|Saproling|||SaprolingToken| |Generate|TOK:DIS|Snake|||PatagiaViperSnakeToken| @@ -616,7 +629,7 @@ |Generate|TOK:EMA|Beast|||CarnivoreToken| |Generate|TOK:EMA|Carnivore|| |Generate|TOK:EMA|Dragon|||DragonEggDragonToken| -|Generate|TOK:EMA|Elemental|1||YoungPyromancerElementalToken| +|Generate|TOK:EMA|Elemental|1||RedElementalToken| |Generate|TOK:EMA|Elemental|2||CallTheSkyBreakerElementalToken| |Generate|TOK:EMA|Elephant|||ElephantToken| |Generate|TOK:EMA|Elf Warrior|||ElfToken| @@ -652,7 +665,7 @@ |Generate|TOK:EVE|Spirit|||BeckonApparitionToken| |Generate|TOK:EVE|Wolf|||WolfToken| |Generate|TOK:EVE|Worm|||WormHarvestToken| -|Generate|TOK:EVG|Elemental|| +|Generate|TOK:EVG|Elemental|||VoiceOfTheWoodsElementalToken| |Generate|TOK:EVG|Elf Warrior|||ElfToken| |Generate|TOK:EVG|Goblin|||GoblinToken| |Generate|TOK:EXO|Pegasus|||PegasusToken| @@ -788,8 +801,8 @@ |Generate|TOK:LRW|Avatar|||AvatarToken| |Generate|TOK:LRW|Beast|||BeastToken| |Generate|TOK:LRW|Elemental Shaman|||ElementalShamanToken| -|Generate|TOK:LRW|Elemental|||WalkerOfTheGroveToken| -|Generate|TOK:LRW|Elemental|||WhiteElementalToken| +|Generate|TOK:LRW|Elemental|1||WalkerOfTheGroveToken| +|Generate|TOK:LRW|Elemental|2||WhiteElementalToken| |Generate|TOK:LRW|Elf Warrior|||ElfToken| |Generate|TOK:LRW|Goblin Rogue|||GoblinRogueToken| |Generate|TOK:LRW|Kithkin Soldier|||KithkinToken| @@ -831,8 +844,8 @@ |Generate|TOK:M14|Beast|||BeastToken| |Generate|TOK:M14|Cat|||CatToken| |Generate|TOK:M14|Dragon|||DragonEggDragonToken| -|Generate|TOK:M14|Elemental|1||YoungPyromancerElementalToken| -|Generate|TOK:M14|Elemental|2||YoungPyromancerElementalToken| +|Generate|TOK:M14|Elemental|1||RedElementalToken| +|Generate|TOK:M14|Elemental|2||RedElementalToken| |Generate|TOK:M14|Goat|||GoatToken| |Generate|TOK:M14|Saproling|||SaprolingToken| |Generate|TOK:M14|Sliver|| @@ -906,7 +919,7 @@ |Generate|TOK:MM3|Bird|| |Generate|TOK:MM3|Centaur|| |Generate|TOK:MM3|Dragon|| -|Generate|TOK:MM3|Elemental|| +|Generate|TOK:MM3|Elemental|||VoiceOfResurgenceToken| |Generate|TOK:MM3|Elephant|| |Generate|TOK:MM3|Giant Warrior|| |Generate|TOK:MM3|Goblin Warrior|| @@ -953,7 +966,7 @@ |Generate|TOK:MOR|Wolf|||WolfToken| |Generate|TOK:MRD|Beast|||OneDozenEyesBeastToken| |Generate|TOK:MRD|Demon|||ReignOfThePitToken| -|Generate|TOK:MRD|Elemental|||ElementalToken| +|Generate|TOK:MRD|Elemental|||ElementalTokenWithHaste| |Generate|TOK:MRD|Insect|||InsectToken| |Generate|TOK:MRD|Myr|||MyrToken| |Generate|TOK:MRD|Pentavite|||PentaviteToken| @@ -962,8 +975,8 @@ |Generate|TOK:MRD|Spirit|||SpiritWhiteToken| |Generate|TOK:NEM|Angel|||AngelToken| |Generate|TOK:NEM|Goblin|||GoblinToken| -|Generate|TOK:NEM|Saproling|||SaprolingBurstToken| -|Generate|TOK:NEM|Saproling|||SaprolingToken| +|Generate|TOK:NEM|Saproling|1||SaprolingBurstToken| +|Generate|TOK:NEM|Saproling|2||SaprolingToken| |Generate|TOK:NPH|Beast|||BeastToken| |Generate|TOK:NPH|Germ|||GermToken| |Generate|TOK:NPH|Goblin|||GoblinTokenWithHaste| @@ -987,7 +1000,7 @@ |Generate|TOK:OGW|Eldrazi Scion|5||EldraziScionToken| |Generate|TOK:OGW|Eldrazi Scion|6||EldraziScionToken| |Generate|TOK:OGW|Elemental|1||SeedGuardianToken| -|Generate|TOK:OGW|Elemental|2||ElementalToken| +|Generate|TOK:OGW|Elemental|2||ElementalTokenWithHaste| |Generate|TOK:OGW|Knight Ally|||KnightAllyToken| |Generate|TOK:OGW|Kor Ally|||KorAllyToken| |Generate|TOK:OGW|Octopus|||OctopusToken| @@ -1000,7 +1013,6 @@ |Generate|TOK:ONS|Soldier|||SoldierToken| |Generate|TOK:ONS|Zombie|||ZombieToken| |Generate|TOK:ORI|Angel|||AngelToken| -|Generate|TOK:ORI|Angel|||AngelTombToken| |Generate|TOK:ORI|Ashaya, the Awoken World|||NissaSageAnimistToken| |Generate|TOK:ORI|Demon|||DemonToken| |Generate|TOK:ORI|Elemental|||ZendikarsRoilElementalToken| @@ -1060,21 +1072,21 @@ |Generate|TOK:RTR|Bird|||BirdToken| |Generate|TOK:RTR|Centaur|||CentaurToken| |Generate|TOK:RTR|Dragon|||UtvaraHellkiteDragonToken| -|Generate|TOK:RTR|Elemental|| +|Generate|TOK:RTR|Elemental|||GreenAndWhiteElementalToken| |Generate|TOK:RTR|Goblin|||GoblinToken| |Generate|TOK:RTR|Knight|||KnightToken| |Generate|TOK:RTR|Ooze|||MysticGenesisOozeToken| |Generate|TOK:RTR|Rhino|||RhinoToken| |Generate|TOK:RTR|Saproling|||SaprolingToken| |Generate|TOK:RTR|Soldier|||SoldierToken| -|Generate|TOK:RTR|Wurm|||WurmToken2| +|Generate|TOK:RTR|Wurm|||WurmWithTrampleToken| |Generate|TOK:SCG|Angel|||AngelToken| |Generate|TOK:SCG|Beast|||BeastToken2| |Generate|TOK:SCG|Dragon|||DragonToken2| |Generate|TOK:SCG|Goblin|||GoblinToken| |Generate|TOK:SCG|Soldier|||SoldierToken| |Generate|TOK:SHM|Elemental|1||DinOfTheFireherdToken| -|Generate|TOK:SHM|Elemental|2||YoungPyromancerElementalToken| +|Generate|TOK:SHM|Elemental|2||RedElementalToken| |Generate|TOK:SHM|Elf Warrior|1| |Generate|TOK:SHM|Elf Warrior|2| |Generate|TOK:SHM|Elf Warrior|||ElfToken| @@ -1103,7 +1115,7 @@ |Generate|TOK:SOI|Vampire Knight|||VampireKnightToken| |Generate|TOK:SOI|Wolf|||WolfToken| |Generate|TOK:SOI|Zombie|||ZombieToken| -|Generate|TOK:SOK|Elemental|||ElementalToken| +|Generate|TOK:SOK|Elemental|||ElementalTokenWithHaste| |Generate|TOK:SOK|Snake|||SnakeToken| |Generate|TOK:SOK|Spirit|||SpiritToken| |Generate|TOK:SOK|Urami|||UramiToken| @@ -1115,8 +1127,8 @@ |Generate|TOK:SOM|Myr|||MyrToken| |Generate|TOK:SOM|Soldier|||SoldierToken| |Generate|TOK:SOM|Wolf|||WolfToken| -|Generate|TOK:SOM|Wurm|1||Wurm1Token| -|Generate|TOK:SOM|Wurm|2||Wurm2Token| +|Generate|TOK:SOM|Wurm|1||WurmWithDeathtouchToken| +|Generate|TOK:SOM|Wurm|2||WurmWithLifelinkToken| |Generate|TOK:STH|Goblin|||GoblinToken| |Generate|TOK:STH|Insect|||HornetToken| |Generate|TOK:STH|Insect|||WaspToken| @@ -1140,11 +1152,11 @@ |Generate|TOK:THS|Golem|||HammerOfPurphorosGolemToken| |Generate|TOK:THS|Harpy|||AbhorrentOverlordHarpyToken| |Generate|TOK:THS|Satyr|||XenagosSatyrToken| -|Generate|TOK:THS|Soldier|||AkroanSoldierToken| |Generate|TOK:THS|Soldier|1||SoldierToken| |Generate|TOK:THS|Soldier|2||SoldierToken| +|Generate|TOK:THS|Soldier|3||AkroanSoldierToken| |Generate|TOK:TMP|Beast|||CarnivoreToken| -|Generate|TOK:TMP|Hound|||HoundToken| +|Generate|TOK:TMP|Dog|||GreenDogToken| |Generate|TOK:TMP|Pegasus|||PegasusToken| |Generate|TOK:TMP|Reflection|||ReflectionToken| |Generate|TOK:TMP|Saproling|||SaprolingToken| @@ -1205,14 +1217,14 @@ |Generate|TOK:VMA|Goblin|||GoblinToken| |Generate|TOK:VMA|Insect|||InsectToken| |Generate|TOK:VMA|Reflection|||ReflectionToken| -|Generate|TOK:VMA|Saproling|||SaprolingBurstToken| -|Generate|TOK:VMA|Saproling|||SaprolingToken| +|Generate|TOK:VMA|Saproling|1||SaprolingBurstToken| +|Generate|TOK:VMA|Saproling|2||SaprolingToken| |Generate|TOK:VMA|Soldier|||SoldierToken| |Generate|TOK:VMA|Spirit|||SpiritWhiteToken| |Generate|TOK:VMA|Squirrel|||SquirrelToken| |Generate|TOK:VMA|Thopter|||ThopterColorlessToken| -|Generate|TOK:VMA|Wurm|||PenumbraWurmToken| -|Generate|TOK:VMA|Wurm|||WurmToken| +|Generate|TOK:VMA|Wurm|1||PenumbraWurmToken| +|Generate|TOK:VMA|Wurm|2||WurmToken| |Generate|TOK:VMA|Zombie|||ZombieToken| |Generate|TOK:WTH|Squirrel|||SquirrelToken| |Generate|TOK:WWK|Construct|||StoneTrapIdolToken| @@ -1236,8 +1248,8 @@ |Generate|TOK:ZEN|Angel|||AngelToken| |Generate|TOK:ZEN|Beast|||BeastToken2| |Generate|TOK:ZEN|Bird|||BirdToken| -|Generate|TOK:ZEN|Elemental|| -|Generate|TOK:ZEN|Elemental|||ZektarShrineElementalToken| +|Generate|TOK:ZEN|Elemental|1||RedElementalWithTrampleAndHaste| +|Generate|TOK:ZEN|Elemental|2||RedElementalWithTrampleAndHaste| |Generate|TOK:ZEN|Illusion|||IllusionToken| |Generate|TOK:ZEN|Kor Soldier|||KorSoldierToken| |Generate|TOK:ZEN|Merfolk|| @@ -1266,6 +1278,7 @@ |Generate|TOK:GRN|Soldier|||SoldierLifelinkToken| |Generate|TOK:WAR|Angel|||AngelVigilanceToken| |Generate|TOK:WAR|Assassin|||AssassinToken2| +|Generate|TOK:WAR|Citizen|||PlanewideCelebrationToken| |Generate|TOK:WAR|Devil|||DevilToken| |Generate|TOK:WAR|Dragon|||DragonToken| |Generate|TOK:WAR|Goblin|||GoblinToken| @@ -1286,7 +1299,7 @@ |Generate|TOK:MH1|Bear|||BearToken| |Generate|TOK:MH1|Bird|||BirdToken| |Generate|TOK:MH1|Construct|||KarnConstructToken| -|Generate|TOK:MH1|Elemental|1||YoungPyromancerElementalToken| +|Generate|TOK:MH1|Elemental|1||RedElementalToken| |Generate|TOK:MH1|Elemental|2||AkoumStonewakerElementalToken| |Generate|TOK:MH1|Elephant|||ElephantToken| |Generate|TOK:MH1|Goblin|||GoblinToken| @@ -1316,7 +1329,7 @@ |Generate|TOK:M19|Zombie|||ZombieToken| |Generate|TOK:M20|Ajani's Pridemate|||AjanisPridemateToken| |Generate|TOK:M20|Demon|||DemonToken| -|Generate|TOK:M20|Elemental|||YoungPyromancerElementalToken| +|Generate|TOK:M20|Elemental|||RedElementalToken| |Generate|TOK:M20|Elemental Bird|||MuYanlingSkyDancerToken| |Generate|TOK:M20|Golem|||GolemToken| |Generate|TOK:M20|Soldier|||SoldierToken| @@ -1324,6 +1337,8 @@ |Generate|TOK:M20|Treasure|||TreasureToken| |Generate|TOK:M20|Wolf|||WolfToken| |Generate|TOK:M20|Zombie|||ZombieToken| + +# ELD |Generate|TOK:ELD|Bear|||BearToken| |Generate|TOK:ELD|Boar|||WolfsQuarryToken| |Generate|TOK:ELD|Dwarf|||DwarfToken| @@ -1342,6 +1357,8 @@ |Generate|TOK:ELD|Mouse|||MouseToken| |Generate|TOK:ELD|Rat|||RatToken| |Generate|TOK:ELD|Wolf|||GarrukCursedHuntsmanToken| + +# THB |Generate|TOK:THB|Goat|||GoatToken| |Generate|TOK:THB|Human Soldier|||HumanSoldierToken| |Generate|TOK:THB|Pegasus|||PegasusToken2| @@ -1354,5 +1371,63 @@ |Generate|TOK:THB|Spider|||SpiderToken| |Generate|TOK:THB|Wolf|||WolfToken| |Generate|TOK:THB|Nightmare|||AshiokNightmareMuseToken| -|Generate|TOK:THB|GoldToken|||Gold| -|Generate|TOK:THB|ArtifactWallToken|||Wall| \ No newline at end of file +|Generate|TOK:THB|Gold|||GoldToken| +|Generate|TOK:THB|Wall|||ArtifactWallToken| + +# IKO +|Generate|TOK:IKO|Beast|||BeastToken| +|Generate|TOK:IKO|Cat Bird|||CatBirdToken| +|Generate|TOK:IKO|Cat|||CatToken2| +|Generate|TOK:IKO|Dinosaur Beast|||DinosaurBeastToken| +|Generate|TOK:IKO|Dinosaur|||DinosaurHasteToken| +|Generate|TOK:IKO|Feather|||FeatherToken| +|Generate|TOK:IKO|Human Soldier|1||HumanSoldierToken| +|Generate|TOK:IKO|Human Soldier|2||HumanSoldierToken| +|Generate|TOK:IKO|Human Soldier|3||HumanSoldierToken| +|Generate|TOK:IKO|Kraken|||KrakenToken| +|Generate|TOK:IKO|Shark|||SharkToken| + +# C20 +|Generate|TOK:C20|Angel|||AngelToken| +|Generate|TOK:C20|Beast|||BeastToken2| +|Generate|TOK:C20|Bird|||BirdToken| +|Generate|TOK:C20|Bird Illusion|||BirdIllusionToken| +|Generate|TOK:C20|Dinosaur Cat|||DinosaurCatToken| +|Generate|TOK:C20|Drake|||DrakeToken| +|Generate|TOK:C20|Elemental|1||ElementalTokenWithHaste| +|Generate|TOK:C20|Elemental|2||WhiteElementalToken| +|Generate|TOK:C20|Goblin Warrior|||GoblinWarriorToken| +|Generate|TOK:C20|Human|||HumanToken| +|Generate|TOK:C20|Hydra|||ZaxaraTheExemplaryHydraToken| +|Generate|TOK:C20|Insect|1||HornetQueenInsectToken| +|Generate|TOK:C20|Insect|2||TheLocustGodInsectToken| +|Generate|TOK:C20|Saproling|||SaprolingToken| +|Generate|TOK:C20|Snake|||SnakeToken| +|Generate|TOK:C20|Soldier|||SoldierToken| +|Generate|TOK:C20|Spirit|||SpiritWhiteToken| +|Generate|TOK:C20|Treasure|||TreasureToken| +|Generate|TOK:C20|Zombie|||ZombieToken| + +# M21 +|Generate|TOK:M21|Angel|||AngelToken| +|Generate|TOK:M21|Beast|||BeastToken| +|Generate|TOK:M21|Bird|||BirdToken| +|Generate|TOK:M21|Cat|1||GreenCatToken| +|Generate|TOK:M21|Cat|2||GreenCat2Token| +|Generate|TOK:M21|Construct|||Construct4Token| +|Generate|TOK:M21|Demon|||DemonToken| +|Generate|TOK:M21|Dog|||WhiteDogToken| +|Generate|TOK:M21|Goblin Wizard|||GoblinWizardToken| +|Generate|TOK:M21|Griffin|||GriffinToken| +|Generate|TOK:M21|Knight|||KnightToken| +|Generate|TOK:M21|Pirate|||PursuedWhaleToken| +|Generate|TOK:M21|Saproling|||SaprolingToken| +|Generate|TOK:M21|Soldier|||SoldierToken| +|Generate|TOK:M21|Treasure|||TreasureToken| +|Generate|TOK:M21|Weird|||WeirdToken2| +|Generate|TOK:M21|Zombie|||ZombieToken| + +# JMP +# Jumpstart uses tokens and emblems from M21 set, +# TODO: check scryfall for JMP tokens after set's release +|Generate|TOK:JMP|Unicorn|||UnicornToken| \ No newline at end of file diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_cleanup.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_cleanup.png new file mode 100644 index 0000000000..1216dc01b3 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_cleanup.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_attack.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_attack.png new file mode 100644 index 0000000000..47ef061ca1 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_attack.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_block.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_block.png new file mode 100644 index 0000000000..42138c22ca Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_block.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_damage.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_damage.png new file mode 100644 index 0000000000..5e13e818a1 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_damage.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_end.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_end.png new file mode 100644 index 0000000000..8bf8f72840 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_end.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_start.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_start.png new file mode 100644 index 0000000000..8e590bd3d4 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_combat_start.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_draw.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_draw.png new file mode 100644 index 0000000000..a4a6883462 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_draw.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_main1.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_main1.png new file mode 100644 index 0000000000..ec11524c0f Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_main1.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_main2.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_main2.png new file mode 100644 index 0000000000..71ed702e17 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_main2.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_next_turn.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_next_turn.png new file mode 100644 index 0000000000..c768643f98 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_next_turn.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_untap.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_untap.png new file mode 100644 index 0000000000..51d0bff648 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_untap.png differ diff --git a/Mage.Client/src/main/resources/phases/16bit-theme/phase_upkeep.png b/Mage.Client/src/main/resources/phases/16bit-theme/phase_upkeep.png new file mode 100644 index 0000000000..19098bb92d Binary files /dev/null and b/Mage.Client/src/main/resources/phases/16bit-theme/phase_upkeep.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_cleanup.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_cleanup.png new file mode 100644 index 0000000000..d586c5337a Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_cleanup.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_attack.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_attack.png new file mode 100644 index 0000000000..c2ba8aeddf Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_attack.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_block.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_block.png new file mode 100644 index 0000000000..15b6a56917 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_block.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_damage.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_damage.png new file mode 100644 index 0000000000..7db4831a83 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_damage.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_end.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_end.png new file mode 100644 index 0000000000..0eaa82979d Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_end.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_start.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_start.png new file mode 100644 index 0000000000..78d0048554 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_combat_start.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_draw.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_draw.png new file mode 100644 index 0000000000..30e2574f65 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_draw.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_main1.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_main1.png new file mode 100644 index 0000000000..7d6af3f2cf Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_main1.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_main2.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_main2.png new file mode 100644 index 0000000000..26bc6f51d5 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_main2.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_next_turn.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_next_turn.png new file mode 100644 index 0000000000..c768643f98 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_next_turn.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_untap.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_untap.png new file mode 100644 index 0000000000..bae633681d Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_untap.png differ diff --git a/Mage.Client/src/main/resources/phases/coffee-theme/phase_upkeep.png b/Mage.Client/src/main/resources/phases/coffee-theme/phase_upkeep.png new file mode 100644 index 0000000000..de91d28293 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/coffee-theme/phase_upkeep.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_cleanup.png b/Mage.Client/src/main/resources/phases/island-theme/phase_cleanup.png new file mode 100644 index 0000000000..82d8ef82fa Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_cleanup.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_combat_attack.png b/Mage.Client/src/main/resources/phases/island-theme/phase_combat_attack.png new file mode 100644 index 0000000000..7b56f796ff Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_combat_attack.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_combat_block.png b/Mage.Client/src/main/resources/phases/island-theme/phase_combat_block.png new file mode 100644 index 0000000000..824b48556d Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_combat_block.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_combat_damage.png b/Mage.Client/src/main/resources/phases/island-theme/phase_combat_damage.png new file mode 100644 index 0000000000..c614cff42d Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_combat_damage.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_combat_end.png b/Mage.Client/src/main/resources/phases/island-theme/phase_combat_end.png new file mode 100644 index 0000000000..83d3cf6dea Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_combat_end.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_combat_start.png b/Mage.Client/src/main/resources/phases/island-theme/phase_combat_start.png new file mode 100644 index 0000000000..d9bf358c18 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_combat_start.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_draw.png b/Mage.Client/src/main/resources/phases/island-theme/phase_draw.png new file mode 100644 index 0000000000..2ab62ebb54 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_draw.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_main1.png b/Mage.Client/src/main/resources/phases/island-theme/phase_main1.png new file mode 100644 index 0000000000..e890b9980d Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_main1.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_main2.png b/Mage.Client/src/main/resources/phases/island-theme/phase_main2.png new file mode 100644 index 0000000000..efa04bd865 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_main2.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_next_turn.png b/Mage.Client/src/main/resources/phases/island-theme/phase_next_turn.png new file mode 100644 index 0000000000..c768643f98 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_next_turn.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_untap.png b/Mage.Client/src/main/resources/phases/island-theme/phase_untap.png new file mode 100644 index 0000000000..1ee0899960 Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_untap.png differ diff --git a/Mage.Client/src/main/resources/phases/island-theme/phase_upkeep.png b/Mage.Client/src/main/resources/phases/island-theme/phase_upkeep.png new file mode 100644 index 0000000000..ee95b1cf1c Binary files /dev/null and b/Mage.Client/src/main/resources/phases/island-theme/phase_upkeep.png differ diff --git a/Mage.Client/src/main/resources/winloss/16bit-theme/game_lost.jpg b/Mage.Client/src/main/resources/winloss/16bit-theme/game_lost.jpg new file mode 100644 index 0000000000..a068f73a83 Binary files /dev/null and b/Mage.Client/src/main/resources/winloss/16bit-theme/game_lost.jpg differ diff --git a/Mage.Client/src/main/resources/winloss/16bit-theme/game_won.jpg b/Mage.Client/src/main/resources/winloss/16bit-theme/game_won.jpg new file mode 100644 index 0000000000..fe57590501 Binary files /dev/null and b/Mage.Client/src/main/resources/winloss/16bit-theme/game_won.jpg differ diff --git a/Mage.Client/src/main/resources/winloss/coffee-theme/game_lost.jpg b/Mage.Client/src/main/resources/winloss/coffee-theme/game_lost.jpg new file mode 100644 index 0000000000..c578b7ea87 Binary files /dev/null and b/Mage.Client/src/main/resources/winloss/coffee-theme/game_lost.jpg differ diff --git a/Mage.Client/src/main/resources/winloss/coffee-theme/game_won.jpg b/Mage.Client/src/main/resources/winloss/coffee-theme/game_won.jpg new file mode 100644 index 0000000000..4ab9705c0a Binary files /dev/null and b/Mage.Client/src/main/resources/winloss/coffee-theme/game_won.jpg differ diff --git a/Mage.Client/src/main/resources/game_lost.jpg b/Mage.Client/src/main/resources/winloss/game_lost.jpg similarity index 100% rename from Mage.Client/src/main/resources/game_lost.jpg rename to Mage.Client/src/main/resources/winloss/game_lost.jpg diff --git a/Mage.Client/src/main/resources/game_won.jpg b/Mage.Client/src/main/resources/winloss/game_won.jpg similarity index 100% rename from Mage.Client/src/main/resources/game_won.jpg rename to Mage.Client/src/main/resources/winloss/game_won.jpg diff --git a/Mage.Client/src/main/resources/winloss/grey-theme/game_lost.jpg b/Mage.Client/src/main/resources/winloss/grey-theme/game_lost.jpg new file mode 100644 index 0000000000..49f4dd8216 Binary files /dev/null and b/Mage.Client/src/main/resources/winloss/grey-theme/game_lost.jpg differ diff --git a/Mage.Client/src/main/resources/winloss/grey-theme/game_won.jpg b/Mage.Client/src/main/resources/winloss/grey-theme/game_won.jpg new file mode 100644 index 0000000000..aca845cb1d Binary files /dev/null and b/Mage.Client/src/main/resources/winloss/grey-theme/game_won.jpg differ diff --git a/Mage.Client/src/main/resources/winloss/island-theme/game_lost.jpg b/Mage.Client/src/main/resources/winloss/island-theme/game_lost.jpg new file mode 100644 index 0000000000..ac71ca0ce0 Binary files /dev/null and b/Mage.Client/src/main/resources/winloss/island-theme/game_lost.jpg differ diff --git a/Mage.Client/src/main/resources/winloss/island-theme/game_won.jpg b/Mage.Client/src/main/resources/winloss/island-theme/game_won.jpg new file mode 100644 index 0000000000..18bdfcfcbe Binary files /dev/null and b/Mage.Client/src/main/resources/winloss/island-theme/game_won.jpg differ diff --git a/Mage.Common/pom.xml b/Mage.Common/pom.xml index 79575ccf25..4ff57dcf30 100644 --- a/Mage.Common/pom.xml +++ b/Mage.Common/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 mage-common @@ -54,7 +54,7 @@ com.google.code.gson gson - 2.8.5 + 2.8.6 diff --git a/Mage.Common/src/main/java/mage/cards/CardBorder.java b/Mage.Common/src/main/java/mage/cards/CardBorder.java index 17f6cd8c27..6ae348313a 100644 --- a/Mage.Common/src/main/java/mage/cards/CardBorder.java +++ b/Mage.Common/src/main/java/mage/cards/CardBorder.java @@ -1,28 +1,28 @@ - -package mage.cards; - -/** - * @author stravant@gmail.com - * - * Enum listing the possible card faces for a card - * - * Because of Time Spiral block's shifted cards it is - * not sufficient to just look at a card's edition to - * determine what the card face should be. - */ -public enum CardBorder { - /* Old border card frames. ALPHA -> 8th ED */ - OLD, - - /* Future Sight frames. FUT futureshifted */ - FUT, - - /* Planar Chaos frames. PLC planeshifted */ - PLC, - - /* Modern card frames. 8th ED -> M15 */ - MOD, - - /* New border cards, M15 -> current */ - M15 -} + +package mage.cards; + +/** + * @author stravant@gmail.com + * + * Enum listing the possible card faces for a card + * + * Because of Time Spiral block's shifted cards it is + * not sufficient to just look at a card's edition to + * determine what the card face should be. + */ +public enum CardBorder { + /* Old border card frames. ALPHA -> 8th ED */ + OLD, + + /* Future Sight frames. FUT futureshifted */ + FUT, + + /* Planar Chaos frames. PLC planeshifted */ + PLC, + + /* Modern card frames. 8th ED -> M15 */ + MOD, + + /* New border cards, M15 -> current */ + M15 +} diff --git a/Mage.Common/src/main/java/mage/filters/MageBufferedImageOp.java b/Mage.Common/src/main/java/mage/filters/MageBufferedImageOp.java index 32a12c6928..f6adc23d75 100644 --- a/Mage.Common/src/main/java/mage/filters/MageBufferedImageOp.java +++ b/Mage.Common/src/main/java/mage/filters/MageBufferedImageOp.java @@ -18,6 +18,7 @@ public abstract class MageBufferedImageOp implements BufferedImageOp { /** * Creates compatible image for @param src image. */ + @Override public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dest) { if (dest == null) { dest = src.getColorModel(); @@ -25,14 +26,17 @@ public abstract class MageBufferedImageOp implements BufferedImageOp { return new BufferedImage(dest, dest.createCompatibleWritableRaster(src.getWidth(), src.getHeight()), dest.isAlphaPremultiplied(), null); } + @Override public RenderingHints getRenderingHints() { return null; } + @Override public Rectangle2D getBounds2D(BufferedImage src) { return new Rectangle(0, 0, src.getWidth(), src.getHeight()); } + @Override public Point2D getPoint2D(Point2D srcPt, Point2D destPt) { if (destPt == null) { destPt = new Point2D.Double(); diff --git a/Mage.Common/src/main/java/mage/interfaces/MageServer.java b/Mage.Common/src/main/java/mage/interfaces/MageServer.java index 52c95550c5..059f21ce63 100644 --- a/Mage.Common/src/main/java/mage/interfaces/MageServer.java +++ b/Mage.Common/src/main/java/mage/interfaces/MageServer.java @@ -16,7 +16,6 @@ import mage.utils.MageVersion; import mage.view.*; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -84,7 +83,7 @@ public interface MageServer { boolean isTableOwner(String sessionId, UUID roomId, UUID tableId) throws MageException; - Optional getTable(UUID roomId, UUID tableId) throws MageException; + TableView getTable(UUID roomId, UUID tableId) throws MageException; List getTables(UUID roomId) throws MageException; @@ -95,13 +94,13 @@ public interface MageServer { void leaveChat(UUID chatId, String sessionId) throws MageException; - Optional getTableChatId(UUID tableId) throws MageException; + UUID getTableChatId(UUID tableId) throws MageException; - Optional getGameChatId(UUID gameId) throws MageException; + UUID getGameChatId(UUID gameId) throws MageException; - Optional getRoomChatId(UUID roomId) throws MageException; + UUID getRoomChatId(UUID roomId) throws MageException; - Optional getTournamentChatId(UUID tournamentId) throws MageException; + UUID getTournamentChatId(UUID tournamentId) throws MageException; //room methods UUID getMainRoomId() throws MageException; diff --git a/Mage.Common/src/main/java/mage/remote/Connection.java b/Mage.Common/src/main/java/mage/remote/Connection.java index ecc7dd4416..f3bf4296ee 100644 --- a/Mage.Common/src/main/java/mage/remote/Connection.java +++ b/Mage.Common/src/main/java/mage/remote/Connection.java @@ -42,6 +42,7 @@ public class Connection { // private UserSkipPrioritySteps userSkipPrioritySteps; private static final String serialization = "?serializationtype=jboss"; private static final String transport = "bisocket"; + private static final String threadpool = "onewayThreadPool=mage.remote.CustomThreadPool"; private final String parameter; @@ -78,13 +79,13 @@ public class Connection { try { InetAddress inet = getLocalAddress(); if (inet != null) { - return transport + "://" + inet.getHostAddress() + ':' + port + '/' + serialization + parameter; + return transport + "://" + inet.getHostAddress() + ':' + port + '/' + serialization + "&" + threadpool + parameter; } } catch (SocketException ex) { // just use localhost if can't find local ip } } - return transport + "://" + host + ':' + port + '/' + serialization + parameter; + return transport + "://" + host + ':' + port + '/' + serialization + "&" + threadpool + parameter; } public ProxyType getProxyType() { diff --git a/Mage.Common/src/main/java/mage/remote/CustomThreadPool.java b/Mage.Common/src/main/java/mage/remote/CustomThreadPool.java new file mode 100644 index 0000000000..456f47bcd7 --- /dev/null +++ b/Mage.Common/src/main/java/mage/remote/CustomThreadPool.java @@ -0,0 +1,32 @@ +package mage.remote; + +import java.lang.reflect.Field; +import java.util.concurrent.ThreadPoolExecutor; + +import org.apache.log4j.Logger; +import org.jboss.util.threadpool.BasicThreadPool; + +public class CustomThreadPool extends BasicThreadPool { + private static final Logger logger = Logger.getLogger(SessionImpl.class); + + @Override + public void setMaximumPoolSize(int size) { + /* + * I really don't want to implement a whole new threadpool + * just to fix this and the executor is private + */ + try { + Field executorField = BasicThreadPool.class.getField("executor"); + executorField.setAccessible(true); + ThreadPoolExecutor executor = (ThreadPoolExecutor) executorField.get(this); + synchronized (executor) { + executor.setMaximumPoolSize(size); + executor.setCorePoolSize(size); + } + } catch (NoSuchFieldException | SecurityException e) { + logger.error("Failed to get field executor from BasicThreadPool", e); + } catch (IllegalArgumentException | IllegalAccessException e) { + logger.error("Failed to get executor object from BasicThreadPool", e); + } + } +} \ No newline at end of file diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java index dd073af9a9..861ad752ec 100644 --- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java +++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java @@ -647,7 +647,7 @@ public class SessionImpl implements Session { public Optional getRoomChatId(UUID roomId) { try { if (isConnected()) { - return server.getRoomChatId(roomId); + return Optional.of(server.getRoomChatId(roomId)); } } catch (MageException ex) { handleMageException(ex); @@ -659,7 +659,7 @@ public class SessionImpl implements Session { public Optional getTableChatId(UUID tableId) { try { if (isConnected()) { - return server.getTableChatId(tableId); + return Optional.of(server.getTableChatId(tableId)); } } catch (MageException ex) { handleMageException(ex); @@ -671,7 +671,7 @@ public class SessionImpl implements Session { public Optional getGameChatId(UUID gameId) { try { if (isConnected()) { - return server.getGameChatId(gameId); + return Optional.of(server.getGameChatId(gameId)); } } catch (MageException ex) { handleMageException(ex); @@ -685,7 +685,7 @@ public class SessionImpl implements Session { public Optional getTable(UUID roomId, UUID tableId) { try { if (isConnected()) { - return server.getTable(roomId, tableId); + return Optional.of(server.getTable(roomId, tableId)); } } catch (MageException ex) { handleMageException(ex); @@ -829,7 +829,7 @@ public class SessionImpl implements Session { public Optional getTournamentChatId(UUID tournamentId) { try { if (isConnected()) { - return server.getTournamentChatId(tournamentId); + return Optional.of(server.getTournamentChatId(tournamentId)); } } catch (MageException ex) { handleMageException(ex); diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index 100dbdf680..7eb4323f50 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -11,9 +11,9 @@ public class MageVersion implements Serializable, Comparable { public static final int MAGE_VERSION_MAJOR = 1; public static final int MAGE_VERSION_MINOR = 4; - public static final int MAGE_VERSION_PATCH = 42; + public static final int MAGE_VERSION_PATCH = 44; public static final String MAGE_EDITION_INFO = ""; // set "-beta2" for 1.4.32V1-beta2 - public static final String MAGE_VERSION_MINOR_PATCH = "V6"; // default + public static final String MAGE_VERSION_MINOR_PATCH = "V0"; // default // strict mode private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = true; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes) diff --git a/Mage.Common/src/main/java/mage/view/AbilityPickerView.java b/Mage.Common/src/main/java/mage/view/AbilityPickerView.java index 8ea265aee0..d4ae6a0c23 100644 --- a/Mage.Common/src/main/java/mage/view/AbilityPickerView.java +++ b/Mage.Common/src/main/java/mage/view/AbilityPickerView.java @@ -1,6 +1,7 @@ package mage.view; import mage.abilities.Ability; +import mage.abilities.SpellAbility; import java.io.Serializable; import java.util.LinkedHashMap; @@ -28,15 +29,32 @@ public class AbilityPickerView implements Serializable { if (objectName == null) { rule = ability.getRule(true); } else { - rule = ability.getRule(objectName); - if (rule.isEmpty()) { - rule = ability.toString(); + // spell abilities must start with "Cast name" (split cards have different names for each spell part) + if (ability instanceof SpellAbility) { + SpellAbility spell = (SpellAbility) ability; + rule = getAbilityRules(spell, spell.getCardName()); + if (!rule.startsWith("Cast ")) { + rule = spell.toString() + ": " + rule; // spell.toString() must return this.name (example: Cast Armed) + } + } else { + rule = getAbilityRules(ability, objectName); } } choices.put(ability.getId(), num + ". " + rule); } } + private String getAbilityRules(Ability ability, String objectName) { + String rule = ability.getRule(objectName); + if (rule.isEmpty()) { + rule = ability.toString(); + } + if (!rule.isEmpty()) { + rule = Character.toUpperCase(rule.charAt(0)) + rule.substring(1); + } + return rule; + } + public AbilityPickerView(Map modes, String message) { this.choices = modes; this.message = message; diff --git a/Mage.Common/src/main/java/mage/view/AbilityView.java b/Mage.Common/src/main/java/mage/view/AbilityView.java index 77e767d7af..7327ac9eef 100644 --- a/Mage.Common/src/main/java/mage/view/AbilityView.java +++ b/Mage.Common/src/main/java/mage/view/AbilityView.java @@ -1,13 +1,11 @@ package mage.view; -import mage.ObjectColor; -import mage.abilities.Ability; -import mage.constants.CardType; -import mage.constants.SuperType; -import mage.util.SubTypeList; - import java.util.ArrayList; import java.util.EnumSet; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.constants.SuperType; +import mage.util.SubTypeList; /** * @author BetaSteward_at_googlemail.com @@ -29,7 +27,7 @@ public class AbilityView extends CardView { this.power = ""; this.toughness = ""; this.loyalty = ""; - this.cardTypes = EnumSet.noneOf(CardType.class); + this.cardTypes = new ArrayList<>(); this.subTypes = new SubTypeList(); this.superTypes = EnumSet.noneOf(SuperType.class); this.color = new ObjectColor(); @@ -45,5 +43,4 @@ public class AbilityView extends CardView { this.name = name; } - } diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index 5dc0de29e3..15dabfd8c1 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -1,6 +1,8 @@ package mage.view; import com.google.gson.annotations.Expose; +import java.util.*; +import java.util.stream.Collectors; import mage.MageObject; import mage.ObjectColor; import mage.abilities.Abilities; @@ -29,9 +31,6 @@ import mage.target.Targets; import mage.util.CardUtil; import mage.util.SubTypeList; -import java.util.*; -import java.util.stream.Collectors; - /** * @author BetaSteward_at_googlemail.com */ @@ -55,7 +54,7 @@ public class CardView extends SimpleCardView { @Expose protected String loyalty = ""; protected String startingLoyalty; - protected Set cardTypes; + protected ArrayList cardTypes; protected SubTypeList subTypes; protected Set superTypes; protected ObjectColor color; @@ -151,7 +150,7 @@ public class CardView extends SimpleCardView { this.toughness = cardView.toughness; this.loyalty = cardView.loyalty; this.startingLoyalty = cardView.startingLoyalty; - this.cardTypes = new HashSet<>(cardView.cardTypes); + this.cardTypes = new ArrayList<>(cardView.cardTypes); this.subTypes = new SubTypeList(cardView.subTypes); this.superTypes = cardView.superTypes; @@ -213,8 +212,8 @@ public class CardView extends SimpleCardView { * @param card * @param game * @param controlled is the card view created for the card controller - used - * for morph / face down cards to know which player may see information for - * the card + * for morph / face down cards to know which player may + * see information for the card */ public CardView(Card card, Game game, boolean controlled) { this(card, game, controlled, false, false); @@ -240,12 +239,14 @@ public class CardView extends SimpleCardView { /** * @param card * @param game - * @param controlled is the card view created for the card controller - used - * for morph / face down cards to know which player may see information for - * the card + * @param controlled is the card view created for the card controller + * - used for morph / face down cards to know which + * player may see information for the card * @param showFaceDownCard if true and the card is not on the battlefield, - * also a face down card is shown in the view, face down cards will be shown - * @param storeZone if true the card zone will be set in the zone attribute. + * also a face down card is shown in the view, face + * down cards will be shown + * @param storeZone if true the card zone will be set in the zone + * attribute. */ public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) { super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null, card.getTokenDescriptor()); @@ -318,13 +319,6 @@ public class CardView extends SimpleCardView { } } - AdventureCard adventureCard = null; - AdventureCardSpell adventureCardSpell = null; - if (card instanceof AdventureCard) { - adventureCard = (AdventureCard) card; - adventureCardSpell = (AdventureCardSpell) adventureCard.getSpellCard(); - } - String fullCardName; if (splitCard != null) { this.isSplitCard = true; @@ -340,7 +334,9 @@ public class CardView extends SimpleCardView { fullCardName = card.getName(); // split card contains full name as normal this.manaCostLeft = splitCard.getLeftHalfCard().getManaCost().getSymbols(); this.manaCostRight = splitCard.getRightHalfCard().getManaCost().getSymbols(); - } else if (adventureCard != null) { + } else if (card instanceof AdventureCard) { + AdventureCard adventureCard = ((AdventureCard) card); + AdventureCardSpell adventureCardSpell = ((AdventureCardSpell) adventureCard.getSpellCard()); fullCardName = adventureCard.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + adventureCardSpell.getName(); this.manaCostLeft = adventureCardSpell.getManaCost().getSymbols(); this.manaCostRight = adventureCard.getManaCost().getSymbols(); @@ -467,7 +463,7 @@ public class CardView extends SimpleCardView { } else if (spell.getCard() != null) { SplitCard wholeCard = ((SplitCardHalf) spell.getCard()).getParentCard(); Abilities aftermathHalfAbilities = wholeCard.getRightHalfCard().getAbilities(game); - if (aftermathHalfAbilities.stream().anyMatch(ability -> ability instanceof AftermathAbility)) { + if (aftermathHalfAbilities.stream().anyMatch(halfAbility -> halfAbility instanceof AftermathAbility)) { if (ty == SpellAbilityType.SPLIT_RIGHT) { artRect = ArtRect.AFTERMATH_BOTTOM; } else { @@ -636,7 +632,7 @@ public class CardView extends SimpleCardView { this.toughness = ""; this.loyalty = ""; this.startingLoyalty = ""; - this.cardTypes = EnumSet.noneOf(CardType.class); + this.cardTypes = new ArrayList<>(); this.subTypes = new SubTypeList(); this.superTypes = EnumSet.noneOf(SuperType.class); this.color = new ObjectColor(); @@ -764,7 +760,7 @@ public class CardView extends SimpleCardView { return startingLoyalty; } - public Set getCardTypes() { + public ArrayList getCardTypes() { return cardTypes; } @@ -1030,26 +1026,25 @@ public class CardView extends SimpleCardView { } public String getColorText() { - - String color = getColor().getDescription(); - return color.substring(0, 1).toUpperCase(Locale.ENGLISH) + color.substring(1); + String colorText = getColor().getDescription(); + return colorText.substring(0, 1).toUpperCase(Locale.ENGLISH) + colorText.substring(1); } public String getTypeText() { - StringBuilder type = new StringBuilder(); + StringBuilder typeText = new StringBuilder(); if (!getSuperTypes().isEmpty()) { - type.append(String.join(" ", getSuperTypes().stream().map(SuperType::toString).collect(Collectors.toList()))); - type.append(" "); + typeText.append(String.join(" ", getSuperTypes().stream().map(SuperType::toString).collect(Collectors.toList()))); + typeText.append(" "); } if (!getCardTypes().isEmpty()) { - type.append(String.join(" ", getCardTypes().stream().map(CardType::toString).collect(Collectors.toList()))); - type.append(" "); + typeText.append(String.join(" ", getCardTypes().stream().map(CardType::toString).collect(Collectors.toList()))); + typeText.append(" "); } if (!getSubTypes().isEmpty()) { - type.append(" - "); - type.append(String.join(" ", getSubTypes().stream().map(SubType::toString).collect(Collectors.toList()))); + typeText.append(" - "); + typeText.append(String.join(" ", getSubTypes().stream().map(SubType::toString).collect(Collectors.toList()))); } - return type.toString(); + return typeText.toString(); } public boolean isLand() { diff --git a/Mage.Common/src/main/java/mage/view/GameView.java b/Mage.Common/src/main/java/mage/view/GameView.java index 20ef9eedd5..3f703c7678 100644 --- a/Mage.Common/src/main/java/mage/view/GameView.java +++ b/Mage.Common/src/main/java/mage/view/GameView.java @@ -49,12 +49,13 @@ public class GameView implements Serializable { private final List exiles = new ArrayList<>(); private final List revealed = new ArrayList<>(); private List lookedAt = new ArrayList<>(); + private final List companion = new ArrayList<>(); private final List combat = new ArrayList<>(); private final TurnPhase phase; private final PhaseStep step; private final UUID activePlayerId; private String activePlayerName = ""; - private String priorityPlayerName; + private final String priorityPlayerName; private final int turn; private boolean special = false; private final boolean isPlayer; // false = watching user @@ -137,7 +138,7 @@ public class GameView implements Serializable { // can happen if a player times out while ability is on the stack LOGGER.debug("Stack Object for stack ability not found: " + stackObject.getStackAbility().getRule()); } - } else { + } else if (stackObject != null) { LOGGER.fatal("Unknown type of StackObject: " + stackObject.getName() + ' ' + stackObject.toString() + ' ' + stackObject.getClass().toString()); } //stackOrder.add(stackObject.getId()); @@ -149,6 +150,12 @@ public class GameView implements Serializable { for (String name : state.getRevealed().keySet()) { revealed.add(new RevealedView(name, state.getRevealed().get(name), game)); } + for (String name : state.getCompanion().keySet()) { + // Only show the companion window when the companion is still outside the game. + if (state.getCompanion().get(name).stream().anyMatch(cardId -> state.getZone(cardId) == Zone.OUTSIDE)) { + companion.add(new RevealedView(name, state.getCompanion().get(name), game)); + } + } this.phase = state.getTurn().getPhaseType(); this.step = state.getTurn().getStepType(); this.turn = state.getTurnNum(); @@ -266,6 +273,10 @@ public class GameView implements Serializable { return lookedAt; } + public List getCompanion() { + return companion; + } + public void setLookedAt(List list) { this.lookedAt = list; } diff --git a/Mage.Common/src/main/java/mage/view/PermanentView.java b/Mage.Common/src/main/java/mage/view/PermanentView.java index 840011f45c..312e1c9e8f 100644 --- a/Mage.Common/src/main/java/mage/view/PermanentView.java +++ b/Mage.Common/src/main/java/mage/view/PermanentView.java @@ -95,10 +95,9 @@ public class PermanentView extends CardView { if (controlled) { // must be a morphed or manifested card for (Ability permanentAbility : permanent.getAbilities()) { - if (permanentAbility instanceof TurnFaceUpAbility && !permanentAbility.getRuleVisible()) { - this.rules.add(permanentAbility.getRule(true)); - } if (permanentAbility.getWorksFaceDown()) { + this.rules.add(permanentAbility.getRule(true)); + } else if (permanentAbility instanceof TurnFaceUpAbility && !permanentAbility.getRuleVisible()) { this.rules.add(permanentAbility.getRule()); } } diff --git a/Mage.Common/src/main/java/mage/view/PlayerView.java b/Mage.Common/src/main/java/mage/view/PlayerView.java index 71c2964258..2269530dc6 100644 --- a/Mage.Common/src/main/java/mage/view/PlayerView.java +++ b/Mage.Common/src/main/java/mage/view/PlayerView.java @@ -31,6 +31,7 @@ public class PlayerView implements Serializable { private final UUID playerId; private final String name; + private final boolean controlled; // gui: player is current user private final int life; private final Counters counters; private final int wins; @@ -64,6 +65,7 @@ public class PlayerView implements Serializable { public PlayerView(Player player, GameState state, Game game, UUID createdForPlayerId, UUID watcherUserId) { this.playerId = player.getId(); this.name = player.getName(); + this.controlled = player.getId().equals(createdForPlayerId); this.life = player.getLife(); this.counters = player.getCounters(); this.wins = player.getMatchPlayer().getWins(); @@ -164,6 +166,10 @@ public class PlayerView implements Serializable { } } + public boolean getControlled() { + return this.controlled; + } + public int getLife() { return this.life; } diff --git a/Mage.Common/src/main/java/mage/view/StackAbilityView.java b/Mage.Common/src/main/java/mage/view/StackAbilityView.java index 6035f687be..a2214b83c4 100644 --- a/Mage.Common/src/main/java/mage/view/StackAbilityView.java +++ b/Mage.Common/src/main/java/mage/view/StackAbilityView.java @@ -17,6 +17,8 @@ import mage.util.GameLog; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import mage.abilities.hint.Hint; +import mage.abilities.hint.HintUtils; /** * @author BetaSteward_at_googlemail.com @@ -115,6 +117,18 @@ public class StackAbilityView extends CardView { this.rules.add("Chosen mode: " + mode.getEffects().getText(mode) + ""); } } + + if (HintUtils.ABILITY_HINTS_ENABLE) { + List abilityHints = new ArrayList<>(); + for (Hint hint : ability.getHints()) { + abilityHints.add(hint.getText(game, ability)); + } + // total hints + if (!abilityHints.isEmpty()) { + rules.add(HintUtils.HINT_START_MARK); + HintUtils.appendHints(rules, abilityHints); + } + } } public CardView getSourceCard() { diff --git a/Mage.Common/src/main/java/mage/view/TournamentTypeView.java b/Mage.Common/src/main/java/mage/view/TournamentTypeView.java index 8bd4d8c3cc..22d7ecf9bc 100644 --- a/Mage.Common/src/main/java/mage/view/TournamentTypeView.java +++ b/Mage.Common/src/main/java/mage/view/TournamentTypeView.java @@ -22,6 +22,7 @@ public class TournamentTypeView implements Serializable { private final boolean elimination; private final boolean random; private final boolean richMan; + private final boolean jumpstart; public TournamentTypeView(TournamentType tournamentType) { this.name = tournamentType.getName(); @@ -34,6 +35,7 @@ public class TournamentTypeView implements Serializable { this.elimination = tournamentType.isElimination(); this.random = tournamentType.isRandom(); this.richMan = tournamentType.isRichMan(); + this.jumpstart = tournamentType.isJumpstart(); } @Override @@ -80,4 +82,9 @@ public class TournamentTypeView implements Serializable { public boolean isRichMan() { return richMan; } + + public boolean isJumpstart() { + return jumpstart; + } + } diff --git a/Mage.Plugins/Mage.Counter.Plugin/pom.xml b/Mage.Plugins/Mage.Counter.Plugin/pom.xml index 813af4df7e..f3b2060977 100644 --- a/Mage.Plugins/Mage.Counter.Plugin/pom.xml +++ b/Mage.Plugins/Mage.Counter.Plugin/pom.xml @@ -7,7 +7,7 @@ org.mage mage-plugins - 1.4.42 + 1.4.44 mage-counter-plugin diff --git a/Mage.Plugins/pom.xml b/Mage.Plugins/pom.xml index 7ec674a862..8ea321317e 100644 --- a/Mage.Plugins/pom.xml +++ b/Mage.Plugins/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 mage-plugins diff --git a/Mage.Server.Console/pom.xml b/Mage.Server.Console/pom.xml index 4102763af4..5eda44ae20 100644 --- a/Mage.Server.Console/pom.xml +++ b/Mage.Server.Console/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 mage.server.console diff --git a/Mage.Server.Console/src/main/java/mage/server/console/ConsoleFrame.java b/Mage.Server.Console/src/main/java/mage/server/console/ConsoleFrame.java index df46fb279e..4ed3d543a1 100644 --- a/Mage.Server.Console/src/main/java/mage/server/console/ConsoleFrame.java +++ b/Mage.Server.Console/src/main/java/mage/server/console/ConsoleFrame.java @@ -60,7 +60,7 @@ public class ConsoleFrame extends javax.swing.JFrame implements MageClient { initComponents(); try { - UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); + UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); session = new SessionImpl(this); connectDialog = new ConnectDialog(); } catch (Exception ex) { diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml b/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml index 05f4f1e9be..0e9e81ccd1 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-deck-constructed @@ -46,7 +46,6 @@ - mage-deck-constructed diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AusHighlander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AusHighlander.java index a66fdab991..437b1fea68 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AusHighlander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AusHighlander.java @@ -4,6 +4,7 @@ import mage.cards.ExpansionSet; import mage.cards.Sets; import mage.cards.decks.Constructed; import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidatorErrorType; import java.util.HashMap; import java.util.Map; @@ -81,7 +82,7 @@ public class AusHighlander extends Constructed { } public AusHighlander() { - this("Australian Highlander"); + super("Australian Highlander", "AU Highlander"); for (ExpansionSet set : Sets.getInstance().values()) { if (set.getSetType().isEternalLegal()) { setCodes.add(set.getCode()); @@ -89,20 +90,17 @@ public class AusHighlander extends Constructed { } } - public AusHighlander(String name) { - super(name); - } - @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); if (deck.getCards().size() != getDeckMinSize()) { - invalid.put("Deck", "Must contain " + getDeckMinSize() + " singleton cards: has " + (deck.getCards().size()) + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + getDeckMinSize() + " singleton cards: has " + (deck.getCards().size()) + " cards"); valid = false; } if (deck.getSideboard().size() > 15) { - invalid.put("Sideboard", "Must contain at most 15 singleton cards: has " + (deck.getSideboard().size()) + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Sideboard", "Must contain at most 15 singleton cards: has " + (deck.getSideboard().size()) + " cards"); valid = false; } @@ -142,12 +140,12 @@ public class AusHighlander extends Constructed { String cn = entry.getKey(); if (pointMap.containsKey(cn)) { totalPoints += pointMap.get(cn); - invalid.put(entry.getKey(), " " + pointMap.get(cn) + " point " + cn); + addError(DeckValidatorErrorType.OTHER, entry.getKey(), " " + pointMap.get(cn) + " point " + cn); } } if (totalPoints > 7) { - invalid.put("Total points too high", "Your calculated point total was " + totalPoints); - invalid.put("Only you can see this!", "Your opponents will not be able to see this message or what cards are in your deck!"); + addError(DeckValidatorErrorType.PRIMARY, "Total points too high", "Your calculated point total was " + totalPoints); + addError(DeckValidatorErrorType.PRIMARY, "Only you can see this!", "Your opponents will not be able to see this message or what cards are in your deck!"); valid = false; } return valid; diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java index 7737857e9d..9a73bb9da7 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java @@ -1,9 +1,12 @@ package mage.deck; +import mage.abilities.Ability; import mage.abilities.common.CanBeYourCommanderAbility; +import mage.abilities.keyword.CompanionAbility; import mage.cards.Card; import mage.cards.decks.Constructed; import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidatorErrorType; import mage.filter.FilterMana; import mage.util.ManaUtil; @@ -23,12 +26,12 @@ public class Brawl extends Constructed { setCodes.addAll(Standard.makeLegalSets()); banned.add("Golos, Tireless Pilgrim"); + banned.add("Drannith Magistrate"); + banned.add("Lutri, the Spellchaser"); banned.add("Oko, Thief of Crowns"); banned.add("Sorcerous Spyglass"); - } - - public Brawl(String name) { - super(name); + banned.add("Teferi, Time Raveler"); + banned.add("Winota, Joiner of Forces"); } @Override @@ -39,10 +42,52 @@ public class Brawl extends Constructed { @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); + Card brawler = null; + Card companion = null; FilterMana colorIdentity = new FilterMana(); - if (deck.getCards().size() + deck.getSideboard().size() != getDeckMinSize()) { - invalid.put("Deck", "Must contain " + getDeckMinSize() + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); + if (deck.getSideboard().size() == 1) { + for (Card card : deck.getSideboard()) { + brawler = card; + } + } else if (deck.getSideboard().size() == 2) { + Iterator iter = deck.getSideboard().iterator(); + Card card1 = iter.next(); + Card card2 = iter.next(); + if (card1.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card1; + brawler = card2; + } else if (card2.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card2; + brawler = card1; + } else { + addError(DeckValidatorErrorType.PRIMARY, "Brawl", "Sideboard must contain only the brawler and up to 1 companion"); + valid = false; + } + } else { + addError(DeckValidatorErrorType.PRIMARY, "Brawl", "Sideboard must contain only the brawler and up to 1 companion"); + valid = false; + } + + if (brawler != null) { + ManaUtil.collectColorIdentity(colorIdentity, brawler.getColorIdentity()); + if (bannedCommander.contains(brawler.getName())) { + addError(DeckValidatorErrorType.PRIMARY, "Brawl", "Brawler banned (" + brawler.getName() + ')'); + valid = false; + } + if (!((brawler.isCreature() && brawler.isLegendary()) + || brawler.isPlaneswalker() || brawler.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) { + addError(DeckValidatorErrorType.PRIMARY, "Brawl", "Invalid Brawler (" + brawler.getName() + ')'); + valid = false; + } + } + + if (companion != null && deck.getCards().size() + deck.getSideboard().size() != getDeckMinSize() + 1) { + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + (getDeckMinSize() + 1) + " cards (companion doesn't count in deck size requirement): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); + valid = false; + } else if (companion == null && deck.getCards().size() + deck.getSideboard().size() != getDeckMinSize()) { + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + getDeckMinSize() + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); valid = false; } @@ -53,28 +98,11 @@ public class Brawl extends Constructed { for (String bannedCard : banned) { if (counts.containsKey(bannedCard)) { - invalid.put(bannedCard, "Banned"); + addError(DeckValidatorErrorType.BANNED, "Banned", bannedCard); valid = false; } } - if (deck.getSideboard().size() != 1) { - invalid.put("Brawl", "Sideboard must contain only the commander)"); - valid = false; - } else { - for (Card commander : deck.getSideboard()) { - if (bannedCommander.contains(commander.getName())) { - invalid.put("Brawl", "Brawl banned (" + commander.getName() + ')'); - valid = false; - } - if (!((commander.isCreature() && commander.isLegendary()) - || commander.isPlaneswalker() || commander.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) { - invalid.put("Brawl", "Invalid Commander (" + commander.getName() + ')'); - valid = false; - } - ManaUtil.collectColorIdentity(colorIdentity, commander.getColorIdentity()); - } - } Set basicsInDeck = new HashSet<>(); if (colorIdentity.isColorless()) { for (Card card : deck.getCards()) { @@ -88,14 +116,23 @@ public class Brawl extends Constructed { && !(colorIdentity.isColorless() && basicsInDeck.size() == 1 && basicsInDeck.contains(card.getName()))) { - invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); + valid = false; + } + } + for (Card card : deck.getSideboard()) { + if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity()) + && !(colorIdentity.isColorless() + && basicsInDeck.size() == 1 + && basicsInDeck.contains(card.getName()))) { + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); valid = false; } } for (Card card : deck.getCards()) { if (!isSetAllowed(card.getExpansionSetCode())) { if (!legalSets(card)) { - invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); + addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); valid = false; } } @@ -103,12 +140,27 @@ public class Brawl extends Constructed { for (Card card : deck.getSideboard()) { if (!isSetAllowed(card.getExpansionSetCode())) { if (!legalSets(card)) { - invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); + addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); valid = false; } } } + // Check for companion legality + if (companion != null) { + Set cards = new HashSet<>(deck.getCards()); + cards.add(brawler); + for (Ability ability : companion.getAbilities()) { + if (ability instanceof CompanionAbility) { + CompanionAbility companionAbility = (CompanionAbility) ability; + if (!companionAbility.isLegal(cards, getDeckMinSize())) { + addError(DeckValidatorErrorType.PRIMARY, companion.getName(), "Deck invalid for companion"); + valid = false; + } + break; + } + } + } + return valid; } - } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java index 4661dd2072..c2b819cab1 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java @@ -4,6 +4,7 @@ import mage.cards.ExpansionSet; import mage.cards.Sets; import mage.cards.decks.Constructed; import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidatorErrorType; import java.util.HashMap; import java.util.Map; @@ -63,7 +64,7 @@ public class CanadianHighlander extends Constructed { } public CanadianHighlander() { - this("Canadian Highlander"); + super("Canadian Highlander"); for (ExpansionSet set : Sets.getInstance().values()) { if (set.getSetType().isEternalLegal()) { setCodes.add(set.getCode()); @@ -71,21 +72,18 @@ public class CanadianHighlander extends Constructed { } } - public CanadianHighlander(String name) { - super(name); - } - @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); if (deck.getCards().size() < 100) { - invalid.put("Deck", "Must contain 100 or more singleton cards: has " + (deck.getCards().size()) + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain 100 or more singleton cards: has " + (deck.getCards().size()) + " cards"); valid = false; } if (!deck.getSideboard().isEmpty()) { - invalid.put("Deck", "Sideboard can't contain any cards: has " + (deck.getSideboard().size()) + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Sideboard can't contain any cards: has " + (deck.getSideboard().size()) + " cards"); valid = false; } @@ -100,11 +98,11 @@ public class CanadianHighlander extends Constructed { String cn = entry.getKey(); if (pointMap.containsKey(cn)) { totalPoints += pointMap.get(cn); - invalid.put(entry.getKey(), " " + pointMap.get(cn) + " point " + cn); + addError(DeckValidatorErrorType.OTHER, entry.getKey(), " " + pointMap.get(cn) + " point " + cn); } } if (totalPoints > allowedPoints) { - invalid.put("Total points too high", "Your calculated point total was " + totalPoints); + addError(DeckValidatorErrorType.PRIMARY, "Total points too high", "Your calculated point total was " + totalPoints); valid = false; } return valid; diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java index 8930973535..dae753fe8b 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java @@ -4,6 +4,7 @@ import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.CanBeYourCommanderAbility; import mage.abilities.costs.mana.ManaCost; +import mage.abilities.keyword.CompanionAbility; import mage.abilities.keyword.PartnerAbility; import mage.abilities.keyword.PartnerWithAbility; import mage.cards.Card; @@ -11,6 +12,7 @@ import mage.cards.ExpansionSet; import mage.cards.Sets; import mage.cards.decks.Constructed; import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidatorErrorType; import mage.filter.FilterMana; import mage.util.ManaUtil; @@ -26,7 +28,7 @@ public class Commander extends Constructed { protected boolean partnerAllowed = true; public Commander() { - this("Commander"); + super("Commander"); for (ExpansionSet set : Sets.getInstance().values()) { if (set.getSetType().isEternalLegal()) { setCodes.add(set.getCode()); @@ -42,6 +44,7 @@ public class Commander extends Constructed { banned.add("Emrakul, the Aeons Torn"); banned.add("Erayo, Soratami Ascendant"); banned.add("Fastbond"); + banned.add("Flash"); banned.add("Gifts Ungiven"); banned.add("Griselbrand"); banned.add("Iona, Shield of Emeria"); @@ -49,6 +52,7 @@ public class Commander extends Constructed { banned.add("Leovold, Emissary of Trest"); banned.add("Library of Alexandria"); banned.add("Limited Resources"); + banned.add("Lutri, the Spellchaser"); banned.add("Mox Emerald"); banned.add("Mox Jet"); banned.add("Mox Pearl"); @@ -77,6 +81,10 @@ public class Commander extends Constructed { super(name); } + public Commander(String name, String shortName) { + super(name, shortName); + } + @Override public int getDeckMinSize() { return 98; @@ -90,10 +98,58 @@ public class Commander extends Constructed { @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); FilterMana colorIdentity = new FilterMana(); + Set commanders = new HashSet<>(); + Card companion = null; - if (deck.getCards().size() + deck.getSideboard().size() != 100) { - invalid.put("Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); + if (deck.getSideboard().size() == 1) { + commanders.add(deck.getSideboard().iterator().next()); + } else if (deck.getSideboard().size() == 2) { + Iterator iter = deck.getSideboard().iterator(); + Card card1 = iter.next(); + Card card2 = iter.next(); + if (card1.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card1; + commanders.add(card2); + } else if (card2.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card2; + commanders.add(card1); + } else { + commanders.add(card1); + commanders.add(card2); + } + } else if (deck.getSideboard().size() == 3) { + Iterator iter = deck.getSideboard().iterator(); + Card card1 = iter.next(); + Card card2 = iter.next(); + Card card3 = iter.next(); + if (card1.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card1; + commanders.add(card2); + commanders.add(card3); + } else if (card2.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card2; + commanders.add(card1); + commanders.add(card3); + } else if (card3.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card3; + commanders.add(card1); + commanders.add(card2); + } else { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion"); + valid = false; + } + } else { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion"); + valid = false; + } + + if (companion != null && deck.getCards().size() + deck.getSideboard().size() != 101) { + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 101 + " cards (companion doesn't count for deck size): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); + valid = false; + } else if (companion == null && deck.getCards().size() + deck.getSideboard().size() != 100) { + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); valid = false; } @@ -104,53 +160,45 @@ public class Commander extends Constructed { for (String bannedCard : banned) { if (counts.containsKey(bannedCard)) { - invalid.put(bannedCard, "Banned"); + addError(DeckValidatorErrorType.BANNED, "Banned", bannedCard); valid = false; } } - if (deck.getSideboard().isEmpty() || deck.getSideboard().size() > 2) { - if ((deck.getSideboard().size() > 1 && !partnerAllowed)) { - invalid.put("Commander", "You may only have one commander"); + Set commanderNames = new HashSet<>(); + for (Card commander : commanders) { + commanderNames.add(commander.getName()); + } + for (Card commander : commanders) { + if (bannedCommander.contains(commander.getName())) { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander banned (" + commander.getName() + ')'); + valid = false; } - invalid.put("Commander", "Sideboard must contain only the commander(s)"); - valid = false; - } else { - Set commanderNames = new HashSet<>(); - for (Card commander : deck.getSideboard()) { - commanderNames.add(commander.getName()); + if ((!commander.isCreature() || !commander.isLegendary()) + && (!commander.isPlaneswalker() || !commander.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander invalid (" + commander.getName() + ')'); + valid = false; } - for (Card commander : deck.getSideboard()) { - if (bannedCommander.contains(commander.getName())) { - invalid.put("Commander", "Commander banned (" + commander.getName() + ')'); - valid = false; - } - if ((!commander.isCreature() || !commander.isLegendary()) - && (!commander.isPlaneswalker() || !commander.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) { - invalid.put("Commander", "Commander invalid (" + commander.getName() + ')'); - valid = false; - } - if (deck.getSideboard().size() == 2) { - if (commander.getAbilities().contains(PartnerAbility.getInstance())) { - if (bannedPartner.contains(commander.getName())) { - invalid.put("Commander", "Partner banned (" + commander.getName() + ')'); - valid = false; - } - } else { - boolean partnersWith = commander.getAbilities() - .stream() - .filter(PartnerWithAbility.class::isInstance) - .map(PartnerWithAbility.class::cast) - .map(PartnerWithAbility::getPartnerName) - .anyMatch(commanderNames::contains); - if (!partnersWith) { - invalid.put("Commander", "Commander without Partner (" + commander.getName() + ')'); - valid = false; - } + if (commanders.size() == 2) { + if (commander.getAbilities().contains(PartnerAbility.getInstance())) { + if (bannedPartner.contains(commander.getName())) { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Partner banned (" + commander.getName() + ')'); + valid = false; + } + } else { + boolean partnersWith = commander.getAbilities() + .stream() + .filter(PartnerWithAbility.class::isInstance) + .map(PartnerWithAbility.class::cast) + .map(PartnerWithAbility::getPartnerName) + .anyMatch(commanderNames::contains); + if (!partnersWith) { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander without Partner (" + commander.getName() + ')'); + valid = false; } } - ManaUtil.collectColorIdentity(colorIdentity, commander.getColorIdentity()); } + ManaUtil.collectColorIdentity(colorIdentity, commander.getColorIdentity()); } // no needs in cards check on wrong commanders @@ -160,14 +208,20 @@ public class Commander extends Constructed { for (Card card : deck.getCards()) { if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) { - invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); + valid = false; + } + } + for (Card card : deck.getSideboard()) { + if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) { + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); valid = false; } } for (Card card : deck.getCards()) { if (!isSetAllowed(card.getExpansionSetCode())) { if (!legalSets(card)) { - invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); + addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); valid = false; } } @@ -175,11 +229,26 @@ public class Commander extends Constructed { for (Card card : deck.getSideboard()) { if (!isSetAllowed(card.getExpansionSetCode())) { if (!legalSets(card)) { - invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); + addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); valid = false; } } } + // Check for companion legality + if (companion != null) { + Set cards = new HashSet<>(deck.getCards()); + cards.addAll(commanders); + for (Ability ability : companion.getAbilities()) { + if (ability instanceof CompanionAbility) { + CompanionAbility companionAbility = (CompanionAbility) ability; + if (!companionAbility.isLegal(cards, getDeckMinSize())) { + addError(DeckValidatorErrorType.PRIMARY, companion.getName(), "Deck invalid for companion"); + valid = false; + } + break; + } + } + } return valid; } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java index 53b287527f..0cb9b35241 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java @@ -13,24 +13,31 @@ public class DuelCommander extends Commander { banned.add("Ancient Tomb"); banned.add("Back to Basics"); banned.add("Black Lotus"); + banned.add("Capture of Jingzhou"); + banned.add("Cavern of Souls"); banned.add("Channel"); banned.add("Chrome Mox"); + banned.add("Deflecting Swat"); banned.add("Dig Through Time"); banned.add("Eidolon of the Great Revel"); banned.add("Emrakul, the Aeons Torn"); banned.add("Entomb"); banned.add("Fastbond"); + banned.add("Field of the Dead"); + banned.add("Fierce Guardianship"); banned.add("Fireblast"); banned.add("Food Chain"); banned.add("Gaea's Cradle"); - banned.add("Gifts Ungiven"); banned.add("Grim Monolith"); banned.add("Hermit Druid"); + banned.add("High Tide"); banned.add("Humility"); banned.add("Imperial Seal"); banned.add("Karakas"); banned.add("Library of Alexandria"); + banned.add("Lion's Eye Diamond"); banned.add("Loyal Retainers"); + banned.add("Lutri, the Spellchaser"); banned.add("Mana Crypt"); banned.add("Mana Drain"); banned.add("Mana Vault"); @@ -53,15 +60,18 @@ public class DuelCommander extends Commander { banned.add("Sensei's Divining Top"); banned.add("Sol Ring"); banned.add("Strip Mine"); + banned.add("Temporal Manipulation"); banned.add("Thassa's Oracle"); banned.add("The Tabernacle at Pendrell Vale"); banned.add("Time Vault"); banned.add("Time Walk"); banned.add("Timetwister"); + banned.add("Time Warp"); banned.add("Tinker"); banned.add("Tolarian Academy"); banned.add("Treasure Cruise"); banned.add("Vampiric Tutor"); + banned.add("Wasteland"); bannedCommander.add("Arahbo, Roar of the World"); bannedCommander.add("Baral, Chief of Compliance"); @@ -72,7 +82,6 @@ public class DuelCommander extends Commander { bannedCommander.add("Edric, Spymaster of Trest"); bannedCommander.add("Emry, Lurker of the Loch"); bannedCommander.add("Geist of Saint Traft"); - bannedCommander.add("Jace, Vryn's Prodigy"); bannedCommander.add("Marath, Will of the Wild"); bannedCommander.add("Najeela, the Blade-Blossom"); bannedCommander.add("Oloro, Ageless Ascetic"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Freeform.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Freeform.java index 41733f9116..f1336bf102 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Freeform.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Freeform.java @@ -2,6 +2,7 @@ package mage.deck; import mage.cards.decks.Deck; import mage.cards.decks.DeckValidator; +import mage.cards.decks.DeckValidatorErrorType; /** * @author fireshoes @@ -12,6 +13,14 @@ public class Freeform extends DeckValidator { super("Constructed - Freeform"); } + public Freeform(String name) { + super(name); + } + + public Freeform(String name, String shortName) { + super(name, shortName); + } + @Override public int getDeckMinSize() { return 40; @@ -25,9 +34,10 @@ public class Freeform extends DeckValidator { @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); // http://magic.wizards.com/en/gameinfo/gameplay/formats/freeform if (deck.getCards().size() < getDeckMinSize()) { - invalid.put("Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards"); valid = false; } return valid; diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/FreeformCommander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/FreeformCommander.java index 8a1a41c631..4c045f3c9a 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/FreeformCommander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/FreeformCommander.java @@ -1,6 +1,7 @@ package mage.deck; import mage.abilities.Ability; +import mage.abilities.keyword.CompanionAbility; import mage.abilities.keyword.PartnerAbility; import mage.abilities.keyword.PartnerWithAbility; import mage.cards.Card; @@ -8,6 +9,7 @@ import mage.cards.ExpansionSet; import mage.cards.Sets; import mage.cards.decks.Constructed; import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidatorErrorType; import mage.filter.FilterMana; import mage.util.ManaUtil; @@ -22,7 +24,7 @@ public class FreeformCommander extends Constructed { private static final Map pdAllowed = new HashMap<>(); public FreeformCommander() { - this("Freeform Commander"); + super("Freeform Commander"); for (ExpansionSet set : Sets.getInstance().values()) { setCodes.add(set.getCode()); } @@ -35,6 +37,10 @@ public class FreeformCommander extends Constructed { super(name); } + public FreeformCommander(String name, String shortName) { + super(name, shortName); + } + @Override public int getDeckMinSize() { return 98; @@ -48,49 +54,90 @@ public class FreeformCommander extends Constructed { @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); FilterMana colorIdentity = new FilterMana(); + Set commanders = new HashSet<>(); + Card companion = null; - if (deck.getCards().size() + deck.getSideboard().size() != 100) { - invalid.put("Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); + if (deck.getSideboard().size() == 1) { + commanders.add(deck.getSideboard().iterator().next()); + } else if (deck.getSideboard().size() == 2) { + Iterator iter = deck.getSideboard().iterator(); + Card card1 = iter.next(); + Card card2 = iter.next(); + if (card1.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card1; + commanders.add(card2); + } else if (card2.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card2; + commanders.add(card1); + } else { + commanders.add(card1); + commanders.add(card2); + } + } else if (deck.getSideboard().size() == 3) { + Iterator iter = deck.getSideboard().iterator(); + Card card1 = iter.next(); + Card card2 = iter.next(); + Card card3 = iter.next(); + if (card1.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card1; + commanders.add(card2); + commanders.add(card3); + } else if (card2.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card2; + commanders.add(card1); + commanders.add(card3); + } else if (card3.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card3; + commanders.add(card1); + commanders.add(card2); + } else { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion"); + valid = false; + } + } else { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion"); + valid = false; + } + + if (companion != null && deck.getCards().size() + deck.getSideboard().size() != 101) { + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 101 + " cards (companion doesn't count for deck size): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); + valid = false; + } else if (companion == null && deck.getCards().size() + deck.getSideboard().size() != 100) { + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); valid = false; } Map counts = new HashMap<>(); countCards(counts, deck.getCards()); countCards(counts, deck.getSideboard()); - valid = checkCounts(1, counts) && valid; - if (deck.getSideboard().isEmpty() || deck.getSideboard().size() > 2) { - invalid.put("Commander", "Sideboard must contain only the commander(s)"); - valid = false; - } else { - Set commanderNames = new HashSet<>(); - for (Card commander : deck.getSideboard()) { - commanderNames.add(commander.getName()); + Set commanderNames = new HashSet<>(); + for (Card commander : commanders) { + commanderNames.add(commander.getName()); + } + for (Card commander : commanders) { + if (!commander.isCreature() || !commander.isLegendary()) { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "For Freeform Commander, the commander must be a creature or be legendary. Yours was: " + commander.getName()); + valid = false; } - for (Card commander : deck.getSideboard()) { - if (!(commander.isCreature() - || commander.isLegendary())) { - invalid.put("Commander", "For Freeform Commander, the commander must be a creature or be legendary. Yours was: " + commander.getName()); - valid = false; - } - if (deck.getSideboard().size() == 2 && !commander.getAbilities().contains(PartnerAbility.getInstance())) { - boolean partnersWith = false; - for (Ability ability : commander.getAbilities()) { - if (ability instanceof PartnerWithAbility - && commanderNames.contains(((PartnerWithAbility) ability).getPartnerName())) { - partnersWith = true; - break; - } - } + if (commanders.size() == 2) { + if (!commander.getAbilities().contains(PartnerAbility.getInstance())) { + boolean partnersWith = commander.getAbilities() + .stream() + .filter(PartnerWithAbility.class::isInstance) + .map(PartnerWithAbility.class::cast) + .map(PartnerWithAbility::getPartnerName) + .anyMatch(commanderNames::contains); if (!partnersWith) { - invalid.put("Commander", "Commander without Partner (" + commander.getName() + ')'); + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander without Partner (" + commander.getName() + ')'); valid = false; } } - ManaUtil.collectColorIdentity(colorIdentity, commander.getColorIdentity()); } + ManaUtil.collectColorIdentity(colorIdentity, commander.getColorIdentity()); } // no needs in cards check on wrong commanders @@ -100,16 +147,28 @@ public class FreeformCommander extends Constructed { for (Card card : deck.getCards()) { if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) { - invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); valid = false; } } - for (Card card : deck.getSideboard()) { - if (!isSetAllowed(card.getExpansionSetCode())) { - if (!legalSets(card)) { - invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); - valid = false; + if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) { + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); + valid = false; + } + } + // Check for companion legality + if (companion != null) { + Set cards = new HashSet<>(deck.getCards()); + cards.addAll(commanders); + for (Ability ability : companion.getAbilities()) { + if (ability instanceof CompanionAbility) { + CompanionAbility companionAbility = (CompanionAbility) ability; + if (!companionAbility.isLegal(cards, getDeckMinSize())) { + addError(DeckValidatorErrorType.PRIMARY, companion.getName(), "Deck invalid for companion"); + valid = false; + } + break; } } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java new file mode 100644 index 0000000000..268e6448c8 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java @@ -0,0 +1,106 @@ +package mage.deck; + +import mage.cards.ExpansionSet; +import mage.cards.Sets; +import mage.cards.decks.Constructed; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +/** + * @author mikalinn777 + * Historic is a nonrotating format in MTGA. https://mtg.gamepedia.com/Historic_(format) + */ +public class Historic extends Constructed { + + public Historic() { + super("Constructed - Historic"); + + Date cutoff = new GregorianCalendar(2017, Calendar.SEPTEMBER, 29).getTime(); // XLN release date + for (ExpansionSet set : Sets.getInstance().values()) { + if (set.getSetType().isStandardLegal() && (set.getReleaseDate().after(cutoff) || set.getReleaseDate().equals(cutoff))) { + setCodes.add(set.getCode()); + } + } + // Regular ban list + banned.add("Agent of Treachery"); + banned.add("Burning-Tree Emissary"); + banned.add("Fires of Invention"); + banned.add("Nexus of Fate"); + banned.add("Oko, Thief of Crowns"); + banned.add("Once Upon a Time"); + banned.add("Teferi, Time Raveler"); + banned.add("Veil of Summer"); + banned.add("Wilderness Reclamation"); + banned.add("Winota, Joiner of Forces"); + + // Individual cards added + setCodes.add(mage.sets.HistoricAnthology1.getInstance().getCode()); + setCodes.add(mage.sets.HistoricAnthology2.getInstance().getCode()); + setCodes.add(mage.sets.HistoricAnthology3.getInstance().getCode()); + singleCards.add("Rhys the Redeemed"); + singleCards.add("Spiritual Guardian"); + singleCards.add("Sanctuary Cat"); + singleCards.add("Raging Goblin"); + singleCards.add("Hanna, Ship's Navigator"); + singleCards.add("Angelic Reward"); + singleCards.add("Confront the Assault"); + singleCards.add("Inspiring Commander"); + singleCards.add("Shrine Keeper"); + singleCards.add("Tactical Advantage"); + singleCards.add("River's Favor"); + singleCards.add("Shorecomber Crab"); + singleCards.add("Zephyr Gull"); + singleCards.add("Feral Roar"); + singleCards.add("Treetop Warden"); + singleCards.add("Angler Turtle"); + singleCards.add("Vengeant Vampire"); + singleCards.add("Rampaging Brontodon"); + singleCards.add("Bladewing the Risen"); + singleCards.add("The Gitrog Monster"); + singleCards.add("Talrand, Sky Summoner"); + + // Jumpstart with replacements + setCodes.add(mage.sets.Jumpstart.getInstance().getCode()); + singleCards.add("Archon of Sun's Grace"); + singleCards.add("Banishing Light"); + singleCards.add("Gadwick, the Wizened"); + singleCards.add("Carnifex Demon"); + singleCards.add("Weight of Memory"); + singleCards.add("Bond of Revival"); + singleCards.add("Audacious Thief"); + singleCards.add("Lightning Serpent"); + singleCards.add("Fanatic of Mogis"); + singleCards.add("Woe Strider"); + singleCards.add("Goblin Oriflamme"); + singleCards.add("Doomed Necromancer"); + singleCards.add("Scorching Dragonfire"); + singleCards.add("Prey Upon"); + singleCards.add("Lightning Strike"); + singleCards.add("Pollenbright Druid"); + singleCards.add("Dryad Greenseeker"); + singleCards.add("Serra's Guardian"); + // these cards aren't technically banned, so if any of them are added later they should be removed here + banned.add("Ajani's Chosen"); + banned.add("Angelic Arbiter"); + banned.add("Path to Exile"); + banned.add("Read the Runes"); + banned.add("Rhystic Study"); + banned.add("Thought Scour"); + banned.add("Exhume"); + banned.add("Mausoleum Turnkey"); + banned.add("Reanimate"); + banned.add("Scourge of Nel Toth"); + banned.add("Sheoldred, Whispering One"); + banned.add("Ball Lightning"); + banned.add("Chain Lightning"); + banned.add("Draconic Roar"); + banned.add("Flametongue Kavu"); + banned.add("Goblin Lore"); + banned.add("Lightning Bolt"); + banned.add("Fa'adiyah Seer"); + banned.add("Scrounging Bandar"); + banned.add("Time to Feed"); + } +} diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalType2.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalType2.java index 04a6957064..6db8a823bc 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalType2.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalType2.java @@ -4,6 +4,7 @@ import mage.cards.ExpansionSet; import mage.cards.Sets; import mage.cards.decks.Constructed; import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidatorError; import java.util.*; @@ -80,7 +81,7 @@ public class HistoricalType2 extends Constructed { * done in the overridden validate function. */ public HistoricalType2() { - super("Constructed - Historical Type 2"); + super("Constructed - Historical Type 2", "Hist. Type 2"); // banned cards banned.add("Balance"); @@ -104,9 +105,10 @@ public class HistoricalType2 extends Constructed { @Override public boolean validate(Deck deck) { - Map leastInvalid = null; + List leastInvalid = null; boolean valid = false; + errorsList.clear(); // first, check whether misty and batterskull are in the same deck. Map counts = new HashMap<>(); @@ -124,7 +126,7 @@ public class HistoricalType2 extends Constructed { for (String[] sets : standards) { // clear the invalid list - invalid.clear(); + errorsList.clear(); // add the sets to the setCodes. setCodes = new ArrayList<>(Arrays.asList(sets)); @@ -138,15 +140,15 @@ public class HistoricalType2 extends Constructed { // if the map holding the invalid cards is empty, set it to a // copy of the current invalid list. if (leastInvalid == null) { - leastInvalid = new HashMap<>(this.getInvalid()); + leastInvalid = new ArrayList<>(this.getErrorsList()); continue; } // see how many invalid cards there are. if there are less invalid // cards than the stored invalid list, assign the current invalid // to leastInvalid. - if (leastInvalid.size() > this.getInvalid().size()) { - leastInvalid = new HashMap<>(this.getInvalid()); + if (leastInvalid.size() > this.getErrorsList().size()) { + leastInvalid = new ArrayList<>(this.getErrorsList()); } } @@ -164,7 +166,7 @@ public class HistoricalType2 extends Constructed { // clear the invalid list and set codes. setCodes.clear(); - invalid.clear(); + errorsList.clear(); // increment the start and end dates. start.set(Calendar.YEAR, start.get(Calendar.YEAR) + 1); @@ -182,7 +184,7 @@ public class HistoricalType2 extends Constructed { // validate it. If it validates, clear the invalid cards and break. if (super.validate(deck)) { - invalid.clear(); + errorsList.clear(); valid = true; break; } @@ -191,17 +193,16 @@ public class HistoricalType2 extends Constructed { // cards than the stored invalid list, assign the current invalid // to leastInvalid. if (leastInvalid == null) { - leastInvalid = new HashMap<>(this.getInvalid()); - - } else if (leastInvalid.size() > this.getInvalid().size()) { - leastInvalid = new HashMap<>(this.getInvalid()); + leastInvalid = new ArrayList<>(this.getErrorsList()); + } else if (leastInvalid.size() > this.getErrorsList().size()) { + leastInvalid = new ArrayList<>(this.getErrorsList()); } } // if no standard environment is valid, set the invalid to the // invalid that had the least errors. if (!valid) { - this.invalid = new HashMap<>(leastInvalid); + this.errorsList = new ArrayList<>(leastInvalid); } // return the validity. diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java index 47abc0531b..d297bdfb8e 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java @@ -49,6 +49,7 @@ public class Legacy extends Constructed { banned.add("Iterative Analysis"); banned.add("Jeweled Bird"); banned.add("Library of Alexandria"); + banned.add("Lurrus of the Dream-Den"); banned.add("Mana Crypt"); banned.add("Mana Drain"); banned.add("Mana Vault"); @@ -94,6 +95,6 @@ public class Legacy extends Constructed { banned.add("Wrenn and Six"); banned.add("Yawgmoth's Bargain"); banned.add("Yawgmoth's Will"); - + banned.add("Zirda, the Dawnwaker"); } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java index 6dedbcfc3c..0e8f646e56 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java @@ -24,6 +24,7 @@ public class Modern extends Constructed { } banned.add("Ancient Den"); + banned.add("Arcum's Astrolabe"); banned.add("Birthing Pod"); banned.add("Blazing Shoal"); banned.add("Bridge from Below"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Momir.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Momir.java index ab5500ebd2..8e9a46bf44 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Momir.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Momir.java @@ -3,6 +3,7 @@ package mage.deck; import mage.cards.Card; import mage.cards.decks.Deck; import mage.cards.decks.DeckValidator; +import mage.cards.decks.DeckValidatorErrorType; import java.util.ArrayList; import java.util.Arrays; @@ -14,11 +15,7 @@ import java.util.List; public class Momir extends DeckValidator { public Momir() { - this("Momir Basic"); - } - - public Momir(String name) { - super(name); + super("Momir Basic", "Momir"); } @Override @@ -34,16 +31,17 @@ public class Momir extends DeckValidator { @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); if (deck.getCards().size() != getDeckMinSize()) { - invalid.put("Deck", "Must contain " + getDeckMinSize() + " cards: has " + deck.getCards().size() + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + getDeckMinSize() + " cards: has " + deck.getCards().size() + " cards"); valid = false; } List basicLandNames = new ArrayList<>(Arrays.asList("Forest", "Island", "Mountain", "Swamp", "Plains", "Wastes")); for (Card card : deck.getCards()) { if (!basicLandNames.contains(card.getName())) { - invalid.put(card.getName(), "Only basic lands are allowed"); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Only basic lands are allowed"); valid = false; } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Oathbreaker.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Oathbreaker.java index b299afbaf6..7b3c8a8ee9 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Oathbreaker.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Oathbreaker.java @@ -5,6 +5,7 @@ import mage.abilities.keyword.PartnerAbility; import mage.abilities.keyword.PartnerWithAbility; import mage.cards.Card; import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidatorErrorType; import mage.filter.FilterMana; import mage.util.ManaUtil; @@ -20,16 +21,17 @@ public class Oathbreaker extends Vintage { public Oathbreaker() { super(); - this.name = "Oathbreaker"; + setName("Oathbreaker"); - // banned = vintage + oathbreaker's list: https://weirdcards.org/oathbreaker-ban-list - // last updated 8/2/19 - Primeval Titan unbanned + // banned = vintage + oathbreaker's list: https://oathbreakermtg.org/banned-list/ + // last updated 4/24/20 - Dark Ritual banned banned.add("Ad Nauseam"); banned.add("Ancestral Recall"); banned.add("Balance"); banned.add("Biorhythm"); banned.add("Black Lotus"); banned.add("Channel"); + banned.add("Dark Ritual"); banned.add("Doomsday"); banned.add("Emrakul, the Aeons Torn"); banned.add("Expropriate"); @@ -81,9 +83,10 @@ public class Oathbreaker extends Vintage { @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); if (deck.getCards().size() + deck.getSideboard().size() != 60) { - invalid.put("Deck", "Must contain " + 60 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 60 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); valid = false; } @@ -93,7 +96,7 @@ public class Oathbreaker extends Vintage { for (String bannedCard : banned) { if (counts.containsKey(bannedCard)) { - invalid.put(bannedCard, "Banned"); + addError(DeckValidatorErrorType.BANNED, "Banned", bannedCard); valid = false; } } @@ -104,7 +107,7 @@ public class Oathbreaker extends Vintage { Set signatureSpells = new HashSet<>(); FilterMana allCommandersColor = new FilterMana(); if (deck.getSideboard().size() < 2 || deck.getSideboard().size() > 4) { - invalid.put("Oathbreaker", "Sideboard must contain only 2 or 4 cards (oathbreaker + signature spell)"); + addError(DeckValidatorErrorType.PRIMARY, "Oathbreaker", "Sideboard must contain only 2 or 4 cards (oathbreaker + signature spell)"); valid = false; } else { // collect data @@ -118,7 +121,7 @@ public class Oathbreaker extends Vintage { // color identity from commanders only, not spell ManaUtil.collectColorIdentity(allCommandersColor, commander.getColorIdentity()); } else { - invalid.put("Oathbreaker", "Only planeswalker can be Oathbreaker, not " + commander.getName()); + addError(DeckValidatorErrorType.PRIMARY, "Oathbreaker", "Only planeswalker can be Oathbreaker, not " + commander.getName()); valid = false; } } @@ -126,15 +129,15 @@ public class Oathbreaker extends Vintage { // check size (1+1 or 2+2 allows) if (commanderNames.isEmpty() || commanderNames.size() > 2) { - invalid.put("Oathbreaker", "Sideboard must contains 1 or 2 oathbreakers, but found: " + commanderNames.size()); + addError(DeckValidatorErrorType.PRIMARY, "Oathbreaker", "Sideboard must contains 1 or 2 oathbreakers, but found: " + commanderNames.size()); valid = false; } if (signatureSpells.isEmpty() || signatureSpells.size() > 2) { - invalid.put("Signature Spell", "Sideboard must contains 1 or 2 signature spells, but found: " + signatureSpells.size()); + addError(DeckValidatorErrorType.PRIMARY, "Signature Spell", "Sideboard must contains 1 or 2 signature spells, but found: " + signatureSpells.size()); valid = false; } if (signatureSpells.size() != commanderNames.size()) { - invalid.put("Oathbreaker", "Sideboard must contains 1 + 1 or 2 + 2 cards, but found: " + commanderNames.size() + " + " + signatureSpells.size()); + addError(DeckValidatorErrorType.PRIMARY, "Oathbreaker", "Sideboard must contains 1 + 1 or 2 + 2 cards, but found: " + commanderNames.size() + " + " + signatureSpells.size()); valid = false; } @@ -151,7 +154,7 @@ public class Oathbreaker extends Vintage { } } if (!partnersWith) { - invalid.put("Oathbreaker", "Oathbreaker without Partner (" + commander.getName() + ')'); + addError(DeckValidatorErrorType.PRIMARY, "Oathbreaker", "Oathbreaker without Partner (" + commander.getName() + ')'); valid = false; } } @@ -173,7 +176,7 @@ public class Oathbreaker extends Vintage { } } if (!haveSameColor) { - invalid.put("Signature Spell", "Can't find oathbreaker with compatible color identity (" + spell.getName() + " - " + spellColor + ")"); + addError(DeckValidatorErrorType.PRIMARY, "Signature Spell", "Can't find oathbreaker with compatible color identity (" + spell.getName() + " - " + spellColor + ")"); valid = false; } } @@ -187,7 +190,7 @@ public class Oathbreaker extends Vintage { for (Card card : deck.getCards()) { if (!ManaUtil.isColorIdentityCompatible(allCommandersColor, card.getColorIdentity())) { - invalid.put(card.getName(), "Invalid color (" + card.getColorIdentity() + ')'); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + card.getColorIdentity() + ')'); valid = false; } } @@ -195,7 +198,7 @@ public class Oathbreaker extends Vintage { for (Card card : deck.getSideboard()) { if (!isSetAllowed(card.getExpansionSetCode())) { if (!legalSets(card)) { - invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); + addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); valid = false; } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java index f22eba8467..af42951c0b 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java @@ -27,6 +27,7 @@ public class Pauper extends Constructed { banned.add("Cranial Plating"); banned.add("Daze"); banned.add("Empty the Warrens"); + banned.add("Expedition Map"); banned.add("Frantic Search"); banned.add("Gitaxian Probe"); banned.add("Grapeshot"); @@ -34,6 +35,7 @@ public class Pauper extends Constructed { banned.add("Hight Tide"); banned.add("Hymn to Tourach"); banned.add("Invigorate"); + banned.add("Mystic Sanctuary"); banned.add("Peregrine Drake"); banned.add("Sinkhole"); banned.add("Temporal Fissure"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/PennyDreadfulCommander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/PennyDreadfulCommander.java index 0be8aa63a9..06e6b594f7 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/PennyDreadfulCommander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/PennyDreadfulCommander.java @@ -2,6 +2,7 @@ package mage.deck; import mage.abilities.Ability; import mage.abilities.common.CanBeYourCommanderAbility; +import mage.abilities.keyword.CompanionAbility; import mage.abilities.keyword.PartnerAbility; import mage.abilities.keyword.PartnerWithAbility; import mage.cards.Card; @@ -9,11 +10,12 @@ import mage.cards.ExpansionSet; import mage.cards.Sets; import mage.cards.decks.Constructed; import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidatorErrorType; +import mage.cards.decks.PennyDreadfulLegalityUtil; import mage.filter.FilterMana; import mage.util.ManaUtil; import java.util.*; -import java.util.Map.Entry; /** * @author spjspj @@ -22,10 +24,9 @@ public class PennyDreadfulCommander extends Constructed { protected List bannedCommander = new ArrayList<>(); private static final Map pdAllowed = new HashMap<>(); - private static boolean setupAllowed = false; public PennyDreadfulCommander() { - this("Penny Dreadful Commander"); + super("Penny Dreadful Commander", "Penny"); for (ExpansionSet set : Sets.getInstance().values()) { if (set.getSetType().isEternalLegal()) { setCodes.add(set.getCode()); @@ -33,10 +34,6 @@ public class PennyDreadfulCommander extends Constructed { } } - public PennyDreadfulCommander(String name) { - super(name); - } - @Override public int getDeckMinSize() { return 98; @@ -50,65 +47,106 @@ public class PennyDreadfulCommander extends Constructed { @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); FilterMana colorIdentity = new FilterMana(); + Set commanders = new HashSet<>(); + Card companion = null; - if (deck.getCards().size() + deck.getSideboard().size() != 100) { - invalid.put("Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); + if (deck.getSideboard().size() == 1) { + commanders.add(deck.getSideboard().iterator().next()); + } else if (deck.getSideboard().size() == 2) { + Iterator iter = deck.getSideboard().iterator(); + Card card1 = iter.next(); + Card card2 = iter.next(); + if (card1.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card1; + commanders.add(card2); + } else if (card2.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card2; + commanders.add(card1); + } else { + commanders.add(card1); + commanders.add(card2); + } + } else if (deck.getSideboard().size() == 3) { + Iterator iter = deck.getSideboard().iterator(); + Card card1 = iter.next(); + Card card2 = iter.next(); + Card card3 = iter.next(); + if (card1.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card1; + commanders.add(card2); + commanders.add(card3); + } else if (card2.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card2; + commanders.add(card1); + commanders.add(card3); + } else if (card3.getAbilities().stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + companion = card3; + commanders.add(card1); + commanders.add(card2); + } else { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion"); + valid = false; + } + } else { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion"); + valid = false; + } + + if (companion != null && deck.getCards().size() + deck.getSideboard().size() != 101) { + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 101 + " cards (companion doesn't count for deck size): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); + valid = false; + } else if (companion == null && deck.getCards().size() + deck.getSideboard().size() != 100) { + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); valid = false; } - List basicLandNames = new ArrayList<>(Arrays.asList("Forest", "Island", "Mountain", "Swamp", "Plains", "Wastes")); Map counts = new HashMap<>(); countCards(counts, deck.getCards()); countCards(counts, deck.getSideboard()); + valid = checkCounts(1, counts) && valid; - for (Map.Entry entry : counts.entrySet()) { - if (entry.getValue() > 1) { - if (!basicLandNames.contains(entry.getKey())) { - invalid.put(entry.getKey(), "Too many: " + entry.getValue()); - valid = false; - } - } + if (pdAllowed.isEmpty()) { + pdAllowed.putAll(PennyDreadfulLegalityUtil.getLegalCardList()); } - generatePennyDreadfulHash(); for (String wantedCard : counts.keySet()) { if (!(pdAllowed.containsKey(wantedCard))) { - invalid.put(wantedCard, "Banned"); + addError(DeckValidatorErrorType.BANNED, "Banned", wantedCard); valid = false; } } - if (deck.getSideboard().isEmpty() || deck.getSideboard().size() > 2) { - invalid.put("Commander", "Sideboard must contain only the commander(s)"); - valid = false; - } else { - Set commanderNames = new HashSet<>(); - for (Card commander : deck.getSideboard()) { - commanderNames.add(commander.getName()); + Set commanderNames = new HashSet<>(); + for (Card commander : commanders) { + commanderNames.add(commander.getName()); + } + for (Card commander : commanders) { + if (bannedCommander.contains(commander.getName())) { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander banned (" + commander.getName() + ')'); + valid = false; } - for (Card commander : deck.getSideboard()) { - if ((!commander.isCreature() || !commander.isLegendary()) - && (!commander.isPlaneswalker() || !commander.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) { - invalid.put("Commander", "Commander invalid (" + commander.getName() + ')'); - valid = false; - } - if (deck.getSideboard().size() == 2 && !commander.getAbilities().contains(PartnerAbility.getInstance())) { - boolean partnersWith = false; - for (Ability ability : commander.getAbilities()) { - if (ability instanceof PartnerWithAbility - && commanderNames.contains(((PartnerWithAbility) ability).getPartnerName())) { - partnersWith = true; - break; - } - } + if ((!commander.isCreature() || !commander.isLegendary()) + && (!commander.isPlaneswalker() || !commander.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) { + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander invalid (" + commander.getName() + ')'); + valid = false; + } + if (commanders.size() == 2) { + if (!commander.getAbilities().contains(PartnerAbility.getInstance())) { + boolean partnersWith = commander.getAbilities() + .stream() + .filter(PartnerWithAbility.class::isInstance) + .map(PartnerWithAbility.class::cast) + .map(PartnerWithAbility::getPartnerName) + .anyMatch(commanderNames::contains); if (!partnersWith) { - invalid.put("Commander", "Commander without Partner (" + commander.getName() + ')'); + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander without Partner (" + commander.getName() + ')'); valid = false; } } - ManaUtil.collectColorIdentity(colorIdentity, commander.getColorIdentity()); } + ManaUtil.collectColorIdentity(colorIdentity, commander.getColorIdentity()); } // no needs in cards check on wrong commanders @@ -118,14 +156,20 @@ public class PennyDreadfulCommander extends Constructed { for (Card card : deck.getCards()) { if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) { - invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); + valid = false; + } + } + for (Card card : deck.getSideboard()) { + if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) { + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); valid = false; } } for (Card card : deck.getCards()) { if (!isSetAllowed(card.getExpansionSetCode())) { if (!legalSets(card)) { - invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); + addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); valid = false; } } @@ -133,29 +177,26 @@ public class PennyDreadfulCommander extends Constructed { for (Card card : deck.getSideboard()) { if (!isSetAllowed(card.getExpansionSetCode())) { if (!legalSets(card)) { - invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); + addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); valid = false; } } } + // Check for companion legality + if (companion != null) { + Set cards = new HashSet<>(deck.getCards()); + cards.addAll(commanders); + for (Ability ability : companion.getAbilities()) { + if (ability instanceof CompanionAbility) { + CompanionAbility companionAbility = (CompanionAbility) ability; + if (!companionAbility.isLegal(cards, getDeckMinSize())) { + addError(DeckValidatorErrorType.PRIMARY, companion.getName(), "Deck invalid for companion"); + valid = false; + } + break; + } + } + } return valid; } - - public void generatePennyDreadfulHash() { - if (setupAllowed == false) { - setupAllowed = true; - } else { - return; - } - - Properties properties = new Properties(); - try { - properties.load(PennyDreadfulCommander.class.getResourceAsStream("pennydreadful.properties")); - } catch (Exception e) { - e.printStackTrace(); - } - for (final Entry entry : properties.entrySet()) { - pdAllowed.put((String) entry.getKey(), 1); - } - } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java index adf387c27d..ff914bf2d9 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java @@ -30,12 +30,15 @@ public class Pioneer extends Constructed { banned.add("Wooded Foothills"); banned.add("Felidar Guardian"); banned.add("Field of the Dead"); + banned.add("Inverter of Truth"); + banned.add("Kethis, the Hidden Hand"); banned.add("Leyline of Abundance"); banned.add("Nexus of Fate"); - banned.add("Oath of Nissa"); banned.add("Oko, Thief of Crowns"); banned.add("Once Upon a Time"); banned.add("Smuggler's Copter"); + banned.add("Underworld Breach"); banned.add("Veil of Summer"); + banned.add("Walking Ballista"); } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java index 86cc46d2b2..22fdd49fe8 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java @@ -17,9 +17,15 @@ public class Standard extends Constructed { setCodes.addAll(makeLegalSets()); + banned.add("Agent of Treachery"); + banned.add("Cauldron Familiar"); banned.add("Field of the Dead"); + banned.add("Fires of Invention"); + banned.add("Growth Spiral"); banned.add("Oko, Thief of Crowns"); banned.add("Once Upon a Time"); + banned.add("Teferi, Time Raveler"); + banned.add("Wilderness Reclamation"); banned.add("Veil of Summer"); } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperType2.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperType2.java index 17a1c9909f..053a0ee3fe 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperType2.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperType2.java @@ -4,6 +4,7 @@ import mage.cards.ExpansionSet; import mage.cards.Sets; import mage.cards.decks.Constructed; import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidatorError; import java.util.*; @@ -77,9 +78,10 @@ public class SuperType2 extends Constructed { @Override public boolean validate(Deck deck) { - Map leastInvalid = null; + List leastInvalid = null; boolean valid = false; + errorsList.clear(); // first, check whether misty and batterskull are in the same deck. Map counts = new HashMap<>(); @@ -97,7 +99,7 @@ public class SuperType2 extends Constructed { for (String[] sets : standards) { // clear the invalid list - invalid.clear(); + errorsList.clear(); // add the sets to the setCodes. setCodes = new ArrayList<>(Arrays.asList(sets)); @@ -119,15 +121,15 @@ public class SuperType2 extends Constructed { // if the map holding the invalid cards is empty, set it to a // copy of the current invalid list. if (leastInvalid == null) { - leastInvalid = new HashMap<>(this.getInvalid()); + leastInvalid = new ArrayList<>(this.getErrorsList()); continue; } // see how many invalid cards there are. if there are less invalid // cards than the stored invalid list, assign the current invalid // to leastInvalid. - if (leastInvalid.size() > this.getInvalid().size()) { - leastInvalid = new HashMap<>(this.getInvalid()); + if (leastInvalid.size() > this.getErrorsList().size()) { + leastInvalid = new ArrayList<>(this.getErrorsList()); } } @@ -145,7 +147,7 @@ public class SuperType2 extends Constructed { // clear the invalid list and set codes. setCodes.clear(); - invalid.clear(); + errorsList.clear(); // increment the start and end dates. start.set(Calendar.YEAR, start.get(Calendar.YEAR) + 1); @@ -171,7 +173,7 @@ public class SuperType2 extends Constructed { // validate it. If it validates, clear the invalid cards and break. if (super.validate(deck)) { - invalid.clear(); + errorsList.clear(); valid = true; break; } @@ -180,17 +182,17 @@ public class SuperType2 extends Constructed { // cards than the stored invalid list, assign the current invalid // to leastInvalid. if (leastInvalid == null) { - leastInvalid = new HashMap<>(this.getInvalid()); + leastInvalid = new ArrayList<>(this.getErrorsList()); - } else if (leastInvalid.size() > this.getInvalid().size()) { - leastInvalid = new HashMap<>(this.getInvalid()); + } else if (leastInvalid.size() > this.getErrorsList().size()) { + leastInvalid = new ArrayList<>(this.getErrorsList()); } } // if no standard environment is valid, set the invalid to the // invalid that had the least errors. if (!valid) { - this.invalid = new HashMap<>(leastInvalid); + this.errorsList = new ArrayList<>(leastInvalid); } // return the validity. diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java index d1aefbcd70..3db537c44b 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java @@ -6,6 +6,7 @@ import mage.cards.Sets; import mage.cards.SplitCard; import mage.cards.decks.Constructed; import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidatorErrorType; import mage.filter.FilterMana; import mage.game.GameTinyLeadersImpl; @@ -22,7 +23,7 @@ public class TinyLeaders extends Constructed { protected List bannedCommander = new ArrayList<>(); public TinyLeaders() { - this("Tiny Leaders"); + super("Tiny Leaders"); for (ExpansionSet set : Sets.getInstance().values()) { if (set.getSetType().isEternalLegal()) { setCodes.add(set.getCode()); @@ -86,10 +87,6 @@ public class TinyLeaders extends Constructed { bannedCommander.add("Derevi, Empyrical Tactician"); } - public TinyLeaders(String name) { - super(name); - } - @Override public int getDeckMinSize() { return 49; // commander gives from deck name @@ -107,9 +104,10 @@ public class TinyLeaders extends Constructed { @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); if (deck.getCards().size() != getDeckMinSize()) { - invalid.put("Deck", "Must contain " + getDeckMinSize() + " cards: has " + deck.getCards().size() + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + getDeckMinSize() + " cards: has " + deck.getCards().size() + " cards"); valid = false; } @@ -121,7 +119,7 @@ public class TinyLeaders extends Constructed { for (String bannedCard : banned) { if (counts.containsKey(bannedCard)) { - invalid.put(bannedCard, "Banned"); + addError(DeckValidatorErrorType.BANNED, "Banned", bannedCard); valid = false; } } @@ -138,16 +136,16 @@ public class TinyLeaders extends Constructed { if (commander == null || commander.getManaCost().convertedManaCost() > 3) { if (commander == null) { if (deck.getName() == null) { - invalid.put("Leader", "You have to save your deck with the leader card name entered to the DECK NAME field of the DECK EDITOR (top left) so that XMage knows your leader." + addError(DeckValidatorErrorType.PRIMARY, "Leader", "You have to save your deck with the leader card name entered to the DECK NAME field of the DECK EDITOR (top left) so that XMage knows your leader." + "(You can use the \"Sultai\" for a UBG (3/3) default Commander or \"Glass\" for a colorless 3/3 default Commander.)"); } else { - invalid.put("Leader", "Leader [" + deck.getName() + "] not found. You have to enter the name of the leader card into the DECK NAME field of the DECK EDITOR (top left). Check your spelling " + addError(DeckValidatorErrorType.PRIMARY, "Leader", "Leader [" + deck.getName() + "] not found. You have to enter the name of the leader card into the DECK NAME field of the DECK EDITOR (top left). Check your spelling " + "(use the \"Sultai\" for a UBG (3/3) default Commander or \"Glass\" for a colorless (3/3) default Commander)"); } } if (commander != null && commander.getManaCost().convertedManaCost() > 3) { - invalid.put("Leader", "Commanders converted mana cost is greater than 3"); + addError(DeckValidatorErrorType.PRIMARY, "Leader", "Commanders converted mana cost is greater than 3"); } return false; } @@ -165,21 +163,21 @@ public class TinyLeaders extends Constructed { } } } else { - invalid.put("Commander", "Commander banned (" + commander.getName() + ')'); + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander banned (" + commander.getName() + ')'); valid = false; } } else { - invalid.put("Commander", "Commander invalide (" + commander.getName() + ')'); + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander invalide (" + commander.getName() + ')'); valid = false; } } else { - invalid.put("Commander", "Sideboard must contain only a maximum of 10 sideboard cards (the Tiny Leader name must be written to the deck name)"); + addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only a maximum of 10 sideboard cards (the Tiny Leader name must be written to the deck name)"); valid = false; } for (Card card : deck.getCards()) { if (!isSetAllowed(card.getExpansionSetCode())) { if (!legalSets(card)) { - invalid.put(card.getName(), "Not allowed Set " + card.getExpansionSetCode()); + addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set " + card.getExpansionSetCode()); valid = false; } } @@ -187,7 +185,7 @@ public class TinyLeaders extends Constructed { for (Card card : deck.getSideboard()) { if (!isSetAllowed(card.getExpansionSetCode())) { if (!legalSets(card)) { - invalid.put(card.getName(), "Not allowed Set " + card.getExpansionSetCode()); + addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set " + card.getExpansionSetCode()); valid = false; } } @@ -197,22 +195,22 @@ public class TinyLeaders extends Constructed { private boolean isCardFormatValid(Card card, Card commander, FilterMana color) { if (!cardHasValideColor(color, card)) { - invalid.put(card.getName(), "Invalid color (" + commander.getName() + ')'); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + commander.getName() + ')'); return false; } //905.5b - Converted mana cost must be 3 or less if (card instanceof SplitCard) { if (((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() > 3) { - invalid.put(card.getName(), "Invalid cost (" + ((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() + ')'); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid cost (" + ((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() + ')'); return false; } if (((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() > 3) { - invalid.put(card.getName(), "Invalid cost (" + ((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() + ')'); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid cost (" + ((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() + ')'); return false; } } else if (card.getManaCost().convertedManaCost() > 3) { - invalid.put(card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ')'); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ')'); return false; } return true; diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java index a01a05ced3..a6cacdf855 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java @@ -30,6 +30,7 @@ public class Vintage extends Constructed { banned.add("Immediate Action"); banned.add("Iterative Analysis"); banned.add("Jeweled Bird"); + banned.add("Lurrus of the Dream-Den"); banned.add("Muzzio's Preparations"); banned.add("Power Play"); banned.add("Rebirth"); diff --git a/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml b/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml index 2c925c27b6..6de6fe4c9c 100644 --- a/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml +++ b/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-deck-limited diff --git a/Mage.Server.Plugins/Mage.Deck.Limited/src/mage/deck/Limited.java b/Mage.Server.Plugins/Mage.Deck.Limited/src/mage/deck/Limited.java index 542f045045..089398d671 100644 --- a/Mage.Server.Plugins/Mage.Deck.Limited/src/mage/deck/Limited.java +++ b/Mage.Server.Plugins/Mage.Deck.Limited/src/mage/deck/Limited.java @@ -2,6 +2,7 @@ package mage.deck; import mage.cards.decks.Deck; import mage.cards.decks.DeckValidator; +import mage.cards.decks.DeckValidatorErrorType; import java.util.HashMap; import java.util.Map; @@ -28,16 +29,17 @@ public class Limited extends DeckValidator { @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); //20091005 - 100.2b if (deck.getCards().size() < getDeckMinSize()) { - invalid.put("Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards"); valid = false; } Map counts = new HashMap<>(); countCards(counts, deck.getCards()); for (Map.Entry entry : counts.entrySet()) { if (entry.getValue() > 7 && entry.getKey().equals("Seven Dwarves")) { - invalid.put(entry.getKey(), "Too many: " + entry.getValue()); + addError(DeckValidatorErrorType.OTHER, entry.getKey(), "Too many: " + entry.getValue()); valid = false; } } diff --git a/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml index 7c67eda5fc..58bd67be9e 100644 --- a/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-brawlduel diff --git a/Mage.Server.Plugins/Mage.Game.BrawlDuel/src/mage/game/BrawlDuel.java b/Mage.Server.Plugins/Mage.Game.BrawlDuel/src/mage/game/BrawlDuel.java index 8aa8ba425e..50ef2e4dab 100644 --- a/Mage.Server.Plugins/Mage.Game.BrawlDuel/src/mage/game/BrawlDuel.java +++ b/Mage.Server.Plugins/Mage.Game.BrawlDuel/src/mage/game/BrawlDuel.java @@ -10,7 +10,7 @@ import mage.game.mulligan.Mulligan; public class BrawlDuel extends GameCommanderImpl { public BrawlDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 60); } public BrawlDuel(final BrawlDuel game) { diff --git a/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml index 82682897fb..cd723a720e 100644 --- a/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-brawlfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/src/mage/game/BrawlFreeForAll.java b/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/src/mage/game/BrawlFreeForAll.java index 29cc7f22e0..6b5f1db755 100644 --- a/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/src/mage/game/BrawlFreeForAll.java +++ b/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/src/mage/game/BrawlFreeForAll.java @@ -17,7 +17,7 @@ public class BrawlFreeForAll extends GameCommanderImpl { private int numPlayers; public BrawlFreeForAll(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 60); } public BrawlFreeForAll(final BrawlFreeForAll game) { diff --git a/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml index 374960f567..9422bfd6f5 100644 --- a/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-canadianhighlanderduel diff --git a/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml index cc12a371a1..bf0391aac4 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-commanderduel diff --git a/Mage.Server.Plugins/Mage.Game.CommanderDuel/src/mage/game/CommanderDuel.java b/Mage.Server.Plugins/Mage.Game.CommanderDuel/src/mage/game/CommanderDuel.java index 6e5df00b82..f8afcb93df 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderDuel/src/mage/game/CommanderDuel.java +++ b/Mage.Server.Plugins/Mage.Game.CommanderDuel/src/mage/game/CommanderDuel.java @@ -10,7 +10,7 @@ import mage.game.mulligan.Mulligan; public class CommanderDuel extends GameCommanderImpl { public CommanderDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 100); } public CommanderDuel(final CommanderDuel game) { diff --git a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml index d7433f0e55..3723f74469 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-commanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java index 2c1a9e7d18..0192a2c0ad 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java +++ b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java @@ -2,14 +2,14 @@ package mage.game; -import java.util.UUID; import mage.constants.MultiplayerAttackOption; import mage.constants.RangeOfInfluence; import mage.game.match.MatchType; import mage.game.mulligan.Mulligan; +import java.util.UUID; + /** - * * @author LevelX2 */ public class CommanderFreeForAll extends GameCommanderImpl { @@ -17,7 +17,7 @@ public class CommanderFreeForAll extends GameCommanderImpl { private int numPlayers; public CommanderFreeForAll(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 100); } public CommanderFreeForAll(final CommanderFreeForAll game) { @@ -28,7 +28,7 @@ public class CommanderFreeForAll extends GameCommanderImpl { @Override protected void init(UUID choosingPlayerId) { startingPlayerSkipsDraw = false; - super.init(choosingPlayerId); + super.init(choosingPlayerId); } @Override diff --git a/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml index c515dde4d9..310af55ab5 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-freeforall diff --git a/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java b/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java index 44209f3388..2d6b1ab80b 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java +++ b/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java @@ -15,7 +15,7 @@ public class FreeForAll extends GameImpl { private int numPlayers; public FreeForAll(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 60); } public FreeForAll(final FreeForAll game) { diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml index 8e37dc5115..5367b945d3 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-freeformcommanderduel diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuel.java b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuel.java index d57f520f07..eccf1bfcd7 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuel.java +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuel.java @@ -11,7 +11,7 @@ import mage.game.mulligan.Mulligan; public class FreeformCommanderDuel extends GameCommanderImpl { public FreeformCommanderDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 60); } public FreeformCommanderDuel(final FreeformCommanderDuel game) { diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml index d2fd9950d9..5907c2a43a 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-freeformcommanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAll.java b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAll.java index 2f9aa0e29f..7638fbe59e 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAll.java +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAll.java @@ -17,7 +17,7 @@ public class FreeformCommanderFreeForAll extends GameCommanderImpl { private int numPlayers; public FreeformCommanderFreeForAll(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 60); } public FreeformCommanderFreeForAll(final FreeformCommanderFreeForAll game) { diff --git a/Mage.Server.Plugins/Mage.Game.FreeformUnlimitedCommander/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformUnlimitedCommander/pom.xml index 249c124fa2..0ace1966a1 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeformUnlimitedCommander/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.FreeformUnlimitedCommander/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-freeformunlimitedcommander @@ -23,7 +23,7 @@ org.mage mage-game-freeformcommanderfreeforall - 1.4.42 + 1.4.44 compile diff --git a/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml index f82f22af9d..6d0032a90c 100644 --- a/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-momirduel diff --git a/Mage.Server.Plugins/Mage.Game.MomirDuel/src/mage/game/MomirDuel.java b/Mage.Server.Plugins/Mage.Game.MomirDuel/src/mage/game/MomirDuel.java index ac51137df9..cc66fc51df 100644 --- a/Mage.Server.Plugins/Mage.Game.MomirDuel/src/mage/game/MomirDuel.java +++ b/Mage.Server.Plugins/Mage.Game.MomirDuel/src/mage/game/MomirDuel.java @@ -24,7 +24,7 @@ import mage.players.Player; public class MomirDuel extends GameImpl { public MomirDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 60); } public MomirDuel(final MomirDuel game) { diff --git a/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml b/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml index 845d640990..548bf0b26a 100644 --- a/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-momirfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java b/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java index 0750d4d7cb..efa5ee8b89 100644 --- a/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java +++ b/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java @@ -26,7 +26,7 @@ public class MomirGame extends GameImpl { private int numPlayers; public MomirGame(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 60); } public MomirGame(final MomirGame game) { diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/pom.xml index 269f85d71b..3ceb2b553d 100644 --- a/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-oathbreakerduel @@ -22,7 +22,7 @@ org.mage mage-game-oathbreakerfreeforall - 1.4.42 + 1.4.44 compile diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/target/classes/mage/game/OathbreakerDuel.class b/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/target/classes/mage/game/OathbreakerDuel.class deleted file mode 100644 index 14929cf061..0000000000 Binary files a/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/target/classes/mage/game/OathbreakerDuel.class and /dev/null differ diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/target/classes/mage/game/OathbreakerDuelMatch.class b/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/target/classes/mage/game/OathbreakerDuelMatch.class deleted file mode 100644 index 57f30413e5..0000000000 Binary files a/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/target/classes/mage/game/OathbreakerDuelMatch.class and /dev/null differ diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/target/classes/mage/game/OathbreakerDuelType.class b/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/target/classes/mage/game/OathbreakerDuelType.class deleted file mode 100644 index 59b0495b17..0000000000 Binary files a/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/target/classes/mage/game/OathbreakerDuelType.class and /dev/null differ diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/pom.xml index 30bcec9cba..2c1ca5182b 100644 --- a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-oathbreakerfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java index 7e068208e2..9c517d0361 100644 --- a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java +++ b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java @@ -25,14 +25,14 @@ import java.util.*; public class OathbreakerFreeForAll extends GameCommanderImpl { private int numPlayers; - private Map> playerSignatureSpells = new HashMap<>(); - private Map> playerOathbreakers = new HashMap<>(); + private final Map> playerSignatureSpells = new HashMap<>(); + private final Map> playerOathbreakers = new HashMap<>(); private static final String COMMANDER_NAME_OATHBREAKER = "Oathbreaker"; private static final String COMMANDER_NAME_SIGNATURE_SPELL = "Signature Spell"; public OathbreakerFreeForAll(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 100); this.startingPlayerSkipsDraw = false; } @@ -122,19 +122,19 @@ public class OathbreakerFreeForAll extends GameCommanderImpl { if (player != null) { Set commanders = this.playerOathbreakers.getOrDefault(player.getId(), new HashSet<>()); Set spells = this.playerSignatureSpells.getOrDefault(player.getId(), new HashSet<>()); - for (UUID id : player.getCommandersIds()) { + for (UUID commanderId : super.getCommandersIds(player, commanderCardType)) { switch (commanderCardType) { case ANY: - res.add(id); + res.add(commanderId); break; case COMMANDER_OR_OATHBREAKER: - if (commanders.contains(id)) { - res.add(id); + if (commanders.contains(commanderId)) { + res.add(commanderId); } break; case SIGNATURE_SPELL: - if (spells.contains(id)) { - res.add(id); + if (spells.contains(commanderId)) { + res.add(commanderId); } break; default: diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAll$1.class b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAll$1.class deleted file mode 100644 index bbcf45ac34..0000000000 Binary files a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAll$1.class and /dev/null differ diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAll.class b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAll.class deleted file mode 100644 index 0bbfcbecd2..0000000000 Binary files a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAll.class and /dev/null differ diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAllMatch.class b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAllMatch.class deleted file mode 100644 index a3dbe6e9b7..0000000000 Binary files a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAllMatch.class and /dev/null differ diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAllType.class b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAllType.class deleted file mode 100644 index dd02f0e8f2..0000000000 Binary files a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/target/classes/mage/game/OathbreakerFreeForAllType.class and /dev/null differ diff --git a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml index 4790c78bea..d228052131 100644 --- a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-pennydreadfulcommanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/src/mage/game/PennyDreadfulCommanderFreeForAll.java b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/src/mage/game/PennyDreadfulCommanderFreeForAll.java index add9c578a9..0f6ea01fae 100644 --- a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/src/mage/game/PennyDreadfulCommanderFreeForAll.java +++ b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/src/mage/game/PennyDreadfulCommanderFreeForAll.java @@ -17,7 +17,7 @@ public class PennyDreadfulCommanderFreeForAll extends GameCommanderImpl { private int numPlayers; public PennyDreadfulCommanderFreeForAll(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 60); } public PennyDreadfulCommanderFreeForAll(final PennyDreadfulCommanderFreeForAll game) { diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml index 1ae9001fe4..a68c4fe2c0 100644 --- a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-tinyleadersduel diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml index c8e14f9687..97a343bd24 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-game-twoplayerduel diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java index b5e7f2b334..21f3a61a60 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java @@ -1,4 +1,3 @@ - package mage.game; import mage.constants.MultiplayerAttackOption; @@ -13,7 +12,11 @@ import java.util.UUID; public class TwoPlayerDuel extends GameImpl { public TwoPlayerDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + this(attackOption, range, mulligan, startLife, 60); + } + + public TwoPlayerDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife, int startingSize) { + super(attackOption, range, mulligan, startLife, startingSize); } public TwoPlayerDuel(final TwoPlayerDuel game) { diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java index 9705f03e5d..720ae4fd24 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java @@ -6,7 +6,6 @@ import mage.game.match.MatchOptions; import mage.game.mulligan.Mulligan; /** - * * @author BetaSteward_at_googlemail.com */ public class TwoPlayerMatch extends MatchImpl { @@ -18,7 +17,7 @@ public class TwoPlayerMatch extends MatchImpl { @Override public void startGame() throws GameException { Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans()); - TwoPlayerDuel game = new TwoPlayerDuel(options.getAttackOption(), options.getRange(), mulligan, 20); + TwoPlayerDuel game = new TwoPlayerDuel(options.getAttackOption(), options.getRange(), mulligan, 20, options.isLimited() ? 40 : 60); // Sets a start message about the match score game.setStartMessage(this.createGameStartMessage()); initGame(game); diff --git a/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml b/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml index b46e30e798..55479c566d 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-player-ai-draftbot diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml b/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml index b25b4bbeab..a93606a948 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-player-ai-ma diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index 54ac89b928..771dc4afcd 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -3,6 +3,7 @@ package mage.player.ai; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.AbilityImpl; +import mage.abilities.ActivatedAbility; import mage.abilities.TriggeredAbility; import mage.abilities.common.PassAbility; import mage.abilities.costs.mana.ManaCost; @@ -94,9 +95,9 @@ public class SimulatedPlayer2 extends ComputerPlayer { } protected void simulateOptions(Game game) { - List playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer); + List playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer); playables = filterAbilities(game, playables, suggested); - for (Ability ability : playables) { + for (ActivatedAbility ability : playables) { if (ability.getAbilityType() == AbilityType.MANA) { continue; } @@ -186,15 +187,15 @@ public class SimulatedPlayer2 extends ComputerPlayer { * @param suggested * @return */ - protected List filterAbilities(Game game, List playables, List suggested) { + protected List filterAbilities(Game game, List playables, List suggested) { if (playables.isEmpty()) { return playables; } if (suggested == null || suggested.isEmpty()) { return playables; } - List filtered = new ArrayList<>(); - for (Ability ability : playables) { + List filtered = new ArrayList<>(); + for (ActivatedAbility ability : playables) { Card card = game.getCard(ability.getSourceId()); if (card != null) { for (String s : suggested) { @@ -212,7 +213,7 @@ public class SimulatedPlayer2 extends ComputerPlayer { return playables; } - protected List filterOptions(Game game, List options, Ability ability, List suggested) { + protected List filterOptions(Game game, List options, ActivatedAbility ability, List suggested) { if (options.isEmpty()) { return options; } diff --git a/Mage.Server.Plugins/Mage.Player.AI/pom.xml b/Mage.Server.Plugins/Mage.Player.AI/pom.xml index aa2e2c2d44..96d830c3e7 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AI/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-player-ai diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 4070da886f..59ae2a469b 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -1,5 +1,9 @@ package mage.player.ai; +import java.io.IOException; +import java.io.Serializable; +import java.util.*; +import java.util.Map.Entry; import mage.ConditionalMana; import mage.MageObject; import mage.MageObjectReference; @@ -57,11 +61,6 @@ import mage.util.TournamentUtil; import mage.util.TreeNode; import org.apache.log4j.Logger; -import java.io.IOException; -import java.io.Serializable; -import java.util.*; -import java.util.Map.Entry; - /** * suitable for two player games and some multiplayer games * @@ -137,8 +136,6 @@ public class ComputerPlayer extends PlayerImpl implements Player { // - target.getTargetController(), this.getId() -- player that must makes choices (must be same with this.getId) // - target.getAbilityController(), abilityControllerId -- affected player/controller for all actions/filters // - affected controler can be different from target controller (another player makes choices for controller) - - // sometimes a target selection can be made from a player that does not control the ability UUID abilityControllerId = playerId; if (target.getTargetController() != null @@ -396,12 +393,16 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } - while ((outcome.isGood() ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen()) + // exile cost workaround: exile is bad, but exile from graveyard in most cases is good (more exiled -- more good things you get, e.g. delve's pay) + boolean isRealGood = outcome.isGood() || outcome == Outcome.Exile; + while ((isRealGood ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen()) && !cards.isEmpty()) { Card pick = pickTarget(abilityControllerId, cards, outcome, target, null, game); if (pick != null) { target.addTarget(pick.getId(), null, game); cards.remove(pick); + } else { + break; } } @@ -474,7 +475,6 @@ public class ComputerPlayer extends PlayerImpl implements Player { // target - real target, make all changes and add targets to it // target.getOriginalTarget() - copy spell effect replaces original target with TargetWithAdditionalFilter // use originalTarget to get filters and target class info - // source can be null (as example: legendary rule permanent selection) UUID sourceId = source != null ? source.getSourceId() : null; @@ -725,17 +725,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; TargetPermanentOrPlayer origTarget = ((TargetPermanentOrPlayer) target.getOriginalTarget()); - // TODO: if effect is bad and no opponent's targets available then AI can't target yourself but must by rules - /* - battlefield:Computer:Mountain:5 - hand:Computer:Viashino Pyromancer:3 - battlefield:Human:Shalai, Voice of Plenty:1 - */ // TODO: in multiplayer game there many opponents - if random opponents don't have targets then AI must use next opponent, but it skips // (e.g. you randomOpponentId must be replaced by List randomOpponents) - // normal cycle (good for you, bad for opponents) - // possible good/bad permanents if (outcome.isGood()) { targets = threats(abilityControllerId, source.getSourceId(), ((FilterPermanentOrPlayer) target.getFilter()).getPermanentFilter(), game, target.getTargets()); @@ -778,6 +770,16 @@ public class ComputerPlayer extends PlayerImpl implements Player { return tryAddTarget(target, randomOpponentId, source, game); } + // try target player as bad (bad on itself, good on opponent) + for (UUID opponentId : game.getOpponents(abilityControllerId)) { + if (target.canTarget(abilityControllerId, opponentId, source, game)) { + return tryAddTarget(target, opponentId, source, game); + } + } + if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { + return tryAddTarget(target, abilityControllerId, source, game); + } + return false; } @@ -1267,7 +1269,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { //play a land that will allow us to play an unplayable for (Mana mana : unplayable.keySet()) { for (Card card : lands) { - for (ActivatedManaAbilityImpl ability : card.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD)) { + for (ActivatedManaAbilityImpl ability : card.getAbilities(game).getActivatedManaAbilities(Zone.BATTLEFIELD)) { for (Mana netMana : ability.getNetMana(game)) { if (netMana.enough(mana)) { this.playLand(card, game, false); @@ -1281,7 +1283,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { //play a land that will get us closer to playing an unplayable for (Mana mana : unplayable.keySet()) { for (Card card : lands) { - for (ActivatedManaAbilityImpl ability : card.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD)) { + for (ActivatedManaAbilityImpl ability : card.getAbilities(game).getActivatedManaAbilities(Zone.BATTLEFIELD)) { for (Mana netMana : ability.getNetMana(game)) { if (mana.contains(netMana)) { this.playLand(card, game, false); @@ -1304,7 +1306,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { playableAbilities.clear(); Set nonLands = hand.getCards(new FilterNonlandCard(), game); ManaOptions available = getManaAvailable(game); - available.addMana(manaPool.getMana()); +// available.addMana(manaPool.getMana()); for (Card card : nonLands) { ManaOptions options = card.getManaCost().getOptions(); @@ -1321,7 +1323,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (ability != null && ability.canActivate(playerId, game).canActivate() && !game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getSourceId(), ability.getSourceId(), playerId), ability, game, true)) { if (card.getCardType().contains(CardType.INSTANT) - || card.hasAbility(FlashAbility.getInstance().getId(), game)) { + || card.hasAbility(FlashAbility.getInstance(), game)) { playableInstant.add(card); } else { playableNonInstant.add(card); @@ -1362,7 +1364,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } for (Card card : graveyard.getCards(game)) { - for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.GRAVEYARD)) { + for (ActivatedAbility ability : card.getAbilities(game).getActivatedAbilities(Zone.GRAVEYARD)) { if (ability.canActivate(playerId, game).canActivate()) { ManaOptions abilityOptions = ability.getManaCosts().getOptions(); if (abilityOptions.isEmpty()) { @@ -1529,10 +1531,34 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } } + // pay phyrexian life costs if (cost instanceof PhyrexianManaCost) { return cost.pay(null, game, null, playerId, false, null) || permittingObject != null; } + + // pay special mana like convoke cost (tap for pay) + // GUI: user see "special" button while pay spell's cost + // TODO: AI can't prioritize special mana types to pay, e.g. it will use first available + SpecialAction specialAction = game.getState().getSpecialActions().getControlledBy(this.getId(), true) + .values().stream().findFirst().orElse(null); + ManaOptions specialMana = specialAction == null ? null : specialAction.getManaOptions(ability, game, unpaid); + if (specialMana != null) { + for (Mana netMana : specialMana) { + if (cost.testPay(netMana) || permittingObject != null) { + if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { + continue; + } + specialAction.setUnpaidMana(unpaid); + if (activateAbility(specialAction, game)) { + return true; + } + // only one time try to pay + break; + } + } + } + return false; } @@ -1895,27 +1921,6 @@ public class ComputerPlayer extends PlayerImpl implements Player { return 0; } - @Override - public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { - switch (ability.getSpellAbilityType()) { - case SPLIT: - case SPLIT_FUSED: - case SPLIT_AFTERMATH: - MageObject object = game.getObject(ability.getSourceId()); - if (object != null) { - LinkedHashMap useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game); - if (useableAbilities != null && !useableAbilities.isEmpty()) { - // game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(useableAbilities.values())); - // TODO: Improve this - return (SpellAbility) useableAbilities.values().iterator().next(); - } - } - return null; - default: - return ability; - } - } - @Override public Mode chooseMode(Modes modes, Ability source, Game game) { log.debug("chooseMode"); @@ -2434,7 +2439,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { simulations = new TreeNode<>(combat); addBlockSimulations(blockers, simulations, game); - combat.simulate(); + combat.simulate(game); return getWorstSimulation(simulations); @@ -2452,7 +2457,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { TreeNode child = new TreeNode<>(combat); node.addChild(child); addBlockSimulations(subList, child, game); - combat.simulate(); + combat.simulate(game); } } } @@ -2551,7 +2556,6 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } - protected List threats(UUID playerId, UUID sourceId, FilterPermanent filter, Game game, List targets) { return threats(playerId, sourceId, filter, game, targets, true); } @@ -2677,7 +2681,6 @@ public class ComputerPlayer extends PlayerImpl implements Player { return before != after; } - /** * Sets a possible target player */ diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CombatGroupSimulator.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CombatGroupSimulator.java index 874a4dd131..ffa8bdee0e 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CombatGroupSimulator.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CombatGroupSimulator.java @@ -1,5 +1,3 @@ - - package mage.player.ai.simulators; import mage.game.Game; @@ -52,15 +50,15 @@ public class CombatGroupSimulator implements Serializable { return blocker.canBlock(attacker.id, game); } - public void simulateCombat() { + public void simulateCombat(Game game) { unblockedDamage = 0; if (hasFirstOrDoubleStrike()) - assignDamage(true); - assignDamage(false); + assignDamage(true, game); + assignDamage(false, game); } - private void assignDamage(boolean first) { + private void assignDamage(boolean first, Game game) { if (blockers.isEmpty()) { if (canDamage(attacker, first)) unblockedDamage += attacker.power; @@ -69,7 +67,7 @@ public class CombatGroupSimulator implements Serializable { CreatureSimulator blocker = blockers.get(0); if (canDamage(attacker, first)) { if (attacker.hasTrample) { - int lethalDamage = blocker.getLethalDamage(); + int lethalDamage = blocker.getLethalDamage(game); if (attacker.power > lethalDamage) { blocker.damage += lethalDamage; unblockedDamage += attacker.power - lethalDamage; @@ -87,7 +85,7 @@ public class CombatGroupSimulator implements Serializable { int damage = attacker.power; for (CreatureSimulator blocker: blockers) { if (damage > 0 && canDamage(attacker, first)) { - int lethalDamage = blocker.getLethalDamage(); + int lethalDamage = blocker.getLethalDamage(game); if (damage > lethalDamage) { blocker.damage += lethalDamage; damage -= lethalDamage; diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CombatSimulator.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CombatSimulator.java index 79f809bbda..7157962a52 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CombatSimulator.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CombatSimulator.java @@ -1,5 +1,3 @@ - - package mage.player.ai.simulators; import mage.counters.CounterType; @@ -52,9 +50,9 @@ public class CombatSimulator implements Serializable { attackerId = null; } - public void simulate() { + public void simulate(Game game) { for (CombatGroupSimulator group: groups) { - group.simulateCombat(); + group.simulateCombat(game); } } diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CreatureSimulator.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CreatureSimulator.java index e6ef4aafdd..1a5e7fe7b8 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CreatureSimulator.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/CreatureSimulator.java @@ -1,14 +1,16 @@ - - package mage.player.ai.simulators; -import java.io.Serializable; -import java.util.UUID; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.TrampleAbility; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; import mage.game.permanent.Permanent; +import java.io.Serializable; +import java.util.List; +import java.util.UUID; + /** * * @author BetaSteward_at_googlemail.com @@ -21,6 +23,7 @@ public class CreatureSimulator implements Serializable { public boolean hasFirstStrike; public boolean hasDoubleStrike; public boolean hasTrample; + public Permanent permanent; public CreatureSimulator(Permanent permanent) { this.id = permanent.getId(); @@ -30,13 +33,25 @@ public class CreatureSimulator implements Serializable { this.hasDoubleStrike = permanent.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId()); this.hasFirstStrike = permanent.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId()); this.hasTrample = permanent.getAbilities().containsKey(TrampleAbility.getInstance().getId()); + this.permanent = permanent; } public boolean isDead() { return damage >= toughness; } - public int getLethalDamage() { - return toughness - damage; + public int getLethalDamage(Game game) { + List usePowerInsteadOfToughnessForDamageLethalityFilters = game.getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters(); + /* + * for handling Zilortha, Strength Incarnate: + * 2020-04-17 + * Any time the game is checking whether damage is lethal or if a creature should be destroyed for having lethal damage marked on it, use the power of your creatures rather than their toughness to check the damage against. This includes being assigned trample damage, damage from Flame Spill, and so on. + */ + boolean usePowerInsteadOfToughnessForDamageLethality = usePowerInsteadOfToughnessForDamageLethalityFilters.stream() + .anyMatch(filter -> filter.match(permanent, game)); + int lethalDamageThreshold = usePowerInsteadOfToughnessForDamageLethality ? + // Zilortha, Strength Incarnate, 2020-04-17: A creature with 0 power isn’t destroyed unless it has at least 1 damage marked on it. + Math.max(power, 1) : toughness; + return Math.max(lethalDamageThreshold - damage, 0); } } diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml b/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml index cbaf2a5aad..cb157bfa73 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-player-ai-mcts diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java index 64d458d588..08fcecdd00 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java @@ -1,12 +1,12 @@ package mage.player.ai; -import mage.constants.PhaseStep; -import mage.constants.RangeOfInfluence; -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.common.PassAbility; import mage.cards.Card; +import mage.constants.PhaseStep; +import mage.constants.RangeOfInfluence; +import mage.constants.Zone; import mage.game.Game; import mage.game.combat.Combat; import mage.game.combat.CombatGroup; @@ -23,7 +23,6 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; /** - * * @author BetaSteward_at_googlemail.com */ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { @@ -59,6 +58,7 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { } protected String lastPhase = ""; + @Override public boolean priority(Game game) { if (game.getStep().getType() == PhaseStep.UPKEEP) { @@ -78,7 +78,7 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { Ability ability = root.getAction(); if (ability == null) logger.fatal("null ability"); - activateAbility((ActivatedAbility)ability, game); + activateAbility((ActivatedAbility) ability, game); if (ability instanceof PassAbility) return false; logLife(game); @@ -104,8 +104,7 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { newRoot = root.getMatchingState(game.getState().getValue(game, playerId)); if (newRoot != null) { newRoot.emancipate(); - } - else + } else logger.info("unable to find matching state"); root = newRoot; } @@ -186,7 +185,7 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { getNextAction(game, NextAction.SELECT_ATTACKERS); Combat combat = root.getCombat(); UUID opponentId = game.getCombat().getDefenders().iterator().next(); - for (UUID attackerId: combat.getAttackers()) { + for (UUID attackerId : combat.getAttackers()) { this.declareAttacker(attackerId, opponentId, game, false); sb.append(game.getPermanent(attackerId).getName()).append(','); } @@ -199,14 +198,19 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { StringBuilder sb = new StringBuilder(); sb.append(game.getTurn().getValue(game.getTurnNum())).append(" player ").append(name).append(" blocking: "); getNextAction(game, NextAction.SELECT_BLOCKERS); - Combat combat = root.getCombat(); - List groups = game.getCombat().getGroups(); - for (int i = 0; i < groups.size(); i++) { - if (i < combat.getGroups().size()) { - sb.append(game.getPermanent(groups.get(i).getAttackers().get(0)).getName()).append(" with: "); - for (UUID blockerId: combat.getGroups().get(i).getBlockers()) { - this.declareBlocker(this.getId(), blockerId, groups.get(i).getAttackers().get(0), game); - sb.append(game.getPermanent(blockerId).getName()).append(','); + Combat simulatedCombat = root.getCombat(); + List currentGroups = game.getCombat().getGroups(); + for (int i = 0; i < currentGroups.size(); i++) { + if (i < simulatedCombat.getGroups().size()) { + CombatGroup currentGroup = currentGroups.get(i); + CombatGroup simulatedGroup = simulatedCombat.getGroups().get(i); + sb.append(game.getPermanent(currentGroup.getAttackers().get(0)).getName()).append(" with: "); + for (UUID blockerId : simulatedGroup.getBlockers()) { + // blockers can be added automaticly by requirement effects, so we must add only missing blockers + if (!currentGroup.getBlockers().contains(blockerId)) { + this.declareBlocker(this.getId(), blockerId, currentGroup.getAttackers().get(0), game); + sb.append(game.getPermanent(blockerId).getName()).append(','); + } } sb.append('|'); } @@ -252,10 +256,11 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { protected long totalThinkTime = 0; protected long totalSimulations = 0; + protected void applyMCTS(final Game game, final NextAction action) { - + int thinkTime = calculateThinkTime(game, action); - + if (thinkTime > 0) { if (USE_MULTIPLE_THREADS) { ExecutorService pool = Executors.newFixedThreadPool(poolSize); @@ -275,9 +280,9 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { } catch (InterruptedException | RejectedExecutionException ex) { logger.warn("applyMCTS interrupted"); } - + int simCount = 0; - for (MCTSExecutor task: tasks) { + for (MCTSExecutor task : tasks) { simCount += task.getSimCount(); root.merge(task.getRoot()); task.clear(); @@ -286,10 +291,9 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { totalThinkTime += thinkTime; totalSimulations += simCount; logger.info("Player: " + name + " Simulated " + simCount + " games in " + thinkTime + " seconds - nodes in tree: " + root.size()); - logger.info("Total: Simulated " + totalSimulations + " games in " + totalThinkTime + " seconds - Average: " + totalSimulations/totalThinkTime); + logger.info("Total: Simulated " + totalSimulations + " games in " + totalThinkTime + " seconds - Average: " + totalSimulations / totalThinkTime); MCTSNode.logHitMiss(); - } - else { + } else { long startTime = System.nanoTime(); long endTime = startTime + (thinkTime * 1000000000l); MCTSNode current; @@ -314,9 +318,8 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { current = current.select(this.playerId); result = current.simulate(this.playerId); simCount++; - } - else { - result = current.isWinner(this.playerId)?1:-1; + } else { + result = current.isWinner(this.playerId) ? 1 : -1; } // Backpropagation current.backpropagate(result); @@ -339,30 +342,23 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { if (action == NextAction.SELECT_ATTACKERS || action == NextAction.SELECT_BLOCKERS) { if (nodeSizeRatio < THINK_MIN_RATIO) { thinkTime = maxThinkTime; - } - else if (nodeSizeRatio >= THINK_MAX_RATIO) { + } else if (nodeSizeRatio >= THINK_MAX_RATIO) { thinkTime = 0; - } - else { + } else { thinkTime = maxThinkTime / 2; } - } - else if (game.isActivePlayer(playerId) && (curStep == PhaseStep.PRECOMBAT_MAIN || curStep == PhaseStep.POSTCOMBAT_MAIN) && game.getStack().isEmpty()) { + } else if (game.isActivePlayer(playerId) && (curStep == PhaseStep.PRECOMBAT_MAIN || curStep == PhaseStep.POSTCOMBAT_MAIN) && game.getStack().isEmpty()) { if (nodeSizeRatio < THINK_MIN_RATIO) { thinkTime = maxThinkTime; - } - else if (nodeSizeRatio >= THINK_MAX_RATIO) { + } else if (nodeSizeRatio >= THINK_MAX_RATIO) { thinkTime = 0; - } - else { + } else { thinkTime = maxThinkTime / 2; } - } - else { + } else { if (nodeSizeRatio < THINK_MIN_RATIO) { thinkTime = maxThinkTime / 2; - } - else { + } else { thinkTime = 0; } } @@ -382,7 +378,7 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { protected Game createMCTSGame(Game game) { Game mcts = game.copy(); - for (Player copyPlayer: mcts.getState().getPlayers().values()) { + for (Player copyPlayer : mcts.getState().getPlayers().values()) { Player origPlayer = game.getState().getPlayers().get(copyPlayer.getId()); MCTSPlayer newPlayer = new MCTSPlayer(copyPlayer.getId()); newPlayer.restore(origPlayer); @@ -396,9 +392,8 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { card.setZone(Zone.HAND, mcts); newPlayer.getHand().add(card); } - } - else { - newPlayer.getLibrary().shuffle(); + } else { + newPlayer.getLibrary().shuffle(); } mcts.getState().getPlayers().put(copyPlayer.getId(), newPlayer); } @@ -414,16 +409,16 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { long heapUsedSize = heapSize - heapFreeSize; long mb = 1024 * 1024; - logger.info("Max heap size: " + heapMaxSize/mb + " Heap size: " + heapSize/mb + " Used: " + heapUsedSize/mb); + logger.info("Max heap size: " + heapMaxSize / mb + " Heap size: " + heapSize / mb + " Used: " + heapUsedSize / mb); } - + protected void logLife(Game game) { StringBuilder sb = new StringBuilder(); sb.append(game.getTurn().getValue(game.getTurnNum())); - for (Player player: game.getPlayers().values()) { + for (Player player : game.getPlayers().values()) { sb.append("[player ").append(player.getName()).append(':').append(player.getLife()).append(']'); } - logger.info(sb.toString()); + logger.info(sb.toString()); } } diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java index 5dedfe41d7..bdd731ffc4 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java @@ -2,6 +2,7 @@ package mage.player.ai; import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; import mage.abilities.SpellAbility; import mage.abilities.common.PassAbility; import mage.abilities.costs.mana.GenericManaCost; @@ -45,16 +46,16 @@ public class MCTSPlayer extends ComputerPlayer { return new MCTSPlayer(this); } - protected List getPlayableAbilities(Game game) { - List playables = getPlayable(game, true); + protected List getPlayableAbilities(Game game) { + List playables = getPlayable(game, true); playables.add(pass); return playables; } public List getPlayableOptions(Game game) { List all = new ArrayList<>(); - List playables = getPlayableAbilities(game); - for (Ability ability: playables) { + List playables = getPlayableAbilities(game); + for (ActivatedAbility ability: playables) { List options = game.getPlayer(playerId).getPlayableOptions(ability, game); if (options.isEmpty()) { if (!ability.getManaCosts().getVariableCosts().isEmpty()) { diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java index 0b3c44f268..957f43e5ac 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java @@ -73,7 +73,7 @@ public class SimulatedPlayerMCTS extends MCTSPlayer { } private Ability getAction(Game game) { - List playables = getPlayableAbilities(game); + List playables = getPlayableAbilities(game); Ability ability; while (true) { if (playables.size() == 1) { diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml b/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml index 619b2d17ad..c4410a97f6 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-player-aiminimax diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java index 3bc58353c7..4b141ec80a 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java @@ -3,6 +3,7 @@ package mage.player.ai; import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbility; import mage.abilities.common.PassAbility; @@ -59,10 +60,10 @@ public class SimulatedPlayer extends ComputerPlayer { return list; } - protected void simulateOptions(Game game, Ability previousActions) { + protected void simulateOptions(Game game, ActivatedAbility previousActions) { allActions.add(previousActions); - List playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer); - for (Ability ability: playables) { + List playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer); + for (ActivatedAbility ability: playables) { List options = game.getPlayer(playerId).getPlayableOptions(ability, game); if (options.isEmpty()) { if (!ability.getManaCosts().getVariableCosts().isEmpty()) { diff --git a/Mage.Server.Plugins/Mage.Player.Human/pom.xml b/Mage.Server.Plugins/Mage.Player.Human/pom.xml index ab624dec8c..78f98e273c 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.Human/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-player-human diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index bc08e0aa8d..8cd1470de5 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -10,6 +10,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.RequirementEffect; import mage.abilities.hint.HintUtils; import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.abilities.mana.ManaAbility; import mage.cards.Card; import mage.cards.Cards; import mage.cards.decks.Deck; @@ -61,6 +62,8 @@ import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL; */ public class HumanPlayer extends PlayerImpl { + private static final boolean ALLOW_USERS_TO_PUT_NON_PLAYABLE_SPELLS_ON_STACK_WORKAROUND = false; // warning, see workaround's info on usage + private transient Boolean responseOpenedForAnswer = false; // can't get response until prepared target (e.g. until send all fire events to all players) private final transient PlayerResponse response = new PlayerResponse(); @@ -134,8 +137,8 @@ public class HumanPlayer extends PlayerImpl { while (!responseOpenedForAnswer && canRespond()) { numTimesWaiting++; if (numTimesWaiting >= 300) { - // game freezed -- need to report about error and continue to execute - String s = "Game freezed in waitResponseOpen for user " + getName() + " (connection problem)"; + // game frozen -- need to report about error and continue to execute + String s = "Game frozen in waitResponseOpen for user " + getName() + " (connection problem)"; logger.warn(s); break; } @@ -1039,7 +1042,7 @@ public class HumanPlayer extends PlayerImpl { if (response.getString() != null && response.getString().equals("special")) { - specialAction(game); + activateSpecialAction(game, null); } else if (response.getUUID() != null) { boolean result = false; MageObject object = game.getObject(response.getUUID()); @@ -1047,24 +1050,42 @@ public class HumanPlayer extends PlayerImpl { Zone zone = game.getState().getZone(object.getId()); if (zone != null) { // look at card or try to cast/activate abilities + LinkedHashMap useableAbilities = new LinkedHashMap<>(); + Player actingPlayer = null; - LinkedHashMap useableAbilities = null; if (playerId.equals(game.getPriorityPlayerId())) { actingPlayer = this; } else if (getPlayersUnderYourControl().contains(game.getPriorityPlayerId())) { actingPlayer = game.getPlayer(game.getPriorityPlayerId()); } if (actingPlayer != null) { - useableAbilities = actingPlayer.getUseableActivatedAbilities(object, zone, game); + useableAbilities = actingPlayer.getPlayableActivatedAbilities(object, zone, game); + + // GUI: workaround to enable users to put spells on stack without real available mana + // (without highlighting, like it was in old versions before June 2020) + // Reason: some gain ability adds cost modification and other things to spells on stack only, + // e.g. xmage can't find playable ability before put that spell on stack (wtf example: Chief Engineer, + // see ConvokeTest) + // TODO: it's a BAD workaround -- users can't see that card/ability is broken and will not report to us, AI can't play that ability too + // Enable it on massive broken cards/abilities only or for manual tests + if (ALLOW_USERS_TO_PUT_NON_PLAYABLE_SPELLS_ON_STACK_WORKAROUND) { + if (object instanceof Card) { + for (Ability ability : ((Card) object).getAbilities(game)) { + if (ability instanceof SpellAbility && ((SpellAbility) ability).canActivate(actingPlayer.getId(), game).canActivate() + || ability instanceof PlayLandAbility) { + useableAbilities.putIfAbsent(ability.getId(), (ActivatedAbility) ability); + } + } + } + } } if (object instanceof Card && ((Card) object).isFaceDown(game) - && lookAtFaceDownCard((Card) object, game, useableAbilities == null ? 0 : useableAbilities.size())) { + && lookAtFaceDownCard((Card) object, game, useableAbilities.size())) { result = true; } else { - if (useableAbilities != null - && !useableAbilities.isEmpty()) { + if (!useableAbilities.isEmpty()) { activateAbility(useableAbilities, object, game); result = true; } @@ -1221,7 +1242,7 @@ public class HumanPlayer extends PlayerImpl { } else if (response.getString() != null && response.getString().equals("special")) { if (unpaid instanceof ManaCostsImpl) { - specialManaAction(unpaid, game); + activateSpecialAction(game, unpaid); } } else if (response.getManaType() != null) { // this mana type can be paid once from pool @@ -1336,19 +1357,30 @@ public class HumanPlayer extends PlayerImpl { if (object == null) { return; } - if (AbilityType.SPELL.equals(abilityToCast.getAbilityType())) { + + // GUI: for user's information only - check if mana abilities allows to use here (getUseableManaAbilities already filter it) + // Reason: when you use special mana ability then normal mana abilities will be restricted to pay. Users + // can't see lands as playable and must know the reason (if they click on land then they get that message) + if (abilityToCast.getAbilityType() == AbilityType.SPELL) { Spell spell = game.getStack().getSpell(abilityToCast.getSourceId()); - if (spell != null && !spell.isResolving() - && spell.isDoneActivatingManaAbilities()) { - game.informPlayer(this, "You can no longer use activated mana abilities to pay for the current spell. Cancel and recast the spell and activate mana abilities first."); - return; + boolean haveManaAbilities = object.getAbilities().stream().anyMatch(a -> a instanceof ManaAbility); + if (spell != null && !spell.isResolving() && haveManaAbilities) { + switch (spell.getCurrentActivatingManaAbilitiesStep()) { + // if you used special mana ability like convoke then normal mana abilities will be restricted to use, see Convoke for details + case BEFORE: + case NORMAL: + break; + case AFTER: + game.informPlayer(this, "You can no longer use activated mana abilities to pay for the current spell (special mana pay already used). Cancel and recast the spell to activate mana abilities first."); + return; + } } } + Zone zone = game.getState().getZone(object.getId()); if (zone != null) { LinkedHashMap useableAbilities = getUseableManaAbilities(object, zone, game); - if (useableAbilities != null - && !useableAbilities.isEmpty()) { + if (!useableAbilities.isEmpty()) { useableAbilities = ManaUtil.tryToAutoPay(unpaid, useableAbilities); // eliminates other abilities if one fits perfectly currentlyUnpaidMana = unpaid; activateAbility(useableAbilities, object, game); @@ -1845,7 +1877,13 @@ public class HumanPlayer extends PlayerImpl { draft.firePickCardEvent(playerId); } - protected void specialAction(Game game) { + /** + * Activate special action (normal or mana) + * + * @param game + * @param unpaidForManaAction - set unpaid for mana actions like convoke + */ + protected void activateSpecialAction(Game game, ManaCost unpaidForManaAction) { if (gameInCheckPlayableState(game)) { return; } @@ -1854,7 +1892,7 @@ public class HumanPlayer extends PlayerImpl { return; } - Map specialActions = game.getState().getSpecialActions().getControlledBy(playerId, false); + Map specialActions = game.getState().getSpecialActions().getControlledBy(playerId, unpaidForManaAction != null); if (!specialActions.isEmpty()) { updateGameStatePriority("specialAction", game); @@ -1864,39 +1902,13 @@ public class HumanPlayer extends PlayerImpl { } waitForResponse(game); - if (response.getUUID() != null) { - if (specialActions.containsKey(response.getUUID())) { - activateAbility(specialActions.get(response.getUUID()), game); - } - } - } - } - - protected void specialManaAction(ManaCost unpaid, Game game) { - if (gameInCheckPlayableState(game)) { - return; - } - - if (!canRespond()) { - return; - } - - Map specialActions = game.getState().getSpecialActions().getControlledBy(playerId, true); - if (!specialActions.isEmpty()) { - updateGameStatePriority("specialAction", game); - prepareForResponse(game); - if (!isExecutingMacro()) { - game.fireGetChoiceEvent(playerId, name, null, new ArrayList<>(specialActions.values())); - } - waitForResponse(game); - if (response.getUUID() != null) { if (specialActions.containsKey(response.getUUID())) { SpecialAction specialAction = specialActions.get(response.getUUID()); - if (specialAction != null) { - specialAction.setUnpaidMana(unpaid); - activateAbility(specialActions.get(response.getUUID()), game); + if (unpaidForManaAction != null) { + specialAction.setUnpaidMana(unpaidForManaAction); } + activateAbility(specialAction, game); } } } @@ -1952,7 +1964,7 @@ public class HumanPlayer extends PlayerImpl { } waitForResponse(game); - if (response.getUUID() != null && isInGame()) { + if (response.getUUID() != null) { if (abilities.containsKey(response.getUUID())) { activateAbility(abilities.get(response.getUUID()), game); } @@ -1973,51 +1985,6 @@ public class HumanPlayer extends PlayerImpl { return true; } - @Override - public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { - if (gameInCheckPlayableState(game)) { - return null; - } - - // TODO: add canRespond cycle? - if (!canRespond()) { - return null; - } - - switch (ability.getSpellAbilityType()) { - case SPLIT: - case SPLIT_FUSED: - case SPLIT_AFTERMATH: - MageObject object = game.getObject(ability.getSourceId()); - if (object != null) { - String message = "Choose ability to cast" + (noMana ? " for FREE" : "") + "
" + object.getLogName(); - LinkedHashMap useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game); - if (useableAbilities != null - && useableAbilities.size() == 1) { - return (SpellAbility) useableAbilities.values().iterator().next(); - } else if (useableAbilities != null - && !useableAbilities.isEmpty()) { - - updateGameStatePriority("chooseSpellAbilityForCast", game); - prepareForResponse(game); - if (!isExecutingMacro()) { - game.fireGetChoiceEvent(playerId, message, object, new ArrayList<>(useableAbilities.values())); - } - waitForResponse(game); - - if (response.getUUID() != null) { - if (useableAbilities.containsKey(response.getUUID())) { - return (SpellAbility) useableAbilities.get(response.getUUID()); - } - } - } - } - return null; - default: - return ability; - } - } - @Override public SpellAbility chooseAbilityForCast(Card card, Game game, boolean nonMana) { if (gameInCheckPlayableState(game)) { @@ -2096,6 +2063,9 @@ public class HumanPlayer extends PlayerImpl { modeText = "(selected " + timesSelected + "x) " + modeText; } } + if (!modeText.isEmpty()) { + modeText = Character.toUpperCase(modeText.charAt(0)) + modeText.substring(1); + } modeMap.put(mode.getId(), modeIndex + ". " + modeText); } } @@ -2140,14 +2110,15 @@ public class HumanPlayer extends PlayerImpl { // cancel choice (remove all selections) if (Modes.CHOOSE_OPTION_CANCEL_ID.equals(response.getUUID())) { - modes.getSelectedModes().clear(); - return null; + modes.clearSelectedModes(); } } else if (canEndChoice) { // end choice by done button in feedback panel // disable after done option implemented // done = true; } + + // triggered abilities can't be skipped by cancel or wrong answer if (source.getAbilityType() != AbilityType.TRIGGERED) { done = true; } diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml index 32f5eadeb1..da27dcf67a 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-tournament-boosterdraft diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MTGACube2020April.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MTGACube2020April.java new file mode 100644 index 0000000000..cecb0570ae --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MTGACube2020April.java @@ -0,0 +1,566 @@ +package mage.tournament.cubes; + +import mage.game.draft.DraftCube; + +public class MTGACube2020April extends DraftCube { + + public MTGACube2020April() { + super("MTGA Cube 2020 April"); // https://magic.wizards.com/en/articles/archive/magic-digital/mtg-arena-cube-sealed-card-list-2020-04-03 + + cubeCards.add(new CardIdentity("Adanto Vanguard", "")); + cubeCards.add(new CardIdentity("Admiral's Order", "")); + cubeCards.add(new CardIdentity("Agent of Treachery", "")); + cubeCards.add(new CardIdentity("Agonizing Remorse", "")); + cubeCards.add(new CardIdentity("Ajani's Pridemate", "")); + cubeCards.add(new CardIdentity("Ajani, Strength of the Pride", "")); + cubeCards.add(new CardIdentity("Ajani, the Greathearted", "")); + cubeCards.add(new CardIdentity("Alirios, Enraptured", "")); + cubeCards.add(new CardIdentity("All That Glitters", "")); + cubeCards.add(new CardIdentity("Alseid of Life's Bounty", "")); + cubeCards.add(new CardIdentity("Anax, Hardened in the Forge", "")); + cubeCards.add(new CardIdentity("Angrath's Rampage", "")); + cubeCards.add(new CardIdentity("Angrath, Captain of Chaos", "")); + cubeCards.add(new CardIdentity("Animating Faerie", "")); + cubeCards.add(new CardIdentity("Anticipate", "")); + cubeCards.add(new CardIdentity("Aphemia, the Cacophony", "")); + cubeCards.add(new CardIdentity("Arasta of the Endless Web", "")); + cubeCards.add(new CardIdentity("Arcane Encyclopedia", "")); + cubeCards.add(new CardIdentity("Arcanist's Owl", "")); + cubeCards.add(new CardIdentity("Arch of Orazca", "")); + cubeCards.add(new CardIdentity("Archon of Sun's Grace", "")); + cubeCards.add(new CardIdentity("Ardenvale Tactician", "")); + cubeCards.add(new CardIdentity("Arguel's Blood Fast", "")); + cubeCards.add(new CardIdentity("Arrester's Zeal", "")); + cubeCards.add(new CardIdentity("Aryel, Knight of Windgrace", "")); + cubeCards.add(new CardIdentity("Ashiok, Dream Render", "")); + cubeCards.add(new CardIdentity("Ashiok, Nightmare Muse", "")); + cubeCards.add(new CardIdentity("Assassin's Trophy", "")); + cubeCards.add(new CardIdentity("Atris, Oracle of Half-Truths", "")); + cubeCards.add(new CardIdentity("Audacious Thief", "")); + cubeCards.add(new CardIdentity("Augur of Bolas", "")); + cubeCards.add(new CardIdentity("Aurelia, Exemplar of Justice", "")); + cubeCards.add(new CardIdentity("Baffling End", "")); + cubeCards.add(new CardIdentity("Bake into a Pie", "")); + cubeCards.add(new CardIdentity("Banefire", "")); + cubeCards.add(new CardIdentity("Banishing Light", "")); + cubeCards.add(new CardIdentity("Barkhide Troll", "")); + cubeCards.add(new CardIdentity("Barren Moor", "")); + cubeCards.add(new CardIdentity("Beanstalk Giant", "")); + cubeCards.add(new CardIdentity("Beast Whisperer", "")); + cubeCards.add(new CardIdentity("Benthic Biomancer", "")); + cubeCards.add(new CardIdentity("Biogenic Ooze", "")); + cubeCards.add(new CardIdentity("Blackblade Reforged", "")); + cubeCards.add(new CardIdentity("Blacklance Paragon", "")); + cubeCards.add(new CardIdentity("Blade Juggler", "")); + cubeCards.add(new CardIdentity("Blast Zone", "")); + cubeCards.add(new CardIdentity("Blink of an Eye", "")); + cubeCards.add(new CardIdentity("Blood Aspirant", "")); + cubeCards.add(new CardIdentity("Blood Crypt", "")); + cubeCards.add(new CardIdentity("Blood Divination", "")); + cubeCards.add(new CardIdentity("Blood for Bones", "")); + cubeCards.add(new CardIdentity("Bloodfell Caves", "")); + cubeCards.add(new CardIdentity("Bloom Hulk", "")); + cubeCards.add(new CardIdentity("Blossoming Sands", "")); + cubeCards.add(new CardIdentity("Board the Weatherlight", "")); + cubeCards.add(new CardIdentity("Bolas's Citadel", "")); + cubeCards.add(new CardIdentity("Bond of Insight", "")); + cubeCards.add(new CardIdentity("Bonecrusher Giant", "")); + cubeCards.add(new CardIdentity("Brain Maggot", "")); + cubeCards.add(new CardIdentity("Brazen Borrower", "")); + cubeCards.add(new CardIdentity("Breeding Pool", "")); + cubeCards.add(new CardIdentity("Brineborn Cutthroat", "")); + cubeCards.add(new CardIdentity("Burning-Tree Emissary", "")); + cubeCards.add(new CardIdentity("Captain Lannery Storm", "")); + cubeCards.add(new CardIdentity("Captain Sisay", "")); + cubeCards.add(new CardIdentity("Captivating Crew", "")); + cubeCards.add(new CardIdentity("Careless Celebrant", "")); + cubeCards.add(new CardIdentity("Cast Down", "")); + cubeCards.add(new CardIdentity("Castle Ardenvale", "")); + cubeCards.add(new CardIdentity("Castle Embereth", "")); + cubeCards.add(new CardIdentity("Castle Garenbrig", "")); + cubeCards.add(new CardIdentity("Castle Locthwain", "")); + cubeCards.add(new CardIdentity("Castle Vantress", "")); + cubeCards.add(new CardIdentity("Casualties of War", "")); + cubeCards.add(new CardIdentity("Cauldron Familiar", "")); + cubeCards.add(new CardIdentity("Cauldron's Gift", "")); + cubeCards.add(new CardIdentity("Cavalcade of Calamity", "")); + cubeCards.add(new CardIdentity("Cavalier of Dawn", "")); + cubeCards.add(new CardIdentity("Cavalier of Flame", "")); + cubeCards.add(new CardIdentity("Cavalier of Night", "")); + cubeCards.add(new CardIdentity("Cavalier of Thorns", "")); + cubeCards.add(new CardIdentity("Cavalry Drillmaster", "")); + cubeCards.add(new CardIdentity("Chainweb Aracnir", "")); + cubeCards.add(new CardIdentity("Chandra's Pyrohelix", "")); + cubeCards.add(new CardIdentity("Chandra, Acolyte of Flame", "")); + cubeCards.add(new CardIdentity("Chandra, Awakened Inferno", "")); + cubeCards.add(new CardIdentity("Charging Monstrosaur", "")); + cubeCards.add(new CardIdentity("Charming Prince", "")); + cubeCards.add(new CardIdentity("Chart a Course", "")); + cubeCards.add(new CardIdentity("Chemister's Insight", "")); + cubeCards.add(new CardIdentity("Chromatic Lantern", "")); + cubeCards.add(new CardIdentity("Clifftop Retreat", "")); + cubeCards.add(new CardIdentity("Clockwork Servant", "")); + cubeCards.add(new CardIdentity("Cloudkin Seer", "")); + cubeCards.add(new CardIdentity("Colossal Majesty", "")); + cubeCards.add(new CardIdentity("Command the Dreadhorde", "")); + cubeCards.add(new CardIdentity("Commence the Endgame", "")); + cubeCards.add(new CardIdentity("Conclave Tribunal", "")); + cubeCards.add(new CardIdentity("Corpse Knight", "")); + cubeCards.add(new CardIdentity("Crash Through", "")); + cubeCards.add(new CardIdentity("Crucible of Worlds", "")); + cubeCards.add(new CardIdentity("Cryptbreaker", "")); + cubeCards.add(new CardIdentity("Cryptic Caves", "")); + cubeCards.add(new CardIdentity("Curious Obsession", "")); + cubeCards.add(new CardIdentity("Dalakos, Crafter of Wonders", "")); + cubeCards.add(new CardIdentity("Dark-Dweller Oracle", "")); + cubeCards.add(new CardIdentity("Dauntless Bodyguard", "")); + cubeCards.add(new CardIdentity("Daxos, Blessed by the Sun", "")); + cubeCards.add(new CardIdentity("Dead Weight", "")); + cubeCards.add(new CardIdentity("Deeproot Champion", "")); + cubeCards.add(new CardIdentity("Demonlord Belzenlok", "")); + cubeCards.add(new CardIdentity("Depose // Deploy", "")); + cubeCards.add(new CardIdentity("Despark", "")); + cubeCards.add(new CardIdentity("Destiny Spinner", "")); + cubeCards.add(new CardIdentity("Didn't Say Please", "")); + cubeCards.add(new CardIdentity("Dire Fleet Daredevil", "")); + cubeCards.add(new CardIdentity("Diregraf Ghoul", "")); + cubeCards.add(new CardIdentity("Disdainful Stroke", "")); + cubeCards.add(new CardIdentity("Disenchant", "")); + cubeCards.add(new CardIdentity("Disfigure", "")); + cubeCards.add(new CardIdentity("Dismal Backwater", "")); + cubeCards.add(new CardIdentity("Dive Down", "")); + cubeCards.add(new CardIdentity("Divine Visitation", "")); + cubeCards.add(new CardIdentity("Domri, Anarch of Bolas", "")); + cubeCards.add(new CardIdentity("Doom Whisperer", "")); + cubeCards.add(new CardIdentity("Dragonmaster Outcast", "")); + cubeCards.add(new CardIdentity("Dragonskull Summit", "")); + cubeCards.add(new CardIdentity("Drakuseth, Maw of Flames", "")); + cubeCards.add(new CardIdentity("Dread Presence", "")); + cubeCards.add(new CardIdentity("Dreadhorde Butcher", "")); + cubeCards.add(new CardIdentity("Dreadhorde Invasion", "")); + cubeCards.add(new CardIdentity("Dream Trawler", "")); + cubeCards.add(new CardIdentity("Dreamstalker Manticore", "")); + cubeCards.add(new CardIdentity("Drill Bit", "")); + cubeCards.add(new CardIdentity("Drowned Catacomb", "")); + cubeCards.add(new CardIdentity("Dryad Greenseeker", "")); + cubeCards.add(new CardIdentity("Dryad of the Ilysian Grove", "")); + cubeCards.add(new CardIdentity("Dungeon Geists", "")); + cubeCards.add(new CardIdentity("Duress", "")); + cubeCards.add(new CardIdentity("Dusk Legion Zealot", "")); + cubeCards.add(new CardIdentity("Eat to Extinction", "")); + cubeCards.add(new CardIdentity("Electrodominance", "")); + cubeCards.add(new CardIdentity("Elspeth Conquers Death", "")); + cubeCards.add(new CardIdentity("Elspeth's Nightmare", "")); + cubeCards.add(new CardIdentity("Elspeth, Sun's Nemesis", "")); + cubeCards.add(new CardIdentity("Elvish Reclaimer", "")); + cubeCards.add(new CardIdentity("Elvish Rejuvenator", "")); + cubeCards.add(new CardIdentity("Elvish Visionary", "")); + cubeCards.add(new CardIdentity("Ember Hauler", "")); + cubeCards.add(new CardIdentity("Embercleave", "")); + cubeCards.add(new CardIdentity("Embereth Shieldbreaker", "")); + cubeCards.add(new CardIdentity("Embodiment of Agonies", "")); + cubeCards.add(new CardIdentity("Empyrean Eagle", "")); + cubeCards.add(new CardIdentity("Emry, Lurker of the Loch", "")); + cubeCards.add(new CardIdentity("End-Raze Forerunners", "")); + cubeCards.add(new CardIdentity("Enter the God-Eternals", "")); + cubeCards.add(new CardIdentity("Entrancing Lyre", "")); + cubeCards.add(new CardIdentity("Entrancing Melody", "")); + cubeCards.add(new CardIdentity("Erebos's Intervention", "")); + cubeCards.add(new CardIdentity("Erebos, Bleak-Hearted", "")); + cubeCards.add(new CardIdentity("Eternal Taskmaster", "")); + cubeCards.add(new CardIdentity("Ethereal Absolution", "")); + cubeCards.add(new CardIdentity("Evolution Sage", "")); + cubeCards.add(new CardIdentity("Evolving Wilds", "")); + cubeCards.add(new CardIdentity("Exclusion Mage", "")); + cubeCards.add(new CardIdentity("Expansion // Explosion", "")); + cubeCards.add(new CardIdentity("Experimental Frenzy", "")); + cubeCards.add(new CardIdentity("Fabled Passage", "")); + cubeCards.add(new CardIdentity("Fae of Wishes", "")); + cubeCards.add(new CardIdentity("Faeburrow Elder", "")); + cubeCards.add(new CardIdentity("Faerie Formation", "")); + cubeCards.add(new CardIdentity("Faerie Guidemother", "")); + cubeCards.add(new CardIdentity("Fanatical Firebrand", "")); + cubeCards.add(new CardIdentity("Fauna Shaman", "")); + cubeCards.add(new CardIdentity("Favorable Winds", "")); + cubeCards.add(new CardIdentity("Fblthp, the Lost", "")); + cubeCards.add(new CardIdentity("Field of Ruin", "")); + cubeCards.add(new CardIdentity("Field of the Dead", "")); + cubeCards.add(new CardIdentity("Fiery Cannonade", "")); + cubeCards.add(new CardIdentity("Fight with Fire", "")); + cubeCards.add(new CardIdentity("Finale of Devastation", "")); + cubeCards.add(new CardIdentity("Finale of Eternity", "")); + cubeCards.add(new CardIdentity("Finale of Glory", "")); + cubeCards.add(new CardIdentity("Find // Finality", "")); + cubeCards.add(new CardIdentity("Firemind Vessel", "")); + cubeCards.add(new CardIdentity("Fires of Invention", "")); + cubeCards.add(new CardIdentity("Flame Sweep", "")); + cubeCards.add(new CardIdentity("Flaxen Intruder", "")); + cubeCards.add(new CardIdentity("Fling", "")); + cubeCards.add(new CardIdentity("Flood of Tears", "")); + cubeCards.add(new CardIdentity("Folio of Fancies", "")); + cubeCards.add(new CardIdentity("Foreboding Fruit", "")); + cubeCards.add(new CardIdentity("Forgotten Cave", "")); + cubeCards.add(new CardIdentity("Foulmire Knight", "")); + cubeCards.add(new CardIdentity("Fountain of Renewal", "")); + cubeCards.add(new CardIdentity("Frilled Sandwalla", "")); + cubeCards.add(new CardIdentity("Furious Rise", "")); + cubeCards.add(new CardIdentity("Gallia of the Endless Dance", "")); + cubeCards.add(new CardIdentity("Garruk, Cursed Huntsman", "")); + cubeCards.add(new CardIdentity("Ghalta, Primal Hunger", "")); + cubeCards.add(new CardIdentity("Ghitu Lavarunner", "")); + cubeCards.add(new CardIdentity("Giant Growth", "")); + cubeCards.add(new CardIdentity("Giant Killer", "")); + cubeCards.add(new CardIdentity("Gideon Blackblade", "")); + cubeCards.add(new CardIdentity("Gilded Goose", "")); + cubeCards.add(new CardIdentity("Gilded Lotus", "")); + cubeCards.add(new CardIdentity("Gingerbrute", "")); + cubeCards.add(new CardIdentity("Glacial Fortress", "")); + cubeCards.add(new CardIdentity("Glass Casket", "")); + cubeCards.add(new CardIdentity("Gleaming Barrier", "")); + cubeCards.add(new CardIdentity("Goblin Banneret", "")); + cubeCards.add(new CardIdentity("Goblin Cratermaker", "")); + cubeCards.add(new CardIdentity("Goblin Electromancer", "")); + cubeCards.add(new CardIdentity("Goblin Instigator", "")); + cubeCards.add(new CardIdentity("Goblin Motivator", "")); + cubeCards.add(new CardIdentity("Goblin Ruinblaster", "")); + cubeCards.add(new CardIdentity("Goblin Trashmaster", "")); + cubeCards.add(new CardIdentity("God-Eternal Bontu", "")); + cubeCards.add(new CardIdentity("God-Eternal Kefnet", "")); + cubeCards.add(new CardIdentity("God-Eternal Oketra", "")); + cubeCards.add(new CardIdentity("Godless Shrine", "")); + cubeCards.add(new CardIdentity("Gods Willing", "")); + cubeCards.add(new CardIdentity("Golden Demise", "")); + cubeCards.add(new CardIdentity("Golden Egg", "")); + cubeCards.add(new CardIdentity("Golos, Tireless Pilgrim", "")); + cubeCards.add(new CardIdentity("Goreclaw, Terror of Qal Sisma", "")); + cubeCards.add(new CardIdentity("Graveyard Marshal", "")); + cubeCards.add(new CardIdentity("Gray Merchant of Asphodel", "")); + cubeCards.add(new CardIdentity("Grim Initiate", "")); + cubeCards.add(new CardIdentity("Grow from the Ashes", "")); + cubeCards.add(new CardIdentity("Growth Spiral", "")); + cubeCards.add(new CardIdentity("Gruul Spellbreaker", "")); + cubeCards.add(new CardIdentity("Guild Globe", "")); + cubeCards.add(new CardIdentity("Gutterbones", "")); + cubeCards.add(new CardIdentity("Guttersnipe", "")); + cubeCards.add(new CardIdentity("Hallowed Fountain", "")); + cubeCards.add(new CardIdentity("Hanged Executioner", "")); + cubeCards.add(new CardIdentity("Harmonious Archon", "")); + cubeCards.add(new CardIdentity("Heliod, Sun-Crowned", "")); + cubeCards.add(new CardIdentity("Helm of the Host", "")); + cubeCards.add(new CardIdentity("Heraldic Banner", "")); + cubeCards.add(new CardIdentity("Hinterland Harbor", "")); + cubeCards.add(new CardIdentity("History of Benalia", "")); + cubeCards.add(new CardIdentity("Hostage Taker", "")); + cubeCards.add(new CardIdentity("Huatli, Warrior Poet", "")); + cubeCards.add(new CardIdentity("Hunted Witness", "")); + cubeCards.add(new CardIdentity("Hydroid Krasis", "")); + cubeCards.add(new CardIdentity("Hypnotic Specter", "")); + cubeCards.add(new CardIdentity("Hypnotic Sprite", "")); + cubeCards.add(new CardIdentity("Icy Manipulator", "")); + cubeCards.add(new CardIdentity("Ilharg, the Raze-Boar", "")); + cubeCards.add(new CardIdentity("Ilysian Caryatid", "")); + cubeCards.add(new CardIdentity("Imperial Aerosaur", "")); + cubeCards.add(new CardIdentity("Imperious Perfect", "")); + cubeCards.add(new CardIdentity("In Bolas's Clutches", "")); + cubeCards.add(new CardIdentity("Incubation Druid", "")); + cubeCards.add(new CardIdentity("Inevitable End", "")); + cubeCards.add(new CardIdentity("Into the Story", "")); + cubeCards.add(new CardIdentity("Ionize", "")); + cubeCards.add(new CardIdentity("Isareth the Awakener", "")); + cubeCards.add(new CardIdentity("Isolated Chapel", "")); + cubeCards.add(new CardIdentity("Jadelight Ranger", "")); + cubeCards.add(new CardIdentity("Jaya's Greeting", "")); + cubeCards.add(new CardIdentity("Jaya's Immolating Inferno", "")); + cubeCards.add(new CardIdentity("Jhoira, Weatherlight Captain", "")); + cubeCards.add(new CardIdentity("Josu Vess, Lich Knight", "")); + cubeCards.add(new CardIdentity("Judith, the Scourge Diva", "")); + cubeCards.add(new CardIdentity("Juggernaut", "")); + cubeCards.add(new CardIdentity("Jungle Hollow", "")); + cubeCards.add(new CardIdentity("Karn's Bastion", "")); + cubeCards.add(new CardIdentity("Karn's Temporal Sundering", "")); + cubeCards.add(new CardIdentity("Karn, Scion of Urza", "")); + cubeCards.add(new CardIdentity("Keeper of Fables", "")); + cubeCards.add(new CardIdentity("Kenrith's Transformation", "")); + cubeCards.add(new CardIdentity("Kiln Fiend", "")); + cubeCards.add(new CardIdentity("Kinjalli's Sunwing", "")); + cubeCards.add(new CardIdentity("Kiora Bests the Sea God", "")); + cubeCards.add(new CardIdentity("Kiora, Behemoth Beckoner", "")); + cubeCards.add(new CardIdentity("Kitesail Freebooter", "")); + cubeCards.add(new CardIdentity("Klothys, God of Destiny", "")); + cubeCards.add(new CardIdentity("Knight of Autumn", "")); + cubeCards.add(new CardIdentity("Knight of Grace", "")); + cubeCards.add(new CardIdentity("Knight of Malice", "")); + cubeCards.add(new CardIdentity("Knight of the Ebon Legion", "")); + cubeCards.add(new CardIdentity("Kraul Harpooner", "")); + cubeCards.add(new CardIdentity("Kronch Wrangler", "")); + cubeCards.add(new CardIdentity("Kroxa, Titan of Death's Hunger", "")); + cubeCards.add(new CardIdentity("Kunoros, Hound of Athreos", "")); + cubeCards.add(new CardIdentity("Labyrinth of Skophos", "")); + cubeCards.add(new CardIdentity("Lava Coil", "")); + cubeCards.add(new CardIdentity("Law-Rune Enforcer", "")); + cubeCards.add(new CardIdentity("Lazav, the Multifarious", "")); + cubeCards.add(new CardIdentity("Leafkin Druid", "")); + cubeCards.add(new CardIdentity("Legion Warboss", "")); + cubeCards.add(new CardIdentity("Legion's Landing", "")); + cubeCards.add(new CardIdentity("Leonin of the Lost Pride", "")); + cubeCards.add(new CardIdentity("Leonin Vanguard", "")); + cubeCards.add(new CardIdentity("Leonin Warleader", "")); + cubeCards.add(new CardIdentity("Light Up the Stage", "")); + cubeCards.add(new CardIdentity("Lightning Strike", "")); + cubeCards.add(new CardIdentity("Liliana, Dreadhorde General", "")); + cubeCards.add(new CardIdentity("Llanowar Elves", "")); + cubeCards.add(new CardIdentity("Lonely Sandbar", "")); + cubeCards.add(new CardIdentity("Lotleth Giant", "")); + cubeCards.add(new CardIdentity("Lotus Field", "")); + cubeCards.add(new CardIdentity("Lovestruck Beast", "")); + cubeCards.add(new CardIdentity("Loyal Pegasus", "")); + cubeCards.add(new CardIdentity("Lyra Dawnbringer", "")); + cubeCards.add(new CardIdentity("Mace of the Valiant", "")); + cubeCards.add(new CardIdentity("Manifold Key", "")); + cubeCards.add(new CardIdentity("Mantle of the Wolf", "")); + cubeCards.add(new CardIdentity("Massacre Girl", "")); + cubeCards.add(new CardIdentity("Mastermind's Acquisition", "")); + cubeCards.add(new CardIdentity("Medomai's Prophecy", "")); + cubeCards.add(new CardIdentity("Mentor of the Meek", "")); + cubeCards.add(new CardIdentity("Merfolk Secretkeeper", "")); + cubeCards.add(new CardIdentity("Merfolk Trickster", "")); + cubeCards.add(new CardIdentity("Midnight Clock", "")); + cubeCards.add(new CardIdentity("Midnight Reaper", "")); + cubeCards.add(new CardIdentity("Militia Bugler", "")); + cubeCards.add(new CardIdentity("Mind Stone", "")); + cubeCards.add(new CardIdentity("Ministrant of Obligation", "")); + cubeCards.add(new CardIdentity("Mire's Grasp", "")); + cubeCards.add(new CardIdentity("Mist-Cloaked Herald", "")); + cubeCards.add(new CardIdentity("Mortify", "")); + cubeCards.add(new CardIdentity("Mox Amber", "")); + cubeCards.add(new CardIdentity("Murder", "")); + cubeCards.add(new CardIdentity("Murderous Rider", "")); + cubeCards.add(new CardIdentity("Murmuring Mystic", "")); + cubeCards.add(new CardIdentity("Nadir Kraken", "")); + cubeCards.add(new CardIdentity("Negate", "")); + cubeCards.add(new CardIdentity("Nessian Hornbeetle", "")); + cubeCards.add(new CardIdentity("Nessian Wanderer", "")); + cubeCards.add(new CardIdentity("Nightmare Shepherd", "")); + cubeCards.add(new CardIdentity("Nightmare's Thirst", "")); + cubeCards.add(new CardIdentity("Niv-Mizzet Reborn", "")); + cubeCards.add(new CardIdentity("Nylea, Keen-Eyed", "")); + cubeCards.add(new CardIdentity("Nyx Lotus", "")); + cubeCards.add(new CardIdentity("Nyxbloom Ancient", "")); + cubeCards.add(new CardIdentity("Oathsworn Knight", "")); + cubeCards.add(new CardIdentity("Omenspeaker", "")); + cubeCards.add(new CardIdentity("Once and Future", "")); + cubeCards.add(new CardIdentity("Once Upon a Time", "")); + cubeCards.add(new CardIdentity("Opt", "")); + cubeCards.add(new CardIdentity("Order of Midnight", "")); + cubeCards.add(new CardIdentity("Orzhov Enforcer", "")); + cubeCards.add(new CardIdentity("Outlaws' Merriment", "")); + cubeCards.add(new CardIdentity("Overgrown Tomb", "")); + cubeCards.add(new CardIdentity("Ox of Agonas", "")); + cubeCards.add(new CardIdentity("Pacifism", "")); + cubeCards.add(new CardIdentity("Paradise Druid", "")); + cubeCards.add(new CardIdentity("Patient Rebuilding", "")); + cubeCards.add(new CardIdentity("Pelt Collector", "")); + cubeCards.add(new CardIdentity("Phoenix of Ash", "")); + cubeCards.add(new CardIdentity("Phyrexian Arena", "")); + cubeCards.add(new CardIdentity("Piper of the Swarm", "")); + cubeCards.add(new CardIdentity("Plaguecrafter", "")); + cubeCards.add(new CardIdentity("Planar Cleansing", "")); + cubeCards.add(new CardIdentity("Planewide Celebration", "")); + cubeCards.add(new CardIdentity("Platinum Angel", "")); + cubeCards.add(new CardIdentity("Polukranos, Unchained", "")); + cubeCards.add(new CardIdentity("Portal of Sanctuary", "")); + cubeCards.add(new CardIdentity("Prey Upon", "")); + cubeCards.add(new CardIdentity("Priest of Forgotten Gods", "")); + cubeCards.add(new CardIdentity("Prime Speaker Vannifar", "")); + cubeCards.add(new CardIdentity("Prison Realm", "")); + cubeCards.add(new CardIdentity("Psychic Corrosion", "")); + cubeCards.add(new CardIdentity("Pteramander", "")); + cubeCards.add(new CardIdentity("Purphoros's Intervention", "")); + cubeCards.add(new CardIdentity("Quench", "")); + cubeCards.add(new CardIdentity("Questing Beast", "")); + cubeCards.add(new CardIdentity("Rabid Bite", "")); + cubeCards.add(new CardIdentity("Ral, Izzet Viceroy", "")); + cubeCards.add(new CardIdentity("Rampaging Ferocidon", "")); + cubeCards.add(new CardIdentity("Rankle, Master of Pranks", "")); + cubeCards.add(new CardIdentity("Realm-Cloaked Giant", "")); + cubeCards.add(new CardIdentity("Reclamation Sage", "")); + cubeCards.add(new CardIdentity("Rekindling Phoenix", "")); + cubeCards.add(new CardIdentity("Relentless Pursuit", "")); + cubeCards.add(new CardIdentity("Relentless Raptor", "")); + cubeCards.add(new CardIdentity("Remorseful Cleric", "")); + cubeCards.add(new CardIdentity("Resplendent Angel", "")); + cubeCards.add(new CardIdentity("Response // Resurgence", "")); + cubeCards.add(new CardIdentity("Return to Nature", "")); + cubeCards.add(new CardIdentity("Revoke Existence", "")); + cubeCards.add(new CardIdentity("Rhys the Redeemed", "")); + cubeCards.add(new CardIdentity("Rigging Runner", "")); + cubeCards.add(new CardIdentity("Rimrock Knight", "")); + cubeCards.add(new CardIdentity("Risen Reef", "")); + cubeCards.add(new CardIdentity("Risk Factor", "")); + cubeCards.add(new CardIdentity("Roalesk, Apex Hybrid", "")); + cubeCards.add(new CardIdentity("Robber of the Rich", "")); + cubeCards.add(new CardIdentity("Rootbound Crag", "")); + cubeCards.add(new CardIdentity("Rotting Regisaur", "")); + cubeCards.add(new CardIdentity("Rugged Highlands", "")); + cubeCards.add(new CardIdentity("Ruin Raider", "")); + cubeCards.add(new CardIdentity("Rupture Spire", "")); + cubeCards.add(new CardIdentity("Sacred Foundry", "")); + cubeCards.add(new CardIdentity("Saheeli, Sublime Artificer", "")); + cubeCards.add(new CardIdentity("Sai, Master Thopterist", "")); + cubeCards.add(new CardIdentity("Saproling Migration", "")); + cubeCards.add(new CardIdentity("Sarkhan the Masterless", "")); + cubeCards.add(new CardIdentity("Savage Stomp", "")); + cubeCards.add(new CardIdentity("Savvy Hunter", "")); + cubeCards.add(new CardIdentity("Scorch Spitter", "")); + cubeCards.add(new CardIdentity("Scorching Dragonfire", "")); + cubeCards.add(new CardIdentity("Scoured Barrens", "")); + cubeCards.add(new CardIdentity("Seal Away", "")); + cubeCards.add(new CardIdentity("Search for Azcanta", "")); + cubeCards.add(new CardIdentity("Season of Growth", "")); + cubeCards.add(new CardIdentity("Secluded Steppe", "")); + cubeCards.add(new CardIdentity("Sentinel's Eyes", "")); + cubeCards.add(new CardIdentity("Sentinel's Mark", "")); + cubeCards.add(new CardIdentity("Seraph of the Scales", "")); + cubeCards.add(new CardIdentity("Setessan Champion", "")); + cubeCards.add(new CardIdentity("Settle the Wreckage", "")); + cubeCards.add(new CardIdentity("Shadowspear", "")); + cubeCards.add(new CardIdentity("Shanna, Sisay's Legacy", "")); + cubeCards.add(new CardIdentity("Shatter the Sky", "")); + cubeCards.add(new CardIdentity("Shepherd of the Flock", "")); + cubeCards.add(new CardIdentity("Shivan Fire", "")); + cubeCards.add(new CardIdentity("Shock", "")); + cubeCards.add(new CardIdentity("Siege-Gang Commander", "")); + cubeCards.add(new CardIdentity("Sigil of the Empty Throne", "")); + cubeCards.add(new CardIdentity("Sigiled Sword of Valeron", "")); + cubeCards.add(new CardIdentity("Silverbeak Griffin", "")); + cubeCards.add(new CardIdentity("Sinister Sabotage", "")); + cubeCards.add(new CardIdentity("Siren Stormtamer", "")); + cubeCards.add(new CardIdentity("Skarrgan Hellkite", "")); + cubeCards.add(new CardIdentity("Skewer the Critics", "")); + cubeCards.add(new CardIdentity("Skilled Animator", "")); + cubeCards.add(new CardIdentity("Sky Terror", "")); + cubeCards.add(new CardIdentity("Skymarcher Aspirant", "")); + cubeCards.add(new CardIdentity("Slaying Fire", "")); + cubeCards.add(new CardIdentity("Soul Warden", "")); + cubeCards.add(new CardIdentity("Spark Double", "")); + cubeCards.add(new CardIdentity("Spark Harvest", "")); + cubeCards.add(new CardIdentity("Sparring Construct", "")); + cubeCards.add(new CardIdentity("Spawn of Mayhem", "")); + cubeCards.add(new CardIdentity("Spectral Sailor", "")); + cubeCards.add(new CardIdentity("Spell Pierce", "")); + cubeCards.add(new CardIdentity("Sprouting Renewal", "")); + cubeCards.add(new CardIdentity("Squee, the Immortal", "")); + cubeCards.add(new CardIdentity("Staggering Insight", "")); + cubeCards.add(new CardIdentity("Starfield Mystic", "")); + cubeCards.add(new CardIdentity("Starlit Mantle", "")); + cubeCards.add(new CardIdentity("Steam Vents", "")); + cubeCards.add(new CardIdentity("Steel Overseer", "")); + cubeCards.add(new CardIdentity("Stolen by the Fae", "")); + cubeCards.add(new CardIdentity("Stomping Ground", "")); + cubeCards.add(new CardIdentity("Stonecoil Serpent", "")); + cubeCards.add(new CardIdentity("Storm Fleet Aerialist", "")); + cubeCards.add(new CardIdentity("Storm's Wrath", "")); + cubeCards.add(new CardIdentity("Stormfist Crusader", "")); + cubeCards.add(new CardIdentity("Sulfur Falls", "")); + cubeCards.add(new CardIdentity("Summary Judgment", "")); + cubeCards.add(new CardIdentity("Sunhome Stalwart", "")); + cubeCards.add(new CardIdentity("Sunpetal Grove", "")); + cubeCards.add(new CardIdentity("Swiftwater Cliffs", "")); + cubeCards.add(new CardIdentity("Sword-Point Diplomacy", "")); + cubeCards.add(new CardIdentity("Syr Faren, the Hengehammer", "")); + cubeCards.add(new CardIdentity("Tajic, Legion's Edge", "")); + cubeCards.add(new CardIdentity("Talrand, Sky Summoner", "")); + cubeCards.add(new CardIdentity("Taranika, Akroan Veteran", "")); + cubeCards.add(new CardIdentity("Taste of Death", "")); + cubeCards.add(new CardIdentity("Tectonic Giant", "")); + cubeCards.add(new CardIdentity("Teferi, Hero of Dominaria", "")); + cubeCards.add(new CardIdentity("Temple Garden", "")); + cubeCards.add(new CardIdentity("Temple of Abandon", "")); + cubeCards.add(new CardIdentity("Temple of Deceit", "")); + cubeCards.add(new CardIdentity("Temple of Enlightenment", "")); + cubeCards.add(new CardIdentity("Temple of Epiphany", "")); + cubeCards.add(new CardIdentity("Temple of Malady", "")); + cubeCards.add(new CardIdentity("Temple of Malice", "")); + cubeCards.add(new CardIdentity("Temple of Mystery", "")); + cubeCards.add(new CardIdentity("Temple of Plenty", "")); + cubeCards.add(new CardIdentity("Temple of Silence", "")); + cubeCards.add(new CardIdentity("Temple of Triumph", "")); + cubeCards.add(new CardIdentity("Tendershoot Dryad", "")); + cubeCards.add(new CardIdentity("Tetsuko Umezawa, Fugitive", "")); + cubeCards.add(new CardIdentity("Tezzeret, Artifice Master", "")); + cubeCards.add(new CardIdentity("Thalia, Guardian of Thraben", "")); + cubeCards.add(new CardIdentity("Thassa's Intervention", "")); + cubeCards.add(new CardIdentity("Thassa's Oracle", "")); + cubeCards.add(new CardIdentity("Thassa, Deep-Dwelling", "")); + cubeCards.add(new CardIdentity("The Akroan War", "")); + cubeCards.add(new CardIdentity("The Birth of Meletis", "")); + cubeCards.add(new CardIdentity("The Circle of Loyalty", "")); + cubeCards.add(new CardIdentity("The Eldest Reborn", "")); + cubeCards.add(new CardIdentity("The First Iroan Games", "")); + cubeCards.add(new CardIdentity("The Great Henge", "")); + cubeCards.add(new CardIdentity("The Immortal Sun", "")); + cubeCards.add(new CardIdentity("The Mending of Dominaria", "")); + cubeCards.add(new CardIdentity("The Mirari Conjecture", "")); + cubeCards.add(new CardIdentity("Theater of Horrors", "")); + cubeCards.add(new CardIdentity("Thirst for Meaning", "")); + cubeCards.add(new CardIdentity("Thorn Lieutenant", "")); + cubeCards.add(new CardIdentity("Thorn Mammoth", "")); + cubeCards.add(new CardIdentity("Thornwood Falls", "")); + cubeCards.add(new CardIdentity("Thought Erasure", "")); + cubeCards.add(new CardIdentity("Thrash // Threat", "")); + cubeCards.add(new CardIdentity("Thrashing Brontodon", "")); + cubeCards.add(new CardIdentity("Threnody Singer", "")); + cubeCards.add(new CardIdentity("Thryx, the Sudden Storm", "")); + cubeCards.add(new CardIdentity("Tibalt, Rakish Instigator", "")); + cubeCards.add(new CardIdentity("Time Wipe", "")); + cubeCards.add(new CardIdentity("Tin Street Dodger", "")); + cubeCards.add(new CardIdentity("Tolsimir, Friend to Wolves", "")); + cubeCards.add(new CardIdentity("Tomik, Distinguished Advokist", "")); + cubeCards.add(new CardIdentity("Tranquil Cove", "")); + cubeCards.add(new CardIdentity("Tranquil Thicket", "")); + cubeCards.add(new CardIdentity("Trapped in the Tower", "")); + cubeCards.add(new CardIdentity("Traveler's Amulet", "")); + cubeCards.add(new CardIdentity("Traxos, Scourge of Kroog", "")); + cubeCards.add(new CardIdentity("Treasure Map", "")); + cubeCards.add(new CardIdentity("Tymaret, Chosen from Death", "")); + cubeCards.add(new CardIdentity("Ugin, the Ineffable", "")); + cubeCards.add(new CardIdentity("Unbreakable Formation", "")); + cubeCards.add(new CardIdentity("Underworld Rage-Hound", "")); + cubeCards.add(new CardIdentity("Unsummon", "")); + cubeCards.add(new CardIdentity("Untamed Kavu", "")); + cubeCards.add(new CardIdentity("Uro, Titan of Nature's Wrath", "")); + cubeCards.add(new CardIdentity("Vantress Gargoyle", "")); + cubeCards.add(new CardIdentity("Venerable Knight", "")); + cubeCards.add(new CardIdentity("Venerated Loxodon", "")); + cubeCards.add(new CardIdentity("Verix Bladewing", "")); + cubeCards.add(new CardIdentity("Viashino Pyromancer", "")); + cubeCards.add(new CardIdentity("Vivien's Arkbow", "")); + cubeCards.add(new CardIdentity("Vivien, Arkbow Ranger", "")); + cubeCards.add(new CardIdentity("Voltaic Servant", "")); + cubeCards.add(new CardIdentity("Voracious Hydra", "")); + cubeCards.add(new CardIdentity("Vraska, Golgari Queen", "")); + cubeCards.add(new CardIdentity("Warbriar Blessing", "")); + cubeCards.add(new CardIdentity("Warkite Marauder", "")); + cubeCards.add(new CardIdentity("Warlord's Fury", "")); + cubeCards.add(new CardIdentity("Watery Grave", "")); + cubeCards.add(new CardIdentity("Wavebreak Hippocamp", "")); + cubeCards.add(new CardIdentity("Wayward Swordtooth", "")); + cubeCards.add(new CardIdentity("Weaselback Redcap", "")); + cubeCards.add(new CardIdentity("Weatherlight", "")); + cubeCards.add(new CardIdentity("Wilderness Reclamation", "")); + cubeCards.add(new CardIdentity("Wildwood Tracker", "")); + cubeCards.add(new CardIdentity("Wind-Scarred Crag", "")); + cubeCards.add(new CardIdentity("Winged Words", "")); + cubeCards.add(new CardIdentity("Witch's Oven", "")); + cubeCards.add(new CardIdentity("Witch's Vengeance", "")); + cubeCards.add(new CardIdentity("Witching Well", "")); + cubeCards.add(new CardIdentity("Woe Strider", "")); + cubeCards.add(new CardIdentity("Wolfwillow Haven", "")); + cubeCards.add(new CardIdentity("Woodland Cemetery", "")); + cubeCards.add(new CardIdentity("Woodland Champion", "")); + cubeCards.add(new CardIdentity("Yawgmoth's Vile Offering", "")); + cubeCards.add(new CardIdentity("Zetalpa, Primal Dawn", "")); + cubeCards.add(new CardIdentity("Zhalfirin Void", "")); + cubeCards.add(new CardIdentity("Zhur-Taa Goblin", "")); + } +} diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeApril2020.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeApril2020.java new file mode 100644 index 0000000000..762f392292 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeApril2020.java @@ -0,0 +1,552 @@ +package mage.tournament.cubes; + +import mage.game.draft.DraftCube; + +public class VintageCubeApril2020 extends DraftCube { + + public VintageCubeApril2020() { + super("MTGO Vintage Cube April 2020"); + + cubeCards.add(new CardIdentity("Kytheon, Hero of Akros", "")); + cubeCards.add(new CardIdentity("Mother of Runes", "")); + cubeCards.add(new CardIdentity("Student of Warfare", "")); + cubeCards.add(new CardIdentity("Adanto Vanguard", "")); + cubeCards.add(new CardIdentity("Containment Priest", "")); + cubeCards.add(new CardIdentity("Leonin Relic-Warder", "")); + cubeCards.add(new CardIdentity("Porcelain Legionnaire", "")); + cubeCards.add(new CardIdentity("Selfless Spirit", "")); + cubeCards.add(new CardIdentity("Soulfire Grand Master", "")); + cubeCards.add(new CardIdentity("Stoneforge Mystic", "")); + cubeCards.add(new CardIdentity("Thalia, Guardian of Thraben", "")); + cubeCards.add(new CardIdentity("Tithe Taker", "")); + cubeCards.add(new CardIdentity("Wall of Omens", "")); + cubeCards.add(new CardIdentity("Blade Splicer", "")); + cubeCards.add(new CardIdentity("Brightling", "")); + cubeCards.add(new CardIdentity("Brimaz, King of Oreskos", "")); + cubeCards.add(new CardIdentity("Fairgrounds Warden", "")); + cubeCards.add(new CardIdentity("Flickerwisp", "")); + cubeCards.add(new CardIdentity("Monastery Mentor", "")); + cubeCards.add(new CardIdentity("Recruiter of the Guard", "")); + cubeCards.add(new CardIdentity("Silverblade Paladin", "")); + cubeCards.add(new CardIdentity("Emeria Angel", "")); + cubeCards.add(new CardIdentity("Hero of Bladehold", "")); + cubeCards.add(new CardIdentity("Linvala, Keeper of Silence", "")); + cubeCards.add(new CardIdentity("Restoration Angel", "")); + cubeCards.add(new CardIdentity("Angel of Invention", "")); + cubeCards.add(new CardIdentity("Elspeth Conquers Death", "")); + cubeCards.add(new CardIdentity("Archangel Avacyn", "")); + cubeCards.add(new CardIdentity("Baneslayer Angel", "")); + cubeCards.add(new CardIdentity("Lyra Dawnbringer", "")); + cubeCards.add(new CardIdentity("Reveillark", "")); + cubeCards.add(new CardIdentity("Sun Titan", "")); + cubeCards.add(new CardIdentity("Angel of Serenity", "")); + cubeCards.add(new CardIdentity("Elesh Norn, Grand Cenobite", "")); + cubeCards.add(new CardIdentity("Iona, Shield of Emeria", "")); + cubeCards.add(new CardIdentity("Gideon Blackblade", "")); + cubeCards.add(new CardIdentity("Elspeth, Sun's Nemesis", "")); + cubeCards.add(new CardIdentity("Gideon, Ally of Zendikar", "")); + cubeCards.add(new CardIdentity("Gideon Jura", "")); + cubeCards.add(new CardIdentity("Elspeth, Sun's Champion", "")); + cubeCards.add(new CardIdentity("Condemn", "")); + cubeCards.add(new CardIdentity("Enlightened Tutor", "")); + cubeCards.add(new CardIdentity("Mana Tithe", "")); + cubeCards.add(new CardIdentity("Path to Exile", "")); + cubeCards.add(new CardIdentity("Swords to Plowshares", "")); + cubeCards.add(new CardIdentity("Disenchant", "")); + cubeCards.add(new CardIdentity("Unexpectedly Absent", "")); + cubeCards.add(new CardIdentity("Balance", "")); + cubeCards.add(new CardIdentity("Council's Judgment", "")); + cubeCards.add(new CardIdentity("Spectral Procession", "")); + cubeCards.add(new CardIdentity("Armageddon", "")); + cubeCards.add(new CardIdentity("Day of Judgment", "")); + cubeCards.add(new CardIdentity("Ravages of War", "")); + cubeCards.add(new CardIdentity("Wrath of God", "")); + cubeCards.add(new CardIdentity("Terminus", "")); + cubeCards.add(new CardIdentity("Land Tax", "")); + cubeCards.add(new CardIdentity("Legion's Landing", "")); + cubeCards.add(new CardIdentity("Honor of the Pure", "")); + cubeCards.add(new CardIdentity("Banishing Light", "")); + cubeCards.add(new CardIdentity("Oblivion Ring", "")); + cubeCards.add(new CardIdentity("Faith's Fetters", "")); + cubeCards.add(new CardIdentity("Moat", "")); + cubeCards.add(new CardIdentity("Parallax Wave", "")); + cubeCards.add(new CardIdentity("Spear of Heliod", "")); + cubeCards.add(new CardIdentity("Karakas", "")); + cubeCards.add(new CardIdentity("Thassa's Oracle", "")); + cubeCards.add(new CardIdentity("Baral, Chief of Compliance", "")); + cubeCards.add(new CardIdentity("Jace, Vryn's Prodigy", "")); + cubeCards.add(new CardIdentity("Looter il-Kor", "")); + cubeCards.add(new CardIdentity("Phantasmal Image", "")); + cubeCards.add(new CardIdentity("Snapcaster Mage", "")); + cubeCards.add(new CardIdentity("Thing in the Ice", "")); + cubeCards.add(new CardIdentity("Arcane Artisan", "")); + cubeCards.add(new CardIdentity("Deceiver Exarch", "")); + cubeCards.add(new CardIdentity("Pestermite", "")); + cubeCards.add(new CardIdentity("Spellseeker", "")); + cubeCards.add(new CardIdentity("Trinket Mage", "")); + cubeCards.add(new CardIdentity("Vendilion Clique", "")); + cubeCards.add(new CardIdentity("Glen Elendra Archmage", "")); + cubeCards.add(new CardIdentity("Phyrexian Metamorph", "")); + cubeCards.add(new CardIdentity("Sower of Temptation", "")); + cubeCards.add(new CardIdentity("Venser, Shaper Savant", "")); + cubeCards.add(new CardIdentity("Mulldrifter", "")); + cubeCards.add(new CardIdentity("Riftwing Cloudskate", "")); + cubeCards.add(new CardIdentity("Consecrated Sphinx", "")); + cubeCards.add(new CardIdentity("Frost Titan", "")); + cubeCards.add(new CardIdentity("Torrential Gearhulk", "")); + cubeCards.add(new CardIdentity("Palinchron", "")); + cubeCards.add(new CardIdentity("Inkwell Leviathan", "")); + cubeCards.add(new CardIdentity("Jace Beleren", "")); + cubeCards.add(new CardIdentity("Jace, the Mind Sculptor", "")); + cubeCards.add(new CardIdentity("Tezzeret the Seeker", "")); + cubeCards.add(new CardIdentity("Ancestral Recall", "")); + cubeCards.add(new CardIdentity("Brainstorm", "")); + cubeCards.add(new CardIdentity("High Tide", "")); + cubeCards.add(new CardIdentity("Mystical Tutor", "")); + cubeCards.add(new CardIdentity("Spell Pierce", "")); + cubeCards.add(new CardIdentity("Brain Freeze", "")); + cubeCards.add(new CardIdentity("Counterspell", "")); + cubeCards.add(new CardIdentity("Daze", "")); + cubeCards.add(new CardIdentity("Impulse", "")); + cubeCards.add(new CardIdentity("Mana Drain", "")); + cubeCards.add(new CardIdentity("Mana Leak", "")); + cubeCards.add(new CardIdentity("Miscalculation", "")); + cubeCards.add(new CardIdentity("Remand", "")); + cubeCards.add(new CardIdentity("Frantic Search", "")); + cubeCards.add(new CardIdentity("Thirst for Knowledge", "")); + cubeCards.add(new CardIdentity("Cryptic Command", "")); + cubeCards.add(new CardIdentity("Fact or Fiction", "")); + cubeCards.add(new CardIdentity("Gifts Ungiven", "")); + cubeCards.add(new CardIdentity("Turnabout", "")); + cubeCards.add(new CardIdentity("Force of Will", "")); + cubeCards.add(new CardIdentity("Gush", "")); + cubeCards.add(new CardIdentity("Mystic Confluence", "")); + cubeCards.add(new CardIdentity("Repeal", "")); + cubeCards.add(new CardIdentity("Dig Through Time", "")); + cubeCards.add(new CardIdentity("Ancestral Vision", "")); + cubeCards.add(new CardIdentity("Gitaxian Probe", "")); + cubeCards.add(new CardIdentity("Ponder", "")); + cubeCards.add(new CardIdentity("Preordain", "")); + cubeCards.add(new CardIdentity("Chart a Course", "")); + cubeCards.add(new CardIdentity("Time Walk", "")); + cubeCards.add(new CardIdentity("Show and Tell", "")); + cubeCards.add(new CardIdentity("Timetwister", "")); + cubeCards.add(new CardIdentity("Tinker", "")); + cubeCards.add(new CardIdentity("Bribery", "")); + cubeCards.add(new CardIdentity("Time Warp", "")); + cubeCards.add(new CardIdentity("Mind's Desire", "")); + cubeCards.add(new CardIdentity("Time Spiral", "")); + cubeCards.add(new CardIdentity("Upheaval", "")); + cubeCards.add(new CardIdentity("Treasure Cruise", "")); + cubeCards.add(new CardIdentity("Search for Azcanta", "")); + cubeCards.add(new CardIdentity("Control Magic", "")); + cubeCards.add(new CardIdentity("Opposition", "")); + cubeCards.add(new CardIdentity("Treachery", "")); + cubeCards.add(new CardIdentity("Shelldock Isle", "")); + cubeCards.add(new CardIdentity("Tolarian Academy", "")); + cubeCards.add(new CardIdentity("Putrid Imp", "")); + cubeCards.add(new CardIdentity("Dark Confidant", "")); + cubeCards.add(new CardIdentity("Kitesail Freebooter", "")); + cubeCards.add(new CardIdentity("Mesmeric Fiend", "")); + cubeCards.add(new CardIdentity("Oona's Prowler", "")); + cubeCards.add(new CardIdentity("Pack Rat", "")); + cubeCards.add(new CardIdentity("Vampire Hexmage", "")); + cubeCards.add(new CardIdentity("Bone Shredder", "")); + cubeCards.add(new CardIdentity("Hypnotic Specter", "")); + cubeCards.add(new CardIdentity("Ophiomancer", "")); + cubeCards.add(new CardIdentity("Plaguecrafter", "")); + cubeCards.add(new CardIdentity("Vampire Nighthawk", "")); + cubeCards.add(new CardIdentity("Gonti, Lord of Luxury", "")); + cubeCards.add(new CardIdentity("Nekrataal", "")); + cubeCards.add(new CardIdentity("Ravenous Chupacabra", "")); + cubeCards.add(new CardIdentity("Shriekmaw", "")); + cubeCards.add(new CardIdentity("Grave Titan", "")); + cubeCards.add(new CardIdentity("Ink-Eyes, Servant of Oni", "")); + cubeCards.add(new CardIdentity("Massacre Wurm", "")); + cubeCards.add(new CardIdentity("Tasigur, the Golden Fang", "")); + cubeCards.add(new CardIdentity("Sheoldred, Whispering One", "")); + cubeCards.add(new CardIdentity("Griselbrand", "")); + cubeCards.add(new CardIdentity("Liliana of the Veil", "")); + cubeCards.add(new CardIdentity("Liliana, Death's Majesty", "")); + cubeCards.add(new CardIdentity("Dark Ritual", "")); + cubeCards.add(new CardIdentity("Entomb", "")); + cubeCards.add(new CardIdentity("Fatal Push", "")); + cubeCards.add(new CardIdentity("Vampiric Tutor", "")); + cubeCards.add(new CardIdentity("Cabal Ritual", "")); + cubeCards.add(new CardIdentity("Go for the Throat", "")); + cubeCards.add(new CardIdentity("Liliana's Triumph", "")); + cubeCards.add(new CardIdentity("Shallow Grave", "")); + cubeCards.add(new CardIdentity("Ultimate Price", "")); + cubeCards.add(new CardIdentity("Corpse Dance", "")); + cubeCards.add(new CardIdentity("Dismember", "")); + cubeCards.add(new CardIdentity("Hero's Downfall", "")); + cubeCards.add(new CardIdentity("Makeshift Mannequin", "")); + cubeCards.add(new CardIdentity("Duress", "")); + cubeCards.add(new CardIdentity("Imperial Seal", "")); + cubeCards.add(new CardIdentity("Inquisition of Kozilek", "")); + cubeCards.add(new CardIdentity("Reanimate", "")); + cubeCards.add(new CardIdentity("Thoughtseize", "")); + cubeCards.add(new CardIdentity("Collective Brutality", "")); + cubeCards.add(new CardIdentity("Demonic Tutor", "")); + cubeCards.add(new CardIdentity("Exhume", "")); + cubeCards.add(new CardIdentity("Hymn to Tourach", "")); + cubeCards.add(new CardIdentity("Night's Whisper", "")); + cubeCards.add(new CardIdentity("Buried Alive", "")); + cubeCards.add(new CardIdentity("Toxic Deluge", "")); + cubeCards.add(new CardIdentity("Yawgmoth's Will", "")); + cubeCards.add(new CardIdentity("Damnation", "")); + cubeCards.add(new CardIdentity("Languish", "")); + cubeCards.add(new CardIdentity("Mastermind's Acquisition", "")); + cubeCards.add(new CardIdentity("Tendrils of Agony", "")); + cubeCards.add(new CardIdentity("Dark Petition", "")); + cubeCards.add(new CardIdentity("Living Death", "")); + cubeCards.add(new CardIdentity("Mind Twist", "")); + cubeCards.add(new CardIdentity("Animate Dead", "")); + cubeCards.add(new CardIdentity("Bitterblossom", "")); + cubeCards.add(new CardIdentity("Necromancy", "")); + cubeCards.add(new CardIdentity("Phyrexian Arena", "")); + cubeCards.add(new CardIdentity("Recurring Nightmare", "")); + cubeCards.add(new CardIdentity("Yawgmoth's Bargain", "")); + cubeCards.add(new CardIdentity("Goblin Guide", "")); + cubeCards.add(new CardIdentity("Goblin Welder", "")); + cubeCards.add(new CardIdentity("Grim Lavamancer", "")); + cubeCards.add(new CardIdentity("Jackal Pup", "")); + cubeCards.add(new CardIdentity("Monastery Swiftspear", "")); + cubeCards.add(new CardIdentity("Zurgo Bellstriker", "")); + cubeCards.add(new CardIdentity("Abbot of Keral Keep", "")); + cubeCards.add(new CardIdentity("Dire Fleet Daredevil", "")); + cubeCards.add(new CardIdentity("Eidolon of the Great Revel", "")); + cubeCards.add(new CardIdentity("Runaway Steam-Kin", "")); + cubeCards.add(new CardIdentity("Young Pyromancer", "")); + cubeCards.add(new CardIdentity("Goblin Rabblemaster", "")); + cubeCards.add(new CardIdentity("Imperial Recruiter", "")); + cubeCards.add(new CardIdentity("Magus of the Moon", "")); + cubeCards.add(new CardIdentity("Avalanche Riders", "")); + cubeCards.add(new CardIdentity("Flametongue Kavu", "")); + cubeCards.add(new CardIdentity("Hazoret the Fervent", "")); + cubeCards.add(new CardIdentity("Hellrider", "")); + cubeCards.add(new CardIdentity("Pia and Kiran Nalaar", "")); + cubeCards.add(new CardIdentity("Rekindling Phoenix", "")); + cubeCards.add(new CardIdentity("Glorybringer", "")); + cubeCards.add(new CardIdentity("Goblin Dark-Dwellers", "")); + cubeCards.add(new CardIdentity("Kiki-Jiki, Mirror Breaker", "")); + cubeCards.add(new CardIdentity("Siege-Gang Commander", "")); + cubeCards.add(new CardIdentity("Thundermaw Hellkite", "")); + cubeCards.add(new CardIdentity("Zealous Conscripts", "")); + cubeCards.add(new CardIdentity("Inferno Titan", "")); + cubeCards.add(new CardIdentity("Chandra, Torch of Defiance", "")); + cubeCards.add(new CardIdentity("Daretti, Scrap Savant", "")); + cubeCards.add(new CardIdentity("Koth of the Hammer", "")); + cubeCards.add(new CardIdentity("Burst Lightning", "")); + cubeCards.add(new CardIdentity("Lightning Bolt", "")); + cubeCards.add(new CardIdentity("Abrade", "")); + cubeCards.add(new CardIdentity("Ancient Grudge", "")); + cubeCards.add(new CardIdentity("Desperate Ritual", "")); + cubeCards.add(new CardIdentity("Fire // Ice", "")); + cubeCards.add(new CardIdentity("Incinerate", "")); + cubeCards.add(new CardIdentity("Lightning Strike", "")); + cubeCards.add(new CardIdentity("Pyretic Ritual", "")); + cubeCards.add(new CardIdentity("Char", "")); + cubeCards.add(new CardIdentity("Seething Song", "")); + cubeCards.add(new CardIdentity("Through the Breach", "")); + cubeCards.add(new CardIdentity("Fireblast", "")); + cubeCards.add(new CardIdentity("Chain Lightning", "")); + cubeCards.add(new CardIdentity("Faithless Looting", "")); + cubeCards.add(new CardIdentity("Firebolt", "")); + cubeCards.add(new CardIdentity("Flame Slash", "")); + cubeCards.add(new CardIdentity("Mizzium Mortars", "")); + cubeCards.add(new CardIdentity("Pyroclasm", "")); + cubeCards.add(new CardIdentity("Light Up the Stage", "")); + cubeCards.add(new CardIdentity("Underworld Breach", "")); + cubeCards.add(new CardIdentity("Wheel of Fortune", "")); + cubeCards.add(new CardIdentity("Empty the Warrens", "")); + cubeCards.add(new CardIdentity("Fiery Confluence", "")); + cubeCards.add(new CardIdentity("Past in Flames", "")); + cubeCards.add(new CardIdentity("Banefire", "")); + cubeCards.add(new CardIdentity("Burning of Xinye", "")); + cubeCards.add(new CardIdentity("Wildfire", "")); + cubeCards.add(new CardIdentity("Bonfire of the Damned", "")); + cubeCards.add(new CardIdentity("Mana Flare", "")); + cubeCards.add(new CardIdentity("Sulfuric Vortex", "")); + cubeCards.add(new CardIdentity("Sneak Attack", "")); + cubeCards.add(new CardIdentity("Splinter Twin", "")); + cubeCards.add(new CardIdentity("Arbor Elf", "")); + cubeCards.add(new CardIdentity("Avacyn's Pilgrim", "")); + cubeCards.add(new CardIdentity("Birds of Paradise", "")); + cubeCards.add(new CardIdentity("Elves of Deep Shadow", "")); + cubeCards.add(new CardIdentity("Elvish Mystic", "")); + cubeCards.add(new CardIdentity("Fyndhorn Elves", "")); + cubeCards.add(new CardIdentity("Joraga Treespeaker", "")); + cubeCards.add(new CardIdentity("Llanowar Elves", "")); + cubeCards.add(new CardIdentity("Noble Hierarch", "")); + cubeCards.add(new CardIdentity("Den Protector", "")); + cubeCards.add(new CardIdentity("Devoted Druid", "")); + cubeCards.add(new CardIdentity("Fauna Shaman", "")); + cubeCards.add(new CardIdentity("Gilded Goose", "")); + cubeCards.add(new CardIdentity("Lotus Cobra", "")); + cubeCards.add(new CardIdentity("Rofellos, Llanowar Emissary", "")); + cubeCards.add(new CardIdentity("Sakura-Tribe Elder", "")); + cubeCards.add(new CardIdentity("Scavenging Ooze", "")); + cubeCards.add(new CardIdentity("Sylvan Caryatid", "")); + cubeCards.add(new CardIdentity("Wall of Blossoms", "")); + cubeCards.add(new CardIdentity("Wall of Roots", "")); + cubeCards.add(new CardIdentity("Courser of Kruphix", "")); + cubeCards.add(new CardIdentity("Eternal Witness", "")); + cubeCards.add(new CardIdentity("Ramunap Excavator", "")); + cubeCards.add(new CardIdentity("Reclamation Sage", "")); + cubeCards.add(new CardIdentity("Tireless Tracker", "")); + cubeCards.add(new CardIdentity("Yavimaya Elder", "")); + cubeCards.add(new CardIdentity("Master of the Wild Hunt", "")); + cubeCards.add(new CardIdentity("Oracle of Mul Daya", "")); + cubeCards.add(new CardIdentity("Polukranos, World Eater", "")); + cubeCards.add(new CardIdentity("Acidic Slime", "")); + cubeCards.add(new CardIdentity("Biogenic Ooze", "")); + cubeCards.add(new CardIdentity("Deranged Hermit", "")); + cubeCards.add(new CardIdentity("Thragtusk", "")); + cubeCards.add(new CardIdentity("Whisperwood Elemental", "")); + cubeCards.add(new CardIdentity("Carnage Tyrant", "")); + cubeCards.add(new CardIdentity("Primeval Titan", "")); + cubeCards.add(new CardIdentity("Avenger of Zendikar", "")); + cubeCards.add(new CardIdentity("Craterhoof Behemoth", "")); + cubeCards.add(new CardIdentity("Terastodon", "")); + cubeCards.add(new CardIdentity("Woodfall Primus", "")); + cubeCards.add(new CardIdentity("Dryad of the Ilysian Grove", "")); + cubeCards.add(new CardIdentity("Garruk Relentless", "")); + cubeCards.add(new CardIdentity("Garruk Wildspeaker", "")); + cubeCards.add(new CardIdentity("Garruk, Primal Hunter", "")); + cubeCards.add(new CardIdentity("Vivien Reid", "")); + cubeCards.add(new CardIdentity("Nature's Claim", "")); + cubeCards.add(new CardIdentity("Beast Within", "")); + cubeCards.add(new CardIdentity("Channel", "")); + cubeCards.add(new CardIdentity("Regrowth", "")); + cubeCards.add(new CardIdentity("Kodama's Reach", "")); + cubeCards.add(new CardIdentity("Search for Tomorrow", "")); + cubeCards.add(new CardIdentity("Eureka", "")); + cubeCards.add(new CardIdentity("Harmonize", "")); + cubeCards.add(new CardIdentity("Natural Order", "")); + cubeCards.add(new CardIdentity("Plow Under", "")); + cubeCards.add(new CardIdentity("Primal Command", "")); + cubeCards.add(new CardIdentity("Green Sun's Zenith", "")); + cubeCards.add(new CardIdentity("Finale of Devastation", "")); + cubeCards.add(new CardIdentity("Tooth and Nail", "")); + cubeCards.add(new CardIdentity("Fastbond", "")); + cubeCards.add(new CardIdentity("Oath of Druids", "")); + cubeCards.add(new CardIdentity("Survival of the Fittest", "")); + cubeCards.add(new CardIdentity("Sylvan Library", "")); + cubeCards.add(new CardIdentity("Heartbeat of Spring", "")); + cubeCards.add(new CardIdentity("Wilderness Reclamation", "")); + cubeCards.add(new CardIdentity("Gaea's Cradle", "")); + cubeCards.add(new CardIdentity("Geist of Saint Traft", "")); + cubeCards.add(new CardIdentity("Teferi, Hero of Dominaria", "")); + cubeCards.add(new CardIdentity("Sphinx's Revelation", "")); + cubeCards.add(new CardIdentity("Fractured Identity", "")); + cubeCards.add(new CardIdentity("Celestial Colonnade", "")); + cubeCards.add(new CardIdentity("Flooded Strand", "")); + cubeCards.add(new CardIdentity("Hallowed Fountain", "")); + cubeCards.add(new CardIdentity("Seachrome Coast", "")); + cubeCards.add(new CardIdentity("Tundra", "")); + cubeCards.add(new CardIdentity("Thief of Sanity", "")); + cubeCards.add(new CardIdentity("The Scarab God", "")); + cubeCards.add(new CardIdentity("Ashiok, Nightmare Weaver", "")); + cubeCards.add(new CardIdentity("Baleful Strix", "")); + cubeCards.add(new CardIdentity("Creeping Tar Pit", "")); + cubeCards.add(new CardIdentity("Darkslick Shores", "")); + cubeCards.add(new CardIdentity("Polluted Delta", "")); + cubeCards.add(new CardIdentity("Underground Sea", "")); + cubeCards.add(new CardIdentity("Watery Grave", "")); + cubeCards.add(new CardIdentity("Daretti, Ingenious Iconoclast", "")); + cubeCards.add(new CardIdentity("Kroxa, Titan of Death's Hunger", "")); + cubeCards.add(new CardIdentity("Kolaghan's Command", "")); + cubeCards.add(new CardIdentity("Rakdos's Return", "")); + cubeCards.add(new CardIdentity("Badlands", "")); + cubeCards.add(new CardIdentity("Blackcleave Cliffs", "")); + cubeCards.add(new CardIdentity("Blood Crypt", "")); + cubeCards.add(new CardIdentity("Bloodstained Mire", "")); + cubeCards.add(new CardIdentity("Lavaclaw Reaches", "")); + cubeCards.add(new CardIdentity("Bloodbraid Elf", "")); + cubeCards.add(new CardIdentity("Huntmaster of the Fells", "")); + cubeCards.add(new CardIdentity("Dragonlord Atarka", "")); + cubeCards.add(new CardIdentity("Manamorphose", "")); + cubeCards.add(new CardIdentity("Copperline Gorge", "")); + cubeCards.add(new CardIdentity("Raging Ravine", "")); + cubeCards.add(new CardIdentity("Stomping Ground", "")); + cubeCards.add(new CardIdentity("Taiga", "")); + cubeCards.add(new CardIdentity("Wooded Foothills", "")); + cubeCards.add(new CardIdentity("Kitchen Finks", "")); + cubeCards.add(new CardIdentity("Knight of Autumn", "")); + cubeCards.add(new CardIdentity("Knight of the Reliquary", "")); + cubeCards.add(new CardIdentity("Trostani Discordant", "")); + cubeCards.add(new CardIdentity("Mirari's Wake", "")); + cubeCards.add(new CardIdentity("Razorverge Thicket", "")); + cubeCards.add(new CardIdentity("Savannah", "")); + cubeCards.add(new CardIdentity("Stirring Wildwood", "")); + cubeCards.add(new CardIdentity("Temple Garden", "")); + cubeCards.add(new CardIdentity("Windswept Heath", "")); + cubeCards.add(new CardIdentity("Ashen Rider", "")); + cubeCards.add(new CardIdentity("Kaya, Orzhov Usurper", "")); + cubeCards.add(new CardIdentity("Tidehollow Sculler", "")); + cubeCards.add(new CardIdentity("Anguished Unmaking", "")); + cubeCards.add(new CardIdentity("Lingering Souls", "")); + cubeCards.add(new CardIdentity("Vindicate", "")); + cubeCards.add(new CardIdentity("Unburial Rites", "")); + cubeCards.add(new CardIdentity("Concealed Courtyard", "")); + cubeCards.add(new CardIdentity("Godless Shrine", "")); + cubeCards.add(new CardIdentity("Marsh Flats", "")); + cubeCards.add(new CardIdentity("Scrubland", "")); + cubeCards.add(new CardIdentity("Shambling Vent", "")); + cubeCards.add(new CardIdentity("Vraska, Golgari Queen", "")); + cubeCards.add(new CardIdentity("Assassin's Trophy", "")); + cubeCards.add(new CardIdentity("Maelstrom Pulse", "")); + cubeCards.add(new CardIdentity("Pernicious Deed", "")); + cubeCards.add(new CardIdentity("Bayou", "")); + cubeCards.add(new CardIdentity("Blooming Marsh", "")); + cubeCards.add(new CardIdentity("Hissing Quagmire", "")); + cubeCards.add(new CardIdentity("Overgrown Tomb", "")); + cubeCards.add(new CardIdentity("Verdant Catacombs", "")); + cubeCards.add(new CardIdentity("Edric, Spymaster of Trest", "")); + cubeCards.add(new CardIdentity("Trygon Predator", "")); + cubeCards.add(new CardIdentity("Hydroid Krasis", "")); + cubeCards.add(new CardIdentity("Uro, Titan of Nature's Wrath", "")); + cubeCards.add(new CardIdentity("Botanical Sanctum", "")); + cubeCards.add(new CardIdentity("Breeding Pool", "")); + cubeCards.add(new CardIdentity("Lumbering Falls", "")); + cubeCards.add(new CardIdentity("Misty Rainforest", "")); + cubeCards.add(new CardIdentity("Tropical Island", "")); + cubeCards.add(new CardIdentity("Goblin Electromancer", "")); + cubeCards.add(new CardIdentity("Dack Fayden", "")); + cubeCards.add(new CardIdentity("Thousand-Year Storm", "")); + cubeCards.add(new CardIdentity("Scalding Tarn", "")); + cubeCards.add(new CardIdentity("Spirebluff Canal", "")); + cubeCards.add(new CardIdentity("Steam Vents", "")); + cubeCards.add(new CardIdentity("Volcanic Island", "")); + cubeCards.add(new CardIdentity("Wandering Fumarole", "")); + cubeCards.add(new CardIdentity("Figure of Destiny", "")); + cubeCards.add(new CardIdentity("Ajani Vengeant", "")); + cubeCards.add(new CardIdentity("Nahiri, the Harbinger", "")); + cubeCards.add(new CardIdentity("Wear // Tear", "")); + cubeCards.add(new CardIdentity("Lightning Helix", "")); + cubeCards.add(new CardIdentity("Arid Mesa", "")); + cubeCards.add(new CardIdentity("Inspiring Vantage", "")); + cubeCards.add(new CardIdentity("Needle Spires", "")); + cubeCards.add(new CardIdentity("Plateau", "")); + cubeCards.add(new CardIdentity("Sacred Foundry", "")); + cubeCards.add(new CardIdentity("Sphinx of the Steel Wind", "")); + cubeCards.add(new CardIdentity("Nicol Bolas, Dragon-God", "")); + cubeCards.add(new CardIdentity("Leovold, Emissary of Trest", "")); + cubeCards.add(new CardIdentity("Progenitus", "")); + cubeCards.add(new CardIdentity("Kozilek, Butcher of Truth", "")); + cubeCards.add(new CardIdentity("Ulamog, the Ceaseless Hunger", "")); + cubeCards.add(new CardIdentity("Ulamog, the Infinite Gyre", "")); + cubeCards.add(new CardIdentity("Emrakul, the Promised End", "")); + cubeCards.add(new CardIdentity("Emrakul, the Aeons Torn", "")); + cubeCards.add(new CardIdentity("Karn, Scion of Urza", "")); + cubeCards.add(new CardIdentity("Karn Liberated", "")); + cubeCards.add(new CardIdentity("Ugin, the Spirit Dragon", "")); + cubeCards.add(new CardIdentity("Bomat Courier", "")); + cubeCards.add(new CardIdentity("Hangarback Walker", "")); + cubeCards.add(new CardIdentity("Phyrexian Revoker", "")); + cubeCards.add(new CardIdentity("Metalworker", "")); + cubeCards.add(new CardIdentity("Lodestone Golem", "")); + cubeCards.add(new CardIdentity("Solemn Simulacrum", "")); + cubeCards.add(new CardIdentity("Kuldotha Forgemaster", "")); + cubeCards.add(new CardIdentity("Wurmcoil Engine", "")); + cubeCards.add(new CardIdentity("Myr Battlesphere", "")); + cubeCards.add(new CardIdentity("Sundering Titan", "")); + cubeCards.add(new CardIdentity("Walking Ballista", "")); + cubeCards.add(new CardIdentity("Blightsteel Colossus", "")); + cubeCards.add(new CardIdentity("Black Lotus", "")); + cubeCards.add(new CardIdentity("Chrome Mox", "")); + cubeCards.add(new CardIdentity("Everflowing Chalice", "")); + cubeCards.add(new CardIdentity("Lion's Eye Diamond", "")); + cubeCards.add(new CardIdentity("Lotus Bloom", "")); + cubeCards.add(new CardIdentity("Mana Crypt", "")); + cubeCards.add(new CardIdentity("Mox Diamond", "")); + cubeCards.add(new CardIdentity("Mox Emerald", "")); + cubeCards.add(new CardIdentity("Mox Jet", "")); + cubeCards.add(new CardIdentity("Mox Pearl", "")); + cubeCards.add(new CardIdentity("Mox Ruby", "")); + cubeCards.add(new CardIdentity("Mox Sapphire", "")); + cubeCards.add(new CardIdentity("Mana Vault", "")); + cubeCards.add(new CardIdentity("Relic of Progenitus", "")); + cubeCards.add(new CardIdentity("Sensei's Divining Top", "")); + cubeCards.add(new CardIdentity("Skullclamp", "")); + cubeCards.add(new CardIdentity("Sol Ring", "")); + cubeCards.add(new CardIdentity("Azorius Signet", "")); + cubeCards.add(new CardIdentity("Boros Signet", "")); + cubeCards.add(new CardIdentity("Dimir Signet", "")); + cubeCards.add(new CardIdentity("Golgari Signet", "")); + cubeCards.add(new CardIdentity("Grim Monolith", "")); + cubeCards.add(new CardIdentity("Gruul Signet", "")); + cubeCards.add(new CardIdentity("Izzet Signet", "")); + cubeCards.add(new CardIdentity("Lightning Greaves", "")); + cubeCards.add(new CardIdentity("Orzhov Signet", "")); + cubeCards.add(new CardIdentity("Rakdos Signet", "")); + cubeCards.add(new CardIdentity("Selesnya Signet", "")); + cubeCards.add(new CardIdentity("Shrine of Burning Rage", "")); + cubeCards.add(new CardIdentity("Simic Signet", "")); + cubeCards.add(new CardIdentity("Smuggler's Copter", "")); + cubeCards.add(new CardIdentity("Umezawa's Jitte", "")); + cubeCards.add(new CardIdentity("Winter Orb", "")); + cubeCards.add(new CardIdentity("Basalt Monolith", "")); + cubeCards.add(new CardIdentity("Coalition Relic", "")); + cubeCards.add(new CardIdentity("Crucible of Worlds", "")); + cubeCards.add(new CardIdentity("Oblivion Stone", "")); + cubeCards.add(new CardIdentity("Sword of Body and Mind", "")); + cubeCards.add(new CardIdentity("Sword of Feast and Famine", "")); + cubeCards.add(new CardIdentity("Sword of Fire and Ice", "")); + cubeCards.add(new CardIdentity("Sword of Light and Shadow", "")); + cubeCards.add(new CardIdentity("Sword of War and Peace", "")); + cubeCards.add(new CardIdentity("Tangle Wire", "")); + cubeCards.add(new CardIdentity("Worn Powerstone", "")); + cubeCards.add(new CardIdentity("Coercive Portal", "")); + cubeCards.add(new CardIdentity("Smokestack", "")); + cubeCards.add(new CardIdentity("Thran Dynamo", "")); + cubeCards.add(new CardIdentity("Batterskull", "")); + cubeCards.add(new CardIdentity("Memory Jar", "")); + cubeCards.add(new CardIdentity("Mindslaver", "")); + cubeCards.add(new CardIdentity("Academy Ruins", "")); + cubeCards.add(new CardIdentity("Ancient Tomb", "")); + cubeCards.add(new CardIdentity("Bazaar of Baghdad", "")); + cubeCards.add(new CardIdentity("Blast Zone", "")); + cubeCards.add(new CardIdentity("Field of Ruin", "")); + cubeCards.add(new CardIdentity("Library of Alexandria", "")); + cubeCards.add(new CardIdentity("Maze of Ith", "")); + cubeCards.add(new CardIdentity("Mishra's Factory", "")); + cubeCards.add(new CardIdentity("Mishra's Workshop", "")); + cubeCards.add(new CardIdentity("Mutavault", "")); + cubeCards.add(new CardIdentity("Nykthos, Shrine to Nyx", "")); + cubeCards.add(new CardIdentity("Rishadan Port", "")); + cubeCards.add(new CardIdentity("Strip Mine", "")); + cubeCards.add(new CardIdentity("Wasteland", "")); + cubeCards.add(new CardIdentity("Expansion // Explosion", "")); + cubeCards.add(new CardIdentity("Giver of Runes", "")); + cubeCards.add(new CardIdentity("Winds of Abandon", "")); + cubeCards.add(new CardIdentity("Hallowed Spiritkeeper", "")); + cubeCards.add(new CardIdentity("Thraben Inspector", "")); + cubeCards.add(new CardIdentity("Narset, Parter of Veils", "")); + cubeCards.add(new CardIdentity("Force of Negation", "")); + cubeCards.add(new CardIdentity("Urza, Lord High Artificer", "")); + cubeCards.add(new CardIdentity("Emry, Lurker of the Loch", "")); + cubeCards.add(new CardIdentity("Brazen Borrower", "")); + cubeCards.add(new CardIdentity("Bolas's Citadel", "")); + cubeCards.add(new CardIdentity("Yawgmoth, Thran Physician", "")); + cubeCards.add(new CardIdentity("Rotting Regisaur", "")); + cubeCards.add(new CardIdentity("Murderous Rider", "")); + cubeCards.add(new CardIdentity("Wishclaw Talisman", "")); + cubeCards.add(new CardIdentity("Dreadhorde Arcanist", "")); + cubeCards.add(new CardIdentity("Seasoned Pyromancer", "")); + cubeCards.add(new CardIdentity("Embereth Shieldbreaker", "")); + cubeCards.add(new CardIdentity("Nissa, Who Shakes the World", "")); + cubeCards.add(new CardIdentity("Questing Beast", "")); + cubeCards.add(new CardIdentity("Teferi, Time Raveler", "")); + cubeCards.add(new CardIdentity("Angrath's Rampage", "")); + cubeCards.add(new CardIdentity("Fallen Shinobi", "")); + cubeCards.add(new CardIdentity("Wrenn and Six", "")); + cubeCards.add(new CardIdentity("Oko, Thief of Crowns", "")); + cubeCards.add(new CardIdentity("Garruk, Cursed Huntsman", "")); + cubeCards.add(new CardIdentity("Prismatic Vista", "")); + cubeCards.add(new CardIdentity("Golos, Tireless Pilgrim", "")); + cubeCards.add(new CardIdentity("Stonecoil Serpent", "")); + } +} + diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeJuly2020.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeJuly2020.java new file mode 100644 index 0000000000..2705dd276f --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeJuly2020.java @@ -0,0 +1,552 @@ +package mage.tournament.cubes; + +import mage.game.draft.DraftCube; + +public class VintageCubeJuly2020 extends DraftCube { + + public VintageCubeJuly2020() { + super("MTGO Vintage Cube July 2020"); + + cubeCards.add(new CardIdentity("Kytheon, Hero of Akros", "")); + cubeCards.add(new CardIdentity("Mother of Runes", "")); + cubeCards.add(new CardIdentity("Student of Warfare", "")); + cubeCards.add(new CardIdentity("Adanto Vanguard", "")); + cubeCards.add(new CardIdentity("Containment Priest", "")); + cubeCards.add(new CardIdentity("Leonin Relic-Warder", "")); + cubeCards.add(new CardIdentity("Porcelain Legionnaire", "")); + cubeCards.add(new CardIdentity("Selfless Spirit", "")); + cubeCards.add(new CardIdentity("Soulfire Grand Master", "")); + cubeCards.add(new CardIdentity("Stoneforge Mystic", "")); + cubeCards.add(new CardIdentity("Thalia, Guardian of Thraben", "")); + cubeCards.add(new CardIdentity("Tithe Taker", "")); + cubeCards.add(new CardIdentity("Wall of Omens", "")); + cubeCards.add(new CardIdentity("Blade Splicer", "")); + cubeCards.add(new CardIdentity("Brightling", "")); + cubeCards.add(new CardIdentity("Brimaz, King of Oreskos", "")); + cubeCards.add(new CardIdentity("Fairgrounds Warden", "")); + cubeCards.add(new CardIdentity("Flickerwisp", "")); + cubeCards.add(new CardIdentity("Monastery Mentor", "")); + cubeCards.add(new CardIdentity("Recruiter of the Guard", "")); + cubeCards.add(new CardIdentity("Silverblade Paladin", "")); + cubeCards.add(new CardIdentity("Emeria Angel", "")); + cubeCards.add(new CardIdentity("Hero of Bladehold", "")); + cubeCards.add(new CardIdentity("Linvala, Keeper of Silence", "")); + cubeCards.add(new CardIdentity("Restoration Angel", "")); + cubeCards.add(new CardIdentity("Angel of Invention", "")); + cubeCards.add(new CardIdentity("Elspeth Conquers Death", "")); + cubeCards.add(new CardIdentity("Archangel Avacyn", "")); + cubeCards.add(new CardIdentity("Baneslayer Angel", "")); + cubeCards.add(new CardIdentity("Lyra Dawnbringer", "")); + cubeCards.add(new CardIdentity("Reveillark", "")); + cubeCards.add(new CardIdentity("Sun Titan", "")); + cubeCards.add(new CardIdentity("Angel of Serenity", "")); + cubeCards.add(new CardIdentity("Elesh Norn, Grand Cenobite", "")); + cubeCards.add(new CardIdentity("Iona, Shield of Emeria", "")); + cubeCards.add(new CardIdentity("Gideon Blackblade", "")); + cubeCards.add(new CardIdentity("Elspeth, Knight-Errant", "")); + cubeCards.add(new CardIdentity("Gideon, Ally of Zendikar", "")); + cubeCards.add(new CardIdentity("Gideon Jura", "")); + cubeCards.add(new CardIdentity("Elspeth, Sun's Champion", "")); + cubeCards.add(new CardIdentity("Condemn", "")); + cubeCards.add(new CardIdentity("Enlightened Tutor", "")); + cubeCards.add(new CardIdentity("Mana Tithe", "")); + cubeCards.add(new CardIdentity("Path to Exile", "")); + cubeCards.add(new CardIdentity("Swords to Plowshares", "")); + cubeCards.add(new CardIdentity("Disenchant", "")); + cubeCards.add(new CardIdentity("Unexpectedly Absent", "")); + cubeCards.add(new CardIdentity("Balance", "")); + cubeCards.add(new CardIdentity("Council's Judgment", "")); + cubeCards.add(new CardIdentity("Spectral Procession", "")); + cubeCards.add(new CardIdentity("Armageddon", "")); + cubeCards.add(new CardIdentity("Day of Judgment", "")); + cubeCards.add(new CardIdentity("Ravages of War", "")); + cubeCards.add(new CardIdentity("Wrath of God", "")); + cubeCards.add(new CardIdentity("Terminus", "")); + cubeCards.add(new CardIdentity("Land Tax", "")); + cubeCards.add(new CardIdentity("Legion's Landing", "")); + cubeCards.add(new CardIdentity("Honor of the Pure", "")); + cubeCards.add(new CardIdentity("Banishing Light", "")); + cubeCards.add(new CardIdentity("Oblivion Ring", "")); + cubeCards.add(new CardIdentity("Faith's Fetters", "")); + cubeCards.add(new CardIdentity("Moat", "")); + cubeCards.add(new CardIdentity("Parallax Wave", "")); + cubeCards.add(new CardIdentity("Spear of Heliod", "")); + cubeCards.add(new CardIdentity("Karakas", "")); + cubeCards.add(new CardIdentity("Thassa's Oracle", "")); + cubeCards.add(new CardIdentity("Baral, Chief of Compliance", "")); + cubeCards.add(new CardIdentity("Jace, Vryn's Prodigy", "")); + cubeCards.add(new CardIdentity("Looter il-Kor", "")); + cubeCards.add(new CardIdentity("Phantasmal Image", "")); + cubeCards.add(new CardIdentity("Snapcaster Mage", "")); + cubeCards.add(new CardIdentity("Thing in the Ice", "")); + cubeCards.add(new CardIdentity("Shark Typhoon", "")); + cubeCards.add(new CardIdentity("Deceiver Exarch", "")); + cubeCards.add(new CardIdentity("Pestermite", "")); + cubeCards.add(new CardIdentity("Spellseeker", "")); + cubeCards.add(new CardIdentity("Trinket Mage", "")); + cubeCards.add(new CardIdentity("Vendilion Clique", "")); + cubeCards.add(new CardIdentity("Glen Elendra Archmage", "")); + cubeCards.add(new CardIdentity("Phyrexian Metamorph", "")); + cubeCards.add(new CardIdentity("Sower of Temptation", "")); + cubeCards.add(new CardIdentity("Venser, Shaper Savant", "")); + cubeCards.add(new CardIdentity("Mulldrifter", "")); + cubeCards.add(new CardIdentity("Riftwing Cloudskate", "")); + cubeCards.add(new CardIdentity("Consecrated Sphinx", "")); + cubeCards.add(new CardIdentity("Frost Titan", "")); + cubeCards.add(new CardIdentity("Torrential Gearhulk", "")); + cubeCards.add(new CardIdentity("Palinchron", "")); + cubeCards.add(new CardIdentity("Inkwell Leviathan", "")); + cubeCards.add(new CardIdentity("Jace Beleren", "")); + cubeCards.add(new CardIdentity("Jace, the Mind Sculptor", "")); + cubeCards.add(new CardIdentity("Tezzeret the Seeker", "")); + cubeCards.add(new CardIdentity("Ancestral Recall", "")); + cubeCards.add(new CardIdentity("Brainstorm", "")); + cubeCards.add(new CardIdentity("High Tide", "")); + cubeCards.add(new CardIdentity("Mystical Tutor", "")); + cubeCards.add(new CardIdentity("Spell Pierce", "")); + cubeCards.add(new CardIdentity("Brain Freeze", "")); + cubeCards.add(new CardIdentity("Counterspell", "")); + cubeCards.add(new CardIdentity("Daze", "")); + cubeCards.add(new CardIdentity("Impulse", "")); + cubeCards.add(new CardIdentity("Mana Drain", "")); + cubeCards.add(new CardIdentity("Mana Leak", "")); + cubeCards.add(new CardIdentity("Miscalculation", "")); + cubeCards.add(new CardIdentity("Remand", "")); + cubeCards.add(new CardIdentity("Frantic Search", "")); + cubeCards.add(new CardIdentity("Thirst for Knowledge", "")); + cubeCards.add(new CardIdentity("Cryptic Command", "")); + cubeCards.add(new CardIdentity("Fact or Fiction", "")); + cubeCards.add(new CardIdentity("Gifts Ungiven", "")); + cubeCards.add(new CardIdentity("Turnabout", "")); + cubeCards.add(new CardIdentity("Force of Will", "")); + cubeCards.add(new CardIdentity("Gush", "")); + cubeCards.add(new CardIdentity("Mystic Confluence", "")); + cubeCards.add(new CardIdentity("Repeal", "")); + cubeCards.add(new CardIdentity("Dig Through Time", "")); + cubeCards.add(new CardIdentity("Ancestral Vision", "")); + cubeCards.add(new CardIdentity("Gitaxian Probe", "")); + cubeCards.add(new CardIdentity("Ponder", "")); + cubeCards.add(new CardIdentity("Preordain", "")); + cubeCards.add(new CardIdentity("Chart a Course", "")); + cubeCards.add(new CardIdentity("Time Walk", "")); + cubeCards.add(new CardIdentity("Show and Tell", "")); + cubeCards.add(new CardIdentity("Timetwister", "")); + cubeCards.add(new CardIdentity("Tinker", "")); + cubeCards.add(new CardIdentity("Bribery", "")); + cubeCards.add(new CardIdentity("Time Warp", "")); + cubeCards.add(new CardIdentity("Mind's Desire", "")); + cubeCards.add(new CardIdentity("Time Spiral", "")); + cubeCards.add(new CardIdentity("Upheaval", "")); + cubeCards.add(new CardIdentity("Treasure Cruise", "")); + cubeCards.add(new CardIdentity("Search for Azcanta", "")); + cubeCards.add(new CardIdentity("Control Magic", "")); + cubeCards.add(new CardIdentity("Opposition", "")); + cubeCards.add(new CardIdentity("Treachery", "")); + cubeCards.add(new CardIdentity("Shelldock Isle", "")); + cubeCards.add(new CardIdentity("Tolarian Academy", "")); + cubeCards.add(new CardIdentity("Putrid Imp", "")); + cubeCards.add(new CardIdentity("Dark Confidant", "")); + cubeCards.add(new CardIdentity("Kitesail Freebooter", "")); + cubeCards.add(new CardIdentity("Mesmeric Fiend", "")); + cubeCards.add(new CardIdentity("Oona's Prowler", "")); + cubeCards.add(new CardIdentity("Pack Rat", "")); + cubeCards.add(new CardIdentity("Vampire Hexmage", "")); + cubeCards.add(new CardIdentity("Bone Shredder", "")); + cubeCards.add(new CardIdentity("Hypnotic Specter", "")); + cubeCards.add(new CardIdentity("Ophiomancer", "")); + cubeCards.add(new CardIdentity("Plaguecrafter", "")); + cubeCards.add(new CardIdentity("Vampire Nighthawk", "")); + cubeCards.add(new CardIdentity("Gonti, Lord of Luxury", "")); + cubeCards.add(new CardIdentity("Nekrataal", "")); + cubeCards.add(new CardIdentity("Ravenous Chupacabra", "")); + cubeCards.add(new CardIdentity("Shriekmaw", "")); + cubeCards.add(new CardIdentity("Grave Titan", "")); + cubeCards.add(new CardIdentity("Ink-Eyes, Servant of Oni", "")); + cubeCards.add(new CardIdentity("Massacre Wurm", "")); + cubeCards.add(new CardIdentity("Tasigur, the Golden Fang", "")); + cubeCards.add(new CardIdentity("Sheoldred, Whispering One", "")); + cubeCards.add(new CardIdentity("Griselbrand", "")); + cubeCards.add(new CardIdentity("Liliana of the Veil", "")); + cubeCards.add(new CardIdentity("Liliana, Death's Majesty", "")); + cubeCards.add(new CardIdentity("Dark Ritual", "")); + cubeCards.add(new CardIdentity("Entomb", "")); + cubeCards.add(new CardIdentity("Fatal Push", "")); + cubeCards.add(new CardIdentity("Vampiric Tutor", "")); + cubeCards.add(new CardIdentity("Cabal Ritual", "")); + cubeCards.add(new CardIdentity("Go for the Throat", "")); + cubeCards.add(new CardIdentity("Liliana's Triumph", "")); + cubeCards.add(new CardIdentity("Shallow Grave", "")); + cubeCards.add(new CardIdentity("Heartless Act", "")); + cubeCards.add(new CardIdentity("Corpse Dance", "")); + cubeCards.add(new CardIdentity("Dismember", "")); + cubeCards.add(new CardIdentity("Hero's Downfall", "")); + cubeCards.add(new CardIdentity("Makeshift Mannequin", "")); + cubeCards.add(new CardIdentity("Duress", "")); + cubeCards.add(new CardIdentity("Imperial Seal", "")); + cubeCards.add(new CardIdentity("Inquisition of Kozilek", "")); + cubeCards.add(new CardIdentity("Reanimate", "")); + cubeCards.add(new CardIdentity("Thoughtseize", "")); + cubeCards.add(new CardIdentity("Collective Brutality", "")); + cubeCards.add(new CardIdentity("Demonic Tutor", "")); + cubeCards.add(new CardIdentity("Exhume", "")); + cubeCards.add(new CardIdentity("Hymn to Tourach", "")); + cubeCards.add(new CardIdentity("Night's Whisper", "")); + cubeCards.add(new CardIdentity("Buried Alive", "")); + cubeCards.add(new CardIdentity("Toxic Deluge", "")); + cubeCards.add(new CardIdentity("Yawgmoth's Will", "")); + cubeCards.add(new CardIdentity("Damnation", "")); + cubeCards.add(new CardIdentity("Languish", "")); + cubeCards.add(new CardIdentity("Mastermind's Acquisition", "")); + cubeCards.add(new CardIdentity("Tendrils of Agony", "")); + cubeCards.add(new CardIdentity("Dark Petition", "")); + cubeCards.add(new CardIdentity("Living Death", "")); + cubeCards.add(new CardIdentity("Mind Twist", "")); + cubeCards.add(new CardIdentity("Animate Dead", "")); + cubeCards.add(new CardIdentity("Bitterblossom", "")); + cubeCards.add(new CardIdentity("Necromancy", "")); + cubeCards.add(new CardIdentity("Phyrexian Arena", "")); + cubeCards.add(new CardIdentity("Recurring Nightmare", "")); + cubeCards.add(new CardIdentity("Yawgmoth's Bargain", "")); + cubeCards.add(new CardIdentity("Goblin Guide", "")); + cubeCards.add(new CardIdentity("Goblin Welder", "")); + cubeCards.add(new CardIdentity("Grim Lavamancer", "")); + cubeCards.add(new CardIdentity("Soul-Scar Mage", "")); + cubeCards.add(new CardIdentity("Monastery Swiftspear", "")); + cubeCards.add(new CardIdentity("Zurgo Bellstriker", "")); + cubeCards.add(new CardIdentity("Abbot of Keral Keep", "")); + cubeCards.add(new CardIdentity("Dire Fleet Daredevil", "")); + cubeCards.add(new CardIdentity("Eidolon of the Great Revel", "")); + cubeCards.add(new CardIdentity("Runaway Steam-Kin", "")); + cubeCards.add(new CardIdentity("Young Pyromancer", "")); + cubeCards.add(new CardIdentity("Goblin Rabblemaster", "")); + cubeCards.add(new CardIdentity("Imperial Recruiter", "")); + cubeCards.add(new CardIdentity("Magus of the Moon", "")); + cubeCards.add(new CardIdentity("Avalanche Riders", "")); + cubeCards.add(new CardIdentity("Flametongue Kavu", "")); + cubeCards.add(new CardIdentity("Hazoret the Fervent", "")); + cubeCards.add(new CardIdentity("Hellrider", "")); + cubeCards.add(new CardIdentity("Pia and Kiran Nalaar", "")); + cubeCards.add(new CardIdentity("Terror of the Peaks", "")); + cubeCards.add(new CardIdentity("Glorybringer", "")); + cubeCards.add(new CardIdentity("Goblin Dark-Dwellers", "")); + cubeCards.add(new CardIdentity("Kiki-Jiki, Mirror Breaker", "")); + cubeCards.add(new CardIdentity("Siege-Gang Commander", "")); + cubeCards.add(new CardIdentity("Thundermaw Hellkite", "")); + cubeCards.add(new CardIdentity("Zealous Conscripts", "")); + cubeCards.add(new CardIdentity("Inferno Titan", "")); + cubeCards.add(new CardIdentity("Chandra, Torch of Defiance", "")); + cubeCards.add(new CardIdentity("Daretti, Scrap Savant", "")); + cubeCards.add(new CardIdentity("Koth of the Hammer", "")); + cubeCards.add(new CardIdentity("Burst Lightning", "")); + cubeCards.add(new CardIdentity("Lightning Bolt", "")); + cubeCards.add(new CardIdentity("Abrade", "")); + cubeCards.add(new CardIdentity("Ancient Grudge", "")); + cubeCards.add(new CardIdentity("Desperate Ritual", "")); + cubeCards.add(new CardIdentity("Fire // Ice", "")); + cubeCards.add(new CardIdentity("Incinerate", "")); + cubeCards.add(new CardIdentity("Lightning Strike", "")); + cubeCards.add(new CardIdentity("Pyretic Ritual", "")); + cubeCards.add(new CardIdentity("Char", "")); + cubeCards.add(new CardIdentity("Seething Song", "")); + cubeCards.add(new CardIdentity("Through the Breach", "")); + cubeCards.add(new CardIdentity("Fireblast", "")); + cubeCards.add(new CardIdentity("Chain Lightning", "")); + cubeCards.add(new CardIdentity("Faithless Looting", "")); + cubeCards.add(new CardIdentity("Firebolt", "")); + cubeCards.add(new CardIdentity("Flame Slash", "")); + cubeCards.add(new CardIdentity("Mizzium Mortars", "")); + cubeCards.add(new CardIdentity("Pyroclasm", "")); + cubeCards.add(new CardIdentity("Light Up the Stage", "")); + cubeCards.add(new CardIdentity("Lukka, Coppercoat Outcast", "")); + cubeCards.add(new CardIdentity("Wheel of Fortune", "")); + cubeCards.add(new CardIdentity("Empty the Warrens", "")); + cubeCards.add(new CardIdentity("Fiery Confluence", "")); + cubeCards.add(new CardIdentity("Past in Flames", "")); + cubeCards.add(new CardIdentity("Banefire", "")); + cubeCards.add(new CardIdentity("Burning of Xinye", "")); + cubeCards.add(new CardIdentity("Wildfire", "")); + cubeCards.add(new CardIdentity("Bonfire of the Damned", "")); + cubeCards.add(new CardIdentity("Mana Flare", "")); + cubeCards.add(new CardIdentity("Sulfuric Vortex", "")); + cubeCards.add(new CardIdentity("Sneak Attack", "")); + cubeCards.add(new CardIdentity("Splinter Twin", "")); + cubeCards.add(new CardIdentity("Arbor Elf", "")); + cubeCards.add(new CardIdentity("Avacyn's Pilgrim", "")); + cubeCards.add(new CardIdentity("Birds of Paradise", "")); + cubeCards.add(new CardIdentity("Elves of Deep Shadow", "")); + cubeCards.add(new CardIdentity("Elvish Mystic", "")); + cubeCards.add(new CardIdentity("Fyndhorn Elves", "")); + cubeCards.add(new CardIdentity("Joraga Treespeaker", "")); + cubeCards.add(new CardIdentity("Llanowar Elves", "")); + cubeCards.add(new CardIdentity("Noble Hierarch", "")); + cubeCards.add(new CardIdentity("Den Protector", "")); + cubeCards.add(new CardIdentity("Devoted Druid", "")); + cubeCards.add(new CardIdentity("Fauna Shaman", "")); + cubeCards.add(new CardIdentity("Gilded Goose", "")); + cubeCards.add(new CardIdentity("Lotus Cobra", "")); + cubeCards.add(new CardIdentity("Rofellos, Llanowar Emissary", "")); + cubeCards.add(new CardIdentity("Sakura-Tribe Elder", "")); + cubeCards.add(new CardIdentity("Scavenging Ooze", "")); + cubeCards.add(new CardIdentity("Sylvan Caryatid", "")); + cubeCards.add(new CardIdentity("Wall of Blossoms", "")); + cubeCards.add(new CardIdentity("Wall of Roots", "")); + cubeCards.add(new CardIdentity("Courser of Kruphix", "")); + cubeCards.add(new CardIdentity("Eternal Witness", "")); + cubeCards.add(new CardIdentity("Ramunap Excavator", "")); + cubeCards.add(new CardIdentity("Reclamation Sage", "")); + cubeCards.add(new CardIdentity("Tireless Tracker", "")); + cubeCards.add(new CardIdentity("Yavimaya Elder", "")); + cubeCards.add(new CardIdentity("Master of the Wild Hunt", "")); + cubeCards.add(new CardIdentity("Oracle of Mul Daya", "")); + cubeCards.add(new CardIdentity("Polukranos, World Eater", "")); + cubeCards.add(new CardIdentity("Acidic Slime", "")); + cubeCards.add(new CardIdentity("Biogenic Ooze", "")); + cubeCards.add(new CardIdentity("Deranged Hermit", "")); + cubeCards.add(new CardIdentity("Thragtusk", "")); + cubeCards.add(new CardIdentity("Whisperwood Elemental", "")); + cubeCards.add(new CardIdentity("Carnage Tyrant", "")); + cubeCards.add(new CardIdentity("Primeval Titan", "")); + cubeCards.add(new CardIdentity("Avenger of Zendikar", "")); + cubeCards.add(new CardIdentity("Craterhoof Behemoth", "")); + cubeCards.add(new CardIdentity("Terastodon", "")); + cubeCards.add(new CardIdentity("Woodfall Primus", "")); + cubeCards.add(new CardIdentity("Dryad of the Ilysian Grove", "")); + cubeCards.add(new CardIdentity("Garruk Relentless", "")); + cubeCards.add(new CardIdentity("Garruk Wildspeaker", "")); + cubeCards.add(new CardIdentity("Vivien, Monsters' Advocate", "")); + cubeCards.add(new CardIdentity("Vivien Reid", "")); + cubeCards.add(new CardIdentity("Nature's Claim", "")); + cubeCards.add(new CardIdentity("Beast Within", "")); + cubeCards.add(new CardIdentity("Channel", "")); + cubeCards.add(new CardIdentity("Regrowth", "")); + cubeCards.add(new CardIdentity("Cultivate", "")); + cubeCards.add(new CardIdentity("Search for Tomorrow", "")); + cubeCards.add(new CardIdentity("Eureka", "")); + cubeCards.add(new CardIdentity("Harmonize", "")); + cubeCards.add(new CardIdentity("Natural Order", "")); + cubeCards.add(new CardIdentity("Plow Under", "")); + cubeCards.add(new CardIdentity("Primal Command", "")); + cubeCards.add(new CardIdentity("Green Sun's Zenith", "")); + cubeCards.add(new CardIdentity("Finale of Devastation", "")); + cubeCards.add(new CardIdentity("Tooth and Nail", "")); + cubeCards.add(new CardIdentity("Fastbond", "")); + cubeCards.add(new CardIdentity("Oath of Druids", "")); + cubeCards.add(new CardIdentity("Survival of the Fittest", "")); + cubeCards.add(new CardIdentity("Sylvan Library", "")); + cubeCards.add(new CardIdentity("Heartbeat of Spring", "")); + cubeCards.add(new CardIdentity("Elder Gargaroth", "")); + cubeCards.add(new CardIdentity("Gaea's Cradle", "")); + cubeCards.add(new CardIdentity("Yorion, Sky Nomad", "")); + cubeCards.add(new CardIdentity("Teferi, Hero of Dominaria", "")); + cubeCards.add(new CardIdentity("Sphinx's Revelation", "")); + cubeCards.add(new CardIdentity("Fractured Identity", "")); + cubeCards.add(new CardIdentity("Celestial Colonnade", "")); + cubeCards.add(new CardIdentity("Flooded Strand", "")); + cubeCards.add(new CardIdentity("Hallowed Fountain", "")); + cubeCards.add(new CardIdentity("Seachrome Coast", "")); + cubeCards.add(new CardIdentity("Tundra", "")); + cubeCards.add(new CardIdentity("Thief of Sanity", "")); + cubeCards.add(new CardIdentity("The Scarab God", "")); + cubeCards.add(new CardIdentity("Ashiok, Nightmare Weaver", "")); + cubeCards.add(new CardIdentity("Baleful Strix", "")); + cubeCards.add(new CardIdentity("Creeping Tar Pit", "")); + cubeCards.add(new CardIdentity("Darkslick Shores", "")); + cubeCards.add(new CardIdentity("Polluted Delta", "")); + cubeCards.add(new CardIdentity("Underground Sea", "")); + cubeCards.add(new CardIdentity("Watery Grave", "")); + cubeCards.add(new CardIdentity("Daretti, Ingenious Iconoclast", "")); + cubeCards.add(new CardIdentity("Kroxa, Titan of Death's Hunger", "")); + cubeCards.add(new CardIdentity("Kolaghan's Command", "")); + cubeCards.add(new CardIdentity("Rakdos's Return", "")); + cubeCards.add(new CardIdentity("Badlands", "")); + cubeCards.add(new CardIdentity("Blackcleave Cliffs", "")); + cubeCards.add(new CardIdentity("Blood Crypt", "")); + cubeCards.add(new CardIdentity("Bloodstained Mire", "")); + cubeCards.add(new CardIdentity("Lavaclaw Reaches", "")); + cubeCards.add(new CardIdentity("Bloodbraid Elf", "")); + cubeCards.add(new CardIdentity("Huntmaster of the Fells", "")); + cubeCards.add(new CardIdentity("Dragonlord Atarka", "")); + cubeCards.add(new CardIdentity("Manamorphose", "")); + cubeCards.add(new CardIdentity("Copperline Gorge", "")); + cubeCards.add(new CardIdentity("Raging Ravine", "")); + cubeCards.add(new CardIdentity("Stomping Ground", "")); + cubeCards.add(new CardIdentity("Taiga", "")); + cubeCards.add(new CardIdentity("Wooded Foothills", "")); + cubeCards.add(new CardIdentity("Kitchen Finks", "")); + cubeCards.add(new CardIdentity("Knight of Autumn", "")); + cubeCards.add(new CardIdentity("Knight of the Reliquary", "")); + cubeCards.add(new CardIdentity("Trostani Discordant", "")); + cubeCards.add(new CardIdentity("Mirari's Wake", "")); + cubeCards.add(new CardIdentity("Razorverge Thicket", "")); + cubeCards.add(new CardIdentity("Savannah", "")); + cubeCards.add(new CardIdentity("Stirring Wildwood", "")); + cubeCards.add(new CardIdentity("Temple Garden", "")); + cubeCards.add(new CardIdentity("Windswept Heath", "")); + cubeCards.add(new CardIdentity("Ashen Rider", "")); + cubeCards.add(new CardIdentity("Kaya, Orzhov Usurper", "")); + cubeCards.add(new CardIdentity("Tidehollow Sculler", "")); + cubeCards.add(new CardIdentity("Anguished Unmaking", "")); + cubeCards.add(new CardIdentity("Lingering Souls", "")); + cubeCards.add(new CardIdentity("Vindicate", "")); + cubeCards.add(new CardIdentity("Unburial Rites", "")); + cubeCards.add(new CardIdentity("Concealed Courtyard", "")); + cubeCards.add(new CardIdentity("Godless Shrine", "")); + cubeCards.add(new CardIdentity("Marsh Flats", "")); + cubeCards.add(new CardIdentity("Scrubland", "")); + cubeCards.add(new CardIdentity("Shambling Vent", "")); + cubeCards.add(new CardIdentity("Vraska, Golgari Queen", "")); + cubeCards.add(new CardIdentity("Assassin's Trophy", "")); + cubeCards.add(new CardIdentity("Maelstrom Pulse", "")); + cubeCards.add(new CardIdentity("Pernicious Deed", "")); + cubeCards.add(new CardIdentity("Bayou", "")); + cubeCards.add(new CardIdentity("Blooming Marsh", "")); + cubeCards.add(new CardIdentity("Hissing Quagmire", "")); + cubeCards.add(new CardIdentity("Overgrown Tomb", "")); + cubeCards.add(new CardIdentity("Verdant Catacombs", "")); + cubeCards.add(new CardIdentity("Edric, Spymaster of Trest", "")); + cubeCards.add(new CardIdentity("Trygon Predator", "")); + cubeCards.add(new CardIdentity("Hydroid Krasis", "")); + cubeCards.add(new CardIdentity("Uro, Titan of Nature's Wrath", "")); + cubeCards.add(new CardIdentity("Botanical Sanctum", "")); + cubeCards.add(new CardIdentity("Breeding Pool", "")); + cubeCards.add(new CardIdentity("Lumbering Falls", "")); + cubeCards.add(new CardIdentity("Misty Rainforest", "")); + cubeCards.add(new CardIdentity("Tropical Island", "")); + cubeCards.add(new CardIdentity("Goblin Electromancer", "")); + cubeCards.add(new CardIdentity("Dack Fayden", "")); + cubeCards.add(new CardIdentity("Thousand-Year Storm", "")); + cubeCards.add(new CardIdentity("Scalding Tarn", "")); + cubeCards.add(new CardIdentity("Spirebluff Canal", "")); + cubeCards.add(new CardIdentity("Steam Vents", "")); + cubeCards.add(new CardIdentity("Volcanic Island", "")); + cubeCards.add(new CardIdentity("Wandering Fumarole", "")); + cubeCards.add(new CardIdentity("Figure of Destiny", "")); + cubeCards.add(new CardIdentity("Ajani Vengeant", "")); + cubeCards.add(new CardIdentity("Nahiri, the Harbinger", "")); + cubeCards.add(new CardIdentity("Wear // Tear", "")); + cubeCards.add(new CardIdentity("Lightning Helix", "")); + cubeCards.add(new CardIdentity("Arid Mesa", "")); + cubeCards.add(new CardIdentity("Inspiring Vantage", "")); + cubeCards.add(new CardIdentity("Needle Spires", "")); + cubeCards.add(new CardIdentity("Plateau", "")); + cubeCards.add(new CardIdentity("Sacred Foundry", "")); + cubeCards.add(new CardIdentity("Sphinx of the Steel Wind", "")); + cubeCards.add(new CardIdentity("Nicol Bolas, Dragon-God", "")); + cubeCards.add(new CardIdentity("Leovold, Emissary of Trest", "")); + cubeCards.add(new CardIdentity("Progenitus", "")); + cubeCards.add(new CardIdentity("Kozilek, Butcher of Truth", "")); + cubeCards.add(new CardIdentity("Ulamog, the Ceaseless Hunger", "")); + cubeCards.add(new CardIdentity("Ulamog, the Infinite Gyre", "")); + cubeCards.add(new CardIdentity("Emrakul, the Promised End", "")); + cubeCards.add(new CardIdentity("Emrakul, the Aeons Torn", "")); + cubeCards.add(new CardIdentity("Karn, Scion of Urza", "")); + cubeCards.add(new CardIdentity("Karn Liberated", "")); + cubeCards.add(new CardIdentity("Ugin, the Spirit Dragon", "")); + cubeCards.add(new CardIdentity("Bomat Courier", "")); + cubeCards.add(new CardIdentity("Hangarback Walker", "")); + cubeCards.add(new CardIdentity("Phyrexian Revoker", "")); + cubeCards.add(new CardIdentity("Metalworker", "")); + cubeCards.add(new CardIdentity("Lodestone Golem", "")); + cubeCards.add(new CardIdentity("Solemn Simulacrum", "")); + cubeCards.add(new CardIdentity("Kuldotha Forgemaster", "")); + cubeCards.add(new CardIdentity("Wurmcoil Engine", "")); + cubeCards.add(new CardIdentity("Myr Battlesphere", "")); + cubeCards.add(new CardIdentity("Sundering Titan", "")); + cubeCards.add(new CardIdentity("Walking Ballista", "")); + cubeCards.add(new CardIdentity("Blightsteel Colossus", "")); + cubeCards.add(new CardIdentity("Black Lotus", "")); + cubeCards.add(new CardIdentity("Chrome Mox", "")); + cubeCards.add(new CardIdentity("Everflowing Chalice", "")); + cubeCards.add(new CardIdentity("Lion's Eye Diamond", "")); + cubeCards.add(new CardIdentity("Lotus Bloom", "")); + cubeCards.add(new CardIdentity("Mana Crypt", "")); + cubeCards.add(new CardIdentity("Mox Diamond", "")); + cubeCards.add(new CardIdentity("Mox Emerald", "")); + cubeCards.add(new CardIdentity("Mox Jet", "")); + cubeCards.add(new CardIdentity("Mox Pearl", "")); + cubeCards.add(new CardIdentity("Mox Ruby", "")); + cubeCards.add(new CardIdentity("Mox Sapphire", "")); + cubeCards.add(new CardIdentity("Mana Vault", "")); + cubeCards.add(new CardIdentity("Relic of Progenitus", "")); + cubeCards.add(new CardIdentity("Sensei's Divining Top", "")); + cubeCards.add(new CardIdentity("Skullclamp", "")); + cubeCards.add(new CardIdentity("Sol Ring", "")); + cubeCards.add(new CardIdentity("Azorius Signet", "")); + cubeCards.add(new CardIdentity("Boros Signet", "")); + cubeCards.add(new CardIdentity("Dimir Signet", "")); + cubeCards.add(new CardIdentity("Golgari Signet", "")); + cubeCards.add(new CardIdentity("Grim Monolith", "")); + cubeCards.add(new CardIdentity("Gruul Signet", "")); + cubeCards.add(new CardIdentity("Izzet Signet", "")); + cubeCards.add(new CardIdentity("Lightning Greaves", "")); + cubeCards.add(new CardIdentity("Orzhov Signet", "")); + cubeCards.add(new CardIdentity("Rakdos Signet", "")); + cubeCards.add(new CardIdentity("Selesnya Signet", "")); + cubeCards.add(new CardIdentity("Shrine of Burning Rage", "")); + cubeCards.add(new CardIdentity("Simic Signet", "")); + cubeCards.add(new CardIdentity("Smuggler's Copter", "")); + cubeCards.add(new CardIdentity("Umezawa's Jitte", "")); + cubeCards.add(new CardIdentity("Winter Orb", "")); + cubeCards.add(new CardIdentity("Basalt Monolith", "")); + cubeCards.add(new CardIdentity("Coalition Relic", "")); + cubeCards.add(new CardIdentity("Crucible of Worlds", "")); + cubeCards.add(new CardIdentity("Oblivion Stone", "")); + cubeCards.add(new CardIdentity("Sword of Body and Mind", "")); + cubeCards.add(new CardIdentity("Sword of Feast and Famine", "")); + cubeCards.add(new CardIdentity("Sword of Fire and Ice", "")); + cubeCards.add(new CardIdentity("Sword of Light and Shadow", "")); + cubeCards.add(new CardIdentity("Sword of War and Peace", "")); + cubeCards.add(new CardIdentity("Tangle Wire", "")); + cubeCards.add(new CardIdentity("Worn Powerstone", "")); + cubeCards.add(new CardIdentity("Coercive Portal", "")); + cubeCards.add(new CardIdentity("Smokestack", "")); + cubeCards.add(new CardIdentity("Thran Dynamo", "")); + cubeCards.add(new CardIdentity("Batterskull", "")); + cubeCards.add(new CardIdentity("Memory Jar", "")); + cubeCards.add(new CardIdentity("Mindslaver", "")); + cubeCards.add(new CardIdentity("Academy Ruins", "")); + cubeCards.add(new CardIdentity("Ancient Tomb", "")); + cubeCards.add(new CardIdentity("Bazaar of Baghdad", "")); + cubeCards.add(new CardIdentity("Blast Zone", "")); + cubeCards.add(new CardIdentity("Field of Ruin", "")); + cubeCards.add(new CardIdentity("Library of Alexandria", "")); + cubeCards.add(new CardIdentity("Maze of Ith", "")); + cubeCards.add(new CardIdentity("Mishra's Factory", "")); + cubeCards.add(new CardIdentity("Mishra's Workshop", "")); + cubeCards.add(new CardIdentity("Mutavault", "")); + cubeCards.add(new CardIdentity("Nykthos, Shrine to Nyx", "")); + cubeCards.add(new CardIdentity("Rishadan Port", "")); + cubeCards.add(new CardIdentity("Strip Mine", "")); + cubeCards.add(new CardIdentity("Wasteland", "")); + cubeCards.add(new CardIdentity("Expansion // Explosion", "")); + cubeCards.add(new CardIdentity("Giver of Runes", "")); + cubeCards.add(new CardIdentity("Winds of Abandon", "")); + cubeCards.add(new CardIdentity("Hallowed Spiritkeeper", "")); + cubeCards.add(new CardIdentity("Thraben Inspector", "")); + cubeCards.add(new CardIdentity("Narset, Parter of Veils", "")); + cubeCards.add(new CardIdentity("Force of Negation", "")); + cubeCards.add(new CardIdentity("Urza, Lord High Artificer", "")); + cubeCards.add(new CardIdentity("Emry, Lurker of the Loch", "")); + cubeCards.add(new CardIdentity("Brazen Borrower", "")); + cubeCards.add(new CardIdentity("Bolas's Citadel", "")); + cubeCards.add(new CardIdentity("Yawgmoth, Thran Physician", "")); + cubeCards.add(new CardIdentity("Rotting Regisaur", "")); + cubeCards.add(new CardIdentity("Murderous Rider", "")); + cubeCards.add(new CardIdentity("Wishclaw Talisman", "")); + cubeCards.add(new CardIdentity("Dreadhorde Arcanist", "")); + cubeCards.add(new CardIdentity("Seasoned Pyromancer", "")); + cubeCards.add(new CardIdentity("Embereth Shieldbreaker", "")); + cubeCards.add(new CardIdentity("Nissa, Who Shakes the World", "")); + cubeCards.add(new CardIdentity("Questing Beast", "")); + cubeCards.add(new CardIdentity("Teferi, Time Raveler", "")); + cubeCards.add(new CardIdentity("Angrath's Rampage", "")); + cubeCards.add(new CardIdentity("Fallen Shinobi", "")); + cubeCards.add(new CardIdentity("Wrenn and Six", "")); + cubeCards.add(new CardIdentity("Oko, Thief of Crowns", "")); + cubeCards.add(new CardIdentity("Fiend Artisan", "")); + cubeCards.add(new CardIdentity("Prismatic Vista", "")); + cubeCards.add(new CardIdentity("Golos, Tireless Pilgrim", "")); + cubeCards.add(new CardIdentity("Stonecoil Serpent", "")); + } +} + diff --git a/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml b/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml index 8fa3ceae29..b1c87275c3 100644 --- a/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml +++ b/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-tournament-constructed diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml b/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml index 3530951a0d..cee6d7b893 100644 --- a/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.42 + 1.4.44 mage-tournament-sealed diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java new file mode 100644 index 0000000000..d65634936a --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java @@ -0,0 +1,44 @@ + + +package mage.tournament; + +import mage.game.tournament.TournamentOptions; +import mage.game.tournament.TournamentSingleElimination; + +public class JumpstartEliminationTournament extends TournamentSingleElimination { + + protected enum TournamentStep { + START, OPEN_BOOSTERS, CONSTRUCT, COMPETE, WINNERS + } + + protected TournamentStep currentStep; + + public JumpstartEliminationTournament(TournamentOptions options) { + super(options); + currentStep = TournamentStep.START; + } + + @Override + public void nextStep() { + switch (currentStep) { + case START: + currentStep = TournamentStep.OPEN_BOOSTERS; + openBoosters(); + break; + case OPEN_BOOSTERS: + currentStep = TournamentStep.CONSTRUCT; + construct(); + break; + case CONSTRUCT: + currentStep = TournamentStep.COMPETE; + runTournament(); + break; + case COMPETE: + currentStep = TournamentStep.WINNERS; + winners(); + end(); + break; + } + } + +} diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java new file mode 100644 index 0000000000..858588e441 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java @@ -0,0 +1,19 @@ + + +package mage.tournament; + +import mage.game.tournament.TournamentType; + +public class JumpstartEliminationTournamentType extends TournamentType { + + public JumpstartEliminationTournamentType() { + this.name = "Jumpstart Elimination"; + this.maxPlayers = 16; + this.minPlayers = 2; + this.numBoosters = 0; + this.isJumpstart = true; + this.elimination = true; + this.limited = true; + } + +} diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java new file mode 100644 index 0000000000..f0e7f34bc9 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java @@ -0,0 +1,44 @@ + + +package mage.tournament; + +import mage.game.tournament.TournamentOptions; +import mage.game.tournament.TournamentSwiss; + +public class JumpstartSwissTournament extends TournamentSwiss { + + protected enum TournamentStep { + START, OPEN_BOOSTERS, CONSTRUCT, COMPETE, WINNERS + } + + protected TournamentStep currentStep; + + public JumpstartSwissTournament(TournamentOptions options) { + super(options); + currentStep = TournamentStep.START; + } + + @Override + public void nextStep() { + switch (currentStep) { + case START: + currentStep = TournamentStep.OPEN_BOOSTERS; + openBoosters(); + break; + case OPEN_BOOSTERS: + currentStep = TournamentStep.CONSTRUCT; + construct(); + break; + case CONSTRUCT: + currentStep = TournamentStep.COMPETE; + runTournament(); + break; + case COMPETE: + currentStep = TournamentStep.WINNERS; + winners(); + end(); + break; + } + } + +} diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java new file mode 100644 index 0000000000..bf5328ed52 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java @@ -0,0 +1,18 @@ + + +package mage.tournament; + +import mage.game.tournament.TournamentType; + +public class JumpstartSwissTournamentType extends TournamentType { + + public JumpstartSwissTournamentType() { + this.name = "Jumpstart Swiss"; + this.maxPlayers = 16; + this.minPlayers = 2; + this.numBoosters = 0; + this.isJumpstart = true; + this.limited = true; + } + +} diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml index c449b61d49..93526f24f2 100644 --- a/Mage.Server.Plugins/pom.xml +++ b/Mage.Server.Plugins/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 mage-server-plugins diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 674531e5f8..ceb13e9ece 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -56,19 +56,19 @@ saveGameActivated="false" authenticationActivated="false" googleAccount="" - mailgunApiKey="key-d93e81f19a9c9ed243ebb7cc9381385c" - mailgunDomain="sandbox401a433f30d445309a5e86b6c53f7812.mailgun.org" - mailSmtpHost="smtp.1und1.de" - mailSmtpPort="465" - mailUser="xmageserver@online.de" - mailPassword="24wrsfxv" - mailFromAddress="xmageserver@online.de" + mailgunApiKey="" + mailgunDomain="" + mailSmtpHost="" + mailSmtpPort="" + mailUser="" + mailPassword="" + mailFromAddress="" /> - + @@ -104,6 +104,8 @@ + + @@ -117,6 +119,7 @@ + @@ -138,11 +141,13 @@ - - - - - + + + + + + + @@ -158,6 +163,7 @@ + diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index 318db625ad..b1cd892e65 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 mage-server @@ -22,7 +22,7 @@ org.apache.commons commons-lang3 - 3.8.1 + 3.10 ${project.groupId} @@ -42,13 +42,13 @@ com.sun.xml.bind jaxb-impl - 2.3.2 + 2.3.3 org.glassfish.jaxb jaxb-runtime - 2.3.2 + 2.3.3 log4j @@ -250,7 +250,7 @@ com.google.oauth-client google-oauth-client-java6 - 1.28.0 + 1.31.0 jar @@ -283,7 +283,7 @@ org.xerial sqlite-jdbc - 3.25.2 + 3.32.3.1 @@ -342,7 +342,7 @@ org.codehaus.mojo build-helper-maven-plugin - 1.7 + 3.2.0 add-source diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index 46c9f625e2..d8950d71a0 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -98,6 +98,8 @@ + + @@ -111,6 +113,7 @@ + @@ -136,6 +139,8 @@ + + @@ -152,6 +157,7 @@ + diff --git a/Mage.Server/release/server.msg.txt b/Mage.Server/release/server.msg.txt index 82d31dc20a..353cf19db3 100644 --- a/Mage.Server/release/server.msg.txt +++ b/Mage.Server/release/server.msg.txt @@ -1,4 +1,3 @@ -Welcome! You are playing XMage version 1.4.3 -Find what was changed since previous versions on project Wiki https://github.com/magefree/mage/wiki -Contact us on the XMAGE board of http://www.slightlymagic.net/forum/viewforum.php?f=70 for bug reports or enhancement requests. -Download newest version from http://XMage.de \ No newline at end of file +You are playing XMage version ${project.version}. Changes: https://github.com/magefree/mage/wiki/Release-changes +Report problems by discord, reddit or by github: https://github.com/magefree/mage/issues +Contact us on the XMage forum at https://www.slightlymagic.net/forum/viewforum.php?f=70 \ No newline at end of file diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index 386c8c2a66..9387231256 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -397,15 +397,15 @@ public class MageServerImpl implements MageServer { @Override //FIXME: why no sessionId here??? - public Optional getTable(UUID roomId, UUID tableId) throws MageException { + public TableView getTable(UUID roomId, UUID tableId) throws MageException { try { Optional room = GamesRoomManager.instance.getRoom(roomId); - return room.flatMap(r -> r.getTable(tableId)); + return room.flatMap(r -> r.getTable(tableId)).orElse(null); } catch (Exception ex) { handleException(ex); } - return Optional.empty(); + return null; } @Override @@ -536,18 +536,18 @@ public class MageServerImpl implements MageServer { @Override //FIXME: why no sessionId here??? - public Optional getRoomChatId(UUID roomId) throws MageException { + public UUID getRoomChatId(UUID roomId) throws MageException { try { Optional room = GamesRoomManager.instance.getRoom(roomId); if (!room.isPresent()) { logger.error("roomId not found : " + roomId); - return Optional.empty(); + return null; } - return Optional.of(room.get().getChatId()); + return room.get().getChatId(); } catch (Exception ex) { handleException(ex); } - return Optional.empty(); + return null; } @Override @@ -602,13 +602,13 @@ public class MageServerImpl implements MageServer { @Override //FIXME: why no sessionId here??? - public Optional getTableChatId(UUID tableId) throws MageException { + public UUID getTableChatId(UUID tableId) throws MageException { try { - return TableManager.instance.getChatId(tableId); + return TableManager.instance.getChatId(tableId).orElse(null); } catch (Exception ex) { handleException(ex); } - return Optional.empty(); + return null; } @Override @@ -646,24 +646,24 @@ public class MageServerImpl implements MageServer { @Override //FIXME: why no sessionId here??? - public Optional getGameChatId(UUID gameId) throws MageException { + public UUID getGameChatId(UUID gameId) throws MageException { try { - return GameManager.instance.getChatId(gameId); + return GameManager.instance.getChatId(gameId).orElse(null); } catch (Exception ex) { handleException(ex); } - return Optional.empty(); + return null; } @Override //FIXME: why no sessionId here??? - public Optional getTournamentChatId(UUID tournamentId) throws MageException { + public UUID getTournamentChatId(UUID tournamentId) throws MageException { try { - return TournamentManager.instance.getChatId(tournamentId); + return TournamentManager.instance.getChatId(tournamentId).orElse(null); } catch (Exception ex) { handleException(ex); } - return Optional.empty(); + return null; } @Override diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 83b8ec85e8..f5bf2d98d8 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -232,7 +232,7 @@ public class Session { if (reconnect) { // must be connected to receive the message Optional room = GamesRoomManager.instance.getRoom(GamesRoomManager.instance.getMainRoomId()); if (!room.isPresent()) { - logger.error("main room not found"); + logger.warn("main room not found"); // after server restart users try to use old rooms on reconnect return null; } ChatManager.instance.joinChat(room.get().getChatId(), userId); diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index 5301e89334..5f42a4c927 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -3,6 +3,7 @@ package mage.server; import mage.MageException; import mage.cards.decks.Deck; import mage.cards.decks.DeckCardLists; +import mage.cards.decks.DeckValidatorError; import mage.cards.decks.DeckValidatorFactory; import mage.constants.RangeOfInfluence; import mage.constants.TableState; @@ -32,11 +33,8 @@ import mage.server.util.ThreadExecutor; import mage.view.ChatMessage; import org.apache.log4j.Logger; -import java.util.Locale; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Optional; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -150,9 +148,10 @@ public class TableController { } if (!Main.isTestMode() && !table.getValidator().validate(deck)) { StringBuilder sb = new StringBuilder("You (").append(name).append(") have an invalid deck for the selected ").append(table.getValidator().getName()).append(" Format. \n\n"); - for (Map.Entry entry : table.getValidator().getInvalid().entrySet()) { - sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n'); - } + List errorsList = table.getValidator().getErrorsListSorted(); + errorsList.stream().forEach(error -> { + sb.append(error.getGroup()).append(": ").append(error.getMessage()).append("\n"); + }); sb.append("\n\nSelect a deck that is appropriate for the selected format and try again!"); user.showUserMessage("Join Table", sb.toString()); if (isOwner(userId)) { @@ -267,9 +266,10 @@ public class TableController { if (!Main.isTestMode() && !table.getValidator().validate(deck)) { StringBuilder sb = new StringBuilder("You (").append(name).append(") have an invalid deck for the selected ").append(table.getValidator().getName()).append(" Format. \n\n"); - for (Map.Entry entry : table.getValidator().getInvalid().entrySet()) { - sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n'); - } + List errorsList = table.getValidator().getErrorsListSorted(); + errorsList.stream().forEach(error -> { + sb.append(error.getGroup()).append(": ").append(error.getMessage()).append("\n"); + }); sb.append("\n\nSelect a deck that is appropriate for the selected format and try again!"); user.showUserMessage("Join Table", sb.toString()); if (isOwner(userId)) { @@ -425,9 +425,10 @@ public class TableController { return false; } StringBuilder sb = new StringBuilder("Invalid deck for the selected ").append(table.getValidator().getName()).append(" format. \n\n"); - for (Map.Entry entry : table.getValidator().getInvalid().entrySet()) { - sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n'); - } + List errorsList = table.getValidator().getErrorsListSorted(); + errorsList.stream().forEach(error -> { + sb.append(error.getGroup()).append(": ").append(error.getMessage()).append("\n"); + }); sb.append("\n\nAdd enough cards and try again!"); _user.get().showUserMessage("Submit deck", sb.toString()); return false; diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index a1963f587c..e290eb3b73 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -326,7 +326,7 @@ public class GameController implements GameCallback { private void sendInfoAboutPlayersNotJoinedYetAndTryToFixIt() { // runs every 5 secs untill all players join for (Player player : game.getPlayers().values()) { - if (player.isInGame() && player.isHuman()) { + if (player.canRespond() && player.isHuman()) { Optional requestedUser = getUserByPlayerId(player.getId()); if (requestedUser.isPresent()) { User user = requestedUser.get(); @@ -1259,10 +1259,10 @@ public class GameController implements GameCallback { .collect(Collectors.joining(", "))); StringBuilder sb = new StringBuilder(); - sb.append("FIX command called by " + user.getName() + ""); + sb.append("FIX command called by ").append(user.getName()).append(""); sb.append(""); // font resize start for all next logs - sb.append("
Game ID: " + game.getId()); - + sb.append("
Game ID: ").append(game.getId()); + sb.append("
Phase: ").append(game.getTurn().getPhaseType().toString()).append(" Step: ").append(game.getTurn().getStepType().toString()); // pings info sb.append("
"); sb.append(getPingsInfo()); @@ -1272,7 +1272,7 @@ public class GameController implements GameCallback { // fix active Player playerActive = game.getPlayer(state.getActivePlayerId()); - sb.append("
Fixing active player: " + getName(playerActive)); + sb.append("
Fixing active player: ").append(getName(playerActive)); if (playerActive != null && !playerActive.canRespond()) { fixActions.add("active player fix"); @@ -1280,16 +1280,16 @@ public class GameController implements GameCallback { sb.append("
Try to concede..."); playerActive.concede(game); playerActive.leave(); // abort any wait response actions - sb.append(" (" + asWarning("OK") + ", concede done)"); + sb.append(" (").append(asWarning("OK")).append(", concede done)"); sb.append("
Try to skip step..."); Phase currentPhase = game.getPhase(); if (currentPhase != null) { currentPhase.getStep().skipStep(game, state.getActivePlayerId()); fixedAlready = true; - sb.append(" (" + asWarning("OK") + ", skip step done)"); + sb.append(" (").append(asWarning("OK")).append(", skip step done)"); } else { - sb.append(" (" + asBad("FAIL") + ", step is null)"); + sb.append(" (").append(asBad("FAIL")).append(", step is null)"); } } else { sb.append(playerActive != null ? " (" + asGood("OK") + ", can respond)" : " (" + asGood("OK") + ", no player)"); @@ -1297,7 +1297,7 @@ public class GameController implements GameCallback { // fix lost choosing dialog Player choosingPlayer = game.getPlayer(state.getChoosingPlayerId()); - sb.append("
Fixing choosing player: " + getName(choosingPlayer)); + sb.append("
Fixing choosing player: ").append(getName(choosingPlayer)); if (choosingPlayer != null && !choosingPlayer.canRespond()) { fixActions.add("choosing player fix"); @@ -1305,7 +1305,7 @@ public class GameController implements GameCallback { sb.append("
Try to concede..."); choosingPlayer.concede(game); choosingPlayer.leave(); // abort any wait response actions - sb.append(" (" + asWarning("OK") + ", concede done)"); + sb.append(" (").append(asWarning("OK")).append(", concede done)"); sb.append("
Try to skip step..."); if (fixedAlready) { @@ -1315,9 +1315,9 @@ public class GameController implements GameCallback { if (currentPhase != null) { currentPhase.getStep().skipStep(game, state.getActivePlayerId()); fixedAlready = true; - sb.append(" (" + asWarning("OK") + ", skip step done)"); + sb.append(" (").append(asWarning("OK")).append(", skip step done)"); } else { - sb.append(" (" + asBad("FAIL") + ", step is null)"); + sb.append(" (").append(asBad("FAIL")).append(", step is null)"); } } } else { @@ -1326,7 +1326,7 @@ public class GameController implements GameCallback { // fix lost priority Player priorityPlayer = game.getPlayer(state.getPriorityPlayerId()); - sb.append("
Fixing priority player: " + getName(priorityPlayer)); + sb.append("
Fixing priority player: ").append(getName(priorityPlayer)); if (priorityPlayer != null && !priorityPlayer.canRespond()) { fixActions.add("priority player fix"); @@ -1334,19 +1334,19 @@ public class GameController implements GameCallback { sb.append("
Try to concede..."); priorityPlayer.concede(game); priorityPlayer.leave(); // abort any wait response actions - sb.append(" (" + asWarning("OK") + ", concede done)"); + sb.append(" (").append(asWarning("OK")).append(", concede done)"); sb.append("
Try to skip step..."); if (fixedAlready) { - sb.append(" (" + asWarning("OK") + ", already skipped before)"); + sb.append(" (").append(asWarning("OK")).append(", already skipped before)"); } else { Phase currentPhase = game.getPhase(); if (currentPhase != null) { currentPhase.getStep().skipStep(game, state.getActivePlayerId()); fixedAlready = true; - sb.append(" (" + asWarning("OK") + ", skip step done)"); + sb.append(" (").append(asWarning("OK")).append(", skip step done)"); } else { - sb.append(" (" + asBad("FAIL") + ", step is null)"); + sb.append(" (").append(asBad("FAIL")).append(", step is null)"); } } } else { @@ -1356,10 +1356,10 @@ public class GameController implements GameCallback { // fix timeout sb.append("
Fixing future timeout: "); if (futureTimeout != null) { - sb.append("cancelled?=" + futureTimeout.isCancelled()); - sb.append("...done?=" + futureTimeout.isDone()); + sb.append("cancelled?=").append(futureTimeout.isCancelled()); + sb.append("...done?=").append(futureTimeout.isDone()); int delay = (int) futureTimeout.getDelay(TimeUnit.SECONDS); - sb.append("...getDelay?=" + delay); + sb.append("...getDelay?=").append(delay); if (delay < 25) { fixActions.add("future timeout fix"); @@ -1367,12 +1367,12 @@ public class GameController implements GameCallback { sb.append("
Try to pass..."); PassAbility pass = new PassAbility(); game.endTurn(pass); - sb.append(" (" + asWarning("OK") + ", pass done)"); + sb.append(" (").append(asWarning("OK")).append(", pass done)"); } else { - sb.append(" (" + asGood("OK") + ", delay > 25)"); + sb.append(" (").append(asGood("OK")).append(", delay > 25)"); } } else { - sb.append(" (" + asGood("OK") + ", timeout is not using)"); + sb.append(" (").append(asGood("OK")).append(", timeout is not using)"); } // TODO: fix non started game (send game started event to user?) @@ -1382,7 +1382,7 @@ public class GameController implements GameCallback { fixActions.add("none"); } String appliedFixes = fixActions.stream().collect(Collectors.joining(", ")); - sb.append("
Applied fixes: " + appliedFixes); + sb.append("
Applied fixes: ").append(appliedFixes); sb.append("
"); // font resize end sb.append("
"); diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoomManager.java b/Mage.Server/src/main/java/mage/server/game/GamesRoomManager.java index 73eaeba89b..c67c834fc2 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoomManager.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoomManager.java @@ -43,7 +43,7 @@ public enum GamesRoomManager { if (rooms.containsKey(roomId)) { return Optional.of(rooms.get(roomId)); } - logger.error("room not found : " + roomId); + logger.warn("room not found : " + roomId); // after server restart users try to use old rooms on reconnect return Optional.empty(); } diff --git a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java index 0bf9c0ac77..73aba819e8 100644 --- a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java +++ b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java @@ -13,6 +13,7 @@ import mage.choices.Choice; import mage.choices.ChoiceImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Planes; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; @@ -21,6 +22,7 @@ import mage.game.command.CommandObject; import mage.game.command.Plane; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; import mage.util.RandomUtil; import java.io.File; @@ -81,7 +83,7 @@ public final class SystemUtil { } private static final Pattern patternGroup = Pattern.compile("\\[(.+)\\]"); // [test new card] - private static final Pattern patternCommand = Pattern.compile("([\\w]+):([\\S]+?):([\\S ]+):([\\d]+)"); // battlefield:Human:Island:10 + private static final Pattern patternCommand = Pattern.compile("([\\w]+):([\\S ]+?):([\\S ]+):([\\d]+)"); // battlefield:Human:Island:10 private static final Pattern patternCardInfo = Pattern.compile("([\\S ]+):([\\S ]+)"); // Island:XLN // show ext info for special commands @@ -424,7 +426,7 @@ public final class SystemUtil { game.firePriorityEvent(opponent.getId()); } - List abilities = opponent.getPlayable(game, true); + List abilities = opponent.getPlayable(game, true); Map choices = new HashMap<>(); abilities.forEach(ability -> { MageObject object = ability.getSourceObject(game); @@ -436,10 +438,10 @@ public final class SystemUtil { choice.setKeyChoices(choices); if (feedbackPlayer.choose(Outcome.Detriment, choice, game) && choice.getChoiceKey() != null) { String needId = choice.getChoiceKey(); - Optional ability = abilities.stream().filter(a -> a.getId().toString().equals(needId)).findFirst(); + Optional ability = abilities.stream().filter(a -> a.getId().toString().equals(needId)).findFirst(); if (ability.isPresent()) { // TODO: set priority for player? - ActivatedAbility activatedAbility = (ActivatedAbility) ability.get(); + ActivatedAbility activatedAbility = ability.get(); game.informPlayers(feedbackPlayer.getLogName() + " as another player " + opponent.getLogName() + " trying to force an activate ability: " + activatedAbility.getGameLogMessage(game)); if (opponent.activateAbility(activatedAbility, game)) { @@ -538,12 +540,11 @@ public final class SystemUtil { break; } } - Class c = Class.forName("mage.game.command.planes." + command.cardName); - Constructor cons = c.getConstructor(); - Object plane = cons.newInstance(); - if (plane instanceof mage.game.command.Plane) { - ((mage.game.command.Plane) plane).setControllerId(player.getId()); - game.addPlane((mage.game.command.Plane) plane, null, player.getId()); + Planes planeType = Planes.fromClassName(command.cardName); + Plane plane = Plane.createPlane(planeType); + if (plane != null) { + plane.setControllerId(player.getId()); + game.addPlane(plane, null, player.getId()); continue; } } else if ("loyalty".equalsIgnoreCase(command.zone)) { @@ -572,7 +573,7 @@ public final class SystemUtil { // move card from exile to stack for (Card card : cardsToLoad) { - swapWithAnyCard(game, player, card, Zone.STACK); + putCardToZone(game, player, card, Zone.STACK); } continue; @@ -647,7 +648,7 @@ public final class SystemUtil { } else { // as other card for (Card card : cardsToLoad) { - swapWithAnyCard(game, player, card, gameZone); + putCardToZone(game, player, card, gameZone); } } } @@ -657,38 +658,33 @@ public final class SystemUtil { } /** - * Swap cards between specified card from library and any hand card. - * - * @param game - * @param card Card to put to player's hand + * Put card to specified (battlefield zone will be tranformed to permanent with initialized effects) */ - private static void swapWithAnyCard(Game game, Player player, Card card, Zone zone) { - // Put the card in Exile to start. Otherwise the game doesn't know where to remove the card from. - game.getExile().getPermanentExile().add(card); - game.setZone(card.getId(), Zone.EXILED); + private static void putCardToZone(Game game, Player player, Card card, Zone zone) { + // TODO: replace by player.move? switch (zone) { case BATTLEFIELD: - card.putOntoBattlefield(game, Zone.EXILED, null, player.getId()); + CardUtil.putCardOntoBattlefieldWithEffects(game, card, player); break; case LIBRARY: card.setZone(Zone.LIBRARY, game); - game.getExile().getPermanentExile().remove(card); player.getLibrary().putOnTop(card, game); break; case STACK: - card.cast(game, Zone.EXILED, card.getSpellAbility(), player.getId()); + card.cast(game, game.getState().getZone(card.getId()), card.getSpellAbility(), player.getId()); break; case EXILED: - // nothing to do + card.setZone(Zone.EXILED, game); + game.getExile().getPermanentExile().add(card); break; case OUTSIDE: card.setZone(Zone.OUTSIDE, game); - game.getExile().getPermanentExile().remove(card); break; default: card.moveToZone(zone, null, game, false); break; } + game.applyEffects(); logger.info("Added card to player's " + zone.toString() + ": " + card.getName() + ", player = " + player.getName()); } diff --git a/Mage.Server/src/main/resources/mage/deck/pennydreadful.properties b/Mage.Server/src/main/resources/mage/deck/pennydreadful.properties deleted file mode 100644 index 21b3685179..0000000000 --- a/Mage.Server/src/main/resources/mage/deck/pennydreadful.properties +++ /dev/null @@ -1,10371 +0,0 @@ -Abandon\ Reason=1 -Abandoned\ Outpost=1 -Abandoned\ Sarcophagus=1 -Abattoir\ Ghoul=1 -Abduction=1 -Aberrant\ Researcher=1 -Abhorrent\ Overlord=1 -Abnormal\ Endurance=1 -Abomination\ of\ Gudul=1 -Aboshan's\ Desire=1 -About\ Face=1 -Absolver\ Thrull=1 -Absorb\ Vis=1 -Abstruse\ Interference=1 -Abuna's\ Chant=1 -Abundant\ Maw=1 -Abyssal\ Horror=1 -Abyssal\ Nocturnus=1 -Abyssal\ Specter=1 -Abzan\ Ascendancy=1 -Abzan\ Banner=1 -Abzan\ Battle\ Priest=1 -Abzan\ Charm=1 -Abzan\ Falconer=1 -Abzan\ Guide=1 -Abzan\ Runemark=1 -Academy\ Drake=1 -Academy\ Elite=1 -Academy\ Journeymage=1 -Academy\ Raider=1 -Academy\ Researchers=1 -Accelerate=1 -Accomplished\ Automaton=1 -Accorder's\ Shield=1 -Accorder\ Paladin=1 -Accursed\ Horde=1 -Accursed\ Spirit=1 -Accursed\ Witch=1 -Acid-Spewer\ Dragon=1 -Acid\ Web\ Spider=1 -Acidic\ Sliver=1 -Acolyte's\ Reward=1 -Acolyte\ of\ Xathrid=1 -Acolyte\ of\ the\ Inferno=1 -Acquire=1 -Acrobatic\ Maneuver=1 -Act\ of\ Aggression=1 -Act\ of\ Heroism=1 -Act\ of\ Treason=1 -Act\ on\ Impulse=1 -Active\ Volcano=1 -Adamant\ Will=1 -Adamaro,\ First\ to\ Desire=1 -Adanto\ Vanguard=1 -Adaptive\ Snapjaw=1 -Adarkar\ Sentinel=1 -Adarkar\ Valkyrie=1 -Adarkar\ Windform=1 -Addle=1 -Advance\ Scout=1 -Advanced\ Hoverguard=1 -Advanced\ Stitchwing=1 -Advent\ of\ the\ Wurm=1 -Adventuring\ Gear=1 -Adverse\ Conditions=1 -Advice\ from\ the\ Fae=1 -Advocate\ of\ the\ Beast=1 -Aegis\ Angel=1 -Aegis\ Automaton=1 -Aeolipile=1 -Aeon\ Chronicler=1 -Aerial\ Caravan=1 -Aerial\ Engineer=1 -Aerial\ Formation=1 -Aerial\ Guide=1 -Aerial\ Maneuver=1 -Aerial\ Modification=1 -Aerial\ Predation=1 -Aerial\ Responder=1 -Aerial\ Volley=1 -Aerie\ Bowmasters=1 -Aerie\ Mystics=1 -Aerie\ Ouphes=1 -Aerie\ Worshippers=1 -Aeronaut\ Admiral=1 -Aeronaut\ Tinkerer=1 -Aesthir\ Glider=1 -Aether\ Adept=1 -Aether\ Chaser=1 -Aether\ Figment=1 -Aether\ Herder=1 -Aether\ Inspector=1 -Aether\ Membrane=1 -Aether\ Mutation=1 -Aether\ Poisoner=1 -Aether\ Shockwave=1 -Aether\ Snap=1 -Aether\ Spellbomb=1 -Aether\ Sting=1 -Aether\ Storm=1 -Aether\ Swooper=1 -Aether\ Theorist=1 -Aether\ Tide=1 -Aether\ Tradewinds=1 -Aetherborn\ Marauder=1 -Aethergeode\ Miner=1 -Aetherling=1 -Aethermage's\ Touch=1 -Aetherplasm=1 -Aethershield\ Artificer=1 -Aethersnipe=1 -Aethersquall\ Ancient=1 -Aetherstorm\ Roc=1 -Aetherstream\ Leopard=1 -Aethertide\ Whale=1 -Aethertorch\ Renegade=1 -Aethertow=1 -Aetherwind\ Basker=1 -Affa\ Protector=1 -Afflict=1 -Afflicted\ Deserter=1 -Afterlife=1 -Aftershock=1 -Agadeem\ Occultist=1 -Agent\ of\ Horizons=1 -Agent\ of\ Masks=1 -Agent\ of\ the\ Fates=1 -Aggressive\ Urge=1 -Agility=1 -Agonizing\ Demise=1 -Agonizing\ Memories=1 -Agony\ Warp=1 -Agoraphobia=1 -Agrus\ Kos,\ Wojek\ Veteran=1 -Ahn-Crop\ Champion=1 -Aid\ from\ the\ Cowl=1 -Aim\ High=1 -Ainok\ Artillerist=1 -Ainok\ Bond-Kin=1 -Ainok\ Tracker=1 -Air\ Bladder=1 -Air\ Elemental=1 -Air\ Servant=1 -Airborne\ Aid=1 -Airdrop\ Aeronauts=1 -Ajani's\ Mantra=1 -Ajani's\ Sunstriker=1 -Akki\ Avalanchers=1 -Akki\ Blizzard-Herder=1 -Akki\ Drillmaster=1 -Akki\ Lavarunner=1 -Akki\ Raider=1 -Akki\ Rockspeaker=1 -Akki\ Underminer=1 -Akoum\ Battlesinger=1 -Akoum\ Boulderfoot=1 -Akoum\ Flameseeker=1 -Akoum\ Hellkite=1 -Akoum\ Stonewaker=1 -Akrasan\ Squire=1 -Akroan\ Conscriptor=1 -Akroan\ Crusader=1 -Akroan\ Hoplite=1 -Akroan\ Horse=1 -Akroan\ Jailer=1 -Akroan\ Line\ Breaker=1 -Akroan\ Mastiff=1 -Akroan\ Phalanx=1 -Akroan\ Sergeant=1 -Akroma's\ Blessing=1 -Akroma's\ Devoted=1 -Akron\ Legionnaire=1 -Aku\ Djinn=1 -Alabaster\ Kirin=1 -Alabaster\ Wall=1 -Alaborn\ Musketeer=1 -Alaborn\ Trooper=1 -Aladdin's\ Ring=1 -Alarum=1 -Albino\ Troll=1 -Alchemist's\ Apprentice=1 -Alchemist's\ Greeting=1 -Aleatory=1 -Algae\ Gharial=1 -Alhammarret,\ High\ Arbiter=1 -Aligned\ Hedron\ Network=1 -Alive\ //\ Well=1 -All\ Suns'\ Dawn=1 -Alley\ Evasion=1 -Alley\ Strangler=1 -Allied\ Reinforcements=1 -Alloy\ Golem=1 -Alloy\ Myr=1 -Alluring\ Scent=1 -Alluring\ Siren=1 -Ally\ Encampment=1 -Alms=1 -Alms\ Beast=1 -Alms\ of\ the\ Vein=1 -Alpha\ Authority=1 -Alpha\ Brawl=1 -Alpha\ Kavu=1 -Alpha\ Myr=1 -Alpine\ Grizzly=1 -Altac\ Bloodseeker=1 -Altar's\ Reap=1 -Altar\ Golem=1 -Altar\ of\ Shadows=1 -Altar\ of\ the\ Brood=1 -Altar\ of\ the\ Lost=1 -Altered\ Ego=1 -Always\ Watching=1 -Amaranthine\ Wall=1 -Amass\ the\ Components=1 -Ambassador\ Laquatus=1 -Ambassador\ Oak=1 -Ambitious\ Aetherborn=1 -Ambuscade=1 -Ambuscade\ Shaman=1 -Ambush\ Krotiq=1 -Ambush\ Party=1 -Ambush\ Viper=1 -Amnesia=1 -Amoeboid\ Changeling=1 -Amok=1 -Amphibious\ Kavu=1 -Amphin\ Cutthroat=1 -Amphin\ Pathmage=1 -Ampryn\ Tactician=1 -Amrou\ Kithkin=1 -Amrou\ Scout=1 -Amrou\ Seekers=1 -Amugaba=1 -Amulet\ of\ Kroog=1 -Ana\ Battlemage=1 -Ana\ Disciple=1 -Ana\ Sanctuary=1 -Anaba\ Ancestor=1 -Anaba\ Shaman=1 -Anaba\ Spirit\ Crafter=1 -Anaconda=1 -Anarchist=1 -Anathemancer=1 -Anax\ and\ Cymede=1 -Ancestor's\ Chosen=1 -Ancestor's\ Prophet=1 -Ancestral\ Memories=1 -Ancestral\ Tribute=1 -Ancestral\ Vengeance=1 -Anchor\ to\ the\ Aether=1 -Ancient\ Amphitheater=1 -Ancient\ Animus=1 -Ancient\ Brontodon=1 -Ancient\ Carp=1 -Ancient\ Crab=1 -Ancient\ Hellkite=1 -Ancient\ Kavu=1 -Ancient\ Runes=1 -Ancient\ Silverback=1 -Andradite\ Leech=1 -Angel's\ Feather=1 -Angel's\ Herald=1 -Angel's\ Mercy=1 -Angel's\ Tomb=1 -Angel's\ Trumpet=1 -Angel\ of\ Condemnation=1 -Angel\ of\ Deliverance=1 -Angel\ of\ Despair=1 -Angel\ of\ Flight\ Alabaster=1 -Angel\ of\ Fury=1 -Angel\ of\ Light=1 -Angel\ of\ Mercy=1 -Angel\ of\ Renewal=1 -Angel\ of\ Retribution=1 -Angel\ of\ Salvation=1 -Angel\ of\ the\ Dawn=1 -Angel\ of\ the\ God-Pharaoh=1 -Angelfire\ Crusader=1 -Angelheart\ Vial=1 -Angelic\ Arbiter=1 -Angelic\ Armaments=1 -Angelic\ Benediction=1 -Angelic\ Blessing=1 -Angelic\ Captain=1 -Angelic\ Curator=1 -Angelic\ Edict=1 -Angelic\ Favor=1 -Angelic\ Gift=1 -Angelic\ Overseer=1 -Angelic\ Page=1 -Angelic\ Protector=1 -Angelic\ Purge=1 -Angelic\ Shield=1 -Angelic\ Skirmisher=1 -Angelic\ Voices=1 -Angelic\ Wall=1 -Angelsong=1 -Angler\ Drake=1 -Angrath's\ Marauders=1 -Angry\ Mob=1 -Animal\ Boneyard=1 -Animal\ Magnetism=1 -Animate\ Artifact=1 -Animate\ Land=1 -Animate\ Wall=1 -Animation\ Module=1 -Animist's\ Awakening=1 -Ankh\ of\ Mishra=1 -Ankle\ Shanker=1 -Annex=1 -Annihilate=1 -Annihilating\ Fire=1 -Anodet\ Lurker=1 -Anoint=1 -Anointed\ Deacon=1 -Anointer\ Priest=1 -Anointer\ of\ Champions=1 -Ant\ Queen=1 -Anthem\ of\ Rakdos=1 -Anticipate=1 -Antler\ Skulkin=1 -Anurid\ Barkripper=1 -Anurid\ Brushhopper=1 -Anurid\ Murkdiver=1 -Anurid\ Scavenger=1 -Anurid\ Swarmsnapper=1 -Anvilwrought\ Raptor=1 -Apes\ of\ Rath=1 -Aphetto\ Dredging=1 -Aphetto\ Exterminator=1 -Aphetto\ Vulture=1 -Aphotic\ Wisps=1 -Apocalypse\ Demon=1 -Apocalypse\ Hydra=1 -Apothecary\ Geist=1 -Apothecary\ Initiate=1 -Appeal\ //\ Authority=1 -Appetite\ for\ Brains=1 -Appetite\ for\ the\ Unnatural=1 -Apprentice\ Wizard=1 -Approach\ of\ the\ Second\ Sun=1 -Aquamoeba=1 -Aquastrand\ Spider=1 -Aquatic\ Incursion=1 -Aquitect's\ Will=1 -Araba\ Mothrider=1 -Arachnus\ Spinner=1 -Arachnus\ Web=1 -Aradara\ Express=1 -Arashi,\ the\ Sky\ Asunder=1 -Arashin\ Cleric=1 -Arashin\ Sovereign=1 -Arashin\ War\ Beast=1 -Arbalest\ Elite=1 -Arbiter\ of\ Knollridge=1 -Arbiter\ of\ the\ Ideal=1 -Arbor\ Armament=1 -Arbor\ Colossus=1 -Arborback\ Stomper=1 -Arc-Slogger=1 -Arc\ Blade=1 -Arc\ Lightning=1 -Arc\ Runner=1 -Arc\ Trail=1 -Arcane\ Adaptation=1 -Arcane\ Flight=1 -Arcane\ Sanctum=1 -Arcane\ Spyglass=1 -Arcane\ Teachings=1 -Arcanis\ the\ Omnipotent=1 -Arcbound\ Bruiser=1 -Arcbound\ Crusher=1 -Arcbound\ Fiend=1 -Arcbound\ Hybrid=1 -Arcbound\ Lancer=1 -Arcbound\ Overseer=1 -Arcbound\ Reclaimer=1 -Arcbound\ Stinger=1 -Arcbound\ Wanderer=1 -Archaeological\ Dig=1 -Archdemon\ of\ Unx=1 -Archers'\ Parapet=1 -Archers\ of\ Qarsi=1 -Archery\ Training=1 -Archetype\ of\ Aggression=1 -Archetype\ of\ Courage=1 -Archetype\ of\ Finality=1 -Archetype\ of\ Imagination=1 -Archfiend\ of\ Ifnir=1 -Architect\ of\ the\ Untamed=1 -Archivist=1 -Archmage\ Ascension=1 -Archon\ of\ Justice=1 -Archon\ of\ Redemption=1 -Archon\ of\ the\ Triumvirate=1 -Archweaver=1 -Arctic\ Aven=1 -Arctic\ Nishoba=1 -Arctic\ Wolves=1 -Ardent\ Militia=1 -Ardent\ Recruit=1 -Ardent\ Soldier=1 -Arena\ Athlete=1 -Argent\ Mutation=1 -Argent\ Sphinx=1 -Argivian\ Blacksmith=1 -Argivian\ Restoration=1 -Argothian\ Pixies=1 -Argothian\ Swine=1 -Argothian\ Treefolk=1 -Ark\ of\ Blight=1 -Armageddon\ Clock=1 -Armament\ Corps=1 -Armament\ Master=1 -Armament\ of\ Nyx=1 -Armed\ //\ Dangerous=1 -Armed\ Response=1 -Armillary\ Sphere=1 -Armor\ Sliver=1 -Armor\ Thrull=1 -Armor\ of\ Faith=1 -Armor\ of\ Thorns=1 -Armorcraft\ Judge=1 -Armored\ Ascension=1 -Armored\ Cancrix=1 -Armored\ Griffin=1 -Armored\ Pegasus=1 -Armored\ Skaab=1 -Armored\ Warhorse=1 -Armored\ Wolf-Rider=1 -Armorer\ Guildmage=1 -Armory\ of\ Iroas=1 -Arms\ Dealer=1 -Arrest=1 -Arrogant\ Bloodlord=1 -Arrow\ Storm=1 -Arrows\ of\ Justice=1 -Arsenal\ Thresher=1 -Arterial\ Flow=1 -Artifact\ Blast=1 -Artificer's\ Assistant=1 -Artificer's\ Epiphany=1 -Artificer's\ Hex=1 -Artificial\ Evolution=1 -Artillerize=1 -Artisan's\ Sorrow=1 -Artisan\ of\ Forms=1 -Artisan\ of\ Kozilek=1 -Arvad\ the\ Cursed=1 -Aryel,\ Knight\ of\ Windgrace=1 -Ascendant\ Evincar=1 -Ascended\ Lawmage=1 -Ash\ Zealot=1 -Asha's\ Favor=1 -Ashcoat\ Bear=1 -Ashen\ Firebeast=1 -Ashen\ Ghoul=1 -Ashen\ Monstrosity=1 -Ashenmoor\ Cohort=1 -Ashenmoor\ Gouger=1 -Ashes\ of\ the\ Fallen=1 -Ashiok's\ Adept=1 -Ashling's\ Prerogative=1 -Ashling,\ the\ Extinguisher=1 -Ashling\ the\ Pilgrim=1 -Ashnod's\ Cylix=1 -Ashnod's\ Transmogrant=1 -Aspect\ of\ Gorgon=1 -Aspect\ of\ Mongoose=1 -Asphodel\ Wanderer=1 -Asphyxiate=1 -Aspiring\ Aeronaut=1 -Assassin's\ Strike=1 -Assassinate=1 -Assault\ //\ Battery=1 -Assault\ Griffin=1 -Assault\ Zeppelid=1 -Assembled\ Alphas=1 -Assert\ Authority=1 -Astral\ Slide=1 -Astral\ Steel=1 -Astrolabe=1 -Asylum\ Visitor=1 -Atarka\ Efreet=1 -Atarka\ Monument=1 -Atarka\ Pummeler=1 -Atogatog=1 -Attended\ Knight=1 -Attune\ with\ Aether=1 -Atzocan\ Archer=1 -Atzocan\ Seer=1 -Audacious\ Infiltrator=1 -Auger\ Spree=1 -Augmenting\ Automaton=1 -Augur\ il-Vec=1 -Augury\ Owl=1 -Auntie's\ Snitch=1 -Aura\ Barbs=1 -Aura\ Extraction=1 -Aura\ Finesse=1 -Aura\ Graft=1 -Auramancer's\ Guise=1 -Auramancer=1 -Auratouched\ Mage=1 -Auriok\ Edgewright=1 -Auriok\ Glaivemaster=1 -Auriok\ Salvagers=1 -Auriok\ Steelshaper=1 -Auriok\ Sunchaser=1 -Auriok\ Survivors=1 -Auriok\ Transfixer=1 -Auriok\ Windwalker=1 -Aurora\ Eidolon=1 -Auspicious\ Ancestor=1 -Autochthon\ Wurm=1 -Autumn's\ Veil=1 -Autumn\ Willow=1 -Autumnal\ Gloom=1 -Avacyn's\ Collar=1 -Avacyn's\ Judgment=1 -Avacyn's\ Pilgrim=1 -Avacyn,\ Guardian\ Angel=1 -Avacynian\ Missionaries=1 -Avacynian\ Priest=1 -Avarax=1 -Avarice\ Amulet=1 -Avarice\ Totem=1 -Avatar\ of\ Might=1 -Aven\ Archer=1 -Aven\ Augur=1 -Aven\ Battle\ Priest=1 -Aven\ Brigadier=1 -Aven\ Cloudchaser=1 -Aven\ Envoy=1 -Aven\ Farseer=1 -Aven\ Fisher=1 -Aven\ Fleetwing=1 -Aven\ Flock=1 -Aven\ Initiate=1 -Aven\ Liberator=1 -Aven\ Mimeomancer=1 -Aven\ Mindcensor=1 -Aven\ Redeemer=1 -Aven\ Reedstalker=1 -Aven\ Riftwatcher=1 -Aven\ Sentry=1 -Aven\ Shrine=1 -Aven\ Smokeweaver=1 -Aven\ Squire=1 -Aven\ Sunstriker=1 -Aven\ Surveyor=1 -Aven\ Tactician=1 -Aven\ Trailblazer=1 -Aven\ Trooper=1 -Aven\ Warcraft=1 -Aven\ Warhawk=1 -Aven\ Wind\ Guide=1 -Aven\ Wind\ Mage=1 -Aven\ Windreader=1 -Aven\ of\ Enduring\ Hope=1 -Avenging\ Angel=1 -Avenging\ Arrow=1 -Avian\ Changeling=1 -Aviary\ Mechanic=1 -Aviation\ Pioneer=1 -Avizoa=1 -Avoid\ Fate=1 -Awaken\ the\ Ancient=1 -Awaken\ the\ Bear=1 -Awakened\ Amalgam=1 -Awakener\ Druid=1 -Awe\ Strike=1 -Awe\ for\ the\ Guilds=1 -Axebane\ Stag=1 -Axelrod\ Gunnarson=1 -Ayli,\ Eternal\ Pilgrim=1 -Aysen\ Bureaucrats=1 -Aysen\ Crusader=1 -Ayumi,\ the\ Last\ Visitor=1 -Azimaet\ Drake=1 -Azor's\ Elocutors=1 -Azorius\ Aethermage=1 -Azorius\ Arrester=1 -Azorius\ Charm=1 -Azorius\ Cluestone=1 -Azorius\ First-Wing=1 -Azorius\ Guildgate=1 -Azorius\ Guildmage=1 -Azorius\ Herald=1 -Azorius\ Justiciar=1 -Azorius\ Keyrune=1 -Azure\ Drake=1 -Azure\ Mage=1 -Back\ from\ the\ Brink=1 -Backwoods\ Survivalists=1 -Bad\ River=1 -Baffling\ End=1 -Baird,\ Steward\ of\ Argive=1 -Baku\ Altar=1 -Bala\ Ged\ Scorpion=1 -Bala\ Ged\ Thief=1 -Balance\ of\ Power=1 -Balduvian\ Barbarians=1 -Balduvian\ Conjurer=1 -Balduvian\ Dead=1 -Balduvian\ Fallen=1 -Balduvian\ Rage=1 -Balduvian\ Warlord=1 -Baleful\ Ammit=1 -Baleful\ Eidolon=1 -Baleful\ Force=1 -Baleful\ Stare=1 -Ballista\ Charger=1 -Balloon\ Peddler=1 -Ballynock\ Cohort=1 -Ballynock\ Trapper=1 -Baloth\ Cage\ Trap=1 -Baloth\ Gorger=1 -Baloth\ Null=1 -Baloth\ Pup=1 -Baloth\ Woodcrasher=1 -Balshan\ Beguiler=1 -Balshan\ Collaborator=1 -Balshan\ Griffin=1 -Balustrade\ Spy=1 -Bamboozle=1 -Bandage=1 -Bane\ Alley\ Broker=1 -Bane\ of\ Bala\ Ged=1 -Baneful\ Omen=1 -Banewhip\ Punisher=1 -Banisher\ Priest=1 -Banishing\ Stroke=1 -Banishment\ Decree=1 -Banners\ Raised=1 -Banshee's\ Blade=1 -Banshee=1 -Bant\ Sojourners=1 -Bar\ the\ Door=1 -Barbarian\ Bully=1 -Barbarian\ Riftcutter=1 -Barbed-Back\ Wurm=1 -Barbed\ Battlegear=1 -Barbed\ Lightning=1 -Barbed\ Sextant=1 -Barbed\ Shocker=1 -Barbed\ Sliver=1 -Barbed\ Wire=1 -Barishi=1 -Barkhide\ Mauler=1 -Barkshell\ Blessing=1 -Barktooth\ Warbeard=1 -Barl's\ Cage=1 -Barrage\ Ogre=1 -Barrage\ Tyrant=1 -Barrage\ of\ Boulders=1 -Barrel\ Down\ Sokenzan=1 -Barren\ Glory=1 -Barrenton\ Cragtreads=1 -Barrenton\ Medic=1 -Barricade\ Breaker=1 -Barrin's\ Codex=1 -Barrin's\ Unmaking=1 -Basal\ Sliver=1 -Basal\ Thrull=1 -Basalt\ Gargoyle=1 -Basandra,\ Battle\ Seraph=1 -Bash\ to\ Bits=1 -Basilica\ Guards=1 -Basilica\ Screecher=1 -Bassara\ Tower\ Archer=1 -Bastion\ Enforcer=1 -Bastion\ Inventor=1 -Bastion\ Mastodon=1 -Bathe\ in\ Light=1 -Batterhorn=1 -Battering\ Craghorn=1 -Battering\ Krasis=1 -Battering\ Sliver=1 -Battering\ Wurm=1 -Battle-Mad\ Ronin=1 -Battle-Rattle\ Shaman=1 -Battle\ Hurda=1 -Battle\ Mastery=1 -Battle\ Screech=1 -Battle\ Sliver=1 -Battle\ Squadron=1 -Battle\ of\ Wits=1 -Battlefield\ Medic=1 -Battlefield\ Percher=1 -Battlefield\ Scavenger=1 -Battlefield\ Thaumaturge=1 -Battlefront\ Krushok=1 -Battlegate\ Mimic=1 -Battlegrace\ Angel=1 -Battleground\ Geist=1 -Battlegrowth=1 -Battletide\ Alchemist=1 -Battlewand\ Oak=1 -Battlewise\ Hoplite=1 -Battlewise\ Valor=1 -Batwing\ Brume=1 -Bay\ Falcon=1 -Bayou\ Dragonfly=1 -Beacon\ Hawk=1 -Beacon\ of\ Destiny=1 -Beacon\ of\ Destruction=1 -Beacon\ of\ Immortality=1 -Beacon\ of\ Unrest=1 -Bear's\ Companion=1 -Bearer\ of\ Silence=1 -Bearer\ of\ the\ Heavens=1 -Bearscape=1 -Beast\ Attack=1 -Beast\ Hunt=1 -Beast\ of\ Burden=1 -Beastbreaker\ of\ Bala\ Ged=1 -Beastcaller\ Savant=1 -Beckon\ Apparition=1 -Bedlam=1 -Bee\ Sting=1 -Beetleback\ Chief=1 -Beetleform\ Mage=1 -Befuddle=1 -Behemoth's\ Herald=1 -Behemoth\ Sledge=1 -Behind\ the\ Scenes=1 -Belbe's\ Percher=1 -Belfry\ Spirit=1 -Belligerent\ Brontodon=1 -Belligerent\ Hatchling=1 -Belligerent\ Sliver=1 -Belligerent\ Whiptail=1 -Bellowing\ Aegisaur=1 -Bellowing\ Fiend=1 -Bellowing\ Saddlebrute=1 -Bellowing\ Tanglewurm=1 -Bellows\ Lizard=1 -Belltoll\ Dragon=1 -Belltower\ Sphinx=1 -Beloved\ Chaplain=1 -Benalish\ Cavalry=1 -Benalish\ Commander=1 -Benalish\ Emissary=1 -Benalish\ Heralds=1 -Benalish\ Hero=1 -Benalish\ Honor\ Guard=1 -Benalish\ Knight=1 -Benalish\ Lancer=1 -Benalish\ Missionary=1 -Benalish\ Trapper=1 -Benalish\ Veteran=1 -Beneath\ the\ Sands=1 -Benediction\ of\ Moons=1 -Benefaction\ of\ Rhonas=1 -Benevolent\ Ancestor=1 -Benevolent\ Bodyguard=1 -Benthic\ Behemoth=1 -Benthic\ Djinn=1 -Benthic\ Explorers=1 -Benthic\ Giant=1 -Benthic\ Infiltrator=1 -Bereavement=1 -Berserk\ Murlodont=1 -Berserkers\ of\ Blood\ Ridge=1 -Bestial\ Menace=1 -Bewilder=1 -Bident\ of\ Thassa=1 -Bile\ Blight=1 -Bile\ Urchin=1 -Binding\ Agony=1 -Binding\ Mummy=1 -Biomantic\ Mastery=1 -Biomass\ Mutation=1 -Bioplasm=1 -Bioshift=1 -Biovisionary=1 -Birthing\ Hulk=1 -Bishop's\ Soldier=1 -Bishop\ of\ Rebirth=1 -Bishop\ of\ the\ Bloodstained=1 -Biting\ Rain=1 -Biting\ Tether=1 -Bitter\ Revelation=1 -Bitterblade\ Warrior=1 -Bitterbow\ Sharpshooters=1 -Bitterheart\ Witch=1 -Bituminous\ Blast=1 -Black\ Knight=1 -Black\ Oak\ of\ Odunos=1 -Black\ Poplar\ Shaman=1 -Blackblade\ Reforged=1 -Blackcleave\ Goblin=1 -Blade-Tribe\ Berserkers=1 -Bladed\ Bracers=1 -Bladed\ Pinions=1 -Bladed\ Sentinel=1 -Blades\ of\ Velis\ Vel=1 -Bladetusk\ Boar=1 -Bladewing's\ Thrall=1 -Blanchwood\ Armor=1 -Blanchwood\ Treefolk=1 -Blast\ of\ Genius=1 -Blastfire\ Bolt=1 -Blastoderm=1 -Blaze=1 -Blaze\ Commando=1 -Blaze\ of\ Glory=1 -Blazethorn\ Scarecrow=1 -Blazing\ Blade\ Askari=1 -Blazing\ Hellhound=1 -Blazing\ Hope=1 -Blazing\ Specter=1 -Blazing\ Torch=1 -Bleak\ Coven\ Vampires=1 -Blessed\ Breath=1 -Blessed\ Light=1 -Blessed\ Orator=1 -Blessed\ Reincarnation=1 -Blessed\ Reversal=1 -Blessed\ Spirits=1 -Blessing=1 -Blessing\ of\ Belzenlok=1 -Blessing\ of\ Leeches=1 -Blessing\ of\ the\ Nephilim=1 -Blight\ Herder=1 -Blight\ Keeper=1 -Blight\ Sickle=1 -Blightcaster=1 -Blighted\ Bat=1 -Blighted\ Cataract=1 -Blighted\ Fen=1 -Blighted\ Gorge=1 -Blighted\ Shaman=1 -Blighted\ Steppe=1 -Blighted\ Woodland=1 -Blightspeaker=1 -Blightwidow=1 -Blind\ Creeper=1 -Blind\ Fury=1 -Blind\ Hunter=1 -Blind\ Zealot=1 -Blind\ with\ Anger=1 -Blinding\ Angel=1 -Blinding\ Beam=1 -Blinding\ Drone=1 -Blinding\ Flare=1 -Blinding\ Fog=1 -Blinding\ Light=1 -Blinding\ Mage=1 -Blinding\ Powder=1 -Blinding\ Souleater=1 -Blinding\ Spray=1 -Blinking\ Spirit=1 -Blinkmoth\ Infusion=1 -Blinkmoth\ Urn=1 -Blinkmoth\ Well=1 -Blister\ Beetle=1 -Blistercoil\ Weird=1 -Blistergrub=1 -Blistering\ Barrier=1 -Blistering\ Dieflyn=1 -Blisterpod=1 -Blitz\ Hellion=1 -Blizzard\ Elemental=1 -Blizzard\ Specter=1 -Bloated\ Toad=1 -Blockade\ Runner=1 -Blockbuster=1 -Blood-Chin\ Fanatic=1 -Blood-Cursed\ Knight=1 -Blood\ Bairn=1 -Blood\ Celebrant=1 -Blood\ Clock=1 -Blood\ Cultist=1 -Blood\ Frenzy=1 -Blood\ Funnel=1 -Blood\ Host=1 -Blood\ Knight=1 -Blood\ Lust=1 -Blood\ Mist=1 -Blood\ Ogre=1 -Blood\ Reckoning=1 -Blood\ Rites=1 -Blood\ Seeker=1 -Blood\ Speaker=1 -Blood\ Tithe=1 -Blood\ Tribute=1 -Blood\ Vassal=1 -Bloodbond\ March=1 -Bloodbond\ Vampire=1 -Bloodbriar=1 -Bloodcrazed\ Goblin=1 -Bloodcrazed\ Hoplite=1 -Bloodcrazed\ Neonate=1 -Bloodcurdler=1 -Bloodfire\ Colossus=1 -Bloodfire\ Dwarf=1 -Bloodfire\ Enforcers=1 -Bloodfire\ Expert=1 -Bloodfire\ Infusion=1 -Bloodfire\ Kavu=1 -Bloodfire\ Mentor=1 -Bloodfray\ Giant=1 -Bloodgift\ Demon=1 -Bloodhall\ Ooze=1 -Bloodhall\ Priest=1 -Bloodhunter\ Bat=1 -Bloodhusk\ Ritualist=1 -Bloodied\ Ghost=1 -Bloodletter\ Quill=1 -Bloodline\ Keeper=1 -Bloodline\ Shaman=1 -Bloodlust\ Inciter=1 -Bloodmad\ Vampire=1 -Bloodmark\ Mentor=1 -Bloodrage\ Brawler=1 -Bloodrage\ Vampire=1 -Bloodrite\ Invoker=1 -Bloodrock\ Cyclops=1 -Bloodscale\ Prowler=1 -Bloodscent=1 -Bloodshed\ Fever=1 -Bloodshot\ Trainee=1 -Bloodstoke\ Howler=1 -Bloodstone\ Cameo=1 -Bloodstone\ Goblin=1 -Bloodtallow\ Candle=1 -Bloodthirsty\ Ogre=1 -Bloodthorn\ Taunter=1 -Bloodthrone\ Vampire=1 -Bloodwater\ Entity=1 -Blossom\ Dryad=1 -Blossoming\ Wreath=1 -Blowfly\ Infestation=1 -Bludgeon\ Brawl=1 -Blunt\ the\ Assault=1 -Blur\ of\ Blades=1 -Blustersquall=1 -Boa\ Constrictor=1 -Boar\ Umbra=1 -Board\ the\ Weatherlight=1 -Body\ of\ Jukai=1 -Bog-Strider\ Ash=1 -Bog\ Down=1 -Bog\ Elemental=1 -Bog\ Gnarr=1 -Bog\ Hoodlums=1 -Bog\ Imp=1 -Bog\ Initiate=1 -Bog\ Raiders=1 -Bog\ Serpent=1 -Bog\ Tatters=1 -Bog\ Wraith=1 -Bog\ Wreckage=1 -Bogardan\ Firefiend=1 -Bogardan\ Hellkite=1 -Bogardan\ Lancer=1 -Bogardan\ Phoenix=1 -Bogbrew\ Witch=1 -Boggart\ Arsonists=1 -Boggart\ Birth\ Rite=1 -Boggart\ Brute=1 -Boggart\ Mob=1 -Boggart\ Shenanigans=1 -Bogstomper=1 -Boiling\ Blood=1 -Boiling\ Earth=1 -Boiling\ Seas=1 -Bojuka\ Brigand=1 -Bola\ Warrior=1 -Bold\ Defense=1 -Bold\ Impaler=1 -Boldwyr\ Heavyweights=1 -Boldwyr\ Intimidator=1 -Bolt\ of\ Keranos=1 -Boltwing\ Marauder=1 -Bomat\ Bazaar\ Barge=1 -Bombard=1 -Bomber\ Corps=1 -Bond\ Beetle=1 -Bond\ of\ Agony=1 -Bonded\ Construct=1 -Bonded\ Fetch=1 -Bonded\ Horncrest=1 -Bonds\ of\ Faith=1 -Bonds\ of\ Mortality=1 -Bone\ Flute=1 -Bone\ Picker=1 -Bone\ Saw=1 -Bone\ Splinters=1 -Bone\ to\ Ash=1 -Bonehoard=1 -Boneknitter=1 -Boneshard\ Slasher=1 -Bonesplitter\ Sliver=1 -Bonethorn\ Valesk=1 -Boneyard\ Wurm=1 -Bontu's\ Monument=1 -Booby\ Trap=1 -Book\ Burning=1 -Book\ of\ Rass=1 -Boon\ Satyr=1 -Boon\ of\ Emrakul=1 -Boon\ of\ Erebos=1 -Boonweaver\ Giant=1 -Borborygmos=1 -Borderland\ Behemoth=1 -Borderland\ Marauder=1 -Borderland\ Minotaur=1 -Borderland\ Ranger=1 -Boreal\ Centaur=1 -Boreal\ Griffin=1 -Boros\ Battleshaper=1 -Boros\ Cluestone=1 -Boros\ Elite=1 -Boros\ Guildgate=1 -Boros\ Guildmage=1 -Boros\ Keyrune=1 -Boros\ Mastiff=1 -Boros\ Recruit=1 -Boros\ Signet=1 -Boros\ Swiftblade=1 -Borrowed\ Grace=1 -Borrowed\ Hostility=1 -Borrowed\ Malevolence=1 -Bosh,\ Iron\ Golem=1 -Bosk\ Banneret=1 -Bottle\ Gnomes=1 -Bottled\ Cloister=1 -Boulderfall=1 -Bound\ //\ Determined=1 -Bound\ by\ Moonsilver=1 -Bound\ in\ Silence=1 -Bounding\ Krasis=1 -Bounteous\ Kirin=1 -Bow\ of\ Nylea=1 -Bower\ Passage=1 -Brace\ for\ Impact=1 -Braids,\ Cabal\ Minion=1 -Braidwood\ Cup=1 -Brain\ Freeze=1 -Brain\ Gorgers=1 -Brain\ Pry=1 -Brain\ Weevil=1 -Brain\ in\ a\ Jar=1 -Brainbite=1 -Brainspoil=1 -Bramble\ Creeper=1 -Bramble\ Elemental=1 -Bramblesnap=1 -Branching\ Bolt=1 -Branded\ Brawlers=1 -Brass's\ Bounty=1 -Brass-Talon\ Chimera=1 -Brass\ Gnat=1 -Brass\ Herald=1 -Brass\ Man=1 -Brass\ Secretary=1 -Brass\ Squire=1 -Brave\ the\ Sands=1 -Brawl-Bash\ Ogre=1 -Brawler's\ Plate=1 -Brawn=1 -Brazen\ Buccaneers=1 -Brazen\ Freebooter=1 -Brazen\ Scourge=1 -Brazen\ Wolves=1 -Breaching\ Hippocamp=1 -Break\ Asunder=1 -Break\ Open=1 -Break\ of\ Day=1 -Breaker\ of\ Armies=1 -Breaking\ Point=1 -Breaking\ Wave=1 -Breakneck\ Rider=1 -Breath\ of\ Fury=1 -Breath\ of\ Life=1 -Breath\ of\ Malfegor=1 -Breathstealer=1 -Bred\ for\ the\ Hunt=1 -Breeding\ Pit=1 -Briar\ Patch=1 -Briarberry\ Cohort=1 -Briarbridge\ Patrol=1 -Briarhorn=1 -Briarknit\ Kami=1 -Briarpack\ Alpha=1 -Briber's\ Purse=1 -Bright\ Reprisal=1 -Brightflame=1 -Brighthearth\ Banneret=1 -Brigid,\ Hero\ of\ Kinsbaile=1 -Brilliant\ Halo=1 -Brilliant\ Spectrum=1 -Brimstone\ Mage=1 -Brindle\ Boar=1 -Brine\ Elemental=1 -Brine\ Seer=1 -Brine\ Shaman=1 -Bring\ to\ Light=1 -Bringer\ of\ the\ Blue\ Dawn=1 -Bringer\ of\ the\ Green\ Dawn=1 -Bringer\ of\ the\ Red\ Dawn=1 -Brink\ of\ Disaster=1 -Brink\ of\ Madness=1 -Brion\ Stoutarm=1 -Bristling\ Boar=1 -Brittle\ Effigy=1 -Broken\ Ambitions=1 -Broken\ Bond=1 -Broken\ Concentration=1 -Broken\ Visage=1 -Bronze\ Bombshell=1 -Bronze\ Horse=1 -Bronze\ Sable=1 -Bronzebeak\ Moa=1 -Brood\ Birthing=1 -Brood\ Butcher=1 -Brood\ Keeper=1 -Brood\ Monitor=1 -Brood\ of\ Cockroaches=1 -Broodbirth\ Viper=1 -Broodhatch\ Nantuko=1 -Broodhunter\ Wurm=1 -Broodmate\ Dragon=1 -Broodstar=1 -Broodwarden=1 -Brothers\ Yamazaki=1 -Brothers\ of\ Fire=1 -Brown\ Ouphe=1 -Browse=1 -Brush\ with\ Death=1 -Brutal\ Expulsion=1 -Brutalizer\ Exarch=1 -Brute\ Force=1 -Brute\ Strength=1 -Bubbling\ Cauldron=1 -Buccaneer's\ Bravado=1 -Builder's\ Blessing=1 -Built\ to\ Last=1 -Built\ to\ Smash=1 -Bull\ Aurochs=1 -Bull\ Hippo=1 -Bull\ Rush=1 -Bullwhip=1 -Buoyancy=1 -Burden\ of\ Greed=1 -Burn\ Away=1 -Burn\ from\ Within=1 -Burn\ the\ Impure=1 -Burning-Eye\ Zubera=1 -Burning-Fist\ Minotaur=1 -Burning-Tree\ Bloodscale=1 -Burning\ Anger=1 -Burning\ Earth=1 -Burning\ Oil=1 -Burning\ Palm\ Efreet=1 -Burning\ Sun's\ Avatar=1 -Burning\ Vengeance=1 -Burning\ of\ Xinye=1 -Burr\ Grafter=1 -Burrenton\ Bombardier=1 -Burrenton\ Shield-Bearers=1 -Burst\ Lightning=1 -Burst\ of\ Energy=1 -Burst\ of\ Speed=1 -Burst\ of\ Strength=1 -Bushi\ Tenderfoot=1 -Butcher's\ Cleaver=1 -Butcher's\ Glee=1 -Butcher\ Orgg=1 -Butcher\ of\ the\ Horde=1 -By\ Force=1 -Bygone\ Bishop=1 -Byway\ Courier=1 -Cabal\ Archon=1 -Cabal\ Conditioning=1 -Cabal\ Evangel=1 -Cabal\ Executioner=1 -Cabal\ Inquisitor=1 -Cabal\ Paladin=1 -Cabal\ Patriarch=1 -Cabal\ Shrine=1 -Cabal\ Surgeon=1 -Cabal\ Torturer=1 -Cache\ Raiders=1 -Cackling\ Counterpart=1 -Cackling\ Flames=1 -Cackling\ Imp=1 -Cackling\ Witch=1 -Cacophodon=1 -Cadaver\ Imp=1 -Cadaverous\ Knight=1 -Cage\ of\ Hands=1 -Cagemail=1 -Cairn\ Wanderer=1 -Calciderm=1 -Calciform\ Pools=1 -Calcite\ Snapper=1 -Calculated\ Dismissal=1 -Caldera\ Hellion=1 -Caldera\ Kavu=1 -Caldera\ Lake=1 -Caligo\ Skin-Witch=1 -Call\ for\ Blood=1 -Call\ for\ Unity=1 -Call\ of\ the\ Conclave=1 -Call\ of\ the\ Full\ Moon=1 -Call\ of\ the\ Herd=1 -Call\ of\ the\ Nightwing=1 -Call\ of\ the\ Wild=1 -Call\ the\ Bloodline=1 -Call\ the\ Cavalry=1 -Call\ the\ Gatewatch=1 -Call\ the\ Scions=1 -Call\ the\ Skybreaker=1 -Call\ to\ Arms=1 -Call\ to\ Glory=1 -Call\ to\ Heel=1 -Call\ to\ Mind=1 -Call\ to\ Serve=1 -Call\ to\ the\ Feast=1 -Call\ to\ the\ Grave=1 -Call\ to\ the\ Kindred=1 -Caller\ of\ Gales=1 -Caller\ of\ the\ Pack=1 -Callous\ Oppressor=1 -Callow\ Jushi=1 -Caltrops=1 -Campaign\ of\ Vengeance=1 -Canal\ Monitor=1 -Cancel=1 -Candles\ of\ Leng=1 -Canker\ Abomination=1 -Cankerous\ Thirst=1 -Cannibalize=1 -Canopy\ Claws=1 -Canopy\ Cover=1 -Canopy\ Crawler=1 -Canopy\ Gorger=1 -Canopy\ Spider=1 -Canyon\ Drake=1 -Canyon\ Lurkers=1 -Canyon\ Minotaur=1 -Canyon\ Wildcat=1 -Capashen\ Knight=1 -Capashen\ Templar=1 -Capashen\ Unicorn=1 -Capricious\ Efreet=1 -Captain's\ Call=1 -Captain's\ Claws=1 -Captain's\ Hook=1 -Captain's\ Maneuver=1 -Captain\ of\ the\ Mists=1 -Captivating\ Crew=1 -Captive\ Flame=1 -Captured\ Sunlight=1 -Caravan\ Escort=1 -Caravan\ Hurda=1 -Carbonize=1 -Careful\ Consideration=1 -Caregiver=1 -Caress\ of\ Phyrexia=1 -Carnage\ Altar=1 -Carnage\ Gladiator=1 -Carnassid=1 -Carnifex\ Demon=1 -Carnival\ Hellsteed=1 -Carnivorous\ Moss-Beast=1 -Carnivorous\ Plant=1 -Carom=1 -Carrier\ Thrall=1 -Carrion\ Beetles=1 -Carrion\ Call=1 -Carrion\ Rats=1 -Carrion\ Screecher=1 -Carrion\ Thrash=1 -Carrion\ Wall=1 -Carrion\ Wurm=1 -Cartel\ Aristocrat=1 -Cartographer=1 -Cartouche\ of\ Ambition=1 -Cartouche\ of\ Knowledge=1 -Cartouche\ of\ Strength=1 -Carven\ Caryatid=1 -Cascading\ Cataracts=1 -Cast\ into\ Darkness=1 -Castle=1 -Castle\ Raptors=1 -Cat\ Burglar=1 -Catacomb\ Sifter=1 -Catacomb\ Slug=1 -Catalog=1 -Catalyst\ Elemental=1 -Catch\ //\ Release=1 -Cateran\ Kidnappers=1 -Cateran\ Summons=1 -Caterwauling\ Boggart=1 -Cathar's\ Companion=1 -Cathar's\ Shield=1 -Cathartic\ Adept=1 -Cathedral\ Membrane=1 -Cathedral\ of\ War=1 -Cathodion=1 -Caught\ in\ the\ Brights=1 -Caustic\ Caterpillar=1 -Caustic\ Crawler=1 -Caustic\ Hound=1 -Caustic\ Rain=1 -Caustic\ Tar=1 -Cautery\ Sliver=1 -Cavalry\ Master=1 -Cavalry\ Pegasus=1 -Cave\ Sense=1 -Cave\ Tiger=1 -Cavern\ Crawler=1 -Cavern\ Lampad=1 -Cavern\ Thoctar=1 -Cease-Fire=1 -Ceaseless\ Searblades=1 -Celestial\ Ancient=1 -Celestial\ Archon=1 -Celestial\ Crusader=1 -Celestial\ Dawn=1 -Celestial\ Flare=1 -Celestial\ Kirin=1 -Celestial\ Mantle=1 -Celestial\ Sword=1 -Cellar\ Door=1 -Cemetery\ Puca=1 -Cemetery\ Recruitment=1 -Cenn's\ Enlistment=1 -Cenn's\ Heir=1 -Cenn's\ Tactician=1 -Centaur's\ Herald=1 -Centaur\ Archer=1 -Centaur\ Battlemaster=1 -Centaur\ Chieftain=1 -Centaur\ Courser=1 -Centaur\ Glade=1 -Centaur\ Healer=1 -Centaur\ Omenreader=1 -Centaur\ Rootcaster=1 -Center\ Soul=1 -Cephalid\ Aristocrat=1 -Cephalid\ Broker=1 -Cephalid\ Illusionist=1 -Cephalid\ Inkshrouder=1 -Cephalid\ Looter=1 -Cephalid\ Retainer=1 -Cephalid\ Sage=1 -Cephalid\ Scout=1 -Cephalid\ Shrine=1 -Cerebral\ Eruption=1 -Cerebral\ Vortex=1 -Ceremonial\ Guard=1 -Cerodon\ Yearling=1 -Certain\ Death=1 -Cerulean\ Sphinx=1 -Cerulean\ Wyvern=1 -Cessation=1 -Ceta\ Disciple=1 -Ceta\ Sanctuary=1 -Chain\ of\ Plasma=1 -Chain\ of\ Silence=1 -Chainbreaker=1 -Chained\ Throatseeker=1 -Chained\ to\ the\ Rocks=1 -Chainer's\ Torment=1 -Chainflinger=1 -Chamber\ of\ Manipulation=1 -Chambered\ Nautilus=1 -Chameleon\ Blur=1 -Champion's\ Drake=1 -Champion\ Lancer=1 -Champion\ of\ Arashin=1 -Champion\ of\ Dusk=1 -Champion\ of\ Rhonas=1 -Champion\ of\ the\ Flame=1 -Chancellor\ of\ the\ Dross=1 -Chancellor\ of\ the\ Forge=1 -Chandra's\ Fury=1 -Chandra's\ Ignition=1 -Chandra's\ Outrage=1 -Chandra's\ Phoenix=1 -Chandra's\ Pyrohelix=1 -Chandra's\ Revolution=1 -Chandra's\ Spitfire=1 -Chandra\ Nalaar=1 -Change\ of\ Heart=1 -Changeling\ Berserker=1 -Changeling\ Hero=1 -Changeling\ Sentinel=1 -Changeling\ Titan=1 -Channel\ Harm=1 -Channel\ the\ Suns=1 -Channeler\ Initiate=1 -Chant\ of\ Vitu-Ghazi=1 -Chaos\ Charm=1 -Chaos\ Imps=1 -Chaos\ Maw=1 -Chaotic\ Backlash=1 -Chaplain's\ Blessing=1 -Char=1 -Charge=1 -Charge\ Across\ the\ Araba=1 -Charging\ Badger=1 -Charging\ Griffin=1 -Charging\ Monstrosaur=1 -Charging\ Paladin=1 -Charging\ Rhino=1 -Charging\ Slateback=1 -Charging\ Tuskodon=1 -Charmbreaker\ Devils=1 -Charmed\ Griffin=1 -Chartooth\ Cougar=1 -Chasm\ Drake=1 -Chasm\ Guide=1 -Chastise=1 -Chemister's\ Trick=1 -Cherished\ Hatchling=1 -Chief\ of\ the\ Edge=1 -Chief\ of\ the\ Foundry=1 -Chief\ of\ the\ Scale=1 -Chieftain\ en-Dal=1 -Child\ of\ Night=1 -Childhood\ Horror=1 -Children\ of\ Korlis=1 -Chill\ Haunting=1 -Chill\ of\ Foreboding=1 -Chill\ to\ the\ Bone=1 -Chilling\ Grasp=1 -Chilling\ Shade=1 -Chime\ of\ Night=1 -Chimeric\ Coils=1 -Chimeric\ Egg=1 -Chimeric\ Idol=1 -Chimeric\ Mass=1 -Chimeric\ Staff=1 -Chisei,\ Heart\ of\ Oceans=1 -Chitinous\ Cloak=1 -Chlorophant=1 -Cho-Arrim\ Legate=1 -Cho-Manno,\ Revolutionary=1 -Choice\ of\ Damnations=1 -Choking\ Fumes=1 -Choking\ Restraints=1 -Choking\ Tethers=1 -Chorus\ of\ Might=1 -Chorus\ of\ the\ Conclave=1 -Chorus\ of\ the\ Tides=1 -Chosen\ by\ Heliod=1 -Chosen\ of\ Markov=1 -Chrome\ Steed=1 -Chromescale\ Drake=1 -Chromium=1 -Chronatog\ Totem=1 -Chronic\ Flooding=1 -Chronicler\ of\ Heroes=1 -Chronomantic\ Escape=1 -Chronomaton=1 -Chronosavant=1 -Chronostutter=1 -Chronozoa=1 -Chub\ Toad=1 -Churning\ Eddy=1 -Cinder\ Barrens=1 -Cinder\ Cloud=1 -Cinder\ Crawler=1 -Cinder\ Elemental=1 -Cinder\ Pyromancer=1 -Cinder\ Seer=1 -Cinder\ Shade=1 -Cinderhaze\ Wretch=1 -Circle\ of\ Affliction=1 -Circle\ of\ Elders=1 -Circle\ of\ Flame=1 -Circle\ of\ Protection:\ Artifacts=1 -Circle\ of\ Protection:\ Shadow=1 -Circle\ of\ Protection:\ White=1 -Circle\ of\ Solace=1 -Circling\ Vultures=1 -Circu,\ Dimir\ Lobotomist=1 -Citadel\ Castellan=1 -Citanul\ Druid=1 -Citanul\ Flute=1 -Citanul\ Woodreaders=1 -Civic\ Guildmage=1 -Civic\ Saber=1 -Civic\ Wayfinder=1 -Civilized\ Scholar=1 -Claim\ //\ Fame=1 -Claim\ of\ Erebos=1 -Clan\ Defiance=1 -Clarion\ Ultimatum=1 -Clash\ of\ Realities=1 -Clash\ of\ Wills=1 -Claustrophobia=1 -Claws\ of\ Valakut=1 -Clay\ Statue=1 -Cleanse=1 -Cleansing\ Beam=1 -Cleansing\ Meditation=1 -Cleansing\ Ray=1 -Clear=1 -Clear\ Shot=1 -Clear\ a\ Path=1 -Clergy\ en-Vec=1 -Cleric\ of\ the\ Forward\ Order=1 -Clickslither=1 -Cliff\ Threader=1 -Cliffhaven\ Vampire=1 -Cliffrunner\ Behemoth=1 -Cliffside\ Lookout=1 -Clinging\ Anemones=1 -Clinging\ Mists=1 -Clip\ Wings=1 -Cloak\ and\ Dagger=1 -Cloak\ of\ Mists=1 -Cloaked\ Siren=1 -Clock\ of\ Omens=1 -Clockspinning=1 -Clockwork\ Avian=1 -Clockwork\ Beetle=1 -Clockwork\ Condor=1 -Clockwork\ Dragon=1 -Clockwork\ Gnomes=1 -Clockwork\ Hydra=1 -Cloistered\ Youth=1 -Clone=1 -Clone\ Shell=1 -Close\ Quarters=1 -Clot\ Sliver=1 -Cloud\ Crusader=1 -Cloud\ Dragon=1 -Cloud\ Elemental=1 -Cloud\ Manta=1 -Cloud\ Spirit=1 -Cloud\ Sprite=1 -Cloud\ of\ Faeries=1 -Cloudblazer=1 -Cloudchaser\ Kestrel=1 -Cloudcrest\ Lake=1 -Cloudcrown\ Oak=1 -Cloudgoat\ Ranger=1 -Cloudheath\ Drake=1 -Cloudhoof\ Kirin=1 -Cloudpost=1 -Cloudreach\ Cavalry=1 -Cloudreader\ Sphinx=1 -Cloudseeder=1 -Cloudshift=1 -Cloudskate=1 -Cloven\ Casting=1 -Clutch\ of\ Currents=1 -Clutch\ of\ Undeath=1 -Clutch\ of\ the\ Undercity=1 -Coal\ Golem=1 -Coal\ Stoker=1 -Coalition\ Flag=1 -Coalition\ Victory=1 -Coast\ Watcher=1 -Coastal\ Discovery=1 -Coastal\ Drake=1 -Coastal\ Hornclaw=1 -Coat\ with\ Venom=1 -Coax\ from\ the\ Blind\ Eternities=1 -Cobalt\ Golem=1 -Cobblebrute=1 -Cobbled\ Wings=1 -Cobra\ Trap=1 -Cockatrice=1 -Codex\ Shredder=1 -Coerced\ Confession=1 -Coercion=1 -Cognivore=1 -Cogwork\ Assembler=1 -Cogworker's\ Puzzleknot=1 -Coiled\ Tinviper=1 -Coiling\ Woodworm=1 -Coils\ of\ the\ Medusa=1 -Cold-Water\ Snapper=1 -Colfenor's\ Plans=1 -Colfenor's\ Urn=1 -Collective\ Blessing=1 -Collective\ Defiance=1 -Collective\ Effort=1 -Colos\ Yearling=1 -Colossal\ Dreadmaw=1 -Colossal\ Heroics=1 -Colossal\ Might=1 -Colossal\ Whale=1 -Colossapede=1 -Colossus\ of\ Akros=1 -Colossus\ of\ Sardia=1 -Coma\ Veil=1 -Combust=1 -Commander's\ Authority=1 -Commander\ Greven\ il-Vec=1 -Commando\ Raid=1 -Commencement\ of\ Festivities=1 -Common\ Bond=1 -Commune\ with\ Dinosaurs=1 -Commune\ with\ Nature=1 -Comparative\ Analysis=1 -Compelling\ Argument=1 -Compelling\ Deterrence=1 -Complete\ Disregard=1 -Complex\ Automaton=1 -Complicate=1 -Composite\ Golem=1 -Compulsory\ Rest=1 -Concerted\ Effort=1 -Conclave's\ Blessing=1 -Conclave\ Equenaut=1 -Conclave\ Naturalists=1 -Conclave\ Phalanx=1 -Concordia\ Pegasus=1 -Conduit\ of\ Ruin=1 -Conduit\ of\ Storms=1 -Cone\ of\ Flame=1 -Confessor=1 -Confirm\ Suspicions=1 -Confiscate=1 -Confiscation\ Coup=1 -Confront\ the\ Unknown=1 -Congregate=1 -Congregation\ at\ Dawn=1 -Conjured\ Currency=1 -Conjurer's\ Ban=1 -Conquer=1 -Conquering\ Manticore=1 -Conqueror's\ Pledge=1 -Consecrate\ Land=1 -Consecrated\ by\ Blood=1 -Consign\ to\ Dream=1 -Consign\ to\ Dust=1 -Constricting\ Sliver=1 -Consul's\ Lieutenant=1 -Consul's\ Shieldguard=1 -Consulate\ Crackdown=1 -Consulate\ Dreadnought=1 -Consulate\ Skygate=1 -Consulate\ Surveillance=1 -Consulate\ Turret=1 -Consult\ the\ Necrosages=1 -Consume\ Spirit=1 -Consume\ Strength=1 -Consume\ the\ Meek=1 -Consuming\ Aberration=1 -Consuming\ Bonfire=1 -Consuming\ Ferocity=1 -Consuming\ Fervor=1 -Consuming\ Sinkhole=1 -Consuming\ Vortex=1 -Consumptive\ Goo=1 -Contagion\ Clasp=1 -Contagious\ Nim=1 -Containment\ Membrane=1 -Contaminated\ Bond=1 -Contaminated\ Ground=1 -Contemplation=1 -Contempt=1 -Contingency\ Plan=1 -Contraband\ Kingpin=1 -Contract\ Killing=1 -Contradict=1 -Control\ Magic=1 -Controvert=1 -Conundrum\ Sphinx=1 -Convalescent\ Care=1 -Conversion\ Chamber=1 -Convicted\ Killer=1 -Conviction=1 -Convincing\ Mirage=1 -Convolute=1 -Coordinated\ Assault=1 -Coordinated\ Barrage=1 -Copper\ Carapace=1 -Copper\ Myr=1 -Copper\ Tablet=1 -Copperhoof\ Vorrac=1 -Copperhorn\ Scout=1 -Coral\ Barrier=1 -Coral\ Eel=1 -Coral\ Merfolk=1 -Coral\ Trickster=1 -Coralhelm\ Guide=1 -Core\ Prowler=1 -Corpse\ Blockade=1 -Corpse\ Churn=1 -Corpse\ Connoisseur=1 -Corpse\ Cur=1 -Corpse\ Hauler=1 -Corpse\ Lunge=1 -Corpse\ Traders=1 -Corpsehatch=1 -Corpsejack\ Menace=1 -Corpseweft=1 -Corrosive\ Gale=1 -Corrosive\ Mentor=1 -Corrosive\ Ooze=1 -Corrupt=1 -Corrupt\ Eunuchs=1 -Corrupt\ Official=1 -Corrupted\ Conscience=1 -Corrupted\ Crossroads=1 -Corrupted\ Grafstone=1 -Corrupted\ Harvester=1 -Corrupted\ Resolve=1 -Corrupted\ Roots=1 -Corrupted\ Zendikon=1 -Corrupting\ Licid=1 -Cosi's\ Ravager=1 -Cosmic\ Horror=1 -Costly\ Plunder=1 -Council\ of\ the\ Absolute=1 -Counsel\ of\ the\ Soratami=1 -Counterbore=1 -Counterflux=1 -Counterlash=1 -Countermand=1 -Countervailing\ Winds=1 -Countless\ Gears\ Renegade=1 -Countryside\ Crusher=1 -Courageous\ Outrider=1 -Courier's\ Capsule=1 -Courier\ Griffin=1 -Coursers'\ Accord=1 -Court\ Archers=1 -Court\ Homunculus=1 -Court\ Hussar=1 -Court\ Street\ Denizen=1 -Courtly\ Provocateur=1 -Covenant\ of\ Blood=1 -Covenant\ of\ Minds=1 -Cover\ of\ Winter=1 -Cowardice=1 -Cowed\ by\ Wisdom=1 -Cower\ in\ Fear=1 -Cowl\ Prowler=1 -Crab\ Umbra=1 -Crabapple\ Cohort=1 -Crackdown\ Construct=1 -Crackleburr=1 -Crackling\ Club=1 -Crackling\ Perimeter=1 -Crackling\ Triton=1 -Cradle\ Guard=1 -Cradle\ of\ Vitality=1 -Cradle\ of\ the\ Accursed=1 -Cradle\ to\ Grave=1 -Crafty\ Cutpurse=1 -Crafty\ Pathmage=1 -Crag\ Puca=1 -Cragganwick\ Cremator=1 -Cranial\ Archive=1 -Cranial\ Extraction=1 -Crash\ Landing=1 -Crash\ Through=1 -Crash\ of\ Rhinos=1 -Crash\ the\ Ramparts=1 -Crashing\ Boars=1 -Crashing\ Centaur=1 -Crashing\ Tide=1 -Crater's\ Claws=1 -Crater\ Elemental=1 -Crater\ Hellion=1 -Craterize=1 -Craven\ Giant=1 -Craw\ Wurm=1 -Crawling\ Filth=1 -Crawling\ Sensation=1 -Crazed\ Armodon=1 -Crazed\ Goblin=1 -Creakwood\ Ghoul=1 -Cream\ of\ the\ Crop=1 -Creeping\ Dread=1 -Creeping\ Mold=1 -Creeping\ Renaissance=1 -Creepy\ Doll=1 -Cremate=1 -Crescendo\ of\ War=1 -Crested\ Craghorn=1 -Crested\ Herdcaller=1 -Crib\ Swap=1 -Crimson\ Hellkite=1 -Crimson\ Mage=1 -Crimson\ Manticore=1 -Crimson\ Muckwader=1 -Crimson\ Roc=1 -Crimson\ Wisps=1 -Crippling\ Blight=1 -Crippling\ Chill=1 -Crocanura=1 -Crocodile\ of\ the\ Crossing=1 -Crookclaw\ Transmuter=1 -Crop\ Sigil=1 -Crosis's\ Attendant=1 -Crossbow\ Ambush=1 -Crossbow\ Infantry=1 -Crossroads\ Consecrator=1 -Crosstown\ Courier=1 -Crossway\ Vampire=1 -Crosswinds=1 -Crovax,\ Ascendant\ Hero=1 -Crovax\ the\ Cursed=1 -Crow\ of\ Dark\ Tidings=1 -Crowd's\ Favor=1 -Crowd\ of\ Cinders=1 -Crown\ of\ Ascension=1 -Crown\ of\ Empires=1 -Crown\ of\ Flames=1 -Crown\ of\ Fury=1 -Crown\ of\ Suspicion=1 -Crown\ of\ Vigor=1 -Crowned\ Ceratok=1 -Crucible\ of\ Fire=1 -Crude\ Rampart=1 -Cruel\ Bargain=1 -Cruel\ Deceiver=1 -Cruel\ Edict=1 -Cruel\ Feeding=1 -Cruel\ Finality=1 -Cruel\ Revival=1 -Cruel\ Sadist=1 -Crumble=1 -Crumbling\ Vestige=1 -Crusader\ of\ Odric=1 -Crush=1 -Crush\ Underfoot=1 -Crusher\ Zendikon=1 -Crushing\ Canopy=1 -Crushing\ Pain=1 -Crushing\ Vines=1 -Cry\ of\ Contrition=1 -Cryoclasm=1 -Crypsis=1 -Crypt\ Champion=1 -Crypt\ Cobra=1 -Crypt\ Ripper=1 -Crypt\ of\ the\ Eternals=1 -Cryptborn\ Horror=1 -Cryptic\ Annelid=1 -Cryptic\ Cruiser=1 -Cryptic\ Serpent=1 -Cryptolith\ Fragment=1 -Cryptwailing=1 -Crystal\ Ball=1 -Crystal\ Rod=1 -Crystal\ Seer=1 -Crystal\ Shard=1 -Crystal\ Spray=1 -Crystalline\ Nautilus=1 -Crystallization=1 -Cudgel\ Troll=1 -Culling\ Dais=1 -Culling\ Drone=1 -Culling\ Mark=1 -Culling\ Scales=1 -Culling\ Sun=1 -Cult\ of\ the\ Waxing\ Moon=1 -Cultist's\ Staff=1 -Cultivator's\ Caravan=1 -Cultivator\ Drone=1 -Cultivator\ of\ Blades=1 -Cumber\ Stone=1 -Cunning\ Bandit=1 -Cunning\ Breezedancer=1 -Cunning\ Lethemancer=1 -Cunning\ Sparkmage=1 -Cunning\ Strike=1 -Cunning\ Survivor=1 -Curator's\ Ward=1 -Curator\ of\ Mysteries=1 -Curio\ Vendor=1 -Curiosity=1 -Curious\ Homunculus=1 -Curse\ of\ Death's\ Hold=1 -Curse\ of\ Oblivion=1 -Curse\ of\ Stalked\ Prey=1 -Curse\ of\ Thirst=1 -Curse\ of\ Wizardry=1 -Curse\ of\ the\ Cabal=1 -Curse\ of\ the\ Nightly\ Hunt=1 -Curse\ of\ the\ Swine=1 -Cursebreak=1 -Cursed\ Flesh=1 -Cursed\ Minotaur=1 -Cursed\ Monstrosity=1 -Cursed\ Rack=1 -Cursed\ Scroll=1 -Curtain\ of\ Light=1 -Custodi\ Soulbinders=1 -Custodian\ of\ the\ Trove=1 -Cut\ the\ Earthly\ Bond=1 -Cut\ the\ Tethers=1 -Cutthroat\ Maneuver=1 -Cutthroat\ il-Dal=1 -Cycle\ of\ Life=1 -Cyclone\ Sire=1 -Cyclopean\ Giant=1 -Cyclopean\ Mummy=1 -Cyclopean\ Snare=1 -Cyclops\ Gladiator=1 -Cyclops\ Tyrant=1 -Cyclops\ of\ Eternal\ Fury=1 -Cyclops\ of\ One-Eyed\ Pass=1 -Cystbearer=1 -Cytoplast\ Manipulator=1 -Cytoplast\ Root-Kin=1 -Cytoshape=1 -Cytospawn\ Shambler=1 -D'Avenant\ Trapper=1 -Dack's\ Duplicate=1 -Dagger\ of\ the\ Worthy=1 -Daggerback\ Basilisk=1 -Daggerclaw\ Imp=1 -Daggerdrome\ Imp=1 -Daily\ Regimen=1 -Dakmor\ Lancer=1 -Dakra\ Mystic=1 -Dampen\ Thought=1 -Dampening\ Pulse=1 -Damping\ Matrix=1 -Dance\ of\ Shadows=1 -Dance\ of\ the\ Skywise=1 -Dance\ with\ Devils=1 -Dancing\ Scimitar=1 -Dandn=1 -Daraja\ Griffin=1 -Darba=1 -Daredevil\ Dragster=1 -Darigaaz's\ Attendant=1 -Darigaaz's\ Caldera=1 -Daring\ Apprentice=1 -Daring\ Archaeologist=1 -Daring\ Buccaneer=1 -Daring\ Demolition=1 -Daring\ Leap=1 -Daring\ Saboteur=1 -Daring\ Skyjek=1 -Daring\ Sleuth=1 -Dark\ Banishing=1 -Dark\ Bargain=1 -Dark\ Betrayal=1 -Dark\ Dabbling=1 -Dark\ Favor=1 -Dark\ Hatchling=1 -Dark\ Heart\ of\ the\ Wood=1 -Dark\ Inquiry=1 -Dark\ Intimations=1 -Dark\ Nourishment=1 -Dark\ Privilege=1 -Dark\ Revenant=1 -Dark\ Supplicant=1 -Dark\ Temper=1 -Dark\ Tutelage=1 -Dark\ Withering=1 -Darkheart\ Sliver=1 -Darkling\ Stalker=1 -Darkslick\ Drake=1 -Darksteel\ Axe=1 -Darksteel\ Brute=1 -Darksteel\ Gargoyle=1 -Darksteel\ Myr=1 -Darksteel\ Pendant=1 -Darksteel\ Sentinel=1 -Darkthicket\ Wolf=1 -Darkwatch\ Elves=1 -Darkwater\ Egg=1 -Daru\ Encampment=1 -Daru\ Lancer=1 -Daru\ Mender=1 -Dash\ Hopes=1 -Dauntless\ Aven=1 -Dauntless\ Cathar=1 -Dauntless\ Dourbark=1 -Dauntless\ Onslaught=1 -Dauntless\ River\ Marshal=1 -Dauthi\ Cutthroat=1 -Dauthi\ Jackal=1 -Dauthi\ Marauder=1 -Dauthi\ Mindripper=1 -Dauthi\ Trapper=1 -Dauthi\ Warlord=1 -Dawn's\ Reflection=1 -Dawn\ Elemental=1 -Dawn\ Gryff=1 -Dawn\ to\ Dusk=1 -Dawnbringer\ Charioteers=1 -Dawnfeather\ Eagle=1 -Dawnglare\ Invoker=1 -Dawnglow\ Infusion=1 -Dawning\ Purist=1 -Dawnray\ Archer=1 -Dawnstrike\ Paladin=1 -Dawntreader\ Elk=1 -Daxos\ of\ Meletis=1 -Day\ of\ the\ Dragons=1 -Daybreak\ Chaplain=1 -Daybreak\ Ranger=1 -Dazzling\ Beauty=1 -Dazzling\ Ramparts=1 -Dazzling\ Reflection=1 -Dead-Iron\ Sledge=1 -Dead\ //\ Gone=1 -Dead\ Drop=1 -Dead\ Man's\ Chest=1 -Dead\ Reveler=1 -Dead\ Ringers=1 -Dead\ Weight=1 -Deadapult=1 -Deadbridge\ Goliath=1 -Deadbridge\ Shaman=1 -Deadeye\ Brawler=1 -Deadeye\ Harpooner=1 -Deadeye\ Plunderers=1 -Deadeye\ Quartermaster=1 -Deadeye\ Rig-Hauler=1 -Deadeye\ Tormentor=1 -Deadlock\ Trap=1 -Deadly\ Allure=1 -Deadly\ Designs=1 -Deadly\ Grub=1 -Deadly\ Insect=1 -Deadly\ Recluse=1 -Deadly\ Wanderings=1 -Dearly\ Departed=1 -Death's\ Approach=1 -Death's\ Duet=1 -Death's\ Presence=1 -Death-Hood\ Cobra=1 -Death\ Bomb=1 -Death\ Cultist=1 -Death\ Denied=1 -Death\ Frenzy=1 -Death\ Grasp=1 -Death\ Match=1 -Death\ Pit\ Offering=1 -Death\ Pits\ of\ Rath=1 -Death\ Pulse=1 -Death\ Rattle=1 -Death\ Stroke=1 -Death\ Wind=1 -Death\ by\ Dragons=1 -Death\ of\ a\ Thousand\ Stings=1 -Deathbellow\ Raider=1 -Deathbloom\ Thallid=1 -Deathbringer\ Thoctar=1 -Deathcap\ Cultivator=1 -Deathcoil\ Wurm=1 -Deathcult\ Rogue=1 -Deathforge\ Shaman=1 -Deathgaze\ Cockatrice=1 -Deathgazer=1 -Deathgreeter=1 -Deathless\ Ancient=1 -Deathless\ Angel=1 -Deathless\ Behemoth=1 -Deathmark=1 -Deathmark\ Prelate=1 -Deathmask\ Nezumi=1 -Deathreap\ Ritual=1 -Deathspore\ Thallid=1 -Debilitating\ Injury=1 -Debtor's\ Pulpit=1 -Debtors'\ Knell=1 -Decaying\ Soil=1 -Deceiver\ of\ Form=1 -Decimator\ Beetle=1 -Decision\ Paralysis=1 -Declare\ Dominance=1 -Decoction\ Module=1 -Decommission=1 -Decompose=1 -Decomposition=1 -Deconstruct=1 -Decorated\ Griffin=1 -Deem\ Worthy=1 -Deep-Sea\ Kraken=1 -Deep-Sea\ Terror=1 -Deep-Slumber\ Titan=1 -Deep\ Freeze=1 -Deep\ Reconnaissance=1 -Deepcavern\ Imp=1 -Deepchannel\ Mentor=1 -Deepfathom\ Skulker=1 -Deepfire\ Elemental=1 -Deeproot\ Warrior=1 -Deeproot\ Waters=1 -Deeptread\ Merrow=1 -Deepwater\ Hypnotist=1 -Deepwood\ Ghoul=1 -Deepwood\ Legate=1 -Deepwood\ Tantiv=1 -Deepwood\ Wolverine=1 -Defeat=1 -Defend\ the\ Hearth=1 -Defender\ en-Vec=1 -Defender\ of\ Law=1 -Defender\ of\ the\ Order=1 -Defensive\ Formation=1 -Defensive\ Maneuvers=1 -Defiant\ Bloodlord=1 -Defiant\ Elf=1 -Defiant\ Greatmaw=1 -Defiant\ Khenra=1 -Defiant\ Ogre=1 -Defiant\ Salvager=1 -Defiant\ Strike=1 -Defiling\ Tears=1 -Deflection=1 -Deft\ Dismissal=1 -Deft\ Duelist=1 -Deftblade\ Elite=1 -Defy\ Death=1 -Defy\ Gravity=1 -Dega\ Sanctuary=1 -Deglamer=1 -Dehydration=1 -Deicide=1 -Delirium\ Skeins=1 -Delusions\ of\ Mediocrity=1 -Dematerialize=1 -Dementia\ Sliver=1 -Demolish=1 -Demolition\ Stomper=1 -Demon's\ Grasp=1 -Demon's\ Herald=1 -Demon's\ Horn=1 -Demon's\ Jester=1 -Demonfire=1 -Demonic\ Appetite=1 -Demonic\ Collusion=1 -Demonic\ Consultation=1 -Demonic\ Taskmaster=1 -Demonic\ Torment=1 -Demonic\ Vigor=1 -Demonmail\ Hauberk=1 -Demonspine\ Whip=1 -Demoralize=1 -Demystify=1 -Denizen\ of\ the\ Deep=1 -Dense\ Canopy=1 -Deny\ Existence=1 -Deny\ Reality=1 -Depala,\ Pilot\ Exemplar=1 -Depths\ of\ Desire=1 -Deputy\ of\ Acquittals=1 -Deranged\ Assistant=1 -Deranged\ Hermit=1 -Deranged\ Outcast=1 -Deranged\ Whelp=1 -Dermoplasm=1 -Descendant\ of\ Soramaro=1 -Descent\ into\ Madness=1 -Desecrated\ Earth=1 -Desecration\ Elemental=1 -Desecration\ Plague=1 -Desecrator\ Hag=1 -Desert's\ Hold=1 -Desert=1 -Desert\ Cerodon=1 -Desert\ Twister=1 -Desert\ of\ the\ Fervent=1 -Desert\ of\ the\ Glorified=1 -Desert\ of\ the\ Indomitable=1 -Desert\ of\ the\ Mindful=1 -Desert\ of\ the\ True=1 -Deserter's\ Quarters=1 -Desolation\ Giant=1 -Desolation\ Twin=1 -Desperate\ Castaways=1 -Desperate\ Charge=1 -Desperate\ Gambit=1 -Desperate\ Ravings=1 -Desperate\ Sentry=1 -Desperate\ Stand=1 -Despise=1 -Despoiler\ of\ Souls=1 -Destined\ //\ Lead=1 -Destroy\ the\ Evidence=1 -Destructive\ Force=1 -Destructive\ Tampering=1 -Destructive\ Urge=1 -Detainment\ Spell=1 -Deviant\ Glee=1 -Devil's\ Play=1 -Devils'\ Playground=1 -Devilthorn\ Fox=1 -Devoted\ Crop-Mate=1 -Devotee\ of\ Strength=1 -Devour\ in\ Flames=1 -Devour\ in\ Shadow=1 -Devouring\ Greed=1 -Devouring\ Light=1 -Devouring\ Rage=1 -Devouring\ Strossus=1 -Devouring\ Swarm=1 -Devout\ Chaplain=1 -Devout\ Harpist=1 -Devout\ Lightcaster=1 -Devout\ Witness=1 -Dewdrop\ Spy=1 -Dhund\ Operative=1 -Diabolic\ Revelation=1 -Diabolic\ Tutor=1 -Diabolic\ Vision=1 -Diamond\ Faerie=1 -Die\ Young=1 -Diligent\ Excavator=1 -Diluvian\ Primordial=1 -Dimensional\ Breach=1 -Dimensional\ Infiltrator=1 -Diminish=1 -Dimir\ Charm=1 -Dimir\ Cluestone=1 -Dimir\ Cutpurse=1 -Dimir\ Guildmage=1 -Dimir\ House\ Guard=1 -Dimir\ Infiltrator=1 -Dimir\ Keyrune=1 -Dimir\ Machinations=1 -Din\ of\ the\ Fireherd=1 -Dingus\ Egg=1 -Dinosaur\ Hunter=1 -Dinosaur\ Stampede=1 -Dinrova\ Horror=1 -Diplomacy\ of\ the\ Wastes=1 -Diplomatic\ Escort=1 -Dire\ Fleet\ Captain=1 -Dire\ Fleet\ Hoarder=1 -Dire\ Fleet\ Interloper=1 -Dire\ Fleet\ Neckbreaker=1 -Dire\ Undercurrents=1 -Diregraf\ Colossus=1 -Diregraf\ Escort=1 -Dirge\ of\ Dread=1 -Dirty\ Wererat=1 -Disappear=1 -Disappearing\ Act=1 -Disarm=1 -Disaster\ Radius=1 -Disciple\ of\ Grace=1 -Disciple\ of\ Griselbrand=1 -Disciple\ of\ Kangee=1 -Disciple\ of\ Malice=1 -Disciple\ of\ Phenax=1 -Disciple\ of\ Tevesh\ Szat=1 -Disciple\ of\ the\ Old\ Ways=1 -Disciple\ of\ the\ Ring=1 -Discordant\ Dirge=1 -Discordant\ Spirit=1 -Disease\ Carriers=1 -Disembowel=1 -Disentomb=1 -Disintegrate=1 -Dismal\ Failure=1 -Dismantle=1 -Dismiss\ into\ Dream=1 -Disorder=1 -Disowned\ Ancestor=1 -Dispeller's\ Capsule=1 -Dispense\ Justice=1 -Dispersal\ Technician=1 -Disperse=1 -Dispersing\ Orb=1 -Displace=1 -Displacement\ Wave=1 -Disposal\ Mummy=1 -Dispossess=1 -Disrupting\ Scepter=1 -Disruptive\ Pitmage=1 -Disruptive\ Student=1 -Dissension\ in\ the\ Ranks=1 -Dissenter's\ Deliverance=1 -Dissipate=1 -Dissipation\ Field=1 -Dissolve=1 -Distant\ Memories=1 -Distemper\ of\ the\ Blood=1 -Distended\ Mindbender=1 -Distorting\ Wake=1 -Disturbing\ Plot=1 -Dive\ Down=1 -Divebomber\ Griffin=1 -Divergent\ Growth=1 -Diversionary\ Tactics=1 -Divest=1 -Divination=1 -Divine\ Deflection=1 -Divine\ Favor=1 -Divine\ Offering=1 -Divine\ Reckoning=1 -Divine\ Transformation=1 -Divine\ Verdict=1 -Diviner's\ Wand=1 -Diviner\ Spirit=1 -Diving\ Griffin=1 -Dizzying\ Gaze=1 -Djeru's\ Renunciation=1 -Djeru's\ Resolve=1 -Djeru,\ With\ Eyes\ Open=1 -Djinn\ Illuminatus=1 -Djinn\ of\ Wishes=1 -Docent\ of\ Perfection=1 -Dodecapod=1 -Dogged\ Hunter=1 -Dogpile=1 -Dolmen\ Gate=1 -Domesticated\ Hydra=1 -Domestication=1 -Dominator\ Drone=1 -Dong\ Zhou,\ the\ Tyrant=1 -Doom\ Cannon=1 -Doomed\ Dissenter=1 -Doomed\ Necromancer=1 -Doomgape=1 -Doomwake\ Giant=1 -Door\ to\ Nothingness=1 -Doorkeeper=1 -Dormant\ Gomazoa=1 -Dormant\ Sliver=1 -Dosan's\ Oldest\ Chant=1 -Double\ Negative=1 -Doubtless\ One=1 -Douse\ in\ Gloom=1 -Down\ //\ Dirty=1 -Downdraft=1 -Downpour=1 -Downsize=1 -Dowsing\ Shaman=1 -Dracoplasm=1 -Drag\ Down=1 -Drag\ Under=1 -Dragon's\ Eye\ Savants=1 -Dragon's\ Eye\ Sentry=1 -Dragon's\ Herald=1 -Dragon-Scarred\ Bear=1 -Dragon-Style\ Twins=1 -Dragon\ Appeasement=1 -Dragon\ Bell\ Monk=1 -Dragon\ Blood=1 -Dragon\ Egg=1 -Dragon\ Engine=1 -Dragon\ Fodder=1 -Dragon\ Grip=1 -Dragon\ Hatchling=1 -Dragon\ Mantle=1 -Dragon\ Mask=1 -Dragon\ Roost=1 -Dragon\ Throne\ of\ Tarkir=1 -Dragon\ Whelp=1 -Dragonloft\ Idol=1 -Dragonlord's\ Prerogative=1 -Dragonlord's\ Servant=1 -Dragonrage=1 -Dragonscale\ Boon=1 -Dragonshift=1 -Dragonsoul\ Knight=1 -Dragonstalker=1 -Drain\ the\ Well=1 -Draining\ Whelk=1 -Drainpipe\ Vermin=1 -Drake-Skull\ Cameo=1 -Drake\ Familiar=1 -Drake\ Hatchling=1 -Drake\ Haven=1 -Drake\ Umbra=1 -Drakestown\ Forgotten=1 -Drakewing\ Krasis=1 -Dralnu's\ Pet=1 -Dralnu,\ Lich\ Lord=1 -Dramatic\ Rescue=1 -Dramatic\ Reversal=1 -Drana's\ Chosen=1 -Drana's\ Emissary=1 -Drastic\ Revelation=1 -Dread=1 -Dread\ Defiler=1 -Dread\ Drone=1 -Dread\ Reaper=1 -Dread\ Slag=1 -Dread\ Specter=1 -Dread\ Warlock=1 -Dread\ Wight=1 -Dreadbringer\ Lampads=1 -Dreadwaters=1 -Dreadwing=1 -Dream's\ Grip=1 -Dream\ Chisel=1 -Dream\ Fighter=1 -Dream\ Leash=1 -Dream\ Prowler=1 -Dream\ Salvage=1 -Dream\ Thief=1 -Dream\ Tides=1 -Dream\ Twist=1 -Dreamborn\ Muse=1 -Dreamcaller\ Siren=1 -Dreamcatcher=1 -Dreampod\ Druid=1 -Dreams\ of\ the\ Dead=1 -Dreamscape\ Artist=1 -Dreamspoiler\ Witches=1 -Dreamstealer=1 -Dreamstone\ Hedron=1 -Dreamwinder=1 -Dreg\ Mangler=1 -Dreg\ Reaver=1 -Dregs\ of\ Sorrow=1 -Dregscape\ Zombie=1 -Drekavac=1 -Drelnoch=1 -Drift\ of\ the\ Dead=1 -Drifter\ il-Dal=1 -Drifting\ Shade=1 -Drill-Skimmer=1 -Drinker\ of\ Sorrow=1 -Dripping-Tongue\ Zubera=1 -Dripping\ Dead=1 -Driven\ //\ Despair=1 -Driver\ of\ the\ Dead=1 -Drogskol\ Cavalry=1 -Drogskol\ Shieldmate=1 -Dromar's\ Attendant=1 -Dromoka's\ Gift=1 -Dromoka\ Captain=1 -Dromoka\ Dunecaster=1 -Dromoka\ Monument=1 -Dromoka\ Warrior=1 -Dromosaur=1 -Droning\ Bureaucrats=1 -Drooling\ Groodion=1 -Drooling\ Ogre=1 -Dross\ Crocodile=1 -Dross\ Golem=1 -Dross\ Harvester=1 -Dross\ Hopper=1 -Dross\ Prowler=1 -Dross\ Scorpion=1 -Drove\ of\ Elves=1 -Drover\ of\ the\ Mighty=1 -Drown\ in\ Filth=1 -Drowned=1 -Drowned\ Rusalka=1 -Drowner\ Initiate=1 -Drowner\ of\ Hope=1 -Drowner\ of\ Secrets=1 -Drownyard\ Behemoth=1 -Drownyard\ Explorers=1 -Drownyard\ Temple=1 -Drudge\ Beetle=1 -Drudge\ Reavers=1 -Drudge\ Sentinel=1 -Drudge\ Skeletons=1 -Druid's\ Call=1 -Druid's\ Deliverance=1 -Druid's\ Familiar=1 -Druid\ Lyrist=1 -Druid\ of\ the\ Anima=1 -Druid\ of\ the\ Cowl=1 -Druidic\ Satchel=1 -Drumhunter=1 -Drunau\ Corpse\ Trawler=1 -Dry\ Spell=1 -Dryad's\ Caress=1 -Dryad\ Sophisticate=1 -Dual\ Shot=1 -Dub=1 -Dubious\ Challenge=1 -Duct\ Crawler=1 -Duergar\ Assailant=1 -Duergar\ Cave-Guard=1 -Duergar\ Mine-Captain=1 -Dukhara\ Peafowl=1 -Dukhara\ Scavenger=1 -Dune-Brood\ Nephilim=1 -Dune\ Beetle=1 -Dune\ Diviner=1 -Duneblast=1 -Dunerider\ Outlaw=1 -Dunes\ of\ the\ Dead=1 -Dungeon\ Geists=1 -Dungeon\ Shade=1 -Duplicity=1 -Durable\ Handicraft=1 -Durkwood\ Baloth=1 -Durkwood\ Tracker=1 -Dusk\ Charger=1 -Dusk\ Feaster=1 -Dusk\ Imp=1 -Dusk\ Legion\ Dreadnought=1 -Duskborne\ Skymarcher=1 -Duskdale\ Wurm=1 -Duskhunter\ Bat=1 -Duskmantle,\ House\ of\ Shadow=1 -Duskmantle\ Prowler=1 -Duskrider\ Peregrine=1 -Duskwalker=1 -Duskworker=1 -Dust\ Corona=1 -Dust\ Elemental=1 -Dutiful\ Attendant=1 -Dutiful\ Return=1 -Dutiful\ Servants=1 -Dutiful\ Thrull=1 -Duty-Bound\ Dead=1 -Dwarven\ Berserker=1 -Dwarven\ Blastminer=1 -Dwarven\ Demolition\ Team=1 -Dwarven\ Driller=1 -Dwarven\ Landslide=1 -Dwarven\ Nomad=1 -Dwarven\ Patrol=1 -Dwarven\ Priest=1 -Dwarven\ Recruiter=1 -Dwarven\ Shrine=1 -Dwarven\ Soldier=1 -Dwarven\ Strike\ Force=1 -Dwarven\ Vigilantes=1 -Dwindle=1 -Dwynen,\ Gilt-Leaf\ Daen=1 -Dying\ Wail=1 -Dying\ Wish=1 -Dynacharge=1 -Dynavolt\ Tower=1 -Eager\ Cadet=1 -Eager\ Construct=1 -Eagle\ of\ the\ Watch=1 -Early\ Frost=1 -Earsplitting\ Rats=1 -Earth\ Elemental=1 -Earthblighter=1 -Earthbrawn=1 -Earthen\ Arms=1 -Earthshaker=1 -Eastern\ Paladin=1 -Eater\ of\ Hope=1 -Ebon\ Drake=1 -Ebonblade\ Reaper=1 -Ebony\ Horse=1 -Ebony\ Rhino=1 -Ebony\ Treefolk=1 -Echo\ Circlet=1 -Echo\ Mage=1 -Echo\ Tracer=1 -Echoes\ of\ the\ Kin\ Tree=1 -Echoing\ Calm=1 -Echoing\ Courage=1 -Echoing\ Ruin=1 -Eddytrail\ Hawk=1 -Edifice\ of\ Authority=1 -Edric,\ Spymaster\ of\ Trest=1 -Eel\ Umbra=1 -Eerie\ Procession=1 -Efficient\ Construction=1 -Efreet\ Weaponmaster=1 -Ego\ Erasure=1 -Eidolon\ of\ Countless\ Battles=1 -Eight-and-a-Half-Tails=1 -Ekundu\ Cyclops=1 -Ekundu\ Griffin=1 -Elaborate\ Firecannon=1 -Eland\ Umbra=1 -Elder\ Cathar=1 -Elder\ Deep-Fiend=1 -Elder\ Land\ Wurm=1 -Elder\ Mastery=1 -Elder\ Pine\ of\ Jukai=1 -Elder\ of\ Laurels=1 -Eldrazi\ Aggressor=1 -Eldrazi\ Devastator=1 -Eldrazi\ Mimic=1 -Eldrazi\ Obligator=1 -Eldrazi\ Skyspawner=1 -Electrify=1 -Electropotence=1 -Electrostatic\ Bolt=1 -Electrostatic\ Pummeler=1 -Electryte=1 -Elegant\ Edgecrafters=1 -Elemental\ Appeal=1 -Elemental\ Bond=1 -Elemental\ Mastery=1 -Elemental\ Resonance=1 -Elemental\ Uprising=1 -Elephant\ Ambush=1 -Elephant\ Guide=1 -Elfhame\ Druid=1 -Elgaud\ Inquisitor=1 -Elgaud\ Shieldmate=1 -Elite\ Arcanist=1 -Elite\ Archers=1 -Elite\ Cat\ Warrior=1 -Elite\ Inquisitor=1 -Elite\ Javelineer=1 -Elite\ Skirmisher=1 -Elite\ Vanguard=1 -Elixir\ of\ Immortality=1 -Elixir\ of\ Vitality=1 -Elkin\ Bottle=1 -Elsewhere\ Flask=1 -Elusive\ Krasis=1 -Elusive\ Tormentor=1 -Elven\ Lyre=1 -Elven\ Riders=1 -Elven\ Rite=1 -Elves\ of\ Deep\ Shadow=1 -Elvish\ Aberration=1 -Elvish\ Berserker=1 -Elvish\ Branchbender=1 -Elvish\ Eulogist=1 -Elvish\ Fury=1 -Elvish\ Handservant=1 -Elvish\ Herder=1 -Elvish\ Hexhunter=1 -Elvish\ Lookout=1 -Elvish\ Lyrist=1 -Elvish\ Pathcutter=1 -Elvish\ Piper=1 -Elvish\ Ranger=1 -Elvish\ Scrapper=1 -Elvish\ Soultiller=1 -Elvish\ Warrior=1 -Embalmed\ Brawler=1 -Embalmer's\ Tools=1 -Ember-Eye\ Wolf=1 -Ember\ Beast=1 -Ember\ Gale=1 -Ember\ Shot=1 -Ember\ Swallower=1 -Emberhorn\ Minotaur=1 -Embermaw\ Hellion=1 -Emberstrike\ Duo=1 -Emberwilde\ Augur=1 -Emberwilde\ Caliph=1 -Emblazoned\ Golem=1 -Emblem\ of\ the\ Warmind=1 -Embodiment\ of\ Fury=1 -Embodiment\ of\ Insight=1 -Embodiment\ of\ Spring=1 -Embolden=1 -Embraal\ Bruiser=1 -Embraal\ Gear-Smasher=1 -Emergent\ Growth=1 -Emeria\ Angel=1 -Emeria\ Shepherd=1 -Emissary\ of\ Despair=1 -Emissary\ of\ Hope=1 -Emissary\ of\ Sunrise=1 -Emissary\ of\ the\ Sleepless=1 -Emmara\ Tandris=1 -Emmessi\ Tome=1 -Emperor's\ Vanguard=1 -Emperor\ Crocodile=1 -Empty-Shrine\ Kannushi=1 -Empyreal\ Voyager=1 -Empyrial\ Armor=1 -Empyrial\ Plate=1 -Emrakul's\ Evangel=1 -Emrakul's\ Hatcher=1 -Emrakul's\ Influence=1 -Enatu\ Golem=1 -Encampment\ Keeper=1 -Encase\ in\ Ice=1 -Enchantment\ Alteration=1 -Encircling\ Fissure=1 -Enclave\ Cryptologist=1 -Enclave\ Elite=1 -Encroaching\ Wastes=1 -Encrust=1 -End\ Hostilities=1 -Endangered\ Armodon=1 -Endbringer's\ Revel=1 -Endemic\ Plague=1 -Endless\ Obedience=1 -Endless\ Ranks\ of\ the\ Dead=1 -Endless\ Sands=1 -Endless\ Scream=1 -Endless\ Swarm=1 -Endless\ Whispers=1 -Endoskeleton=1 -Endrek\ Sahr,\ Master\ Breeder=1 -Endure=1 -Energizer=1 -Energy\ Arc=1 -Enervate=1 -Enfeeblement=1 -Engineered\ Might=1 -Engulfing\ Flames=1 -Engulfing\ Slagwurm=1 -Enhanced\ Awareness=1 -Enigma\ Drake=1 -Enigma\ Sphinx=1 -Enlarge=1 -Enlightened\ Ascetic=1 -Enlightened\ Maniac=1 -Enlisted\ Wurm=1 -Enormous\ Baloth=1 -Enrage=1 -Enraged\ Giant=1 -Enraging\ Licid=1 -Enshrined\ Memories=1 -Enshrouding\ Mist=1 -Enslave=1 -Enslaved\ Dwarf=1 -Ensouled\ Scimitar=1 -Entangling\ Trap=1 -Entangling\ Vines=1 -Enter\ the\ Unknown=1 -Enthralling\ Victor=1 -Entomber\ Exarch=1 -Entrails\ Feaster=1 -Entrancing\ Melody=1 -Entropic\ Eidolon=1 -Envelop=1 -Eon\ Hub=1 -Ephara's\ Enlightenment=1 -Ephara's\ Radiance=1 -Ephemeral\ Shields=1 -Ephemeron=1 -Epic\ Proportions=1 -Epicenter=1 -Epiphany\ Storm=1 -Epiphany\ at\ the\ Drownyard=1 -Epitaph\ Golem=1 -Epochrasite=1 -Equal\ Treatment=1 -Equestrian\ Skill=1 -Era\ of\ Innovation=1 -Eradicate=1 -Erase=1 -Erdwal\ Illuminator=1 -Erdwal\ Ripper=1 -Erebos's\ Emissary=1 -Erg\ Raiders=1 -Eron\ the\ Relentless=1 -Errand\ of\ Duty=1 -Errant\ Doomsayers=1 -Errant\ Ephemeron=1 -Erratic\ Mutation=1 -Ersatz\ Gnomes=1 -Ertai's\ Trickery=1 -Escaped\ Null=1 -Escaped\ Shapeshifter=1 -Esper\ Sojourners=1 -Esperzoa=1 -Essence\ Backlash=1 -Essence\ Depleter=1 -Essence\ Drain=1 -Essence\ Feed=1 -Essence\ Filter=1 -Essence\ Flare=1 -Essence\ Flux=1 -Essence\ Fracture=1 -Essence\ Leak=1 -Etched\ Monstrosity=1 -Etched\ Oracle=1 -Eternal\ Dominion=1 -Eternal\ Scourge=1 -Eternal\ Thirst=1 -Eternal\ of\ Harsh\ Truths=1 -Eternity\ Snare=1 -Ether\ Well=1 -Ethercaste\ Knight=1 -Ethereal\ Champion=1 -Ethereal\ Guidance=1 -Ethereal\ Usher=1 -Etherium\ Astrolabe=1 -Ethersworn\ Shieldmage=1 -Etherwrought\ Page=1 -Evacuation=1 -Evanescent\ Intellect=1 -Evangel\ of\ Heliod=1 -Evangelize=1 -Even\ the\ Odds=1 -Ever\ After=1 -Everbark\ Shaman=1 -Everdawn\ Champion=1 -Everflame\ Eidolon=1 -Everflowing\ Chalice=1 -Everglades=1 -Evernight\ Shade=1 -Evershrike=1 -Evil\ Eye\ of\ Orms-by-Gore=1 -Evil\ Eye\ of\ Urborg=1 -Evil\ Presence=1 -Evil\ Twin=1 -Eviscerate=1 -Eviscerator=1 -Evolution\ Charm=1 -Evolution\ Vat=1 -Evolving\ Wilds=1 -Evra,\ Halcyon\ Witness=1 -Exalted\ Dragon=1 -Exava,\ Rakdos\ Blood\ Witch=1 -Excavation\ Elephant=1 -Excavator=1 -Exclusion\ Ritual=1 -Excommunicate=1 -Excoriate=1 -Excruciator=1 -Execute=1 -Executioner's\ Capsule=1 -Executioner's\ Hood=1 -Executioner's\ Swing=1 -Exemplar\ of\ Strength=1 -Exert\ Influence=1 -Exhumer\ Thrull=1 -Exile=1 -Exile\ into\ Darkness=1 -Exiled\ Boggart=1 -Exiled\ Doomsayer=1 -Exoskeletal\ Armor=1 -Exotic\ Curse=1 -Exotic\ Disease=1 -Expedite=1 -Expedition\ Envoy=1 -Expedition\ Raptor=1 -Expel\ from\ Orazca=1 -Expendable\ Troops=1 -Experiment\ Kraj=1 -Experimental\ Aviator=1 -Exploding\ Borders=1 -Explorer's\ Scope=1 -Explosive\ Apparatus=1 -Explosive\ Growth=1 -Explosive\ Impact=1 -Explosive\ Revelation=1 -Expunge=1 -Exquisite\ Archangel=1 -Extinguish\ All\ Hope=1 -Extract\ from\ Darkness=1 -Extractor\ Demon=1 -Extricator\ of\ Sin=1 -Extruder=1 -Exultant\ Cultist=1 -Exultant\ Skymarcher=1 -Eye\ Gouge=1 -Eye\ for\ an\ Eye=1 -Eye\ of\ the\ Storm=1 -Eyeblight's\ Ending=1 -Eyeblight\ Assassin=1 -Eyeblight\ Massacre=1 -Eyeless\ Watcher=1 -Eyes\ in\ the\ Skies=1 -Eyes\ of\ the\ Watcher=1 -Eyes\ of\ the\ Wisent=1 -Ezuri's\ Archers=1 -Fa'adiyah\ Seer=1 -Fable\ of\ Wolf\ and\ Owl=1 -Fabled\ Hero=1 -Fabrication\ Module=1 -Face\ of\ Fear=1 -Facevaulter=1 -Fact\ or\ Fiction=1 -Fade\ from\ Memory=1 -Fade\ into\ Antiquity=1 -Faerie\ Harbinger=1 -Faerie\ Impostor=1 -Faerie\ Invaders=1 -Faerie\ Mechanist=1 -Faerie\ Squadron=1 -Faerie\ Swarm=1 -Faerie\ Tauntings=1 -Faerie\ Trickery=1 -Failed\ Inspection=1 -Failure\ //\ Comply=1 -Fairgrounds\ Trumpeter=1 -Fairgrounds\ Warden=1 -Faith's\ Fetters=1 -Faith\ Unbroken=1 -Faith\ of\ the\ Devoted=1 -Faithbearer\ Paladin=1 -Faithful\ Squire=1 -Falkenrath\ Gorger=1 -Falkenrath\ Marauders=1 -Falkenrath\ Noble=1 -Falkenrath\ Reaver=1 -Falkenrath\ Torturer=1 -Fall\ of\ the\ Gavel=1 -Fall\ of\ the\ Hammer=1 -Fall\ of\ the\ Thran=1 -Fall\ of\ the\ Titans=1 -Fallen\ Angel=1 -Fallen\ Cleric=1 -Fallen\ Ferromancer=1 -Fallen\ Ideal=1 -Falling\ Timber=1 -Fallowsage=1 -False\ Dawn=1 -False\ Memories=1 -Familiar's\ Ruse=1 -Famine=1 -Famished\ Ghoul=1 -Famished\ Paladin=1 -Fan\ Bearer=1 -Fanatic\ of\ Mogis=1 -Fanatic\ of\ Xenagos=1 -Fang\ Skulkin=1 -Fangren\ Firstborn=1 -Fangren\ Hunter=1 -Fanning\ the\ Flames=1 -Far\ //\ Away=1 -Farbog\ Boneflinger=1 -Farbog\ Revenant=1 -Farm\ //\ Market=1 -Farrel's\ Zealot=1 -Fatal\ Blow=1 -Fatal\ Frenzy=1 -Fate\ Foretold=1 -Fate\ Forgotten=1 -Fate\ Transfer=1 -Fate\ Unraveler=1 -Fated\ Conflagration=1 -Fated\ Infatuation=1 -Fated\ Intervention=1 -Fated\ Retribution=1 -Fated\ Return=1 -Fateful\ Showdown=1 -Fatespinner=1 -Fathom\ Feeder=1 -Fathom\ Fleet\ Boarder=1 -Fathom\ Fleet\ Captain=1 -Fathom\ Fleet\ Cutthroat=1 -Fathom\ Fleet\ Firebrand=1 -Fathom\ Mage=1 -Fathom\ Seer=1 -Fathom\ Trawl=1 -Fatigue=1 -Fault\ Riders=1 -Favor\ of\ the\ Mighty=1 -Favor\ of\ the\ Woods=1 -Favored\ Hoplite=1 -Fear=1 -Fearsome\ Temper=1 -Feast\ of\ Dreams=1 -Feast\ of\ Flesh=1 -Feast\ on\ the\ Fallen=1 -Feast\ or\ Famine=1 -Feat\ of\ Resistance=1 -Feebleness=1 -Feed\ the\ Pack=1 -Feeding\ Frenzy=1 -Feeling\ of\ Dread=1 -Felhide\ Brawler=1 -Felhide\ Minotaur=1 -Felhide\ Petrifier=1 -Felidar\ Cub=1 -Felidar\ Sovereign=1 -Fell\ Flagship=1 -Femeref\ Healer=1 -Femeref\ Knight=1 -Femeref\ Scouts=1 -Fen\ Hauler=1 -Fencer's\ Magemark=1 -Fencing\ Ace=1 -Fendeep\ Summoner=1 -Feral\ Abomination=1 -Feral\ Animist=1 -Feral\ Contest=1 -Feral\ Hydra=1 -Feral\ Incarnation=1 -Feral\ Instinct=1 -Feral\ Invocation=1 -Feral\ Krushok=1 -Feral\ Lightning=1 -Feral\ Prowler=1 -Feral\ Shadow=1 -Feral\ Thallid=1 -Ferocity=1 -Ferropede=1 -Ferrovore=1 -Fertile\ Imagination=1 -Fertile\ Thicket=1 -Fervent\ Cathar=1 -Fervent\ Charge=1 -Fervent\ Denial=1 -Fervent\ Paincaster=1 -Fervent\ Strike=1 -Fervor=1 -Festercreep=1 -Festergloom=1 -Festerhide\ Boar=1 -Festering\ Evil=1 -Festering\ Goblin=1 -Festering\ Mummy=1 -Festering\ Newt=1 -Festering\ Wound=1 -Festival\ of\ the\ Guildpact=1 -Fetid\ Horror=1 -Fetid\ Imp=1 -Fettergeist=1 -Feudkiller's\ Verdict=1 -Fevered\ Strength=1 -Fevered\ Visions=1 -Field\ Creeper=1 -Field\ Surgeon=1 -Field\ of\ Souls=1 -Fiend\ Binder=1 -Fierce\ Invocation=1 -Fiery\ Cannonade=1 -Fiery\ Conclusion=1 -Fiery\ Fall=1 -Fiery\ Finish=1 -Fiery\ Hellhound=1 -Fiery\ Impulse=1 -Fiery\ Intervention=1 -Fiery\ Temper=1 -Fight\ or\ Flight=1 -Fight\ to\ the\ Death=1 -Fighting\ Chance=1 -Fighting\ Drake=1 -Filigree\ Angel=1 -Filigree\ Crawler=1 -Filigree\ Familiar=1 -Filigree\ Sages=1 -Fill\ with\ Fright=1 -Filth=1 -Filthy\ Cur=1 -Final-Sting\ Faerie=1 -Final\ Parting=1 -Final\ Punishment=1 -Final\ Revels=1 -Final\ Reward=1 -Finest\ Hour=1 -Fire-Belly\ Changeling=1 -Fire-Field\ Ogre=1 -Fire\ Ambush=1 -Fire\ Ants=1 -Fire\ Drake=1 -Fire\ Elemental=1 -Fire\ Imp=1 -Fire\ Juggler=1 -Fire\ Servant=1 -Fire\ Shrine\ Keeper=1 -Fire\ Sprites=1 -Fire\ Tempest=1 -Fire\ Whip=1 -Fire\ at\ Will=1 -Fireball=1 -Firebrand\ Archer=1 -Firebreathing=1 -Firecannon\ Blast=1 -Firefiend\ Elemental=1 -Firefist\ Adept=1 -Firefist\ Striker=1 -Firefly=1 -Fireforger's\ Puzzleknot=1 -Firefright\ Mage=1 -Firemane\ Angel=1 -Firemane\ Avenger=1 -Firemantle\ Mage=1 -Firemind's\ Foresight=1 -Fires\ of\ Undeath=1 -Fires\ of\ Yavimaya=1 -Firescreamer=1 -Fireshrieker=1 -Firestorm\ Hellkite=1 -Firestorm\ Phoenix=1 -Firewake\ Sliver=1 -First\ Response=1 -First\ Volley=1 -Fishliver\ Oil=1 -Fissure=1 -Fissure\ Vent=1 -Fists\ of\ Ironwood=1 -Fists\ of\ the\ Anvil=1 -Fists\ of\ the\ Demigod=1 -Five-Alarm\ Fire=1 -Flailing\ Drake=1 -Flailing\ Manticore=1 -Flame-Kin\ War\ Scout=1 -Flame-Kin\ Zealot=1 -Flame-Wreathed\ Phoenix=1 -Flame\ Burst=1 -Flame\ Elemental=1 -Flame\ Fusillade=1 -Flame\ Javelin=1 -Flame\ Spirit=1 -Flame\ Wave=1 -Flameblade\ Angel=1 -Flameblast\ Dragon=1 -Flameborn\ Hellion=1 -Flamebreak=1 -Flamecast\ Wheel=1 -Flamecore\ Elemental=1 -Flamekin\ Bladewhirl=1 -Flamekin\ Brawler=1 -Flames\ of\ the\ Blood\ Hand=1 -Flames\ of\ the\ Firebrand=1 -Flameshadow\ Conjuring=1 -Flamespeaker\ Adept=1 -Flametongue\ Kavu=1 -Flamewave\ Invoker=1 -Flaming\ Sword=1 -Flare=1 -Flaring\ Flame-Kin=1 -Flash\ Conscription=1 -Flash\ Foliage=1 -Flash\ of\ Defiance=1 -Flashfreeze=1 -Flatten=1 -Flay=1 -Flayed\ Nim=1 -Flayer\ Drone=1 -Flaying\ Tendrils=1 -Fledgling\ Dragon=1 -Fledgling\ Imp=1 -Fledgling\ Mawcor=1 -Fledgling\ Osprey=1 -Fleecemane\ Lion=1 -Fleet\ Swallower=1 -Fleetfeather\ Cockatrice=1 -Fleetfeather\ Sandals=1 -Fleetfoot\ Panther=1 -Fleeting\ Aven=1 -Fleeting\ Image=1 -Fleeting\ Memories=1 -Fleetwheel\ Cruiser=1 -Flensermite=1 -Flesh-Eater\ Imp=1 -Flesh\ //\ Blood=1 -Flesh\ Allergy=1 -Flesh\ Reaver=1 -Flesh\ to\ Dust=1 -Fleshbag\ Marauder=1 -Fleshwrither=1 -Flickerform=1 -Flickering\ Spirit=1 -Flight=1 -Flight\ Spellbomb=1 -Flight\ of\ Fancy=1 -Fling=1 -Flint\ Golem=1 -Flinthoof\ Boar=1 -Flitterstep\ Eidolon=1 -Flood\ Plain=1 -Flood\ of\ Recollection=1 -Floodbringer=1 -Floodchaser=1 -Floodgate=1 -Floodtide\ Serpent=1 -Floodwater\ Dam=1 -Floodwaters=1 -Flourishing\ Defenses=1 -Flow\ of\ Ideas=1 -Flowstone\ Armor=1 -Flowstone\ Blade=1 -Flowstone\ Channeler=1 -Flowstone\ Charger=1 -Flowstone\ Crusher=1 -Flowstone\ Embrace=1 -Flowstone\ Flood=1 -Flowstone\ Mauler=1 -Flowstone\ Overseer=1 -Flowstone\ Salamander=1 -Flowstone\ Sculpture=1 -Flowstone\ Shambler=1 -Flowstone\ Slide=1 -Flowstone\ Strike=1 -Flowstone\ Surge=1 -Flowstone\ Thopter=1 -Flowstone\ Wyvern=1 -Flurry\ of\ Wings=1 -Fluxcharger=1 -Flying\ Carpet=1 -Flying\ Crane\ Technique=1 -Flying\ Men=1 -Fodder\ Cannon=1 -Fodder\ Launch=1 -Fog=1 -Fog\ Elemental=1 -Fog\ Patch=1 -Fog\ of\ Gnats=1 -Fogwalker=1 -Fold\ into\ Aether=1 -Folk\ of\ the\ Pines=1 -Followed\ Footsteps=1 -Font\ of\ Fortunes=1 -Font\ of\ Return=1 -Font\ of\ Vigor=1 -Fool's\ Demise=1 -Fool's\ Tome=1 -Foot\ Soldiers=1 -Footbottom\ Feast=1 -Foratog=1 -Forbidden\ Lore=1 -Force\ Away=1 -Force\ of\ Nature=1 -Force\ of\ Savagery=1 -Forced\ Adaptation=1 -Forced\ Worship=1 -Forcemage\ Advocate=1 -Forebear's\ Blade=1 -Forerunner\ of\ Slaughter=1 -Forerunner\ of\ the\ Coalition=1 -Forerunner\ of\ the\ Empire=1 -Forerunner\ of\ the\ Heralds=1 -Forerunner\ of\ the\ Legion=1 -Foresee=1 -Foreshadow=1 -Forest=1 -Forfend=1 -Forge\ Armor=1 -Forge\ Devil=1 -Forgeborn\ Oreads=1 -Forgotten\ Creation=1 -Forgotten\ Lore=1 -Foriysian\ Interceptor=1 -Foriysian\ Totem=1 -Fork\ in\ the\ Road=1 -Forked-Branch\ Garami=1 -Forked\ Lightning=1 -Forlorn\ Pseudamma=1 -Form\ of\ the\ Dinosaur=1 -Formless\ Nurturing=1 -Forsaken\ Drifters=1 -Forsaken\ Sanctuary=1 -Fortified\ Rampart=1 -Fortify=1 -Fortitude=1 -Fortress\ Cyclops=1 -Fortuitous\ Find=1 -Fortune's\ Favor=1 -Fortune\ Thief=1 -Fossil\ Find=1 -Foul\ Emissary=1 -Foul\ Familiar=1 -Foul\ Imp=1 -Foul\ Orchard=1 -Foul\ Presence=1 -Foul\ Renewal=1 -Foundry\ Assembler=1 -Foundry\ Champion=1 -Foundry\ Hornet=1 -Foundry\ Screecher=1 -Foundry\ of\ the\ Consuls=1 -Fountain\ of\ Youth=1 -Fourth\ Bridge\ Prowler=1 -Fractured\ Loyalty=1 -Fragmentize=1 -Frantic\ Purification=1 -Frantic\ Salvage=1 -Freejam\ Regent=1 -Freewind\ Equenaut=1 -Freewind\ Falcon=1 -Frenetic\ Ogre=1 -Frenetic\ Raptor=1 -Frenetic\ Sliver=1 -Frenzied\ Goblin=1 -Frenzied\ Rage=1 -Frenzied\ Raptor=1 -Frenzied\ Tilling=1 -Frenzy\ Sliver=1 -Fresh\ Meat=1 -Fresh\ Volunteers=1 -Fretwork\ Colony=1 -Freyalise's\ Winds=1 -Frightcrawler=1 -Frightful\ Delusion=1 -Frightshroud\ Courier=1 -Frilled\ Deathspitter=1 -Frilled\ Sandwalla=1 -Frilled\ Sea\ Serpent=1 -From\ Beyond=1 -From\ Under\ the\ Floorboards=1 -Frontier\ Bivouac=1 -Frontier\ Guide=1 -Frontline\ Devastator=1 -Frontline\ Medic=1 -Frontline\ Rebel=1 -Frontline\ Sage=1 -Frontline\ Strategist=1 -Frost\ Lynx=1 -Frost\ Marsh=1 -Frost\ Raptor=1 -Frostburn\ Weird=1 -Frostweb\ Spider=1 -Frozen\ Aether=1 -Frozen\ Solid=1 -Fuel\ for\ the\ Cause=1 -Fugitive\ Wizard=1 -Fugue=1 -Fulgent\ Distraction=1 -Full\ Moon's\ Rise=1 -Fumarole=1 -Fumiko\ the\ Lowblood=1 -Funeral\ Pyre=1 -Fungal\ Behemoth=1 -Fungal\ Infection=1 -Fungal\ Plots=1 -Fungal\ Reaches=1 -Fungal\ Shambler=1 -Fungus\ Sliver=1 -Furious\ Assault=1 -Furious\ Reprisal=1 -Furious\ Resistance=1 -Furnace\ Brood=1 -Furnace\ Celebration=1 -Furnace\ Dragon=1 -Furnace\ Scamp=1 -Furnace\ Spirit=1 -Furnace\ Whelp=1 -Furor\ of\ the\ Bitten=1 -Furtive\ Homunculus=1 -Fury\ Charm=1 -Furyblade\ Vampire=1 -Furystoke\ Giant=1 -Fusion\ Elemental=1 -Future\ Sight=1 -Fylamarid=1 -Fyndhorn\ Pollen=1 -Gaea's\ Anthem=1 -Gaea's\ Blessing=1 -Gaea's\ Bounty=1 -Gaea's\ Embrace=1 -Gaea's\ Herald=1 -Gaea's\ Liege=1 -Gaea's\ Protector=1 -Gaea's\ Revenge=1 -Gainsay=1 -Gale\ Force=1 -Galepowder\ Mage=1 -Galestrike=1 -Gallant\ Cavalry=1 -Gallantry=1 -Gallows\ Warden=1 -Gallows\ at\ Willow\ Hill=1 -Galvanic\ Bombardment=1 -Galvanic\ Juggernaut=1 -Galvanic\ Key=1 -Galvanoth=1 -Game-Trail\ Changeling=1 -Game\ Trail=1 -Gamekeeper=1 -Gang\ of\ Devils=1 -Gang\ of\ Elk=1 -Gangrenous\ Goliath=1 -Gangrenous\ Zombies=1 -Gargantuan\ Gorilla=1 -Gargoyle\ Castle=1 -Gargoyle\ Sentinel=1 -Garna,\ the\ Bloodflame=1 -Garruk's\ Companion=1 -Garruk's\ Horde=1 -Garruk's\ Packleader=1 -Garrulous\ Sycophant=1 -Garza\ Zol,\ Plague\ Queen=1 -Gaseous\ Form=1 -Gate\ Hound=1 -Gate\ Smasher=1 -Gatecreeper\ Vine=1 -Gatekeeper\ of\ Malakir=1 -Gateway\ Shade=1 -Gathan\ Raiders=1 -Gather\ Courage=1 -Gather\ Specimens=1 -Gather\ the\ Pack=1 -Gatherer\ of\ Graces=1 -Gatstaf\ Arsonists=1 -Gatstaf\ Shepherd=1 -Gauntlets\ of\ Chaos=1 -Gavony\ Ironwright=1 -Gavony\ Unhallowed=1 -Gaze\ of\ Adamaro=1 -Gaze\ of\ Justice=1 -Gearseeker\ Serpent=1 -Gearshift\ Ace=1 -Gearsmith\ Guardian=1 -Gearsmith\ Prodigy=1 -Geier\ Reach\ Bandit=1 -Geist-Fueled\ Scarecrow=1 -Geist-Honored\ Monk=1 -Geist\ of\ the\ Archives=1 -Geist\ of\ the\ Lonely\ Vigil=1 -Geistblast=1 -Geistflame=1 -Gelatinous\ Genesis=1 -Gelectrode=1 -Gelid\ Shackles=1 -Gem\ of\ Becoming=1 -Gemini\ Engine=1 -Gempalm\ Avenger=1 -Gemstone\ Array=1 -General's\ Kabuto=1 -General\ Tazri=1 -Genesis=1 -Genju\ of\ the\ Cedars=1 -Genju\ of\ the\ Falls=1 -Genju\ of\ the\ Fens=1 -Genju\ of\ the\ Fields=1 -Genju\ of\ the\ Realm=1 -Genju\ of\ the\ Spires=1 -Geosurge=1 -Geralf's\ Mindcrusher=1 -Gerrard's\ Battle\ Cry=1 -Gerrard's\ Command=1 -Gerrard's\ Irregulars=1 -Geth's\ Grimoire=1 -Geyserfield\ Stalker=1 -Ghastbark\ Twins=1 -Ghastly\ Remains=1 -Ghazbn\ Ogre=1 -Ghirapur\ Gearcrafter=1 -Ghirapur\ Guide=1 -Ghirapur\ Orrery=1 -Ghirapur\ Osprey=1 -Ghitu\ Chronicler=1 -Ghitu\ Fire-Eater=1 -Ghitu\ Fire=1 -Ghitu\ Firebreathing=1 -Ghitu\ Journeymage=1 -Ghitu\ Slinger=1 -Ghitu\ War\ Cry=1 -Ghor-Clan\ Bloodscale=1 -Ghor-Clan\ Rampager=1 -Ghost-Lit\ Raider=1 -Ghost-Lit\ Redeemer=1 -Ghost-Lit\ Stalker=1 -Ghost-Lit\ Warder=1 -Ghost\ Council\ of\ Orzhova=1 -Ghost\ Ship=1 -Ghost\ Tactician=1 -Ghost\ Warden=1 -Ghostblade\ Eidolon=1 -Ghostfire=1 -Ghostfire\ Blade=1 -Ghostflame\ Sliver=1 -Ghostform=1 -Ghostly\ Changeling=1 -Ghostly\ Possession=1 -Ghostly\ Sentinel=1 -Ghostly\ Touch=1 -Ghostly\ Visit=1 -Ghostly\ Wings=1 -Ghosts\ of\ the\ Damned=1 -Ghosts\ of\ the\ Innocent=1 -Ghoul's\ Feast=1 -Ghoulcaller's\ Accomplice=1 -Ghoulcaller's\ Bell=1 -Ghoulflesh=1 -Ghoulsteed=1 -Giant's\ Ire=1 -Giant\ Badger=1 -Giant\ Caterpillar=1 -Giant\ Cockroach=1 -Giant\ Crab=1 -Giant\ Growth=1 -Giant\ Harbinger=1 -Giant\ Mantis=1 -Giant\ Octopus=1 -Giant\ Oyster=1 -Giant\ Scorpion=1 -Giant\ Solifuge=1 -Giant\ Spectacle=1 -Giant\ Spider=1 -Giant\ Strength=1 -Giant\ Tortoise=1 -Giant\ Trap\ Door\ Spider=1 -Giantbaiting=1 -Gibbering\ Descent=1 -Gibbering\ Fiend=1 -Gibbering\ Hyenas=1 -Gideon's\ Avenger=1 -Gideon's\ Defeat=1 -Gideon's\ Intervention=1 -Gideon's\ Lawkeeper=1 -Gideon's\ Phalanx=1 -Gideon's\ Reproach=1 -Gift\ of\ Granite=1 -Gift\ of\ Growth=1 -Gift\ of\ Immortality=1 -Gift\ of\ Orzhova=1 -Gift\ of\ Strength=1 -Gift\ of\ Tusks=1 -Gift\ of\ the\ Deity=1 -Gift\ of\ the\ Gargantuan=1 -Gigapede=1 -Gild=1 -Gilded\ Cerodon=1 -Gilded\ Light=1 -Gilded\ Lotus=1 -Gilded\ Sentinel=1 -Gilt-Leaf\ Ambush=1 -Gilt-Leaf\ Seer=1 -Gilt-Leaf\ Winnower=1 -Giltgrove\ Stalker=1 -Giltspire\ Avenger=1 -Gisa's\ Bidding=1 -Give\ //\ Take=1 -Give\ No\ Ground=1 -Glacial\ Crevasses=1 -Glacial\ Plating=1 -Glacial\ Ray=1 -Glacial\ Stalker=1 -Glacial\ Wall=1 -Glade\ Gnarr=1 -Glade\ Watcher=1 -Gladehart\ Cavalry=1 -Glamer\ Spinners=1 -Glamerdye=1 -Glare\ of\ Heresy=1 -Glare\ of\ Subdual=1 -Glarecaster=1 -Glaring\ Aegis=1 -Glaring\ Spotlight=1 -Glass\ Asp=1 -Glass\ Golem=1 -Glassblower's\ Puzzleknot=1 -Glassdust\ Hulk=1 -Glaze\ Fiend=1 -Gleam\ of\ Authority=1 -Gleam\ of\ Battle=1 -Gleam\ of\ Resistance=1 -Gleaming\ Barrier=1 -Gleancrawler=1 -Glen\ Elendra\ Liege=1 -Glimmerdust\ Nap=1 -Glimmerpoint\ Stag=1 -Glimmerpost=1 -Glimpse\ the\ Future=1 -Glimpse\ the\ Sun\ God=1 -Glint-Eye\ Nephilim=1 -Glint-Sleeve\ Artisan=1 -Glint=1 -Glint\ Hawk\ Idol=1 -Glintwing\ Invoker=1 -Glissa's\ Scorn=1 -Glissa\ Sunseeker=1 -Glistening\ Oil=1 -Glitterfang=1 -Glittering\ Lion=1 -Gloom\ Surgeon=1 -Gloomhunter=1 -Gloomwidow's\ Feast=1 -Gloomwidow=1 -Glorifier\ of\ Dusk=1 -Glorious\ Anthem=1 -Glorious\ Charge=1 -Glory-Bound\ Initiate=1 -Glory\ Seeker=1 -Glory\ of\ Warfare=1 -Glowering\ Rogon=1 -Glowing\ Anemone=1 -Gluttonous\ Slime=1 -Gluttonous\ Zombie=1 -Glyph\ Keeper=1 -Gnarled\ Effigy=1 -Gnarled\ Mass=1 -Gnarled\ Scarhide=1 -Gnarlid\ Pack=1 -Gnarlroot\ Trapper=1 -Gnarlwood\ Dryad=1 -Gnathosaur=1 -Gnawing\ Zombie=1 -Goatnapper=1 -Gobbling\ Ooze=1 -Gobhobbler\ Rats=1 -Goblin\ Archaeologist=1 -Goblin\ Arsonist=1 -Goblin\ Artillery=1 -Goblin\ Assault=1 -Goblin\ Bangchuckers=1 -Goblin\ Barrage=1 -Goblin\ Battle\ Jester=1 -Goblin\ Boom\ Keg=1 -Goblin\ Brigand=1 -Goblin\ Bully=1 -Goblin\ Burrows=1 -Goblin\ Cadets=1 -Goblin\ Cannon=1 -Goblin\ Chariot=1 -Goblin\ Commando=1 -Goblin\ Deathraiders=1 -Goblin\ Digging\ Team=1 -Goblin\ Diplomats=1 -Goblin\ Elite\ Infantry=1 -Goblin\ Fire\ Fiend=1 -Goblin\ Firebug=1 -Goblin\ Fireslinger=1 -Goblin\ Firestarter=1 -Goblin\ Flectomancer=1 -Goblin\ Freerunner=1 -Goblin\ Furrier=1 -Goblin\ Gardener=1 -Goblin\ Gaveleer=1 -Goblin\ General=1 -Goblin\ Glider=1 -Goblin\ Glory\ Chaser=1 -Goblin\ Goon=1 -Goblin\ Grappler=1 -Goblin\ Grenadiers=1 -Goblin\ Kaboomist=1 -Goblin\ Legionnaire=1 -Goblin\ Machinist=1 -Goblin\ Medics=1 -Goblin\ Mountaineer=1 -Goblin\ Outlander=1 -Goblin\ Patrol=1 -Goblin\ Piker=1 -Goblin\ Pyromancer=1 -Goblin\ Raider=1 -Goblin\ Rally=1 -Goblin\ Razerunners=1 -Goblin\ Rimerunner=1 -Goblin\ Roughrider=1 -Goblin\ Settler=1 -Goblin\ Shortcutter=1 -Goblin\ Shrine=1 -Goblin\ Ski\ Patrol=1 -Goblin\ Sky\ Raider=1 -Goblin\ Snowman=1 -Goblin\ Spelunkers=1 -Goblin\ Striker=1 -Goblin\ Swine-Rider=1 -Goblin\ Taskmaster=1 -Goblin\ Test\ Pilot=1 -Goblin\ Trailblazer=1 -Goblin\ Trenches=1 -Goblin\ Turncoat=1 -Goblin\ War\ Buggy=1 -Goblin\ War\ Paint=1 -Goblin\ War\ Wagon=1 -Goblin\ Warchief=1 -Goblins\ of\ the\ Flarg=1 -Goblinslide=1 -God-Favored\ General=1 -God-Pharaoh's\ Faithful=1 -Godhead\ of\ Awe=1 -Godo's\ Irregulars=1 -Gods'\ Eye,\ Gate\ to\ the\ Reikai=1 -Gods\ Willing=1 -Godtoucher=1 -Goham\ Djinn=1 -Gold-Forged\ Sentinel=1 -Gold\ Myr=1 -Golden\ Demise=1 -Golden\ Hind=1 -Golden\ Urn=1 -Golden\ Wish=1 -Goldenglow\ Moth=1 -Goldenhide\ Ox=1 -Goldmeadow\ Dodger=1 -Goldmeadow\ Harrier=1 -Goldmeadow\ Stalwart=1 -Goldnight\ Commander=1 -Goldnight\ Redeemer=1 -Golem's\ Heart=1 -Golem-Skin\ Gauntlets=1 -Golem\ Foundry=1 -Golgari\ Cluestone=1 -Golgari\ Germination=1 -Golgari\ Guildgate=1 -Golgari\ Guildmage=1 -Golgari\ Keyrune=1 -Golgari\ Rotwurm=1 -Golgari\ Signet=1 -Goliath\ Beetle=1 -Goliath\ Sphinx=1 -Gomazoa=1 -Gone\ Missing=1 -Gonti's\ Aether\ Heart=1 -Gonti's\ Machinations=1 -Gore-House\ Chainwalker=1 -Gore\ Swine=1 -Gorehorn\ Minotaurs=1 -Goretusk\ Firebeast=1 -Gorgon's\ Head=1 -Gorgon\ Flail=1 -Gorgon\ Recluse=1 -Gorilla\ Titan=1 -Gorilla\ War\ Cry=1 -Gorilla\ Warrior=1 -Goring\ Ceratops=1 -Gossamer\ Chains=1 -Gossamer\ Phantasm=1 -Govern\ the\ Guildless=1 -Grab\ the\ Reins=1 -Graceful\ Adept=1 -Graceful\ Antelope=1 -Graceful\ Reprieve=1 -Graf\ Harvest=1 -Graf\ Mole=1 -Graf\ Rats=1 -Grafted\ Exoskeleton=1 -Grand\ Melee=1 -Grand\ Warlord\ Radha=1 -Grandmother\ Sengir=1 -Granite\ Gargoyle=1 -Granite\ Grip=1 -Granite\ Shard=1 -Granitic\ Titan=1 -Grapeshot\ Catapult=1 -Grapple\ with\ the\ Past=1 -Grappling\ Hook=1 -Grasp\ of\ Darkness=1 -Grasp\ of\ Phantoms=1 -Grasp\ of\ the\ Hieromancer=1 -Grasping\ Dunes=1 -Grasping\ Scoundrel=1 -Grasslands=1 -Grave-Shell\ Scarab=1 -Grave\ Betrayal=1 -Grave\ Birthing=1 -Grave\ Peril=1 -Grave\ Servitude=1 -Gravebane\ Zombie=1 -Gravebind=1 -Graveblade\ Marauder=1 -Gravedigger=1 -Gravel\ Slinger=1 -Gravelgill\ Axeshark=1 -Gravelgill\ Duo=1 -Graven\ Abomination=1 -Graven\ Dominator=1 -Gravepurge=1 -Graverobber\ Spider=1 -Gravity\ Negator=1 -Gravity\ Well=1 -Graxiplon=1 -Graypelt\ Hunter=1 -Grazing\ Gladehart=1 -Grazing\ Kelpie=1 -Grazing\ Whiptail=1 -Great-Horn\ Krushok=1 -Great\ Hart=1 -Great\ Teacher's\ Decree=1 -Greatbow\ Doyen=1 -Greater\ Basilisk=1 -Greater\ Forgeling=1 -Greater\ Harvester=1 -Greater\ Mossdog=1 -Greater\ Sandwurm=1 -Greatsword=1 -Greel's\ Caress=1 -Greenhilt\ Trainee=1 -Greenseeker=1 -Greenside\ Watcher=1 -Greenweaver\ Druid=1 -Greenwheel\ Liberator=1 -Gremlin\ Infestation=1 -Grenzo,\ Dungeon\ Warden=1 -Grid\ Monitor=1 -Gridlock=1 -Griffin\ Dreamfinder=1 -Griffin\ Guide=1 -Griffin\ Sentinel=1 -Grifter's\ Blade=1 -Grim\ Affliction=1 -Grim\ Captain's\ Call=1 -Grim\ Contest=1 -Grim\ Discovery=1 -Grim\ Flowering=1 -Grim\ Haruspex=1 -Grim\ Poppet=1 -Grim\ Reminder=1 -Grim\ Return=1 -Grim\ Roustabout=1 -Grim\ Strider=1 -Grimoire\ Thief=1 -Grind\ //\ Dust=1 -Grindclock=1 -Grinning\ Demon=1 -Grinning\ Ignus=1 -Grinning\ Totem=1 -Grip\ of\ Amnesia=1 -Grip\ of\ Desolation=1 -Grip\ of\ the\ Roil=1 -Griptide=1 -Grisly\ Spectacle=1 -Grisly\ Survivor=1 -Grisly\ Transformation=1 -Gristle\ Grinner=1 -Gristleback=1 -Grixis\ Battlemage=1 -Grixis\ Charm=1 -Grixis\ Grimblade=1 -Grixis\ Illusionist=1 -Grixis\ Slavedriver=1 -Grixis\ Sojourners=1 -Grizzled\ Angler=1 -Grizzled\ Leotau=1 -Grizzled\ Outcasts=1 -Grizzly\ Bears=1 -Grizzly\ Fate=1 -Groffskithur=1 -Grollub=1 -Grotag\ Thrasher=1 -Grotesque\ Mutation=1 -Ground\ Assault=1 -Ground\ Rift=1 -Groundling\ Pouncer=1 -Groundskeeper=1 -Grove\ Rumbler=1 -Grove\ of\ the\ Guardian=1 -Grovetender\ Druids=1 -Grow\ from\ the\ Ashes=1 -Growing\ Ranks=1 -Grozoth=1 -Gruesome\ Deformity=1 -Gruesome\ Encore=1 -Gruesome\ Fate=1 -Gruesome\ Slaughter=1 -Grunn,\ the\ Lonely\ King=1 -Gruul\ Charm=1 -Gruul\ Cluestone=1 -Gruul\ Guildgate=1 -Gruul\ Guildmage=1 -Gruul\ Keyrune=1 -Gruul\ Nodorog=1 -Gruul\ Ragebeast=1 -Gruul\ Scrapper=1 -Gruul\ War\ Chant=1 -Gruul\ War\ Plow=1 -Gryff's\ Boon=1 -Guan\ Yu's\ 1,000-Li\ March=1 -Guan\ Yu,\ Sainted\ Warrior=1 -Guard\ Dogs=1 -Guard\ Duty=1 -Guard\ Gomazoa=1 -Guardian\ Automaton=1 -Guardian\ Seraph=1 -Guardian\ Shield-Bearer=1 -Guardian\ Zendikon=1 -Guardian\ of\ Pilgrims=1 -Guardian\ of\ Solitude=1 -Guardian\ of\ Tazeem=1 -Guardian\ of\ the\ Ages=1 -Guardian\ of\ the\ Gateless=1 -Guardians\ of\ Akrasa=1 -Guardians\ of\ Koilos=1 -Guardians\ of\ Meletis=1 -Guerrilla\ Tactics=1 -Guild\ Feud=1 -Guildscorn\ Ward=1 -Guile=1 -Guilty\ Conscience=1 -Guma=1 -Gurmag\ Drowner=1 -Gurmag\ Swiftwing=1 -Gust-Skimmer=1 -Gust\ Walker=1 -Gustcloak\ Cavalier=1 -Gustcloak\ Runner=1 -Gustcloak\ Savior=1 -Gustcloak\ Sentinel=1 -Gustha's\ Scepter=1 -Gustrider\ Exuberant=1 -Gutter\ Grime=1 -Gutter\ Skulk=1 -Gutwrencher\ Oni=1 -Guul\ Draz\ Assassin=1 -Guul\ Draz\ Overseer=1 -Guul\ Draz\ Specter=1 -Gwafa\ Hazid,\ Profiteer=1 -Gwyllion\ Hedge-Mage=1 -Haazda\ Shield\ Mate=1 -Hada\ Spy\ Patrol=1 -Hag\ Hedge-Mage=1 -Hagra\ Crocodile=1 -Hagra\ Diabolist=1 -Hagra\ Sharpshooter=1 -Hail\ of\ Arrows=1 -Hair-Strung\ Koto=1 -Halam\ Djinn=1 -Halcyon\ Glaze=1 -Halimar\ Excavator=1 -Halimar\ Tidecaller=1 -Halimar\ Wavewatch=1 -Hallar,\ the\ Firefletcher=1 -Hallowed\ Moonlight=1 -Halo\ Hunter=1 -Hamlet\ Captain=1 -Hamletback\ Goliath=1 -Hammer\ of\ Bogardan=1 -Hammer\ of\ Purphoros=1 -Hammerfist\ Giant=1 -Hammerhand=1 -Hammerhead\ Shark=1 -Hammerheim=1 -Hammerheim\ Deadeye=1 -Hana\ Kami=1 -Hanabi\ Blast=1 -Hand\ of\ Cruelty=1 -Hand\ of\ Emrakul=1 -Hand\ of\ Justice=1 -Hand\ of\ Silumgar=1 -Hand\ of\ the\ Praetors=1 -Hand\ to\ Hand=1 -Hands\ of\ Binding=1 -Hanweir\ Battlements=1 -Hanweir\ Lancer=1 -Hanweir\ Militia\ Captain=1 -Hanweir\ Watchkeep=1 -Hapatra's\ Mark=1 -Hapatra,\ Vizier\ of\ Poisons=1 -Haphazard\ Bombardment=1 -Harbinger\ of\ the\ Hunt=1 -Harbor\ Bandit=1 -Harbor\ Guardian=1 -Harbor\ Serpent=1 -Hardened\ Berserker=1 -Hardy\ Veteran=1 -Harm's\ Way=1 -Harmattan\ Efreet=1 -Harmless\ Offering=1 -Harmonic\ Convergence=1 -Harness\ the\ Storm=1 -Harpoon\ Sniper=1 -Harrier\ Griffin=1 -Harrier\ Naga=1 -Harrow=1 -Harrowing\ Journey=1 -Harsh\ Deceiver=1 -Harsh\ Scrutiny=1 -Haru-Onna=1 -Harvest\ Gwyllion=1 -Harvest\ Hand=1 -Harvest\ Mage=1 -Harvest\ Season=1 -Harvester\ Troll=1 -Harvestguard\ Alseids=1 -Hasran\ Ogress=1 -Hatchet\ Bully=1 -Hate\ Weaver=1 -Hateflayer=1 -Haunted\ Angel=1 -Haunted\ Cadaver=1 -Haunted\ Cloak=1 -Haunted\ Dead=1 -Haunted\ Guardian=1 -Haunted\ Plate\ Mail=1 -Haunter\ of\ Nightveil=1 -Haunting\ Apparition=1 -Haunting\ Echoes=1 -Haunting\ Hymn=1 -Havengul\ Runebinder=1 -Havengul\ Skaab=1 -Havengul\ Vampire=1 -Havenwood\ Battleground=1 -Havenwood\ Wurm=1 -Havoc\ Demon=1 -Havoc\ Devils=1 -Havoc\ Festival=1 -Havoc\ Sower=1 -Hawkeater\ Moth=1 -Hazardous\ Conditions=1 -Haze\ Frog=1 -Haze\ of\ Pollen=1 -Hazerider\ Drake=1 -Hazezon\ Tamar=1 -Hazoret's\ Favor=1 -Hazoret's\ Monument=1 -Hazoret's\ Undying\ Fury=1 -Hazy\ Homunculus=1 -He\ Who\ Hungers=1 -Head\ Games=1 -Headless\ Skaab=1 -Headstrong\ Brute=1 -Headwater\ Sentries=1 -Heal=1 -Healer's\ Headdress=1 -Healer\ of\ the\ Pride=1 -Healing\ Grace=1 -Healing\ Hands=1 -Healing\ Leaves=1 -Healing\ Salve=1 -Heap\ Doll=1 -Heart-Piercer\ Bow=1 -Heart-Piercer\ Manticore=1 -Heart\ Warden=1 -Hearth\ Kami=1 -Hearthfire\ Hobgoblin=1 -Heartlash\ Cinder=1 -Heartless\ Pillage=1 -Heartmender=1 -Heartseeker=1 -Heartstabber\ Mosquito=1 -Heartwood\ Dryad=1 -Heartwood\ Giant=1 -Heartwood\ Shard=1 -Heartwood\ Treefolk=1 -Heat\ Ray=1 -Heat\ Shimmer=1 -Heat\ Wave=1 -Heat\ of\ Battle=1 -Heaven\ //\ Earth=1 -Heavy\ Arbalest=1 -Heavy\ Ballista=1 -Heavy\ Fog=1 -Heavy\ Infantry=1 -Heavy\ Mattock=1 -Hecatomb=1 -Heckling\ Fiends=1 -Hedonist's\ Trove=1 -Hedron-Field\ Purists=1 -Hedron\ Alignment=1 -Hedron\ Archive=1 -Hedron\ Blade=1 -Hedron\ Crawler=1 -Hedron\ Matrix=1 -Hedron\ Rover=1 -Hedron\ Scrabbler=1 -Heed\ the\ Mists=1 -Heidar,\ Rimewind\ Master=1 -Heir\ of\ Falkenrath=1 -Heir\ of\ the\ Wilds=1 -Heirs\ of\ Stromkirk=1 -Hekma\ Sentinels=1 -Heliod's\ Emissary=1 -Helium\ Squirter=1 -Hellcarver\ Demon=1 -Helldozer=1 -Hellhole\ Flailer=1 -Hellhole\ Rats=1 -Hellion\ Crucible=1 -Hellion\ Eruption=1 -Hellkite\ Charger=1 -Hellkite\ Hatchling=1 -Hellraiser\ Goblin=1 -Helm\ of\ the\ Ghastlord=1 -Helm\ of\ the\ Gods=1 -Helm\ of\ the\ Host=1 -Hematite\ Golem=1 -Henchfiend\ of\ Ukor=1 -Henge\ Guardian=1 -Herald\ of\ Anafenza=1 -Herald\ of\ Faith=1 -Herald\ of\ Kozilek=1 -Herald\ of\ Secret\ Streams=1 -Herald\ of\ Torment=1 -Herald\ of\ the\ Fair=1 -Herald\ of\ the\ Host=1 -Herald\ of\ the\ Pantheon=1 -Herbal\ Poultice=1 -Herdchaser\ Dragon=1 -Heretic's\ Punishment=1 -Hermetic\ Study=1 -Hermit\ Druid=1 -Hermit\ of\ the\ Natterknolls=1 -Hero's\ Demise=1 -Hero's\ Resolve=1 -Hero\ of\ Goma\ Fada=1 -Hero\ of\ Iroas=1 -Hero\ of\ Leina\ Tower=1 -Heroes'\ Bane=1 -Heroes'\ Podium=1 -Heroes'\ Reunion=1 -Heroes\ Remembered=1 -Heroic\ Defiance=1 -Heroic\ Reinforcements=1 -Heron's\ Grace\ Champion=1 -Hesitation=1 -Hewed\ Stone\ Retainers=1 -Hex=1 -Hibernation's\ End=1 -Hidden\ Ancients=1 -Hidden\ Gibbons=1 -Hidden\ Guerrillas=1 -Hidden\ Herbalists=1 -Hidden\ Horror=1 -Hidden\ Stockpile=1 -Hideous\ End=1 -Hideous\ Laughter=1 -Hideous\ Visage=1 -Hidetsugu's\ Second\ Rite=1 -Hieroglyphic\ Illumination=1 -Hieromancer's\ Cage=1 -Hierophant's\ Chalice=1 -High\ Ground=1 -High\ Priest\ of\ Penance=1 -High\ Sentinels\ of\ Arashin=1 -Highborn\ Ghoul=1 -Highland\ Berserker=1 -Highland\ Game=1 -Highland\ Lake=1 -Highland\ Weald=1 -Highspire\ Artisan=1 -Highspire\ Infusion=1 -Highspire\ Mantis=1 -Hightide\ Hermit=1 -Highway\ Robber=1 -Higure,\ the\ Still\ Wind=1 -Hijack=1 -Hikari,\ Twilight\ Guardian=1 -Hill\ Giant=1 -Hinder=1 -Hindering\ Light=1 -Hindering\ Touch=1 -Hindervines=1 -Hint\ of\ Insanity=1 -Hinterland\ Drake=1 -Hinterland\ Hermit=1 -Hinterland\ Logger=1 -Hired\ Blade=1 -Hired\ Giant=1 -Hired\ Muscle=1 -Hired\ Torturer=1 -Hisoka's\ Guard=1 -Hisoka,\ Minamo\ Sensei=1 -Hissing\ Iguanar=1 -Hissing\ Miasma=1 -Hit\ //\ Run=1 -Hitchclaw\ Recluse=1 -Hivestone=1 -Hivis\ of\ the\ Scale=1 -Hixus,\ Prison\ Warden=1 -Hoard-Smelter\ Dragon=1 -Hoarder's\ Greed=1 -Hoarding\ Dragon=1 -Hobgoblin\ Dragoon=1 -Hold\ at\ Bay=1 -Hold\ the\ Gates=1 -Hold\ the\ Line=1 -Holdout\ Settlement=1 -Holistic\ Wisdom=1 -Hollow\ Dogs=1 -Hollow\ Specter=1 -Hollowborn\ Barghest=1 -Hollowhenge\ Spirit=1 -Hollowsage=1 -Holy\ Mantle=1 -Holy\ Strength=1 -Homarid\ Explorer=1 -Homarid\ Spawning\ Bed=1 -Homing\ Lightning=1 -Honden\ of\ Cleansing\ Fire=1 -Honden\ of\ Infinite\ Rage=1 -Honden\ of\ Life's\ Web=1 -Honden\ of\ Night's\ Reach=1 -Honden\ of\ Seeing\ Winds=1 -Honed\ Khopesh=1 -Honor-Worn\ Shaku=1 -Honor\ Guard=1 -Honorable\ Passage=1 -Honored\ Crop-Captain=1 -Honored\ Hierarch=1 -Honored\ Hydra=1 -Hooded\ Brawler=1 -Hooded\ Horror=1 -Hooded\ Kavu=1 -Hoof\ Skulkin=1 -Hoofprints\ of\ the\ Stag=1 -Hope\ Against\ Hope=1 -Hope\ Charm=1 -Hope\ Tender=1 -Hope\ and\ Glory=1 -Hopeful\ Eidolon=1 -Hopping\ Automaton=1 -Horde\ of\ Boggarts=1 -Horde\ of\ Notions=1 -Hordeling\ Outburst=1 -Horizon\ Chimera=1 -Horizon\ Drake=1 -Horizon\ Scholar=1 -Horizon\ Seed=1 -Horizon\ Spellbomb=1 -Horncaller's\ Chant=1 -Horned\ Cheetah=1 -Horned\ Helm=1 -Horned\ Turtle=1 -Hornet\ Cannon=1 -Hornet\ Harasser=1 -Hornet\ Sting=1 -Hornswoggle=1 -Horobi's\ Whisper=1 -Horrible\ Hordes=1 -Horribly\ Awry=1 -Horrifying\ Revelation=1 -Horror\ of\ Horrors=1 -Horror\ of\ the\ Broken\ Lands=1 -Horseshoe\ Crab=1 -Hostile\ Minotaur=1 -Hostile\ Realm=1 -Hostility=1 -Hot\ Soup=1 -Hound\ of\ the\ Farbogs=1 -Hour\ of\ Eternity=1 -Hour\ of\ Need=1 -Hour\ of\ Revelation=1 -Hover\ Barrier=1 -Hoverguard\ Observer=1 -Hoverguard\ Sweepers=1 -Hovermyr=1 -Howl\ from\ Beyond=1 -Howl\ of\ the\ Night\ Pack=1 -Howlgeist=1 -Howling\ Gale=1 -Howling\ Golem=1 -Howlpack\ Resurgence=1 -Howlpack\ Wolf=1 -Howltooth\ Hollow=1 -Hubris=1 -Hulking\ Cyclops=1 -Hulking\ Devil=1 -Hum\ of\ the\ Radix=1 -Human\ Frailty=1 -Humble=1 -Humble\ Budoka=1 -Humble\ the\ Brute=1 -Humbler\ of\ Mortals=1 -Hundred-Handed\ One=1 -Hundred-Talon\ Strike=1 -Hundroog=1 -Hunger\ of\ the\ Nim=1 -Hungering\ Yeti=1 -Hungry\ Flames=1 -Hungry\ Mist=1 -Hungry\ Spriggan=1 -Hunt\ the\ Hunter=1 -Hunt\ the\ Weak=1 -Hunted\ Dragon=1 -Hunted\ Ghoul=1 -Hunted\ Lammasu=1 -Hunted\ Phantasm=1 -Hunted\ Troll=1 -Hunted\ Wumpus=1 -Hunter's\ Ambush=1 -Hunter's\ Insight=1 -Hunter's\ Prowess=1 -Hunter\ of\ Eyeblights=1 -Hunters'\ Feast=1 -Hunting\ Cheetah=1 -Hunting\ Drake=1 -Hunting\ Moa=1 -Hunting\ Pack=1 -Hunting\ Triad=1 -Hunting\ Wilds=1 -Hurloon\ Minotaur=1 -Hurloon\ Shaman=1 -Hurly-Burly=1 -Hurricane=1 -Hush=1 -Hussar\ Patrol=1 -Hydra\ Broodmaster=1 -Hydroform=1 -Hydrolash=1 -Hydromorph\ Guardian=1 -Hydromorph\ Gull=1 -Hydrosurge=1 -Hyena\ Pack=1 -Hymn\ of\ Rebirth=1 -Hypersonic\ Dragon=1 -Hypervolt\ Grasp=1 -Hypnotic\ Cloud=1 -Hypnotic\ Siren=1 -Hypnotic\ Specter=1 -Hypochondria=1 -Hysterical\ Blindness=1 -Hythonia\ the\ Cruel=1 -Ib\ Halfheart,\ Goblin\ Tactician=1 -Icatian\ Crier=1 -Icatian\ Lieutenant=1 -Icatian\ Priest=1 -Icatian\ Town=1 -Ice\ Cage=1 -Ice\ Cauldron=1 -Ice\ Floe=1 -Ice\ Over=1 -Iceberg=1 -Icefall=1 -Icefeather\ Aven=1 -Ichor\ Explosion=1 -Ichor\ Rats=1 -Ichor\ Slick=1 -Icy\ Blast=1 -Icy\ Manipulator=1 -Icy\ Prison=1 -Identity\ Crisis=1 -Identity\ Thief=1 -Idle\ Thoughts=1 -Ifh-Bff\ Efreet=1 -Igneous\ Golem=1 -Igneous\ Pouncer=1 -Ignite\ Disorder=1 -Ignite\ Memories=1 -Ignoble\ Soldier=1 -Ignorant\ Bliss=1 -Iizuka\ the\ Ruthless=1 -Ikiral\ Outrider=1 -Illness\ in\ the\ Ranks=1 -Illuminate=1 -Illuminated\ Folio=1 -Illuminated\ Wings=1 -Illusion\ //\ Reality=1 -Illusionary\ Forces=1 -Illusionary\ Servant=1 -Illusionist's\ Bracers=1 -Illusory\ Ambusher=1 -Illusory\ Angel=1 -Illusory\ Demon=1 -Illusory\ Gains=1 -Illusory\ Wrappings=1 -Imagecrafter=1 -Imaginary\ Pet=1 -Imaginary\ Threats=1 -Imi\ Statue=1 -Immaculate\ Magistrate=1 -Imminent\ Doom=1 -Immobilizer\ Eldrazi=1 -Immobilizing\ Ink=1 -Immolating\ Glare=1 -Immolation=1 -Immortal\ Coil=1 -Immortal\ Servitude=1 -Imp's\ Mischief=1 -Impale=1 -Impatience=1 -Impeccable\ Timing=1 -Impelled\ Giant=1 -Imperial\ Aerosaur=1 -Imperial\ Ceratops=1 -Imperial\ Hellkite=1 -Imperial\ Lancer=1 -Imperiosaur=1 -Impetuous\ Devils=1 -Impetuous\ Sunchaser=1 -Implement\ of\ Combustion=1 -Implement\ of\ Examination=1 -Implement\ of\ Ferocity=1 -Implement\ of\ Improvement=1 -Implement\ of\ Malice=1 -Imprisoned\ in\ the\ Moon=1 -Impromptu\ Raid=1 -Improvised\ Armor=1 -In\ Bolas's\ Clutches=1 -In\ Oketra's\ Name=1 -In\ the\ Web\ of\ War=1 -Inaction\ Injunction=1 -Iname,\ Death\ Aspect=1 -Iname,\ Life\ Aspect=1 -Incandescent\ Soulstoke=1 -Incendiary\ Command=1 -Incendiary\ Sabotage=1 -Incite=1 -Incite\ Hysteria=1 -Incite\ War=1 -Incorrigible\ Youths=1 -Incremental\ Blight=1 -Incremental\ Growth=1 -Incubator\ Drone=1 -Incurable\ Ogre=1 -Incursion\ Specialist=1 -Indebted\ Samurai=1 -Indentured\ Oaf=1 -Indestructibility=1 -Index=1 -Indigo\ Faerie=1 -Indomitable\ Ancients=1 -Indomitable\ Archangel=1 -Indrik\ Stomphowler=1 -Induce\ Despair=1 -Induce\ Paranoia=1 -Induced\ Amnesia=1 -Indulgent\ Aristocrat=1 -Indulgent\ Tormentor=1 -Inertia\ Bubble=1 -Inexorable\ Blob=1 -Inexorable\ Tide=1 -Infantry\ Veteran=1 -Infected\ Vermin=1 -Infectious\ Horror=1 -Infectious\ Host=1 -Infectious\ Rage=1 -Infernal\ Harvest=1 -Infernal\ Kirin=1 -Infernal\ Scarring=1 -Inferno=1 -Inferno\ Elemental=1 -Inferno\ Fist=1 -Inferno\ Jet=1 -Inferno\ Trap=1 -Infest=1 -Infested\ Roothold=1 -Infiltration\ Lens=1 -Infiltrator's\ Magemark=1 -Infiltrator\ il-Kor=1 -Infinite\ Obliteration=1 -Infinite\ Reflection=1 -Infuse=1 -Infuse\ with\ the\ Elements=1 -Infused\ Arrows=1 -Ingenious\ Skaab=1 -Inheritance=1 -Initiate's\ Companion=1 -Initiate\ of\ Blood=1 -Ink-Treader\ Nephilim=1 -Ink\ Dissolver=1 -Inkfathom\ Divers=1 -Inkfathom\ Witch=1 -Inner-Chamber\ Guard=1 -Inner-Flame\ Acolyte=1 -Inner-Flame\ Igniter=1 -Inner\ Struggle=1 -Innocence\ Kami=1 -Inquisitor's\ Flail=1 -Inquisitor's\ Ox=1 -Insatiable\ Gorgers=1 -Insatiable\ Harpy=1 -Insidious\ Will=1 -Insight=1 -Insolence=1 -Inspiration=1 -Inspired\ Charge=1 -Inspired\ Sprite=1 -Inspiring\ Captain=1 -Inspiring\ Cleric=1 -Inspirit=1 -Instigator\ Gang=1 -Instill\ Furor=1 -Instill\ Infection=1 -Insult\ //\ Injury=1 -Intervene=1 -Intet,\ the\ Dreamer=1 -Intimidation=1 -Intimidation\ Bolt=1 -Intimidator\ Initiate=1 -Into\ Thin\ Air=1 -Into\ the\ Core=1 -Into\ the\ Fray=1 -Into\ the\ Maw\ of\ Hell=1 -Into\ the\ Void=1 -Intrepid\ Hero=1 -Intrepid\ Provisioner=1 -Inundate=1 -Invader\ Parasite=1 -Invasive\ Species=1 -Invasive\ Surgery=1 -Inventor's\ Apprentice=1 -Inventor's\ Goggles=1 -Invert\ the\ Skies=1 -Inverter\ of\ Truth=1 -Invigorate=1 -Invigorated\ Rampage=1 -Invigorating\ Boon=1 -Invigorating\ Falls=1 -Invincible\ Hymn=1 -Invisibility=1 -Invoke\ the\ Divine=1 -Invoke\ the\ Firemind=1 -Invulnerability=1 -Ion\ Storm=1 -Iona's\ Blessing=1 -Ior\ Ruin\ Expedition=1 -Ire\ Shaman=1 -Ire\ of\ Kaminari=1 -Iridescent\ Drake=1 -Iroas's\ Champion=1 -Iron-Barb\ Hellion=1 -Iron-Heart\ Chimera=1 -Iron\ Lance=1 -Iron\ League\ Steed=1 -Iron\ Myr=1 -Iron\ Star=1 -Iron\ Tusk\ Elephant=1 -Iron\ Will=1 -Ironclad\ Revolutionary=1 -Ironclad\ Slayer=1 -Ironclaw\ Buzzardiers=1 -Ironfist\ Crusher=1 -Ironhoof\ Ox=1 -Irontread\ Crusher=1 -Ironwright's\ Cleansing=1 -Irradiate=1 -Irresistible\ Prey=1 -Isao,\ Enlightened\ Bushi=1 -Ishi-Ishi,\ Akki\ Crackshot=1 -Island=1 -Isleback\ Spawn=1 -Isolation\ Cell=1 -Isolation\ Zone=1 -Isperia's\ Skywatch=1 -Isperia\ the\ Inscrutable=1 -It\ of\ the\ Horrid\ Swarm=1 -Ith,\ High\ Arcanist=1 -Ivory\ Crane\ Netsuke=1 -Ivory\ Cup=1 -Ivory\ Gargoyle=1 -Ivory\ Giant=1 -Ivory\ Guardians=1 -Ivory\ Tower=1 -Ivorytusk\ Fortress=1 -Ivy\ Dancer=1 -Ivy\ Elemental=1 -Ivy\ Lane\ Denizen=1 -Ivy\ Seer=1 -Iwamori\ of\ the\ Open\ Fist=1 -Ixalli's\ Diviner=1 -Ixalli's\ Keeper=1 -Ixidor's\ Will=1 -Ixidron=1 -Izzet\ Chemister=1 -Izzet\ Chronarch=1 -Izzet\ Cluestone=1 -Izzet\ Guildgate=1 -Izzet\ Guildmage=1 -Izzet\ Keyrune=1 -Jabari's\ Banner=1 -Jace's\ Ingenuity=1 -Jace's\ Mindseeker=1 -Jace's\ Sanctum=1 -Jace's\ Scrutiny=1 -Jackal\ Pup=1 -Jackalope\ Herd=1 -Jaddi\ Lifestrider=1 -Jaddi\ Offshoot=1 -Jade\ Bearer=1 -Jade\ Guardian=1 -Jade\ Idol=1 -Jade\ Mage=1 -Jade\ Monolith=1 -Jade\ Statue=1 -Jadecraft\ Artisan=1 -Jagged-Scar\ Archers=1 -Jagged\ Lightning=1 -Jagged\ Poppet=1 -Jagwasp\ Swarm=1 -Jalira,\ Master\ Polymorphist=1 -Jalum\ Tome=1 -Jamuraan\ Lion=1 -Janjeet\ Sentry=1 -Jar\ of\ Eyeballs=1 -Jarad's\ Orders=1 -Jareth,\ Leonine\ Titan=1 -Jasmine\ Seer=1 -Jawbone\ Skulkin=1 -Jaya's\ Immolating\ Inferno=1 -Jaya\ Ballard,\ Task\ Mage=1 -Jayemdae\ Tome=1 -Jedit\ Ojanen=1 -Jedit\ Ojanen\ of\ Efrava=1 -Jeering\ Instigator=1 -Jelenn\ Sphinx=1 -Jerrard\ of\ the\ Closed\ Fist=1 -Jeskai\ Ascendancy=1 -Jeskai\ Banner=1 -Jeskai\ Charm=1 -Jeskai\ Elder=1 -Jeskai\ Infiltrator=1 -Jeskai\ Student=1 -Jeskai\ Windscout=1 -Jester's\ Scepter=1 -Jeweled\ Torque=1 -Jhessian\ Balmgiver=1 -Jhessian\ Infiltrator=1 -Jhessian\ Lookout=1 -Jhessian\ Thief=1 -Jhessian\ Zombies=1 -Jhoira's\ Familiar=1 -Jhoira's\ Timebug=1 -Jhoira's\ Toolbox=1 -Jilt=1 -Jinxed\ Choker=1 -Jinxed\ Idol=1 -Jinxed\ Ring=1 -Jiwari,\ the\ Earth\ Aflame=1 -Jodah's\ Avenger=1 -Jodah,\ Archmage\ Eternal=1 -Johtull\ Wurm=1 -Join\ the\ Ranks=1 -Joiner\ Adept=1 -Jokulmorder=1 -Jolrael,\ Empress\ of\ Beasts=1 -Jolt=1 -Jolting\ Merfolk=1 -Joraga\ Auxiliary=1 -Joraga\ Bard=1 -Joraga\ Invocation=1 -Jori\ En,\ Ruin\ Diver=1 -Jorubai\ Murk\ Lurker=1 -Journey\ of\ Discovery=1 -Jousting\ Lance=1 -Joven's\ Ferrets=1 -Joyous\ Respite=1 -Judge\ Unworthy=1 -Judge\ of\ Currents=1 -Jugan,\ the\ Rising\ Star=1 -Juggernaut=1 -Juju\ Bubble=1 -Jukai\ Messenger=1 -Jund\ Battlemage=1 -Jund\ Charm=1 -Jund\ Sojourners=1 -Jungle\ Barrier=1 -Jungle\ Creeper=1 -Jungle\ Delver=1 -Jungle\ Shrine=1 -Jungle\ Weaver=1 -Jungle\ Wurm=1 -Jungleborn\ Pioneer=1 -Juniper\ Order\ Advocate=1 -Junk\ Golem=1 -Junktroller=1 -Junkyo\ Bell=1 -Jushi\ Apprentice=1 -Just\ Fate=1 -Just\ the\ Wind=1 -Juvenile\ Gloomwidow=1 -Juxtapose=1 -Jwar\ Isle\ Avenger=1 -Jwari\ Scuttler=1 -Jwari\ Shapeshifter=1 -Jtun\ Grunt=1 -Jtun\ Owl\ Keeper=1 -Kabira\ Vindicator=1 -Kaboom!=1 -Kabuto\ Moth=1 -Kaervek's\ Hex=1 -Kaervek's\ Torch=1 -Kaervek\ the\ Merciless=1 -Kagemaro's\ Clutch=1 -Kagemaro,\ First\ to\ Suffer=1 -Kaho,\ Minamo\ Historian=1 -Kaijin\ of\ the\ Vanishing\ Touch=1 -Kalastria\ Healer=1 -Kalastria\ Nightwatch=1 -Kaleidostone=1 -Kalonian\ Behemoth=1 -Kalonian\ Twingrove=1 -Kamahl's\ Desire=1 -Kamahl's\ Druidic\ Vow=1 -Kamahl's\ Sledge=1 -Kamahl,\ Pit\ Fighter=1 -Kami\ of\ Ancient\ Law=1 -Kami\ of\ Fire's\ Roar=1 -Kami\ of\ Lunacy=1 -Kami\ of\ Twisted\ Reflection=1 -Kami\ of\ the\ Honored\ Dead=1 -Kami\ of\ the\ Painted\ Road=1 -Kami\ of\ the\ Tended\ Garden=1 -Karametra's\ Acolyte=1 -Karametra's\ Favor=1 -Karn's\ Temporal\ Sundering=1 -Karplusan\ Giant=1 -Karplusan\ Strider=1 -Karplusan\ Wolverine=1 -Karplusan\ Yeti=1 -Karstoderm=1 -Kashi-Tribe\ Elite=1 -Kashi-Tribe\ Reaver=1 -Kashi-Tribe\ Warriors=1 -Katabatic\ Winds=1 -Kathari\ Bomber=1 -Kathari\ Remnant=1 -Kathari\ Screecher=1 -Kavu\ Aggressor=1 -Kavu\ Chameleon=1 -Kavu\ Climber=1 -Kavu\ Glider=1 -Kavu\ Howler=1 -Kavu\ Mauler=1 -Kavu\ Predator=1 -Kavu\ Primarch=1 -Kavu\ Recluse=1 -Kavu\ Runner=1 -Kazandu\ Refuge=1 -Kazandu\ Tuskcaller=1 -Kazarov,\ Sengir\ Pureblood=1 -Kazuul's\ Toll\ Collector=1 -Kazuul,\ Tyrant\ of\ the\ Cliffs=1 -Kazuul\ Warlord=1 -Kederekt\ Creeper=1 -Kederekt\ Leviathan=1 -Keeneye\ Aven=1 -Keening\ Apparition=1 -Keening\ Banshee=1 -Keening\ Stone=1 -Keeper\ of\ Kookus=1 -Keeper\ of\ Progenitus=1 -Keeper\ of\ the\ Beasts=1 -Keeper\ of\ the\ Dead=1 -Keeper\ of\ the\ Flame=1 -Keeper\ of\ the\ Lens=1 -Keeper\ of\ the\ Light=1 -Keeper\ of\ the\ Mind=1 -Keepsake\ Gorgon=1 -Kefnet's\ Monument=1 -Keiga,\ the\ Tide\ Star=1 -Keldon\ Berserker=1 -Keldon\ Champion=1 -Keldon\ Halberdier=1 -Keldon\ Mantle=1 -Keldon\ Megaliths=1 -Keldon\ Necropolis=1 -Keldon\ Overseer=1 -Keldon\ Raider=1 -Keldon\ Twilight=1 -Keldon\ Warcaller=1 -Keldon\ Warlord=1 -Kelinore\ Bat=1 -Kemba's\ Legion=1 -Kemba's\ Skyguard=1 -Kemuri-Onna=1 -Kentaro,\ the\ Smiling\ Cat=1 -Kessig\ Cagebreakers=1 -Kessig\ Dire\ Swine=1 -Kessig\ Forgemaster=1 -Kessig\ Prowler=1 -Kessig\ Recluse=1 -Key\ to\ the\ City=1 -Keymaster\ Rogue=1 -Kezzerdrix=1 -Khalni\ Gem=1 -Khenra\ Charioteer=1 -Khenra\ Eternal=1 -Khenra\ Scrapper=1 -Kheru\ Bloodsucker=1 -Kheru\ Dreadmaw=1 -Kheru\ Lich\ Lord=1 -Kheru\ Spellsnatcher=1 -Kiku's\ Shadow=1 -Kiku,\ Night's\ Flower=1 -Kill-Suit\ Cultist=1 -Kill\ Shot=1 -Killer\ Bees=1 -Killer\ Instinct=1 -Killer\ Whale=1 -Killing\ Glare=1 -Kiln\ Walker=1 -Kin-Tree\ Invocation=1 -Kin-Tree\ Warden=1 -Kindle=1 -Kindle\ the\ Carnage=1 -Kindled\ Fury=1 -Kindly\ Stranger=1 -King\ Cheetah=1 -King\ Crab=1 -King\ Macar,\ the\ Gold-Cursed=1 -Kingfisher=1 -Kingpin's\ Pet=1 -Kinjalli's\ Caller=1 -Kinsbaile\ Balloonist=1 -Kinsbaile\ Skirmisher=1 -Kinscaer\ Harpoonist=1 -Kiora's\ Dismissal=1 -Kiora's\ Follower=1 -Kird\ Chieftain=1 -Kiri-Onna=1 -Kiss\ of\ the\ Amesha=1 -Kite\ Shield=1 -Kitesail=1 -Kitesail\ Apprentice=1 -Kitesail\ Corsair=1 -Kitesail\ Scout=1 -Kithkin\ Armor=1 -Kithkin\ Daggerdare=1 -Kithkin\ Greatheart=1 -Kithkin\ Harbinger=1 -Kithkin\ Healer=1 -Kithkin\ Mourncaller=1 -Kithkin\ Spellduster=1 -Kithkin\ Zealot=1 -Kithkin\ Zephyrnaut=1 -Kitsune\ Bonesetter=1 -Kitsune\ Dawnblade=1 -Kitsune\ Diviner=1 -Kitsune\ Healer=1 -Kitsune\ Palliator=1 -Kitsune\ Riftwalker=1 -Kiyomaro,\ First\ to\ Stand=1 -Kjeldoran\ Elite\ Guard=1 -Kjeldoran\ Frostbeast=1 -Kjeldoran\ Gargoyle=1 -Kjeldoran\ Home\ Guard=1 -Kjeldoran\ Javelineer=1 -Kjeldoran\ Outpost=1 -Kjeldoran\ Outrider=1 -Kjeldoran\ Royal\ Guard=1 -Kjeldoran\ Skycaptain=1 -Kjeldoran\ War\ Cry=1 -Knacksaw\ Clique=1 -Knight's\ Pledge=1 -Knight-Captain\ of\ Eos=1 -Knight\ Errant=1 -Knight\ Watch=1 -Knight\ of\ Cliffhaven=1 -Knight\ of\ Dusk=1 -Knight\ of\ Glory=1 -Knight\ of\ Infamy=1 -Knight\ of\ New\ Benalia=1 -Knight\ of\ Obligation=1 -Knight\ of\ Stromgald=1 -Knight\ of\ Sursi=1 -Knight\ of\ Valor=1 -Knight\ of\ the\ Holy\ Nimbus=1 -Knight\ of\ the\ Mists=1 -Knight\ of\ the\ Pilgrim's\ Road=1 -Knight\ of\ the\ Skyward\ Eye=1 -Knight\ of\ the\ Stampede=1 -Knight\ of\ the\ Tusk=1 -Knighthood=1 -Knightly\ Valor=1 -Knollspine\ Dragon=1 -Knotvine\ Mystic=1 -Knotvine\ Paladin=1 -Knowledge\ Exploitation=1 -Knowledge\ Pool=1 -Knowledge\ and\ Power=1 -Knucklebone\ Witch=1 -Kobold\ Drill\ Sergeant=1 -Kobold\ Taskmaster=1 -Kodama's\ Might=1 -Kodama\ of\ the\ Center\ Tree=1 -Kodama\ of\ the\ North\ Tree=1 -Kodama\ of\ the\ South\ Tree=1 -Kolaghan\ Aspirant=1 -Kolaghan\ Forerunners=1 -Kolaghan\ Monument=1 -Kolaghan\ Stormsinger=1 -Konda's\ Banner=1 -Konda's\ Hatamoto=1 -Konda,\ Lord\ of\ Eiganjo=1 -Kongming,\ "Sleeping\ Dragon"=1 -Kookus=1 -Kor\ Bladewhirl=1 -Kor\ Cartographer=1 -Kor\ Castigator=1 -Kor\ Chant=1 -Kor\ Duelist=1 -Kor\ Entanglers=1 -Kor\ Hookmaster=1 -Kor\ Outfitter=1 -Kor\ Scythemaster=1 -Kor\ Sky\ Climber=1 -Korozda\ Gorgon=1 -Korozda\ Guildmage=1 -Korozda\ Monitor=1 -Kothophed,\ Soul\ Hoarder=1 -Kozilek's\ Channeler=1 -Kozilek's\ Pathfinder=1 -Kozilek's\ Predator=1 -Kozilek's\ Sentinel=1 -Kozilek's\ Shrieker=1 -Kozilek's\ Translator=1 -Kragma\ Butcher=1 -Kragma\ Warcaller=1 -Kraken's\ Eye=1 -Kraken\ Hatchling=1 -Kraken\ of\ the\ Straits=1 -Krakilin=1 -Kranioceros=1 -Krasis\ Incubation=1 -Kraul\ Warrior=1 -Krenko's\ Enforcer=1 -Krosan\ Avenger=1 -Krosan\ Cloudscraper=1 -Krosan\ Colossus=1 -Krosan\ Drover=1 -Krosan\ Druid=1 -Krosan\ Groundshaker=1 -Krosan\ Reclamation=1 -Krosan\ Tusker=1 -Krosan\ Vorine=1 -Krosan\ Warchief=1 -Krosan\ Wayfarer=1 -Krovikan\ Fetish=1 -Krovikan\ Horror=1 -Krovikan\ Rot=1 -Krovikan\ Scoundrel=1 -Krovikan\ Vampire=1 -Kruin\ Outlaw=1 -Kruin\ Striker=1 -Kudzu=1 -Kujar\ Seedsculptor=1 -Kukemssa\ Serpent=1 -Kuldotha\ Flamefiend=1 -Kuldotha\ Phoenix=1 -Kulrath\ Knight=1 -Kumano's\ Blessing=1 -Kumano's\ Pupils=1 -Kumano,\ Master\ Yamabushi=1 -Kumena's\ Awakening=1 -Kumena's\ Speaker=1 -Kuon,\ Ogre\ Ascendant=1 -Kurgadon=1 -Kurkesh,\ Onakke\ Ancient=1 -Kuro's\ Taken=1 -Kuro,\ Pitlord=1 -Kusari-Gama=1 -Kwende,\ Pride\ of\ Femeref=1 -Kyoki,\ Sanity's\ Eclipse=1 -Kyren\ Legate=1 -Kyren\ Sniper=1 -Kytheon's\ Irregulars=1 -Kytheon's\ Tactics=1 -Lab\ Rats=1 -Laboratory\ Brute=1 -Labyrinth\ Champion=1 -Labyrinth\ Guardian=1 -Labyrinth\ Minotaur=1 -Laccolith\ Grunt=1 -Laccolith\ Rig=1 -Laccolith\ Warrior=1 -Lady\ Orca=1 -Lagac\ Lizard=1 -Lagonna-Band\ Elder=1 -Lambholt\ Elder=1 -Lambholt\ Pacifist=1 -Lammastide\ Weave=1 -Lamplighter\ of\ Selhoff=1 -Landbind\ Ritual=1 -Landslide=1 -Lantern-Lit\ Graveyard=1 -Lantern\ Kami=1 -Lantern\ Scout=1 -Lantern\ Spirit=1 -Lapse\ of\ Certainty=1 -Laquatus's\ Champion=1 -Laquatus's\ Disdain=1 -Larceny=1 -Larger\ Than\ Life=1 -Lash\ Out=1 -Lashknife=1 -Lashknife\ Barrier=1 -Lashweed\ Lurker=1 -Last-Ditch\ Effort=1 -Last\ Breath=1 -Last\ Caress=1 -Last\ Gasp=1 -Last\ Kiss=1 -Last\ Laugh=1 -Last\ Stand=1 -Last\ Thoughts=1 -Last\ Word=1 -Lat-Nam's\ Legacy=1 -Latch\ Seeker=1 -Latchkey\ Faerie=1 -Lathnu\ Hellion=1 -Lathnu\ Sailback=1 -Launch=1 -Lava\ Axe=1 -Lava\ Burst=1 -Lava\ Dart=1 -Lava\ Flow=1 -Lava\ Hounds=1 -Lava\ Runner=1 -Lavaball\ Trap=1 -Lavaborn\ Muse=1 -Lavafume\ Invoker=1 -Lavalanche=1 -Lavastep\ Raider=1 -Lavinia\ of\ the\ Tenth=1 -Lawless\ Broker=1 -Lay\ Bare\ the\ Heart=1 -Lay\ Claim=1 -Lay\ Waste=1 -Lay\ of\ the\ Land=1 -Lead-Belly\ Chimera=1 -Lead\ Astray=1 -Lead\ Golem=1 -Lead\ by\ Example=1 -Leaden\ Fists=1 -Leaden\ Myr=1 -Leaf\ Dancer=1 -Leaf\ Gilder=1 -Leafcrown\ Dryad=1 -Leafdrake\ Roost=1 -Leaping\ Lizard=1 -Leaping\ Master=1 -Learn\ from\ the\ Past=1 -Leashling=1 -Leave\ //\ Chance=1 -Leave\ in\ the\ Dust=1 -Leech\ Bonder=1 -Leeches=1 -Leeching\ Licid=1 -Leering\ Emblem=1 -Leering\ Gargoyle=1 -Leery\ Fogbeast=1 -Legacy's\ Allure=1 -Legacy\ Weapon=1 -Legerdemain=1 -Legion's\ Judgment=1 -Legion\ Conquistador=1 -Legion\ Lieutenant=1 -Lens\ of\ Clarity=1 -Leonin\ Abunas=1 -Leonin\ Armorguard=1 -Leonin\ Battlemage=1 -Leonin\ Bola=1 -Leonin\ Den-Guard=1 -Leonin\ Elder=1 -Leonin\ Iconoclast=1 -Leonin\ Scimitar=1 -Leonin\ Skyhunter=1 -Leonin\ Snarecaster=1 -Leonin\ Squire=1 -Leonin\ Sun\ Standard=1 -Leshrac's\ Rite=1 -Lesser\ Gargadon=1 -Lesser\ Werewolf=1 -Lethal\ Sting=1 -Lethargy\ Trap=1 -Leveler=1 -Leviathan=1 -Levitation=1 -Ley\ Druid=1 -Ley\ Line=1 -Leyline\ Phantom=1 -Leyline\ of\ Lightning=1 -Leyline\ of\ Vitality=1 -Lhurgoyf=1 -Liar's\ Pendulum=1 -Liberate=1 -Liberated\ Dwarf=1 -Lich's\ Caress=1 -Lich's\ Mastery=1 -Lich's\ Tomb=1 -Liege\ of\ the\ Pit=1 -Life's\ Finale=1 -Life\ Goes\ On=1 -Life\ and\ Limb=1 -Lifecraft\ Awakening=1 -Lifecraft\ Cavalry=1 -Lifecrafter's\ Gift=1 -Lifegift=1 -Lifespark\ Spellbomb=1 -Lifespinner=1 -Lifespring\ Druid=1 -Light\ of\ Sanction=1 -Lightbringer=1 -Lightkeeper\ of\ Emeria=1 -Lightning-Rig\ Crew=1 -Lightning\ Blast=1 -Lightning\ Blow=1 -Lightning\ Cloud=1 -Lightning\ Elemental=1 -Lightning\ Hounds=1 -Lightning\ Javelin=1 -Lightning\ Reaver=1 -Lightning\ Reflexes=1 -Lightning\ Rift=1 -Lightning\ Runner=1 -Lightning\ Talons=1 -Lightning\ Volley=1 -Lightwielder\ Paladin=1 -Liliana's\ Defeat=1 -Liliana's\ Elite=1 -Liliana's\ Indignation=1 -Liliana's\ Shade=1 -Liliana's\ Specter=1 -Lilting\ Refrain=1 -Lim-Dl's\ Cohort=1 -Lim-Dl's\ High\ Guard=1 -Lim-Dl\ the\ Necromancer=1 -Limestone\ Golem=1 -Limits\ of\ Solidarity=1 -Linessa,\ Zephyr\ Mage=1 -Lingering\ Mirage=1 -Lingering\ Phantom=1 -Lingering\ Tormentor=1 -Linvala,\ the\ Preserver=1 -Lionheart\ Maverick=1 -Liquid\ Fire=1 -Liquimetal\ Coating=1 -Lithatog=1 -Lithomancer's\ Focus=1 -Lithophage=1 -Liturgy\ of\ Blood=1 -Liu\ Bei,\ Lord\ of\ Shu=1 -Live\ Fast=1 -Livewire\ Lash=1 -Living\ Airship=1 -Living\ Death=1 -Living\ Destiny=1 -Living\ Hive=1 -Living\ Inferno=1 -Living\ Lands=1 -Living\ Terrain=1 -Living\ Totem=1 -Living\ Tsunami=1 -Llanowar\ Behemoth=1 -Llanowar\ Cavalry=1 -Llanowar\ Dead=1 -Llanowar\ Empath=1 -Llanowar\ Envoy=1 -Llanowar\ Mentor=1 -Llanowar\ Scout=1 -Llanowar\ Sentinel=1 -Llanowar\ Vanguard=1 -Loafing\ Giant=1 -Loam\ Dryad=1 -Loam\ Dweller=1 -Loam\ Lion=1 -Loamdragger\ Giant=1 -Loathsome\ Catoblepas=1 -Lobber\ Crew=1 -Locket\ of\ Yesterdays=1 -Lockjaw\ Snapper=1 -Locust\ Swarm=1 -Lodestone\ Myr=1 -Lone\ Missionary=1 -Lone\ Revenant=1 -Lone\ Rider=1 -Lone\ Wolf=1 -Long-Finned\ Skywhale=1 -Long-Forgotten\ Gohei=1 -Long\ Road\ Home=1 -Longbow\ Archer=1 -Longshot\ Squad=1 -Longtusk\ Cub=1 -Lookout's\ Dispersal=1 -Looming\ Altisaur=1 -Looming\ Hoverguard=1 -Looming\ Shade=1 -Looming\ Spires=1 -Lord\ of\ Shatterskull\ Pass=1 -Lord\ of\ the\ Pit=1 -Lore\ Broker=1 -Lorescale\ Coatl=1 -Lorthos,\ the\ Tidemaker=1 -Lose\ Calm=1 -Lose\ Hope=1 -Lost\ Auramancers=1 -Lost\ Leonin=1 -Lost\ Order\ of\ Jarkeld=1 -Lost\ in\ Thought=1 -Lost\ in\ a\ Labyrinth=1 -Lost\ in\ the\ Mist=1 -Lost\ in\ the\ Woods=1 -Lotus\ Path\ Djinn=1 -Lowland\ Basilisk=1 -Lowland\ Giant=1 -Loxodon\ Gatekeeper=1 -Loxodon\ Hierarch=1 -Loxodon\ Line\ Breaker=1 -Loxodon\ Mystic=1 -Loxodon\ Partisan=1 -Loxodon\ Peacekeeper=1 -Loxodon\ Punisher=1 -Loyal\ Cathar=1 -Loyal\ Gyrfalcon=1 -Loyal\ Pegasus=1 -Lu\ Bu,\ Master-at-Arms=1 -Lu\ Meng,\ Wu\ General=1 -Lucent\ Liminid=1 -Ludevic's\ Test\ Subject=1 -Lullmage\ Mentor=1 -Lumbering\ Satyr=1 -Lumberknot=1 -Lumengrid\ Augur=1 -Lumengrid\ Gargoyle=1 -Lumengrid\ Sentinel=1 -Lumengrid\ Warden=1 -Luminate\ Primordial=1 -Luminous\ Angel=1 -Luminous\ Bonds=1 -Luminous\ Wake=1 -Lunar\ Avenger=1 -Lunar\ Force=1 -Lunar\ Mystic=1 -Lunarch\ Mantle=1 -Lunk\ Errant=1 -Lupine\ Prototype=1 -Lurching\ Rotbeast=1 -Lure=1 -Lurebound\ Scarecrow=1 -Lurking\ Arynx=1 -Lurking\ Chupacabra=1 -Lurking\ Informant=1 -Lurking\ Skirge=1 -Lush\ Growth=1 -Lust\ for\ War=1 -Luxa\ River\ Shrine=1 -Lyev\ Decree=1 -Lyev\ Skyknight=1 -Lymph\ Sliver=1 -Lys\ Alana\ Bowmaster=1 -Lys\ Alana\ Scarblade=1 -Macabre\ Waltz=1 -Macetail\ Hystrodon=1 -Machinate=1 -Mad\ Auntie=1 -Mad\ Prophet=1 -Madblind\ Mountain=1 -Madcap\ Skills=1 -Madrush\ Cyclops=1 -Maelstrom\ Djinn=1 -Maga,\ Traitor\ to\ Mortals=1 -Mage-Ring\ Bully=1 -Mage-Ring\ Network=1 -Mage-Ring\ Responder=1 -Mage\ Slayer=1 -Magebane\ Armor=1 -Magefire\ Wings=1 -Magewright's\ Stone=1 -Maggot\ Carrier=1 -Maggot\ Therapy=1 -Magister\ Sphinx=1 -Magister\ of\ Worth=1 -Magistrate's\ Veto=1 -Magma\ Burst=1 -Magma\ Giant=1 -Magma\ Mine=1 -Magma\ Phoenix=1 -Magma\ Rift=1 -Magmaquake=1 -Magmaroth=1 -Magmasaur=1 -Magmatic\ Chasm=1 -Magmatic\ Core=1 -Magmatic\ Insight=1 -Magmaw=1 -Magnetic\ Theft=1 -Magnifying\ Glass=1 -Magnivore=1 -Magosi,\ the\ Waterveil=1 -Magus\ of\ the\ Abyss=1 -Magus\ of\ the\ Candelabra=1 -Magus\ of\ the\ Coffers=1 -Magus\ of\ the\ Disk=1 -Magus\ of\ the\ Future=1 -Magus\ of\ the\ Jar=1 -Magus\ of\ the\ Library=1 -Magus\ of\ the\ Mirror=1 -Magus\ of\ the\ Scroll=1 -Magus\ of\ the\ Tabernacle=1 -Magus\ of\ the\ Vineyard=1 -Mahamoti\ Djinn=1 -Majestic\ Heliopterus=1 -Major\ Teroh=1 -Make\ Mischief=1 -Make\ Obsolete=1 -Make\ a\ Stand=1 -Make\ a\ Wish=1 -Makeshift\ Mannequin=1 -Makeshift\ Mauler=1 -Makeshift\ Munitions=1 -Makindi\ Aeronaut=1 -Makindi\ Griffin=1 -Makindi\ Patrol=1 -Makindi\ Shieldmate=1 -Makindi\ Sliderunner=1 -Malach\ of\ the\ Dawn=1 -Malachite\ Golem=1 -Malakir\ Bloodwitch=1 -Malakir\ Cullblade=1 -Malakir\ Familiar=1 -Malakir\ Soothsayer=1 -Malevolent\ Awakening=1 -Malevolent\ Whispers=1 -Malfunction=1 -Malicious\ Advice=1 -Mammoth\ Spider=1 -Mammoth\ Umbra=1 -Man-o'-War=1 -Mana\ Bloom=1 -Mana\ Clash=1 -Mana\ Cylix=1 -Mana\ Leech=1 -Mana\ Seism=1 -Mana\ Skimmer=1 -Manabarbs=1 -Manacles\ of\ Decay=1 -Manaforce\ Mace=1 -Manaforge\ Cinder=1 -Managorger\ Hydra=1 -Manakin=1 -Manalith=1 -Manaplasm=1 -Mangara's\ Blessing=1 -Mangara\ of\ Corondor=1 -Manglehorn=1 -Maniacal\ Rage=1 -Manic\ Scribe=1 -Manic\ Vandal=1 -Mannichi,\ the\ Fevered\ Dream=1 -Manor\ Gargoyle=1 -Manor\ Skeleton=1 -Manta\ Ray=1 -Manta\ Riders=1 -Manticore\ Eternal=1 -Manticore\ of\ the\ Gauntlet=1 -Mantis\ Engine=1 -Mantle\ of\ Leadership=1 -Mantle\ of\ Webs=1 -Map\ the\ Wastes=1 -Marang\ River\ Skeleton=1 -Marauder's\ Axe=1 -Marauding\ Boneslasher=1 -Marauding\ Looter=1 -Marauding\ Maulhorn=1 -Marble\ Chalice=1 -Marble\ Titan=1 -March\ of\ the\ Drowned=1 -March\ of\ the\ Machines=1 -March\ of\ the\ Returned=1 -Mardu\ Ascendancy=1 -Mardu\ Banner=1 -Mardu\ Blazebringer=1 -Mardu\ Charm=1 -Mardu\ Hateblade=1 -Mardu\ Heart-Piercer=1 -Mardu\ Hordechief=1 -Mardu\ Roughrider=1 -Mardu\ Runemark=1 -Mardu\ Skullhunter=1 -Mardu\ Warshrieker=1 -Marionette\ Master=1 -Marisi's\ Twinclaws=1 -Maritime\ Guard=1 -Mark\ for\ Death=1 -Mark\ of\ Eviction=1 -Mark\ of\ Sakiko=1 -Mark\ of\ the\ Oni=1 -Mark\ of\ the\ Vampire=1 -Marked\ by\ Honor=1 -Marker\ Beetles=1 -Markov\ Blademaster=1 -Markov\ Crusader=1 -Markov\ Dreadknight=1 -Markov\ Patrician=1 -Markov\ Warlord=1 -Maro=1 -Marrow\ Bats=1 -Marrow\ Chomper=1 -Marrow\ Shards=1 -Marsh\ Casualties=1 -Marsh\ Flitter=1 -Marsh\ Hulk=1 -Marsh\ Lurker=1 -Marshal's\ Anthem=1 -Marshaling\ Cry=1 -Marshdrinker\ Giant=1 -Marshmist\ Titan=1 -Martial\ Glory=1 -Martial\ Law=1 -Martyr's\ Cause=1 -Martyr's\ Cry=1 -Martyr\ of\ Bones=1 -Martyr\ of\ Dusk=1 -Martyr\ of\ Frost=1 -Martyred\ Rusalka=1 -Martyrs'\ Tomb=1 -Martyrs\ of\ Korlis=1 -Marwyn,\ the\ Nurturer=1 -Masako\ the\ Humorless=1 -Mask\ of\ Avacyn=1 -Mask\ of\ Intolerance=1 -Mask\ of\ Riddles=1 -Mask\ of\ the\ Mimic=1 -Masked\ Admirers=1 -Mass\ Appeal=1 -Mass\ Calcify=1 -Mass\ Polymorph=1 -Mass\ of\ Ghouls=1 -Master's\ Call=1 -Master\ Decoy=1 -Master\ Splicer=1 -Master\ Thief=1 -Master\ Trinketeer=1 -Master\ Warcraft=1 -Master\ of\ Arms=1 -Master\ of\ Diversion=1 -Master\ of\ Pearls=1 -Master\ of\ Predicaments=1 -Master\ the\ Way=1 -Masticore=1 -Masumaro,\ First\ to\ Live=1 -Matca\ Rioters=1 -Matsu-Tribe\ Birdstalker=1 -Matsu-Tribe\ Decoy=1 -Matsu-Tribe\ Sniper=1 -Maul\ Splicer=1 -Maulfist\ Doorbuster=1 -Maulfist\ Revolutionary=1 -Maulfist\ Squad=1 -Mausoleum\ Guard=1 -Mausoleum\ Harpy=1 -Mausoleum\ Turnkey=1 -Maverick\ Thopterist=1 -Mavren\ Fein,\ Dusk\ Apostle=1 -Maw\ of\ Kozilek=1 -Maw\ of\ the\ Mire=1 -Maw\ of\ the\ Obzedat=1 -Mawcor=1 -Mayor\ of\ Avabruck=1 -Maze\ Abomination=1 -Maze\ Behemoth=1 -Maze\ Glider=1 -Maze\ Rusher=1 -Maze\ Sentinel=1 -Maze\ of\ Shadows=1 -Meadowboon=1 -Meandering\ River=1 -Meandering\ Towershell=1 -Measure\ of\ Wickedness=1 -Meddle=1 -Medicine\ Runner=1 -Meditation\ Puzzle=1 -Megatherium=1 -Megatog=1 -Meglonoth=1 -Megrim=1 -Melancholy=1 -Melek,\ Izzet\ Paragon=1 -Melesse\ Spirit=1 -Meletis\ Astronomer=1 -Meletis\ Charlatan=1 -Melira's\ Keepers=1 -Meloku\ the\ Clouded\ Mirror=1 -Melt\ Terrain=1 -Memorial\ to\ Folly=1 -Memorial\ to\ Glory=1 -Memorial\ to\ Unity=1 -Memory's\ Journey=1 -Memory\ Erosion=1 -Menacing\ Ogre=1 -Mending\ Hands=1 -Mending\ Touch=1 -Meng\ Huo's\ Horde=1 -Mental\ Discipline=1 -Mental\ Vapors=1 -Mephidross\ Vampire=1 -Mephitic\ Ooze=1 -Mer-Ek\ Nightblade=1 -Mercadian\ Bazaar=1 -Merchant's\ Dockhand=1 -Merciless\ Eternal=1 -Merciless\ Javelineer=1 -Merciless\ Resolve=1 -Mercurial\ Chemister=1 -Mercurial\ Kite=1 -Mercurial\ Pretender=1 -Merfolk\ Assassin=1 -Merfolk\ Looter=1 -Merfolk\ Mesmerist=1 -Merfolk\ Mistbinder=1 -Merfolk\ Observer=1 -Merfolk\ Seastalkers=1 -Merfolk\ Seer=1 -Merfolk\ Skyscout=1 -Merfolk\ Sovereign=1 -Merfolk\ Spy=1 -Merfolk\ Thaumaturgist=1 -Merfolk\ Traders=1 -Merfolk\ Wayfinder=1 -Merfolk\ of\ the\ Depths=1 -Merfolk\ of\ the\ Pearl\ Trident=1 -Merieke\ Ri\ Berit=1 -Merrow\ Bonegnawer=1 -Merrow\ Commerce=1 -Merrow\ Grimeblotter=1 -Merrow\ Harbinger=1 -Merrow\ Levitator=1 -Merrow\ Witsniper=1 -Mesa\ Enchantress=1 -Mesa\ Unicorn=1 -Mesmeric\ Sliver=1 -Mesmeric\ Trance=1 -Messenger's\ Speed=1 -Messenger\ Drake=1 -Messenger\ Falcons=1 -Metal\ Fatigue=1 -Metallic\ Mastery=1 -Metallurgeon=1 -Metalspinner's\ Puzzleknot=1 -Metamorphic\ Wurm=1 -Metathran\ Aerostat=1 -Metathran\ Soldier=1 -Metathran\ Transport=1 -Metathran\ Zombie=1 -Meteor\ Shower=1 -Meteorite=1 -Metrognome=1 -Metropolis\ Sprite=1 -Miasmic\ Mummy=1 -Midnight\ Banshee=1 -Midnight\ Charm=1 -Midnight\ Covenant=1 -Midnight\ Entourage=1 -Midnight\ Guard=1 -Midnight\ Haunting=1 -Midnight\ Oil=1 -Midnight\ Ritual=1 -Midnight\ Scavengers=1 -Might\ Beyond\ Reason=1 -Might\ Makes\ Right=1 -Might\ Sliver=1 -Might\ Weaver=1 -Might\ of\ Alara=1 -Might\ of\ Oaks=1 -Might\ of\ the\ Masses=1 -Might\ of\ the\ Nephilim=1 -Mightstone=1 -Mighty\ Emergence=1 -Mighty\ Leap=1 -Militant\ Inquisitor=1 -Militant\ Monk=1 -Militia's\ Pride=1 -Millennial\ Gargoyle=1 -Millikin=1 -Millstone=1 -Mimeofacture=1 -Mimic\ Vat=1 -Miming\ Slime=1 -Mina\ and\ Denn,\ Wildborn=1 -Minamo\ Scrollkeeper=1 -Minamo\ Sightbender=1 -Mind\ Bend=1 -Mind\ Burst=1 -Mind\ Control=1 -Mind\ Extraction=1 -Mind\ Grind=1 -Mind\ Peel=1 -Mind\ Raker=1 -Mind\ Rot=1 -Mind\ Shatter=1 -Mind\ Sludge=1 -Mind\ Spring=1 -Mindclaw\ Shaman=1 -Mindlash\ Sliver=1 -Mindleech\ Mass=1 -Mindless\ Automaton=1 -Mindless\ Null=1 -Mindlock\ Orb=1 -Mindmelter=1 -Mindreaver=1 -Mindshrieker=1 -Mindstab=1 -Mindstab\ Thrull=1 -Mindstatic=1 -Mindswipe=1 -Mindwarper=1 -Mindwrack\ Liege=1 -Mine\ Bearer=1 -Mine\ Excavation=1 -Minion\ Reflector=1 -Minion\ of\ the\ Wastes=1 -Minions'\ Murmurs=1 -Minister\ of\ Impediments=1 -Minotaur\ Abomination=1 -Minotaur\ Aggressor=1 -Minotaur\ Explorer=1 -Minotaur\ Illusionist=1 -Minotaur\ Skullcleaver=1 -Minotaur\ Sureshot=1 -Minotaur\ Tactician=1 -Miraculous\ Recovery=1 -Mirage\ Mirror=1 -Mirari=1 -Mire's\ Malice=1 -Mire's\ Toll=1 -Mire\ Boa=1 -Mire\ Kavu=1 -Mire\ Shade=1 -Mirozel=1 -Mirran\ Mettle=1 -Mirran\ Spy=1 -Mirri,\ Cat\ Warrior=1 -Mirri\ the\ Cursed=1 -Mirror-Mad\ Phantasm=1 -Mirror\ Sheen=1 -Mirror\ Strike=1 -Mirror\ Wall=1 -Mirror\ of\ Fate=1 -Mirrorweave=1 -Mirrorwood\ Treefolk=1 -Mirrorworks=1 -Mischievous\ Poltergeist=1 -Misery\ Charm=1 -Misfortune's\ Gain=1 -Misguided\ Rage=1 -Mishra's\ Groundbreaker=1 -Mishra's\ Self-Replicator=1 -Mishra,\ Artificer\ Prodigy=1 -Misinformation=1 -Misstep=1 -Mist-Cloaked\ Herald=1 -Mist\ Intruder=1 -Mist\ Leopard=1 -Mist\ Raven=1 -Mistcutter\ Hydra=1 -Mistfire\ Weaver=1 -Mistform\ Dreamer=1 -Mistform\ Mask=1 -Mistform\ Mutant=1 -Mistform\ Shrieker=1 -Mistform\ Skyreaver=1 -Mistform\ Sliver=1 -Mistform\ Stalker=1 -Mistform\ Ultimus=1 -Mistform\ Wakecaster=1 -Mistform\ Wall=1 -Mistform\ Warchief=1 -Misthoof\ Kirin=1 -Mistmeadow\ Skulk=1 -Mistmeadow\ Witch=1 -Mistmoon\ Griffin=1 -Mistral\ Charger=1 -Mitotic\ Manipulation=1 -Mitotic\ Slime=1 -Mizzium\ Meddler=1 -Mizzium\ Mortars=1 -Mizzium\ Skin=1 -Mizzium\ Transreliquat=1 -Mnemonic\ Nexus=1 -Moan\ of\ the\ Unhallowed=1 -Moaning\ Wall=1 -Mobile\ Fort=1 -Mobile\ Garrison=1 -Mockery\ of\ Nature=1 -Mogg\ Bombers=1 -Mogg\ Flunkies=1 -Mogg\ Hollows=1 -Mogg\ Salvage=1 -Mogg\ Sentry=1 -Mogg\ Squad=1 -Mogis's\ Marauder=1 -Mogis's\ Warhound=1 -Mold\ Adder=1 -Molder=1 -Molder\ Beast=1 -Molder\ Slug=1 -Moldervine\ Cloak=1 -Moldgraf\ Monstrosity=1 -Moldgraf\ Scavenger=1 -Molimo,\ Maro-Sorcerer=1 -Molten\ Disaster=1 -Molten\ Firebird=1 -Molten\ Hydra=1 -Molten\ Influence=1 -Molten\ Nursery=1 -Molten\ Primordial=1 -Molten\ Psyche=1 -Molten\ Ravager=1 -Molten\ Sentry=1 -Molten\ Slagheap=1 -Molten\ Vortex=1 -Moltensteel\ Dragon=1 -Molting\ Harpy=1 -Molting\ Skin=1 -Molting\ Snakeskin=1 -Moment\ of\ Craving=1 -Moment\ of\ Heroism=1 -Moment\ of\ Triumph=1 -Momentary\ Blink=1 -Momentous\ Fall=1 -Momir\ Vig,\ Simic\ Visionary=1 -Monastery\ Flock=1 -Monastery\ Loremaster=1 -Mondronen\ Shaman=1 -Monk\ Idealist=1 -Monk\ Realist=1 -Monomania=1 -Monstrify=1 -Monstrous\ Growth=1 -Monstrous\ Hound=1 -Monstrous\ Onslaught=1 -Moonbow\ Illusionist=1 -Moonglove\ Changeling=1 -Moonglove\ Winnower=1 -Moonhold=1 -Moonlace=1 -Moonlight\ Bargain=1 -Moonlight\ Hunt=1 -Moonlit\ Strider=1 -Moonmist=1 -Moonring\ Island=1 -Moonring\ Mirror=1 -Moonsilver\ Spear=1 -Moonwing\ Moth=1 -Moorish\ Cavalry=1 -Moorland\ Drifter=1 -Morality\ Shift=1 -Moratorium\ Stone=1 -Morbid\ Bloom=1 -Morbid\ Curiosity=1 -Mordant\ Dragon=1 -Morgue\ Burst=1 -Morgue\ Thrull=1 -Morgue\ Toad=1 -Morinfen=1 -Moriok\ Reaver=1 -Moriok\ Replica=1 -Moriok\ Rigger=1 -Moriok\ Scavenger=1 -Morkrut\ Banshee=1 -Morkrut\ Necropod=1 -Morningtide=1 -Moroii=1 -Morphling=1 -Morsel\ Theft=1 -Morselhoarder=1 -Mortal's\ Ardor=1 -Mortal's\ Resolve=1 -Mortal\ Combat=1 -Mortal\ Obstinacy=1 -Mortal\ Wound=1 -Mortarpod=1 -Mortician\ Beetle=1 -Mortipede=1 -Mortiphobia=1 -Mortivore=1 -Mortuary=1 -Mortuary\ Mire=1 -Mortus\ Strider=1 -Mosquito\ Guard=1 -Moss\ Diamond=1 -Moss\ Kami=1 -Moss\ Monster=1 -Mossbridge\ Troll=1 -Mossfire\ Egg=1 -Mothdust\ Changeling=1 -Mothrider\ Samurai=1 -Mountain=1 -Mountain\ Valley=1 -Mountain\ Yeti=1 -Mourner's\ Shield=1 -Mournful\ Zombie=1 -Mourning=1 -Mournwhelk=1 -Mournwillow=1 -Mouth\ //\ Feed=1 -Mtenda\ Griffin=1 -Muck\ Drubb=1 -Mudbutton\ Clanger=1 -Mudbutton\ Torchrunner=1 -Mugging=1 -Mul\ Daya\ Channelers=1 -Mulch=1 -Multani's\ Acolyte=1 -Multani's\ Harmony=1 -Multani's\ Presence=1 -Mummy\ Paramount=1 -Munda's\ Vanguard=1 -Munda,\ Ambush\ Leader=1 -Mundungu=1 -Murasa\ Pyromancer=1 -Murasa\ Ranger=1 -Murder=1 -Murder\ Investigation=1 -Murder\ of\ Crows=1 -Murderer's\ Axe=1 -Murderous\ Betrayal=1 -Murderous\ Compulsion=1 -Murderous\ Cut=1 -Murderous\ Redcap=1 -Murderous\ Spoils=1 -Murk\ Strider=1 -Murmuring\ Phantasm=1 -Murmurs\ from\ Beyond=1 -Muscle\ Burst=1 -Muse\ Vessel=1 -Musician=1 -Mutant's\ Prey=1 -Mutiny=1 -Muzzle=1 -Mwonvuli\ Beast\ Tracker=1 -Mwonvuli\ Ooze=1 -Mycoid\ Shepherd=1 -Mycologist=1 -Mycosynth\ Fiend=1 -Mycosynth\ Wellspring=1 -Myojin\ of\ Infinite\ Rage=1 -Myr\ Adapter=1 -Myr\ Galvanizer=1 -Myr\ Incubator=1 -Myr\ Moonvessel=1 -Myr\ Propagator=1 -Myr\ Prototype=1 -Myr\ Reservoir=1 -Myr\ Sire=1 -Myr\ Turbine=1 -Myr\ Welder=1 -Myrsmith=1 -Mysteries\ of\ the\ Deep=1 -Mystic\ Decree=1 -Mystic\ Genesis=1 -Mystic\ Meditation=1 -Mystic\ Melting=1 -Mystic\ Monastery=1 -Mystic\ Penitent=1 -Mystic\ Restraints=1 -Mystic\ Snake=1 -Mystic\ Speculation=1 -Mystic\ Veil=1 -Mystic\ Zealot=1 -Mystic\ of\ the\ Hidden\ Way=1 -Mystifying\ Maze=1 -Mythic\ Proportions=1 -Nacatl\ Hunt-Pride=1 -Nacatl\ Outlander=1 -Nacatl\ Savage=1 -Nacatl\ War-Pride=1 -Naga\ Oracle=1 -Naga\ Vitalist=1 -Nagao,\ Bound\ by\ Honor=1 -Nagging\ Thoughts=1 -Nahiri's\ Machinations=1 -Nakaya\ Shade=1 -Naked\ Singularity=1 -Nalathni\ Dragon=1 -Nameless\ One=1 -Nantuko\ Blightcutter=1 -Nantuko\ Disciple=1 -Nantuko\ Elder=1 -Nantuko\ Husk=1 -Nantuko\ Mentor=1 -Nantuko\ Monastery=1 -Nantuko\ Shade=1 -Nantuko\ Shaman=1 -Nantuko\ Tracer=1 -Nantuko\ Vigilante=1 -Narcissism=1 -Narcolepsy=1 -Narnam\ Cobra=1 -Narnam\ Renegade=1 -Narrow\ Escape=1 -Narwhal=1 -Nath's\ Buffoon=1 -Nath's\ Elite=1 -Natural\ Affinity=1 -Natural\ Connection=1 -Natural\ Emergence=1 -Natural\ Obsolescence=1 -Natural\ Spring=1 -Naturalize=1 -Nature's\ Blessing=1 -Nature's\ Kiss=1 -Nature's\ Panoply=1 -Nature's\ Resurgence=1 -Nature's\ Spiral=1 -Nature's\ Way=1 -Nature's\ Will=1 -Navigator's\ Compass=1 -Navigator's\ Ruin=1 -Naya\ Battlemage=1 -Naya\ Panorama=1 -Near-Death\ Experience=1 -Nearheath\ Chaplain=1 -Nearheath\ Stalker=1 -Nebelgast\ Herald=1 -Nebuchadnezzar=1 -Neck\ Snap=1 -Necra\ Disciple=1 -Necra\ Sanctuary=1 -Necravolver=1 -Necrobite=1 -Necrogen\ Scudder=1 -Necrogen\ Spellbomb=1 -Necrogenesis=1 -Necrologia=1 -Necromancer's\ Assistant=1 -Necromancer's\ Covenant=1 -Necromantic\ Summons=1 -Necromantic\ Thirst=1 -Necromaster\ Dragon=1 -Necroplasm=1 -Necropolis\ Fiend=1 -Necropouncer=1 -Necrosavant=1 -Necrotic\ Plague=1 -Nectar\ Faerie=1 -Needle\ Storm=1 -Needlebite\ Trap=1 -Needlebug=1 -Needlepeak\ Spider=1 -Needleshot\ Gourna=1 -Needletooth\ Raptor=1 -Nef-Crop\ Entangler=1 -Nefarox,\ Overlord\ of\ Grixis=1 -Neglected\ Heirloom=1 -Neheb,\ the\ Worthy=1 -Neko-Te=1 -Nekrataal=1 -Nema\ Siltlurker=1 -Nemesis\ Mask=1 -Nemesis\ Trap=1 -Nemesis\ of\ Mortals=1 -Nephalia\ Academy=1 -Nephalia\ Moondrakes=1 -Nephalia\ Seakite=1 -Nephalia\ Smuggler=1 -Nessian\ Asp=1 -Nessian\ Courser=1 -Nessian\ Demolok=1 -Nessian\ Game\ Warden=1 -Nessian\ Wilds\ Ravager=1 -Nest\ Invader=1 -Nest\ Robber=1 -Nest\ of\ Scarabs=1 -Nested\ Ghoul=1 -Nesting\ Wurm=1 -Netcaster\ Spider=1 -Nether\ Horror=1 -Netherborn\ Phalanx=1 -Netter\ en-Dal=1 -Nettle\ Drone=1 -Nettlevine\ Blight=1 -Nettling\ Curse=1 -Neurok\ Commando=1 -Neurok\ Familiar=1 -Neurok\ Hoversail=1 -Neurok\ Invisimancer=1 -Neurok\ Prodigy=1 -Neurok\ Stealthsuit=1 -Neurok\ Transmuter=1 -Neutralizing\ Blast=1 -Nevermaker=1 -New\ Benalia=1 -New\ Horizons=1 -New\ Perspectives=1 -New\ Prahv\ Guildmage=1 -Nezumi\ Bone-Reader=1 -Nezumi\ Cutthroat=1 -Nezumi\ Graverobber=1 -Nezumi\ Ronin=1 -Nezumi\ Shadow-Watcher=1 -Niblis\ of\ Dusk=1 -Niblis\ of\ Frost=1 -Niblis\ of\ the\ Urn=1 -Nicol\ Bolas=1 -Night\ Dealings=1 -Night\ Market\ Aeronaut=1 -Night\ Market\ Guard=1 -Night\ Market\ Lookout=1 -Night\ Terrors=1 -Nightbird's\ Clutches=1 -Nightcreep=1 -Nightfire\ Giant=1 -Nightguard\ Patrol=1 -Nighthaze=1 -Nighthowler=1 -Nightmare=1 -Nightmare\ Incursion=1 -Nightmare\ Lash=1 -Nightmare\ Void=1 -Nightmarish\ End=1 -Nightscape\ Apprentice=1 -Nightscape\ Battlemage=1 -Nightscape\ Familiar=1 -Nightshade\ Assassin=1 -Nightshade\ Peddler=1 -Nightshade\ Schemers=1 -Nightshade\ Seer=1 -Nightshade\ Stinger=1 -Nightsnare=1 -Nightwing\ Shade=1 -Nihilistic\ Glee=1 -Nihilith=1 -Nikko-Onna=1 -Nim\ Abomination=1 -Nim\ Devourer=1 -Nim\ Grotesque=1 -Nim\ Lasher=1 -Nim\ Replica=1 -Nim\ Shambler=1 -Nim\ Shrieker=1 -Nimana\ Sell-Sword=1 -Nimble-Blade\ Khenra=1 -Nimble\ Innovator=1 -Nimbus\ Naiad=1 -Nimbus\ Swimmer=1 -Nimbus\ Wings=1 -Ninth\ Bridge\ Patrol=1 -Nirkana\ Assassin=1 -Nirkana\ Cutthroat=1 -Nissa's\ Chosen=1 -Nissa's\ Defeat=1 -Nissa's\ Expedition=1 -Nissa's\ Judgment=1 -Nissa's\ Pilgrimage=1 -Nissa's\ Renewal=1 -Nissa's\ Revelation=1 -Niv-Mizzet,\ Dracogenius=1 -Niv-Mizzet,\ the\ Firemind=1 -Niveous\ Wisps=1 -Nivix,\ Aerie\ of\ the\ Firemind=1 -Nivix\ Barrier=1 -Nivix\ Guildmage=1 -Nivmagus\ Elemental=1 -No-Dachi=1 -No\ Rest\ for\ the\ Wicked=1 -Nobilis\ of\ War=1 -Noble\ Elephant=1 -Noble\ Quarry=1 -Noble\ Stand=1 -Noble\ Templar=1 -Noble\ Vestige=1 -Nocturnal\ Raid=1 -Noggin\ Whack=1 -Noggle\ Bandit=1 -Noggle\ Hedge-Mage=1 -Nomad\ Decoy=1 -Nomad\ Mythmaker=1 -Nomad\ Outpost=1 -Nomad\ Stadium=1 -Nomads'\ Assembly=1 -Noose\ Constrictor=1 -Noosegraf\ Mob=1 -Norwood\ Priestess=1 -Norwood\ Ranger=1 -Nostalgic\ Dreams=1 -Not\ Forgotten=1 -Not\ of\ This\ World=1 -Nourish=1 -Nova\ Pentacle=1 -Novijen,\ Heart\ of\ Progress=1 -Novijen\ Sages=1 -Noxious\ Hatchling=1 -Noxious\ Vapors=1 -Nucklavee=1 -Nuisance\ Engine=1 -Null\ Caller=1 -Null\ Champion=1 -Null\ Profusion=1 -Nullify=1 -Nullmage\ Advocate=1 -Nullmage\ Shepherd=1 -Nullstone\ Gargoyle=1 -Nulltread\ Gargantuan=1 -Numbing\ Dose=1 -Numot,\ the\ Devastator=1 -Nurturing\ Licid=1 -Nylea's\ Disciple=1 -Nylea's\ Emissary=1 -Nylea's\ Presence=1 -Nyx\ Infusion=1 -Nyxborn\ Eidolon=1 -Nyxborn\ Rollicker=1 -Nyxborn\ Shieldmate=1 -Nyxborn\ Triton=1 -Nyxborn\ Wolf=1 -Oak\ Street\ Innkeeper=1 -Oaken\ Brawler=1 -Oakenform=1 -Oakgnarl\ Warrior=1 -Oakheart\ Dryads=1 -Oashra\ Cultivator=1 -Oasis=1 -Oasis\ Ritualist=1 -Oath\ of\ Ajani=1 -Oath\ of\ Chandra=1 -Oath\ of\ Gideon=1 -Oath\ of\ Jace=1 -Oath\ of\ Liliana=1 -Oath\ of\ Teferi=1 -Oath\ of\ the\ Ancient\ Wood=1 -Oathkeeper,\ Takeno's\ Daisho=1 -Oathsworn\ Giant=1 -Oathsworn\ Vampire=1 -Obelisk\ Spider=1 -Obelisk\ of\ Alara=1 -Obelisk\ of\ Bant=1 -Obelisk\ of\ Esper=1 -Obelisk\ of\ Grixis=1 -Obelisk\ of\ Jund=1 -Obelisk\ of\ Naya=1 -Oblivion\ Crown=1 -Oblivion\ Strike=1 -Oboro\ Envoy=1 -Obscuring\ Aether=1 -Observant\ Alseid=1 -Obsessive\ Search=1 -Obsessive\ Skinner=1 -Obsianus\ Golem=1 -Obsidian\ Battle-Axe=1 -Obsidian\ Fireheart=1 -Obstinate\ Familiar=1 -Obzedat's\ Aid=1 -Ocular\ Halo=1 -Oculus=1 -Odds\ //\ Ends=1 -Odious\ Trow=1 -Odric,\ Lunarch\ Marshal=1 -Odric,\ Master\ Tactician=1 -Odunos\ River\ Trawler=1 -Off\ Balance=1 -Offalsnout=1 -Offering\ to\ Asha=1 -Ogre's\ Cleaver=1 -Ogre\ Enforcer=1 -Ogre\ Gatecrasher=1 -Ogre\ Geargrabber=1 -Ogre\ Jailbreaker=1 -Ogre\ Leadfoot=1 -Ogre\ Marauder=1 -Ogre\ Menial=1 -Ogre\ Recluse=1 -Ogre\ Resister=1 -Ogre\ Savant=1 -Ogre\ Sentry=1 -Ogre\ Shaman=1 -Ogre\ Slumlord=1 -Ogre\ Taskmaster=1 -Ohran\ Yeti=1 -Ojutai's\ Breath=1 -Ojutai's\ Summons=1 -Ojutai\ Interceptor=1 -Ojutai\ Monument=1 -Oketra's\ Attendant=1 -Oketra's\ Avenger=1 -Oketra's\ Last\ Mercy=1 -Okina,\ Temple\ to\ the\ Grandfathers=1 -Okk=1 -Old-Growth\ Dryads=1 -Old\ Ghastbark=1 -Olivia's\ Bloodsworn=1 -Olivia's\ Dragoon=1 -Omen\ Machine=1 -Omenspeaker=1 -Ominous\ Sphinx=1 -Omnibian=1 -On\ Serra's\ Wings=1 -Onakke\ Ogre=1 -Ondu\ Champion=1 -Ondu\ Cleric=1 -Ondu\ Giant=1 -Ondu\ Greathorn=1 -Ondu\ Rising=1 -Ondu\ War\ Cleric=1 -One-Eyed\ Scarecrow=1 -One\ Dozen\ Eyes=1 -One\ Thousand\ Lashes=1 -One\ With\ the\ Wind=1 -One\ with\ Nothing=1 -Ongoing\ Investigation=1 -Oni\ Possession=1 -Oni\ of\ Wild\ Places=1 -Onulet=1 -Onward\ //\ Victory=1 -Onyx\ Goblet=1 -Oona's\ Blackguard=1 -Oona's\ Gatewarden=1 -Oona's\ Grace=1 -Oona's\ Prowler=1 -Oona,\ Queen\ of\ the\ Fae=1 -Ooze\ Flux=1 -Ooze\ Garden=1 -Opal\ Acrolith=1 -Opal\ Archangel=1 -Opal\ Avenger=1 -Opal\ Caryatid=1 -Opal\ Champion=1 -Opal\ Gargoyle=1 -Opal\ Guardian=1 -Opal\ Lake\ Gatekeepers=1 -Opaline\ Sliver=1 -Opaline\ Unicorn=1 -Open\ Fire=1 -Open\ into\ Wonder=1 -Open\ the\ Armory=1 -Ophidian=1 -Opportunist=1 -Opportunity=1 -Oppressive\ Rays=1 -Oppressive\ Will=1 -Oracle's\ Attendants=1 -Oracle's\ Insight=1 -Oracle's\ Vault=1 -Oracle\ en-Vec=1 -Oracle\ of\ Bones=1 -Oracle\ of\ Dust=1 -Oran-Rief\ Hydra=1 -Oran-Rief\ Invoker=1 -Oran-Rief\ Recluse=1 -Orator\ of\ Ojutai=1 -Oraxid=1 -Orazca\ Frillback=1 -Orazca\ Raptor=1 -Orazca\ Relic=1 -Orb\ of\ Dreams=1 -Orbs\ of\ Warding=1 -Orbweaver\ Kumo=1 -Orchard\ Spirit=1 -Orchard\ Warden=1 -Orcish\ Artillery=1 -Orcish\ Bloodpainter=1 -Orcish\ Cannonade=1 -Orcish\ Cannoneers=1 -Orcish\ Captain=1 -Orcish\ Librarian=1 -Orcish\ Lumberjack=1 -Orcish\ Mechanics=1 -Orcish\ Oriflamme=1 -Orcish\ Spy=1 -Orcish\ Squatters=1 -Orcish\ Vandal=1 -Orcish\ Veteran=1 -Ordeal\ of\ Erebos=1 -Ordeal\ of\ Heliod=1 -Ordeal\ of\ Nylea=1 -Ordeal\ of\ Purphoros=1 -Ordeal\ of\ Thassa=1 -Order\ //\ Chaos=1 -Order\ of\ Yawgmoth=1 -Order\ of\ the\ Golden\ Cricket=1 -Order\ of\ the\ Sacred\ Bell=1 -Order\ of\ the\ Stars=1 -Order\ of\ the\ White\ Shield=1 -Ordered\ Migration=1 -Ordruun\ Commando=1 -Ordruun\ Veteran=1 -Ore\ Gorger=1 -Oreskos\ Sun\ Guide=1 -Oreskos\ Swiftclaw=1 -Organ\ Grinder=1 -Orgg=1 -Origin\ Spellbomb=1 -Orim,\ Samite\ Healer=1 -Ornamental\ Courage=1 -Ornery\ Kudu=1 -Ornitharch=1 -Orochi\ Eggwatcher=1 -Orochi\ Hatchery=1 -Orochi\ Leafcaller=1 -Orochi\ Ranger=1 -Orochi\ Sustainer=1 -Oros,\ the\ Avenger=1 -Orzhov\ Charm=1 -Orzhov\ Cluestone=1 -Orzhov\ Euthanist=1 -Orzhov\ Guildgate=1 -Orzhov\ Guildmage=1 -Orzhov\ Keyrune=1 -Orzhova,\ the\ Church\ of\ Deals=1 -Osai\ Vultures=1 -Ostiary\ Thrull=1 -Ostracize=1 -Otarian\ Juggernaut=1 -Otepec\ Huntmaster=1 -Otherworldly\ Journey=1 -Otherworldly\ Outburst=1 -Ouphe\ Vandals=1 -Outbreak=1 -Outland\ Boar=1 -Outland\ Colossus=1 -Outmaneuver=1 -Outnumber=1 -Outrage\ Shaman=1 -Outrider\ en-Kor=1 -Outrider\ of\ Jhess=1 -Ovalchase\ Daredevil=1 -Ovalchase\ Dragster=1 -Overblaze=1 -Overcome=1 -Overgrown\ Armasaur=1 -Override=1 -Overrule=1 -Overrun=1 -Overwhelm=1 -Overwhelming\ Denial=1 -Overwhelming\ Stampede=1 -Ovinize=1 -Ovinomancer=1 -Oviya\ Pashiri,\ Sage\ Lifecrafter=1 -Owl\ Familiar=1 -Oxidda\ Daredevil=1 -Oxidda\ Golem=1 -Oxidda\ Scrapmelter=1 -Oyobi,\ Who\ Split\ the\ Heavens=1 -Pacification\ Array=1 -Pacifism=1 -Pack's\ Disdain=1 -Pack\ Guardian=1 -Pain\ //\ Suffering=1 -Pain\ Kami=1 -Pain\ Magnification=1 -Pain\ Seer=1 -Painbringer=1 -Painful\ Lesson=1 -Painful\ Memories=1 -Painful\ Quandary=1 -Painful\ Truths=1 -Painsmith=1 -Painted\ Bluffs=1 -Painwracker\ Oni=1 -Palace\ Familiar=1 -Palace\ Guard=1 -Paladin\ of\ Prahv=1 -Paladin\ of\ the\ Bloodstained=1 -Pale\ Recluse=1 -Pale\ Rider\ of\ Trostad=1 -Pale\ Wayfarer=1 -Paleoloth=1 -Palinchron=1 -Palisade\ Giant=1 -Palladia-Mors=1 -Pallid\ Mycoderm=1 -Pallimud=1 -Panacea=1 -Panic=1 -Panic\ Attack=1 -Panic\ Spellbomb=1 -Panoptic\ Mirror=1 -Panther\ Warriors=1 -Paperfin\ Rascal=1 -Paragon\ of\ Eternal\ Wilds=1 -Paragon\ of\ Fierce\ Defiance=1 -Paragon\ of\ Gathering\ Mists=1 -Paragon\ of\ New\ Dawns=1 -Paragon\ of\ Open\ Graves=1 -Paragon\ of\ the\ Amesha=1 -Parallax\ Wave=1 -Parallectric\ Feedback=1 -Paralyze=1 -Paralyzing\ Grasp=1 -Paranoid\ Delusions=1 -Paranoid\ Parish-Blade=1 -Parapet\ Watchers=1 -Paraselene=1 -Parasitic\ Bond=1 -Parasitic\ Implant=1 -Parasitic\ Strix=1 -Parch=1 -Pardic\ Collaborator=1 -Pardic\ Dragon=1 -Pardic\ Firecat=1 -Pardic\ Lancer=1 -Pardic\ Miner=1 -Pardic\ Wanderer=1 -Pariah's\ Shield=1 -Part\ the\ Veil=1 -Patagia\ Golem=1 -Patagia\ Viper=1 -Patchwork\ Gnomes=1 -Path\ of\ Anger's\ Flame=1 -Path\ of\ Bravery=1 -Pathmaker\ Initiate=1 -Pathrazer\ of\ Ulamog=1 -Pathway\ Arrows=1 -Patriarch's\ Desire=1 -Patron\ of\ the\ Akki=1 -Patron\ of\ the\ Kitsune=1 -Patron\ of\ the\ Moon=1 -Patron\ of\ the\ Nezumi=1 -Patron\ of\ the\ Valiant=1 -Patron\ of\ the\ Wild=1 -Pawn\ of\ Ulamog=1 -Pay\ No\ Heed=1 -Peace\ Strider=1 -Peace\ and\ Quiet=1 -Peace\ of\ Mind=1 -Peacewalker\ Colossus=1 -Peach\ Garden\ Oath=1 -Peak\ Eruption=1 -Pearl\ Shard=1 -Pearlspear\ Courier=1 -Peel\ from\ Reality=1 -Peema\ Aether-Seer=1 -Peema\ Outrider=1 -Peer\ Pressure=1 -Peer\ Through\ Depths=1 -Pegasus\ Charger=1 -Pegasus\ Courser=1 -Pegasus\ Refuge=1 -Pegasus\ Stampede=1 -Pelakka\ Wurm=1 -Penance=1 -Pendelhaven\ Elder=1 -Pendrell\ Drake=1 -Pennon\ Blade=1 -Pensive\ Minotaur=1 -Pentagram\ of\ the\ Ages=1 -Pentarch\ Paladin=1 -Pentarch\ Ward=1 -Pentavus=1 -Penumbra\ Bobcat=1 -Penumbra\ Kavu=1 -Penumbra\ Spider=1 -Penumbra\ Wurm=1 -Peppersmoke=1 -Peregrination=1 -Peregrine\ Griffin=1 -Peregrine\ Mask=1 -Perilous\ Forays=1 -Perilous\ Myr=1 -Perilous\ Predicament=1 -Perilous\ Shadow=1 -Perilous\ Voyage=1 -Perish\ the\ Thought=1 -Permafrost\ Trap=1 -Permeating\ Mass=1 -Perpetual\ Timepiece=1 -Perplex=1 -Perplexing\ Chimera=1 -Persecute=1 -Personal\ Incarnation=1 -Personal\ Sanctuary=1 -Persuasion=1 -Pestilence\ Demon=1 -Pestilent\ Souleater=1 -Petalmane\ Baku=1 -Petals\ of\ Insight=1 -Petradon=1 -Petrahydrox=1 -Petravark=1 -Petrified\ Wood-Kin=1 -Pewter\ Golem=1 -Phalanx\ Formation=1 -Phalanx\ Leader=1 -Phantasmagorian=1 -Phantasmal\ Abomination=1 -Phantasmal\ Bear=1 -Phantasmal\ Dragon=1 -Phantasmal\ Fiend=1 -Phantasmal\ Mount=1 -Phantasmal\ Terrain=1 -Phantatog=1 -Phantom\ Beast=1 -Phantom\ Centaur=1 -Phantom\ Flock=1 -Phantom\ General=1 -Phantom\ Monster=1 -Phantom\ Nomad=1 -Phantom\ Tiger=1 -Phantom\ Warrior=1 -Phantom\ Wurm=1 -Pharagax\ Giant=1 -Pharika's\ Cure=1 -Pharika's\ Disciple=1 -Pharika's\ Mender=1 -Pheres-Band\ Centaurs=1 -Pheres-Band\ Raiders=1 -Pheres-Band\ Thunderhoof=1 -Pheres-Band\ Tromper=1 -Pheres-Band\ Warchief=1 -Phobian\ Phantasm=1 -Phosphorescent\ Feast=1 -Phthisis=1 -Phylactery\ Lich=1 -Phyresis=1 -Phyrexia's\ Core=1 -Phyrexian\ Battleflies=1 -Phyrexian\ Bloodstock=1 -Phyrexian\ Colossus=1 -Phyrexian\ Defiler=1 -Phyrexian\ Denouncer=1 -Phyrexian\ Devourer=1 -Phyrexian\ Digester=1 -Phyrexian\ Etchings=1 -Phyrexian\ Gargantua=1 -Phyrexian\ Grimoire=1 -Phyrexian\ Hulk=1 -Phyrexian\ Hydra=1 -Phyrexian\ Infiltrator=1 -Phyrexian\ Ingester=1 -Phyrexian\ Ironfoot=1 -Phyrexian\ Juggernaut=1 -Phyrexian\ Monitor=1 -Phyrexian\ Prowler=1 -Phyrexian\ Reaper=1 -Phyrexian\ Rebirth=1 -Phyrexian\ Slayer=1 -Phyrexian\ Splicer=1 -Phyrexian\ Totem=1 -Phyrexian\ Vatmother=1 -Phyrexian\ Vault=1 -Phytoburst=1 -Phytohydra=1 -Phytotitan=1 -Pia's\ Revolution=1 -Pianna,\ Nomad\ Captain=1 -Pick\ the\ Brain=1 -Pierce\ Strider=1 -Pierce\ the\ Sky=1 -Piety\ Charm=1 -Pilfered\ Plans=1 -Pilgrim's\ Eye=1 -Pilgrim\ of\ Justice=1 -Pilgrim\ of\ Virtue=1 -Pilgrim\ of\ the\ Fires=1 -Pillage=1 -Pillar\ Tombs\ of\ Aku=1 -Pillar\ of\ Origins=1 -Pillar\ of\ War=1 -Pillar\ of\ the\ Paruns=1 -Pillarfield\ Ox=1 -Pillory\ of\ the\ Sleepless=1 -Pin\ to\ the\ Earth=1 -Pincer\ Spider=1 -Pincher\ Beetles=1 -Pine\ Barrens=1 -Pine\ Walker=1 -Pinecrest\ Ridge=1 -Pinion\ Feast=1 -Pious\ Evangel=1 -Pious\ Interdiction=1 -Pious\ Kitsune=1 -Piper's\ Melody=1 -Piranha\ Marsh=1 -Pirate's\ Cutlass=1 -Pirate's\ Pillage=1 -Pirate's\ Prize=1 -Pirate\ Ship=1 -Pit\ Fight=1 -Pit\ Keeper=1 -Pit\ Raptor=1 -Pit\ Trap=1 -Pitfall\ Trap=1 -Pith\ Driller=1 -Pitiless\ Horde=1 -Pitiless\ Plunderer=1 -Pitiless\ Vizier=1 -Plagiarize=1 -Plague\ Beetle=1 -Plague\ Belcher=1 -Plague\ Boiler=1 -Plague\ Sliver=1 -Plague\ Spores=1 -Plague\ Wind=1 -Plague\ of\ Vermin=1 -Plaguemaw\ Beast=1 -Plains=1 -Planar\ Cleansing=1 -Planar\ Guide=1 -Planar\ Outburst=1 -Planar\ Overlay=1 -Planar\ Void=1 -Planeswalker's\ Fury=1 -Planeswalker's\ Mirth=1 -Planeswalker's\ Scorn=1 -Plasma\ Elemental=1 -Plated\ Crusher=1 -Plated\ Geopede=1 -Plated\ Pegasus=1 -Plated\ Seastrider=1 -Plated\ Spider=1 -Plaxcaster\ Frogling=1 -Plaxmanta=1 -Plea\ for\ Guidance=1 -Pledge\ of\ Loyalty=1 -Plow\ Through\ Reito=1 -Plumes\ of\ Peace=1 -Plumeveil=1 -Plummet=1 -Plunder=1 -Poison\ the\ Well=1 -Poisonbelly\ Ogre=1 -Polis\ Crusher=1 -Pollen\ Lullaby=1 -Pollen\ Remedy=1 -Pollenbright\ Wings=1 -Polluted\ Bonds=1 -Polluted\ Dead=1 -Polymorphist's\ Jest=1 -Polymorphous\ Rush=1 -Pontiff\ of\ Blight=1 -Ponyback\ Brigade=1 -Pooling\ Venom=1 -Pore\ Over\ the\ Pages=1 -Portent\ of\ Betrayal=1 -Possessed\ Aven=1 -Possessed\ Barbarian=1 -Possessed\ Centaur=1 -Possessed\ Nomad=1 -Possessed\ Skaab=1 -Poultice\ Sliver=1 -Pounce=1 -Pouncing\ Cheetah=1 -Pouncing\ Kavu=1 -Power\ Armor=1 -Power\ Taint=1 -Power\ of\ Fire=1 -Powerstone\ Minefield=1 -Powerstone\ Shard=1 -Prahv,\ Spires\ of\ Order=1 -Prakhata\ Club\ Security=1 -Prakhata\ Pillar-Bug=1 -Precinct\ Captain=1 -Precise\ Strike=1 -Precognition=1 -Precognition\ Field=1 -Precursor\ Golem=1 -Predator's\ Rapport=1 -Predator,\ Flagship=1 -Predator\ Dragon=1 -Predatory\ Advantage=1 -Predatory\ Nightstalker=1 -Predatory\ Urge=1 -Premature\ Burial=1 -Prepare\ //\ Fight=1 -Prescient\ Chimera=1 -Presence\ of\ the\ Master=1 -Presence\ of\ the\ Wise=1 -Press\ into\ Service=1 -Press\ the\ Advantage=1 -Pressure\ Point=1 -Prey's\ Vengeance=1 -Prey\ Upon=1 -Prickleboar=1 -Prickly\ Boggart=1 -Pride\ Guardian=1 -Pride\ of\ Conquerors=1 -Priest\ of\ Gix=1 -Priest\ of\ Iroas=1 -Priest\ of\ Urabrask=1 -Priest\ of\ the\ Blood\ Rite=1 -Priests\ of\ Norn=1 -Primal\ Bellow=1 -Primal\ Beyond=1 -Primal\ Clay=1 -Primal\ Druid=1 -Primal\ Forcemage=1 -Primal\ Huntbeast=1 -Primal\ Plasma=1 -Primal\ Rage=1 -Primal\ Visitation=1 -Primal\ Whisperer=1 -Primeval\ Force=1 -Primeval\ Light=1 -Primeval\ Shambler=1 -Primevals'\ Glorious\ Rebirth=1 -Primitive\ Etchings=1 -Primitive\ Justice=1 -Primordial\ Sage=1 -Primordial\ Wurm=1 -Prism\ Array=1 -Prism\ Ring=1 -Prismatic\ Boon=1 -Prismatic\ Lens=1 -Prismwake\ Merrow=1 -Prison\ Barricade=1 -Prison\ Term=1 -Pristine\ Angel=1 -Pristine\ Skywise=1 -Private\ Research=1 -Prized\ Elephant=1 -Prized\ Unicorn=1 -Prizefighter\ Construct=1 -Processor\ Assault=1 -Prodigal\ Pyromancer=1 -Prodigal\ Sorcerer=1 -Profane\ Command=1 -Profane\ Prayers=1 -Profaner\ of\ the\ Dead=1 -Profit\ //\ Loss=1 -Profound\ Journey=1 -Prognostic\ Sphinx=1 -Promise\ of\ Power=1 -Promised\ Kannushi=1 -Propeller\ Pioneer=1 -Proper\ Burial=1 -Prophet\ of\ Distortion=1 -Prophet\ of\ Kruphix=1 -Prophetic\ Bolt=1 -Prophetic\ Ravings=1 -Prosperous\ Pirates=1 -Protean\ Hydra=1 -Protean\ Raider=1 -Protection\ of\ the\ Hekma=1 -Protective\ Bubble=1 -Proteus\ Machine=1 -Protomatter\ Powder=1 -Prototype\ Portal=1 -Proven\ Combatant=1 -Providence=1 -Prowess\ of\ the\ Fair=1 -Prowler's\ Helm=1 -Prowling\ Nightstalker=1 -Prowling\ Pangolin=1 -Prying\ Blade=1 -Prying\ Questions=1 -Psionic\ Gift=1 -Psionic\ Sliver=1 -Psychatog=1 -Psychic\ Barrier=1 -Psychic\ Drain=1 -Psychic\ Intrusion=1 -Psychic\ Membrane=1 -Psychic\ Miasma=1 -Psychic\ Overload=1 -Psychic\ Possession=1 -Psychic\ Puppetry=1 -Psychic\ Purge=1 -Psychic\ Rebuttal=1 -Psychic\ Spear=1 -Psychic\ Spiral=1 -Psychic\ Surgery=1 -Psychic\ Symbiont=1 -Psychic\ Trance=1 -Psychic\ Transfer=1 -Psychogenic\ Probe=1 -Psychotic\ Episode=1 -Psychotic\ Fury=1 -Psychotrope\ Thallid=1 -Pterodon\ Knight=1 -Pteron\ Ghost=1 -Public\ Execution=1 -Puca's\ Mischief=1 -Puffer\ Extract=1 -Pull\ Under=1 -Pull\ from\ Eternity=1 -Pull\ from\ the\ Deep=1 -Pulling\ Teeth=1 -Pulsating\ Illusion=1 -Pulse\ of\ Llanowar=1 -Pulse\ of\ the\ Dross=1 -Pulse\ of\ the\ Fields=1 -Pulse\ of\ the\ Forge=1 -Pulse\ of\ the\ Grid=1 -Pulse\ of\ the\ Tangle=1 -Puncture\ Blast=1 -Puncture\ Bolt=1 -Puncturing\ Blow=1 -Puncturing\ Light=1 -Punish\ Ignorance=1 -Puppet\ Conjurer=1 -Puppet\ Strings=1 -Puppeteer=1 -Puppeteer\ Clique=1 -Pure\ //\ Simple=1 -Pure\ Reflection=1 -Puresight\ Merrow=1 -Purge\ the\ Profane=1 -Purging\ Scythe=1 -Purity=1 -Purphoros's\ Emissary=1 -Pursue\ Glory=1 -Pursuit\ of\ Flight=1 -Pus\ Kami=1 -Put\ Away=1 -Putrefaction=1 -Putrefax=1 -Putrefy=1 -Putrid\ Cyclops=1 -Putrid\ Raptor=1 -Putrid\ Warrior=1 -Pygmy\ Kavu=1 -Pygmy\ Pyrosaur=1 -Pygmy\ Razorback=1 -Pygmy\ Troll=1 -Pyramid\ of\ the\ Pantheon=1 -Pyre\ Charger=1 -Pyre\ Hound=1 -Pyrewild\ Shaman=1 -Pyric\ Salamander=1 -Pyrite\ Spellbomb=1 -Pyroclasm=1 -Pyroclast\ Consul=1 -Pyrohemia=1 -Pyromancer's\ Assault=1 -Pyromancer's\ Gauntlet=1 -Pyromancer's\ Swath=1 -Pyromancy=1 -Pyromania=1 -Pyrotechnics=1 -Pyrrhic\ Revival=1 -Python=1 -Pyxis\ of\ Pandemonium=1 -Qal\ Sisma\ Behemoth=1 -Qarsi\ Deceiver=1 -Qasali\ Ambusher=1 -Quag\ Sickness=1 -Quag\ Vampires=1 -Quagmire\ Druid=1 -Quarry\ Beetle=1 -Quarry\ Colossus=1 -Quarry\ Hauler=1 -Quash=1 -Queen's\ Agent=1 -Queen's\ Bay\ Soldier=1 -Queen's\ Commission=1 -Quenchable\ Fire=1 -Quest\ for\ Ancient\ Secrets=1 -Quest\ for\ Renewal=1 -Quest\ for\ Ula's\ Temple=1 -Quest\ for\ the\ Gemblades=1 -Quest\ for\ the\ Gravelord=1 -Quicken=1 -Quicksand=1 -Quicksilver\ Behemoth=1 -Quicksilver\ Dagger=1 -Quicksilver\ Dragon=1 -Quicksilver\ Fountain=1 -Quicksilver\ Geyser=1 -Quicksilver\ Wall=1 -Quicksmith\ Genius=1 -Quicksmith\ Rebel=1 -Quicksmith\ Spy=1 -Quiet\ Contemplation=1 -Quiet\ Purity=1 -Quiet\ Speculation=1 -Quietus\ Spike=1 -Quill-Slinger\ Boggart=1 -Quilled\ Slagwurm=1 -Quilled\ Sliver=1 -Quilled\ Wolf=1 -Quillmane\ Baku=1 -Quirion\ Dryad=1 -Quirion\ Explorer=1 -Quirion\ Sentinel=1 -Qumulox=1 -Rabble-Rouser=1 -Rabid\ Bite=1 -Rabid\ Bloodsucker=1 -Rabid\ Rats=1 -Rabid\ Wolverines=1 -Rabid\ Wombat=1 -Rack\ and\ Ruin=1 -Radiant's\ Dragoons=1 -Radiant's\ Judgment=1 -Radiant,\ Archangel=1 -Radiant\ Flames=1 -Radiant\ Purge=1 -Radiating\ Lightning=1 -Radjan\ Spirit=1 -Raff\ Capashen,\ Ship's\ Mage=1 -Ragamuffyn=1 -Rage\ Forger=1 -Rage\ Reflection=1 -Rage\ Thrower=1 -Rage\ Weaver=1 -Rage\ of\ Purphoros=1 -Rageblood\ Shaman=1 -Ragemonger=1 -Ragged\ Veins=1 -Raging\ Goblin=1 -Raging\ Gorilla=1 -Raging\ Kavu=1 -Raging\ Minotaur=1 -Raging\ Regisaur=1 -Raging\ Swordtooth=1 -Rags\ //\ Riches=1 -Raid\ Bombardment=1 -Raiders'\ Spoils=1 -Raiders'\ Wake=1 -Rain\ of\ Daggers=1 -Rain\ of\ Embers=1 -Rain\ of\ Rust=1 -Rain\ of\ Salt=1 -Rain\ of\ Thorns=1 -Rainbow\ Crow=1 -Rainbow\ Efreet=1 -Raise\ Dead=1 -Raised\ by\ Wolves=1 -Raka\ Disciple=1 -Raka\ Sanctuary=1 -Rakalite=1 -Rakavolver=1 -Rakdos\ Augermage=1 -Rakdos\ Cackler=1 -Rakdos\ Cluestone=1 -Rakdos\ Drake=1 -Rakdos\ Guildgate=1 -Rakdos\ Guildmage=1 -Rakdos\ Ickspitter=1 -Rakdos\ Keyrune=1 -Rakdos\ Pit\ Dragon=1 -Rakdos\ Ragemutt=1 -Rakdos\ Ringleader=1 -Rakdos\ Riteknife=1 -Rakdos\ Shred-Freak=1 -Rakdos\ Signet=1 -Rakeclaw\ Gargantuan=1 -Raking\ Canopy=1 -Rakish\ Heir=1 -Rakka\ Mar=1 -Raksha\ Golden\ Cub=1 -Rakshasa's\ Secret=1 -Rakshasa\ Deathdealer=1 -Rakshasa\ Gravecaller=1 -Rakshasa\ Vizier=1 -Rally\ the\ Forces=1 -Rally\ the\ Horde=1 -Rally\ the\ Peasants=1 -Rally\ the\ Righteous=1 -Rallying\ Roar=1 -Ramirez\ DePietro=1 -Ramosian\ Commander=1 -Ramosian\ Revivalist=1 -Rampaging\ Cyclops=1 -Rampaging\ Hippo=1 -Rampant\ Growth=1 -Ramroller=1 -Ramunap\ Hydra=1 -Ramunap\ Ruins=1 -Rancid\ Rats=1 -Ranger's\ Guile=1 -Ranger\ en-Vec=1 -Ranging\ Raptors=1 -Rapacious\ One=1 -Raptor\ Companion=1 -Raptor\ Hatchling=1 -Ratcatcher=1 -Rathi\ Dragon=1 -Rathi\ Fiend=1 -Rathi\ Trapper=1 -Rats'\ Feast=1 -Rats\ of\ Rath=1 -Rattleblaze\ Scarecrow=1 -Rattleclaw\ Mystic=1 -Ravaged\ Highlands=1 -Ravaging\ Blaze=1 -Ravaging\ Riftwurm=1 -Raven\ Familiar=1 -Raven\ Guild\ Initiate=1 -Ravenous\ Baloth=1 -Ravenous\ Bloodseeker=1 -Ravenous\ Daggertooth=1 -Ravenous\ Demon=1 -Ravenous\ Harpy=1 -Ravenous\ Intruder=1 -Raving\ Oni-Slave=1 -Ray\ of\ Command=1 -Ray\ of\ Dissolution=1 -Ray\ of\ Distortion=1 -Razaketh's\ Rite=1 -Razia's\ Purification=1 -Razia,\ Boros\ Archangel=1 -Razing\ Snidd=1 -Razor\ Barrier=1 -Razor\ Boomerang=1 -Razor\ Golem=1 -Razor\ Hippogriff=1 -Razor\ Pendulum=1 -Razor\ Swine=1 -Razorfin\ Abolisher=1 -Razorfoot\ Griffin=1 -Razorgrass\ Screen=1 -Razormane\ Masticore=1 -Razortip\ Whip=1 -Razortooth\ Rats=1 -Reach\ Through\ Mists=1 -Reach\ of\ Branches=1 -Reach\ of\ Shadows=1 -Read\ the\ Runes=1 -Reality\ Acid=1 -Reality\ Anchor=1 -Reality\ Hemorrhage=1 -Reality\ Ripple=1 -Reality\ Spasm=1 -Reality\ Strobe=1 -Realm\ Razer=1 -Realm\ Seekers=1 -Realms\ Uncharted=1 -Realmwright=1 -Reap=1 -Reap\ Intellect=1 -Reap\ What\ Is\ Sown=1 -Reap\ the\ Seagraf=1 -Reaper\ of\ Flight\ Moonsilver=1 -Reaper\ of\ Sheoldred=1 -Reaper\ of\ the\ Wilds=1 -Reaping\ the\ Rewards=1 -Reason\ //\ Believe=1 -Reassembling\ Skeleton=1 -Reave\ Soul=1 -Reaver\ Ambush=1 -Reaver\ Drone=1 -Rebellion\ of\ the\ Flamekin=1 -Reborn\ Hero=1 -Reborn\ Hope=1 -Rebound=1 -Rebuff\ the\ Wicked=1 -Rebuke=1 -Rebuking\ Ceremony=1 -Recantation=1 -Reciprocate=1 -Reckless\ Charge=1 -Reckless\ Cohort=1 -Reckless\ Embermage=1 -Reckless\ Fireweaver=1 -Reckless\ Imp=1 -Reckless\ Ogre=1 -Reckless\ One=1 -Reckless\ Racer=1 -Reckless\ Rage=1 -Reckless\ Reveler=1 -Reckless\ Scholar=1 -Reckless\ Spite=1 -Reckless\ Waif=1 -Reckless\ Wurm=1 -Reclaim=1 -Reclusive\ Artificer=1 -Reclusive\ Wight=1 -Recollect=1 -Reconstruction=1 -Recoup=1 -Recover=1 -Recumbent\ Bliss=1 -Recuperate=1 -Recurring\ Nightmare=1 -Red\ Cliffs\ Armada=1 -Red\ Sun's\ Zenith=1 -Redeem=1 -Redeem\ the\ Lost=1 -Redirect=1 -Reduce\ //\ Rubble=1 -Reduce\ in\ Stature=1 -Reduce\ to\ Ashes=1 -Reduce\ to\ Dreams=1 -Redwood\ Treefolk=1 -Reflex\ Sliver=1 -Reflexes=1 -Refraction\ Trap=1 -Refresh=1 -Refreshing\ Rain=1 -Refuse\ //\ Cooperate=1 -Regenerate=1 -Regeneration=1 -Regress=1 -Reign\ of\ the\ Pit=1 -Reinforced\ Bulwark=1 -Reinforcements=1 -Reins\ of\ the\ Vinesteed=1 -Reiterate=1 -Reito\ Lantern=1 -Reiver\ Demon=1 -Rejuvenate=1 -Rejuvenation\ Chamber=1 -Rekindled\ Flame=1 -Reknit=1 -Relearn=1 -Release\ the\ Ants=1 -Release\ the\ Gremlins=1 -Release\ to\ the\ Wind=1 -Relentless\ Assault=1 -Relentless\ Hunter=1 -Relentless\ Raptor=1 -Relentless\ Rats=1 -Relentless\ Skaabs=1 -Relic\ Bane=1 -Relic\ Barrier=1 -Relic\ Crush=1 -Relic\ Putrescence=1 -Relic\ Runner=1 -Relic\ Seeker=1 -Relic\ Ward=1 -Relief\ Captain=1 -Reliquary\ Monk=1 -Remember\ the\ Fallen=1 -Reminisce=1 -Remorseless\ Punishment=1 -Rend\ Flesh=1 -Render\ Silent=1 -Rending\ Vines=1 -Renegade's\ Getaway=1 -Renegade\ Doppelganger=1 -Renegade\ Freighter=1 -Renegade\ Krasis=1 -Renegade\ Rallier=1 -Renegade\ Tactics=1 -Renegade\ Warlord=1 -Renegade\ Wheelsmith=1 -Renewed\ Faith=1 -Renounce=1 -Renounce\ the\ Guilds=1 -Renowned\ Weaver=1 -Repay\ in\ Kind=1 -Repeal=1 -Repeating\ Barrage=1 -Repel\ Intruders=1 -Repel\ the\ Abominable=1 -Repentance=1 -Repentant\ Vampire=1 -Repopulate=1 -Reprisal=1 -Reprocess=1 -Requiem\ Angel=1 -Reroute=1 -Rescind=1 -Rescue=1 -Rescue\ from\ the\ Underworld=1 -Research\ //\ Development=1 -Research\ Assistant=1 -Research\ the\ Deep=1 -Reservoir\ Walker=1 -Resilient\ Wanderer=1 -Resistance\ Fighter=1 -Resize=1 -Resolute\ Archangel=1 -Resolute\ Blademaster=1 -Resolute\ Survivors=1 -Resounding\ Scream=1 -Resounding\ Silence=1 -Resounding\ Thunder=1 -Resounding\ Wave=1 -Resourceful\ Return=1 -Resplendent\ Griffin=1 -Resplendent\ Mentor=1 -Rest\ for\ the\ Weary=1 -Restless\ Apparition=1 -Restless\ Bones=1 -Restless\ Dreams=1 -Restock=1 -Restoration\ Gearsmith=1 -Restoration\ Specialist=1 -Restore\ the\ Peace=1 -Restrain=1 -Resupply=1 -Resurrection=1 -Retaliate=1 -Retaliation=1 -Retaliator\ Griffin=1 -Retether=1 -Rethink=1 -Retraction\ Helix=1 -Retreat\ to\ Coralhelm=1 -Retreat\ to\ Emeria=1 -Retreat\ to\ Hagra=1 -Retreat\ to\ Kazandu=1 -Retreat\ to\ Valakut=1 -Retribution=1 -Retribution\ of\ the\ Ancients=1 -Retromancer=1 -Return\ to\ the\ Earth=1 -Returned\ Centaur=1 -Returned\ Phalanx=1 -Returned\ Reveler=1 -Revealing\ Wind=1 -Reveille\ Squad=1 -Revel\ in\ Riches=1 -Revel\ of\ the\ Fallen\ God=1 -Revelsong\ Horn=1 -Revenant=1 -Revenant\ Patriarch=1 -Reverberate=1 -Revered\ Dead=1 -Revered\ Elder=1 -Revered\ Unicorn=1 -Reverence=1 -Reverent\ Hunter=1 -Reversal\ of\ Fortune=1 -Reverse\ Engineer=1 -Reverse\ the\ Sands=1 -Revive=1 -Reviving\ Dose=1 -Reviving\ Melody=1 -Reviving\ Vapors=1 -Revoke\ Existence=1 -Revoke\ Privileges=1 -Revolutionary\ Rebuff=1 -Reward\ the\ Faithful=1 -Rewards\ of\ Diversity=1 -Reweave=1 -Rewind=1 -Rhet-Crop\ Spearmaster=1 -Rhonas's\ Last\ Stand=1 -Rhonas's\ Monument=1 -Rhonas's\ Stalwart=1 -Rhox=1 -Rhox\ Bodyguard=1 -Rhox\ Brute=1 -Rhox\ Charger=1 -Rhox\ Maulers=1 -Rhox\ Meditant=1 -Rhox\ Oracle=1 -Rhox\ Pikemaster=1 -Rhox\ War\ Monk=1 -Rhystic\ Shield=1 -Rib\ Cage\ Spider=1 -Ribbon\ Snake=1 -Ribbons\ of\ the\ Reikai=1 -Riddle\ of\ Lightning=1 -Riddleform=1 -Riddlesmith=1 -Ride\ Down=1 -Ridge\ Rannet=1 -Ridged\ Kusite=1 -Ridgeline\ Rager=1 -Ridgescale\ Tusker=1 -Ridgetop\ Raptor=1 -Riding\ the\ Dilu\ Horse=1 -Rift\ Elemental=1 -Riftmarked\ Knight=1 -Riftsweeper=1 -Riftwing\ Cloudskate=1 -Righteous\ Authority=1 -Righteous\ Avengers=1 -Righteous\ Blow=1 -Righteous\ Charge=1 -Righteous\ Fury=1 -Righteousness=1 -Rile=1 -Rime\ Transfusion=1 -Rimebound\ Dead=1 -Rimefeather\ Owl=1 -Rimescale\ Dragon=1 -Rimewind\ Cryomancer=1 -Rimewind\ Taskmage=1 -Ring\ of\ Evos\ Isle=1 -Ring\ of\ Gix=1 -Ring\ of\ Kalonia=1 -Ring\ of\ Thune=1 -Ring\ of\ Valkas=1 -Ring\ of\ Xathrid=1 -Ringskipper=1 -Ringwarden\ Owl=1 -Riot\ Control=1 -Riot\ Gear=1 -Riot\ Piker=1 -Riot\ Spikes=1 -Riparian\ Tiger=1 -Ripscale\ Predator=1 -Riptide\ Biologist=1 -Riptide\ Chimera=1 -Riptide\ Chronologist=1 -Riptide\ Entrancer=1 -Riptide\ Mangler=1 -Riptide\ Pilferer=1 -Riptide\ Replicator=1 -Rise\ from\ the\ Grave=1 -Rise\ from\ the\ Tides=1 -Rise\ of\ Eagles=1 -Rise\ to\ the\ Challenge=1 -Risen\ Sanctuary=1 -Rishadan\ Airship=1 -Rishkar's\ Expertise=1 -Rising\ Miasma=1 -Rite\ of\ Belzenlok=1 -Rite\ of\ Ruin=1 -Rite\ of\ Undoing=1 -Rites\ of\ Initiation=1 -Rites\ of\ Reaping=1 -Rites\ of\ Refusal=1 -Rith's\ Attendant=1 -Ritual\ of\ Rejuvenation=1 -Ritual\ of\ Restoration=1 -Ritual\ of\ Subdual=1 -Ritual\ of\ the\ Returned=1 -Rivalry=1 -Rivals'\ Duel=1 -River's\ Grasp=1 -River\ Bear=1 -River\ Darter=1 -River\ Heralds'\ Boon=1 -River\ Hoopoe=1 -River\ Kaijin=1 -River\ Merfolk=1 -River\ Serpent=1 -River\ Sneak=1 -Riverfall\ Mimic=1 -Riverwheel\ Aerialists=1 -Riverwise\ Augur=1 -Rix\ Maadi,\ Dungeon\ Palace=1 -Rix\ Maadi\ Guildmage=1 -Roar\ of\ Challenge=1 -Roar\ of\ Jukai=1 -Roar\ of\ Reclamation=1 -Roar\ of\ the\ Crowd=1 -Roar\ of\ the\ Wurm=1 -Roaring\ Primadox=1 -Roaring\ Slagwurm=1 -Robber\ Fly=1 -Robe\ of\ Mirrors=1 -Roc\ Egg=1 -Roc\ Hatchling=1 -Rock\ Badger=1 -Rock\ Basilisk=1 -Rock\ Hydra=1 -Rock\ Jockey=1 -Rock\ Slide=1 -Rockshard\ Elemental=1 -Rockslide\ Ambush=1 -Rockslide\ Elemental=1 -Rocky\ Tar\ Pit=1 -Rod\ of\ Ruin=1 -Rofellos,\ Llanowar\ Emissary=1 -Rogue's\ Gloves=1 -Rogue's\ Passage=1 -Rogue\ Kavu=1 -Rogue\ Refiner=1 -Rogue\ Skycaptain=1 -Roil's\ Retribution=1 -Roil\ Spout=1 -Roiling\ Horror=1 -Roiling\ Terrain=1 -Roiling\ Waters=1 -Roilmage's\ Trick=1 -Rollick\ of\ Abandon=1 -Rolling\ Spoil=1 -Rolling\ Temblor=1 -Rolling\ Thunder=1 -Rona,\ Disciple\ of\ Gix=1 -Ronin\ Cavekeeper=1 -Ronin\ Cliffrider=1 -Ronin\ Houndmaster=1 -Ronin\ Warclub=1 -Ronom\ Hulk=1 -Ronom\ Serpent=1 -Ronom\ Unicorn=1 -Roofstalker\ Wight=1 -Rooftop\ Storm=1 -Root-Kin\ Ally=1 -Root\ Out=1 -Root\ Snare=1 -Rootborn\ Defenses=1 -Rootbreaker\ Wurm=1 -Rootgrapple=1 -Rooting\ Kavu=1 -Rootrunner=1 -Roots=1 -Rootwalla=1 -Rootwater\ Alligator=1 -Rootwater\ Diver=1 -Rootwater\ Hunter=1 -Rootwater\ Matriarch=1 -Rootwater\ Mystic=1 -Rorix\ Bladewing=1 -Rosheen\ Meanderer=1 -Rot\ Farm\ Skeleton=1 -Rot\ Shambler=1 -Rot\ Wolf=1 -Rotcrown\ Ghoul=1 -Roterothopter=1 -Rotfeaster\ Maggot=1 -Rotted\ Hulk=1 -Rottenheart\ Ghoul=1 -Rotting\ Giant=1 -Rotting\ Legion=1 -Rotting\ Mastodon=1 -Roughshod\ Mentor=1 -Rouse\ the\ Mob=1 -Royal\ Assassin=1 -Royal\ Decree=1 -Royal\ Trooper=1 -Rubbleback\ Rhino=1 -Rubblebelt\ Maaka=1 -Rubblebelt\ Raiders=1 -Rubblehulk=1 -Rude\ Awakening=1 -Rugged\ Highlands=1 -Ruham\ Djinn=1 -Ruin\ Processor=1 -Ruin\ Rat=1 -Ruin\ in\ Their\ Wake=1 -Ruination\ Guide=1 -Ruination\ Wurm=1 -Ruinous\ Gremlin=1 -Ruinous\ Minotaur=1 -Ruinous\ Path=1 -Ruins\ of\ Oran-Rief=1 -Ruins\ of\ Trokair=1 -Rukh\ Egg=1 -Rumbling\ Aftershocks=1 -Rumbling\ Baloth=1 -Rumbling\ Slum=1 -Rummaging\ Goblin=1 -Rummaging\ Wizard=1 -Run\ Aground=1 -Run\ Amok=1 -Run\ Wild=1 -Rune\ of\ Protection:\ Artifacts=1 -Rune\ of\ Protection:\ Lands=1 -Runeboggle=1 -Runechanter's\ Pike=1 -Runeclaw\ Bear=1 -Runed\ Servitor=1 -Runed\ Stalactite=1 -Runeflare\ Trap=1 -Runes\ of\ the\ Deus=1 -Runic\ Repetition=1 -Runner's\ Bane=1 -Rush\ of\ Adrenaline=1 -Rush\ of\ Battle=1 -Rush\ of\ Blood=1 -Rush\ of\ Ice=1 -Rush\ of\ Vitality=1 -Rushing-Tide\ Zubera=1 -Rushwood\ Dryad=1 -Rushwood\ Herbalist=1 -Rust\ Scarab=1 -Rusted\ Relic=1 -Rusted\ Sentinel=1 -Rusted\ Slasher=1 -Rustic\ Clachan=1 -Rusting\ Golem=1 -Rustmouth\ Ogre=1 -Rustrazor\ Butcher=1 -Rustspore\ Ram=1 -Rustwing\ Falcon=1 -Ruthless\ Cullblade=1 -Ruthless\ Deathfang=1 -Ruthless\ Disposal=1 -Ruthless\ Instincts=1 -Ruthless\ Knave=1 -Ruthless\ Ripper=1 -Ruthless\ Sniper=1 -Ryusei,\ the\ Falling\ Star=1 -Saberclaw\ Golem=1 -Sabertooth\ Outrider=1 -Sabertooth\ Wyvern=1 -Sabretooth\ Tiger=1 -Sacellum\ Godspeaker=1 -Sacred\ Armory=1 -Sacred\ Excavation=1 -Sacred\ Ground=1 -Sacred\ Mesa=1 -Sacred\ Nectar=1 -Sacred\ Prey=1 -Sacred\ Rites=1 -Sacred\ Wolf=1 -Saddleback\ Lagac=1 -Sadistic\ Augermage=1 -Sadistic\ Skymarcher=1 -Safe\ Passage=1 -Safeguard=1 -Safehold\ Sentry=1 -Safewright\ Quest=1 -Saffi\ Eriksdotter=1 -Sage's\ Dousing=1 -Sage's\ Row\ Denizen=1 -Sage-Eye\ Avengers=1 -Sage-Eye\ Harrier=1 -Sage\ Aven=1 -Sage\ Owl=1 -Sage\ of\ Ancient\ Lore=1 -Sage\ of\ Lat-Nam=1 -Sage\ of\ Shaila's\ Claim=1 -Sage\ of\ the\ Inward\ Eye=1 -Sages\ of\ the\ Anima=1 -Sagu\ Archer=1 -Sagu\ Mauler=1 -Saheeli's\ Artistry=1 -Sailmonger=1 -Sailor\ of\ Means=1 -Sakiko,\ Mother\ of\ Summer=1 -Sakura-Tribe\ Springcaller=1 -Salivating\ Gremlins=1 -Salt\ Flats=1 -Salt\ Marsh=1 -Salt\ Road\ Ambushers=1 -Salt\ Road\ Patrol=1 -Salt\ Road\ Quartermasters=1 -Saltblast=1 -Saltcrusted\ Steppe=1 -Saltfield\ Recluse=1 -Saltskitter=1 -Salvage\ Drone=1 -Salvage\ Scout=1 -Salvage\ Scuttler=1 -Salvage\ Slasher=1 -Salvage\ Titan=1 -Salvager\ of\ Secrets=1 -Salvaging\ Station=1 -Samite\ Archer=1 -Samite\ Blessing=1 -Samite\ Censer-Bearer=1 -Samite\ Healer=1 -Samite\ Ministration=1 -Samite\ Pilgrim=1 -Samite\ Sanctuary=1 -Samurai\ of\ the\ Pale\ Curtain=1 -Sanctified\ Charge=1 -Sanctifier\ of\ Souls=1 -Sanctuary\ Cat=1 -Sanctum\ Gargoyle=1 -Sanctum\ Guardian=1 -Sanctum\ Plowbeast=1 -Sanctum\ Spirit=1 -Sand\ Golem=1 -Sand\ Strangler=1 -Sandbar\ Merfolk=1 -Sandbar\ Serpent=1 -Sandblast=1 -Sandcrafter\ Mage=1 -Sands\ of\ Delirium=1 -Sandskin=1 -Sandsower=1 -Sandsteppe\ Citadel=1 -Sandsteppe\ Outcast=1 -Sandsteppe\ Scavenger=1 -Sandstone\ Bridge=1 -Sandstone\ Deadfall=1 -Sandstone\ Warrior=1 -Sandstorm\ Eidolon=1 -Sandwurm\ Convergence=1 -Sangromancer=1 -Sanguimancy=1 -Sanguinary\ Mage=1 -Sanguine\ Glorifier=1 -Sanguine\ Guard=1 -Sanguine\ Praetor=1 -Sanguine\ Sacrament=1 -Sanitarium\ Skeleton=1 -Sanity\ Gnawers=1 -Sanity\ Grinding=1 -Sapphire\ Drake=1 -Sapphire\ Leech=1 -Saprazzan\ Legate=1 -Saprazzan\ Raider=1 -Saproling\ Burst=1 -Saproling\ Migration=1 -Sapseep\ Forest=1 -Sarcatog=1 -Sarcomancy=1 -Sarcomite\ Myr=1 -Sarkhan's\ Rage=1 -Sarpadian\ Empires,\ Vol.\ VII=1 -Saruli\ Gatekeepers=1 -Sasaya,\ Orochi\ Ascendant=1 -Satyr\ Firedancer=1 -Satyr\ Grovedancer=1 -Satyr\ Hedonist=1 -Satyr\ Nyx-Smith=1 -Satyr\ Piper=1 -Satyr\ Rambler=1 -Savage\ Alliance=1 -Savage\ Beating=1 -Savage\ Conception=1 -Savage\ Gorilla=1 -Savage\ Knuckleblade=1 -Savage\ Punch=1 -Savage\ Silhouette=1 -Savage\ Stomp=1 -Savage\ Surge=1 -Savage\ Thallid=1 -Savage\ Twister=1 -Saving\ Grace=1 -Saving\ Grasp=1 -Savra,\ Queen\ of\ the\ Golgari=1 -Sawback\ Manticore=1 -Sawtooth\ Loon=1 -Sawtooth\ Ogre=1 -Sawtooth\ Thresher=1 -Scab-Clan\ Berserker=1 -Scab-Clan\ Mauler=1 -Scabland=1 -Scald=1 -Scalding\ Tongs=1 -Scaldkin=1 -Scale\ Blessing=1 -Scale\ of\ Chiss-Goria=1 -Scalebane's\ Elite=1 -Scaled\ Behemoth=1 -Scaled\ Hulk=1 -Scaleguard\ Sentinels=1 -Scalpelexis=1 -Scandalmonger=1 -Scapegoat=1 -Scar=1 -Scarab\ Feast=1 -Scarblade\ Elite=1 -Scarecrow=1 -Scarred\ Puma=1 -Scarred\ Vinebreeder=1 -Scars\ of\ the\ Veteran=1 -Scarscale\ Ritual=1 -Scarwood\ Bandits=1 -Scarwood\ Treefolk=1 -Scathe\ Zombies=1 -Scatter\ Arc=1 -Scatter\ the\ Seeds=1 -Scatter\ to\ the\ Winds=1 -Scattering\ Stroke=1 -Scattershot=1 -Scavenged\ Weaponry=1 -Scavenger\ Drake=1 -Scavenger\ Folk=1 -Scavenging\ Scarab=1 -Scent\ of\ Brine=1 -Scent\ of\ Jasmine=1 -Scent\ of\ Nightshade=1 -Scepter\ of\ Empires=1 -Scepter\ of\ Insight=1 -Schismotivate=1 -Scholar\ of\ Athreos=1 -Scholar\ of\ Stars=1 -School\ of\ Piranha=1 -Scion\ Summoner=1 -Scion\ of\ Glaciers=1 -Scion\ of\ Ugin=1 -Scion\ of\ Vitu-Ghazi=1 -Scion\ of\ the\ Wild=1 -Scorch\ the\ Fields=1 -Scorched\ Rusalka=1 -Scorching\ Lava=1 -Scorchwalker=1 -Scoria\ Elemental=1 -Scoria\ Wurm=1 -Scorned\ Villager=1 -Scornful\ Aether-Lich=1 -Scornful\ Egotist=1 -Scour=1 -Scour\ from\ Existence=1 -Scour\ the\ Laboratory=1 -Scoured\ Barrens=1 -Scourge\ Devil=1 -Scourge\ Servant=1 -Scourge\ Wolf=1 -Scourge\ of\ Geier\ Reach=1 -Scourge\ of\ Kher\ Ridges=1 -Scourge\ of\ Numai=1 -Scourge\ of\ Skola\ Vale=1 -Scourge\ of\ the\ Nobilis=1 -Scourgemark=1 -Scourglass=1 -Scouring\ Sands=1 -Scout's\ Warning=1 -Scout\ the\ Borders=1 -Scragnoth=1 -Scrambleverse=1 -Scrapbasket=1 -Scrapheap=1 -Scrapper\ Champion=1 -Scrapskin\ Drake=1 -Scrapyard\ Mongrel=1 -Scrapyard\ Salvo=1 -Screaming\ Fury=1 -Screaming\ Seahawk=1 -Screamreach\ Brawler=1 -Screams\ from\ Within=1 -Screams\ of\ the\ Damned=1 -Screeching\ Bat=1 -Screeching\ Drake=1 -Screeching\ Griffin=1 -Screeching\ Harpy=1 -Screeching\ Silcaw=1 -Screeching\ Skaab=1 -Screeching\ Sliver=1 -Scrib\ Nibblers=1 -Scribe\ of\ the\ Mindful=1 -Scrivener=1 -Scroll\ of\ Avacyn=1 -Scroll\ of\ Griselbrand=1 -Scroll\ of\ Origins=1 -Scrounge=1 -Scrounger\ of\ Souls=1 -Scrounging\ Bandar=1 -Scryb\ Ranger=1 -Scryb\ Sprites=1 -Scrying\ Glass=1 -Scute\ Mob=1 -Scuttlemutt=1 -Scuttling\ Death=1 -Scuttling\ Doom\ Engine=1 -Scuzzback\ Marauders=1 -Scuzzback\ Scrapper=1 -Scythe\ Leopard=1 -Scythe\ Tiger=1 -Scythe\ of\ the\ Wretched=1 -Sea\ Drake=1 -Sea\ Gate\ Loremaster=1 -Sea\ Gate\ Wreckage=1 -Sea\ God's\ Revenge=1 -Sea\ Legs=1 -Sea\ Monster=1 -Sea\ Scryer=1 -Sea\ Serpent=1 -Sea\ Snidd=1 -Sea\ Sprite=1 -Seacoast\ Drake=1 -Seagraf\ Skaab=1 -Seal\ of\ Cleansing=1 -Seal\ of\ Doom=1 -Seal\ of\ Primordium=1 -Seal\ of\ Strength=1 -Sealed\ Fate=1 -Sealock\ Monster=1 -Search\ Warrant=1 -Search\ the\ City=1 -Searing\ Flesh=1 -Searing\ Light=1 -Searing\ Meditation=1 -Searing\ Rays=1 -Searing\ Spear=1 -Searing\ Touch=1 -Searing\ Wind=1 -Seascape\ Aerialist=1 -Seashell\ Cameo=1 -Seaside\ Citadel=1 -Seaside\ Haven=1 -Seasinger=1 -Seasoned\ Marshal=1 -Second\ Guess=1 -Second\ Harvest=1 -Second\ Sunrise=1 -Second\ Wind=1 -Secret\ Plans=1 -Secret\ Salvage=1 -Secretkeeper=1 -Secrets\ of\ the\ Golden\ City=1 -Security\ Detail=1 -Sedge\ Scorpion=1 -Sedge\ Troll=1 -Sedraxis\ Alchemist=1 -Sedraxis\ Specter=1 -See\ Beyond=1 -See\ Red=1 -Seed\ Guardian=1 -Seed\ Spark=1 -Seed\ the\ Land=1 -Seedcradle\ Witch=1 -Seedguide\ Ash=1 -Seeds\ of\ Strength=1 -Seek\ the\ Horizon=1 -Seek\ the\ Wilds=1 -Seeker\ of\ Insight=1 -Seekers'\ Squire=1 -Seer's\ Lantern=1 -Seer's\ Sundial=1 -Seer\ of\ the\ Last\ Tomorrow=1 -Seething\ Pathblazer=1 -Segmented\ Krotiq=1 -Seismic\ Elemental=1 -Seismic\ Rupture=1 -Seismic\ Shift=1 -Seismic\ Spike=1 -Seismic\ Stomp=1 -Seismic\ Strike=1 -Seizan,\ Perverter\ of\ Truth=1 -Seize\ the\ Soul=1 -Sejiri\ Merfolk=1 -Sek'Kuar,\ Deathkeeper=1 -Select\ for\ Inspection=1 -Selective\ Memory=1 -Selesnya\ Charm=1 -Selesnya\ Cluestone=1 -Selesnya\ Evangel=1 -Selesnya\ Guildgate=1 -Selesnya\ Guildmage=1 -Selesnya\ Keyrune=1 -Selesnya\ Sagittars=1 -Selesnya\ Sanctuary=1 -Selesnya\ Signet=1 -Self-Assembler=1 -Self-Inflicted\ Wound=1 -Selfless\ Cathar=1 -Selfless\ Exorcist=1 -Selhoff\ Occultist=1 -Selkie\ Hedge-Mage=1 -Sell-Sword\ Brute=1 -Seller\ of\ Songbirds=1 -Selvala,\ Explorer\ Returned=1 -Send\ to\ Sleep=1 -Sengir\ Autocrat=1 -Sengir\ Nosferatu=1 -Sengir\ Vampire=1 -Sensation\ Gorger=1 -Sensei\ Golden-Tail=1 -Senseless\ Rage=1 -Sensor\ Splicer=1 -Sentinel\ Spider=1 -Sentinel\ of\ the\ Eternal\ Watch=1 -Sentinel\ of\ the\ Pearl\ Trident=1 -Sentinels\ of\ Glen\ Elendra=1 -Sentry\ of\ the\ Underworld=1 -Separatist\ Voidmage=1 -Septic\ Rats=1 -Sepulchral\ Primordial=1 -Sequestered\ Stash=1 -Seraph\ of\ the\ Masses=1 -Seraph\ of\ the\ Suns=1 -Serendib\ Efreet=1 -Serendib\ Sorcerer=1 -Serene\ Offering=1 -Serene\ Remembrance=1 -Serene\ Steward=1 -Serene\ Sunset=1 -Sergeant-at-Arms=1 -Serpent\ Skin=1 -Serpent\ Warrior=1 -Serpentine\ Kavu=1 -Serpentine\ Spike=1 -Serra's\ Blessing=1 -Serra's\ Boon=1 -Serra's\ Embrace=1 -Serra's\ Hymn=1 -Serra\ Advocate=1 -Serra\ Angel=1 -Serra\ Avenger=1 -Serra\ Aviary=1 -Serra\ Bestiary=1 -Serra\ Disciple=1 -Serra\ Sphinx=1 -Serra\ Zealot=1 -Serrated\ Biskelion=1 -Serum\ Raker=1 -Serum\ Tank=1 -Servant\ of\ Nefarox=1 -Servant\ of\ Tymaret=1 -Servant\ of\ Volrath=1 -Servo\ Exhibition=1 -Servo\ Schematic=1 -Seshiro\ the\ Anointed=1 -Set\ Adrift=1 -Setessan\ Battle\ Priest=1 -Setessan\ Griffin=1 -Setessan\ Oathsworn=1 -Setessan\ Starbreaker=1 -Setessan\ Tactics=1 -Seton's\ Desire=1 -Seton's\ Scout=1 -Settle\ the\ Score=1 -Sever\ Soul=1 -Sever\ the\ Bloodline=1 -Severed\ Legion=1 -Sewer\ Rats=1 -Sewer\ Shambler=1 -Sewerdreg=1 -Sewn-Eye\ Drake=1 -Shackles=1 -Shade's\ Breath=1 -Shade's\ Form=1 -Shade\ of\ Trokair=1 -Shadow\ Alley\ Denizen=1 -Shadow\ Glider=1 -Shadow\ Guildmage=1 -Shadow\ Rider=1 -Shadow\ Slice=1 -Shadow\ Sliver=1 -Shadow\ of\ the\ Grave=1 -Shadowblood\ Egg=1 -Shadowcloak\ Vampire=1 -Shadowed\ Caravel=1 -Shadowfeed=1 -Shadowmage\ Infiltrator=1 -Shadows\ of\ the\ Past=1 -Shadowstorm=1 -Shadowstorm\ Vizier=1 -Shake\ the\ Foundations=1 -Shaleskin\ Bruiser=1 -Shaleskin\ Plower=1 -Shaman's\ Trance=1 -Shaman\ of\ Spring=1 -Shamble\ Back=1 -Shambleshark=1 -Shambling\ Attendants=1 -Shambling\ Ghoul=1 -Shambling\ Remains=1 -Shambling\ Shell=1 -Shambling\ Strider=1 -Shambling\ Swarm=1 -Shanna,\ Sisay's\ Legacy=1 -Shanodin\ Dryads=1 -Shape\ Anew=1 -Shape\ Stealer=1 -Shape\ the\ Sands=1 -Shaper\ Apprentice=1 -Shaper\ Guildmage=1 -Shapers\ of\ Nature=1 -Shapeshifter's\ Marrow=1 -Shard\ Convergence=1 -Shard\ Phoenix=1 -Shard\ of\ Broken\ Glass=1 -Sharding\ Sphinx=1 -Shared\ Discovery=1 -Shared\ Fate=1 -Sharpened\ Pitchfork=1 -Shatter=1 -Shattered\ Angel=1 -Shattered\ Crypt=1 -Shattered\ Dreams=1 -Shattered\ Perception=1 -Shattering\ Blow=1 -Shatterskull\ Giant=1 -Shatterskull\ Recruit=1 -Shauku's\ Minion=1 -Shed\ Weakness=1 -Sheer\ Drop=1 -Shefet\ Monitor=1 -Shell\ Skulkin=1 -Shell\ of\ the\ Last\ Kappa=1 -Shelter=1 -Sheltered\ Aerie=1 -Sheltering\ Light=1 -Shield\ Bearer=1 -Shield\ Dancer=1 -Shield\ Wall=1 -Shield\ of\ the\ Ages=1 -Shield\ of\ the\ Avatar=1 -Shield\ of\ the\ Oversoul=1 -Shield\ of\ the\ Realm=1 -Shield\ of\ the\ Righteous=1 -Shielded\ Aether\ Thief=1 -Shielded\ Passage=1 -Shieldhide\ Dragon=1 -Shielding\ Plax=1 -Shieldmage\ Elder=1 -Shields\ of\ Velis\ Vel=1 -Shifting\ Borders=1 -Shifting\ Sky=1 -Shimatsu\ the\ Bloodcloaked=1 -Shimian\ Specter=1 -Shimmering\ Barrier=1 -Shimmering\ Efreet=1 -Shimmering\ Glasskite=1 -Shimmering\ Grotto=1 -Shimmering\ Mirage=1 -Shimmering\ Wings=1 -Shimmerscale\ Drake=1 -Shinen\ of\ Life's\ Roar=1 -Shinewend=1 -Shining\ Aerosaur=1 -Shinka\ Gatekeeper=1 -Shipbreaker\ Kraken=1 -Shipwreck\ Looter=1 -Shipwreck\ Moray=1 -Shipwreck\ Singer=1 -Shirei,\ Shizo's\ Caretaker=1 -Shisato,\ Whispering\ Hunter=1 -Shiv's\ Embrace=1 -Shivan\ Dragon=1 -Shivan\ Emissary=1 -Shivan\ Hellkite=1 -Shivan\ Meteor=1 -Shivan\ Oasis=1 -Shivan\ Phoenix=1 -Shivan\ Raptor=1 -Shivan\ Sand-Mage=1 -Shivan\ Wumpus=1 -Shivan\ Wurm=1 -Shizuko,\ Caller\ of\ Autumn=1 -Shoal\ Serpent=1 -Shock=1 -Shockmaw\ Dragon=1 -Shore\ Keeper=1 -Shore\ Snapper=1 -Shorecrasher\ Mimic=1 -Shoreline\ Raider=1 -Shoreline\ Ranger=1 -Short\ Sword=1 -Shoulder\ to\ Shoulder=1 -Shoving\ Match=1 -Shower\ of\ Coals=1 -Shower\ of\ Sparks=1 -Showstopper=1 -Shrapnel\ Blast=1 -Shredding\ Winds=1 -Shreds\ of\ Sanity=1 -Shrewd\ Hatchling=1 -Shrewd\ Negotiation=1 -Shriek\ Raptor=1 -Shriek\ of\ Dread=1 -Shriekgeist=1 -Shriekhorn=1 -Shrike\ Harpy=1 -Shrill\ Howler=1 -Shrine\ of\ Boundless\ Growth=1 -Shrine\ of\ Limitless\ Power=1 -Shrine\ of\ Loyal\ Legions=1 -Shrine\ of\ Piercing\ Vision=1 -Shrine\ of\ the\ Forsaken\ Gods=1 -Shrink=1 -Shriveling\ Rot=1 -Shrouded\ Lore=1 -Shu\ Cavalry=1 -Shu\ Elite\ Companions=1 -Shu\ Soldier-Farmers=1 -Shuriken=1 -Shyft=1 -Sibilant\ Spirit=1 -Sibsig\ Icebreakers=1 -Sicken=1 -Sickening\ Dreams=1 -Sickle\ Ripper=1 -Sickleslicer=1 -Sideswipe=1 -Sidewinder\ Naga=1 -Sidisi's\ Pet=1 -Siege\ Dragon=1 -Siege\ Mastodon=1 -Siege\ Modification=1 -Siege\ Wurm=1 -Siege\ of\ Towers=1 -Siegebreaker\ Giant=1 -Sift=1 -Sift\ Through\ Sands=1 -Sifter\ of\ Skulls=1 -Sigarda's\ Aid=1 -Sigardian\ Priest=1 -Sight\ Beyond\ Sight=1 -Sight\ of\ the\ Scalelords=1 -Sighted-Caste\ Sorcerer=1 -Sightless\ Brawler=1 -Sightless\ Ghoul=1 -Sigil\ Blessing=1 -Sigil\ Captain=1 -Sigil\ Tracer=1 -Sigil\ of\ Distinction=1 -Sigil\ of\ Valor=1 -Sigil\ of\ the\ Empty\ Throne=1 -Sigil\ of\ the\ Nayan\ Gods=1 -Sigiled\ Behemoth=1 -Sigiled\ Skink=1 -Sigiled\ Starfish=1 -Signal\ the\ Clans=1 -Silburlind\ Snapper=1 -Silence\ the\ Believers=1 -Silent-Chant\ Zubera=1 -Silent\ Artisan=1 -Silent\ Departure=1 -Silent\ Observer=1 -Silent\ Sentinel=1 -Silent\ Skimmer=1 -Silhana\ Starfletcher=1 -Silk\ Net=1 -Silkbind\ Faerie=1 -Silkenfist\ Fighter=1 -Silkenfist\ Order=1 -Silklash\ Spider=1 -Silkweaver\ Elite=1 -Silt\ Crawler=1 -Silumgar\ Assassin=1 -Silumgar\ Butcher=1 -Silumgar\ Monument=1 -Silumgar\ Sorcerer=1 -Silumgar\ Spell-Eater=1 -Silver-Inlaid\ Dagger=1 -Silver\ Myr=1 -Silver\ Seraph=1 -Silver\ Wyvern=1 -Silverback\ Ape=1 -Silverchase\ Fox=1 -Silverclad\ Ferocidons=1 -Silvercoat\ Lion=1 -Silverfur\ Partisan=1 -Silvergill\ Douser=1 -Silverskin\ Armor=1 -Silverstorm\ Samurai=1 -Silverstrike=1 -Silvos,\ Rogue\ Elemental=1 -Simian\ Brawler=1 -Simian\ Grunts=1 -Simic\ Basilisk=1 -Simic\ Cluestone=1 -Simic\ Fluxmage=1 -Simic\ Guildgate=1 -Simic\ Guildmage=1 -Simic\ Initiate=1 -Simic\ Keyrune=1 -Simic\ Manipulator=1 -Simic\ Ragworm=1 -Simic\ Sky\ Swallower=1 -Simoon=1 -Sin\ Prodder=1 -Sindbad=1 -Singe-Mind\ Ogre=1 -Singe=1 -Singing\ Bell\ Strike=1 -Singing\ Tree=1 -Sinister\ Concoction=1 -Sinister\ Possession=1 -Sinister\ Strength=1 -Sink\ into\ Takenuma=1 -Sinking\ Feeling=1 -Sins\ of\ the\ Past=1 -Sinuous\ Striker=1 -Sir\ Shandlar\ of\ Eberyn=1 -Sire\ of\ Insanity=1 -Siren's\ Ruse=1 -Siren\ Lookout=1 -Siren\ Reaver=1 -Siren\ Song\ Lyre=1 -Siren\ of\ the\ Fanged\ Coast=1 -Siren\ of\ the\ Silent\ Song=1 -Sisay's\ Ingenuity=1 -Sisay's\ Ring=1 -Sisters\ of\ Stone\ Death=1 -Sivitri\ Scarzam=1 -Sixth\ Sense=1 -Skaab\ Goliath=1 -Skarrg,\ the\ Rage\ Pits=1 -Skarrg\ Goliath=1 -Skarrg\ Guildmage=1 -Skarrgan\ Firebird=1 -Skarrgan\ Skybreaker=1 -Skeletal\ Changeling=1 -Skeletal\ Grimace=1 -Skeletal\ Kathari=1 -Skeletal\ Vampire=1 -Skeletal\ Wurm=1 -Skeleton\ Archer=1 -Skeleton\ Key=1 -Skeleton\ Scavengers=1 -Skeleton\ Shard=1 -Skeletonize=1 -Skill\ Borrower=1 -Skillful\ Lunge=1 -Skin\ Invasion=1 -Skinbrand\ Goblin=1 -Skinrender=1 -Skinshifter=1 -Skinthinner=1 -Skinwing=1 -Skirge\ Familiar=1 -Skirk\ Alarmist=1 -Skirk\ Drill\ Sergeant=1 -Skirk\ Outrider=1 -Skirk\ Prospector=1 -Skirk\ Ridge\ Exhumer=1 -Skirk\ Shaman=1 -Skirk\ Volcanist=1 -Skirsdag\ Flayer=1 -Skirsdag\ High\ Priest=1 -Skirsdag\ Supplicant=1 -Skitter\ of\ Lizards=1 -Skittering\ Heartstopper=1 -Skittering\ Horror=1 -Skittering\ Invasion=1 -Skittering\ Monstrosity=1 -Skittering\ Skirge=1 -Skittering\ Surveyor=1 -Skitterskin=1 -Skittish\ Kavu=1 -Skittish\ Valesk=1 -Skizzik=1 -Skulduggery=1 -Skulking\ Ghost=1 -Skulking\ Knight=1 -Skull\ Collector=1 -Skull\ Rend=1 -Skull\ of\ Orm=1 -Skullcage=1 -Skullmead\ Cauldron=1 -Skullmulcher=1 -Skullsnatcher=1 -Skulltap=1 -Sky\ Ruin\ Drake=1 -Sky\ Scourer=1 -Sky\ Skiff=1 -Sky\ Spirit=1 -Sky\ Swallower=1 -Sky\ Terror=1 -Skybind=1 -Skyblade\ of\ the\ Legion=1 -Skyblinder\ Staff=1 -Skycloud\ Egg=1 -Skygames=1 -Skyhunter\ Cub=1 -Skyhunter\ Prowler=1 -Skyhunter\ Skirmisher=1 -Skyknight\ Legionnaire=1 -Skylasher=1 -Skyline\ Cascade=1 -Skyline\ Predator=1 -Skymarch\ Bloodletter=1 -Skymarcher\ Aspirant=1 -Skymark\ Roc=1 -Skyraker\ Giant=1 -Skyreach\ Manta=1 -Skyreaping=1 -Skyrider\ Elf=1 -Skyrider\ Trainee=1 -Skyscribing=1 -Skyshaper=1 -Skyship\ Plunderer=1 -Skyship\ Stalker=1 -Skyshooter=1 -Skyshroud\ Archer=1 -Skyshroud\ Blessing=1 -Skyshroud\ Condor=1 -Skyshroud\ Elf=1 -Skyshroud\ Elite=1 -Skyshroud\ Forest=1 -Skyshroud\ Ranger=1 -Skyshroud\ Sentinel=1 -Skyshroud\ Vampire=1 -Skyshroud\ War\ Beast=1 -Skysnare\ Spider=1 -Skyspear\ Cavalry=1 -Skyswirl\ Harrier=1 -Skyward\ Eye\ Prophets=1 -Skywatcher\ Adept=1 -Skywhaler's\ Shot=1 -Skywinder\ Drake=1 -Skywing\ Aven=1 -Skywise\ Teachings=1 -Slab\ Hammer=1 -Slag\ Fiend=1 -Slagwurm\ Armor=1 -Slash\ Panther=1 -Slash\ of\ Talons=1 -Slashing\ Tiger=1 -Slate\ Street\ Ruffian=1 -Slaughter=1 -Slaughter\ Cry=1 -Slaughter\ Drone=1 -Slaughterhorn=1 -Slaughterhouse\ Bouncer=1 -Slave\ of\ Bolas=1 -Slavering\ Nulls=1 -Slay=1 -Slayer's\ Cleaver=1 -Slayer's\ Plate=1 -Slayer\ of\ the\ Wicked=1 -Sleek\ Schooner=1 -Sleep=1 -Sleep\ Paralysis=1 -Sleeper's\ Robe=1 -Sleeper\ Agent=1 -Sleeping\ Potion=1 -Slice\ and\ Dice=1 -Slice\ in\ Twain=1 -Slime\ Molding=1 -Slimefoot,\ the\ Stowaway=1 -Slimy\ Kavu=1 -Slingbow\ Trap=1 -Slingshot\ Goblin=1 -Slinking\ Serpent=1 -Slinking\ Skirge=1 -Slinn\ Voda,\ the\ Rising\ Deep=1 -Slip\ Through\ Space=1 -Slippery\ Scoundrel=1 -Slipstream\ Eel=1 -Slipstream\ Serpent=1 -Sliptide\ Serpent=1 -Slith\ Ascendant=1 -Slith\ Bloodletter=1 -Slith\ Firewalker=1 -Slith\ Predator=1 -Slith\ Strider=1 -Slither\ Blade=1 -Slitherhead=1 -Slithering\ Shade=1 -Slithermuse=1 -Slithery\ Stalker=1 -Sliver\ Construct=1 -Sliversmith=1 -Slobad,\ Goblin\ Tinkerer=1 -Slow\ Motion=1 -Sludge\ Crawler=1 -Sludge\ Strider=1 -Sluggishness=1 -Sluiceway\ Scorpion=1 -Slum\ Reaper=1 -Slumbering\ Dragon=1 -Slumbering\ Tora=1 -Sly\ Requisitioner=1 -Smash=1 -Smash\ to\ Smithereens=1 -Smelt-Ward\ Gatekeepers=1 -Smelt=1 -Smite=1 -Smite\ the\ Monstrous=1 -Smogsteed\ Rider=1 -Smoke\ Teller=1 -Smokebraider=1 -Smokespew\ Invoker=1 -Smolder\ Initiate=1 -Smoldering\ Efreet=1 -Smoldering\ Spires=1 -Smoldering\ Tar=1 -Smoldering\ Werewolf=1 -Smother=1 -Smothering\ Abomination=1 -Snake\ Cult\ Initiation=1 -Snake\ Umbra=1 -Snake\ of\ the\ Golden\ Grove=1 -Snapback=1 -Snapping\ Drake=1 -Snapping\ Gnarlid=1 -Snapping\ Sailback=1 -Snapping\ Thragg=1 -Snare\ Thopter=1 -Sneaky\ Homunculus=1 -Snorting\ Gahr=1 -Snowhorn\ Rider=1 -Snubhorn\ Sentry=1 -Soar=1 -Soaring\ Hope=1 -Soaring\ Seacliff=1 -Soilshaper=1 -Sokenzan\ Renegade=1 -Sokenzan\ Spellblade=1 -Sol'kanar\ the\ Swamp\ King=1 -Solar\ Tide=1 -Solarion=1 -Soldevi\ Digger=1 -Soldevi\ Golem=1 -Soldevi\ Machinist=1 -Soldevi\ Simulacrum=1 -Soldier\ of\ the\ Pantheon=1 -Solemn\ Offering=1 -Solemn\ Recruit=1 -Solfatara=1 -Solidarity=1 -Solitary\ Camel=1 -Solitary\ Hunter=1 -Soliton=1 -Soltari\ Champion=1 -Soltari\ Crusader=1 -Soltari\ Lancer=1 -Soltari\ Monk=1 -Soltari\ Priest=1 -Soltari\ Trooper=1 -Somber\ Hoverguard=1 -Somberwald\ Alpha=1 -Somberwald\ Spider=1 -Somberwald\ Stag=1 -Somnomancer=1 -Somnophore=1 -Song\ of\ Serenity=1 -Songstitcher=1 -Sonic\ Seizure=1 -Soot\ Imp=1 -Sootfeather\ Flock=1 -Sootstoke\ Kindler=1 -Sophic\ Centaur=1 -Soramaro,\ First\ to\ Dream=1 -Soratami\ Cloud\ Chariot=1 -Soratami\ Cloudskater=1 -Soratami\ Mindsweeper=1 -Soratami\ Mirror-Guard=1 -Soratami\ Mirror-Mage=1 -Soratami\ Rainshaper=1 -Soratami\ Savant=1 -Soratami\ Seer=1 -Sorcerer's\ Strongbox=1 -Sorcerer's\ Wand=1 -Sorin's\ Thirst=1 -Sorin's\ Vengeance=1 -Sorrow's\ Path=1 -Sosuke's\ Summons=1 -Sosuke,\ Son\ of\ Seshiro=1 -Soul's\ Fire=1 -Soul's\ Grace=1 -Soul's\ Might=1 -Soul\ Channeling=1 -Soul\ Collector=1 -Soul\ Conduit=1 -Soul\ Exchange=1 -Soul\ Feast=1 -Soul\ Foundry=1 -Soul\ Kiss=1 -Soul\ Link=1 -Soul\ Manipulation=1 -Soul\ Net=1 -Soul\ Nova=1 -Soul\ Ransom=1 -Soul\ Rend=1 -Soul\ Salvage=1 -Soul\ Seizer=1 -Soul\ Separator=1 -Soul\ Shepherd=1 -Soul\ Shred=1 -Soul\ Stair\ Expedition=1 -Soul\ Swallower=1 -Soul\ Tithe=1 -Soul\ of\ Magma=1 -Soul\ of\ the\ Rapids=1 -Soulblade\ Djinn=1 -Soulblast=1 -Soulbright\ Flamekin=1 -Soulcage\ Fiend=1 -Soulcatcher=1 -Soulcatchers'\ Aerie=1 -Souldrinker=1 -Soulgorger\ Orgg=1 -Soulless\ Revival=1 -Soulmender=1 -Soulquake=1 -Souls\ of\ the\ Faultless=1 -Soulstinger=1 -Soulsworn\ Jury=1 -Soulsworn\ Spirit=1 -Soultether\ Golem=1 -Southern\ Paladin=1 -Sowing\ Salt=1 -Spare\ from\ Evil=1 -Spark\ Jolt=1 -Spark\ Mage=1 -Spark\ Spray=1 -Spark\ Trooper=1 -Spark\ of\ Creativity=1 -Sparkmage's\ Gambit=1 -Sparkspitter=1 -Sparktongue\ Dragon=1 -Sparring\ Construct=1 -Sparring\ Golem=1 -Sparring\ Mummy=1 -Spatial\ Binding=1 -Spawn\ of\ Thraxes=1 -Spawnbinder\ Mage=1 -Spawnbroker=1 -Spawning\ Bed=1 -Spawning\ Breath=1 -Spawnsire\ of\ Ulamog=1 -Spear\ of\ Heliod=1 -Spearbreaker\ Behemoth=1 -Spearpoint\ Oread=1 -Species\ Gorger=1 -Specter's\ Shroud=1 -Spectra\ Ward=1 -Spectral\ Bears=1 -Spectral\ Flight=1 -Spectral\ Force=1 -Spectral\ Guardian=1 -Spectral\ Prison=1 -Spectral\ Reserves=1 -Spectral\ Searchlight=1 -Spectral\ Shepherd=1 -Spectral\ Shield=1 -Spectral\ Shift=1 -Spectral\ Sliver=1 -Speedway\ Fanatic=1 -Spell\ Blast=1 -Spell\ Burst=1 -Spell\ Contortion=1 -Spell\ Rupture=1 -Spell\ Shrivel=1 -Spell\ Swindle=1 -Spell\ Syphon=1 -Spellbane\ Centaur=1 -Spellbinder=1 -Spellbook=1 -Spellbound\ Dragon=1 -Spellheart\ Chimera=1 -Spellshift=1 -Spelltithe\ Enforcer=1 -Spelltwine=1 -Spellweaver\ Eternal=1 -Spellweaver\ Helix=1 -Sphere\ of\ Duty=1 -Sphere\ of\ Grace=1 -Sphere\ of\ Purity=1 -Sphere\ of\ Reason=1 -Sphere\ of\ Truth=1 -Sphere\ of\ the\ Suns=1 -Sphinx's\ Disciple=1 -Sphinx's\ Herald=1 -Sphinx-Bone\ Wand=1 -Sphinx\ Summoner=1 -Sphinx\ of\ Jwar\ Isle=1 -Sphinx\ of\ Lost\ Truths=1 -Sphinx\ of\ Magosi=1 -Sphinx\ of\ Uthuun=1 -Sphinx\ of\ the\ Chimes=1 -Spider\ Climb=1 -Spider\ Spawning=1 -Spidery\ Grasp=1 -Spike-Tailed\ Ceratops=1 -Spike\ Breeder=1 -Spike\ Cannibal=1 -Spike\ Colony=1 -Spike\ Drone=1 -Spike\ Hatcher=1 -Spike\ Jester=1 -Spike\ Rogue=1 -Spike\ Soldier=1 -Spike\ Tiller=1 -Spike\ Worker=1 -Spikeshot\ Elder=1 -Spikeshot\ Goblin=1 -Spiketail\ Drakeling=1 -Spiketail\ Hatchling=1 -Spin\ Engine=1 -Spin\ into\ Myth=1 -Spinal\ Graft=1 -Spincrusher=1 -Spinebiter=1 -Spined\ Basher=1 -Spined\ Fluke=1 -Spined\ Sliver=1 -Spined\ Thopter=1 -Spined\ Wurm=1 -Spineless\ Thug=1 -Spiny\ Starfish=1 -Spiraling\ Duelist=1 -Spiraling\ Embers=1 -Spire\ Monitor=1 -Spire\ Owl=1 -Spire\ Patrol=1 -Spire\ Tracer=1 -Spire\ Winder=1 -Spireside\ Infiltrator=1 -Spirespine=1 -Spirit\ Away=1 -Spirit\ Bonds=1 -Spirit\ Cairn=1 -Spirit\ Flare=1 -Spirit\ Loop=1 -Spirit\ Mirror=1 -Spirit\ Shackle=1 -Spirit\ Weaver=1 -Spirit\ en-Dal=1 -Spirit\ en-Kor=1 -Spirit\ of\ the\ Hearth=1 -Spirit\ of\ the\ Hunt=1 -Spiritmonger=1 -Spiritual\ Visit=1 -Spiritualize=1 -Spite\ of\ Mogis=1 -Spitebellows=1 -Spiteflame\ Witch=1 -Spiteful\ Blow=1 -Spiteful\ Bully=1 -Spiteful\ Motives=1 -Spiteful\ Returned=1 -Spiteful\ Shadows=1 -Spitting\ Drake=1 -Spitting\ Earth=1 -Spitting\ Gourna=1 -Spitting\ Hydra=1 -Spitting\ Sliver=1 -Spitting\ Slug=1 -Spitting\ Spider=1 -Splatter\ Thug=1 -Splendid\ Agony=1 -Splendid\ Reclamation=1 -Splinterfright=1 -Split-Tail\ Miko=1 -Splitting\ Headache=1 -Splitting\ Slime=1 -Spoils\ of\ Victory=1 -Spontaneous\ Artist=1 -Spontaneous\ Combustion=1 -Spontaneous\ Mutation=1 -Spore\ Burst=1 -Spore\ Cloud=1 -Spore\ Swarm=1 -Sporeback\ Troll=1 -Sporecap\ Spider=1 -Sporecrown\ Thallid=1 -Sporesower\ Thallid=1 -Sporoloth\ Ancient=1 -Spotted\ Griffin=1 -Spread\ the\ Sickness=1 -Spreading\ Algae=1 -Spreading\ Flames=1 -Spreading\ Rot=1 -Spring\ Cleaning=1 -Springing\ Tiger=1 -Springsage\ Ritual=1 -Sprinting\ Warbrute=1 -Sprite\ Noble=1 -Sprout=1 -Sprouting\ Thrinax=1 -Spurnmage\ Advocate=1 -Spurred\ Wolverine=1 -Spy\ Network=1 -Squadron\ Hawk=1 -Squall=1 -Squall\ Drifter=1 -Squall\ Line=1 -Squeaking\ Pie\ Grubfellows=1 -Squeaking\ Pie\ Sneak=1 -Squealing\ Devil=1 -Squee's\ Toy=1 -Squee,\ the\ Immortal=1 -Squelch=1 -Squelching\ Leeches=1 -Squire's\ Devotion=1 -Squire=1 -Squirming\ Mass=1 -Stab\ Wound=1 -Stabilizer=1 -Staff\ of\ the\ Death\ Magus=1 -Staff\ of\ the\ Flame\ Magus=1 -Staff\ of\ the\ Mind\ Magus=1 -Staff\ of\ the\ Sun\ Magus=1 -Staff\ of\ the\ Wild\ Magus=1 -Stag\ Beetle=1 -Stalker\ Hag=1 -Stalking\ Assassin=1 -Stalking\ Bloodsucker=1 -Stalking\ Drone=1 -Stalking\ Stones=1 -Stalking\ Tiger=1 -Stalking\ Yeti=1 -Stalwart\ Aven=1 -Stalwart\ Shield-Bearers=1 -Stamina=1 -Stampede=1 -Stampeding\ Elk\ Herd=1 -Stampeding\ Horncrest=1 -Stampeding\ Rhino=1 -Stampeding\ Wildebeests=1 -Stand\ //\ Deliver=1 -Stand\ Firm=1 -Stand\ Together=1 -Standardize=1 -Standing\ Troops=1 -Stangg=1 -Star-Crowned\ Stag=1 -Star\ Compass=1 -Starlight=1 -Starlight\ Invoker=1 -Starlit\ Sanctum=1 -Start\ //\ Finish=1 -Start\ Your\ Engines=1 -Starved\ Rusalka=1 -Stasis\ Cell=1 -Stasis\ Cocoon=1 -Stasis\ Snare=1 -Statute\ of\ Denial=1 -Staunch-Hearted\ Warrior=1 -Staunch\ Defenders=1 -Stave\ Off=1 -Steadfast\ Armasaur=1 -Steadfast\ Cathar=1 -Steadfast\ Guard=1 -Steadfast\ Sentinel=1 -Steady\ Progress=1 -Steal\ Artifact=1 -Steal\ Strength=1 -Stealer\ of\ Secrets=1 -Steam\ Augury=1 -Steam\ Blast=1 -Steam\ Catapult=1 -Steam\ Spitter=1 -Steam\ Vines=1 -Steamclaw=1 -Steamcore\ Weird=1 -Steamflogger\ Boss=1 -Steel\ Golem=1 -Steel\ Leaf\ Paladin=1 -Steel\ Sabotage=1 -Steelclad\ Serpent=1 -Steeling\ Stance=1 -Steelshaper\ Apprentice=1 -Steeple\ Roc=1 -Stenchskipper=1 -Stensia\ Banquet=1 -Stensia\ Bloodhall=1 -Stensia\ Innkeeper=1 -Stensia\ Masquerade=1 -Steppe\ Glider=1 -Stern\ Constable=1 -Stern\ Judge=1 -Stern\ Mentor=1 -Stern\ Proctor=1 -Steward\ of\ Solidarity=1 -Steward\ of\ Valeron=1 -Still\ Life=1 -Stingerfling\ Spider=1 -Stinging\ Licid=1 -Stinging\ Shot=1 -Stingmoggie=1 -Stingscourger=1 -Stinkdrinker\ Daredevil=1 -Stir\ the\ Grave=1 -Stir\ the\ Pride=1 -Stir\ the\ Sands=1 -Stitch\ in\ Time=1 -Stitched\ Drake=1 -Stitched\ Mangler=1 -Stitcher's\ Apprentice=1 -Stitchwing\ Skaab=1 -Stoic\ Angel=1 -Stoic\ Builder=1 -Stoic\ Ephemera=1 -Stoic\ Rebuttal=1 -Stoke\ the\ Flames=1 -Stolen\ Goods=1 -Stolen\ Grain=1 -Stolen\ Identity=1 -Stomper\ Cub=1 -Stomping\ Slabs=1 -Stone-Seeder\ Hierophant=1 -Stone-Tongue\ Basilisk=1 -Stone\ Calendar=1 -Stone\ Giant=1 -Stone\ Haven\ Medic=1 -Stone\ Haven\ Outfitter=1 -Stone\ Idol\ Trap=1 -Stone\ Quarry=1 -Stone\ Spirit=1 -Stonebrow,\ Krosan\ Hero=1 -Stonecloaker=1 -Stoneforge\ Acolyte=1 -Stonefury=1 -Stonehands=1 -Stonehewer\ Giant=1 -Stoneshaker\ Shaman=1 -Stoneshock\ Giant=1 -Stonewise\ Fortifier=1 -Stonewood\ Invocation=1 -Stonework\ Puma=1 -Stonewright=1 -Stonybrook\ Angler=1 -Stonybrook\ Schoolmaster=1 -Storm\ Crow=1 -Storm\ Elemental=1 -Storm\ Entity=1 -Storm\ Fleet\ Aerialist=1 -Storm\ Fleet\ Arsonist=1 -Storm\ Fleet\ Pyromancer=1 -Storm\ Fleet\ Sprinter=1 -Storm\ Fleet\ Spy=1 -Storm\ Fleet\ Swashbuckler=1 -Storm\ Front=1 -Storm\ Herd=1 -Storm\ Sculptor=1 -Storm\ Seeker=1 -Storm\ Shaman=1 -Storm\ Spirit=1 -Storm\ the\ Vault=1 -Stormblood\ Berserker=1 -Stormchaser\ Chimera=1 -Stormfront\ Pegasus=1 -Stormfront\ Riders=1 -Stormrider\ Rig=1 -Stormrider\ Spirit=1 -Stormscale\ Anarch=1 -Stormscape\ Apprentice=1 -Stormscape\ Battlemage=1 -Stormscape\ Familiar=1 -Stormtide\ Leviathan=1 -Stormwatch\ Eagle=1 -Stormwing\ Dragon=1 -Strands\ of\ Night=1 -Strandwalker=1 -Strange\ Augmentation=1 -Strangling\ Soot=1 -Strangling\ Spores=1 -Strata\ Scythe=1 -Stratadon=1 -Stratozeppelid=1 -Stratus\ Walk=1 -Straw\ Golem=1 -Stream\ Hopper=1 -Stream\ of\ Consciousness=1 -Stream\ of\ Life=1 -Stream\ of\ Unconsciousness=1 -Streambed\ Aquitects=1 -Street\ Savvy=1 -Street\ Spasm=1 -Street\ Sweeper=1 -Streetbreaker\ Wurm=1 -Strength\ from\ the\ Fallen=1 -Strength\ in\ Numbers=1 -Strength\ of\ Arms=1 -Strength\ of\ Isolation=1 -Strength\ of\ Lunacy=1 -Strength\ of\ Night=1 -Strength\ of\ Unity=1 -Strength\ of\ the\ Pack=1 -Strength\ of\ the\ Tajuru=1 -Strider\ Harness=1 -Strip\ Bare=1 -Striped\ Riverwinder=1 -Stromkirk\ Mentor=1 -Stromkirk\ Noble=1 -Stromkirk\ Occultist=1 -Strongarm\ Monk=1 -Strongarm\ Tactics=1 -Strongarm\ Thug=1 -Stronghold\ Assassin=1 -Stronghold\ Biologist=1 -Stronghold\ Confessor=1 -Stronghold\ Discipline=1 -Stronghold\ Machinist=1 -Stronghold\ Overseer=1 -Stronghold\ Rats=1 -Stronghold\ Taskmaster=1 -Stronghold\ Zeppelin=1 -Structural\ Collapse=1 -Structural\ Distortion=1 -Struggle\ for\ Sanity=1 -Student\ of\ Elements=1 -Student\ of\ Ojutai=1 -Stuffy\ Doll=1 -Stun=1 -Stun\ Sniper=1 -Stunted\ Growth=1 -Stupefying\ Touch=1 -Sturdy\ Hatchling=1 -Sturmgeist=1 -Stymied\ Hopes=1 -Subjugator\ Angel=1 -Submerged\ Boneyard=1 -Subterranean\ Scout=1 -Subtle\ Strike=1 -Succumb\ to\ Temptation=1 -Sudden\ Death=1 -Sudden\ Disappearance=1 -Sudden\ Impact=1 -Sudden\ Spoiling=1 -Sudden\ Storm=1 -Sudden\ Strength=1 -Suffer\ the\ Past=1 -Suicidal\ Charge=1 -Sulam\ Djinn=1 -Sulfur\ Elemental=1 -Sulfuric\ Vapors=1 -Sulfurous\ Blast=1 -Sultai\ Ascendancy=1 -Sultai\ Banner=1 -Sultai\ Charm=1 -Sultai\ Flayer=1 -Sultai\ Scavenger=1 -Sultai\ Soothsayer=1 -Summary\ Dismissal=1 -Summit\ Apes=1 -Summit\ Prowler=1 -Summon\ the\ School=1 -Summoner's\ Bane=1 -Summoner's\ Egg=1 -Summoning\ Station=1 -Sun's\ Bounty=1 -Sun-Collared\ Raptor=1 -Sun-Crested\ Pterodon=1 -Sun-Crowned\ Hunters=1 -Sun\ Sentinel=1 -Sunastian\ Falconer=1 -Sunbeam\ Spellbomb=1 -Sunbird's\ Invocation=1 -Sunblade\ Elf=1 -Sunblast\ Angel=1 -Sunbond=1 -Sunbringer's\ Touch=1 -Suncrusher=1 -Sunder\ from\ Within=1 -Sundering\ Growth=1 -Sundering\ Vitae=1 -Sunfire\ Balm=1 -Sunflare\ Shaman=1 -Sunforger=1 -Sungrace\ Pegasus=1 -Sungrass\ Egg=1 -Sunhome\ Enforcer=1 -Sunhome\ Guildmage=1 -Sunken\ City=1 -Sunken\ Hope=1 -Sunlance=1 -Sunrise\ Seeker=1 -Sunrise\ Sovereign=1 -Sunscape\ Apprentice=1 -Sunscorched\ Desert=1 -Sunseed\ Nurturer=1 -Sunset\ Pyramid=1 -Sunspear\ Shikari=1 -Sunspire\ Gatekeepers=1 -Sunspire\ Griffin=1 -Sunspring\ Expedition=1 -Sunstrike\ Legionnaire=1 -Suntail\ Hawk=1 -Suntouched\ Myr=1 -Sunweb=1 -Superior\ Numbers=1 -Supernatural\ Stamina=1 -Supply\ //\ Demand=1 -Supply\ Caravan=1 -Suppress=1 -Suppression\ Bonds=1 -Supreme\ Exemplar=1 -Supreme\ Inquisitor=1 -Suq'Ata\ Assassin=1 -Suq'Ata\ Lancer=1 -Sure\ Strike=1 -Surestrike\ Trident=1 -Surge\ Node=1 -Surge\ of\ Righteousness=1 -Surge\ of\ Thoughtweft=1 -Surge\ of\ Zeal=1 -Surgespanner=1 -Surging\ Flame=1 -Surging\ Might=1 -Surging\ Sentinels=1 -Surprise\ Deployment=1 -Surrakar\ Banisher=1 -Surrakar\ Spellblade=1 -Surreal\ Memoir=1 -Surveilling\ Sprite=1 -Survey\ the\ Wreckage=1 -Survival\ Cache=1 -Survive\ the\ Night=1 -Survivor\ of\ the\ Unseen=1 -Survivors'\ Encampment=1 -Suspension\ Field=1 -Suture\ Spirit=1 -Sutured\ Ghoul=1 -Svogthos,\ the\ Restless\ Tomb=1 -Svyelunite\ Temple=1 -Swaggering\ Corsair=1 -Swallowing\ Plague=1 -Swamp=1 -Swamp\ Mosquito=1 -Swarm\ Intelligence=1 -Swarm\ Surge=1 -Swarm\ of\ Bloodflies=1 -Swarmborn\ Giant=1 -Swashbuckling=1 -Swat=1 -Sway\ of\ Illusion=1 -Sway\ of\ the\ Stars=1 -Sweatworks\ Brawler=1 -Sweep\ Away=1 -Swell\ of\ Courage=1 -Swell\ of\ Growth=1 -Swelter=1 -Swerve=1 -Swift\ Justice=1 -Swift\ Kick=1 -Swift\ Reckoning=1 -Swift\ Silence=1 -Swift\ Spinner=1 -Swift\ Warden=1 -Swift\ Warkite=1 -Swirl\ the\ Mists=1 -Swirling\ Spriggan=1 -Switcheroo=1 -Sword-Point\ Diplomacy=1 -Sword\ Dancer=1 -Swordwise\ Centaur=1 -Sworn\ Guardian=1 -Sygg,\ River\ Guide=1 -Sylvan\ Awakening=1 -Sylvan\ Basilisk=1 -Sylvan\ Bounty=1 -Sylvan\ Echoes=1 -Sylvan\ Messenger=1 -Sylvan\ Might=1 -Sylvan\ Primordial=1 -Sylvan\ Ranger=1 -Sylvok\ Explorer=1 -Sylvok\ Replica=1 -Symbiosis=1 -Symbiotic\ Deployment=1 -Symbiotic\ Wurm=1 -Synchronized\ Strike=1 -Synchronous\ Sliver=1 -Syncopate=1 -Syndic\ of\ Tithes=1 -Syndicate\ Enforcer=1 -Syndicate\ Trafficker=1 -Synod\ Artificer=1 -Synod\ Centurion=1 -Synod\ Sanctum=1 -Syphon\ Soul=1 -Szadek,\ Lord\ of\ Secrets=1 -Sance=1 -Tablet\ of\ Epityr=1 -Tablet\ of\ the\ Guilds=1 -Tah-Crop\ Elite=1 -Tah-Crop\ Skirmisher=1 -Tahngarth's\ Glare=1 -Tahngarth's\ Rage=1 -Taigam's\ Scheming=1 -Taigam's\ Strike=1 -Tail\ Slash=1 -Tainted\ Remedy=1 -Tainted\ Sigil=1 -Taj-Nar\ Swordsmith=1 -Tajic,\ Blade\ of\ the\ Legion=1 -Tajuru\ Archer=1 -Tajuru\ Beastmaster=1 -Tajuru\ Pathwarden=1 -Tajuru\ Preserver=1 -Tajuru\ Stalwart=1 -Tajuru\ Warcaller=1 -Take\ Down=1 -Take\ Inventory=1 -Take\ Possession=1 -Take\ Vengeance=1 -Take\ into\ Custody=1 -Takeno,\ Samurai\ General=1 -Takenuma\ Bleeder=1 -Takklemaggot=1 -Talas\ Researcher=1 -Talent\ of\ the\ Telepath=1 -Talisman\ of\ Impulse=1 -Talisman\ of\ Unity=1 -Talon\ Trooper=1 -Talon\ of\ Pain=1 -Talonrend=1 -Talrand's\ Invocation=1 -Talruum\ Champion=1 -Talruum\ Minotaur=1 -Talruum\ Piper=1 -Talus\ Paladin=1 -Tamiyo's\ Journal=1 -Tandem\ Lookout=1 -Tandem\ Tactics=1 -Tangle=1 -Tangle\ Angler=1 -Tangle\ Asp=1 -Tangle\ Golem=1 -Tangle\ Mantis=1 -Tangle\ Spider=1 -Tanglebloom=1 -Tangleclaw\ Werewolf=1 -Tangleroot=1 -Tanglesap=1 -Tanglewalker=1 -Tapestry\ of\ the\ Ages=1 -Tar\ Fiend=1 -Tar\ Pit\ Warrior=1 -Tasseled\ Dromedary=1 -Taste\ for\ Mayhem=1 -Tatsumasa,\ the\ Dragon's\ Fang=1 -Tattered\ Drake=1 -Tattered\ Haunter=1 -Tatterkite=1 -Tattermunge\ Duo=1 -Tattermunge\ Maniac=1 -Tattermunge\ Witch=1 -Tatyova,\ Benthic\ Druid=1 -Tawnos's\ Coffin=1 -Tawnos's\ Wand=1 -Teardrop\ Kami=1 -Tears\ of\ Valakut=1 -Tectonic\ Rift=1 -Teferi's\ Care=1 -Teferi's\ Honor\ Guard=1 -Tel-Jilad\ Archers=1 -Tel-Jilad\ Chosen=1 -Tel-Jilad\ Defiance=1 -Tel-Jilad\ Fallen=1 -Tel-Jilad\ Lifebreather=1 -Tel-Jilad\ Outrider=1 -Tel-Jilad\ Stylus=1 -Tel-Jilad\ Wolf=1 -Telekinetic\ Bonds=1 -Telekinetic\ Sliver=1 -Telemin\ Performance=1 -Telepathic\ Spies=1 -Telepathy=1 -Teleportal=1 -Telim'Tor's\ Darts=1 -Telim'Tor=1 -Teller\ of\ Tales=1 -Telling\ Time=1 -Temmet,\ Vizier\ of\ Naktamun=1 -Temper=1 -Tempest\ Caller=1 -Tempest\ Drake=1 -Tempest\ of\ Light=1 -Temple\ Acolyte=1 -Temple\ Altisaur=1 -Temporal\ Adept=1 -Temporal\ Cascade=1 -Temporal\ Distortion=1 -Temporal\ Fissure=1 -Temporal\ Isolation=1 -Tempting\ Licid=1 -Tempting\ Wurm=1 -Temur\ Ascendancy=1 -Temur\ Banner=1 -Temur\ Charger=1 -Temur\ Charm=1 -Tenacious\ Dead=1 -Tenacious\ Hunter=1 -Tenacity=1 -Teneb,\ the\ Harvester=1 -Tenement\ Crasher=1 -Tenza,\ Godo's\ Maul=1 -Tephraderm=1 -Terashi's\ Cry=1 -Terashi's\ Grasp=1 -Terashi's\ Verdict=1 -Teremko\ Griffin=1 -Teroh's\ Vanguard=1 -Terra\ Eternal=1 -Terra\ Stomper=1 -Terraformer=1 -Terrarion=1 -Territorial\ Baloth=1 -Territorial\ Gorger=1 -Territorial\ Hammerskull=1 -Terror=1 -Terror\ of\ the\ Fairgrounds=1 -Terrus\ Wurm=1 -Teshar,\ Ancestor's\ Apostle=1 -Test\ of\ Faith=1 -Testament\ of\ Faith=1 -Tethered\ Skirge=1 -Tethmos\ High\ Priest=1 -Tetsuko\ Umezawa,\ Fugitive=1 -Teysa,\ Envoy\ of\ Ghosts=1 -Tezzeret's\ Ambition=1 -Tezzeret's\ Gambit=1 -Tezzeret's\ Touch=1 -Thada\ Adel,\ Acquisitor=1 -Thalakos\ Drifters=1 -Thalakos\ Lowlands=1 -Thalakos\ Mistfolk=1 -Thalakos\ Seer=1 -Thalia's\ Lancers=1 -Thallid=1 -Thallid\ Germinator=1 -Thallid\ Omnivore=1 -Thallid\ Shell-Dweller=1 -Thallid\ Soothsayer=1 -Thassa's\ Bounty=1 -Thassa's\ Devourer=1 -Thassa's\ Emissary=1 -Thassa's\ Ire=1 -Thassa's\ Rebuff=1 -The\ First\ Eruption=1 -The\ Hive=1 -The\ Lady\ of\ the\ Mountain=1 -The\ Mending\ of\ Dominaria=1 -The\ Unspeakable=1 -The\ Wretched=1 -Theft\ of\ Dreams=1 -Thelonite\ Druid=1 -Thermal\ Flux=1 -Thermopod=1 -Thick-Skinned\ Goblin=1 -Thief\ of\ Hope=1 -Thieves'\ Fortune=1 -Thieving\ Magpie=1 -Thieving\ Sprite=1 -Thing\ from\ the\ Deep=1 -Think\ Tank=1 -Thirst=1 -Thirsting\ Axe=1 -Thistledown\ Duo=1 -Thopter\ Arrest=1 -Thopter\ Engineer=1 -Thopter\ Spy\ Network=1 -Thopter\ Squadron=1 -Thorn-Thrash\ Viashino=1 -Thorn\ Elemental=1 -Thornbite\ Staff=1 -Thornbow\ Archer=1 -Thorned\ Moloch=1 -Thornhide\ Wolves=1 -Thornscape\ Apprentice=1 -Thornscape\ Battlemage=1 -Thornscape\ Master=1 -Thorntooth\ Witch=1 -Thornweald\ Archer=1 -Thornwind\ Faeries=1 -Thornwood\ Falls=1 -Those\ Who\ Serve=1 -Thought\ Courier=1 -Thought\ Devourer=1 -Thought\ Dissector=1 -Thought\ Eater=1 -Thought\ Gorger=1 -Thought\ Harvester=1 -Thought\ Hemorrhage=1 -Thought\ Prison=1 -Thought\ Reflection=1 -Thoughtbind=1 -Thoughtbound\ Primoc=1 -Thoughtflare=1 -Thoughtleech=1 -Thoughtpicker\ Witch=1 -Thoughtrender\ Lamia=1 -Thoughts\ of\ Ruin=1 -Thousand-legged\ Kami=1 -Thousand\ Winds=1 -Thraben\ Foulbloods=1 -Thraben\ Gargoyle=1 -Thraben\ Sentry=1 -Thraben\ Standard\ Bearer=1 -Thran\ Forge=1 -Thran\ Foundry=1 -Thran\ Golem=1 -Thran\ Temporal\ Gateway=1 -Thran\ Weaponry=1 -Thrash\ of\ Raptors=1 -Thrashing\ Mossdog=1 -Threaten=1 -Three\ Dreams=1 -Thresher\ Lizard=1 -Thrill-Kill\ Assassin=1 -Thrill\ of\ the\ Hunt=1 -Thriss,\ Nantuko\ Primus=1 -Thrive=1 -Thriving\ Grubs=1 -Thriving\ Ibex=1 -Thriving\ Rats=1 -Thriving\ Rhino=1 -Thriving\ Turtle=1 -Throat\ Slitter=1 -Throne\ of\ Bone=1 -Throne\ of\ Empires=1 -Throne\ of\ the\ God-Pharaoh=1 -Throttle=1 -Throwing\ Knife=1 -Thrull\ Parasite=1 -Thrull\ Retainer=1 -Thrull\ Surgeon=1 -Thrummingbird=1 -Thumbscrews=1 -Thunder-Thrash\ Elder=1 -Thunder\ Brute=1 -Thunder\ Spirit=1 -Thunder\ Totem=1 -Thunder\ of\ Hooves=1 -Thunderblade\ Charge=1 -Thunderblust=1 -Thunderbolt=1 -Thunderclap\ Wyvern=1 -Thundercloud\ Elemental=1 -Thundercloud\ Shaman=1 -Thunderheads=1 -Thunderherd\ Migration=1 -Thundering\ Giant=1 -Thundering\ Spineback=1 -Thundering\ Tanadon=1 -Thundermare=1 -Thunderous\ Might=1 -Thunderscape\ Battlemage=1 -Thundersong\ Trumpeter=1 -Thunderstaff=1 -Tiana,\ Ship's\ Caretaker=1 -Tibor\ and\ Lumia=1 -Tidal\ Visionary=1 -Tidal\ Wave=1 -Tide\ Drifter=1 -Tide\ of\ War=1 -Tideforce\ Elemental=1 -Tidehollow\ Strix=1 -Tideshaper\ Mystic=1 -Tidewalker=1 -Tidewater\ Minion=1 -Tidings=1 -Tidy\ Conclusion=1 -Tiger\ Claws=1 -Tigereye\ Cameo=1 -Tightening\ Coils=1 -Tilonalli's\ Crown=1 -Tilonalli's\ Knight=1 -Tilonalli's\ Skinshifter=1 -Timber\ Gorge=1 -Timberland\ Ruins=1 -Timbermare=1 -Timbermaw\ Larva=1 -Timberpack\ Wolf=1 -Time\ Bomb=1 -Time\ Ebb=1 -Time\ Stop=1 -Time\ and\ Tide=1 -Time\ of\ Heroes=1 -Time\ of\ Ice=1 -Time\ to\ Feed=1 -Time\ to\ Reflect=1 -Timebender=1 -Timecrafting=1 -Timely\ Hordemate=1 -Timid\ Drake=1 -Tin-Wing\ Chimera=1 -Tine\ Shrike=1 -Tireless\ Missionaries=1 -Tishana's\ Wayfinder=1 -Titan's\ Presence=1 -Titan's\ Revenge=1 -Titan's\ Strength=1 -Titan\ Forge=1 -Titan\ of\ Eternal\ Fire=1 -Titania's\ Boon=1 -Titania's\ Chosen=1 -Titanic\ Bulvox=1 -Titanic\ Growth=1 -Titanic\ Ultimatum=1 -Titanium\ Golem=1 -Tivadar's\ Crusade=1 -Tivadar\ of\ Thorn=1 -To\ Arms!=1 -To\ the\ Slaughter=1 -Tobias\ Andrion=1 -Toil\ //\ Trouble=1 -Toil\ to\ Renown=1 -Toils\ of\ Night\ and\ Day=1 -Tolarian\ Drake=1 -Tolarian\ Emissary=1 -Tolarian\ Scholar=1 -Tolarian\ Sentinel=1 -Tolarian\ Serpent=1 -Tolsimir\ Wolfblood=1 -Tomb\ Robber=1 -Tomb\ of\ Urami=1 -Tomb\ of\ the\ Spirit\ Dragon=1 -Tome\ Scour=1 -Tomorrow,\ Azami's\ Familiar=1 -Toolcraft\ Exemplar=1 -Tooth\ Collector=1 -Tooth\ and\ Claw=1 -Tooth\ of\ Chiss-Goria=1 -Topan\ Ascetic=1 -Topan\ Freeblade=1 -Topplegeist=1 -Tor\ Wauki=1 -Torch\ Fiend=1 -Torch\ Gauntlet=1 -Torch\ Song=1 -Torchling=1 -Torgaar,\ Famine\ Incarnate=1 -Torment\ of\ Scarabs=1 -Torment\ of\ Venom=1 -Tormented\ Angel=1 -Tormented\ Hero=1 -Tormented\ Pariah=1 -Tormented\ Soul=1 -Tormented\ Thoughts=1 -Tormenting\ Voice=1 -Tormentor\ Exarch=1 -Tornado=1 -Tornado\ Elemental=1 -Torpid\ Moloch=1 -Torpor\ Dust=1 -Torrent\ of\ Fire=1 -Torrent\ of\ Souls=1 -Torrent\ of\ Stone=1 -Torsten\ Von\ Ursus=1 -Tortoise\ Formation=1 -Torture=1 -Toshiro\ Umezawa=1 -Totally\ Lost=1 -Totem-Guide\ Hartebeest=1 -Totem\ Speaker=1 -Touch\ of\ Invisibility=1 -Touch\ of\ Moonglove=1 -Touch\ of\ the\ Eternal=1 -Touch\ of\ the\ Void=1 -Touchstone=1 -Tower\ Above=1 -Tower\ Defense=1 -Tower\ Drake=1 -Tower\ Gargoyle=1 -Tower\ Geist=1 -Tower\ of\ Calamities=1 -Tower\ of\ Champions=1 -Tower\ of\ Eons=1 -Tower\ of\ Fortunes=1 -Tower\ of\ Murmurs=1 -Towering\ Baloth=1 -Towering\ Indrik=1 -Town\ Gossipmonger=1 -Toxic\ Nim=1 -Trade\ Routes=1 -Trade\ Secrets=1 -Tradewind\ Rider=1 -Tragic\ Arrogance=1 -Tragic\ Lesson=1 -Tragic\ Poet=1 -Trail\ of\ Evidence=1 -Trail\ of\ Mystery=1 -Trailblazer's\ Boots=1 -Trained\ Armodon=1 -Trained\ Caracal=1 -Trained\ Orgg=1 -Trained\ Pronghorn=1 -Trait\ Doctoring=1 -Traitor's\ Clutch=1 -Traitorous\ Blood=1 -Traitorous\ Instinct=1 -Tranquil\ Cove=1 -Tranquil\ Expanse=1 -Tranquil\ Garden=1 -Transcendence=1 -Transgress\ the\ Mind=1 -Transguild\ Courier=1 -Transguild\ Promenade=1 -Transluminant=1 -Transmogrifying\ Licid=1 -Trap\ Digger=1 -Trap\ Essence=1 -Trapjaw\ Kelpie=1 -Trapmaker's\ Snare=1 -Traumatize=1 -Travel\ Preparations=1 -Traveler's\ Amulet=1 -Traveling\ Plague=1 -Treacherous\ Urge=1 -Treacherous\ Werewolf=1 -Tread\ Upon=1 -Treasure\ Cruise=1 -Treasure\ Hunter=1 -Treasure\ Keeper=1 -Treasure\ Trove=1 -Treasured\ Find=1 -Treasury\ Thrull=1 -Tree\ Monkey=1 -Treefolk\ Healer=1 -Treefolk\ Mystic=1 -Treefolk\ Seedlings=1 -Treespring\ Lorian=1 -Treetop\ Rangers=1 -Treetop\ Sentinel=1 -Tremor=1 -Trench\ Wurm=1 -Trenching\ Steed=1 -Trepanation\ Blade=1 -Trespasser's\ Curse=1 -Trespasser\ il-Vec=1 -Tresserhorn\ Skyknight=1 -Trestle\ Troll=1 -Treva's\ Attendant=1 -Triad\ of\ Fates=1 -Trial\ //\ Error=1 -Trial\ of\ Ambition=1 -Trial\ of\ Knowledge=1 -Trial\ of\ Solidarity=1 -Trial\ of\ Strength=1 -Trial\ of\ Zeal=1 -Triangle\ of\ War=1 -Triassic\ Egg=1 -Tribal\ Flames=1 -Tribal\ Golem=1 -Tribal\ Unity=1 -Tribute\ to\ Hunger=1 -Tribute\ to\ the\ Wild=1 -Trickbind=1 -Trickery\ Charm=1 -Tricks\ of\ the\ Trade=1 -Trickster\ Mage=1 -Triclopean\ Sight=1 -Trigon\ of\ Corruption=1 -Trigon\ of\ Infestation=1 -Trip\ Noose=1 -Trip\ Wire=1 -Triskaidekaphobia=1 -Triskelavus=1 -Triskelion=1 -Triton\ Cavalry=1 -Triton\ Fortune\ Hunter=1 -Triton\ Shorethief=1 -Triton\ Tactics=1 -Triumph\ of\ Ferocity=1 -Triumph\ of\ Gerrard=1 -Troll-Horn\ Cameo=1 -Tromokratis=1 -Tromp\ the\ Domains=1 -Trophy\ Hunter=1 -Trophy\ Mage=1 -Tropical\ Storm=1 -Trostani's\ Judgment=1 -Trostani's\ Summoner=1 -Troubled\ Healer=1 -Troublesome\ Spirit=1 -Trove\ of\ Temptation=1 -True-Faith\ Censer=1 -True\ Believer=1 -True\ Conviction=1 -Truefire\ Paladin=1 -Trueheart\ Duelist=1 -Trueheart\ Twins=1 -Trumpet\ Blast=1 -Trumpeting\ Armodon=1 -Trusted\ Advisor=1 -Trusted\ Forcemage=1 -Trusty\ Companion=1 -Trusty\ Machete=1 -Trusty\ Packbeast=1 -Truth\ or\ Tale=1 -Trygon\ Predator=1 -Tukatongue\ Thallid=1 -Tuktuk\ Grunts=1 -Tuktuk\ Scrapper=1 -Tuktuk\ the\ Explorer=1 -Tumble\ Magnet=1 -Tundra\ Kavu=1 -Tunnel\ Vision=1 -Tunneling\ Geopede=1 -Turbulent\ Dreams=1 -Turn\ //\ Burn=1 -Turn\ Against=1 -Turn\ Aside=1 -Turn\ the\ Tables=1 -Turn\ the\ Tide=1 -Turn\ to\ Dust=1 -Turn\ to\ Frog=1 -Turn\ to\ Mist=1 -Turn\ to\ Slag=1 -Turntimber\ Basilisk=1 -Turntimber\ Grove=1 -Turntimber\ Ranger=1 -Turtleshell\ Changeling=1 -Tusked\ Colossodon=1 -Tuskguard\ Captain=1 -Twiddle=1 -Twigwalker=1 -Twilight\ Drover=1 -Twilight\ Shepherd=1 -Twinblade\ Slasher=1 -Twincast=1 -Twinning\ Glass=1 -Twins\ of\ Maurer\ Estate=1 -Twinstrike=1 -Twist\ Allegiance=1 -Twisted\ Abomination=1 -Twisted\ Image=1 -Twitch=1 -Two-Headed\ Cerberus=1 -Two-Headed\ Dragon=1 -Two-Headed\ Giant=1 -Two-Headed\ Giant\ of\ Foriys=1 -Two-Headed\ Sliver=1 -Two-Headed\ Zombie=1 -Tymaret,\ the\ Murder\ King=1 -Typhoid\ Rats=1 -Tyrannize=1 -Tyrant's\ Choice=1 -Tyrant\ of\ Valakut=1 -Tyrranax=1 -Uba\ Mask=1 -Ubul\ Sar\ Gatekeepers=1 -Ugin's\ Construct=1 -Ugin's\ Insight=1 -Uktabi\ Drake=1 -Uktabi\ Efreet=1 -Uktabi\ Faerie=1 -Uktabi\ Orangutan=1 -Uktabi\ Wildcats=1 -Ukud\ Cobra=1 -Ulamog's\ Despoiler=1 -Ulamog's\ Nullifier=1 -Ulamog's\ Reclaimer=1 -Ulcerate=1 -Ulrich's\ Kindred=1 -Ultimate\ Price=1 -Ulvenwald\ Bear=1 -Ulvenwald\ Captive=1 -Ulvenwald\ Mysteries=1 -Ulvenwald\ Mystics=1 -Ulvenwald\ Observer=1 -Umara\ Entangler=1 -Umara\ Raptor=1 -Umbra\ Mystic=1 -Umbra\ Stalker=1 -Umbral\ Mantle=1 -Unbender\ Tine=1 -Unblinking\ Bleb=1 -Unbreathing\ Horde=1 -Unbridled\ Growth=1 -Unburden=1 -Uncaged\ Fury=1 -Unchecked\ Growth=1 -Uncomfortable\ Chill=1 -Uncontrollable\ Anger=1 -Unconventional\ Tactics=1 -Uncovered\ Clues=1 -Undead\ Alchemist=1 -Undead\ Gladiator=1 -Undead\ Leotau=1 -Undead\ Minotaur=1 -Undead\ Servant=1 -Undead\ Slayer=1 -Undercity\ Informer=1 -Undercity\ Plague=1 -Undercity\ Shade=1 -Undercity\ Troll=1 -Undergrowth\ Scavenger=1 -Underhanded\ Designs=1 -Undertaker=1 -Underworld\ Coinsmith=1 -Undo=1 -Undying\ Rage=1 -Unerring\ Sling=1 -Unesh,\ Criosphinx\ Sovereign=1 -Unexpected\ Results=1 -Unflinching\ Courage=1 -Unforge=1 -Unfriendly\ Fire=1 -Unhallowed\ Pact=1 -Unholy\ Hunger=1 -Unholy\ Strength=1 -Unified\ Front=1 -Unified\ Strike=1 -Uninvited\ Geist=1 -Unity\ of\ Purpose=1 -Universal\ Solvent=1 -Unknown\ Shores=1 -Unliving\ Psychopath=1 -Unmake\ the\ Graves=1 -Unnatural\ Aggression=1 -Unnatural\ Endurance=1 -Unnatural\ Predation=1 -Unnerving\ Assault=1 -Unquenchable\ Thirst=1 -Unravel\ the\ Aether=1 -Unraveling\ Mummy=1 -Unruly\ Mob=1 -Unstable\ Footing=1 -Unstable\ Frontier=1 -Unstable\ Hulk=1 -Unstoppable\ Ash=1 -Unsummon=1 -Untamed\ Hunger=1 -Untamed\ Kavu=1 -Untamed\ Might=1 -Untamed\ Wilds=1 -Untethered\ Express=1 -Unwavering\ Initiate=1 -Unwilling\ Recruit=1 -Unwind=1 -Unyaro\ Bee\ Sting=1 -Unyaro\ Bees=1 -Unyielding\ Krumar=1 -Uphill\ Battle=1 -Uproot=1 -Ur-Golem's\ Eye=1 -Urban\ Burgeoning=1 -Urban\ Evolution=1 -Urbis\ Protector=1 -Urborg\ Elf=1 -Urborg\ Emissary=1 -Urborg\ Mindsucker=1 -Urborg\ Phantom=1 -Urborg\ Shambler=1 -Urborg\ Skeleton=1 -Urborg\ Stalker=1 -Urborg\ Syphon-Mage=1 -Urborg\ Uprising=1 -Urborg\ Volcano=1 -Urge\ to\ Feed=1 -Urgoros,\ the\ Empty\ One=1 -Ursapine=1 -Urza's\ Armor=1 -Urza's\ Blueprints=1 -Urza's\ Chalice=1 -Urza's\ Guilt=1 -Urza's\ Tome=1 -Uthden\ Troll=1 -Utopia\ Vow=1 -Utter\ End=1 -Utvara\ Scalper=1 -Uyo,\ Silent\ Prophet=1 -Vacuumelt=1 -Vaevictis\ Asmadi=1 -Vagrant\ Plowbeasts=1 -Valakut\ Fireboar=1 -Valakut\ Invoker=1 -Valakut\ Predator=1 -Valduk,\ Keeper\ of\ the\ Flame=1 -Valeron\ Outlander=1 -Valeron\ Wardens=1 -Valiant\ Guard=1 -Valley\ Dasher=1 -Valley\ Rannet=1 -Valleymaker=1 -Valor=1 -Valor\ Made\ Real=1 -Valor\ in\ Akros=1 -Vampire's\ Bite=1 -Vampire's\ Zeal=1 -Vampire\ Aristocrat=1 -Vampire\ Cutthroat=1 -Vampire\ Envoy=1 -Vampire\ Interloper=1 -Vampire\ Nighthawk=1 -Vampire\ Noble=1 -Vampire\ Outcasts=1 -Vampire\ Revenant=1 -Vampire\ Warlord=1 -Vampiric\ Embrace=1 -Vampiric\ Fury=1 -Vampiric\ Link=1 -Vampiric\ Rites=1 -Vampiric\ Sliver=1 -Vampiric\ Spirit=1 -Vampirism=1 -Vandalize=1 -Vanguard's\ Shield=1 -Vanguard\ of\ Brimaz=1 -Vanish\ into\ Memory=1 -Vanishment=1 -Vanquish=1 -Vanquish\ the\ Weak=1 -Vaporkin=1 -Vaporous\ Djinn=1 -Varchild's\ Crusader=1 -Varolz,\ the\ Scar-Striped=1 -Vassal's\ Duty=1 -Vassal\ Soul=1 -Vastwood\ Animist=1 -Vastwood\ Gorger=1 -Vastwood\ Zendikon=1 -Vault\ Skyward=1 -Vaultbreaker=1 -Vebulid=1 -Vec\ Townships=1 -Vectis\ Agents=1 -Vectis\ Silencers=1 -Vector\ Asp=1 -Vedalken\ Anatomist=1 -Vedalken\ Blademaster=1 -Vedalken\ Certarch=1 -Vedalken\ Dismisser=1 -Vedalken\ Engineer=1 -Vedalken\ Entrancer=1 -Vedalken\ Ghoul=1 -Vedalken\ Heretic=1 -Vedalken\ Infuser=1 -Vedalken\ Mastermind=1 -Vedalken\ Outlander=1 -Vedalken\ Plotter=1 -Veil\ of\ Secrecy=1 -Veilborn\ Ghoul=1 -Veiled\ Apparition=1 -Veiled\ Crocodile=1 -Veiled\ Sentry=1 -Veiled\ Serpent=1 -Veilstone\ Amulet=1 -Vein\ Drinker=1 -Venarian\ Glimmer=1 -Vendetta=1 -Venerable\ Kumo=1 -Venerable\ Lammasu=1 -Venerable\ Monk=1 -Venerated\ Teacher=1 -Vengeance=1 -Vengeful\ Firebrand=1 -Vengeful\ Rebel=1 -Vengeful\ Rebirth=1 -Vengeful\ Vampire=1 -Venomous\ Dragonfly=1 -Venomspout\ Brackus=1 -Venser's\ Diffusion=1 -Venser's\ Journal=1 -Vent\ Sentinel=1 -Ventifact\ Bottle=1 -Verdant\ Automaton=1 -Verdant\ Eidolon=1 -Verdant\ Embrace=1 -Verdant\ Field=1 -Verdant\ Force=1 -Verdant\ Haven=1 -Verdant\ Rebirth=1 -Verdant\ Sun's\ Avatar=1 -Verdant\ Touch=1 -Verdigris=1 -Vermiculos=1 -Vertigo\ Spawn=1 -Vesper\ Ghoul=1 -Vessel\ of\ Endless\ Rest=1 -Vessel\ of\ Ephemera=1 -Vessel\ of\ Malignity=1 -Vessel\ of\ Nascency=1 -Vessel\ of\ Paramnesia=1 -Vessel\ of\ Volatility=1 -Vestige\ of\ Emrakul=1 -Vesuvan\ Shapeshifter=1 -Veteran's\ Armaments=1 -Veteran's\ Reflexes=1 -Veteran's\ Sidearm=1 -Veteran\ Armorer=1 -Veteran\ Armorsmith=1 -Veteran\ Cathar=1 -Veteran\ Motorist=1 -Veteran\ Warleader=1 -Veteran\ of\ the\ Depths=1 -Vex=1 -Vexing\ Scuttler=1 -Vial\ of\ Dragonfire=1 -Vial\ of\ Poison=1 -Viashino\ Bladescout=1 -Viashino\ Cutthroat=1 -Viashino\ Firstblade=1 -Viashino\ Grappler=1 -Viashino\ Outrider=1 -Viashino\ Racketeer=1 -Viashino\ Runner=1 -Viashino\ Sandscout=1 -Viashino\ Sandstalker=1 -Viashino\ Skeleton=1 -Viashino\ Slasher=1 -Viashino\ Slaughtermaster=1 -Vibrating\ Sphere=1 -Vicious\ Conquistador=1 -Vicious\ Hunger=1 -Vicious\ Kavu=1 -Vicious\ Offering=1 -Vicious\ Shadows=1 -Victorious\ Destruction=1 -Victory's\ Herald=1 -Victual\ Sliver=1 -View\ from\ Above=1 -Vigean\ Graftmage=1 -Vigean\ Hydropon=1 -Vigean\ Intuition=1 -Vigil\ for\ the\ Lost=1 -Vigilance=1 -Vigilant\ Baloth=1 -Vigilant\ Drake=1 -Vigilant\ Sentry=1 -Vigilante\ Justice=1 -Vigor\ Mortis=1 -Vigorous\ Charge=1 -Vildin-Pack\ Outcast=1 -Vile\ Aggregate=1 -Vile\ Deacon=1 -Vile\ Manifestation=1 -Vile\ Rebirth=1 -Vile\ Redeemer=1 -Vile\ Requiem=1 -Village\ Bell-Ringer=1 -Village\ Cannibals=1 -Village\ Elder=1 -Village\ Ironsmith=1 -Village\ Messenger=1 -Village\ Survivors=1 -Villagers\ of\ Estwald=1 -Villainous\ Wealth=1 -Vindictive\ Mob=1 -Vine\ Kami=1 -Vine\ Snare=1 -Vinelasher\ Kudzu=1 -Vines\ of\ the\ Recluse=1 -Vineshaper\ Mystic=1 -Vineweft=1 -Vintara\ Snapper=1 -Violent\ Impact=1 -Violet\ Pall=1 -Viper's\ Kiss=1 -Viral\ Drake=1 -Viridescent\ Wisps=1 -Viridian\ Acolyte=1 -Viridian\ Betrayers=1 -Viridian\ Claw=1 -Viridian\ Emissary=1 -Viridian\ Joiner=1 -Viridian\ Lorebearers=1 -Viridian\ Revel=1 -Viridian\ Shaman=1 -Viridian\ Zealot=1 -Virulent\ Swipe=1 -Virulent\ Wound=1 -Visara\ the\ Dreadful=1 -Viscerid\ Armor=1 -Viscerid\ Drone=1 -Viscid\ Lemures=1 -Vision\ Skeins=1 -Visionary\ Augmenter=1 -Visions\ of\ Brutality=1 -Vital\ Splicer=1 -Vital\ Surge=1 -Vitality\ Charm=1 -Vitalizing\ Cascade=1 -Vitaspore\ Thallid=1 -Vithian\ Renegades=1 -Vithian\ Stinger=1 -Vitu-Ghazi,\ the\ City-Tree=1 -Vitu-Ghazi\ Guildmage=1 -Vivify=1 -Vivisection=1 -Vizier\ of\ Deferment=1 -Vizier\ of\ Many\ Faces=1 -Vizier\ of\ Remedies=1 -Vizier\ of\ Tumbling\ Sands=1 -Vizier\ of\ the\ Anointed=1 -Vizier\ of\ the\ True=1 -Vizkopa\ Confessor=1 -Vizkopa\ Guildmage=1 -Vizzerdrix=1 -Vodalian\ Arcanist=1 -Vodalian\ Hypnotist=1 -Vodalian\ Knights=1 -Vodalian\ Merchant=1 -Vodalian\ Serpent=1 -Voice\ of\ All=1 -Voice\ of\ Duty=1 -Voice\ of\ Grace=1 -Voice\ of\ Law=1 -Voice\ of\ Reason=1 -Void\ Attendant=1 -Void\ Grafter=1 -Void\ Maw=1 -Void\ Shatter=1 -Void\ Squall=1 -Void\ Stalker=1 -Voidmage\ Apprentice=1 -Voidmage\ Husher=1 -Voidstone\ Gargoyle=1 -Voidwalk=1 -Voidwielder=1 -Volatile\ Rig=1 -Volcanic\ Awakening=1 -Volcanic\ Dragon=1 -Volcanic\ Geyser=1 -Volcanic\ Rambler=1 -Volcanic\ Rush=1 -Volcanic\ Spray=1 -Volcanic\ Strength=1 -Volcanic\ Upheaval=1 -Volcanic\ Wind=1 -Volcano\ Hellion=1 -Volcano\ Imp=1 -Voldaren\ Duelist=1 -Voldaren\ Pariah=1 -Volition\ Reins=1 -Volrath's\ Curse=1 -Volrath's\ Dungeon=1 -Volrath's\ Gardens=1 -Volrath's\ Laboratory=1 -Volrath's\ Shapeshifter=1 -Volt\ Charge=1 -Voltaic\ Brawler=1 -Voltaic\ Construct=1 -Voltaic\ Servant=1 -Volunteer\ Reserves=1 -Vona's\ Hunger=1 -Voodoo\ Doll=1 -Voracious\ Cobra=1 -Voracious\ Dragon=1 -Voracious\ Null=1 -Voracious\ Vampire=1 -Voracious\ Wurm=1 -Vorrac\ Battlehorns=1 -Vortex\ Elemental=1 -Votary\ of\ the\ Conclave=1 -Vow\ of\ Duty=1 -Vow\ of\ Flight=1 -Vow\ of\ Lightning=1 -Vow\ of\ Malice=1 -Vow\ of\ Wildness=1 -Voyage's\ End=1 -Voyager\ Drake=1 -Voyager\ Staff=1 -Voyaging\ Satyr=1 -Vug\ Lizard=1 -Vulpine\ Goliath=1 -Vulshok\ Battlemaster=1 -Vulshok\ Berserker=1 -Vulshok\ Gauntlets=1 -Vulshok\ Heartstoker=1 -Vulshok\ Morningstar=1 -Vulshok\ Replica=1 -Vulshok\ Sorcerer=1 -Vulshok\ War\ Boar=1 -Vulturous\ Zombie=1 -Wail\ of\ the\ Nim=1 -Wailing\ Ghoul=1 -Wake\ of\ Vultures=1 -Wake\ the\ Reflections=1 -Wakedancer=1 -Waker\ of\ the\ Wilds=1 -Waking\ Nightmare=1 -Walk\ the\ Plank=1 -Walker\ of\ Secret\ Ways=1 -Walker\ of\ the\ Grove=1 -Walker\ of\ the\ Wastes=1 -Walking\ Archive=1 -Walking\ Atlas=1 -Walking\ Corpse=1 -Walking\ Desecration=1 -Walking\ Dream=1 -Walking\ Sponge=1 -Walking\ Wall=1 -Wall\ of\ Air=1 -Wall\ of\ Blood=1 -Wall\ of\ Bone=1 -Wall\ of\ Deceit=1 -Wall\ of\ Denial=1 -Wall\ of\ Diffusion=1 -Wall\ of\ Distortion=1 -Wall\ of\ Essence=1 -Wall\ of\ Faith=1 -Wall\ of\ Fire=1 -Wall\ of\ Forgotten\ Pharaohs=1 -Wall\ of\ Frost=1 -Wall\ of\ Light=1 -Wall\ of\ Limbs=1 -Wall\ of\ Mist=1 -Wall\ of\ Mulch=1 -Wall\ of\ Resurgence=1 -Wall\ of\ Souls=1 -Wall\ of\ Spears=1 -Wall\ of\ Stone=1 -Wall\ of\ Swords=1 -Wall\ of\ Tanglecord=1 -Wall\ of\ Vines=1 -Wall\ of\ Vipers=1 -Wall\ of\ Wonder=1 -Wallop=1 -Wand\ of\ Denial=1 -Wand\ of\ the\ Elements=1 -Wander\ in\ Death=1 -Wanderbrine\ Rootcutters=1 -Wanderer's\ Twig=1 -Wanderguard\ Sentry=1 -Wandering\ Champion=1 -Wandering\ Eye=1 -Wandering\ Goblins=1 -Wandering\ Graybeard=1 -Wandering\ Mage=1 -Wandering\ Ones=1 -Wandering\ Stream=1 -Wandering\ Wolf=1 -Wanderlust=1 -Wanderwine\ Prophets=1 -Waning\ Wurm=1 -Wanted\ Scoundrels=1 -War-Name\ Aspirant=1 -War-Spike\ Changeling=1 -War-Wing\ Siren=1 -War\ Barge=1 -War\ Behemoth=1 -War\ Dance=1 -War\ Elemental=1 -War\ Falcon=1 -War\ Flare=1 -War\ Horn=1 -War\ Oracle=1 -War\ Priest\ of\ Thune=1 -War\ Report=1 -Warbreak\ Trumpeter=1 -Warbringer=1 -Warchanter\ of\ Mogis=1 -Warchief\ Giant=1 -Warcry\ Phoenix=1 -Ward\ of\ Piety=1 -Warden\ of\ Evos\ Isle=1 -Warden\ of\ Geometries=1 -Warden\ of\ the\ Eye=1 -Wardscale\ Dragon=1 -Warfire\ Javelineer=1 -Warleader's\ Helix=1 -Warlord's\ Fury=1 -Warmind\ Infantry=1 -Warmonger's\ Chariot=1 -Warmonger=1 -Warning=1 -Warp\ Artifact=1 -Warp\ World=1 -Warpath\ Ghoul=1 -Warped\ Devotion=1 -Warped\ Landscape=1 -Warped\ Researcher=1 -Warren-Scourge\ Elf=1 -Warren\ Pilferers=1 -Warren\ Weirding=1 -Warrior's\ Honor=1 -Warrior\ Angel=1 -Warrior\ en-Kor=1 -Warriors'\ Lesson=1 -Warthog=1 -Wasp\ Lancer=1 -Waste\ Away=1 -Wasteland\ Scorpion=1 -Wasteland\ Strangler=1 -Wasteland\ Viper=1 -Watchdog=1 -Watcher\ Sliver=1 -Watcher\ in\ the\ Web=1 -Watcher\ of\ the\ Roost=1 -Watchers\ of\ the\ Dead=1 -Watchful\ Automaton=1 -Watchful\ Naga=1 -Watchwing\ Scarecrow=1 -Watchwolf=1 -Water\ Elemental=1 -Water\ Servant=1 -Watercourser=1 -Waterfront\ Bouncer=1 -Waterknot=1 -Waterspout\ Djinn=1 -Waterspout\ Elemental=1 -Waterspout\ Weavers=1 -Watertrap\ Weaver=1 -Waterveil\ Cavern=1 -Wave-Wing\ Elemental=1 -Wave\ of\ Indifference=1 -Wavecrash\ Triton=1 -Waveskimmer\ Aven=1 -Wax\ //\ Wane=1 -Waxing\ Moon=1 -Waxmane\ Baku=1 -Way\ of\ the\ Thief=1 -Wayfaring\ Giant=1 -Wayfaring\ Temple=1 -Waylay=1 -Wayward\ Giant=1 -Wayward\ Servant=1 -Wayward\ Soul=1 -Weakness=1 -Weakstone=1 -Weapon\ Surge=1 -Weaponcraft\ Enthusiast=1 -Weapons\ Trainer=1 -Wear\ Away=1 -Weathered\ Bodyguards=1 -Weatherseed\ Elf=1 -Weatherseed\ Faeries=1 -Weatherseed\ Totem=1 -Weave\ Fate=1 -Weaver\ of\ Currents=1 -Weaver\ of\ Lies=1 -Weaver\ of\ Lightning=1 -Web=1 -Wee\ Dragonauts=1 -Weed-Pruner\ Poplar=1 -Weed\ Strangle=1 -Wei\ Elite\ Companions=1 -Wei\ Infantry=1 -Wei\ Night\ Raiders=1 -Weight\ of\ Conscience=1 -Weight\ of\ Memory=1 -Weight\ of\ Spires=1 -Weight\ of\ the\ Underworld=1 -Weird\ Harvest=1 -Weirded\ Vampire=1 -Weirding\ Wood=1 -Welcome\ to\ the\ Fold=1 -Welder\ Automaton=1 -Weldfast\ Engineer=1 -Weldfast\ Monitor=1 -Weldfast\ Wingsmith=1 -Welding\ Sparks=1 -Welkin\ Guide=1 -Welkin\ Tern=1 -Well\ of\ Life=1 -Western\ Paladin=1 -Wetland\ Sambar=1 -Wharf\ Infiltrator=1 -Whelming\ Wave=1 -Where\ Ancients\ Tread=1 -Whetwheel=1 -Whims\ of\ the\ Fates=1 -Whimwader=1 -Whip\ Sergeant=1 -Whip\ Silk=1 -Whip\ of\ Erebos=1 -Whipcorder=1 -Whipkeeper=1 -Whiplash\ Trap=1 -Whiptail\ Moloch=1 -Whiptail\ Wurm=1 -Whiptongue\ Frog=1 -Whirler\ Rogue=1 -Whirlermaker=1 -Whirling\ Catapult=1 -Whirling\ Dervish=1 -Whirlpool\ Drake=1 -Whirlwind\ Adept=1 -Whisper,\ Blood\ Liturgist=1 -Whispering\ Madness=1 -Whispering\ Shade=1 -Whispering\ Specter=1 -Whispers\ of\ Emrakul=1 -Whispersilk\ Cloak=1 -White\ Knight=1 -White\ Shield\ Crusader=1 -Whitemane\ Lion=1 -Whiteout=1 -Whitesun's\ Passage=1 -Wicked\ Akuba=1 -Wicked\ Reward=1 -Wicker\ Warcrawler=1 -Wicker\ Witch=1 -Wiitigo=1 -Wild-Field\ Scarecrow=1 -Wild\ Aesthir=1 -Wild\ Beastmaster=1 -Wild\ Colos=1 -Wild\ Dogs=1 -Wild\ Evocation=1 -Wild\ Griffin=1 -Wild\ Guess=1 -Wild\ Hunger=1 -Wild\ Instincts=1 -Wild\ Leotau=1 -Wild\ Mammoth=1 -Wild\ Might=1 -Wild\ Onslaught=1 -Wild\ Ox=1 -Wild\ Pair=1 -Wild\ Ricochet=1 -Wild\ Swing=1 -Wild\ Wanderer=1 -Wild\ Wurm=1 -Wildcall=1 -Wilderness\ Elemental=1 -Wilderness\ Hypnotist=1 -Wildest\ Dreams=1 -Wildfire\ Cerberus=1 -Wildfire\ Emissary=1 -Wildfire\ Eternal=1 -Wildgrowth\ Walker=1 -Wildheart\ Invoker=1 -Wildsize=1 -Wildwood\ Rebirth=1 -Will-Forged\ Golem=1 -Willbender=1 -Willbreaker=1 -Willow\ Priestess=1 -Wily\ Bandar=1 -Wily\ Goblin=1 -Wind-Kin\ Raiders=1 -Wind-Scarred\ Crag=1 -Wind\ Dancer=1 -Wind\ Drake=1 -Wind\ Shear=1 -Wind\ Strider=1 -Windborne\ Charge=1 -Windbrisk\ Raptor=1 -Windgrace\ Acolyte=1 -Winding\ Wurm=1 -Windreaper\ Falcon=1 -Windreaver=1 -Windrider\ Patrol=1 -Winds\ of\ Rath=1 -Winds\ of\ Rebuke=1 -Windseeker\ Centaur=1 -Windstorm=1 -Windwright\ Mage=1 -Wine\ of\ Blood\ and\ Iron=1 -Wing\ Puncture=1 -Wing\ Shards=1 -Wing\ Snare=1 -Wing\ Splicer=1 -Wing\ Storm=1 -Wingcrafter=1 -Winged\ Coatl=1 -Winged\ Shepherd=1 -Wingmate\ Roc=1 -Wingrattle\ Scarecrow=1 -Wings\ of\ Aesthir=1 -Wings\ of\ Velis\ Vel=1 -Wingsteed\ Rider=1 -Winnow=1 -Winnower\ Patrol=1 -Winter\ Blast=1 -Winterflame=1 -Wipe\ Clean=1 -Wirecat=1 -Wirefly\ Hive=1 -Wirewood\ Elf=1 -Wirewood\ Guardian=1 -Wirewood\ Savage=1 -Wispweaver\ Angel=1 -Wistful\ Thinking=1 -Wit's\ End=1 -Witch's\ Familiar=1 -Witch's\ Mist=1 -Witch-Maw\ Nephilim=1 -Witches'\ Eye=1 -Withered\ Wretch=1 -Withering\ Gaze=1 -Withering\ Hex=1 -Withering\ Wisps=1 -Witherscale\ Wurm=1 -Without\ Weakness=1 -Withstand=1 -Withstand\ Death=1 -Witness\ of\ the\ Ages=1 -Witness\ the\ End=1 -Wizard\ Mentor=1 -Wizard\ Replica=1 -Wizened\ Cenn=1 -Woebearer=1 -Woebringer\ Demon=1 -Woeleecher=1 -Wojek\ Apothecary=1 -Wojek\ Siren=1 -Wolf-Skull\ Shaman=1 -Wolfbriar\ Elemental=1 -Wolfhunter's\ Quiver=1 -Wolfir\ Avenger=1 -Wolfkin\ Bond=1 -Wonder=1 -Wood\ Elemental=1 -Woodborn\ Behemoth=1 -Woodcloaker=1 -Woodcutter's\ Grit=1 -Wooden\ Sphere=1 -Wooden\ Stake=1 -Woodland\ Changeling=1 -Woodland\ Druid=1 -Woodland\ Guidance=1 -Woodland\ Patrol=1 -Woodland\ Sleuth=1 -Woodland\ Stream=1 -Woodland\ Wanderer=1 -Woodlot\ Crawler=1 -Woodlurker\ Mimic=1 -Woodripper=1 -Woodweaver's\ Puzzleknot=1 -Woodwraith\ Corrupter=1 -Woodwraith\ Strangler=1 -Woolly\ Loxodon=1 -Woolly\ Mammoths=1 -Woolly\ Spider=1 -Woolly\ Thoctar=1 -Word\ of\ Seizing=1 -Word\ of\ Undoing=1 -Words\ of\ War=1 -Words\ of\ Waste=1 -Words\ of\ Wilding=1 -Workshop\ Assistant=1 -World\ Queller=1 -World\ Shaper=1 -World\ at\ War=1 -Worldgorger\ Dragon=1 -Worldheart\ Phoenix=1 -Worldly\ Counsel=1 -Worldpurge=1 -Worldslayer=1 -Worm\ Harvest=1 -Wormfang\ Drake=1 -Wormfang\ Manta=1 -Wormwood\ Dryad=1 -Wormwood\ Treefolk=1 -Wort,\ Boggart\ Auntie=1 -Wort,\ the\ Raidmother=1 -Worthy\ Cause=1 -Wound\ Reflection=1 -Wrangle=1 -Wrap\ in\ Flames=1 -Wrath\ of\ Marit\ Lage=1 -Wreak\ Havoc=1 -Wreath\ of\ Geists=1 -Wrecking\ Ball=1 -Wrecking\ Ogre=1 -Wren's\ Run\ Packmaster=1 -Wren's\ Run\ Vanquisher=1 -Wretched\ Camel=1 -Wretched\ Gryff=1 -Writ\ of\ Passage=1 -Wu\ Elite\ Cavalry=1 -Wu\ Longbowman=1 -Wu\ Warship=1 -Wurm's\ Tooth=1 -Wurmcalling=1 -Wurmskin\ Forger=1 -Wurmweaver\ Coil=1 -Wydwen,\ the\ Biting\ Gale=1 -Wyluli\ Wolf=1 -Xathrid\ Gorgon=1 -Xathrid\ Slyblade=1 -Xenic\ Poltergeist=1 -Yamabushi's\ Flame=1 -Yamabushi's\ Storm=1 -Yare=1 -Yargle,\ Glutton\ of\ Urborg=1 -Yavimaya\ Ancients=1 -Yavimaya\ Ants=1 -Yavimaya\ Dryad=1 -Yavimaya\ Enchantress=1 -Yavimaya\ Kavu=1 -Yavimaya\ Sapherd=1 -Yavimaya\ Scion=1 -Yavimaya\ Wurm=1 -Yawgmoth's\ Edict=1 -Yawgmoth\ Demon=1 -Yawning\ Fissure=1 -Yeva's\ Forcemage=1 -Yeva,\ Nature's\ Herald=1 -Yew\ Spirit=1 -Yixlid\ Jailer=1 -Yoke\ of\ the\ Damned=1 -Yoked\ Ox=1 -Yoked\ Plowbeast=1 -Yomiji,\ Who\ Bars\ the\ Way=1 -Yore-Tiller\ Nephilim=1 -Yosei,\ the\ Morning\ Star=1 -Yotian\ Soldier=1 -Young\ Wei\ Recruits=1 -Youthful\ Knight=1 -Youthful\ Scholar=1 -Yuki-Onna=1 -Yukora,\ the\ Prisoner=1 -Zada's\ Commando=1 -Zada,\ Hedron\ Grinder=1 -Zameck\ Guildmage=1 -Zanikev\ Locust=1 -Zap=1 -Zarichi\ Tiger=1 -Zealot\ il-Vec=1 -Zealous\ Guardian=1 -Zealous\ Inquisitor=1 -Zebra\ Unicorn=1 -Zektar\ Shrine\ Expedition=1 -Zendikar's\ Roil=1 -Zendikar\ Farguide=1 -Zendikar\ Incarnate=1 -Zendikar\ Resurgent=1 -Zenith\ Seeker=1 -Zephid's\ Embrace=1 -Zephid=1 -Zephyr\ Charge=1 -Zephyr\ Net=1 -Zephyr\ Spirit=1 -Zephyr\ Sprite=1 -Zerapa\ Minotaur=1 -Zhalfirin\ Commander=1 -Zhalfirin\ Crusader=1 -Zhalfirin\ Knight=1 -Zhang\ Fei,\ Fierce\ Warrior=1 -Zhur-Taa\ Ancient=1 -Zhur-Taa\ Druid=1 -Zhur-Taa\ Swine=1 -Zodiac\ Monkey=1 -Zoetic\ Cavern=1 -Zof\ Shade=1 -Zombie\ Boa=1 -Zombie\ Cannibal=1 -Zombie\ Cutthroat=1 -Zombie\ Goliath=1 -Zombie\ Musher=1 -Zombie\ Trailblazer=1 -Zombify=1 -Zoologist=1 -Zulaport\ Chainmage=1 -Zulaport\ Cutthroat=1 -Zulaport\ Enforcer=1 -Zur's\ Weirding=1 -Zuran\ Spellcaster=1 diff --git a/Mage.Sets/pom.xml b/Mage.Sets/pom.xml index e6bb3dc467..645fc7da99 100644 --- a/Mage.Sets/pom.xml +++ b/Mage.Sets/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 mage-sets diff --git a/Mage.Sets/src/mage/cards/a/AbandonHope.java b/Mage.Sets/src/mage/cards/a/AbandonHope.java index 635917a898..3c3e6e538c 100644 --- a/Mage.Sets/src/mage/cards/a/AbandonHope.java +++ b/Mage.Sets/src/mage/cards/a/AbandonHope.java @@ -1,4 +1,3 @@ - package mage.cards.a; import mage.abilities.Ability; @@ -15,8 +14,8 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; -import mage.target.TargetPlayer; import mage.target.common.TargetCardInHand; +import mage.target.common.TargetOpponent; import java.util.UUID; @@ -29,18 +28,21 @@ public final class AbandonHope extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{1}{B}"); // As an additional cost to cast Abandon Hope, discard X cards. - Ability ability = new SimpleStaticAbility(Zone.ALL, new InfoEffect("As an additional cost to cast this spell, discard X cards")); + Ability ability = new SimpleStaticAbility( + Zone.ALL, new InfoEffect("As an additional cost to cast this spell, discard X cards") + ); ability.setRuleAtTheTop(true); this.addAbility(ability); // Look at target opponent's hand and choose X cards from it. That player discards those cards. - ManacostVariableValue manaX = ManacostVariableValue.instance; - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(manaX, TargetController.ANY)); - this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addEffect( + new DiscardCardYouChooseTargetEffect(ManacostVariableValue.instance, TargetController.ANY) + .setText("Look at target opponent's hand and choose X cards from it. That player discards those cards")); + this.getSpellAbility().addTarget(new TargetOpponent()); this.getSpellAbility().setCostAdjuster(AbandonHopeAdjuster.instance); } - public AbandonHope(final AbandonHope card) { + private AbandonHope(final AbandonHope card) { super(card); } @@ -60,4 +62,4 @@ enum AbandonHopeAdjuster implements CostAdjuster { ability.addCost(new DiscardTargetCost(new TargetCardInHand(xValue, xValue, StaticFilters.FILTER_CARD_CARDS))); } } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/a/AbattoirGhoul.java b/Mage.Sets/src/mage/cards/a/AbattoirGhoul.java index 5350171150..2f78aafef9 100644 --- a/Mage.Sets/src/mage/cards/a/AbattoirGhoul.java +++ b/Mage.Sets/src/mage/cards/a/AbattoirGhoul.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -7,13 +6,11 @@ import mage.abilities.Ability; import mage.abilities.common.DealtDamageAndDiedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FirstStrikeAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -25,7 +22,7 @@ import mage.players.Player; public final class AbattoirGhoul extends CardImpl { public AbattoirGhoul(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(3); @@ -65,18 +62,15 @@ class AbattoirGhoulEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); - Card card = game.getCard(targetPointer.getFirst(game, source)); - if (card != null) { - Permanent creature = (Permanent) game.getLastKnownInformation(card.getId(), Zone.BATTLEFIELD); - if (creature != null) { - int toughness = creature.getToughness().getValue(); - if (you != null) { - you.gainLife(toughness, game, source); - return true; - } + Player controller = game.getPlayer(source.getControllerId()); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (creature != null) { + int toughness = creature.getToughness().getValue(); + if (controller != null) { + controller.gainLife(toughness, game, source); + return true; } } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/a/AbbotOfKeralKeep.java b/Mage.Sets/src/mage/cards/a/AbbotOfKeralKeep.java index 0d85e9cf3c..92be44357d 100644 --- a/Mage.Sets/src/mage/cards/a/AbbotOfKeralKeep.java +++ b/Mage.Sets/src/mage/cards/a/AbbotOfKeralKeep.java @@ -76,7 +76,7 @@ class AbbotOfKeralKeepExileEffect extends OneShotEffect { String exileName = sourcePermanent.getIdName() + " "; controller.moveCardsToExile(card, source, game, true, source.getSourceId(), exileName); ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/a/AberrantResearcher.java b/Mage.Sets/src/mage/cards/a/AberrantResearcher.java index 54c1bd42d4..c83c858867 100644 --- a/Mage.Sets/src/mage/cards/a/AberrantResearcher.java +++ b/Mage.Sets/src/mage/cards/a/AberrantResearcher.java @@ -1,22 +1,22 @@ package mage.cards.a; -import java.util.UUID; - import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.TransformAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** * @author fireshoes */ @@ -54,7 +54,7 @@ class AberrantResearcherEffect extends OneShotEffect { public AberrantResearcherEffect() { super(Outcome.Benefit); - staticText = "put the top card of your library into your graveyard. If it's an instant or sorcery card, transform {this}"; + staticText = "mill a card. If an instant or sorcery card was milled this way, transform {this}"; } public AberrantResearcherEffect(final AberrantResearcherEffect effect) { @@ -64,15 +64,16 @@ class AberrantResearcherEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && controller.getLibrary().hasCards()) { - Card card = controller.getLibrary().getFromTop(game); - controller.moveCards(card, Zone.GRAVEYARD, source, game); - if (card.isInstant() || card.isSorcery()) { - new TransformSourceEffect(true).apply(game, source); - } - return true; + if (controller == null + || controller + .millCards(1, source, game) + .getCards(game) + .stream() + .noneMatch(MageObject::isInstantOrSorcery)) { + return false; } - return false; + new TransformSourceEffect(true).apply(game, source); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/a/AbnormalEndurance.java b/Mage.Sets/src/mage/cards/a/AbnormalEndurance.java index 847823acad..1aa6c11826 100644 --- a/Mage.Sets/src/mage/cards/a/AbnormalEndurance.java +++ b/Mage.Sets/src/mage/cards/a/AbnormalEndurance.java @@ -1,7 +1,7 @@ package mage.cards.a; import java.util.UUID; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -26,7 +26,7 @@ public final class AbnormalEndurance extends CardImpl { .setText("Until end of turn, target creature gets +2/+0") ); getSpellAbility().addEffect(new GainAbilityTargetEffect( - new DiesTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false), + new DiesSourceTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false), Duration.EndOfTurn, "and gains \"When this creature dies, return it to the battlefield tapped under its owner's control.\"" )); diff --git a/Mage.Sets/src/mage/cards/a/Abolish.java b/Mage.Sets/src/mage/cards/a/Abolish.java index 47d2170ba0..e619b5c4ae 100644 --- a/Mage.Sets/src/mage/cards/a/Abolish.java +++ b/Mage.Sets/src/mage/cards/a/Abolish.java @@ -1,4 +1,3 @@ - package mage.cards.a; import mage.abilities.costs.AlternativeCostSourceAbility; @@ -16,7 +15,6 @@ import mage.target.common.TargetCardInHand; import java.util.UUID; /** - * * @author Backfir3 */ public final class Abolish extends CardImpl { @@ -28,8 +26,7 @@ public final class Abolish extends CardImpl { } public Abolish(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}{W}"); // You may discard a Plains card rather than pay Abolish's mana cost. this.addAbility(new AlternativeCostSourceAbility(new DiscardTargetCost(new TargetCardInHand(filterCost)))); diff --git a/Mage.Sets/src/mage/cards/a/Abomination.java b/Mage.Sets/src/mage/cards/a/Abomination.java index 167f58ad8e..e9b37fb336 100644 --- a/Mage.Sets/src/mage/cards/a/Abomination.java +++ b/Mage.Sets/src/mage/cards/a/Abomination.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -39,7 +39,7 @@ public final class Abomination extends CardImpl { // Whenever Abomination blocks or becomes blocked by a green or white creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false)); } public Abomination(final Abomination card) { diff --git a/Mage.Sets/src/mage/cards/a/AbuJafar.java b/Mage.Sets/src/mage/cards/a/AbuJafar.java index 1435c02087..b83054c00e 100644 --- a/Mage.Sets/src/mage/cards/a/AbuJafar.java +++ b/Mage.Sets/src/mage/cards/a/AbuJafar.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -31,7 +31,7 @@ public final class AbuJafar extends CardImpl { new BlockingAttackerIdPredicate(this.getId()))); // When Abu Ja'far dies, destroy all creatures blocking or blocked by it. They can't be regenerated. - this.addAbility(new DiesTriggeredAbility(new DestroyAllEffect(filter, true), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DestroyAllEffect(filter, true), false)); } public AbuJafar(final AbuJafar card) { diff --git a/Mage.Sets/src/mage/cards/a/AbundantMaw.java b/Mage.Sets/src/mage/cards/a/AbundantMaw.java index 3d558b7498..8892b9070c 100644 --- a/Mage.Sets/src/mage/cards/a/AbundantMaw.java +++ b/Mage.Sets/src/mage/cards/a/AbundantMaw.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; @@ -15,14 +13,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class AbundantMaw extends CardImpl { public AbundantMaw(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{8}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{8}"); this.subtype.add(SubType.ELDRAZI); this.subtype.add(SubType.LEECH); this.power = new MageInt(6); @@ -30,11 +29,11 @@ public final class AbundantMaw extends CardImpl { // Emerge {6}{B} this.addAbility(new EmergeAbility(this, new ManaCostsImpl<>("{6}{B}"))); - + // When you cast Abundant Maw, target opponent loses 3 life and you gain 3 life. Ability ability = new CastSourceTriggeredAbility(new GainLifeEffect(3)); + ability.addEffect(new LoseLifeTargetEffect(3).concatBy("and")); ability.addTarget(new TargetOpponent()); - ability.addEffect(new LoseLifeTargetEffect(3)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AbyssalGatekeeper.java b/Mage.Sets/src/mage/cards/a/AbyssalGatekeeper.java index cb8017787d..2219dcb20e 100644 --- a/Mage.Sets/src/mage/cards/a/AbyssalGatekeeper.java +++ b/Mage.Sets/src/mage/cards/a/AbyssalGatekeeper.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class AbyssalGatekeeper extends CardImpl { this.toughness = new MageInt(1); // When Abyssal Gatekeeper dies, each player sacrifices a creature. - this.addAbility(new DiesTriggeredAbility(new SacrificeAllEffect(1, new FilterControlledCreaturePermanent("creature")))); + this.addAbility(new DiesSourceTriggeredAbility(new SacrificeAllEffect(1, new FilterControlledCreaturePermanent("creature")))); } public AbyssalGatekeeper(final AbyssalGatekeeper card) { diff --git a/Mage.Sets/src/mage/cards/a/AbzanAscendancy.java b/Mage.Sets/src/mage/cards/a/AbzanAscendancy.java index 594e077713..ffea394e3d 100644 --- a/Mage.Sets/src/mage/cards/a/AbzanAscendancy.java +++ b/Mage.Sets/src/mage/cards/a/AbzanAscendancy.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -17,13 +15,14 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AbzanAscendancy extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature you control"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a nontoken creature you control"); static { filter.add(TargetController.YOU.getControllerPredicate()); @@ -37,7 +36,7 @@ public final class AbzanAscendancy extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE), false)); // Whenever a nontoken creature you control dies, create a 1/1 white Spirit creature token with flying. - this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken("KTK")), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken()), false, filter)); } diff --git a/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java b/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java index e050309518..76707204e8 100644 --- a/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java +++ b/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java @@ -21,7 +21,7 @@ public final class AbzanBeastmaster extends CardImpl { public AbzanBeastmaster(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(2); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/a/AbzanSkycaptain.java b/Mage.Sets/src/mage/cards/a/AbzanSkycaptain.java index 72dd6ba011..50faba04db 100644 --- a/Mage.Sets/src/mage/cards/a/AbzanSkycaptain.java +++ b/Mage.Sets/src/mage/cards/a/AbzanSkycaptain.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.keyword.BolsterEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class AbzanSkycaptain extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Abzan Captain dies, bolster 2. - this.addAbility(new DiesTriggeredAbility(new BolsterEffect(2))); + this.addAbility(new DiesSourceTriggeredAbility(new BolsterEffect(2))); } public AbzanSkycaptain(final AbzanSkycaptain card) { diff --git a/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java b/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java index cb65e5f8f5..7036d80a8f 100644 --- a/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java +++ b/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java @@ -1,25 +1,26 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; -import mage.constants.SubType; +import mage.abilities.hint.ConditionHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author JRHerlehy */ public final class AcademyJourneymage extends CardImpl { @@ -32,14 +33,16 @@ public final class AcademyJourneymage extends CardImpl { public AcademyJourneymage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); - + this.subtype.add(SubType.HUMAN, SubType.WIZARD); this.power = new MageInt(3); this.toughness = new MageInt(2); // This spell costs {1} less to cast if you control a Wizard. - Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter))); + Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, condition)); ability.setRuleAtTheTop(true); + ability.addHint(new ConditionHint(condition, "You control a Wizard")); this.addAbility(ability); // When Academy Journeymage enters the battlefield, return target creature an opponent controls to its owner's hand. diff --git a/Mage.Sets/src/mage/cards/a/AcademyRector.java b/Mage.Sets/src/mage/cards/a/AcademyRector.java index e20df2f5ed..2c519616d3 100644 --- a/Mage.Sets/src/mage/cards/a/AcademyRector.java +++ b/Mage.Sets/src/mage/cards/a/AcademyRector.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.common.ExileSourceFromGraveCost; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; @@ -29,7 +29,7 @@ public final class AcademyRector extends CardImpl { this.toughness = new MageInt(2); // When Academy Rector dies, you may exile it. If you do, search your library for an enchantment card, put that card onto the battlefield, then shuffle your library. - this.addAbility(new DiesTriggeredAbility( + this.addAbility(new DiesSourceTriggeredAbility( new DoIfCostPaid( new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(new FilterEnchantmentCard())), new ExileSourceFromGraveCost(), diff --git a/Mage.Sets/src/mage/cards/a/AcademyRuins.java b/Mage.Sets/src/mage/cards/a/AcademyRuins.java index 6c3f06bd42..fc58483d3a 100644 --- a/Mage.Sets/src/mage/cards/a/AcademyRuins.java +++ b/Mage.Sets/src/mage/cards/a/AcademyRuins.java @@ -26,9 +26,9 @@ public final class AcademyRuins extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.LAND},""); addSuperType(SuperType.LEGENDARY); - // {tap}: Add {C}. + // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); - // {1}{U}, {tap}: Put target artifact card from your graveyard on top of your library. + // {1}{U}, {T}: Put target artifact card from your graveyard on top of your library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutOnLibraryTargetEffect(true), new ManaCostsImpl("{1}{U}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard"))); diff --git a/Mage.Sets/src/mage/cards/a/AccursedWitch.java b/Mage.Sets/src/mage/cards/a/AccursedWitch.java index bcde8821e6..0bc6e8d72a 100644 --- a/Mage.Sets/src/mage/cards/a/AccursedWitch.java +++ b/Mage.Sets/src/mage/cards/a/AccursedWitch.java @@ -1,25 +1,20 @@ - package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.SpellAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterCard; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetOpponent; -import mage.util.CardUtil; import java.util.UUID; @@ -39,10 +34,13 @@ public final class AccursedWitch extends CardImpl { this.secondSideCardClazz = mage.cards.i.InfectiousCurse.class; // Spells your opponents cast that target Accursed Witch cost {1} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AccursedWitchSpellsCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(-1, new FilterCard("Spells"), TargetController.OPPONENT)) + ); + // When Accursed Witch dies, return it to the battlefield transformed under your control attached to target opponent. this.addAbility(new TransformAbility()); - Ability ability = new DiesTriggeredAbility(new AccursedWitchReturnTransformedEffect()); + Ability ability = new DiesSourceTriggeredAbility(new AccursedWitchReturnTransformedEffect()); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } @@ -93,45 +91,3 @@ class AccursedWitchReturnTransformedEffect extends OneShotEffect { return true; } } - -class AccursedWitchSpellsCostReductionEffect extends CostModificationEffectImpl { - - AccursedWitchSpellsCostReductionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.REDUCE_COST); - this.staticText = "Spells your opponents cast that target {this} cost {1} less to cast."; - } - - private AccursedWitchSpellsCostReductionEffect(AccursedWitchSpellsCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - CardUtil.reduceCost(abilityToModify, 1); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (!(abilityToModify instanceof SpellAbility) || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - return false; - } - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - Permanent permanent = game.getPermanent(targetUUID); - if (permanent != null && permanent.getId().equals(source.getSourceId())) { - return true; - } - } - } - } - return false; - } - - @Override - public AccursedWitchSpellsCostReductionEffect copy() { - return new AccursedWitchSpellsCostReductionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AcolyteOfAffliction.java b/Mage.Sets/src/mage/cards/a/AcolyteOfAffliction.java index 3a158f7778..58791c6deb 100644 --- a/Mage.Sets/src/mage/cards/a/AcolyteOfAffliction.java +++ b/Mage.Sets/src/mage/cards/a/AcolyteOfAffliction.java @@ -53,8 +53,7 @@ class AcolyteOfAfflictionEffect extends OneShotEffect { AcolyteOfAfflictionEffect() { super(Outcome.Benefit); - staticText = "put the top two cards of your library into your graveyard, " + - "then you may return a permanent card from your graveyard to your hand."; + staticText = "mill two cards, then you may return a permanent card from your graveyard to your hand."; } private AcolyteOfAfflictionEffect(final AcolyteOfAfflictionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AcornCatapult.java b/Mage.Sets/src/mage/cards/a/AcornCatapult.java index d19f4460e2..7742ba3ae9 100644 --- a/Mage.Sets/src/mage/cards/a/AcornCatapult.java +++ b/Mage.Sets/src/mage/cards/a/AcornCatapult.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -28,7 +27,7 @@ public final class AcornCatapult extends CardImpl { public AcornCatapult(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {1}, {tap}: Acorn Catapult deals 1 damage to any target. That creature's controller or that player creates a 1/1 green Squirrel creature token. + // {1}, {T}: Acorn Catapult deals 1 damage to any target. That creature's controller or that player creates a 1/1 green Squirrel creature token. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("{1}")); ability.addCost(new TapSourceCost()); ability.addEffect(new AcornCatapultEffect()); @@ -64,10 +63,9 @@ class AcornCatapultEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID targetId = getTargetPointer().getFirst(game, source); - Player player = game.getPlayer(targetId); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); if (player == null) { - Permanent permanent = game.getPermanent(targetId); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { player = game.getPlayer(permanent.getControllerId()); } diff --git a/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java b/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java index a7af03c18d..6ee33d1681 100644 --- a/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java +++ b/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect; @@ -11,8 +9,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AcrobaticManeuver extends CardImpl { @@ -24,7 +23,7 @@ public final class AcrobaticManeuver extends CardImpl { this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); Effect effect = new ExileTargetForSourceEffect(); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/cards/a/ActOfAuthority.java b/Mage.Sets/src/mage/cards/a/ActOfAuthority.java index 916d33963d..c1acf361ec 100644 --- a/Mage.Sets/src/mage/cards/a/ActOfAuthority.java +++ b/Mage.Sets/src/mage/cards/a/ActOfAuthority.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -66,7 +65,7 @@ class ActOfAuthorityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetPermanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); + Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (targetPermanent != null && new ExileTargetEffect().apply(game, source)) { Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); if (sourcePermanent != null) { diff --git a/Mage.Sets/src/mage/cards/a/ActOnImpulse.java b/Mage.Sets/src/mage/cards/a/ActOnImpulse.java index a2f44ab166..0f7dfa3ff8 100644 --- a/Mage.Sets/src/mage/cards/a/ActOnImpulse.java +++ b/Mage.Sets/src/mage/cards/a/ActOnImpulse.java @@ -1,20 +1,11 @@ package mage.cards.a; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; -import mage.cards.Card; +import mage.abilities.effects.common.ExileTop3MayPlayUntilEndOfTurnEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.players.Player; -import mage.target.targetpointer.FixedTargets; +import mage.constants.CardType; + +import java.util.UUID; /** * @@ -26,7 +17,7 @@ public final class ActOnImpulse extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); // Exile the top three cards of your library. Until end of turn, you may play cards exiled this way. - this.getSpellAbility().addEffect(new ActOnImpulseExileEffect()); + this.getSpellAbility().addEffect(new ExileTop3MayPlayUntilEndOfTurnEffect()); } public ActOnImpulse(final ActOnImpulse card) { @@ -39,45 +30,3 @@ public final class ActOnImpulse extends CardImpl { } } -class ActOnImpulseExileEffect extends OneShotEffect { - - public ActOnImpulseExileEffect() { - super(Outcome.Benefit); - this.staticText = "Exile the top three cards of your library. Until end of turn, you may play cards exiled this way"; - } - - public ActOnImpulseExileEffect(final ActOnImpulseExileEffect effect) { - super(effect); - } - - @Override - public ActOnImpulseExileEffect copy() { - return new ActOnImpulseExileEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && sourceObject != null) { - Set cards = new HashSet<>(controller.getLibrary().getTopCards(game, 3)); - if (!cards.isEmpty()) { - controller.moveCardsToExile(cards, source, game, true, source.getSourceId(), sourceObject.getIdName()); - // remove cards that could not be moved to exile - for (Card card : cards) { - if (!Zone.EXILED.equals(game.getState().getZone(card.getId()))) { - cards.remove(card); - } - } - if (!cards.isEmpty()) { - ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTargets(cards, game)); - game.addEffect(effect, source); - } - } - return true; - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/cards/a/AdNauseam.java b/Mage.Sets/src/mage/cards/a/AdNauseam.java index 8242867b1d..743144cf13 100644 --- a/Mage.Sets/src/mage/cards/a/AdNauseam.java +++ b/Mage.Sets/src/mage/cards/a/AdNauseam.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -14,6 +12,8 @@ import mage.constants.Zone; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** * @author North */ @@ -62,13 +62,19 @@ class AdNauseamEffect extends OneShotEffect { } while (controller.chooseUse(outcome, message, source, game) && controller.getLibrary().hasCards()) { Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - controller.moveCards(card, Zone.HAND, source, game); - int cmc = card.getConvertedManaCost(); - if (cmc > 0) { - controller.loseLife(cmc, game, false); - } - controller.revealCards(sourceCard.getIdName() + " put into hand", new CardsImpl(card), game); + if (card == null) { + break; + } + controller.moveCards(card, Zone.HAND, source, game); + int cmc = card.getConvertedManaCost(); + if (cmc > 0) { + controller.loseLife(cmc, game, false); + } + controller.revealCards(sourceCard.getIdName() + " put into hand", new CardsImpl(card), game); + + // AI workaround to stop infinite choose (only one card allows) + if (!controller.isHuman() && !controller.isTestMode()) { + break; } } return true; diff --git a/Mage.Sets/src/mage/cards/a/AdaptiveShimmerer.java b/Mage.Sets/src/mage/cards/a/AdaptiveShimmerer.java new file mode 100644 index 0000000000..47e21901a9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AdaptiveShimmerer.java @@ -0,0 +1,44 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AdaptiveShimmerer extends CardImpl { + + public AdaptiveShimmerer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}"); + + this.subtype.add(SubType.INSECT); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Adaptive Shimmerer enters the battlefield with three +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(3), true + ), "with three +1/+1 counters on it")); + } + + private AdaptiveShimmerer(final AdaptiveShimmerer card) { + super(card); + } + + @Override + public AdaptiveShimmerer copy() { + return new AdaptiveShimmerer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AdherentOfHope.java b/Mage.Sets/src/mage/cards/a/AdherentOfHope.java new file mode 100644 index 0000000000..be05c2aaaf --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AdherentOfHope.java @@ -0,0 +1,54 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class AdherentOfHope extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(CardType.PLANESWALKER.getPredicate()); + filter.add(SubType.BASRI.getPredicate()); + } + + public AdherentOfHope(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfCombatTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), TargetController.YOU, false), + new PermanentsOnTheBattlefieldCondition(filter), + "At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on {this}.")); + } + + private AdherentOfHope(final AdherentOfHope card) { + super(card); + } + + @Override + public AdherentOfHope copy() { + return new AdherentOfHope(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AdmiralsOrder.java b/Mage.Sets/src/mage/cards/a/AdmiralsOrder.java index c152c78694..d43cbaf41a 100644 --- a/Mage.Sets/src/mage/cards/a/AdmiralsOrder.java +++ b/Mage.Sets/src/mage/cards/a/AdmiralsOrder.java @@ -1,32 +1,36 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.condition.common.RaidCondition; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.target.TargetSpell; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author L_J */ -public final class AdmiralsOrder extends CardImpl { +public final class AdmiralsOrder extends CardImpl { public AdmiralsOrder(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}"); // Raid - If you attacked with a creature this turn, you may pay {U} rather than pay this spell's mana cost. - this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{U}"), RaidCondition.instance, - "

Raid — If you attacked with a creature this turn, you may pay {U} rather than pay this spell's mana cost"), new PlayerAttackedWatcher()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{U}"), RaidCondition.instance, + "

Raid — If you attacked with a creature this turn, you may pay {U} rather than pay this spell's mana cost"), + new PlayerAttackedWatcher()); // Counter target spell. this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addTarget(new TargetSpell()); + this.getSpellAbility().setAbilityWord(AbilityWord.RAID); + this.getSpellAbility().addHint(RaidHint.instance); } public AdmiralsOrder(final AdmiralsOrder card) { diff --git a/Mage.Sets/src/mage/cards/a/AdventOfTheWurm.java b/Mage.Sets/src/mage/cards/a/AdventOfTheWurm.java index e00ee2edd2..88295ecad2 100644 --- a/Mage.Sets/src/mage/cards/a/AdventOfTheWurm.java +++ b/Mage.Sets/src/mage/cards/a/AdventOfTheWurm.java @@ -1,16 +1,14 @@ - - package mage.cards.a; -import java.util.UUID; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.game.permanent.token.WurmToken2; +import mage.game.permanent.token.WurmWithTrampleToken; + +import java.util.UUID; /** - * * @author LevelX2 */ @@ -18,10 +16,10 @@ import mage.game.permanent.token.WurmToken2; public final class AdventOfTheWurm extends CardImpl { public AdventOfTheWurm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}{G}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}{G}{W}"); // Create a 5/5 green Wurm creature token with trample. - this.getSpellAbility().addEffect(new CreateTokenEffect(new WurmToken2())); + this.getSpellAbility().addEffect(new CreateTokenEffect(new WurmWithTrampleToken())); } public AdventOfTheWurm(final AdventOfTheWurm card) { diff --git a/Mage.Sets/src/mage/cards/a/AdventurersGuildhouse.java b/Mage.Sets/src/mage/cards/a/AdventurersGuildhouse.java index 0d65831b1f..a705c733c7 100644 --- a/Mage.Sets/src/mage/cards/a/AdventurersGuildhouse.java +++ b/Mage.Sets/src/mage/cards/a/AdventurersGuildhouse.java @@ -32,7 +32,7 @@ public final class AdventurersGuildhouse extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Green legendary creatures you control have "bands with other legendary creatures." - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(new BandsWithOtherAbility(SuperType.LEGENDARY), Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(new BandsWithOtherAbility(SuperType.LEGENDARY), Duration.WhileOnBattlefield, filter).withForceQuotes())); } public AdventurersGuildhouse(final AdventurersGuildhouse card) { diff --git a/Mage.Sets/src/mage/cards/a/AegisAngel.java b/Mage.Sets/src/mage/cards/a/AegisAngel.java index 7222995d67..bef99e72ea 100644 --- a/Mage.Sets/src/mage/cards/a/AegisAngel.java +++ b/Mage.Sets/src/mage/cards/a/AegisAngel.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -18,6 +17,7 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.target.TargetPermanent; +import mage.watchers.common.LostControlWatcher; /** * @author Loki @@ -47,6 +47,7 @@ public final class AegisAngel extends CardImpl { "another target permanent is indestructible for as long as you control Aegis Angel"); Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); ability.addTarget(new TargetPermanent(filter)); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AegisTurtle.java b/Mage.Sets/src/mage/cards/a/AegisTurtle.java new file mode 100644 index 0000000000..55bf35202b --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AegisTurtle.java @@ -0,0 +1,32 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AegisTurtle extends CardImpl { + + public AegisTurtle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); + + this.subtype.add(SubType.TURTLE); + this.power = new MageInt(0); + this.toughness = new MageInt(5); + } + + private AegisTurtle(final AegisTurtle card) { + super(card); + } + + @Override + public AegisTurtle copy() { + return new AegisTurtle(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AerialCaravan.java b/Mage.Sets/src/mage/cards/a/AerialCaravan.java index 27524c47b4..be2342656c 100644 --- a/Mage.Sets/src/mage/cards/a/AerialCaravan.java +++ b/Mage.Sets/src/mage/cards/a/AerialCaravan.java @@ -81,7 +81,7 @@ class AerialCaravanExileEffect extends OneShotEffect { String exileName = sourcePermanent.getIdName() + " "; controller.moveCardsToExile(card, source, game, true, source.getSourceId(), exileName); ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/a/AerialFormation.java b/Mage.Sets/src/mage/cards/a/AerialFormation.java index 0ecadbb6ec..3e4ca48e6c 100644 --- a/Mage.Sets/src/mage/cards/a/AerialFormation.java +++ b/Mage.Sets/src/mage/cards/a/AerialFormation.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -13,20 +11,21 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AerialFormation extends CardImpl { public AerialFormation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); // Strive — Aerial Formation costs {2}{U} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{U}")); + // Any number of target creatures each get +1/+1 and gain flying until end of turn. - Effect effect = new BoostTargetEffect(1,1, Duration.EndOfTurn); + Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); effect.setText("Any number of target creatures each get +1/+1"); this.getSpellAbility().addEffect(effect); effect = new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); diff --git a/Mage.Sets/src/mage/cards/a/AerieBowmasters.java b/Mage.Sets/src/mage/cards/a/AerieBowmasters.java index 8d4f11ce50..baac41ba67 100644 --- a/Mage.Sets/src/mage/cards/a/AerieBowmasters.java +++ b/Mage.Sets/src/mage/cards/a/AerieBowmasters.java @@ -19,7 +19,7 @@ public final class AerieBowmasters extends CardImpl { public AerieBowmasters(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.ARCHER); this.power = new MageInt(3); this.toughness = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/a/AetherStorm.java b/Mage.Sets/src/mage/cards/a/AetherStorm.java index a2298fa7be..3bc436ae30 100644 --- a/Mage.Sets/src/mage/cards/a/AetherStorm.java +++ b/Mage.Sets/src/mage/cards/a/AetherStorm.java @@ -50,7 +50,7 @@ class AetherStormReplacementEffect extends ContinuousRuleModifyingEffectImpl { public AetherStormReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Creature spells can't be cast."; + staticText = "Creature spells can't be cast"; } private AetherStormReplacementEffect(final AetherStormReplacementEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AetherTide.java b/Mage.Sets/src/mage/cards/a/AetherTide.java index f368a6ff02..835aebf549 100644 --- a/Mage.Sets/src/mage/cards/a/AetherTide.java +++ b/Mage.Sets/src/mage/cards/a/AetherTide.java @@ -1,76 +1,76 @@ -package mage.cards.a; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.CostAdjuster; -import mage.abilities.costs.common.DiscardTargetCost; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.InfoEffect; -import mage.abilities.effects.common.ReturnToHandTargetEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.target.common.TargetCardInHand; -import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.TargetAdjuster; - -/** - * - * @author jeffwadsworth - */ -public final class AetherTide extends CardImpl { - - public AetherTide(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}"); - - // As an additional cost to cast Aether Tide, discard X creature cards. - Ability ability = new SimpleStaticAbility(Zone.ALL, new InfoEffect("As an additional cost to cast {this}, discard X creature cards")); - ability.setRuleAtTheTop(true); - this.addAbility(ability); - - // Return X target creatures to their owners' hands. - Effect effect = new ReturnToHandTargetEffect(true); - effect.setText("Return X target creatures to their owners' hands"); - this.getSpellAbility().addEffect(effect); - this.getSpellAbility().setTargetAdjuster(AetherTideTargetAdjuster.instance); - this.getSpellAbility().setCostAdjuster(AetherTideCostAdjuster.instance); - - } - - public AetherTide(final AetherTide card) { - super(card); - } - - @Override - public AetherTide copy() { - return new AetherTide(this); - } -} - -enum AetherTideTargetAdjuster implements TargetAdjuster { - instance; - - @Override - public void adjustTargets(Ability ability, Game game) { - ability.getTargets().clear(); - int xValue = ability.getManaCostsToPay().getX(); - ability.addTarget(new TargetCreaturePermanent(xValue, xValue, new FilterCreaturePermanent(), false)); - } -} - -enum AetherTideCostAdjuster implements CostAdjuster { - instance; - - @Override - public void adjustCosts(Ability ability, Game game) { - int xValue = ability.getManaCostsToPay().getX(); - if (xValue > 0) { - ability.addCost(new DiscardTargetCost(new TargetCardInHand(xValue, xValue, new FilterCreatureCard("creature cards")))); - } - } -} +package mage.cards.a; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +/** + * + * @author jeffwadsworth + */ +public final class AetherTide extends CardImpl { + + public AetherTide(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}"); + + // As an additional cost to cast Aether Tide, discard X creature cards. + Ability ability = new SimpleStaticAbility(Zone.ALL, new InfoEffect("As an additional cost to cast {this}, discard X creature cards")); + ability.setRuleAtTheTop(true); + this.addAbility(ability); + + // Return X target creatures to their owners' hands. + Effect effect = new ReturnToHandTargetEffect(true); + effect.setText("Return X target creatures to their owners' hands"); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().setTargetAdjuster(AetherTideTargetAdjuster.instance); + this.getSpellAbility().setCostAdjuster(AetherTideCostAdjuster.instance); + + } + + public AetherTide(final AetherTide card) { + super(card); + } + + @Override + public AetherTide copy() { + return new AetherTide(this); + } +} + +enum AetherTideTargetAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + int xValue = ability.getManaCostsToPay().getX(); + ability.addTarget(new TargetCreaturePermanent(xValue, xValue, new FilterCreaturePermanent(), false)); + } +} + +enum AetherTideCostAdjuster implements CostAdjuster { + instance; + + @Override + public void adjustCosts(Ability ability, Game game) { + int xValue = ability.getManaCostsToPay().getX(); + if (xValue > 0) { + ability.addCost(new DiscardTargetCost(new TargetCardInHand(xValue, xValue, new FilterCreatureCard("creature cards")))); + } + } +} diff --git a/Mage.Sets/src/mage/cards/a/AetherfluxReservoir.java b/Mage.Sets/src/mage/cards/a/AetherfluxReservoir.java index 871b4d90ac..ae9adf70a8 100644 --- a/Mage.Sets/src/mage/cards/a/AetherfluxReservoir.java +++ b/Mage.Sets/src/mage/cards/a/AetherfluxReservoir.java @@ -10,6 +10,7 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -28,12 +29,14 @@ public final class AetherfluxReservoir extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); // Whenever you cast a spell, you gain 1 life for each spell you've cast this turn. - this.addAbility(new SpellCastControllerTriggeredAbility(new GainLifeEffect(new AetherfluxReservoirDynamicValue()), false)); + Ability abilityGainLife = new SpellCastControllerTriggeredAbility(new GainLifeEffect(new AetherfluxReservoirDynamicValue()), false); + abilityGainLife.addHint(new ValueHint("You've cast spells this turn", new AetherfluxReservoirDynamicValue())); + this.addAbility(abilityGainLife); // Pay 50 life: Aetherflux Reservoir deals 50 damage to any target. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(50), new PayLifeCost(50)); - ability.addTarget(new TargetAnyTarget()); - this.addAbility(ability); + Ability abilityPayLife = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(50), new PayLifeCost(50)); + abilityPayLife.addTarget(new TargetAnyTarget()); + this.addAbility(abilityPayLife); } public AetherfluxReservoir(final AetherfluxReservoir card) { diff --git a/Mage.Sets/src/mage/cards/a/AetherstormRoc.java b/Mage.Sets/src/mage/cards/a/AetherstormRoc.java index aff1bf10b0..a476bfef1a 100644 --- a/Mage.Sets/src/mage/cards/a/AetherstormRoc.java +++ b/Mage.Sets/src/mage/cards/a/AetherstormRoc.java @@ -4,7 +4,7 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.costs.common.PayEnergyCost; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.TapTargetEffect; @@ -16,11 +16,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.game.Game; -import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.TargetAdjuster; +import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate; +import mage.target.TargetPermanent; import java.util.UUID; @@ -29,6 +29,13 @@ import java.util.UUID; */ public final class AetherstormRoc extends CardImpl { + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature defending player controls"); + + static { + filter.add(DefendingPlayerControlsPredicate.instance); + } + public AetherstormRoc(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.subtype.add(SubType.BIRD); @@ -37,21 +44,24 @@ public final class AetherstormRoc extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Whenever Aetherstorm Roc or another creature enters the battlefield under your control, you get {E}. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new GetEnergyCountersControllerEffect(1), new FilterCreaturePermanent("{this} or another creature"))); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new GetEnergyCountersControllerEffect(1), + StaticFilters.FILTER_PERMANENT_CREATURE, false, true + )); // Whenever Aetherstorm Roc attacks, you may pay {E}{E}. If you do, put a +1/+1 counter on it and tap up to one target creature defending player controls. - DoIfCostPaid doIfCostPaidEffect = new DoIfCostPaid(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new PayEnergyCost(2)); - doIfCostPaidEffect.addEffect(new TapTargetEffect()); - Ability ability = new AttacksTriggeredAbility(doIfCostPaidEffect, false, - "Whenever {this} attacks you may pay {E}{E}. If you do, put a +1/+1 counter on it and tap up to one target creature defending player controls."); - ability.addTarget(new TargetCreaturePermanent(0, 1, new FilterCreaturePermanent("creature defending player controls"), false)); - ability.setTargetAdjuster(AetherstormRocAdjuster.instance); + Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it"), + new PayEnergyCost(2) + ).addEffect(new TapTargetEffect().concatBy("and")), false); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); this.addAbility(ability); - } - public AetherstormRoc(final AetherstormRoc card) { + private AetherstormRoc(final AetherstormRoc card) { super(card); } @@ -61,17 +71,3 @@ public final class AetherstormRoc extends CardImpl { return new AetherstormRoc(this); } } - -enum AetherstormRocAdjuster implements TargetAdjuster { - instance; - - @Override - public void adjustTargets(Ability ability, Game game) { - ability.getTargets().clear(); - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature defending player controls"); - UUID defenderId = game.getCombat().getDefenderId(ability.getSourceId()); - filter.add(new ControllerIdPredicate(defenderId)); - TargetCreaturePermanent target = new TargetCreaturePermanent(0, 1, filter, false); - ability.addTarget(target); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/Aethertow.java b/Mage.Sets/src/mage/cards/a/Aethertow.java index d2c060a9bb..4922cb7142 100644 --- a/Mage.Sets/src/mage/cards/a/Aethertow.java +++ b/Mage.Sets/src/mage/cards/a/Aethertow.java @@ -7,12 +7,14 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.ConspireAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterAttackingOrBlockingCreature; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; /** @@ -58,9 +60,9 @@ class AethertowEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source)); + Player controller = game.getPlayer(source.getControllerId()); if (targetCreature != null) { - targetCreature.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - return true; + return controller.putCardsOnTopOfLibrary(targetCreature, game, source, true); } return false; } diff --git a/Mage.Sets/src/mage/cards/a/AetherwindBasker.java b/Mage.Sets/src/mage/cards/a/AetherwindBasker.java index e2bc62492d..3ca50e8766 100644 --- a/Mage.Sets/src/mage/cards/a/AetherwindBasker.java +++ b/Mage.Sets/src/mage/cards/a/AetherwindBasker.java @@ -17,7 +17,6 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; /** * diff --git a/Mage.Sets/src/mage/cards/a/AffaGuardHound.java b/Mage.Sets/src/mage/cards/a/AffaGuardHound.java index a5415096b0..419bf0c33e 100644 --- a/Mage.Sets/src/mage/cards/a/AffaGuardHound.java +++ b/Mage.Sets/src/mage/cards/a/AffaGuardHound.java @@ -23,7 +23,7 @@ public final class AffaGuardHound extends CardImpl { public AffaGuardHound (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/a/AffectionateIndrik.java b/Mage.Sets/src/mage/cards/a/AffectionateIndrik.java index 332ab789f2..ef55269359 100644 --- a/Mage.Sets/src/mage/cards/a/AffectionateIndrik.java +++ b/Mage.Sets/src/mage/cards/a/AffectionateIndrik.java @@ -1,31 +1,23 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.FightTargetSourceEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class AffectionateIndrik extends CardImpl { - private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public AffectionateIndrik(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}"); @@ -40,11 +32,11 @@ public final class AffectionateIndrik extends CardImpl { + "target creature you don't control"), true ); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); } - public AffectionateIndrik(final AffectionateIndrik card) { + private AffectionateIndrik(final AffectionateIndrik card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/a/Afterlife.java b/Mage.Sets/src/mage/cards/a/Afterlife.java index 802a5e90ba..ba29b8b578 100644 --- a/Mage.Sets/src/mage/cards/a/Afterlife.java +++ b/Mage.Sets/src/mage/cards/a/Afterlife.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -21,7 +20,7 @@ import mage.target.common.TargetCreaturePermanent; public final class Afterlife extends CardImpl { public Afterlife(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); // Destroy target creature. It can't be regenerated. Its controller puts a // 1/1 white Spirit creature token with flying. @@ -58,7 +57,7 @@ class AfterlifeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { SpiritWhiteToken token = new SpiritWhiteToken(); token.putOntoBattlefield(1, game, source.getSourceId(), permanent.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/a/AgentOfTheFates.java b/Mage.Sets/src/mage/cards/a/AgentOfTheFates.java index 68fabc013f..f310246ec0 100644 --- a/Mage.Sets/src/mage/cards/a/AgentOfTheFates.java +++ b/Mage.Sets/src/mage/cards/a/AgentOfTheFates.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -29,7 +29,7 @@ public final class AgentOfTheFates extends CardImpl { // Deathtouch this.addAbility(DeathtouchAbility.getInstance()); // Heroic - Whenever you cast a spell that targets Agent of the Fates, each opponent sacrifices a creature. - this.addAbility(new HeroicAbility(new SacrificeOpponentsEffect(new FilterControlledCreaturePermanent("a creature")))); + this.addAbility(new HeroicAbility(new SacrificeOpponentsEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); } public AgentOfTheFates(final AgentOfTheFates card) { diff --git a/Mage.Sets/src/mage/cards/a/Aggression.java b/Mage.Sets/src/mage/cards/a/Aggression.java index 3f2aedd7a2..6da304a221 100644 --- a/Mage.Sets/src/mage/cards/a/Aggression.java +++ b/Mage.Sets/src/mage/cards/a/Aggression.java @@ -1,103 +1,103 @@ -package mage.cards.a; - -import java.util.UUID; -import mage.MageObjectReference; -import mage.constants.SubType; -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalTriggeredAbility; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DestroyAttachedToEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.abilities.keyword.FirstStrikeAbility; -import mage.abilities.keyword.TrampleAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.watchers.common.AttackedThisTurnWatcher; - -/** - * - * @author jeffwadsworth - */ -public final class Aggression extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(Predicates.not(SubType.WALL.getPredicate())); - } - - public Aggression(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); - - this.subtype.add(SubType.AURA); - - // Enchant non-Wall creature - TargetPermanent auraTarget = new TargetPermanent(filter); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // Enchanted creature has first strike and trample. - Ability ability2 = new SimpleStaticAbility( - Zone.BATTLEFIELD, - new GainAbilityAttachedEffect( - FirstStrikeAbility.getInstance(), - AttachmentType.AURA)); - ability2.addEffect(new GainAbilityAttachedEffect( - TrampleAbility.getInstance(), - AttachmentType.AURA)); - this.addAbility(ability2); - - // At the beginning of the end step of enchanted creature's controller, destroy that creature if it didn't attack this turn. - this.addAbility(new ConditionalTriggeredAbility( - new AtTheBeginOfNextEndStepDelayedTriggeredAbility( - new DestroyAttachedToEffect("enchanted"), - TargetController.CONTROLLER_ATTACHED_TO), - DidNotAttackedThisTurnEnchantedCondition.instance, - "At the beginning of the end step of enchanted creature's controller, destroy that creature if it didn't attack this turn."), - new AttackedThisTurnWatcher()); - - } - - private Aggression(final Aggression card) { - super(card); - } - - @Override - public Aggression copy() { - return new Aggression(this); - } -} - -enum DidNotAttackedThisTurnEnchantedCondition implements Condition { - instance; - - @Override - public boolean apply(Game game, Ability source) { - Permanent auraPermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (auraPermanent != null) { - Permanent enchantedPermanent = game.getPermanent(auraPermanent.getAttachedTo()); - AttackedThisTurnWatcher watcher = game.getState().getWatcher(AttackedThisTurnWatcher.class); - return enchantedPermanent != null - && watcher != null - && !watcher.getAttackedThisTurnCreatures().contains( - new MageObjectReference(enchantedPermanent, game)); - } - return false; - } -} +package mage.cards.a; + +import java.util.UUID; +import mage.MageObjectReference; +import mage.constants.SubType; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroyAttachedToEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.watchers.common.AttackedThisTurnWatcher; + +/** + * + * @author jeffwadsworth + */ +public final class Aggression extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(Predicates.not(SubType.WALL.getPredicate())); + } + + public Aggression(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + this.subtype.add(SubType.AURA); + + // Enchant non-Wall creature + TargetPermanent auraTarget = new TargetPermanent(filter); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has first strike and trample. + Ability ability2 = new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityAttachedEffect( + FirstStrikeAbility.getInstance(), + AttachmentType.AURA)); + ability2.addEffect(new GainAbilityAttachedEffect( + TrampleAbility.getInstance(), + AttachmentType.AURA)); + this.addAbility(ability2); + + // At the beginning of the end step of enchanted creature's controller, destroy that creature if it didn't attack this turn. + this.addAbility(new ConditionalTriggeredAbility( + new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new DestroyAttachedToEffect("enchanted"), + TargetController.CONTROLLER_ATTACHED_TO), + DidNotAttackedThisTurnEnchantedCondition.instance, + "At the beginning of the end step of enchanted creature's controller, destroy that creature if it didn't attack this turn."), + new AttackedThisTurnWatcher()); + + } + + private Aggression(final Aggression card) { + super(card); + } + + @Override + public Aggression copy() { + return new Aggression(this); + } +} + +enum DidNotAttackedThisTurnEnchantedCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent auraPermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (auraPermanent != null) { + Permanent enchantedPermanent = game.getPermanent(auraPermanent.getAttachedTo()); + AttackedThisTurnWatcher watcher = game.getState().getWatcher(AttackedThisTurnWatcher.class); + return enchantedPermanent != null + && watcher != null + && !watcher.getAttackedThisTurnCreatures().contains( + new MageObjectReference(enchantedPermanent, game)); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AggressiveInstinct.java b/Mage.Sets/src/mage/cards/a/AggressiveInstinct.java index 793bf6b499..5b7bad83ff 100644 --- a/Mage.Sets/src/mage/cards/a/AggressiveInstinct.java +++ b/Mage.Sets/src/mage/cards/a/AggressiveInstinct.java @@ -4,8 +4,7 @@ import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -16,22 +15,16 @@ import java.util.UUID; */ public final class AggressiveInstinct extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public AggressiveInstinct(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Target creature you control deals damage equal to its power to target creature you don't control. this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public AggressiveInstinct(final AggressiveInstinct card) { + private AggressiveInstinct(final AggressiveInstinct card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/a/AgitatorAnt.java b/Mage.Sets/src/mage/cards/a/AgitatorAnt.java new file mode 100644 index 0000000000..e33d7a1a23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AgitatorAnt.java @@ -0,0 +1,98 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.GoadTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class AgitatorAnt extends CardImpl { + + public AgitatorAnt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.INSECT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // At the beginning of your end step, each player may put two +1/+1 counters on a creature they control. Goad each creature that had counters put on it this way. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new AgitatorAntEffect(), TargetController.YOU, false + )); + } + + private AgitatorAnt(final AgitatorAnt card) { + super(card); + } + + @Override + public AgitatorAnt copy() { + return new AgitatorAnt(this); + } +} + +class AgitatorAntEffect extends OneShotEffect { + + AgitatorAntEffect() { + super(Outcome.Benefit); + staticText = "each player may put two +1/+1 counters on a creature they control. " + + "Goad each creature that had counters put on it this way."; + } + + private AgitatorAntEffect(final AgitatorAntEffect effect) { + super(effect); + } + + @Override + public AgitatorAntEffect copy() { + return new AgitatorAntEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Player player : game + .getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull).collect(Collectors.toList())) { + if (game.getBattlefield().countAll( + StaticFilters.FILTER_PERMANENT_CREATURE, player.getId(), game + ) < 1 || !player.chooseUse( + Outcome.BoostCreature, "Put two +1/+1 counters on a creature you control?", source, game + )) { + continue; + } + TargetPermanent targetPermanent = new TargetControlledCreaturePermanent(0, 1); + targetPermanent.setNotTarget(true); + player.choose(Outcome.BoostCreature, targetPermanent, source.getSourceId(), game); + Permanent permanent = game.getPermanent(targetPermanent.getFirstTarget()); + if (permanent == null || !permanent.addCounters(CounterType.P1P1.createInstance(2), source, game)) { + continue; + } + new GoadTargetEffect().setTargetPointer(new FixedTarget(permanent, game)).apply(game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AinokArtillerist.java b/Mage.Sets/src/mage/cards/a/AinokArtillerist.java index 589990f025..ffb5401177 100644 --- a/Mage.Sets/src/mage/cards/a/AinokArtillerist.java +++ b/Mage.Sets/src/mage/cards/a/AinokArtillerist.java @@ -23,7 +23,7 @@ public final class AinokArtillerist extends CardImpl { public AinokArtillerist(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.ARCHER); this.power = new MageInt(4); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/a/AinokBondKin.java b/Mage.Sets/src/mage/cards/a/AinokBondKin.java index ccec3c06fc..5c0aea14cf 100644 --- a/Mage.Sets/src/mage/cards/a/AinokBondKin.java +++ b/Mage.Sets/src/mage/cards/a/AinokBondKin.java @@ -33,7 +33,7 @@ public final class AinokBondKin extends CardImpl { public AinokBondKin(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.SOLDIER); this.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/a/AinokGuide.java b/Mage.Sets/src/mage/cards/a/AinokGuide.java index 2136d4f976..b59cd42b1b 100644 --- a/Mage.Sets/src/mage/cards/a/AinokGuide.java +++ b/Mage.Sets/src/mage/cards/a/AinokGuide.java @@ -25,7 +25,7 @@ public final class AinokGuide extends CardImpl { public AinokGuide(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.SCOUT); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/a/AinokSurvivalist.java b/Mage.Sets/src/mage/cards/a/AinokSurvivalist.java index 0a406fc343..aa812a835d 100644 --- a/Mage.Sets/src/mage/cards/a/AinokSurvivalist.java +++ b/Mage.Sets/src/mage/cards/a/AinokSurvivalist.java @@ -31,7 +31,7 @@ public final class AinokSurvivalist extends CardImpl { public AinokSurvivalist(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(2); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/a/AinokTracker.java b/Mage.Sets/src/mage/cards/a/AinokTracker.java index 1c3d8b1475..c4909ef07e 100644 --- a/Mage.Sets/src/mage/cards/a/AinokTracker.java +++ b/Mage.Sets/src/mage/cards/a/AinokTracker.java @@ -19,7 +19,7 @@ public final class AinokTracker extends CardImpl { public AinokTracker(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.SCOUT); this.power = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/a/AislingLeprechaun.java b/Mage.Sets/src/mage/cards/a/AislingLeprechaun.java index 94dc800975..b1bee8438d 100644 --- a/Mage.Sets/src/mage/cards/a/AislingLeprechaun.java +++ b/Mage.Sets/src/mage/cards/a/AislingLeprechaun.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BecomesColorTargetEffect; import mage.cards.CardImpl; @@ -29,7 +29,7 @@ public final class AislingLeprechaun extends CardImpl { // Whenever Aisling Leprechaun blocks or becomes blocked by a creature, that creature becomes green. Effect effect = new BecomesColorTargetEffect(ObjectColor.GREEN, Duration.EndOfGame); effect.setText("that creature becomes green"); - Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(effect, false); + Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AjaniValiantProtector.java b/Mage.Sets/src/mage/cards/a/AjaniValiantProtector.java index 146fe5af3e..f252b54b6c 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniValiantProtector.java +++ b/Mage.Sets/src/mage/cards/a/AjaniValiantProtector.java @@ -44,7 +44,7 @@ public final class AjaniValiantProtector extends CardImpl { // -11: Put X +1/+1 counters on target creature, where X is your life total. That creature gains trample until end of turn. Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(), ControllerLifeCount.instance); - effect.setText("Put X +1/+1 counters on target creature, where X is your life total."); + effect.setText("Put X +1/+1 counters on target creature, where X is your life total"); ability = new LoyaltyAbility(effect, -11); effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); effect.setText("That creature gains trample until end of turn"); diff --git a/Mage.Sets/src/mage/cards/a/AjanisPresence.java b/Mage.Sets/src/mage/cards/a/AjanisPresence.java index 0c454a615a..d3abf63694 100644 --- a/Mage.Sets/src/mage/cards/a/AjanisPresence.java +++ b/Mage.Sets/src/mage/cards/a/AjanisPresence.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -13,21 +11,21 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AjanisPresence extends CardImpl { public AjanisPresence(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); // Strive - Ajani's Presence costs {2}{W} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{W}")); - + // Any number of target creatures each get +1/+1 and gain indestructible until end of turn. - Effect effect = new BoostTargetEffect(1,1,Duration.EndOfTurn); + Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); effect.setText("Any number of target creatures each get +1/+1"); this.getSpellAbility().addEffect(effect); effect = new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn); diff --git a/Mage.Sets/src/mage/cards/a/AkimTheSoaringWind.java b/Mage.Sets/src/mage/cards/a/AkimTheSoaringWind.java new file mode 100644 index 0000000000..56c93f04c7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AkimTheSoaringWind.java @@ -0,0 +1,175 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import mage.game.permanent.token.BirdToken; +import mage.players.Player; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author AsterAether + */ +public final class AkimTheSoaringWind extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Creature tokens"); + + static { + filter.add(TokenPredicate.instance); + } + + public AkimTheSoaringWind(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever you create one or more tokens for the first time each turn, create a 1/1 white Bird creature token with flying. + this.addAbility( + new AkimTheSoaringTokenAbility() + .addHint(new ConditionHint(AkimTheSoaringWindCondition.instance, + "You create one or more tokens for the first time each turn")), + new AkimTheSoaringWindWatcher() + ); + + // {3}{U}{R}{W}: Creature tokens you control gain double strike until end of turn. + this.addAbility(new SimpleActivatedAbility( + new GainAbilityControlledEffect( + DoubleStrikeAbility.getInstance(), + Duration.EndOfTurn, + filter, + false), + new ManaCostsImpl("{3}{U}{R}{W}")) + ); + } + + private AkimTheSoaringWind(final AkimTheSoaringWind card) { + super(card); + } + + @Override + public AkimTheSoaringWind copy() { + return new AkimTheSoaringWind(this); + } +} + +enum AkimTheSoaringWindCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + AkimTheSoaringWindWatcher watcher = game.getState().getWatcher(AkimTheSoaringWindWatcher.class); + return watcher != null && watcher.firstToken(controller.getId()); + } + + @Override + public String toString() { + return "you create one or more tokens for the first time each turn"; + } +} + +class AkimTheSoaringTokenAbility extends TriggeredAbilityImpl { + + public AkimTheSoaringTokenAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new BirdToken(), 1), false); + } + + public AkimTheSoaringTokenAbility(final AkimTheSoaringTokenAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CREATED_TOKEN; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!AkimTheSoaringWindCondition.instance.apply(game, this)) { + return false; + } + + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null && permanent.isControlledBy(this.getControllerId()); + } + + @Override + public TriggeredAbility copy() { + return new AkimTheSoaringTokenAbility(this); + } + + @Override + public String getRule() { + return "Whenever you create one or more tokens for the first time each turn, " + + "create a 1/1 white Bird creature token with flying."; + } +} + +class AkimTheSoaringWindWatcher extends Watcher { + + public enum TokenState { + NoToken, + FirstToken, + MoreThanOneToken + } + + private final Map playerIds = new HashMap<>(); + + AkimTheSoaringWindWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.CREATED_TOKEN) { + return; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent instanceof PermanentToken) { + if (!playerIds.containsKey(permanent.getControllerId())) { + playerIds.put(permanent.getControllerId(), TokenState.FirstToken); + } else { + playerIds.put(permanent.getControllerId(), TokenState.MoreThanOneToken); + } + } + } + + @Override + public void reset() { + playerIds.clear(); + } + + boolean firstToken(UUID playerId) { + return playerIds.getOrDefault(playerId, TokenState.NoToken) == TokenState.FirstToken; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AkiriLineSlinger.java b/Mage.Sets/src/mage/cards/a/AkiriLineSlinger.java index 5cd997b7e0..507e6796b0 100644 --- a/Mage.Sets/src/mage/cards/a/AkiriLineSlinger.java +++ b/Mage.Sets/src/mage/cards/a/AkiriLineSlinger.java @@ -1,36 +1,29 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.PartnerAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; /** - * * @author spjspj */ public final class AkiriLineSlinger extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifact you control"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } - public AkiriLineSlinger(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}"); - + addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KOR); this.subtype.add(SubType.SOLDIER); @@ -45,9 +38,10 @@ public final class AkiriLineSlinger extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Akiri, Line-Slinger gets +1/+0 for each artifact you control. - Effect effect = new BoostSourceEffect(new PermanentsOnBattlefieldCount(filter), StaticValue.get(0), Duration.WhileOnBattlefield); + Effect effect = new BoostSourceEffect(ArtifactYouControlCount.instance, StaticValue.get(0), Duration.WhileOnBattlefield); effect.setText("{this} gets +1/+0 for each artifact you control"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect) + .addHint(ArtifactYouControlHint.instance)); // Partner this.addAbility(PartnerAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/a/AkkiBlizzardHerder.java b/Mage.Sets/src/mage/cards/a/AkkiBlizzardHerder.java index 41eb809c1d..03a3061252 100644 --- a/Mage.Sets/src/mage/cards/a/AkkiBlizzardHerder.java +++ b/Mage.Sets/src/mage/cards/a/AkkiBlizzardHerder.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -29,7 +29,7 @@ public final class AkkiBlizzardHerder extends CardImpl { this.toughness = new MageInt(1); // When Akki Blizzard-Herder dies, each player sacrifices a land. - this.addAbility(new DiesTriggeredAbility(new SacrificeAllEffect(filter))); + this.addAbility(new DiesSourceTriggeredAbility(new SacrificeAllEffect(filter))); } public AkkiBlizzardHerder(final AkkiBlizzardHerder card) { diff --git a/Mage.Sets/src/mage/cards/a/AkoumFlameseeker.java b/Mage.Sets/src/mage/cards/a/AkoumFlameseeker.java index 4a671ca6c9..c8cf5009c3 100644 --- a/Mage.Sets/src/mage/cards/a/AkoumFlameseeker.java +++ b/Mage.Sets/src/mage/cards/a/AkoumFlameseeker.java @@ -80,7 +80,7 @@ class AkoumFlameseekerEffect extends OneShotEffect { if (controller != null) { Cards cards = controller.discard(1, false, source, game); if (!cards.isEmpty()) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/a/AkoumHellkite.java b/Mage.Sets/src/mage/cards/a/AkoumHellkite.java index 991fae1edd..09d4da7474 100644 --- a/Mage.Sets/src/mage/cards/a/AkoumHellkite.java +++ b/Mage.Sets/src/mage/cards/a/AkoumHellkite.java @@ -115,7 +115,7 @@ class AkoumHellkiteDamageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent land = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent land = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Player player = game.getPlayer(source.getFirstTarget()); if (land != null && player != null) { if (land.hasSubtype(SubType.MOUNTAIN, game)) { diff --git a/Mage.Sets/src/mage/cards/a/AkroanMastiff.java b/Mage.Sets/src/mage/cards/a/AkroanMastiff.java index f1e385cde9..7b61b400b2 100644 --- a/Mage.Sets/src/mage/cards/a/AkroanMastiff.java +++ b/Mage.Sets/src/mage/cards/a/AkroanMastiff.java @@ -23,7 +23,7 @@ public final class AkroanMastiff extends CardImpl { public AkroanMastiff(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/a/AkromaAngelOfFury.java b/Mage.Sets/src/mage/cards/a/AkromaAngelOfFury.java index 6e65dda3ce..42aaaa5096 100644 --- a/Mage.Sets/src/mage/cards/a/AkromaAngelOfFury.java +++ b/Mage.Sets/src/mage/cards/a/AkromaAngelOfFury.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -31,7 +31,7 @@ public final class AkromaAngelOfFury extends CardImpl { this.toughness = new MageInt(6); // Akroma, Angel of Fury can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Flying this.addAbility(FlyingAbility.getInstance()); // Trample diff --git a/Mage.Sets/src/mage/cards/a/AlabasterDragon.java b/Mage.Sets/src/mage/cards/a/AlabasterDragon.java index 49843f304a..62fbda2d27 100644 --- a/Mage.Sets/src/mage/cards/a/AlabasterDragon.java +++ b/Mage.Sets/src/mage/cards/a/AlabasterDragon.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class AlabasterDragon extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Alabaster Dragon dies, shuffle it into its owner's library. - this.addAbility(new DiesTriggeredAbility(new ShuffleIntoLibrarySourceEffect())); } + this.addAbility(new DiesSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect())); } public AlabasterDragon(final AlabasterDragon card) { super(card); diff --git a/Mage.Sets/src/mage/cards/a/AlabasterLeech.java b/Mage.Sets/src/mage/cards/a/AlabasterLeech.java index 63e2ad4d6a..cfdf0d916e 100644 --- a/Mage.Sets/src/mage/cards/a/AlabasterLeech.java +++ b/Mage.Sets/src/mage/cards/a/AlabasterLeech.java @@ -1,22 +1,22 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author LoneFox */ public final class AlabasterLeech extends CardImpl { @@ -28,14 +28,14 @@ public final class AlabasterLeech extends CardImpl { } public AlabasterLeech(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); this.subtype.add(SubType.LEECH); this.power = new MageInt(1); this.toughness = new MageInt(3); // White spells you cast cost {W} more to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{W}")))); + new SpellsCostIncreasingAllEffect(new ManaCostsImpl("{W}"), filter, TargetController.YOU))); } public AlabasterLeech(final AlabasterLeech card) { diff --git a/Mage.Sets/src/mage/cards/a/Aladdin.java b/Mage.Sets/src/mage/cards/a/Aladdin.java index 7456422621..58b44e3ae8 100644 --- a/Mage.Sets/src/mage/cards/a/Aladdin.java +++ b/Mage.Sets/src/mage/cards/a/Aladdin.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -17,6 +16,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; import mage.target.common.TargetArtifactPermanent; +import mage.watchers.common.LostControlWatcher; /** * @@ -25,7 +25,7 @@ import mage.target.common.TargetArtifactPermanent; public final class Aladdin extends CardImpl { public Aladdin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ROGUE); this.power = new MageInt(1); @@ -39,6 +39,7 @@ public final class Aladdin extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{1}{R}{R}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetArtifactPermanent()); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AladdinsLamp.java b/Mage.Sets/src/mage/cards/a/AladdinsLamp.java index 6db19c46d5..5fb6e9b43c 100644 --- a/Mage.Sets/src/mage/cards/a/AladdinsLamp.java +++ b/Mage.Sets/src/mage/cards/a/AladdinsLamp.java @@ -74,8 +74,8 @@ class AladdinsLampEffect extends ReplacementEffectImpl { cards.remove(target.getFirstTarget()); } controller.putCardsOnBottomOfLibrary(cards, game, source, false); - game.applyEffects(); - controller.drawCards(1, game, event.getAppliedEffects()); + game.getState().processAction(game); + controller.drawCards(1, event.getSourceId(), game, event.getAppliedEffects()); discard(); return true; } diff --git a/Mage.Sets/src/mage/cards/a/AlchemistsGift.java b/Mage.Sets/src/mage/cards/a/AlchemistsGift.java new file mode 100644 index 0000000000..0c3ee91bcf --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlchemistsGift.java @@ -0,0 +1,73 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AlchemistsGift extends CardImpl { + + public AlchemistsGift(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Target creature gets +1/+1 and gains your choice of deathtouch or lifelink until end of turn. + this.getSpellAbility().addEffect(new AlchemistsGiftEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private AlchemistsGift(final AlchemistsGift card) { + super(card); + } + + @Override + public AlchemistsGift copy() { + return new AlchemistsGift(this); + } +} + +class AlchemistsGiftEffect extends OneShotEffect { + + AlchemistsGiftEffect() { + super(Outcome.Benefit); + staticText = "Target creature gets +1/+1 and gains your choice of deathtouch or lifelink until end of turn."; + } + + private AlchemistsGiftEffect(final AlchemistsGiftEffect effect) { + super(effect); + } + + @Override + public AlchemistsGiftEffect copy() { + return new AlchemistsGiftEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Ability ability = player.chooseUse( + outcome, "Deathtouch or lifelink?", null, + "Deathtouch", "Lifelink", source, game + ) ? DeathtouchAbility.getInstance() : LifelinkAbility.getInstance(); + game.addEffect(new BoostTargetEffect(1, 1, Duration.EndOfTurn), source); + game.addEffect(new GainAbilityTargetEffect(ability, Duration.EndOfTurn), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AlertHeedbonder.java b/Mage.Sets/src/mage/cards/a/AlertHeedbonder.java new file mode 100644 index 0000000000..8e77c371bd --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlertHeedbonder.java @@ -0,0 +1,59 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AlertHeedbonder extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("creature you control with vigilance"); + + static { + filter.add(new AbilityPredicate(VigilanceAbility.class)); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + + public AlertHeedbonder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G/W}{G/W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // At the beginning of your end step, you gain 1 life for each creature you control with vigilance. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new GainLifeEffect(xValue), TargetController.YOU, false + )); + } + + private AlertHeedbonder(final AlertHeedbonder card) { + super(card); + } + + @Override + public AlertHeedbonder copy() { + return new AlertHeedbonder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java b/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java index 3d9f281655..05df483327 100644 --- a/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java +++ b/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java @@ -7,6 +7,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -35,7 +36,9 @@ public final class AlexiZephyrMage extends CardImpl { this.toughness = new MageInt(3); // {X}{U}, {tap}, Discard two cards: Return X target creatures to their owners' hands. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{X}{U}")); + Effect effect = new ReturnToHandTargetEffect(); + effect.setText("Return X target creatures to their owner's hands"); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{X}{U}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardTargetCost(new TargetCardInHand(2, new FilterCard("two cards")))); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_CREATURES)); diff --git a/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java b/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java index 7934c8b858..b410035bf3 100644 --- a/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java +++ b/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; @@ -13,14 +11,15 @@ import mage.game.events.GameEvent; import mage.players.Player; import mage.watchers.common.CardsDrawnDuringDrawStepWatcher; +import java.util.UUID; + /** - * * @author fireshoes */ public final class AlhammarretsArchive extends CardImpl { public AlhammarretsArchive(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); addSuperType(SuperType.LEGENDARY); // If you would gain life, you gain twice that much life instead. @@ -30,7 +29,7 @@ public final class AlhammarretsArchive extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AlhammarretsArchiveReplacementEffect()), new CardsDrawnDuringDrawStepWatcher()); } - public AlhammarretsArchive(final AlhammarretsArchive card) { + private AlhammarretsArchive(final AlhammarretsArchive card) { super(card); } @@ -42,12 +41,12 @@ public final class AlhammarretsArchive extends CardImpl { class AlhammarretsArchiveEffect extends ReplacementEffectImpl { - public AlhammarretsArchiveEffect() { + AlhammarretsArchiveEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); staticText = "If you would gain life, you gain twice that much life instead"; } - public AlhammarretsArchiveEffect(final AlhammarretsArchiveEffect effect) { + private AlhammarretsArchiveEffect(final AlhammarretsArchiveEffect effect) { super(effect); } @@ -75,12 +74,12 @@ class AlhammarretsArchiveEffect extends ReplacementEffectImpl { class AlhammarretsArchiveReplacementEffect extends ReplacementEffectImpl { - public AlhammarretsArchiveReplacementEffect() { + AlhammarretsArchiveReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Neutral); staticText = "If you draw a card except the first one you draw in each of your draw steps, draw two cards instead"; } - public AlhammarretsArchiveReplacementEffect(final AlhammarretsArchiveReplacementEffect effect) { + private AlhammarretsArchiveReplacementEffect(final AlhammarretsArchiveReplacementEffect effect) { super(effect); } @@ -98,7 +97,7 @@ class AlhammarretsArchiveReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(2, game, event.getAppliedEffects()); + controller.drawCards(2, event.getSourceId(), game, event.getAppliedEffects()); } return true; } @@ -110,17 +109,14 @@ class AlhammarretsArchiveReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getPlayerId().equals(source.getControllerId())) { - if (game.isActivePlayer(event.getPlayerId()) - && game.getPhase().getStep().getType() == PhaseStep.DRAW) { - CardsDrawnDuringDrawStepWatcher watcher = game.getState().getWatcher(CardsDrawnDuringDrawStepWatcher.class); - if (watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) > 0) { - return true; - } - } else { - return true; - } + if (!event.getPlayerId().equals(source.getControllerId())) { + return false; } - return false; + if (!game.isActivePlayer(event.getPlayerId()) + || game.getPhase().getStep().getType() != PhaseStep.DRAW) { + return true; + } + CardsDrawnDuringDrawStepWatcher watcher = game.getState().getWatcher(CardsDrawnDuringDrawStepWatcher.class); + return watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) > 0; } } diff --git a/Mage.Sets/src/mage/cards/a/AlleyGrifters.java b/Mage.Sets/src/mage/cards/a/AlleyGrifters.java index 88483704b9..bb0bbe0f54 100644 --- a/Mage.Sets/src/mage/cards/a/AlleyGrifters.java +++ b/Mage.Sets/src/mage/cards/a/AlleyGrifters.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -29,7 +29,7 @@ public final class AlleyGrifters extends CardImpl { this.toughness = new MageInt(2); // Whenever Alley Grifters becomes blocked, defending player discards a card. - this.addAbility(new BecomesBlockedByCreatureTriggeredAbility(new AlleyGriftersDiscardEffect(), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new AlleyGriftersDiscardEffect(), false)); } public AlleyGrifters(final AlleyGrifters card) { diff --git a/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java b/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java new file mode 100644 index 0000000000..dc32fd4fd6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java @@ -0,0 +1,69 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.CantBeCounteredSourceAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CantBeCounteredControlledEffect; +import mage.abilities.effects.common.continuous.CreaturesBecomeOtherTypeEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; + +import java.util.UUID; +import mage.filter.FilterSpell; + +public class AllosaurusShepherd extends CardImpl { + + private static final FilterSpell greenSpellsFilter = new FilterSpell("Green spells you control"); + private static final FilterPermanent elvesFilter = new FilterControlledCreaturePermanent("each Elf creature you control"); + + static { + greenSpellsFilter.add(new ColorPredicate(ObjectColor.GREEN)); + elvesFilter.add(SubType.ELF.getPredicate()); + } + + public AllosaurusShepherd(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.SHAMAN); + + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + //Allosaurus Shepherd can't be countered. + this.addAbility(new CantBeCounteredSourceAbility()); + + //Green spells you control can't be countered. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new CantBeCounteredControlledEffect(greenSpellsFilter, null, Duration.WhileOnBattlefield))); + + //4GG: Until end of turn, each Elf creature you control has base power and toughness 5/5 + // and becomes a Dinosaur in addition to its other creature types. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new SetPowerToughnessAllEffect(5, 5, Duration.EndOfTurn, elvesFilter, true) + .setText("Until end of turn, each Elf creature you control has base power and toughness 5/5"), + new ManaCostsImpl("{4}{G}{G}")); + ability.addEffect(new CreaturesBecomeOtherTypeEffect(elvesFilter, SubType.DINOSAUR, Duration.EndOfTurn) + .setText("and becomes a Dinosaur in addition to its other creature types")); + this.addAbility(ability); + + } + + public AllosaurusShepherd(final AllosaurusShepherd card) { + super(card); + } + + @Override + public AllosaurusShepherd copy() { + return new AllosaurusShepherd(this); + } + +} diff --git a/Mage.Sets/src/mage/cards/a/AlmightyBrushwagg.java b/Mage.Sets/src/mage/cards/a/AlmightyBrushwagg.java new file mode 100644 index 0000000000..a3b015d8db --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlmightyBrushwagg.java @@ -0,0 +1,45 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AlmightyBrushwagg extends CardImpl { + + public AlmightyBrushwagg(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); + + this.subtype.add(SubType.BRUSHWAGG); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // {3}{G}: Almighty Brushwagg gets +3/+3 until end of turn. + this.addAbility(new SimpleActivatedAbility( + new BoostSourceEffect(3, 3, Duration.EndOfTurn), new ManaCostsImpl("{3}{G}") + )); + } + + private AlmightyBrushwagg(final AlmightyBrushwagg card) { + super(card); + } + + @Override + public AlmightyBrushwagg copy() { + return new AlmightyBrushwagg(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AlmsCollector.java b/Mage.Sets/src/mage/cards/a/AlmsCollector.java index 9750fc5b29..7413e54927 100644 --- a/Mage.Sets/src/mage/cards/a/AlmsCollector.java +++ b/Mage.Sets/src/mage/cards/a/AlmsCollector.java @@ -74,8 +74,8 @@ class AlmsCollectorReplacementEffect extends ReplacementEffectImpl { Player controller = game.getPlayer(source.getControllerId()); Player opponent = game.getPlayer(event.getPlayerId()); if (controller != null && opponent != null) { - controller.drawCards(1, game, event.getAppliedEffects()); - opponent.drawCards(1, game, event.getAppliedEffects()); + controller.drawCards(1, source.getSourceId(), game, event.getAppliedEffects()); + opponent.drawCards(1, source.getSourceId(), game, event.getAppliedEffects()); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/a/AlmsOfTheVein.java b/Mage.Sets/src/mage/cards/a/AlmsOfTheVein.java index bca4bcfbdb..6b337a6bf0 100644 --- a/Mage.Sets/src/mage/cards/a/AlmsOfTheVein.java +++ b/Mage.Sets/src/mage/cards/a/AlmsOfTheVein.java @@ -1,9 +1,6 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.keyword.MadnessAbility; @@ -12,23 +9,20 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class AlmsOfTheVein extends CardImpl { public AlmsOfTheVein(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Target opponent loses 3 life and you gain 3 life. - Effect effect = new LoseLifeTargetEffect(3); - effect.setText("Target opponent loses 3 life"); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new LoseLifeTargetEffect(3)); + this.getSpellAbility().addEffect(new GainLifeEffect(3).concatBy("and")); this.getSpellAbility().addTarget(new TargetOpponent()); - effect = new GainLifeEffect(3); - effect.setText("and you gain 3 life"); - this.getSpellAbility().addEffect(effect); // Madness {B} this.addAbility(new MadnessAbility(this, new ManaCostsImpl("{B}"))); diff --git a/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java b/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java new file mode 100644 index 0000000000..5bf4ad432d --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java @@ -0,0 +1,140 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AlpineHoundmaster extends CardImpl { + + private static final FilterPermanent filter = new FilterAttackingCreature("the number of other attacking creatures"); + + static { + filter.add(AnotherPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null); + + public AlpineHoundmaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Alpine Houndmaster enters the battlefield, you may search your library for a card named Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library. + this.addAbility(new EntersBattlefieldTriggeredAbility(new AlpineHoundmasterEffect(), true)); + + // Whenever Alpine Houndmaster attacks, it gets +X/+0 until end of turn, where X is the number of other attacking creatures. + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect( + xValue, StaticValue.get(0), Duration.EndOfTurn, true + ), false)); + } + + private AlpineHoundmaster(final AlpineHoundmaster card) { + super(card); + } + + @Override + public AlpineHoundmaster copy() { + return new AlpineHoundmaster(this); + } +} + +class AlpineHoundmasterEffect extends OneShotEffect { + + AlpineHoundmasterEffect() { + super(Outcome.Benefit); + staticText = "search your library for a card named Alpine Watchdog and/or a card named Igneous Cur, " + + "reveal them, put them into your hand, then shuffle your library"; + } + + private AlpineHoundmasterEffect(final AlpineHoundmasterEffect effect) { + super(effect); + } + + @Override + public AlpineHoundmasterEffect copy() { + return new AlpineHoundmasterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCardInLibrary target = new AlpineHoundmasterTarget(); + player.searchLibrary(target, source, game); + Cards cards = new CardsImpl(target.getTargets()); + player.revealCards(source, cards, game); + player.moveCards(cards, Zone.HAND, source, game); + player.shuffleLibrary(source, game); + return true; + } +} + +class AlpineHoundmasterTarget extends TargetCardInLibrary { + + private static final FilterCard filter + = new FilterCard("card named Alpine Watchdog and/or a card named Igneous Cur"); + + static { + filter.add(Predicates.or( + new NamePredicate("Alpine Watchdog"), + new NamePredicate("Igneous Cur") + )); + } + + AlpineHoundmasterTarget() { + super(0, 2, filter); + } + + private AlpineHoundmasterTarget(final AlpineHoundmasterTarget target) { + super(target); + } + + @Override + public AlpineHoundmasterTarget copy() { + return new AlpineHoundmasterTarget(this); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + if (!super.canTarget(controllerId, id, source, game)) { + return false; + } + Card card = game.getCard(id); + if (card == null) { + return false; + } + return this.getTargets() + .stream() + .map(game::getCard) + .map(MageObject::getName) + .noneMatch(card.getName()::equals); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AlpineWatchdog.java b/Mage.Sets/src/mage/cards/a/AlpineWatchdog.java new file mode 100644 index 0000000000..65c6d9bdb4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlpineWatchdog.java @@ -0,0 +1,36 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AlpineWatchdog extends CardImpl { + + public AlpineWatchdog(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.DOG); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + } + + private AlpineWatchdog(final AlpineWatchdog card) { + super(card); + } + + @Override + public AlpineWatchdog copy() { + return new AlpineWatchdog(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AltarOfDementia.java b/Mage.Sets/src/mage/cards/a/AltarOfDementia.java index 0413e9b960..339134c9b4 100644 --- a/Mage.Sets/src/mage/cards/a/AltarOfDementia.java +++ b/Mage.Sets/src/mage/cards/a/AltarOfDementia.java @@ -48,7 +48,7 @@ class AltarOfDementiaEffect extends OneShotEffect { public AltarOfDementiaEffect() { super(Outcome.Damage); - staticText = "Target player puts a number of cards equal to the sacrificed creature's power from the top of their library into their graveyard"; + staticText = "Target player mills cards equal to the sacrificed creature's power"; } public AltarOfDementiaEffect(final AltarOfDementiaEffect effect) { @@ -67,7 +67,7 @@ class AltarOfDementiaEffect extends OneShotEffect { } } if (amount > 0) { - player.moveCards(player.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game); + player.millCards(amount, source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/a/AltarOfTheBrood.java b/Mage.Sets/src/mage/cards/a/AltarOfTheBrood.java index 9512286798..55810ac8b1 100644 --- a/Mage.Sets/src/mage/cards/a/AltarOfTheBrood.java +++ b/Mage.Sets/src/mage/cards/a/AltarOfTheBrood.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -25,9 +24,9 @@ public final class AltarOfTheBrood extends CardImpl { } public AltarOfTheBrood(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); - // Whenever another permanent enters the battlefield under your control, each opponent puts the top card of their library into their graveyard. + // Whenever another permanent enters the battlefield under your control, each opponent mills a card. this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new PutTopCardOfLibraryIntoGraveEachPlayerEffect(1, TargetController.OPPONENT), filter, false, null, true)); } diff --git a/Mage.Sets/src/mage/cards/a/AltarsReap.java b/Mage.Sets/src/mage/cards/a/AltarsReap.java index c98881036c..67878e923f 100644 --- a/Mage.Sets/src/mage/cards/a/AltarsReap.java +++ b/Mage.Sets/src/mage/cards/a/AltarsReap.java @@ -7,7 +7,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -21,7 +21,7 @@ public final class AltarsReap extends CardImpl { // As an additional cost to cast Altar's Reap, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1, new FilterControlledCreaturePermanent("a creature"), true))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, true))); // Draw two cards. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); } diff --git a/Mage.Sets/src/mage/cards/a/AlteredEgo.java b/Mage.Sets/src/mage/cards/a/AlteredEgo.java index ae603d0566..3cb307ca5b 100644 --- a/Mage.Sets/src/mage/cards/a/AlteredEgo.java +++ b/Mage.Sets/src/mage/cards/a/AlteredEgo.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CopyPermanentEffect; @@ -32,7 +32,7 @@ public final class AlteredEgo extends CardImpl { this.toughness = new MageInt(0); // Altered Ego can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // You may have Altered Ego enter the battlefield as a copy of any creature on the battlefield, except it enters with an additional X +1/+1 counters on it. Effect effect = new CopyPermanentEffect(StaticFilters.FILTER_PERMANENT_CREATURE, null); diff --git a/Mage.Sets/src/mage/cards/a/AmassTheComponents.java b/Mage.Sets/src/mage/cards/a/AmassTheComponents.java index 359cd2fe90..c3cdc37ece 100644 --- a/Mage.Sets/src/mage/cards/a/AmassTheComponents.java +++ b/Mage.Sets/src/mage/cards/a/AmassTheComponents.java @@ -63,7 +63,7 @@ class AmassTheComponentsEffect extends OneShotEffect { return false; } - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); if (!player.getHand().isEmpty()) { FilterCard filter = new FilterCard("card from your hand to put on the bottom of your library"); TargetCard target = new TargetCard(Zone.HAND, filter); @@ -71,8 +71,7 @@ class AmassTheComponentsEffect extends OneShotEffect { if (player.choose(Outcome.Detriment, player.getHand(), target, game)) { Card card = player.getHand().get(target.getFirstTarget(), game); if (card != null) { - player.removeFromHand(card, game); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); + return player.putCardsOnBottomOfLibrary(card, game, source, true); } } } diff --git a/Mage.Sets/src/mage/cards/a/Ambuscade.java b/Mage.Sets/src/mage/cards/a/Ambuscade.java index fe76f0616b..28ab3c6e17 100644 --- a/Mage.Sets/src/mage/cards/a/Ambuscade.java +++ b/Mage.Sets/src/mage/cards/a/Ambuscade.java @@ -7,8 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -19,12 +18,6 @@ import java.util.UUID; */ public final class Ambuscade extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public Ambuscade(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); @@ -35,10 +28,10 @@ public final class Ambuscade extends CardImpl { // It deals damage equal to its power to target creature you don't control. this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("It")); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); // second target for effect + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); // second target for effect } - public Ambuscade(final Ambuscade card) { + private Ambuscade(final Ambuscade card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java b/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java index 2c8e04683a..65390caee2 100644 --- a/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java +++ b/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java @@ -1,25 +1,20 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.DashAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class AmbuscadeShaman extends CardImpl { @@ -32,16 +27,18 @@ public final class AmbuscadeShaman extends CardImpl { this.toughness = new MageInt(2); // Whenever Ambuscade Shaman or another creature enters the battlefield under your control, that creature gets +2/+2 until end of turn. - Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); - effect.setText("that creature gets +2/+2 until end of turn"); - this.addAbility(new AmbuscadeShamanTriggeredAbility(effect)); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new BoostTargetEffect(2, 2, Duration.EndOfTurn) + .setText("that creature gets +2/+2 until end of turn"), + StaticFilters.FILTER_PERMANENT_CREATURE, false, + SetTargetPointer.PERMANENT, true + )); // Dash {3}{B} (You may cast this spell for its dash cost. If you do, it gains haste, and it's returned from the battlefield to its owner's hand at the beginning of the next end step.)); this.addAbility(new DashAbility(this, "{3}{B}")); - } - public AmbuscadeShaman(final AmbuscadeShaman card) { + private AmbuscadeShaman(final AmbuscadeShaman card) { super(card); } @@ -50,41 +47,3 @@ public final class AmbuscadeShaman extends CardImpl { return new AmbuscadeShaman(this); } } - -class AmbuscadeShamanTriggeredAbility extends TriggeredAbilityImpl { - - AmbuscadeShamanTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, false); - } - - AmbuscadeShamanTriggeredAbility(final AmbuscadeShamanTriggeredAbility ability) { - super(ability); - } - - @Override - public AmbuscadeShamanTriggeredAbility copy() { - return new AmbuscadeShamanTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - UUID targetId = event.getTargetId(); - Permanent permanent = game.getPermanent(targetId); - if (permanent.isControlledBy(this.controllerId) - && permanent.isCreature()) { - this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} or another creature enters the battlefield under your control, that creature gets +2/+2 until end of turn."; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java b/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java index c2226fb5eb..d2f3040e85 100644 --- a/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java +++ b/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java @@ -98,7 +98,7 @@ class AminatouPlusEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); putOnLibrary(player, source, game); return true; } @@ -172,7 +172,7 @@ class AminatouUltimateEffect extends OneShotEffect { continue; } ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, currentPlayer); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } currentPlayer = nextPlayer; diff --git a/Mage.Sets/src/mage/cards/a/AminatousAugury.java b/Mage.Sets/src/mage/cards/a/AminatousAugury.java index 9e230a447c..2c46320553 100644 --- a/Mage.Sets/src/mage/cards/a/AminatousAugury.java +++ b/Mage.Sets/src/mage/cards/a/AminatousAugury.java @@ -104,7 +104,7 @@ class AminatousAuguryEffect extends OneShotEffect { } for (Card card : cardsToCast.getCards(StaticFilters.FILTER_CARD_NON_LAND, game)) { AminatousAuguryCastFromExileEffect effect = new AminatousAuguryCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); } } @@ -175,7 +175,8 @@ class AminatousAuguryCastFromExileEffect extends AsThoughEffectImpl { usedCardTypes.add(CardType.fromString(choice.getChoice())); } usedCardTypes.addAll(unusedCardTypes); - player.setCastSourceIdWithAlternateMana(sourceId, null, null); + player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts()); + // TODO- This does not correctly work when you cancel the cast (has to be done by watcher I guess) game.getState().setValue(source.getSourceId().toString() + "cardTypes", usedCardTypes); } return true; diff --git a/Mage.Sets/src/mage/cards/a/Amnesia.java b/Mage.Sets/src/mage/cards/a/Amnesia.java index ae4eebaa75..b452e933bc 100644 --- a/Mage.Sets/src/mage/cards/a/Amnesia.java +++ b/Mage.Sets/src/mage/cards/a/Amnesia.java @@ -1,28 +1,25 @@ - package mage.cards.a; -import java.util.Set; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.Set; +import java.util.UUID; + /** - * * @author fireshoes */ public final class Amnesia extends CardImpl { public Amnesia(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}{U}{U}"); // Target player reveals their hand and discards all nonland cards. this.getSpellAbility().addEffect(new AmnesiaEffect()); @@ -60,13 +57,10 @@ class AmnesiaEffect extends OneShotEffect { Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { Cards hand = player.getHand(); - player.revealCards("Amnesia", hand, game); + player.revealCards(source, hand, game); Set cards = hand.getCards(game); - for (Card card : cards) { - if (card != null && !card.isLand()) { - player.discard(card, source, game); - } - } + cards.removeIf(MageObject::isLand); + player.discard(new CardsImpl(cards), source, game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/a/AnaBattlemage.java b/Mage.Sets/src/mage/cards/a/AnaBattlemage.java index 38d6c7af25..bed405a197 100644 --- a/Mage.Sets/src/mage/cards/a/AnaBattlemage.java +++ b/Mage.Sets/src/mage/cards/a/AnaBattlemage.java @@ -20,8 +20,8 @@ import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.target.TargetPlayer; import mage.target.common.TargetCreaturePermanent; -import mage.target.common.TargetOpponent; import java.util.UUID; @@ -49,7 +49,7 @@ public final class AnaBattlemage extends CardImpl { this.addAbility(kickerAbility); // When Ana Battlemage enters the battlefield, if it was kicked with its {2}{U} kicker, target player discards three cards. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DiscardTargetEffect(3)); - ability.addTarget(new TargetOpponent()); + ability.addTarget(new TargetPlayer()); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new KickedCostCondition("{2}{U}"), "When {this} enters the battlefield, if it was kicked with its {2}{U} kicker, target player discards three cards.")); // When Ana Battlemage enters the battlefield, if it was kicked with its {1}{B} kicker, tap target untapped creature and that creature deals damage equal to its power to its controller. diff --git a/Mage.Sets/src/mage/cards/a/AnakinSkywalker.java b/Mage.Sets/src/mage/cards/a/AnakinSkywalker.java index cc479f50a4..86d85aab36 100644 --- a/Mage.Sets/src/mage/cards/a/AnakinSkywalker.java +++ b/Mage.Sets/src/mage/cards/a/AnakinSkywalker.java @@ -1,8 +1,5 @@ - package mage.cards.a; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; @@ -26,6 +23,8 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author Styxo */ @@ -82,7 +81,7 @@ class AnakinSkywalkerEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null) { - permanent.regenerate(source.getSourceId(), game); + permanent.regenerate(source, game); return new TransformSourceEffect(true).apply(game, source); } return false; diff --git a/Mage.Sets/src/mage/cards/a/AnaxHardenedInTheForge.java b/Mage.Sets/src/mage/cards/a/AnaxHardenedInTheForge.java index 2412c0a40f..b55489b2c7 100644 --- a/Mage.Sets/src/mage/cards/a/AnaxHardenedInTheForge.java +++ b/Mage.Sets/src/mage/cards/a/AnaxHardenedInTheForge.java @@ -1,7 +1,6 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.DevotionCount; import mage.abilities.effects.common.CreateTokenEffect; @@ -12,17 +11,27 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.PermanentToken; import mage.game.permanent.token.SatyrCantBlockToken; -import java.util.Objects; import java.util.UUID; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TokenPredicate; /** * @author TheElk801 */ public final class AnaxHardenedInTheForge extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(Predicates.not(TokenPredicate.instance)); + } + public AnaxHardenedInTheForge(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{R}{R}"); @@ -38,8 +47,9 @@ public final class AnaxHardenedInTheForge extends CardImpl { .setText("{this}'s power is equal to your devotion to red") ).addHint(DevotionCount.R.getHint())); - // Whenever Anax or another nontoken creature you control dies, create a 1/1 red Satyr creature token with "This creature can't block." If the creature had power 4 or greater, create two of those tokens instead. - this.addAbility(new AnaxHardenedInTheForgeTriggeredAbility()); + // Whenever Anax or another nontoken creature you control dies, create a 1/1 red Satyr creature token + // with "This creature can't block." If the creature had power 4 or greater, create two of those tokens instead. + this.addAbility(new AnaxHardenedInTheForgeTriggeredAbility(null, false, filter)); } private AnaxHardenedInTheForge(final AnaxHardenedInTheForge card) { @@ -52,10 +62,10 @@ public final class AnaxHardenedInTheForge extends CardImpl { } } -class AnaxHardenedInTheForgeTriggeredAbility extends TriggeredAbilityImpl { +class AnaxHardenedInTheForgeTriggeredAbility extends DiesThisOrAnotherCreatureTriggeredAbility { - AnaxHardenedInTheForgeTriggeredAbility() { - super(Zone.BATTLEFIELD, null, false); + AnaxHardenedInTheForgeTriggeredAbility(Effect effect, boolean optional, FilterCreaturePermanent filter) { + super(effect, optional, filter); } private AnaxHardenedInTheForgeTriggeredAbility(final AnaxHardenedInTheForgeTriggeredAbility ability) { @@ -67,33 +77,21 @@ class AnaxHardenedInTheForgeTriggeredAbility extends TriggeredAbilityImpl { return new AnaxHardenedInTheForgeTriggeredAbility(this); } - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - @Override public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (!zEvent.isDiesEvent()) { - return false; + if (super.checkTrigger(event, game)) { + int tokenCount = ((ZoneChangeEvent) event).getTarget().getPower().getValue() > 3 ? 2 : 1; + this.getEffects().clear(); + this.addEffect(new CreateTokenEffect(new SatyrCantBlockToken(), tokenCount)); + return true; } - if (!zEvent.getTarget().getId().equals(getSourceId()) - && (zEvent.getTarget() instanceof PermanentToken - || !zEvent.getTarget().isCreature() - || !Objects.equals(zEvent.getTarget().getControllerId(), getControllerId()))) { - return false; - } - int tokenCount = zEvent.getTarget().getPower().getValue() > 3 ? 2 : 1; - this.getEffects().clear(); - this.addEffect(new CreateTokenEffect(new SatyrCantBlockToken(), tokenCount)); - return true; + return false; } @Override public String getRule() { - return "Whenever {this} or another nontoken creature you control dies, " + - "create a 1/1 red Satyr creature token with \"This creature can't block.\" " + - "If the creature had power 4 or greater, create two of those tokens instead."; + return "Whenever {this} or another nontoken creature you control dies, " + + "create a 1/1 red Satyr creature token with \"This creature can't block.\" " + + "If the creature had power 4 or greater, create two of those tokens instead."; } } diff --git a/Mage.Sets/src/mage/cards/a/AncientAnimus.java b/Mage.Sets/src/mage/cards/a/AncientAnimus.java index 3695248b28..1e17063646 100644 --- a/Mage.Sets/src/mage/cards/a/AncientAnimus.java +++ b/Mage.Sets/src/mage/cards/a/AncientAnimus.java @@ -30,7 +30,7 @@ public final class AncientAnimus extends CardImpl { new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new TargetHasSuperTypeCondition(SuperType.LEGENDARY) ); - effect.setText("Put a +1/+1 counter on target creature you control if it's legendary."); + effect.setText("Put a +1/+1 counter on target creature you control if it's legendary"); this.getSpellAbility().addEffect(effect); effect = new FightTargetsEffect(); effect.setText("Then it fights target creature an opponent controls"); diff --git a/Mage.Sets/src/mage/cards/a/AncientExcavation.java b/Mage.Sets/src/mage/cards/a/AncientExcavation.java index c339ac270d..6aee0c9753 100644 --- a/Mage.Sets/src/mage/cards/a/AncientExcavation.java +++ b/Mage.Sets/src/mage/cards/a/AncientExcavation.java @@ -63,7 +63,7 @@ class AncientExcavationEffect extends OneShotEffect { if (player != null) { DynamicValue numCards = CardsInControllerHandCount.instance; int amount = numCards.calculate(game, source, this); - player.drawCards(amount, game); + player.drawCards(amount, source.getSourceId(), game); player.discard(amount, false, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java b/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java index bc7ddf8dac..095ca784ba 100644 --- a/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java +++ b/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java @@ -1,31 +1,25 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.AttackingCreatureCount; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.constants.SubType; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AttackingPredicate; -import mage.game.Game; import mage.game.permanent.token.StoneTrapIdolToken; -import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class AncientStoneIdol extends CardImpl { @@ -41,13 +35,16 @@ public final class AncientStoneIdol extends CardImpl { this.addAbility(FlashAbility.getInstance()); // This spell costs {1} less to cast for each attacking creature. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AncientStoneIdolCostReductionEffect())); + DynamicValue xValue = new AttackingCreatureCount(); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)) + .addHint(new ValueHint("Attacking creatures", xValue)) + ); // Trample this.addAbility(TrampleAbility.getInstance()); // When Ancient Stone Idol dies, create a 6/12 colorless Construct artifact creature token with trample. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new StoneTrapIdolToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new StoneTrapIdolToken()))); } public AncientStoneIdol(final AncientStoneIdol card) { @@ -59,41 +56,3 @@ public final class AncientStoneIdol extends CardImpl { return new AncientStoneIdol(this); } } - -class AncientStoneIdolCostReductionEffect extends CostModificationEffectImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(AttackingPredicate.instance); - } - - public AncientStoneIdolCostReductionEffect() { - super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "This spell costs {1} less to cast for each attacking creature"; - } - - protected AncientStoneIdolCostReductionEffect(AncientStoneIdolCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - int reductionAmount = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); - CardUtil.reduceCost(abilityToModify, reductionAmount); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if ((abilityToModify instanceof SpellAbility) && abilityToModify.getSourceId().equals(source.getSourceId())) { - return game.getCard(abilityToModify.getSourceId()) != null; - } - return false; - } - - @Override - public AncientStoneIdolCostReductionEffect copy() { - return new AncientStoneIdolCostReductionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AndraditeLeech.java b/Mage.Sets/src/mage/cards/a/AndraditeLeech.java index 0102b19379..3d91d654f5 100644 --- a/Mage.Sets/src/mage/cards/a/AndraditeLeech.java +++ b/Mage.Sets/src/mage/cards/a/AndraditeLeech.java @@ -1,25 +1,21 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author LoneFox */ public final class AndraditeLeech extends CardImpl { @@ -31,17 +27,17 @@ public final class AndraditeLeech extends CardImpl { } public AndraditeLeech(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.LEECH); this.power = new MageInt(2); this.toughness = new MageInt(2); // Black spells you cast cost {B} more to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{B}")))); + new SpellsCostIncreasingAllEffect(new ManaCostsImpl("{B}"), filter, TargetController.YOU))); // {B}: Andradite Leech gets +1/+1 until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{B}"))); + new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{B}"))); } public AndraditeLeech(final AndraditeLeech card) { diff --git a/Mage.Sets/src/mage/cards/a/AngelOfCondemnation.java b/Mage.Sets/src/mage/cards/a/AngelOfCondemnation.java index b407c9e41f..f82b5cbfe4 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfCondemnation.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfCondemnation.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -31,13 +29,15 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author emerald000 */ public final class AngelOfCondemnation extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another target creature"); + static { filter.add(AnotherPredicate.instance); } @@ -101,7 +101,7 @@ class AngelOfCondemnationExileUntilEOTEffect extends OneShotEffect { if (controller != null && permanent != null && sourcePermanent != null) { if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setText("return that card to the battlefield under its owner's control"); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/a/AngelOfFury.java b/Mage.Sets/src/mage/cards/a/AngelOfFury.java index 9bbdb35252..e6f7115a0c 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfFury.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfFury.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class AngelOfFury extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Angel of Fury is put into your graveyard from the battlefield, you may shuffle it into your library. - this.addAbility(new DiesTriggeredAbility(new ShuffleIntoLibrarySourceEffect(), true)); + this.addAbility(new DiesSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect(), true)); } public AngelOfFury(final AngelOfFury card) { diff --git a/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java b/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java index 2b3826169c..e02c3656b3 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ExileAllEffect; import mage.abilities.keyword.FlashAbility; @@ -36,7 +36,7 @@ public final class AngelOfTheDireHour extends CardImpl { // When Angel of the Dire Hour enters the battlefield, if you cast it from your hand, exile all attacking creatures. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new ExileAllEffect(new FilterAttackingCreature("attacking creatures")), false), - CastFromHandSourceCondition.instance, + CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, exile all attacking creatures."), new CastFromHandWatcher()); } diff --git a/Mage.Sets/src/mage/cards/a/AngelicAccord.java b/Mage.Sets/src/mage/cards/a/AngelicAccord.java index afe279414a..e618a780c8 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicAccord.java +++ b/Mage.Sets/src/mage/cards/a/AngelicAccord.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.condition.common.YouGainedLifeCondition; import mage.abilities.effects.common.CreateTokenEffect; @@ -14,8 +12,9 @@ import mage.constants.Zone; import mage.game.permanent.token.AngelToken; import mage.watchers.common.PlayerGainedLifeWatcher; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class AngelicAccord extends CardImpl { @@ -25,7 +24,8 @@ public final class AngelicAccord extends CardImpl { // At the beginning of each end step, if you gained 4 or more life this turn, create a 4/4 white Angel creature token with flying. this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new AngelToken()), TargetController.ANY, - new YouGainedLifeCondition(ComparisonType.MORE_THAN, 3), false), new PlayerGainedLifeWatcher()); + new YouGainedLifeCondition(ComparisonType.MORE_THAN, 3), false), + new PlayerGainedLifeWatcher()); } public AngelicAccord(final AngelicAccord card) { diff --git a/Mage.Sets/src/mage/cards/a/AngelicAscension.java b/Mage.Sets/src/mage/cards/a/AngelicAscension.java new file mode 100644 index 0000000000..afe5de86d2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AngelicAscension.java @@ -0,0 +1,67 @@ +package mage.cards.a; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.AngelToken; +import mage.game.permanent.token.Token; +import mage.target.common.TargetCreatureOrPlaneswalker; + +/** + * @author TheElk801 + */ +public final class AngelicAscension extends CardImpl { + + public AngelicAscension(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Exile target creature or planeswalker. Its controller creates a 4/4 white Angel creature token with flying. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addEffect(new AngelicAscensionEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private AngelicAscension(final AngelicAscension card) { + super(card); + } + + @Override + public AngelicAscension copy() { + return new AngelicAscension(this); + } +} + +class AngelicAscensionEffect extends OneShotEffect { + + private static final Token token = new AngelToken(); + + AngelicAscensionEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Its controller creates a 4/4 white Angel creature token with flying"; + } + + private AngelicAscensionEffect(final AngelicAscensionEffect effect) { + super(effect); + } + + @Override + public AngelicAscensionEffect copy() { + return new AngelicAscensionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (permanent == null) { + return false; + } + return token.putOntoBattlefield(1, game, source.getSourceId(), permanent.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AngelicRenewal.java b/Mage.Sets/src/mage/cards/a/AngelicRenewal.java index 32c670b17c..7e423095ad 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicRenewal.java +++ b/Mage.Sets/src/mage/cards/a/AngelicRenewal.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.common.DoIfCostPaid; @@ -11,19 +9,20 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterCreaturePermanent; +import java.util.UUID; + /** - * * @author LoneFox */ public final class AngelicRenewal extends CardImpl { public AngelicRenewal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // Whenever a creature is put into your graveyard from the battlefield, you may sacrifice Angelic Renewal. If you do, return that card to the battlefield. this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility(new DoIfCostPaid( - new ReturnToBattlefieldUnderOwnerControlTargetEffect(), new SacrificeSourceCost()), false, - new FilterCreaturePermanent("a creature"), true, true)); + new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false), new SacrificeSourceCost()), false, + new FilterCreaturePermanent("a creature"), true, true)); } public AngelicRenewal(final AngelicRenewal card) { diff --git a/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java b/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java index 2c5a6aef46..d9eff6ea19 100644 --- a/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java +++ b/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java @@ -98,7 +98,7 @@ class AngrathMinotaurPirateThirdAbilityEffect extends OneShotEffect { permanent.destroy(source.getSourceId(), game, false); powerSum += permanent.getPower().getValue(); } - game.applyEffects(); + game.getState().processAction(game); targetOpponent.damage(powerSum, source.getSourceId(), game); } return true; diff --git a/Mage.Sets/src/mage/cards/a/AnimalSanctuary.java b/Mage.Sets/src/mage/cards/a/AnimalSanctuary.java new file mode 100644 index 0000000000..b87c98b14c --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AnimalSanctuary.java @@ -0,0 +1,61 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AnimalSanctuary extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Bird, Cat, Dog, Goat, Ox, or Snake"); + + static { + filter.add(Predicates.or( + SubType.BIRD.getPredicate(), + SubType.CAT.getPredicate(), + SubType.DOG.getPredicate(), + SubType.GOAT.getPredicate(), + SubType.OX.getPredicate(), + SubType.SNAKE.getPredicate() + )); + } + + public AnimalSanctuary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake. + Ability ability = new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new GenericManaCost(2) + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private AnimalSanctuary(final AnimalSanctuary card) { + super(card); + } + + @Override + public AnimalSanctuary copy() { + return new AnimalSanctuary(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java b/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java index 94e44b7c8e..b4f77b1dc4 100644 --- a/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java +++ b/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -22,7 +21,6 @@ import mage.game.permanent.Permanent; import mage.util.CardUtil; /** - * * @author LevelX2 */ public final class AnimarSoulOfElements extends CardImpl { @@ -73,10 +71,8 @@ class AnimarCostReductionEffect extends CostModificationEffectImpl { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (sourcePermanent != null && spellAbility != null) { int amount = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); - if (amount > 0) { - CardUtil.reduceCost(spellAbility, amount); - return true; - } + CardUtil.reduceCost(spellAbility, amount); + return true; } return false; } @@ -85,9 +81,9 @@ class AnimarCostReductionEffect extends CostModificationEffectImpl { public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify instanceof SpellAbility) { if (abilityToModify.isControlledBy(source.getControllerId())) { - Card spell = ((SpellAbility) abilityToModify).getCharacteristics(game); - if (spell != null) { - return spell.isCreature(); + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + if (spellCard != null) { + return spellCard.isCreature(); } } } diff --git a/Mage.Sets/src/mage/cards/a/AnimateDead.java b/Mage.Sets/src/mage/cards/a/AnimateDead.java index 0f833cc16e..cab559a1e4 100644 --- a/Mage.Sets/src/mage/cards/a/AnimateDead.java +++ b/Mage.Sets/src/mage/cards/a/AnimateDead.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -9,6 +8,7 @@ import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.SourceOnBattlefieldCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; @@ -27,6 +27,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; /** * @@ -89,27 +90,28 @@ class AnimateDeadReAttachEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent enchantment = game.getPermanent(source.getSourceId()); + Permanent animateDead = game.getPermanent(source.getSourceId()); - if (controller != null && enchantment != null) { - Card cardInGraveyard = game.getCard(enchantment.getAttachedTo()); + if (controller != null && animateDead != null) { + Card cardInGraveyard = game.getCard(animateDead.getAttachedTo()); if (cardInGraveyard == null) { return true; } - - // put card into play + // put card into play from Graveyard controller.moveCards(cardInGraveyard, Zone.BATTLEFIELD, source, game); Permanent enchantedCreature = game.getPermanent(cardInGraveyard.getId()); - FilterCreaturePermanent filter = new FilterCreaturePermanent("enchant creature put onto the battlefield with Animate Dead"); - filter.add(new PermanentIdPredicate(cardInGraveyard.getId())); - Target target = new TargetCreaturePermanent(filter); - //enchantAbility.setTargetName(target.getTargetName()); if (enchantedCreature != null) { + FilterCreaturePermanent filter = new FilterCreaturePermanent("enchant creature put onto the battlefield with Animate Dead"); + filter.add(new PermanentIdPredicate(cardInGraveyard.getId())); + Target target = new TargetCreaturePermanent(filter); target.addTarget(enchantedCreature.getId(), source, game); - enchantment.getSpellAbility().getTargets().clear(); - enchantment.getSpellAbility().getTargets().add(target); - enchantedCreature.addAttachment(enchantment.getId(), game); + animateDead.getSpellAbility().getTargets().clear(); + animateDead.getSpellAbility().getTargets().add(target); + enchantedCreature.addAttachment(animateDead.getId(), game); + ContinuousEffect effect = new AnimateDeadAttachToPermanentEffect(); + effect.setTargetPointer(new FixedTarget(enchantedCreature, game)); + game.addEffect(effect, source); } return true; } @@ -225,12 +227,45 @@ class AnimateDeadChangeAbilityEffect extends ContinuousEffectImpl implements Sou abilityToRemove = ability; } } - if (abilityToRemove != null) { - permanent.getAbilities().remove(abilityToRemove); - } + permanent.removeAbility(abilityToRemove, source.getSourceId(), game); permanent.addAbility(newAbility, source.getSourceId(), game); return true; } return false; } } + +class AnimateDeadAttachToPermanentEffect extends ContinuousEffectImpl { + + public AnimateDeadAttachToPermanentEffect() { + super(Duration.Custom, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); + } + + public AnimateDeadAttachToPermanentEffect(final AnimateDeadAttachToPermanentEffect effect) { + super(effect); + } + + @Override + public AnimateDeadAttachToPermanentEffect copy() { + return new AnimateDeadAttachToPermanentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent animateDead = game.getPermanent(source.getSourceId()); + if (animateDead != null) { + // The target has to be changed to CreaturePermanent because the reset from card resets it to Card in Graveyard + FilterCreaturePermanent filter = new FilterCreaturePermanent("enchant creature put onto the battlefield with Animate Dead"); + filter.add(new PermanentIdPredicate(getTargetPointer().getFirst(game, source))); + Target target = new TargetCreaturePermanent(filter); + target.addTarget(((FixedTarget) getTargetPointer()).getTarget(), source, game); + animateDead.getSpellAbility().getTargets().clear(); + animateDead.getSpellAbility().getTargets().add(target); + } + if (animateDead == null) { + discard(); + } + return true; + } + +} diff --git a/Mage.Sets/src/mage/cards/a/AnimatingFaerie.java b/Mage.Sets/src/mage/cards/a/AnimatingFaerie.java index cd22af0c0b..d1e765400f 100644 --- a/Mage.Sets/src/mage/cards/a/AnimatingFaerie.java +++ b/Mage.Sets/src/mage/cards/a/AnimatingFaerie.java @@ -47,7 +47,7 @@ public final class AnimatingFaerie extends AdventureCard { ).setText("Target noncreature artifact you control becomes")); this.getSpellCard().getSpellAbility().addEffect(new SetPowerToughnessTargetEffect( 0, 0, Duration.EndOfGame - ).setText("a 0/0 artifact creature.")); + ).setText("a 0/0 artifact creature")); this.getSpellCard().getSpellAbility().addEffect(new AddCountersTargetEffect( CounterType.P1P1.createInstance(4) ).setText("Put four +1/+1 counters on it.")); diff --git a/Mage.Sets/src/mage/cards/a/AnjeFalkenrath.java b/Mage.Sets/src/mage/cards/a/AnjeFalkenrath.java index 9d0e2834dd..70c8b6c5a0 100644 --- a/Mage.Sets/src/mage/cards/a/AnjeFalkenrath.java +++ b/Mage.Sets/src/mage/cards/a/AnjeFalkenrath.java @@ -83,7 +83,7 @@ class AnjeFalkenrathTriggeredAbility extends TriggeredAbilityImpl { if (card == null) { return false; } - return card.getAbilities(game).stream().anyMatch(ability -> ability instanceof MadnessAbility); + return card.getAbilities(game).containsClass(MadnessAbility.class); } @Override diff --git a/Mage.Sets/src/mage/cards/a/AnodetLurker.java b/Mage.Sets/src/mage/cards/a/AnodetLurker.java index ecd459aad4..983985f035 100644 --- a/Mage.Sets/src/mage/cards/a/AnodetLurker.java +++ b/Mage.Sets/src/mage/cards/a/AnodetLurker.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class AnodetLurker extends CardImpl { this.toughness = new MageInt(3); // When Anodet Lurker dies, you gain 3 life. - this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(3))); + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(3))); } public AnodetLurker(final AnodetLurker card) { diff --git a/Mage.Sets/src/mage/cards/a/AnointedChorister.java b/Mage.Sets/src/mage/cards/a/AnointedChorister.java new file mode 100644 index 0000000000..c4761f699a --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AnointedChorister.java @@ -0,0 +1,46 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AnointedChorister extends CardImpl { + + public AnointedChorister(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // {4}{W}: Anointed Chorister gets +3/+3 until end of turn. + this.addAbility(new SimpleActivatedAbility( + new BoostSourceEffect(3, 3, Duration.EndOfTurn), new ManaCostsImpl("{4}{W}") + )); + } + + private AnointedChorister(final AnointedChorister card) { + super(card); + } + + @Override + public AnointedChorister copy() { + return new AnointedChorister(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/Antagonism.java b/Mage.Sets/src/mage/cards/a/Antagonism.java index 0ad3603e87..fa1b534afd 100644 --- a/Mage.Sets/src/mage/cards/a/Antagonism.java +++ b/Mage.Sets/src/mage/cards/a/Antagonism.java @@ -1,64 +1,64 @@ -package mage.cards.a; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfEndStepTriggeredAbility; -import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.TargetController; -import mage.game.Game; -import mage.watchers.common.BloodthirstWatcher; - -/** - * - * @author jeffwadsworth - */ -public final class Antagonism extends CardImpl { - - private static final String rule = "{this} deals 2 damage to that player unless one of their opponents was dealt damage this turn"; - - public Antagonism(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}"); - - // At the beginning of each player's end step, Antagonism deals 2 damage to that player unless one of their opponents was dealt damage this turn. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new ConditionalOneShotEffect(new DamageTargetEffect(2), - new OpponentWasNotDealtDamageCondition(), rule), TargetController.ANY, false)); - - } - - public Antagonism(final Antagonism card) { - super(card); - } - - @Override - public Antagonism copy() { - return new Antagonism(this); - } -} - -class OpponentWasNotDealtDamageCondition implements Condition { - - public OpponentWasNotDealtDamageCondition() { - } - - @Override - public boolean apply(Game game, Ability source) { - UUID activePlayer = game.getState().getActivePlayerId(); - if (activePlayer != null) { - BloodthirstWatcher watcher = game.getState().getWatcher(BloodthirstWatcher.class, activePlayer); - if (watcher != null) { - return !watcher.conditionMet(); - } - } - return false; - } - - @Override - public String toString() { - return "if an opponent was dealt damage this turn"; - } -} +package mage.cards.a; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.game.Game; +import mage.watchers.common.BloodthirstWatcher; + +/** + * + * @author jeffwadsworth + */ +public final class Antagonism extends CardImpl { + + private static final String rule = "{this} deals 2 damage to that player unless one of their opponents was dealt damage this turn"; + + public Antagonism(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}"); + + // At the beginning of each player's end step, Antagonism deals 2 damage to that player unless one of their opponents was dealt damage this turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new ConditionalOneShotEffect(new DamageTargetEffect(2), + new OpponentWasNotDealtDamageCondition(), rule), TargetController.ANY, false)); + + } + + public Antagonism(final Antagonism card) { + super(card); + } + + @Override + public Antagonism copy() { + return new Antagonism(this); + } +} + +class OpponentWasNotDealtDamageCondition implements Condition { + + public OpponentWasNotDealtDamageCondition() { + } + + @Override + public boolean apply(Game game, Ability source) { + UUID activePlayer = game.getState().getActivePlayerId(); + if (activePlayer != null) { + BloodthirstWatcher watcher = game.getState().getWatcher(BloodthirstWatcher.class, activePlayer); + if (watcher != null) { + return !watcher.conditionMet(); + } + } + return false; + } + + @Override + public String toString() { + return "if an opponent was dealt damage this turn"; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AnvilOfBogardan.java b/Mage.Sets/src/mage/cards/a/AnvilOfBogardan.java index 9a15bf7de8..7c25620ed5 100644 --- a/Mage.Sets/src/mage/cards/a/AnvilOfBogardan.java +++ b/Mage.Sets/src/mage/cards/a/AnvilOfBogardan.java @@ -57,7 +57,7 @@ class AnvilOfBogardanEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); if (targetPlayer != null) { - targetPlayer.drawCards(1, game); + targetPlayer.drawCards(1, source.getSourceId(), game); targetPlayer.discard(1, false, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/a/AnyaMercilessAngel.java b/Mage.Sets/src/mage/cards/a/AnyaMercilessAngel.java index fcd66fa2b2..2accd0b07c 100644 --- a/Mage.Sets/src/mage/cards/a/AnyaMercilessAngel.java +++ b/Mage.Sets/src/mage/cards/a/AnyaMercilessAngel.java @@ -1,14 +1,11 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.MultipliedValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; @@ -16,18 +13,22 @@ import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author emerald000 */ public final class AnyaMercilessAngel extends CardImpl { public AnyaMercilessAngel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ANGEL); this.power = new MageInt(4); @@ -35,21 +36,24 @@ public final class AnyaMercilessAngel extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // Anya, Merciless Angel gets +3/+3 for each opponent whose life total is less than half their starting life total. - DynamicValue dValue = new MultipliedValue(new AnyaMercilessAngelDynamicValue(), 3); - Effect effect = new BoostSourceEffect(dValue, dValue, Duration.WhileOnBattlefield); - effect.setText("{this} gets +3/+3 for each opponent whose life total is less than half their starting life total"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(dValue, dValue, Duration.WhileOnBattlefield))); - + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + AnyaMercilessAngelDynamicValue.instance, + AnyaMercilessAngelDynamicValue.instance, + Duration.WhileOnBattlefield + ).setText("{this} gets +3/+3 for each opponent whose life total is less than half their starting life total"))); + // As long as an opponent's life total is less than half their starting life total, Anya has indestructible. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield), - AnyaMercilessAngelCondition.instance, - "As long as an opponent's life total is less than half their starting life total, {this} has indestructible"))); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect( + IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield + ), AnyaMercilessAngelCondition.instance, "As long as an opponent's life total " + + "is less than half their starting life total, {this} has indestructible" + ))); } - public AnyaMercilessAngel(final AnyaMercilessAngel card) { + private AnyaMercilessAngel(final AnyaMercilessAngel card) { super(card); } @@ -59,27 +63,29 @@ public final class AnyaMercilessAngel extends CardImpl { } } -class AnyaMercilessAngelDynamicValue implements DynamicValue { +enum AnyaMercilessAngelDynamicValue implements DynamicValue { + instance; @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { int opponentCount = 0; Player controller = game.getPlayer(sourceAbility.getControllerId()); - if (controller != null) { - int startingLifeTotal = game.getLife(); - for (UUID opponentId : game.getOpponents(controller.getId())) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null && opponent.getLife() < startingLifeTotal / 2) { - opponentCount++; - } + if (controller == null) { + return 3 * opponentCount; + } + int startingLifeTotal = game.getLife(); + for (UUID opponentId : game.getOpponents(controller.getId())) { + Player opponent = game.getPlayer(opponentId); + if (opponent != null && opponent.getLife() < startingLifeTotal / 2) { + opponentCount++; } } - return opponentCount; + return 3 * opponentCount; } @Override public AnyaMercilessAngelDynamicValue copy() { - return new AnyaMercilessAngelDynamicValue(); + return instance; } @Override @@ -96,9 +102,10 @@ class AnyaMercilessAngelDynamicValue implements DynamicValue { enum AnyaMercilessAngelCondition implements Condition { instance; + @Override public boolean apply(Game game, Ability source) { - return new AnyaMercilessAngelDynamicValue().calculate(game, source, null) > 0; + return AnyaMercilessAngelDynamicValue.instance.calculate(game, source, null) > 0; } @Override diff --git a/Mage.Sets/src/mage/cards/a/ApexAltisaur.java b/Mage.Sets/src/mage/cards/a/ApexAltisaur.java index b97a614a8d..45f3d0821a 100644 --- a/Mage.Sets/src/mage/cards/a/ApexAltisaur.java +++ b/Mage.Sets/src/mage/cards/a/ApexAltisaur.java @@ -10,9 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -22,12 +20,6 @@ import java.util.UUID; */ public final class ApexAltisaur extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public ApexAltisaur(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{7}{G}{G}"); @@ -40,12 +32,12 @@ public final class ApexAltisaur extends CardImpl { // When Apex Altisaur enters the battlefield, it fights up to one target creature you don't control. Ability ability = new EntersBattlefieldTriggeredAbility(effect); - ability.addTarget(new TargetPermanent(0, 1, filter, false)); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); this.addAbility(ability); // Enrage — Whenever Apex Altisaur is dealt damage, it fights up to one target creature you don't control. ability = new DealtDamageToSourceTriggeredAbility(effect, false, true); - ability.addTarget(new TargetPermanent(0, 1, filter, false)); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ApexOfPower.java b/Mage.Sets/src/mage/cards/a/ApexOfPower.java index edf7670988..ed498bf484 100644 --- a/Mage.Sets/src/mage/cards/a/ApexOfPower.java +++ b/Mage.Sets/src/mage/cards/a/ApexOfPower.java @@ -79,7 +79,7 @@ class ApexOfPowerSpellEffect extends OneShotEffect { continue; } ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/a/AphettoVulture.java b/Mage.Sets/src/mage/cards/a/AphettoVulture.java index f62b8b9f22..7096706082 100644 --- a/Mage.Sets/src/mage/cards/a/AphettoVulture.java +++ b/Mage.Sets/src/mage/cards/a/AphettoVulture.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -38,7 +38,7 @@ public final class AphettoVulture extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Aphetto Vulture dies, you may put target Zombie card from your graveyard on top of your library. - Ability ability = new DiesTriggeredAbility(new PutOnLibraryTargetEffect(true), true); + Ability ability = new DiesSourceTriggeredAbility(new PutOnLibraryTargetEffect(true), true); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/Apocalypse.java b/Mage.Sets/src/mage/cards/a/Apocalypse.java index 09008dac71..073e15d336 100644 --- a/Mage.Sets/src/mage/cards/a/Apocalypse.java +++ b/Mage.Sets/src/mage/cards/a/Apocalypse.java @@ -1,30 +1,25 @@ - package mage.cards.a; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.ExileAllEffect; +import mage.abilities.effects.common.discard.DiscardHandControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author markedagain */ public final class Apocalypse extends CardImpl { public Apocalypse(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}{R}"); // Exile all permanents. You discard your hand. - this.getSpellAbility().addEffect(new ApocalypseExileAllPermanentsEffect()); - this.getSpellAbility().addEffect(new ApocalypseDiscardEffect()); + this.getSpellAbility().addEffect(new ExileAllEffect(StaticFilters.FILTER_PERMANENTS)); + this.getSpellAbility().addEffect(new DiscardHandControllerEffect().setText("You discard your hand")); } public Apocalypse(final Apocalypse card) { @@ -36,55 +31,3 @@ public final class Apocalypse extends CardImpl { return new Apocalypse(this); } } -class ApocalypseDiscardEffect extends OneShotEffect { - - public ApocalypseDiscardEffect() { - super(Outcome.Discard); - this.staticText = "Discard your hand"; - } - - public ApocalypseDiscardEffect(final ApocalypseDiscardEffect effect) { - super(effect); - } - - @Override - public ApocalypseDiscardEffect copy() { - return new ApocalypseDiscardEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - for (Card card : player.getHand().getCards(game)) { - player.discard(card, source, game); - } - return true; - } - return false; - } -} -class ApocalypseExileAllPermanentsEffect extends OneShotEffect { - - public ApocalypseExileAllPermanentsEffect() { - super(Outcome.Exile); - staticText = "Exile all permanents"; - } - - public ApocalypseExileAllPermanentsEffect(final ApocalypseExileAllPermanentsEffect effect) { - super(effect); - } - - @Override - public ApocalypseExileAllPermanentsEffect copy() { - return new ApocalypseExileAllPermanentsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { - permanent.moveToExile(null, null, source.getSourceId(), game); - } - return true; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java b/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java index ea4a3c9548..f47ae0b081 100644 --- a/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java +++ b/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java @@ -27,6 +27,8 @@ public final class ApproachOfTheSecondSun extends CardImpl { public ApproachOfTheSecondSun(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{W}"); + // If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game, + // you win the game. Otherwise, put Approach of the Second Sun into its owner's library seventh from the top and you gain 7 life. getSpellAbility().addEffect(new ApproachOfTheSecondSunEffect()); getSpellAbility().addWatcher(new ApproachOfTheSecondSunWatcher()); } @@ -46,7 +48,7 @@ class ApproachOfTheSecondSunEffect extends OneShotEffect { public ApproachOfTheSecondSunEffect() { super(Outcome.Win); this.staticText - = "If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game, you win the game. " + = "If this spell was cast from your hand and you've cast another spell named {this} this game, you win the game. " + "Otherwise, put {this} into its owner's library seventh from the top and you gain 7 life."; } @@ -93,7 +95,7 @@ class ApproachOfTheSecondSunEffect extends OneShotEffect { class ApproachOfTheSecondSunWatcher extends Watcher { - private Map approachesCast = new HashMap<>(); + private final Map approachesCast = new HashMap<>(); public ApproachOfTheSecondSunWatcher() { super(WatcherScope.GAME); diff --git a/Mage.Sets/src/mage/cards/a/AquastrandSpider.java b/Mage.Sets/src/mage/cards/a/AquastrandSpider.java index f6994a33fb..7b648deaf6 100644 --- a/Mage.Sets/src/mage/cards/a/AquastrandSpider.java +++ b/Mage.Sets/src/mage/cards/a/AquastrandSpider.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -13,6 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; @@ -26,13 +26,15 @@ import mage.target.common.TargetCreaturePermanent; */ public final class AquastrandSpider extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with a +1/+1 counter on it"); - static { - filter.add(new CounterPredicate(CounterType.P1P1)); - } + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creature with a +1/+1 counter on it"); + static { + filter.add(new CounterPredicate(CounterType.P1P1)); + } + public AquastrandSpider(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.SPIDER); this.subtype.add(SubType.MUTANT); this.power = new MageInt(0); @@ -40,17 +42,19 @@ public final class AquastrandSpider extends CardImpl { // Graft 2 this.addAbility(new GraftAbility(this, 2)); - - // {G}: Target creature with a +1/+1 counter on it gains reach until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(ReachAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{G}")); - ability.addTarget(new TargetCreaturePermanent(filter)); - this.addAbility(ability); - } + // {G}: Target creature with a +1/+1 counter on it gains reach until end of turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new GainAbilityTargetEffect(ReachAbility.getInstance(), + Duration.EndOfTurn), new ManaCostsImpl("{G}")); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability.addCustomOutcome(Outcome.Benefit)); + } + public AquastrandSpider(final AquastrandSpider card) { super(card); } - + @Override public AquastrandSpider copy() { return new AquastrandSpider(this); diff --git a/Mage.Sets/src/mage/cards/a/ArashinSovereign.java b/Mage.Sets/src/mage/cards/a/ArashinSovereign.java index 88676ac5f6..0aeac68a2d 100644 --- a/Mage.Sets/src/mage/cards/a/ArashinSovereign.java +++ b/Mage.Sets/src/mage/cards/a/ArashinSovereign.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; @@ -33,7 +33,7 @@ public final class ArashinSovereign extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Arashin Sovereign dies, you may put it on the top or bottom of its owner's library. - this.addAbility(new DiesTriggeredAbility(new ArashinSovereignEffect(), true)); + this.addAbility(new DiesSourceTriggeredAbility(new ArashinSovereignEffect(), true)); } public ArashinSovereign(final ArashinSovereign card) { diff --git a/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java b/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java index 99cceae788..d46ca7710f 100644 --- a/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java +++ b/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java @@ -30,6 +30,7 @@ import mage.util.CardUtil; import java.util.HashSet; import java.util.Set; import java.util.UUID; +import mage.cards.CardsImpl; /** * @author TheElk801 @@ -90,7 +91,7 @@ class ArcaneArtisanCreateTokenEffect extends OneShotEffect { if (player == null) { return false; } - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); TargetCard target = new TargetCardInHand(1, StaticFilters.FILTER_CARD); if (!player.chooseTarget(Outcome.Exile, player.getHand(), target, source, game)) { return false; @@ -167,12 +168,10 @@ class ArcaneArtisanExileEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Object object = game.getState().getValue(CardUtil.getCardZoneString("_tokensCreated", source.getSourceId(), game, true)); if (object != null) { - Set tokensCreated = (Set) object; - for (UUID tokenId : tokensCreated) { - Permanent token = game.getPermanent(tokenId); - if (token != null) { - token.destroy(source.getSourceId(), game, true); - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + controller.moveCards(new CardsImpl((Set) object), Zone.EXILED, source, game); + return true; } } return true; diff --git a/Mage.Sets/src/mage/cards/a/ArcaneMelee.java b/Mage.Sets/src/mage/cards/a/ArcaneMelee.java index 9e45b06db7..2657238404 100644 --- a/Mage.Sets/src/mage/cards/a/ArcaneMelee.java +++ b/Mage.Sets/src/mage/cards/a/ArcaneMelee.java @@ -1,17 +1,14 @@ - package mage.cards.a; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.cards.Card; +import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.common.FilterInstantOrSorceryCard; + +import java.util.UUID; /** * @author noxx @@ -22,7 +19,9 @@ public final class ArcaneMelee extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}"); // Instant and sorcery spells cost {2} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ArcaneMeleeCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostReductionAllEffect(new FilterInstantOrSorceryCard("Instant and sorcery spells"), 2)) + ); } public ArcaneMelee(final ArcaneMelee card) { @@ -34,39 +33,3 @@ public final class ArcaneMelee extends CardImpl { return new ArcaneMelee(this); } } - -class ArcaneMeleeCostReductionEffect extends CostModificationEffectImpl { - - ArcaneMeleeCostReductionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "Instant and sorcery spells cost {2} less to cast"; - } - - ArcaneMeleeCostReductionEffect(ArcaneMeleeCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, 2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - Card sourceCard = game.getCard((abilityToModify).getSourceId()); - if (sourceCard != null && (sourceCard.isInstant() || sourceCard.isSorcery())) { - return true; - } - } - return false; - } - - @Override - public ArcaneMeleeCostReductionEffect copy() { - return new ArcaneMeleeCostReductionEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/a/Archaeomender.java b/Mage.Sets/src/mage/cards/a/Archaeomender.java new file mode 100644 index 0000000000..7d796a4504 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/Archaeomender.java @@ -0,0 +1,47 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Archaeomender extends CardImpl { + + private static final FilterCard filter + = new FilterArtifactCard("artifact card from your graveyard"); + + public Archaeomender(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // When Archaeomender enters the battlefield, return target artifact card from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private Archaeomender(final Archaeomender card) { + super(card); + } + + @Override + public Archaeomender copy() { + return new Archaeomender(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArcheryTraining.java b/Mage.Sets/src/mage/cards/a/ArcheryTraining.java index f70caaa22c..6fad28fe0f 100644 --- a/Mage.Sets/src/mage/cards/a/ArcheryTraining.java +++ b/Mage.Sets/src/mage/cards/a/ArcheryTraining.java @@ -1,67 +1,67 @@ -package mage.cards.a; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.target.common.TargetAttackingOrBlockingCreature; - -/** - * - * @author jeffwadsworth - */ -public final class ArcheryTraining extends CardImpl { - - private static final String rule = "This creature deals X damage to target attacking or blocking creature, where X is the number of arrow counters on {this}."; - - public ArcheryTraining(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // At the beginning of your upkeep, you may put an arrow counter on Archery Training. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.ARROW.createInstance(), true), TargetController.YOU, true)); - - // Enchanted creature has "{tap}: This creature deals X damage to target attacking or blocking creature, where X is the number of arrow counters on Archery Training." - Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new CountersSourceCount(CounterType.ARROW)).setText(rule), new TapSourceCost()); - gainedAbility.addTarget(new TargetAttackingOrBlockingCreature()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA))); - - } - - public ArcheryTraining(final ArcheryTraining card) { - super(card); - } - - @Override - public ArcheryTraining copy() { - return new ArcheryTraining(this); - } -} +package mage.cards.a; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.common.TargetAttackingOrBlockingCreature; + +/** + * + * @author jeffwadsworth + */ +public final class ArcheryTraining extends CardImpl { + + private static final String rule = "This creature deals X damage to target attacking or blocking creature, where X is the number of arrow counters on {this}."; + + public ArcheryTraining(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of your upkeep, you may put an arrow counter on Archery Training. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.ARROW.createInstance(), true), TargetController.YOU, true)); + + // Enchanted creature has "{tap}: This creature deals X damage to target attacking or blocking creature, where X is the number of arrow counters on Archery Training." + Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new CountersSourceCount(CounterType.ARROW)).setText(rule), new TapSourceCost()); + gainedAbility.addTarget(new TargetAttackingOrBlockingCreature()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA))); + + } + + public ArcheryTraining(final ArcheryTraining card) { + super(card); + } + + @Override + public ArcheryTraining copy() { + return new ArcheryTraining(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java b/Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java new file mode 100644 index 0000000000..95d0680393 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java @@ -0,0 +1,131 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.DemonToken; +import mage.game.permanent.token.Token; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.List; +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class ArchfiendsVessel extends CardImpl { + + public ArchfiendsVessel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // When Archfiend's Vessel enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying. + this.addAbility(new ArchfiendsVesselAbility(), new SpellsCastWatcher()); + } + + private ArchfiendsVessel(final ArchfiendsVessel card) { + super(card); + } + + @Override + public ArchfiendsVessel copy() { + return new ArchfiendsVessel(this); + } +} + +class ArchfiendsVesselAbility extends EntersBattlefieldTriggeredAbility { + + ArchfiendsVesselAbility() { + super(new ArchfiendsVesselEffect()); + } + + private ArchfiendsVesselAbility(ArchfiendsVesselAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (super.checkTrigger(event, game)) { + if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { + EntersTheBattlefieldEvent entersTheBattlefieldEvent = (EntersTheBattlefieldEvent) event; + if (entersTheBattlefieldEvent.getTargetId().equals(getSourceId()) && entersTheBattlefieldEvent.getFromZone() == Zone.GRAVEYARD) { + return true; + } else { + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + List spellsCastFromGraveyard = watcher.getSpellsCastFromGraveyardThisTurn(getControllerId()); + if (spellsCastFromGraveyard != null) { + return spellsCastFromGraveyard.stream() + .anyMatch(spell -> spell.getMainCard().getId().equals((entersTheBattlefieldEvent.getTarget().getMainCard().getId()))); + } + } + } + } + return false; + } + + @Override + public ArchfiendsVesselAbility copy() { + return new ArchfiendsVesselAbility(this); + } +} + +class ArchfiendsVesselEffect extends OneShotEffect { + + ArchfiendsVesselEffect() { + super(Outcome.Benefit); + staticText = "if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying"; + } + + private ArchfiendsVesselEffect(ArchfiendsVesselEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent archfiendsVessel = source.getSourcePermanentIfItStillExists(game); + if (archfiendsVessel != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + boolean moved = controller.moveCards(archfiendsVessel.getMainCard(), Zone.EXILED, source, game); + if (moved) { + Token token = new DemonToken(); + token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId()); + } + return true; + } + } + return false; + } + + @Override + public ArchfiendsVesselEffect copy() { + return new ArchfiendsVesselEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/Archipelagore.java b/Mage.Sets/src/mage/cards/a/Archipelagore.java new file mode 100644 index 0000000000..55a15bdb0d --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/Archipelagore.java @@ -0,0 +1,63 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.dynamicvalue.common.SourceMutatedCount; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Archipelagore extends CardImpl { + + public Archipelagore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); + + this.subtype.add(SubType.LEVIATHAN); + this.power = new MageInt(7); + this.toughness = new MageInt(7); + + // Mutate {5}{U} + this.addAbility(new MutateAbility(this, "{5}{U}")); + + // Whenever this creature mutates, tap up to X target creatures, where X is the number of times this creature has mutated. Those creatures don't untap during their controller's next untap step. + Ability ability = new MutatesSourceTriggeredAbility(new TapTargetEffect( + "up to X target creatures, where X is the number of times this creature has mutated." + )); + ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("Those creatures")); + ability.setTargetAdjuster(ArchipelagoreAdjuster.instance); + this.addAbility(ability); + } + + private Archipelagore(final Archipelagore card) { + super(card); + } + + @Override + public Archipelagore copy() { + return new Archipelagore(this); + } +} + +enum ArchipelagoreAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + int mutateCount = SourceMutatedCount.instance.calculate(game, ability, null); + ability.getTargets().clear(); + ability.addTarget(new TargetCreaturePermanent(0, mutateCount)); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java b/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java index 0ac34127dd..0e32d82850 100644 --- a/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java +++ b/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java @@ -2,7 +2,7 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -33,7 +33,7 @@ public final class ArchonOfFallingStars extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Archon of Falling Stars dies, you may return target enchantment card from your graveyard to the battlefield. - Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), true); + Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), true); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfJustice.java b/Mage.Sets/src/mage/cards/a/ArchonOfJustice.java index 755522cb19..c08485495b 100644 --- a/Mage.Sets/src/mage/cards/a/ArchonOfJustice.java +++ b/Mage.Sets/src/mage/cards/a/ArchonOfJustice.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class ArchonOfJustice extends CardImpl { this.toughness = new MageInt(4); this.addAbility(FlyingAbility.getInstance()); - Ability ability = new DiesTriggeredAbility(new ExileTargetEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new ExileTargetEffect(), false); ability.addTarget(new TargetPermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java b/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java index c450ed3074..df21ecbce8 100644 --- a/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java +++ b/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java @@ -1,26 +1,35 @@ package mage.cards.a; +import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; - -import java.util.UUID; +import mage.players.Player; /** * @author Loki */ public final class ArchonOfRedemption extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("creature with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + public ArchonOfRedemption(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); this.subtype.add(SubType.ARCHON); @@ -29,11 +38,14 @@ public final class ArchonOfRedemption extends CardImpl { this.toughness = new MageInt(4); this.addAbility(FlyingAbility.getInstance()); + // Whenever Archon of Redemption or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power. - this.addAbility(new ArchonOfRedemptionTriggeredAbility()); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new ArchonOfRedemptionEffect(), filter, true, SetTargetPointer.PERMANENT, true + )); } - public ArchonOfRedemption(final ArchonOfRedemption card) { + private ArchonOfRedemption(final ArchonOfRedemption card) { super(card); } @@ -43,43 +55,29 @@ public final class ArchonOfRedemption extends CardImpl { } } -class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl { +class ArchonOfRedemptionEffect extends OneShotEffect { - ArchonOfRedemptionTriggeredAbility() { - super(Zone.BATTLEFIELD, null, true); + ArchonOfRedemptionEffect() { + super(Outcome.Benefit); + staticText = "gain life equal to that creature's power"; } - ArchonOfRedemptionTriggeredAbility(final ArchonOfRedemptionTriggeredAbility ability) { - super(ability); + private ArchonOfRedemptionEffect(final ArchonOfRedemptionEffect effect) { + super(effect); } @Override - public ArchonOfRedemptionTriggeredAbility copy() { - return new ArchonOfRedemptionTriggeredAbility(this); + public ArchonOfRedemptionEffect copy() { + return new ArchonOfRedemptionEffect(this); } @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null - && permanent.isControlledBy(getControllerId()) - && permanent.isCreature() - && (permanent.getId().equals(getSourceId()) - || (permanent.getAbilities().contains(FlyingAbility.getInstance())))) { - this.getEffects().clear(); - this.addEffect(new GainLifeEffect(permanent.getPower().getValue())); - return true; + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (player == null || permanent == null) { + return false; } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power."; + return player.gainLife(permanent.getPower().getValue(), game, source) > 0; } } diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfValorsReach.java b/Mage.Sets/src/mage/cards/a/ArchonOfValorsReach.java index fd4d1baa2a..35badc4c43 100644 --- a/Mage.Sets/src/mage/cards/a/ArchonOfValorsReach.java +++ b/Mage.Sets/src/mage/cards/a/ArchonOfValorsReach.java @@ -1,27 +1,22 @@ - package mage.cards.a; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseCardTypeEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.choices.ChoiceImpl; import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.util.CardUtil; +import java.util.Arrays; import java.util.UUID; /** @@ -46,7 +41,10 @@ public final class ArchonOfValorsReach extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // As Archon of Valor's Reach enters the battlefield, choose artifact, enchantment, instant, sorcery, or planeswalker. - this.addAbility(new AsEntersBattlefieldAbility(new ArchonOfValorsReachChooseEffect())); + this.addAbility(new AsEntersBattlefieldAbility( + new ChooseCardTypeEffect(Outcome.Benefit, Arrays.asList(CardType.ARTIFACT, CardType.ENCHANTMENT, CardType.INSTANT, CardType.PLANESWALKER)) + .setText("choose artifact, enchantment, instant, sorcery, or planeswalker") + )); // Players can't cast spells of the chosen type. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ArchonOfValorsReachReplacementEffect())); @@ -62,85 +60,6 @@ public final class ArchonOfValorsReach extends CardImpl { } } -class ArchonOfValorsReachChooseEffect extends OneShotEffect { - - ArchonOfValorsReachChooseEffect() { - super(Outcome.Benefit); - this.staticText = "choose artifact, enchantment, instant, sorcery, or planeswalker"; - } - - private ArchonOfValorsReachChooseEffect(final ArchonOfValorsReachChooseEffect effect) { - super(effect); - } - - @Override - public ArchonOfValorsReachChooseEffect copy() { - return new ArchonOfValorsReachChooseEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject mageObject = game.getPermanentEntering(source.getSourceId()); - if (mageObject == null) { - mageObject = game.getObject(source.getSourceId()); - } - if (controller != null && mageObject != null) { - ArchonOfValorsReachChoice choices = new ArchonOfValorsReachChoice(); - if (controller.choose(Outcome.Neutral, choices, game)) { - game.informPlayers(mageObject.getName() + ": Chosen card type is " + choices.getChoice()); - System.out.println(mageObject.getId()); - game.getState().setValue(mageObject.getId().toString() + "_cardtype", choices.getChoice()); - if (mageObject instanceof Permanent) { - ((Permanent) mageObject).addInfo("chosen color", CardUtil.addToolTipMarkTags("Chosen card type: " + choices.getChoice()), game); - } - return true; - } - } - return false; - } -} - -class ArchonOfValorsReachChoice extends ChoiceImpl { - - ArchonOfValorsReachChoice() { - super(true); - this.choices.add("Artifact"); - this.choices.add("Enchantment"); - this.choices.add("Instant"); - this.choices.add("Sorcery"); - this.choices.add("Planeswalker"); - this.message = "Choose artifact, enchantment, instant, sorcery, or planeswalker"; - } - - private ArchonOfValorsReachChoice(final ArchonOfValorsReachChoice choice) { - super(choice); - } - - public static CardType getType(String ch) { - switch (ch) { - case "Artifact": - return CardType.ARTIFACT; - case "Enchantment": - return CardType.ENCHANTMENT; - case "Instant": - return CardType.INSTANT; - case "Sorcery": - return CardType.SORCERY; - case "Planeswalker": - return CardType.PLANESWALKER; - default: - return null; - } - } - - @Override - public ArchonOfValorsReachChoice copy() { - return new ArchonOfValorsReachChoice(this); - } - -} - class ArchonOfValorsReachReplacementEffect extends ContinuousRuleModifyingEffectImpl { ArchonOfValorsReachReplacementEffect() { @@ -154,10 +73,11 @@ class ArchonOfValorsReachReplacementEffect extends ContinuousRuleModifyingEffect @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { - CardType cardType = (CardType) game.getState().getValue(source.getSourceId().toString() + "_cardtype"); - MageObject mageObject = game.getObject(source.getSourceId()); - if (mageObject != null && cardType != null) { - return "You can't cast " + cardType.toString() + " spells (" + mageObject.getIdName() + ")."; + Object savedType = game.getState().getValue(source.getSourceId() + "_type"); + Card sourceCard = game.getCard(source.getSourceId()); + if (savedType instanceof String && sourceCard != null) { + CardType cardType = CardType.fromString((String) savedType); + return "You can't cast " + cardType.toString() + " spells (" + sourceCard.getIdName() + ")."; } return null; } @@ -169,10 +89,13 @@ class ArchonOfValorsReachReplacementEffect extends ContinuousRuleModifyingEffect @Override public boolean applies(GameEvent event, Ability source, Game game) { - CardType cardType = ArchonOfValorsReachChoice.getType((String) game.getState().getValue(source.getSourceId().toString() + "_cardtype")); - // spell is not on the stack yet, so we have to check the card - Card card = game.getCard(event.getSourceId()); - return cardType != null && card != null && card.getCardType().contains(cardType); + Object savedType = game.getState().getValue(source.getSourceId() + "_type"); + Card sourceCard = game.getCard(source.getSourceId()); + if (savedType instanceof String && sourceCard != null) { + CardType cardType = CardType.fromString((String) savedType); + return cardType != null && sourceCard != null && sourceCard.getCardType().contains(cardType); + } + return false; } @Override diff --git a/Mage.Sets/src/mage/cards/a/ArcticMerfolk.java b/Mage.Sets/src/mage/cards/a/ArcticMerfolk.java index 712deb7181..8c92184503 100644 --- a/Mage.Sets/src/mage/cards/a/ArcticMerfolk.java +++ b/Mage.Sets/src/mage/cards/a/ArcticMerfolk.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -30,7 +30,7 @@ public final class ArcticMerfolk extends CardImpl { this.toughness = new MageInt(1); // Kicker—Return a creature you control to its owner's hand. (You may return a creature you control to its owner's hand in addition to any other costs as you cast this spell.) - this.addAbility(new KickerAbility(new ReturnToHandChosenControlledPermanentCost(new TargetControlledCreaturePermanent(1,1,new FilterControlledCreaturePermanent("a creature"),true)))); + this.addAbility(new KickerAbility(new ReturnToHandChosenControlledPermanentCost(new TargetControlledCreaturePermanent(1,1,StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT,true)))); // If Arctic Merfolk was kicked, it enters the battlefield with a +1/+1 counter on it. this.addAbility(new EntersBattlefieldAbility( diff --git a/Mage.Sets/src/mage/cards/a/ArcticNishoba.java b/Mage.Sets/src/mage/cards/a/ArcticNishoba.java index 5425eed036..11fb4439dd 100644 --- a/Mage.Sets/src/mage/cards/a/ArcticNishoba.java +++ b/Mage.Sets/src/mage/cards/a/ArcticNishoba.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.OrCost; import mage.constants.SubType; import mage.abilities.keyword.TrampleAbility; @@ -45,7 +45,7 @@ public final class ArcticNishoba extends CardImpl { // When Arctic Nishoba dies, you gain 2 life for each age counter on it. Effect effect = new GainLifeEffect(new MultipliedValue(new CountersSourceCount(CounterType.AGE), 2)); effect.setText("you gain 2 life for each age counter on it"); - this.addAbility(new DiesTriggeredAbility(effect)); + this.addAbility(new DiesSourceTriggeredAbility(effect)); } public ArcticNishoba(final ArcticNishoba card) { diff --git a/Mage.Sets/src/mage/cards/a/ArdentRecruit.java b/Mage.Sets/src/mage/cards/a/ArdentRecruit.java index 7759f1aa3c..a3637c55e6 100644 --- a/Mage.Sets/src/mage/cards/a/ArdentRecruit.java +++ b/Mage.Sets/src/mage/cards/a/ArdentRecruit.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -9,10 +7,13 @@ import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import java.util.UUID; + /** * @author Loki */ @@ -32,6 +33,7 @@ public final class ArdentRecruit extends CardImpl { "{this} gets +2/+2 as long as you control three or more artifacts"); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); ability.setAbilityWord(AbilityWord.METALCRAFT); + ability.addHint(MetalcraftHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArenaRector.java b/Mage.Sets/src/mage/cards/a/ArenaRector.java index b1fa212eb2..53510af308 100644 --- a/Mage.Sets/src/mage/cards/a/ArenaRector.java +++ b/Mage.Sets/src/mage/cards/a/ArenaRector.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.common.ExileSourceFromGraveCost; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; @@ -29,7 +29,7 @@ public final class ArenaRector extends CardImpl { this.toughness = new MageInt(2); // When Arena Rector dies, you may exile it. If you do, search your library for a planeswalker card, put it onto the battlefield, then shuffle your library. - this.addAbility(new DiesTriggeredAbility( + this.addAbility(new DiesSourceTriggeredAbility( new DoIfCostPaid( new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(new FilterPlaneswalkerCard())), new ExileSourceFromGraveCost(), diff --git a/Mage.Sets/src/mage/cards/a/ArgentSphinx.java b/Mage.Sets/src/mage/cards/a/ArgentSphinx.java index 7f76777b23..cef7246659 100644 --- a/Mage.Sets/src/mage/cards/a/ArgentSphinx.java +++ b/Mage.Sets/src/mage/cards/a/ArgentSphinx.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -12,6 +10,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderYourControlTargetEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -20,8 +19,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author Loki */ public final class ArgentSphinx extends CardImpl { @@ -39,6 +39,7 @@ public final class ArgentSphinx extends CardImpl { // Metalcraft — {U}: Exile Argent Sphinx. Return it to the battlefield under your control at the beginning of the next end step. Activate this ability only if you control three or more artifacts. Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new ArgentSphinxEffect(), new ManaCostsImpl("{U}"), MetalcraftCondition.instance); ability.setAbilityWord(AbilityWord.METALCRAFT); + ability.addHint(MetalcraftHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArgivianFind.java b/Mage.Sets/src/mage/cards/a/ArgivianFind.java index 62599a94b9..81fe6f61af 100644 --- a/Mage.Sets/src/mage/cards/a/ArgivianFind.java +++ b/Mage.Sets/src/mage/cards/a/ArgivianFind.java @@ -1,8 +1,6 @@ - package mage.cards.a; -import java.util.UUID; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -10,26 +8,28 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author emerald000 */ public final class ArgivianFind extends CardImpl { - - private static final FilterCard filter = new FilterCard("artifact or enchantment card"); + + private static final FilterCard filter = new FilterCard("artifact or enchantment card from your graveyard"); + static { filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), CardType.ENCHANTMENT.getPredicate())); } public ArgivianFind(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); // Return target artifact or enchantment card from your graveyard to your hand. this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(filter)); - this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); } - public ArgivianFind(final ArgivianFind card) { + private ArgivianFind(final ArgivianFind card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java b/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java index ba7aa7f660..6332eb2124 100644 --- a/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java +++ b/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -9,18 +7,18 @@ import mage.constants.CardType; import mage.filter.common.FilterArtifactCard; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author North */ public final class ArgivianRestoration extends CardImpl { public ArgivianRestoration(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}"); // Return target artifact card from your graveyard to the battlefield. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard"))); } diff --git a/Mage.Sets/src/mage/cards/a/ArjunTheShiftingFlame.java b/Mage.Sets/src/mage/cards/a/ArjunTheShiftingFlame.java index 458786bfd5..8a92258bf4 100644 --- a/Mage.Sets/src/mage/cards/a/ArjunTheShiftingFlame.java +++ b/Mage.Sets/src/mage/cards/a/ArjunTheShiftingFlame.java @@ -64,7 +64,7 @@ class ArjunTheShiftingFlameEffect extends OneShotEffect { if (you != null) { int count = you.getHand().size(); you.putCardsOnBottomOfLibrary(you.getHand(), game, source, true); - you.drawCards(count, game); + you.drawCards(count, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/a/ArmadaWurm.java b/Mage.Sets/src/mage/cards/a/ArmadaWurm.java index c6fbc3025b..64455089fc 100644 --- a/Mage.Sets/src/mage/cards/a/ArmadaWurm.java +++ b/Mage.Sets/src/mage/cards/a/ArmadaWurm.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -10,10 +8,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.game.permanent.token.WurmToken2; +import mage.game.permanent.token.WurmWithTrampleToken; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ArmadaWurm extends CardImpl { @@ -29,7 +28,7 @@ public final class ArmadaWurm extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Armada Wurm enters the battlefield, create a 5/5 green Wurm creature token with trample. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WurmToken2()), false)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WurmWithTrampleToken()), false)); } diff --git a/Mage.Sets/src/mage/cards/a/ArmorSliver.java b/Mage.Sets/src/mage/cards/a/ArmorSliver.java index cc1e4401fa..5a6d32b5be 100644 --- a/Mage.Sets/src/mage/cards/a/ArmorSliver.java +++ b/Mage.Sets/src/mage/cards/a/ArmorSliver.java @@ -29,7 +29,7 @@ public final class ArmorSliver extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( - new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(0, 1, Duration.EndOfTurn), + new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(0, 1, Duration.EndOfTurn).setText("this creature gets +0/+1 until end of turn"), new GenericManaCost(2)), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, false))); } diff --git a/Mage.Sets/src/mage/cards/a/ArmoredAscension.java b/Mage.Sets/src/mage/cards/a/ArmoredAscension.java index ad9a117331..25b7a1516b 100644 --- a/Mage.Sets/src/mage/cards/a/ArmoredAscension.java +++ b/Mage.Sets/src/mage/cards/a/ArmoredAscension.java @@ -34,11 +34,13 @@ public final class ArmoredAscension extends CardImpl { this.subtype.add(SubType.AURA); + // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + // Enchanted creature gets +1/+1 for each Plains you control and has flying. PermanentsOnBattlefieldCount amount = new PermanentsOnBattlefieldCount(filter, 1); SimpleStaticAbility ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(amount, amount, Duration.WhileOnBattlefield)); ability.addEffect(new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA)); diff --git a/Mage.Sets/src/mage/cards/a/ArrowStorm.java b/Mage.Sets/src/mage/cards/a/ArrowStorm.java index f8508be32d..66efca8486 100644 --- a/Mage.Sets/src/mage/cards/a/ArrowStorm.java +++ b/Mage.Sets/src/mage/cards/a/ArrowStorm.java @@ -1,19 +1,20 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.target.common.TargetAnyTarget; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ArrowStorm extends CardImpl { @@ -33,6 +34,8 @@ public final class ArrowStorm extends CardImpl { RaidCondition.instance, "

Raid — If you attacked with a creature this turn, instead {this} deals 5 damage to that permanent or player and the damage can't be prevented")); this.getSpellAbility().addWatcher(new PlayerAttackedWatcher()); + this.getSpellAbility().setAbilityWord(AbilityWord.RAID); + this.getSpellAbility().addHint(RaidHint.instance); } public ArrowStorm(final ArrowStorm card) { diff --git a/Mage.Sets/src/mage/cards/a/ArtifactMutation.java b/Mage.Sets/src/mage/cards/a/ArtifactMutation.java index 43c0240f8e..abc4b8b08c 100644 --- a/Mage.Sets/src/mage/cards/a/ArtifactMutation.java +++ b/Mage.Sets/src/mage/cards/a/ArtifactMutation.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -11,21 +9,22 @@ import mage.constants.CardType; import mage.game.permanent.token.SaprolingToken; import mage.target.common.TargetArtifactPermanent; +import java.util.UUID; + /** - * * @author North */ public final class ArtifactMutation extends CardImpl { public ArtifactMutation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{G}"); // Destroy target artifact. It can't be regenerated. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); this.getSpellAbility().addTarget(new TargetArtifactPermanent()); + // create X 1/1 green Saproling creature tokens, where X is that artifact's converted mana cost. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetConvertedManaCost.instance)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetConvertedManaCost.instance).setText("create X 1/1 green Saproling creature tokens, where X is that artifact's converted mana cost")); } public ArtifactMutation(final ArtifactMutation card) { diff --git a/Mage.Sets/src/mage/cards/a/ArtisanOfForms.java b/Mage.Sets/src/mage/cards/a/ArtisanOfForms.java index 1d8a35af4a..a6848d62d5 100644 --- a/Mage.Sets/src/mage/cards/a/ArtisanOfForms.java +++ b/Mage.Sets/src/mage/cards/a/ArtisanOfForms.java @@ -63,7 +63,7 @@ class ArtisanOfFormsApplyToPermanent extends ApplyToPermanent { @Override public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { - permanent.addAbility(ArtisanOfForms.createAbility(), game); + permanent.addAbility(ArtisanOfForms.createAbility(), source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/a/AsajjVentress.java b/Mage.Sets/src/mage/cards/a/AsajjVentress.java index 3e2aa60f81..4cf2118aab 100644 --- a/Mage.Sets/src/mage/cards/a/AsajjVentress.java +++ b/Mage.Sets/src/mage/cards/a/AsajjVentress.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.condition.common.HateCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.BlockedCreatureCount; @@ -42,7 +42,7 @@ public final class AsajjVentress extends CardImpl { BlockedCreatureCount value = new BlockedCreatureCount(); Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true); effect.setText("she gets +1/+1 for each creature blocking her until end of turn"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); // Hate — Whenever Asajj Ventress attacks, if an opponent lost life from a source other than combat damage this turn, target creature blocks this turn if able. Ability ability = new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/a/AshcloudPhoenix.java b/Mage.Sets/src/mage/cards/a/AshcloudPhoenix.java index c63e5d77ab..d4b74755af 100644 --- a/Mage.Sets/src/mage/cards/a/AshcloudPhoenix.java +++ b/Mage.Sets/src/mage/cards/a/AshcloudPhoenix.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; @@ -36,7 +36,7 @@ public final class AshcloudPhoenix extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Ashcloud Phoenix dies, return it to the battlefield face down under your control. - this.addAbility(new DiesTriggeredAbility(new AshcloudPhoenixEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new AshcloudPhoenixEffect())); // Morph {4}{R}{R} this.addAbility(new MorphAbility(this, new ManaCostsImpl<>("{4}{R}{R}"))); diff --git a/Mage.Sets/src/mage/cards/a/AshenSkinZubera.java b/Mage.Sets/src/mage/cards/a/AshenSkinZubera.java index 050b2493a3..79fb5cfd07 100644 --- a/Mage.Sets/src/mage/cards/a/AshenSkinZubera.java +++ b/Mage.Sets/src/mage/cards/a/AshenSkinZubera.java @@ -5,7 +5,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.ZuberasDiedDynamicValue; import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class AshenSkinZubera extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - Ability ability = new DiesTriggeredAbility(new DiscardTargetEffect(ZuberasDiedDynamicValue.instance)); + Ability ability = new DiesSourceTriggeredAbility(new DiscardTargetEffect(ZuberasDiedDynamicValue.instance)); ability.addTarget(new TargetOpponent()); this.addAbility(ability, new ZuberasDiedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java b/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java index fef376a769..e1ee8dde9e 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java +++ b/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java @@ -1,10 +1,13 @@ package mage.cards.a; +import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -17,11 +20,6 @@ import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInHand; import mage.target.common.TargetNonlandPermanent; -import java.util.UUID; - -import mage.MageObjectReference; -import mage.cards.Card; - /** * @author TheElk801 */ @@ -42,7 +40,7 @@ public final class AshiokNightmareMuse extends CardImpl { ability.addTarget(new TargetNonlandPermanent()); this.addAbility(ability); - // −7: You may cast up to three face-up cards your opponents own from exile without paying their mana costs. + // −7: You may cast up to three spells from among face-up cards your opponents own from exile without paying their mana costs. this.addAbility(new LoyaltyAbility(new AshiokNightmareMuseCastEffect(), -7)); } @@ -102,7 +100,7 @@ class AshiokNightmareMuseCastEffect extends OneShotEffect { AshiokNightmareMuseCastEffect() { super(Outcome.Discard); - staticText = "You may cast up to three face-up cards your opponents own from exile without paying their mana costs."; + staticText = "You may cast up to three spells from among face-up cards your opponents own from exile without paying their mana costs."; } private AshiokNightmareMuseCastEffect(final AshiokNightmareMuseCastEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java b/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java index af1641e22a..9188c0d57e 100644 --- a/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java +++ b/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java @@ -1,31 +1,35 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageEverythingEffect; +import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.common.AbilityResolutionCountHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.watchers.common.AbilityResolvedWatcher; + +import java.util.UUID; /** - * - * @author LevelX2 + * @author TheElk801 */ public final class AshlingThePilgrim extends CardImpl { public AshlingThePilgrim(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ELEMENTAL); this.subtype.add(SubType.SHAMAN); @@ -34,12 +38,15 @@ public final class AshlingThePilgrim extends CardImpl { this.toughness = new MageInt(1); // {1}{R}: Put a +1/+1 counter on Ashling the Pilgrim. If this is the third time this ability has resolved this turn, remove all +1/+1 counters from Ashling the Pilgrim, and it deals that much damage to each creature and each player. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(), true), new ManaCostsImpl("{1}{R}")); - ability.addEffect(new AshlingThePilgrimEffect()); - this.addAbility(ability); + Ability ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{1}{R}") + ); + ability.addEffect(new IfAbilityHasResolvedXTimesEffect(Outcome.Damage, 3, new AshlingThePilgrimEffect())); + ability.addHint(AbilityResolutionCountHint.instance); + this.addAbility(ability, new AbilityResolvedWatcher()); } - public AshlingThePilgrim(final AshlingThePilgrim card) { + private AshlingThePilgrim(final AshlingThePilgrim card) { super(card); } @@ -51,19 +58,13 @@ public final class AshlingThePilgrim extends CardImpl { class AshlingThePilgrimEffect extends OneShotEffect { - static class ActivationInfo { - public int zoneChangeCounter; - public int turn; - public int activations; - } - - public AshlingThePilgrimEffect() { + AshlingThePilgrimEffect() { super(Outcome.Damage); - this.staticText = "If this is the third time this ability has resolved this turn, remove all +1/+1 counters from {this}, and it deals that much damage to each creature and each player"; + this.staticText = "remove all +1/+1 counters from {this}, and it deals that much damage to each creature and each player"; } - public AshlingThePilgrimEffect(final AshlingThePilgrimEffect effect) { - super(effect); + private AshlingThePilgrimEffect(final AshlingThePilgrimEffect effect) { + super(effect); } @Override @@ -73,34 +74,15 @@ class AshlingThePilgrimEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - ActivationInfo info; - Object object = game.getState().getValue(source.getSourceId() + "ActivationInfo"); - if (object instanceof ActivationInfo) { - info = (ActivationInfo) object; - if (info.turn != game.getTurnNum() || sourcePermanent.getZoneChangeCounter(game) != info.zoneChangeCounter) { - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - info.activations = 0; - } - } else { - info = new ActivationInfo(); - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - game.getState().setValue(source.getSourceId() + "ActivationInfo", info); + if (sourcePermanent != null) { + int counters = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); + if (counters < 1) { + return false; } - info.activations++; - if (info.activations == 3) { - int damage = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); - if (damage > 0) { - sourcePermanent.removeCounters(CounterType.P1P1.getName(), damage, game); - return new DamageEverythingEffect(damage, new FilterCreaturePermanent()).apply(game, source); - } - } - return true; + sourcePermanent.removeCounters(CounterType.P1P1.createInstance(counters), game); + return new DamageEverythingEffect(counters, StaticFilters.FILTER_PERMANENT_CREATURE).apply(game, source); } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/a/AshlingsPrerogative.java b/Mage.Sets/src/mage/cards/a/AshlingsPrerogative.java index 89361ee164..7db112e08d 100644 --- a/Mage.Sets/src/mage/cards/a/AshlingsPrerogative.java +++ b/Mage.Sets/src/mage/cards/a/AshlingsPrerogative.java @@ -31,7 +31,7 @@ public final class AshlingsPrerogative extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{R}"); // As Ashling's Prerogative enters the battlefield, choose odd or even. - this.addAbility(new EntersBattlefieldAbility(new ChooseModeEffect("Odd or even?", "Odd", "Even"), null, "As {this} enters the battlefield, choose odd or even. (Zero is even.)", "")); + this.addAbility(new EntersBattlefieldAbility(new ChooseModeEffect("Odd or even?", "Odd", "Even"), null, "As {this} enters the battlefield, choose odd or even. (Zero is even.)", "")); // Each creature with converted mana cost of the chosen value has haste. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AshlingsPrerogativeCorrectOddityEffect())); diff --git a/Mage.Sets/src/mage/cards/a/AshmouthHound.java b/Mage.Sets/src/mage/cards/a/AshmouthHound.java index 15e10029c7..e3e5ed4fb4 100644 --- a/Mage.Sets/src/mage/cards/a/AshmouthHound.java +++ b/Mage.Sets/src/mage/cards/a/AshmouthHound.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -19,13 +19,13 @@ public final class AshmouthHound extends CardImpl { public AshmouthHound(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.ELEMENTAL); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(1); // Whenever Ashmouth Hound blocks or becomes blocked by a creature, Ashmouth Hound deals 1 damage to that creature. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new DamageTargetEffect(1, true, "that creature"), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(1, true, "that creature"), false)); } public AshmouthHound(final AshmouthHound card) { diff --git a/Mage.Sets/src/mage/cards/a/AshnodsAltar.java b/Mage.Sets/src/mage/cards/a/AshnodsAltar.java index 9d2f503df6..046ecfe89c 100644 --- a/Mage.Sets/src/mage/cards/a/AshnodsAltar.java +++ b/Mage.Sets/src/mage/cards/a/AshnodsAltar.java @@ -4,6 +4,8 @@ package mage.cards.a; import java.util.UUID; import mage.Mana; import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; +import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +25,9 @@ public final class AshnodsAltar extends CardImpl { // Sacrifice a creature: Add {C}{C}. SacrificeTargetCost cost = new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT)); - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(2), cost)); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, + new BasicManaEffect(Mana.ColorlessMana(2), CreaturesYouControlCount.instance), + cost)); } public AshnodsAltar(final AshnodsAltar card) { diff --git a/Mage.Sets/src/mage/cards/a/AssassinsTrophy.java b/Mage.Sets/src/mage/cards/a/AssassinsTrophy.java index 829c6253fa..5bafb3999a 100644 --- a/Mage.Sets/src/mage/cards/a/AssassinsTrophy.java +++ b/Mage.Sets/src/mage/cards/a/AssassinsTrophy.java @@ -70,7 +70,7 @@ class AssassinsTrophyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player controller = game.getPlayer(permanent.getControllerId()); if (controller != null) { diff --git a/Mage.Sets/src/mage/cards/a/AssembledAlphas.java b/Mage.Sets/src/mage/cards/a/AssembledAlphas.java index 95a23a8ece..03807132bb 100644 --- a/Mage.Sets/src/mage/cards/a/AssembledAlphas.java +++ b/Mage.Sets/src/mage/cards/a/AssembledAlphas.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetControllerEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -27,7 +27,7 @@ public final class AssembledAlphas extends CardImpl { this.toughness = new MageInt(5); // Whenever Assembled Alphas blocks or becomes blocked by a creature, Assembled Alphas deals 3 damage to that creature and 3 damage to that creature's controller. - Ability ability = new BlocksOrBecomesBlockedTriggeredAbility( + Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility( new DamageTargetEffect(3, true, "that creature"), StaticFilters.FILTER_PERMANENT_CREATURE, false, null, true); Effect effect = new DamageTargetControllerEffect(3); diff --git a/Mage.Sets/src/mage/cards/a/AstralDrift.java b/Mage.Sets/src/mage/cards/a/AstralDrift.java index 82b191e991..7879752767 100644 --- a/Mage.Sets/src/mage/cards/a/AstralDrift.java +++ b/Mage.Sets/src/mage/cards/a/AstralDrift.java @@ -124,7 +124,7 @@ class AstralDriftEffect extends OneShotEffect { return true; } //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTarget(permanent.getId(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/a/AstralSlide.java b/Mage.Sets/src/mage/cards/a/AstralSlide.java index f5efb9a9ec..0f0db9bf8e 100644 --- a/Mage.Sets/src/mage/cards/a/AstralSlide.java +++ b/Mage.Sets/src/mage/cards/a/AstralSlide.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.CycleAllTriggeredAbility; @@ -19,8 +17,9 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author Plopman */ public final class AstralSlide extends CardImpl { @@ -48,7 +47,7 @@ class AstralSlideEffect extends OneShotEffect { public AstralSlideEffect() { super(Outcome.Detriment); - staticText = "exile target creature. If you do, eturn that card to the battlefield under its owner's control at the beginning of the next end step"; + staticText = "exile target creature. If you do, return that card to the battlefield under its owner's control at the beginning of the next end step"; } public AstralSlideEffect(final AstralSlideEffect effect) { @@ -65,7 +64,7 @@ class AstralSlideEffect extends OneShotEffect { UUID exileId = UUID.randomUUID(); if (controller.moveCardsToExile(permanent, source, game, true, exileId, sourceObject.getIdName())) { //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTarget(permanent.getId(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java b/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java index ca986d1948..0222b63049 100644 --- a/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java +++ b/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java @@ -2,7 +2,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; @@ -38,7 +38,7 @@ public final class AttendantOfVraska extends CardImpl { // When Attendant of Vraska dies, if you control a Vraska planeswalker, you gain life equal to Attendant of Vraska's power. this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new DiesTriggeredAbility(new GainLifeEffect( + new DiesSourceTriggeredAbility(new GainLifeEffect( new SourcePermanentPowerCount() ), false), new PermanentsOnTheBattlefieldCondition(filter), "When {this} dies, if you control a Vraska planeswalker, " diff --git a/Mage.Sets/src/mage/cards/a/AuraOfSilence.java b/Mage.Sets/src/mage/cards/a/AuraOfSilence.java index 78336fd491..23ae3bcc87 100644 --- a/Mage.Sets/src/mage/cards/a/AuraOfSilence.java +++ b/Mage.Sets/src/mage/cards/a/AuraOfSilence.java @@ -1,35 +1,42 @@ - package mage.cards.a; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.cards.Card; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.game.Game; +import mage.filter.predicate.Predicates; import mage.target.TargetPermanent; -import mage.util.CardUtil; import java.util.UUID; /** - * * @author emerald000 */ public final class AuraOfSilence extends CardImpl { + + private static final FilterCard filter = new FilterCard("Artifact and enchantment spells"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.ENCHANTMENT.getPredicate())); + } + public AuraOfSilence(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); // Artifact and enchantment spells your opponents cast cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AuraOfSilenceCostModificationEffect())); - + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(2, filter, TargetController.OPPONENT))); + // Sacrifice Aura of Silence: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); @@ -44,41 +51,4 @@ public final class AuraOfSilence extends CardImpl { public AuraOfSilence copy() { return new AuraOfSilence(this); } -} - -class AuraOfSilenceCostModificationEffect extends CostModificationEffectImpl { - - AuraOfSilenceCostModificationEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Artifact and enchantment spells your opponents cast cost {2} more to cast"; - } - - AuraOfSilenceCostModificationEffect(AuraOfSilenceCostModificationEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - Card card = game.getCard(abilityToModify.getSourceId()); - if (card != null && (card.isArtifact() || card.isEnchantment())) { - return true; - } - } - } - return false; - } - - @Override - public AuraOfSilenceCostModificationEffect copy() { - return new AuraOfSilenceCostModificationEffect(this); - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AuraShards.java b/Mage.Sets/src/mage/cards/a/AuraShards.java index 029b8b1c51..e577dd7c7d 100644 --- a/Mage.Sets/src/mage/cards/a/AuraShards.java +++ b/Mage.Sets/src/mage/cards/a/AuraShards.java @@ -1,4 +1,3 @@ - package mage.cards.a; import mage.abilities.Ability; @@ -22,8 +21,12 @@ public final class AuraShards extends CardImpl { public AuraShards(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}{W}"); - // Whenever a creature enters the battlefield under your control, you may destroy target artifact or enchantment. - Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), StaticFilters.FILTER_PERMANENT_CREATURE, true, "Whenever a creature enters the battlefield under your control, you may destroy target artifact or enchantment"); + // Whenever a creature enters the battlefield under your control, + // you may destroy target artifact or enchantment. + Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, + new DestroyTargetEffect(), StaticFilters.FILTER_PERMANENT_CREATURE, true, + "Whenever a creature enters the battlefield under your control, " + + "you may destroy target artifact or enchantment"); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AuraThief.java b/Mage.Sets/src/mage/cards/a/AuraThief.java index 9a148b7e92..d44d77becb 100644 --- a/Mage.Sets/src/mage/cards/a/AuraThief.java +++ b/Mage.Sets/src/mage/cards/a/AuraThief.java @@ -7,7 +7,7 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; @@ -43,7 +43,7 @@ public final class AuraThief extends CardImpl { // When Aura Thief dies, you gain control of all enchantments. You don't get // to move Auras. - this.addAbility(new DiesTriggeredAbility(new AuraThiefDiesTriggeredEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new AuraThiefDiesTriggeredEffect())); } public AuraThief(final AuraThief card) { diff --git a/Mage.Sets/src/mage/cards/a/AuriokEdgewright.java b/Mage.Sets/src/mage/cards/a/AuriokEdgewright.java index a38161a38a..4d03eb2490 100644 --- a/Mage.Sets/src/mage/cards/a/AuriokEdgewright.java +++ b/Mage.Sets/src/mage/cards/a/AuriokEdgewright.java @@ -1,23 +1,20 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.DoubleStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; + +import java.util.UUID; /** - * * @author maurer.it_at_gmail.com */ public final class AuriokEdgewright extends CardImpl { @@ -25,7 +22,7 @@ public final class AuriokEdgewright extends CardImpl { protected static String effectText = "Metalcraft — Auriok Edgewright has double strike as long as you control three or more artifacts."; public AuriokEdgewright(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); @@ -33,7 +30,12 @@ public final class AuriokEdgewright extends CardImpl { this.toughness = new MageInt(2); ContinuousEffect effect = new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect, MetalcraftCondition.instance, effectText))); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new ConditionalContinuousEffect(effect, MetalcraftCondition.instance, effectText)) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); } public AuriokEdgewright(final AuriokEdgewright card) { diff --git a/Mage.Sets/src/mage/cards/a/AuriokSunchaser.java b/Mage.Sets/src/mage/cards/a/AuriokSunchaser.java index 249c912b49..59ce1fcd79 100644 --- a/Mage.Sets/src/mage/cards/a/AuriokSunchaser.java +++ b/Mage.Sets/src/mage/cards/a/AuriokSunchaser.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; @@ -9,16 +7,15 @@ import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; + +import java.util.UUID; /** - * * @author maurer.it_at_gmail.com */ public final class AuriokSunchaser extends CardImpl { @@ -27,7 +24,7 @@ public final class AuriokSunchaser extends CardImpl { protected static String effect2Text = "Metalcraft — As long as you control three or more artifacts, Auriok Sunchaser has flying"; public AuriokSunchaser(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); @@ -35,9 +32,18 @@ public final class AuriokSunchaser extends CardImpl { this.toughness = new MageInt(1); ContinuousEffect effect1 = new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect1, MetalcraftCondition.instance, effect1Text))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(effect1, MetalcraftCondition.instance, effect1Text)) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); + ContinuousEffect effect2 = new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect2, MetalcraftCondition.instance, effect2Text))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(effect2, MetalcraftCondition.instance, effect2Text)) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); } public AuriokSunchaser(final AuriokSunchaser card) { diff --git a/Mage.Sets/src/mage/cards/a/AuspiciousAncestor.java b/Mage.Sets/src/mage/cards/a/AuspiciousAncestor.java index 505daa753c..09612cb5c4 100644 --- a/Mage.Sets/src/mage/cards/a/AuspiciousAncestor.java +++ b/Mage.Sets/src/mage/cards/a/AuspiciousAncestor.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SpellCastAllTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DoIfCostPaid; @@ -36,7 +36,7 @@ public final class AuspiciousAncestor extends CardImpl { this.toughness = new MageInt(3); // When Auspicious Ancestor dies, you gain 3 life. - this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(3), false)); + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(3), false)); // Whenever a player casts a white spell, you may pay {1}. If you do, you gain 1 life. this.addAbility(new SpellCastAllTriggeredAbility(new DoIfCostPaid(new GainLifeEffect(1), new ManaCostsImpl("{1}")), filter, true)); } diff --git a/Mage.Sets/src/mage/cards/a/AuspiciousStarrix.java b/Mage.Sets/src/mage/cards/a/AuspiciousStarrix.java new file mode 100644 index 0000000000..e183749608 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AuspiciousStarrix.java @@ -0,0 +1,90 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.dynamicvalue.common.SourceMutatedCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AuspiciousStarrix extends CardImpl { + + public AuspiciousStarrix(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.ELK); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Mutate {5}{G} + this.addAbility(new MutateAbility(this, "{5}{G}")); + + // Whenever this creature mutates, exile cards from the top of your library until you exile X permanent cards, where X is the number of times this creature has mutated. Put those permanent cards onto the battlefield. + this.addAbility(new MutatesSourceTriggeredAbility(new AuspiciousStarrixEffect())); + } + + private AuspiciousStarrix(final AuspiciousStarrix card) { + super(card); + } + + @Override + public AuspiciousStarrix copy() { + return new AuspiciousStarrix(this); + } +} + +class AuspiciousStarrixEffect extends OneShotEffect { + + AuspiciousStarrixEffect() { + super(Outcome.Benefit); + staticText = "exile cards from the top of your library until you exile X permanent cards, " + + "where X is the number of times this creature has mutated. " + + "Put those permanent cards onto the battlefield."; + } + + private AuspiciousStarrixEffect(final AuspiciousStarrixEffect effect) { + super(effect); + } + + @Override + public AuspiciousStarrixEffect copy() { + return new AuspiciousStarrixEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int xValue = SourceMutatedCount.instance.calculate(game, source, this); + int count = 0; + Cards toExile = new CardsImpl(); + Cards toBattlefield = new CardsImpl(); + for (Card card : player.getLibrary().getCards(game)) { + if (card != null && card.isPermanent()) { + toBattlefield.add(card); + count++; + } + toExile.add(card); + if (count >= xValue) { + break; + } + } + player.moveCards(toExile, Zone.EXILED, source, game); + return player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfFury.java b/Mage.Sets/src/mage/cards/a/AvatarOfFury.java index 9153c0a4f9..6489605608 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfFury.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfFury.java @@ -1,33 +1,41 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.CostModificationType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; import mage.game.Game; -import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class AvatarOfFury extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("artifact, creature, or enchantment"); + + static { + filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate(), + CardType.ENCHANTMENT.getPredicate())); + } + public AvatarOfFury(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{R}{R}"); this.subtype.add(SubType.AVATAR); @@ -36,9 +44,14 @@ public final class AvatarOfFury extends CardImpl { this.toughness = new MageInt(6); // If an opponent controls seven or more lands, Avatar of Fury costs {6} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfFuryAdjustingCostsEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, AvatarOfFuryCondition.instance) + .setText("if an opponent controls seven or more lands, Avatar of Fury costs {6} less to cast")) + .addHint(new ConditionHint(AvatarOfFuryCondition.instance, "Opponent controls seven or more lands")) + ); + // Flying this.addAbility(FlyingAbility.getInstance()); + // {R}: Avatar of Fury gets +1/+0 until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}"))); } @@ -53,39 +66,22 @@ public final class AvatarOfFury extends CardImpl { } } -class AvatarOfFuryAdjustingCostsEffect extends CostModificationEffectImpl { +enum AvatarOfFuryCondition implements Condition { - AvatarOfFuryAdjustingCostsEffect() { - super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "If an opponent controls seven or more lands, {this} costs {6} less to cast"; - } - - AvatarOfFuryAdjustingCostsEffect(AvatarOfFuryAdjustingCostsEffect effect) { - super(effect); - } + instance; @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - CardUtil.reduceCost(abilityToModify, 6); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify.getSourceId().equals(source.getSourceId()) - && (abilityToModify instanceof SpellAbility)) { - for (UUID playerId : game.getOpponents(abilityToModify.getControllerId())) { - if (game.getBattlefield().countAll(StaticFilters.FILTER_LAND, playerId, game) > 6) { - return true; - } + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getOpponents(source.getControllerId())) { + if (game.getBattlefield().countAll(StaticFilters.FILTER_LAND, playerId, game) > 6) { + return true; } } return false; } @Override - public AvatarOfFuryAdjustingCostsEffect copy() { - return new AvatarOfFuryAdjustingCostsEffect(this); + public String toString() { + return "an opponent controls seven or more lands"; } - } diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java b/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java index cd41b415dd..0bee9d45c8 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java @@ -4,8 +4,9 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.OpponentsCount; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.cost.SpellCostReductionSourceForOpponentsEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -35,7 +36,8 @@ public final class AvatarOfGrowth extends CardImpl { this.toughness = new MageInt(4); // This spell costs {1} less to cast for each opponent you have. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceForOpponentsEffect("This spell costs {1} less to cast for each opponent you have"))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, OpponentsCount.instance) + .setText("This spell costs {1} less to cast for each opponent you have"))); // Trample this.addAbility(TrampleAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfHope.java b/Mage.Sets/src/mage/cards/a/AvatarOfHope.java index d1e82950fe..08957746ee 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfHope.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfHope.java @@ -1,22 +1,24 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.effects.common.combat.CanBlockAdditionalCreatureEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; import mage.game.Game; import mage.players.Player; -import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author Plopman */ public final class AvatarOfHope extends CardImpl { @@ -29,9 +31,14 @@ public final class AvatarOfHope extends CardImpl { this.toughness = new MageInt(9); // If you have 3 or less life, Avatar of Hope costs {6} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfHopeAdjustingCostsEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, AvatarOfHopeCondition.instance) + .setText("if you have 3 or less life, Avatar of Hope costs {6} less to cast")) + .addHint(new ConditionHint(AvatarOfHopeCondition.instance)) + ); + // Flying this.addAbility(FlyingAbility.getInstance()); + // Avatar of Hope can block any number of creatures. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CanBlockAdditionalCreatureEffect(0))); } @@ -46,39 +53,21 @@ public final class AvatarOfHope extends CardImpl { } } -class AvatarOfHopeAdjustingCostsEffect extends CostModificationEffectImpl { +enum AvatarOfHopeCondition implements Condition { - AvatarOfHopeAdjustingCostsEffect() { - super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "If you have 3 or less life, {this} costs {6} less to cast"; - } - - AvatarOfHopeAdjustingCostsEffect(AvatarOfHopeAdjustingCostsEffect effect) { - super(effect); - } + instance; @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - CardUtil.reduceCost(abilityToModify, 6); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify.getSourceId().equals(source.getSourceId()) - && (abilityToModify instanceof SpellAbility)) { - Player player = game.getPlayer(abilityToModify.getControllerId()); - if (player != null && player.getLife() < 4) { - return true; - } + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null && player.getLife() <= 3) { + return true; } - return false; } @Override - public AvatarOfHopeAdjustingCostsEffect copy() { - return new AvatarOfHopeAdjustingCostsEffect(this); + public String toString() { + return "you have 3 or less life"; } - } diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfMight.java b/Mage.Sets/src/mage/cards/a/AvatarOfMight.java index 8c127b2a30..d051a3174d 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfMight.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfMight.java @@ -1,27 +1,30 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.Mana; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AvatarOfMight extends CardImpl { + private static final Condition condition = new AvatarOfMightCondition(); + public AvatarOfMight(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{G}{G}"); this.subtype.add(SubType.AVATAR); @@ -29,7 +32,10 @@ public final class AvatarOfMight extends CardImpl { this.toughness = new MageInt(8); // If an opponent controls at least four more creatures than you, Avatar of Might costs {6} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new AvatarOfMightCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, condition) + .setText("If an opponent controls at least four more creatures than you, {this} costs {6} less to cast")) + .addHint(new ConditionHint(condition, "Opponent controls at least four more creatures than you")) + ); // Trample this.addAbility(TrampleAbility.getInstance()); @@ -45,49 +51,17 @@ public final class AvatarOfMight extends CardImpl { } } -class AvatarOfMightCostReductionEffect extends CostModificationEffectImpl { - - AvatarOfMightCostReductionEffect() { - super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "If an opponent controls at least four more creatures than you, {this} costs {6} less to cast"; - } - - AvatarOfMightCostReductionEffect(final AvatarOfMightCostReductionEffect effect) { - super(effect); - } +class AvatarOfMightCondition implements Condition { @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - Mana mana = spellAbility.getManaCostsToPay().getMana(); - if (mana.getGeneric() > 0) { - int newCount = mana.getGeneric() - 6; - if (newCount < 0) { - newCount = 0; - } - mana.setGeneric(newCount); - spellAbility.getManaCostsToPay().load(mana.toString()); - return true; - } - return false; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) { - int creatures = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game); - for (UUID playerId : game.getOpponents(source.getControllerId())) { - Player opponent = game.getPlayer(playerId); - if (opponent != null && game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, opponent.getId(), game) >= creatures + 4) { - return true; - } + public boolean apply(Game game, Ability source) { + int creatures = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(playerId); + if (opponent != null && game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, opponent.getId(), game) >= creatures + 4) { + return true; } } return false; } - - @Override - public AvatarOfMightCostReductionEffect copy() { - return new AvatarOfMightCostReductionEffect(this); - } } diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfWill.java b/Mage.Sets/src/mage/cards/a/AvatarOfWill.java index 89651d2328..d67fb66059 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfWill.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfWill.java @@ -1,22 +1,20 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.Mana; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.condition.common.OpponentHasNoCardsInHandCondition; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.players.Player; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class AvatarOfWill extends CardImpl { @@ -28,7 +26,10 @@ public final class AvatarOfWill extends CardImpl { this.toughness = new MageInt(6); // If an opponent has no cards in hand, Avatar of Will costs {6} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new AvatarOfWillCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, OpponentHasNoCardsInHandCondition.instance) + .setText("If an opponent has no cards in hand, Avatar of Will costs {6} less to cast") + ).addHint(new ConditionHint(OpponentHasNoCardsInHandCondition.instance, "Opponent has no cards in hand")) + ); // Flying this.addAbility(FlyingAbility.getInstance()); @@ -43,49 +44,3 @@ public final class AvatarOfWill extends CardImpl { return new AvatarOfWill(this); } } - -class AvatarOfWillCostReductionEffect extends CostModificationEffectImpl { - - AvatarOfWillCostReductionEffect() { - super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "If an opponent has no cards in hand, {this} costs {6} less to cast"; - } - - AvatarOfWillCostReductionEffect(final AvatarOfWillCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - Mana mana = spellAbility.getManaCostsToPay().getMana(); - if (mana.getGeneric() > 0) { - int newCount = mana.getGeneric() - 6; - if (newCount < 0) { - newCount = 0; - } - mana.setGeneric(newCount); - spellAbility.getManaCostsToPay().load(mana.toString()); - return true; - } - return false; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify.getSourceId().equals(source.getSourceId())) { - for (UUID playerId : game.getOpponents(source.getControllerId())) { - Player opponent = game.getPlayer(playerId); - if (opponent != null && opponent.getHand().isEmpty()) { - return true; - } - } - } - return false; - } - - @Override - public AvatarOfWillCostReductionEffect copy() { - return new AvatarOfWillCostReductionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java b/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java index a8f9ca1893..70911593e4 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java @@ -1,30 +1,36 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.Mana; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInAllGraveyardsCount; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.FearAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author emerald000 */ public final class AvatarOfWoe extends CardImpl { + protected static final DynamicValue graveyardCount = new CardsInAllGraveyardsCount(StaticFilters.FILTER_CARD_CREATURE); + private static final Condition condition = new AvatarOfWoeCondition(); + public AvatarOfWoe(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{B}{B}"); this.subtype.add(SubType.AVATAR); @@ -32,12 +38,15 @@ public final class AvatarOfWoe extends CardImpl { this.toughness = new MageInt(5); // If there are ten or more creature cards total in all graveyards, Avatar of Woe costs {6} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new AvatarOfWoeCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, condition) + .setText("If there are ten or more creature cards total in all graveyards, {this} costs {6} less to cast")) + .addHint(new ValueHint("Creature cards in all graveyards", graveyardCount)) + ); // Fear this.addAbility(FearAbility.getInstance()); - // {tap}: Destroy target creature. It can't be regenerated. + // {T}: Destroy target creature. It can't be regenerated. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(true), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); @@ -53,42 +62,13 @@ public final class AvatarOfWoe extends CardImpl { } } -class AvatarOfWoeCostReductionEffect extends CostModificationEffectImpl { - - AvatarOfWoeCostReductionEffect() { - super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "If there are ten or more creature cards total in all graveyards, {this} costs {6} less to cast"; - } - - AvatarOfWoeCostReductionEffect(final AvatarOfWoeCostReductionEffect effect) { - super(effect); - } +class AvatarOfWoeCondition implements Condition { @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - Mana mana = spellAbility.getManaCostsToPay().getMana(); - if (mana.getGeneric() > 0) { - int newCount = mana.getGeneric() - 6; - if (newCount < 0) { - newCount = 0; - } - mana.setGeneric(newCount); - spellAbility.getManaCostsToPay().load(mana.toString()); + public boolean apply(Game game, Ability source) { + if (AvatarOfWoe.graveyardCount.calculate(game, source, null) >= 10) { return true; } return false; } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify.getSourceId().equals(source.getSourceId()) - && (abilityToModify instanceof SpellAbility) - && new CardsInAllGraveyardsCount(StaticFilters.FILTER_CARD_CREATURE).calculate(game, source, this) >= 10; - } - - @Override - public AvatarOfWoeCostReductionEffect copy() { - return new AvatarOfWoeCostReductionEffect(this); - } } diff --git a/Mage.Sets/src/mage/cards/a/AvenFisher.java b/Mage.Sets/src/mage/cards/a/AvenFisher.java index 8ce46a7e9f..7842c555fe 100644 --- a/Mage.Sets/src/mage/cards/a/AvenFisher.java +++ b/Mage.Sets/src/mage/cards/a/AvenFisher.java @@ -3,7 +3,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class AvenFisher extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); } public AvenFisher(final AvenFisher card) { diff --git a/Mage.Sets/src/mage/cards/a/AvenGagglemaster.java b/Mage.Sets/src/mage/cards/a/AvenGagglemaster.java new file mode 100644 index 0000000000..0ce91fdf8e --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AvenGagglemaster.java @@ -0,0 +1,56 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AvenGagglemaster extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 2); + + public AvenGagglemaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Aven Gagglemaster enters the battlefield, you gain 2 life for each creature you control with flying. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(xValue) + .setText("you gain 2 life for each creature you control with flying"))); + } + + private AvenGagglemaster(final AvenGagglemaster card) { + super(card); + } + + @Override + public AvenGagglemaster copy() { + return new AvenGagglemaster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AvenMimeomancer.java b/Mage.Sets/src/mage/cards/a/AvenMimeomancer.java index dcb30c301d..112bc2c041 100644 --- a/Mage.Sets/src/mage/cards/a/AvenMimeomancer.java +++ b/Mage.Sets/src/mage/cards/a/AvenMimeomancer.java @@ -71,7 +71,7 @@ class AvenEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - Permanent target = game.getPermanent(source.getFirstTarget()); + Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); if (target != null) { target.getPower().setValue(3); target.getToughness().setValue(1); @@ -82,7 +82,7 @@ class AvenEffect extends ContinuousEffectImpl { @Override public boolean isInactive(Ability source, Game game) { - Permanent creature = game.getPermanent(this.targetPointer.getFirst(game, source)); + Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); if (creature != null && creature.getCounters(game).getCount(CounterType.FEATHER) < 1) { return true; } @@ -112,7 +112,7 @@ class AvenEffect2 extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - Permanent target = game.getPermanent(source.getFirstTarget()); + Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); if (target != null) { if (!target.getAbilities().contains(FlyingAbility.getInstance())) { target.addAbility(FlyingAbility.getInstance(), source.getSourceId(), game); @@ -124,7 +124,7 @@ class AvenEffect2 extends ContinuousEffectImpl { @Override public boolean isInactive(Ability source, Game game) { - Permanent creature = game.getPermanent(this.targetPointer.getFirst(game, source)); + Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); if (creature != null && creature.getCounters(game).getCount(CounterType.FEATHER) < 1) { return true; } diff --git a/Mage.Sets/src/mage/cards/a/AvengerEnDal.java b/Mage.Sets/src/mage/cards/a/AvengerEnDal.java index 78d78ccd1e..aba9f96d53 100644 --- a/Mage.Sets/src/mage/cards/a/AvengerEnDal.java +++ b/Mage.Sets/src/mage/cards/a/AvengerEnDal.java @@ -70,7 +70,7 @@ class AvengerEnDalEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/a/AvengingAngel.java b/Mage.Sets/src/mage/cards/a/AvengingAngel.java index 2397ba3109..5769a74414 100644 --- a/Mage.Sets/src/mage/cards/a/AvengingAngel.java +++ b/Mage.Sets/src/mage/cards/a/AvengingAngel.java @@ -4,7 +4,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; @@ -33,7 +33,7 @@ public final class AvengingAngel extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Avenging Angel dies, you may put it on top of its owner's library. - this.addAbility(new DiesTriggeredAbility(new AvengingAngelEffect(), true)); + this.addAbility(new DiesSourceTriggeredAbility(new AvengingAngelEffect(), true)); } public AvengingAngel(final AvengingAngel card) { diff --git a/Mage.Sets/src/mage/cards/a/AvengingHuntbonder.java b/Mage.Sets/src/mage/cards/a/AvengingHuntbonder.java new file mode 100644 index 0000000000..0867621cbf --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AvengingHuntbonder.java @@ -0,0 +1,59 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AvengingHuntbonder extends CardImpl { + + private static final FilterPermanent filter + = new FilterAttackingCreature("another target attacking creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public AvengingHuntbonder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // Whenever Avenging Huntbonder attacks, put a double strike counter on another target attacking creature. + Ability ability = new AttacksTriggeredAbility( + new AddCountersTargetEffect(CounterType.DOUBLE_STRIKE.createInstance()), false + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private AvengingHuntbonder(final AvengingHuntbonder card) { + super(card); + } + + @Override + public AvengingHuntbonder copy() { + return new AvengingHuntbonder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AvianOddity.java b/Mage.Sets/src/mage/cards/a/AvianOddity.java new file mode 100644 index 0000000000..1fd336ea1a --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AvianOddity.java @@ -0,0 +1,51 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AvianOddity extends CardImpl { + + public AvianOddity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.BIRD); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Cycling {2}{U} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}{U}"))); + + // When you cycle Avian Oddity, put a flying counter on target creature you control. + Ability ability = new CycleTriggeredAbility(new AddCountersTargetEffect(CounterType.FLYING.createInstance())); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private AvianOddity(final AvianOddity card) { + super(card); + } + + @Override + public AvianOddity copy() { + return new AvianOddity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AwakenerDruid.java b/Mage.Sets/src/mage/cards/a/AwakenerDruid.java index e2fa750bf9..a14bb44b09 100644 --- a/Mage.Sets/src/mage/cards/a/AwakenerDruid.java +++ b/Mage.Sets/src/mage/cards/a/AwakenerDruid.java @@ -96,6 +96,7 @@ class AwakenerDruidToken extends TokenImpl { super(token); } + @Override public AwakenerDruidToken copy() { return new AwakenerDruidToken(this); } diff --git a/Mage.Sets/src/mage/cards/a/AwesomePresence.java b/Mage.Sets/src/mage/cards/a/AwesomePresence.java index 2d45ef5aed..732939c93c 100644 --- a/Mage.Sets/src/mage/cards/a/AwesomePresence.java +++ b/Mage.Sets/src/mage/cards/a/AwesomePresence.java @@ -1,94 +1,94 @@ -package mage.cards.a; - -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.PayCostToAttackBlockEffectImpl; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.TargetPermanent; -import mage.target.common.TargetCreaturePermanent; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class AwesomePresence extends CardImpl { - - public AwesomePresence(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // Enchanted creature can't be blocked unless defending player pays {3} for each creature they control that's blocking it. - this.addAbility(new SimpleStaticAbility(new AwesomePresenceRestrictionEffect(new ManaCostsImpl("{3}")))); - - } - - private AwesomePresence(final AwesomePresence card) { - super(card); - } - - @Override - public AwesomePresence copy() { - return new AwesomePresence(this); - } -} - -class AwesomePresenceRestrictionEffect extends PayCostToAttackBlockEffectImpl { - - public AwesomePresenceRestrictionEffect(ManaCosts manaCosts) { - super(Duration.WhileOnBattlefield, Outcome.Neutral, RestrictType.BLOCK, manaCosts); - staticText = "Enchanted creature" - + " can't be blocked " - + "unless defending player pays " - + (manaCosts == null ? "" : manaCosts.getText() - + " for each creature they control that's blocking it"); - } - - public AwesomePresenceRestrictionEffect(AwesomePresenceRestrictionEffect effect) { - super(effect); - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Permanent blockingCreature = game.getPermanent(event.getSourceId()); - Permanent enchantedAttackingCreature = game.getPermanent(event.getTargetId()); - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (blockingCreature != null - && enchantedAttackingCreature != null - && enchantment != null - && enchantment.isAttachedTo(enchantedAttackingCreature.getId())) { - Player defendingPlayer = game.getPlayer(blockingCreature.getControllerId()); - if (defendingPlayer != null) { - return !manaCosts.canPay(source, source.getSourceId(), defendingPlayer.getId(), game) - || !manaCosts.pay(source, game, source.getSourceId(), defendingPlayer.getId(), false); - } - } - return true; - } - - @Override - public AwesomePresenceRestrictionEffect copy() { - return new AwesomePresenceRestrictionEffect(this); - } -} +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.PayCostToAttackBlockEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class AwesomePresence extends CardImpl { + + public AwesomePresence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature can't be blocked unless defending player pays {3} for each creature they control that's blocking it. + this.addAbility(new SimpleStaticAbility(new AwesomePresenceRestrictionEffect(new ManaCostsImpl("{3}")))); + + } + + private AwesomePresence(final AwesomePresence card) { + super(card); + } + + @Override + public AwesomePresence copy() { + return new AwesomePresence(this); + } +} + +class AwesomePresenceRestrictionEffect extends PayCostToAttackBlockEffectImpl { + + public AwesomePresenceRestrictionEffect(ManaCosts manaCosts) { + super(Duration.WhileOnBattlefield, Outcome.Neutral, RestrictType.BLOCK, manaCosts); + staticText = "Enchanted creature" + + " can't be blocked " + + "unless defending player pays " + + (manaCosts == null ? "" : manaCosts.getText() + + " for each creature they control that's blocking it"); + } + + public AwesomePresenceRestrictionEffect(AwesomePresenceRestrictionEffect effect) { + super(effect); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent blockingCreature = game.getPermanent(event.getSourceId()); + Permanent enchantedAttackingCreature = game.getPermanent(event.getTargetId()); + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (blockingCreature != null + && enchantedAttackingCreature != null + && enchantment != null + && enchantment.isAttachedTo(enchantedAttackingCreature.getId())) { + Player defendingPlayer = game.getPlayer(blockingCreature.getControllerId()); + if (defendingPlayer != null) { + return !manaCosts.canPay(source, source.getSourceId(), defendingPlayer.getId(), game) + || !manaCosts.pay(source, game, source.getSourceId(), defendingPlayer.getId(), false); + } + } + return true; + } + + @Override + public AwesomePresenceRestrictionEffect copy() { + return new AwesomePresenceRestrictionEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java b/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java index b7f6b297ca..0e45252d44 100644 --- a/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java +++ b/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java @@ -3,7 +3,7 @@ package mage.cards.a; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; @@ -20,10 +20,10 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; -import mage.filter.predicate.permanent.AnotherPredicate; /** * @author TheElk801 @@ -31,7 +31,7 @@ import mage.filter.predicate.permanent.AnotherPredicate; public final class AyaraFirstOfLocthwain extends CardImpl { private static final FilterPermanent filter - = new FilterCreaturePermanent("{this} or another black creature"); + = new FilterCreaturePermanent("black creature"); private static final FilterControlledPermanent filter2 = new FilterControlledCreaturePermanent("another black creature"); @@ -51,7 +51,9 @@ public final class AyaraFirstOfLocthwain extends CardImpl { this.toughness = new MageInt(3); // Whenever Ayara, First of Locthwain or another black creature enters the battlefield under your control, each opponent loses 1 life and you gain 1 life. - Ability ability = new EntersBattlefieldControlledTriggeredAbility(new LoseLifeOpponentsEffect(1), filter); + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new LoseLifeOpponentsEffect(1), filter, false, true + ); ability.addEffect(new GainLifeEffect(1).concatBy("and")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java b/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java index bff48fbe4a..1b08f1aa6e 100644 --- a/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java +++ b/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java @@ -11,11 +11,10 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetControlledPermanent; @@ -30,12 +29,10 @@ public final class AyulaQueenAmongBears extends CardImpl { private static final FilterPermanent filter = new FilterPermanent(SubType.BEAR, "another Bear"); private static final FilterPermanent filter2 = new FilterPermanent(SubType.BEAR, "Bear"); private static final FilterControlledPermanent filter3 = new FilterControlledPermanent("Bear you controls"); - private static final FilterPermanent filter4 = new FilterCreaturePermanent("creature you don't control"); static { filter.add(AnotherPredicate.instance); filter3.add(SubType.BEAR.getPredicate()); - filter4.add(TargetController.NOT_YOU.getControllerPredicate()); } public AyulaQueenAmongBears(UUID ownerId, CardSetInfo setInfo) { @@ -56,7 +53,7 @@ public final class AyulaQueenAmongBears extends CardImpl { // • Target Bear you control fights target creature you don't control. Mode mode = new Mode(new FightTargetsEffect()); mode.addTarget(new TargetControlledPermanent(filter3)); - mode.addTarget(new TargetPermanent(filter4)); + mode.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); ability.addMode(mode); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java b/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java index 0933ac1fa1..5a69e07060 100644 --- a/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java +++ b/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java @@ -168,7 +168,7 @@ class AzorTheLawbringerAttacksEffect extends OneShotEffect { if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { controller.resetStoredBookmark(game); // otherwise you can undo the payment controller.gainLife(costX, game, source); - controller.drawCards(costX, game); + controller.drawCards(costX, source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/a/AzorsGateway.java b/Mage.Sets/src/mage/cards/a/AzorsGateway.java index 499991f9ea..06a30c883a 100644 --- a/Mage.Sets/src/mage/cards/a/AzorsGateway.java +++ b/Mage.Sets/src/mage/cards/a/AzorsGateway.java @@ -78,7 +78,7 @@ class AzorsGatewayEffect extends OneShotEffect { UUID exileId = CardUtil.getCardExileZoneId(game, source); MageObject sourceObject = source.getSourceObject(game); if (controller != null && exileId != null && sourceObject != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); TargetCardInHand target = new TargetCardInHand(); controller.choose(outcome, target, source.getSourceId(), game); Card cardToExile = game.getCard(target.getFirstTarget()); diff --git a/Mage.Sets/src/mage/cards/a/AzraBladeseeker.java b/Mage.Sets/src/mage/cards/a/AzraBladeseeker.java index 47a9210eaa..3dc89cd111 100644 --- a/Mage.Sets/src/mage/cards/a/AzraBladeseeker.java +++ b/Mage.Sets/src/mage/cards/a/AzraBladeseeker.java @@ -84,7 +84,7 @@ class AzraBladeseekerEffect extends OneShotEffect { } for (PlayerCard playerCard : playerCardList) { if (playerCard.getPlayer().discard(playerCard.getCard(), source, game)) { - playerCard.getPlayer().drawCards(1, game); + playerCard.getPlayer().drawCards(1, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/b/BINGO.java b/Mage.Sets/src/mage/cards/b/BINGO.java index 185acbd19d..d6d4b004a2 100644 --- a/Mage.Sets/src/mage/cards/b/BINGO.java +++ b/Mage.Sets/src/mage/cards/b/BINGO.java @@ -39,7 +39,7 @@ public final class BINGO extends CardImpl { public BINGO(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/b/BackForMore.java b/Mage.Sets/src/mage/cards/b/BackForMore.java new file mode 100644 index 0000000000..26ed102c20 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BackForMore.java @@ -0,0 +1,126 @@ +package mage.cards.b; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BackForMore extends CardImpl { + + public BackForMore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}{G}"); + + // Return target creature card from your graveyard to the battlefield. When you do, it fights up to one target creature you don't control. + this.getSpellAbility().addEffect(new BackForMoreEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD + )); + } + + private BackForMore(final BackForMore card) { + super(card); + } + + @Override + public BackForMore copy() { + return new BackForMore(this); + } +} + +class BackForMoreEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); + + static { + filter.add(TargetController.NOT_YOU.getControllerPredicate()); + } + + BackForMoreEffect() { + super(Outcome.Benefit); + staticText = "Return target creature card from your graveyard to the battlefield. " + + "When you do, it fights up to one target creature you don't control."; + } + + private BackForMoreEffect(final BackForMoreEffect effect) { + super(effect); + } + + @Override + public BackForMoreEffect copy() { + return new BackForMoreEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { + return false; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new BackForMoreDamageEffect(new MageObjectReference(permanent, game)), + false, "it fights up to one target creature you don't control" + ); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } +} + +class BackForMoreDamageEffect extends OneShotEffect { + + private final MageObjectReference mor; + + BackForMoreDamageEffect(MageObjectReference mor) { + super(Outcome.Benefit); + this.mor = mor; + } + + private BackForMoreDamageEffect(final BackForMoreDamageEffect effect) { + super(effect); + mor = effect.mor; + } + + @Override + public BackForMoreDamageEffect copy() { + return new BackForMoreDamageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (mor == null) { + return false; + } + Permanent creature = game.getPermanent(source.getFirstTarget()); + Permanent permanent = mor.getPermanent(game); + if (creature == null || permanent == null) { + return false; + } + return permanent.fight(creature, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BadDeal.java b/Mage.Sets/src/mage/cards/b/BadDeal.java new file mode 100644 index 0000000000..22116bb83c --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BadDeal.java @@ -0,0 +1,38 @@ +package mage.cards.b; + +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeAllPlayersEffect; +import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BadDeal extends CardImpl { + + public BadDeal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}"); + + // You draw two cards and each opponent discards two cards. Each player loses 2 life. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText("You draw two cards")); + this.getSpellAbility().addEffect(new DiscardEachPlayerEffect( + StaticValue.get(2), false, TargetController.OPPONENT + ).concatBy("and")); + this.getSpellAbility().addEffect(new LoseLifeAllPlayersEffect(2).setText("Each player loses 2 life")); + } + + private BadDeal(final BadDeal card) { + super(card); + } + + @Override + public BadDeal copy() { + return new BadDeal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/Balance.java b/Mage.Sets/src/mage/cards/b/Balance.java index 153b24ecc5..2c7ea4d675 100644 --- a/Mage.Sets/src/mage/cards/b/Balance.java +++ b/Mage.Sets/src/mage/cards/b/Balance.java @@ -1,21 +1,10 @@ package mage.cards.b; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.abilities.effects.common.BalanceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterControlledLandPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCardInHand; -import mage.target.common.TargetControlledPermanent; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; /** @@ -39,139 +28,3 @@ public final class Balance extends CardImpl { return new Balance(this); } } - -class BalanceEffect extends OneShotEffect { - - BalanceEffect() { - super(Outcome.Sacrifice); - staticText = "each player chooses a number of lands they control " - + "equal to the number of lands controlled by the player " - + "who controls the fewest, then sacrifices the rest. " - + "Players discard cards and sacrifice creatures the same way"; - } - - BalanceEffect(final BalanceEffect effect) { - super(effect); - } - - @Override - public BalanceEffect copy() { - return new BalanceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - //Lands - int minLand = Integer.MAX_VALUE; - Cards landsToSacrifice = new CardsImpl(); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - int count = game.getBattlefield().countAll(new FilterControlledLandPermanent(), player.getId(), game); - if (count < minLand) { - minLand = count; - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - TargetControlledPermanent target = new TargetControlledPermanent(minLand, minLand, new FilterControlledLandPermanent("lands to keep"), true); - if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), player.getId(), source.getSourceId(), game)) { - if (permanent != null && !target.getTargets().contains(permanent.getId())) { - landsToSacrifice.add(permanent); - } - } - } - } - } - - for (UUID cardId : landsToSacrifice) { - Permanent permanent = game.getPermanent(cardId); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - } - } - - //Creatures - int minCreature = Integer.MAX_VALUE; - Cards creaturesToSacrifice = new CardsImpl(); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - int count = game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), player.getId(), game); - if (count < minCreature) { - minCreature = count; - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - TargetControlledPermanent target = new TargetControlledPermanent(minCreature, minCreature, new FilterControlledCreaturePermanent("creatures to keep"), true); - if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), player.getId(), source.getSourceId(), game)) { - if (permanent != null && !target.getTargets().contains(permanent.getId())) { - creaturesToSacrifice.add(permanent); - } - } - } - } - } - - for (UUID cardId : creaturesToSacrifice) { - Permanent permanent = game.getPermanent(cardId); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - } - } - - //Cards in hand - int minCard = Integer.MAX_VALUE; - Map cardsToDiscard = new HashMap<>(2); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - int count = player.getHand().size(); - if (count < minCard) { - minCard = count; - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Cards cards = new CardsImpl(); - TargetCardInHand target = new TargetCardInHand(minCard, new FilterCard("cards to keep")); - if (target.choose(Outcome.Discard, player.getId(), source.getSourceId(), game)) { - for (Card card : player.getHand().getCards(game)) { - if (card != null && !target.getTargets().contains(card.getId())) { - cards.add(card); - } - } - cardsToDiscard.put(playerId, cards); - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null && cardsToDiscard.get(playerId) != null) { - for (UUID cardId : cardsToDiscard.get(playerId)) { - Card card = game.getCard(cardId); - player.discard(card, source, game); - - } - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BalanceOfPower.java b/Mage.Sets/src/mage/cards/b/BalanceOfPower.java index 7b2b9be3f4..a53f5812eb 100644 --- a/Mage.Sets/src/mage/cards/b/BalanceOfPower.java +++ b/Mage.Sets/src/mage/cards/b/BalanceOfPower.java @@ -59,7 +59,7 @@ class BalanceOfPowerEffect extends OneShotEffect { Player opponent = game.getPlayer(source.getFirstTarget()); if (opponent != null && player != null && opponent.getHand().size() > player.getHand().size()) { - player.drawCards(opponent.getHand().size() - player.getHand().size(), game); + player.drawCards(opponent.getHand().size() - player.getHand().size(), source.getSourceId(), game); return true; } diff --git a/Mage.Sets/src/mage/cards/b/BalancingAct.java b/Mage.Sets/src/mage/cards/b/BalancingAct.java index 2052ec14e8..d8cd45b70c 100644 --- a/Mage.Sets/src/mage/cards/b/BalancingAct.java +++ b/Mage.Sets/src/mage/cards/b/BalancingAct.java @@ -1,10 +1,7 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.Cards; @@ -18,21 +15,21 @@ import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author Plopman (Restore Balance), cbt33 */ public final class BalancingAct extends CardImpl { public BalancingAct(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{W}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}{W}"); // Each player chooses a number of permanents they control equal to the number of permanents controlled by the player who controls the fewest, then sacrifices the rest. Each player discards cards the same way. this.getSpellAbility().addEffect(new BalancingActEffect()); } - public BalancingAct(final BalancingAct card) { + private BalancingAct(final BalancingAct card) { super(card); } @@ -44,13 +41,12 @@ public final class BalancingAct extends CardImpl { class BalancingActEffect extends OneShotEffect { - - public BalancingActEffect() { + BalancingActEffect() { super(Outcome.Sacrifice); staticText = "Each player chooses a number of permanents they control equal to the number of permanents controlled by the player who controls the fewest, then sacrifices the rest. Each player discards cards the same way"; } - public BalancingActEffect(final BalancingActEffect effect) { + private BalancingActEffect(final BalancingActEffect effect) { super(effect); } @@ -65,23 +61,23 @@ class BalancingActEffect extends OneShotEffect { if (controller != null) { int minPermanent = Integer.MAX_VALUE, minCard = Integer.MAX_VALUE; // count minimal permanents - for(UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)){ + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); - if(player != null){ + if (player != null) { int count = game.getBattlefield().getActivePermanents(new FilterControlledPermanent(), player.getId(), source.getSourceId(), game).size(); - if(count < minPermanent){ + if (count < minPermanent) { minPermanent = count; } } } // sacrifice permanents over the minimum - for(UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)){ + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); - if(player != null){ + if (player != null) { TargetControlledPermanent target = new TargetControlledPermanent(minPermanent, minPermanent, new FilterControlledPermanent(), true); - if(target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game)){ - for(Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledPermanent(), player.getId(), source.getSourceId(), game)){ - if(permanent != null && !target.getTargets().contains(permanent.getId())){ + if (target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledPermanent(), player.getId(), source.getSourceId(), game)) { + if (permanent != null && !target.getTargets().contains(permanent.getId())) { permanent.sacrifice(source.getSourceId(), game); } } @@ -90,29 +86,25 @@ class BalancingActEffect extends OneShotEffect { } // count minimal cards in hand - for(UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)){ + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); - if(player != null){ + if (player != null) { int count = player.getHand().size(); - if(count < minCard){ + if (count < minCard) { minCard = count; } } } - + // discard cards over the minimum - for(UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)){ + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); - if(player != null){ + if (player != null) { TargetCardInHand target = new TargetCardInHand(minCard, new FilterCard()); - if(target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game)){ - Cards cards = player.getHand().copy(); - for(UUID cardUUID : cards){ - Card card = player.getHand().get(cardUUID, game); - if(!target.getTargets().contains(cardUUID)){ - player.discard(card, source, game); - } - } + if (target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game)) { + Cards cards = player.getHand().copy(); + cards.removeIf(target.getTargets()::contains); + player.discard(cards, source, game); } } } diff --git a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java index 7cc9356024..4c3d6e2644 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java @@ -1,11 +1,7 @@ - package mage.cards.b; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.condition.common.IsStepCondition; import mage.abilities.costs.common.TapSourceCost; @@ -15,11 +11,7 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.RemoveFromCombatTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.PhaseStep; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterBlockingCreature; import mage.filter.predicate.permanent.PermanentInListPredicate; @@ -32,8 +24,9 @@ import mage.target.TargetPermanent; import mage.target.common.TargetAttackingCreature; import mage.watchers.common.BlockedByOnlyOneCreatureThisCombatWatcher; +import java.util.*; + /** - * * @author L_J */ public final class BalduvianWarlord extends CardImpl { @@ -64,7 +57,7 @@ public final class BalduvianWarlord extends CardImpl { class BalduvianWarlordUnblockEffect extends OneShotEffect { - public BalduvianWarlordUnblockEffect() { + BalduvianWarlordUnblockEffect() { super(Outcome.Benefit); this.staticText = " Remove target blocking creature from combat. Creatures it was blocking that hadn't become blocked by another creature this combat become unblocked, then it blocks an attacking creature of your choice"; } @@ -140,12 +133,22 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { game.getCombat().addBlockingGroup(permanent.getId(), chosenPermanent.getId(), controller.getId(), game); // 702.21h if (notYetBlocked) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null)); + Set morSet = new HashSet<>(); + morSet.add(new MageObjectReference(chosenPermanent, game)); for (UUID bandedId : chosenPermanent.getBandedCards()) { CombatGroup bandedGroup = game.getCombat().findGroup(bandedId); if (bandedGroup != null && chosenGroup.getBlockers().size() == 1) { + morSet.add(new MageObjectReference(bandedId, game)); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, bandedId, null)); } } + String key = UUID.randomUUID().toString(); + game.getState().setValue("becameBlocked_" + key, morSet); + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.BATCH_BLOCK_NONCOMBAT, + source.getSourceId(), source.getSourceId(), + source.getControllerId(), key, 0) + ); } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); } diff --git a/Mage.Sets/src/mage/cards/b/BalefulStare.java b/Mage.Sets/src/mage/cards/b/BalefulStare.java index cccfe6ab8d..16a49f5fa1 100644 --- a/Mage.Sets/src/mage/cards/b/BalefulStare.java +++ b/Mage.Sets/src/mage/cards/b/BalefulStare.java @@ -70,7 +70,7 @@ class BalefulStareEffect extends OneShotEffect { count++; } } - controller.drawCards(count, game); + controller.drawCards(count, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java b/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java index 6c7e3f8b9b..51c9ff6ae7 100644 --- a/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java +++ b/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java @@ -106,7 +106,7 @@ class BaneAlleyBrokerDrawExileEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); Target target = new TargetCardInHand(new FilterCard("card to exile")); if (controller.chooseTarget(outcome, target, source, game)) { Card card = game.getCard(target.getFirstTarget()); diff --git a/Mage.Sets/src/mage/cards/b/Banehound.java b/Mage.Sets/src/mage/cards/b/Banehound.java index 493e33caa6..20f196c29a 100644 --- a/Mage.Sets/src/mage/cards/b/Banehound.java +++ b/Mage.Sets/src/mage/cards/b/Banehound.java @@ -19,7 +19,7 @@ public final class Banehound extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); this.subtype.add(SubType.NIGHTMARE); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/b/BantSojourners.java b/Mage.Sets/src/mage/cards/b/BantSojourners.java index 85063e89a3..1aa6ebb6db 100644 --- a/Mage.Sets/src/mage/cards/b/BantSojourners.java +++ b/Mage.Sets/src/mage/cards/b/BantSojourners.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CycleTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.CyclingAbility; @@ -30,7 +30,7 @@ public final class BantSojourners extends CardImpl { // When you cycle Bant Sojourners or it dies, you may create a 1/1 white Soldier creature token. Ability ability1 = new CycleTriggeredAbility(new CreateTokenEffect(new SoldierToken()), true); - Ability ability2 = new DiesTriggeredAbility(new CreateTokenEffect(new SoldierToken()), true); + Ability ability2 = new DiesSourceTriggeredAbility(new CreateTokenEffect(new SoldierToken()), true); this.addAbility(ability1); this.addAbility(ability2); diff --git a/Mage.Sets/src/mage/cards/b/BarbedShocker.java b/Mage.Sets/src/mage/cards/b/BarbedShocker.java index 463e2c07f6..48177c00ff 100644 --- a/Mage.Sets/src/mage/cards/b/BarbedShocker.java +++ b/Mage.Sets/src/mage/cards/b/BarbedShocker.java @@ -1,14 +1,11 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsDamageToAPlayerTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.TrampleAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -17,14 +14,15 @@ import mage.constants.SubType; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author markedagain */ public final class BarbedShocker extends CardImpl { public BarbedShocker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.INSECT); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -37,7 +35,7 @@ public final class BarbedShocker extends CardImpl { this.addAbility(new DealsDamageToAPlayerTriggeredAbility(new BarbedShockerEffect(), false, true)); } - public BarbedShocker(final BarbedShocker card) { + private BarbedShocker(final BarbedShocker card) { super(card); } @@ -46,14 +44,15 @@ public final class BarbedShocker extends CardImpl { return new BarbedShocker(this); } } + class BarbedShockerEffect extends OneShotEffect { - public BarbedShockerEffect() { + BarbedShockerEffect() { super(Outcome.Discard); this.staticText = " that player discards all the cards in their hand, then draws that many cards"; } - public BarbedShockerEffect(final BarbedShockerEffect effect) { + private BarbedShockerEffect(final BarbedShockerEffect effect) { super(effect); } @@ -65,14 +64,11 @@ class BarbedShockerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (targetPlayer != null) { - int count = targetPlayer.getHand().size(); - for (Card card : targetPlayer.getHand().getCards(game)) { - targetPlayer.discard(card, source, game); - } - targetPlayer.drawCards(count, game); - return false; - } + if (targetPlayer == null) { + return false; + } + int count = targetPlayer.discard(targetPlayer.getHand(), source, game).size(); + targetPlayer.drawCards(count, source.getSourceId(), game); return true; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BarbedSliver.java b/Mage.Sets/src/mage/cards/b/BarbedSliver.java index 4138fec434..d807e39457 100644 --- a/Mage.Sets/src/mage/cards/b/BarbedSliver.java +++ b/Mage.Sets/src/mage/cards/b/BarbedSliver.java @@ -28,7 +28,7 @@ public final class BarbedSliver extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn).setText("this creature gets +1/+0 until end of turn"), new GenericManaCost(2)), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, false))); } diff --git a/Mage.Sets/src/mage/cards/b/BarbedWire.java b/Mage.Sets/src/mage/cards/b/BarbedWire.java index 4e820c3114..0726332bbc 100644 --- a/Mage.Sets/src/mage/cards/b/BarbedWire.java +++ b/Mage.Sets/src/mage/cards/b/BarbedWire.java @@ -1,105 +1,105 @@ -package mage.cards.b; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.PreventionEffectImpl; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; - -/** - * - * @author jeffwadsworth - */ -public final class BarbedWire extends CardImpl { - - private final String rule = "At the beginning of each player's upkeep, " - + "Barbed Wire deals 1 damage to that player."; - - public BarbedWire(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - - // At the beginning of each player's upkeep, Barbed Wire deals 1 damage to that player. - this.addAbility(new BeginningOfUpkeepTriggeredAbility( - Zone.BATTLEFIELD, - new BarbwireDamageEffect(), - TargetController.ACTIVE, - false, true, rule)); - - // {2}: Prevent the next 1 damage that would be dealt by Barbed Wire this turn. - this.addAbility(new SimpleActivatedAbility( - new BarbedWirePreventionEffect(), new ManaCostsImpl("{2}"))); - - } - - private BarbedWire(final BarbedWire card) { - super(card); - } - - @Override - public BarbedWire copy() { - return new BarbedWire(this); - } -} - -class BarbwireDamageEffect extends OneShotEffect { - - public BarbwireDamageEffect() { - super(Outcome.Damage); - this.staticText = ""; - } - - public BarbwireDamageEffect(final BarbwireDamageEffect effect) { - super(effect); - } - - @Override - public BarbwireDamageEffect copy() { - return new BarbwireDamageEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player activePlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (activePlayer != null) { - activePlayer.damage(1, source.getSourceId(), game); - return true; - } - - return false; - } -} - -class BarbedWirePreventionEffect extends PreventionEffectImpl { - - public BarbedWirePreventionEffect() { - super(Duration.EndOfTurn, 1, false); - staticText = "Prevent the next 1 damage that would be dealt by {this} this turn"; - } - - public BarbedWirePreventionEffect(final BarbedWirePreventionEffect effect) { - super(effect); - } - - @Override - public BarbedWirePreventionEffect copy() { - return new BarbedWirePreventionEffect(this); - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return super.applies(event, source, game) - && event.getSourceId().equals(source.getSourceId()); - } -} +package mage.cards.b; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; + +/** + * + * @author jeffwadsworth + */ +public final class BarbedWire extends CardImpl { + + private final String rule = "At the beginning of each player's upkeep, " + + "Barbed Wire deals 1 damage to that player."; + + public BarbedWire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // At the beginning of each player's upkeep, Barbed Wire deals 1 damage to that player. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, + new BarbwireDamageEffect(), + TargetController.ACTIVE, + false, true, rule)); + + // {2}: Prevent the next 1 damage that would be dealt by Barbed Wire this turn. + this.addAbility(new SimpleActivatedAbility( + new BarbedWirePreventionEffect(), new ManaCostsImpl("{2}"))); + + } + + private BarbedWire(final BarbedWire card) { + super(card); + } + + @Override + public BarbedWire copy() { + return new BarbedWire(this); + } +} + +class BarbwireDamageEffect extends OneShotEffect { + + public BarbwireDamageEffect() { + super(Outcome.Damage); + this.staticText = ""; + } + + public BarbwireDamageEffect(final BarbwireDamageEffect effect) { + super(effect); + } + + @Override + public BarbwireDamageEffect copy() { + return new BarbwireDamageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player activePlayer = game.getPlayer(targetPointer.getFirst(game, source)); + if (activePlayer != null) { + activePlayer.damage(1, source.getSourceId(), game); + return true; + } + + return false; + } +} + +class BarbedWirePreventionEffect extends PreventionEffectImpl { + + public BarbedWirePreventionEffect() { + super(Duration.EndOfTurn, 1, false); + staticText = "Prevent the next 1 damage that would be dealt by {this} this turn"; + } + + public BarbedWirePreventionEffect(final BarbedWirePreventionEffect effect) { + super(effect); + } + + @Override + public BarbedWirePreventionEffect copy() { + return new BarbedWirePreventionEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return super.applies(event, source, game) + && event.getSourceId().equals(source.getSourceId()); + } +} diff --git a/Mage.Sets/src/mage/cards/b/Barishi.java b/Mage.Sets/src/mage/cards/b/Barishi.java index 41c85f15c7..fa9196a7e4 100644 --- a/Mage.Sets/src/mage/cards/b/Barishi.java +++ b/Mage.Sets/src/mage/cards/b/Barishi.java @@ -3,7 +3,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.constants.SubType; @@ -31,7 +31,7 @@ public final class Barishi extends CardImpl { this.toughness = new MageInt(3); // When Barishi dies, exile Barishi, then shuffle all creature cards from your graveyard into your library. - this.addAbility(new DiesTriggeredAbility(new BarishiEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new BarishiEffect(), false)); } public Barishi(final Barishi card) { diff --git a/Mage.Sets/src/mage/cards/b/BarrageOfBoulders.java b/Mage.Sets/src/mage/cards/b/BarrageOfBoulders.java index e9b154a1c4..949cade172 100644 --- a/Mage.Sets/src/mage/cards/b/BarrageOfBoulders.java +++ b/Mage.Sets/src/mage/cards/b/BarrageOfBoulders.java @@ -11,8 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import java.util.UUID; @@ -23,17 +22,11 @@ import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURES; */ public final class BarrageOfBoulders extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public BarrageOfBoulders(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); // Barrage of Boulders deals 1 damage to each creature you don't control. - this.getSpellAbility().addEffect(new DamageAllEffect(1, filter)); + this.getSpellAbility().addEffect(new DamageAllEffect(1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); // Ferocious - If you control a creature with power 4 or greater, creatures can't block this turn Effect effect = new ConditionalRestrictionEffect( Duration.EndOfTurn, @@ -44,7 +37,7 @@ public final class BarrageOfBoulders extends CardImpl { this.getSpellAbility().addHint(FerociousHint.instance); } - public BarrageOfBoulders(final BarrageOfBoulders card) { + private BarrageOfBoulders(final BarrageOfBoulders card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/b/BarrierBreach.java b/Mage.Sets/src/mage/cards/b/BarrierBreach.java new file mode 100644 index 0000000000..8a7a197134 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BarrierBreach.java @@ -0,0 +1,39 @@ +package mage.cards.b; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterEnchantmentPermanent; +import mage.target.common.TargetEnchantmentPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BarrierBreach extends CardImpl { + + public BarrierBreach(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // Exile up to three target enchantments. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addTarget(new TargetEnchantmentPermanent(0, 3, + new FilterEnchantmentPermanent("enchantments"), false)); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private BarrierBreach(final BarrierBreach card) { + super(card); + } + + @Override + public BarrierBreach copy() { + return new BarrierBreach(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java b/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java new file mode 100644 index 0000000000..77dc1727ab --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java @@ -0,0 +1,111 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.target.TargetPermanent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BarrinTolarianArchmage extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreatureOrPlaneswalkerPermanent("other target creature or planeswalker"); + + static { + filter.add(AnotherPredicate.instance); + } + + public BarrinTolarianArchmage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Barrin, Tolarian Archmage enters the battlefield, return up to one other target creature or planeswalker to its owner's hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect()); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + this.addAbility(ability); + + // At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new DrawCardSourceControllerEffect(1), TargetController.YOU, false + ), BarrinTolarianArchmageCondition.instance, "At the beginning of your end step, " + + "if a permanent was put into your hand from the battlefield this turn, draw a card." + ), new BarrinTolarianArchmageWatcher()); + } + + private BarrinTolarianArchmage(final BarrinTolarianArchmage card) { + super(card); + } + + @Override + public BarrinTolarianArchmage copy() { + return new BarrinTolarianArchmage(this); + } +} + +enum BarrinTolarianArchmageCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + BarrinTolarianArchmageWatcher watcher = game.getState().getWatcher(BarrinTolarianArchmageWatcher.class); + return watcher != null && watcher.checkPlayer(source.getControllerId()); + } +} + +class BarrinTolarianArchmageWatcher extends Watcher { + + private final Set playerSet = new HashSet<>(); + + BarrinTolarianArchmageWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ZONE_CHANGE) { + return; + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getFromZone() == Zone.BATTLEFIELD + && zEvent.getToZone() == Zone.HAND) { + playerSet.add(zEvent.getTarget().getOwnerId()); + } + } + + @Override + public void reset() { + playerSet.clear(); + super.reset(); + } + + boolean checkPlayer(UUID playerId) { + return playerSet.contains(playerId); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BarteredCow.java b/Mage.Sets/src/mage/cards/b/BarteredCow.java index 07fb03f4e6..9675188862 100644 --- a/Mage.Sets/src/mage/cards/b/BarteredCow.java +++ b/Mage.Sets/src/mage/cards/b/BarteredCow.java @@ -2,7 +2,7 @@ package mage.cards.b; import mage.MageInt; import mage.MageObject; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DiscardCardControllerTriggeredAbility; @@ -41,7 +41,7 @@ public final class BarteredCow extends CardImpl { // When Bartered Cow dies or when you discard it, create a Food token. this.addAbility(new OrTriggeredAbility( Zone.ALL, new CreateTokenEffect(new FoodToken()), false, - "When {this} dies or when you discard it, ", new DiesTriggeredAbility((Effect) null), + "When {this} dies or when you discard it, ", new DiesSourceTriggeredAbility((Effect) null), new DiscardCardControllerTriggeredAbility(null, false, filter) )); } diff --git a/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java b/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java new file mode 100644 index 0000000000..c484ec5995 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java @@ -0,0 +1,113 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class BasriDevotedPaladin extends CardImpl { + + public BasriDevotedPaladin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{W}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.BASRI); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // +1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn. + Ability ability = new LoyaltyAbility(new AddCountersTargetEffect( + CounterType.P1P1.createInstance() + ).setText("Put a +1/+1 counter on up to one target creature"), 1); + ability.addEffect(new GainAbilityTargetEffect( + VigilanceAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains vigilance until end of turn")); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + + // −1: Whenever a creature attacks this turn, put a +1/+1 counter on it. + this.addAbility(new LoyaltyAbility(new CreateDelayedTriggeredAbilityEffect(new BasriDevotedPaladinTriggeredAbility()), -1)); + + // −6: Creatures you control get +2/+2 and gain flying until end of turn. + Effect effect = new BoostControlledEffect(2, 2, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE); + effect.setText("Creatures you control get +2/+2"); + LoyaltyAbility ultimateAbility = new LoyaltyAbility(effect, -6); + effect = new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE); + effect.setText("and gain flying until end of turn"); + ultimateAbility.addEffect(effect); + this.addAbility(ultimateAbility); + } + + private BasriDevotedPaladin(final BasriDevotedPaladin card) { + super(card); + } + + @Override + public BasriDevotedPaladin copy() { + return new BasriDevotedPaladin(this); + } +} + +class BasriDevotedPaladinTriggeredAbility extends DelayedTriggeredAbility { + + public BasriDevotedPaladinTriggeredAbility() { + super(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), Duration.EndOfTurn, false); + } + + public BasriDevotedPaladinTriggeredAbility(BasriDevotedPaladinTriggeredAbility ability) { + super(ability); + } + + @Override + public BasriDevotedPaladinTriggeredAbility copy() { + return new BasriDevotedPaladinTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ATTACKER_DECLARED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null) { + for (Effect effect : getEffects()) { + effect.setTargetPointer(new FixedTarget(permanent, game)); + } + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever a creature attacks this turn, put a +1/+1 counter on it."; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BasriKet.java b/Mage.Sets/src/mage/cards/b/BasriKet.java new file mode 100644 index 0000000000..ea8957545d --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BasriKet.java @@ -0,0 +1,119 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.command.emblems.BasriKetEmblem; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SoldierToken; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class BasriKet extends CardImpl { + + public BasriKet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{W}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.BASRI); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + + // +1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn. + Ability ability = new LoyaltyAbility(new AddCountersTargetEffect( + CounterType.P1P1.createInstance() + ).setText("Put a +1/+1 counter on up to one target creature"), 1); + ability.addEffect(new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains indestructible until end of turn")); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + + // −2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking. + this.addAbility(new LoyaltyAbility(new CreateDelayedTriggeredAbilityEffect(new BasriKetTriggeredAbility()), -2)); + + // −6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control." + this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new BasriKetEmblem()), -6)); + } + + private BasriKet(final BasriKet card) { + super(card); + } + + @Override + public BasriKet copy() { + return new BasriKet(this); + } +} + +class BasriKetTriggeredAbility extends DelayedTriggeredAbility { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature"); + + static { + filter.add(Predicates.not(TokenPredicate.instance)); + } + + public BasriKetTriggeredAbility() { + super(null, Duration.EndOfTurn, false); + } + + public BasriKetTriggeredAbility(BasriKetTriggeredAbility ability) { + super(ability); + } + + @Override + public BasriKetTriggeredAbility copy() { + return new BasriKetTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + int attackingNonTokens = 0; + for (UUID attacker : game.getCombat().getAttackers()) { + Permanent permanent = game.getPermanent(attacker); + if (filter.match(permanent, game)) { + attackingNonTokens++; + } + } + if (attackingNonTokens > 0) { + this.getEffects().clear(); + addEffect(new CreateTokenEffect(new SoldierToken(), attackingNonTokens, true, true)); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking."; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BasrisAcolyte.java b/Mage.Sets/src/mage/cards/b/BasrisAcolyte.java new file mode 100644 index 0000000000..a1e1a2e520 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BasrisAcolyte.java @@ -0,0 +1,59 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BasrisAcolyte extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("other creatures you control"); + + static { + filter.add(AnotherPredicate.instance); + } + + public BasrisAcolyte(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); + + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on each of up to two other target creatures you control") + ); + ability.addTarget(new TargetPermanent(0, 2, filter, false)); + this.addAbility(ability); + } + + private BasrisAcolyte(final BasrisAcolyte card) { + super(card); + } + + @Override + public BasrisAcolyte copy() { + return new BasrisAcolyte(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BasrisAegis.java b/Mage.Sets/src/mage/cards/b/BasrisAegis.java new file mode 100644 index 0000000000..e0dbee48d2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BasrisAegis.java @@ -0,0 +1,46 @@ +package mage.cards.b; + +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class BasrisAegis extends CardImpl { + + private static final FilterCard filter = new FilterCard("Basri, Devoted Paladin"); + + static { + filter.add(new NamePredicate("Basri, Devoted Paladin")); + } + + public BasrisAegis(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}{W}"); + + // Put a +1/+1 counter on each of up to two target creatures. You may search your library and/or graveyard for a card named Basri, Devoted Paladin, reveal it, and put it into your hand. If you search your library this way, shuffle it. + this.getSpellAbility().addEffect(new AddCountersTargetEffect( + CounterType.P1P1.createInstance() + ).setText("Put a +1/+1 counter on each of up to two target creatures")); + this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter, false, true)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); + } + + private BasrisAegis(final BasrisAegis card) { + super(card); + } + + @Override + public BasrisAegis copy() { + return new BasrisAegis(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BasrisLieutenant.java b/Mage.Sets/src/mage/cards/b/BasrisLieutenant.java new file mode 100644 index 0000000000..eae31d1a61 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BasrisLieutenant.java @@ -0,0 +1,76 @@ +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.ProtectionAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.FilterObject; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.MulticoloredPredicate; +import mage.filter.predicate.permanent.CounterPredicate; +import mage.game.permanent.token.KnightToken; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author htrajan + */ +public final class BasrisLieutenant extends CardImpl { + + private static final FilterObject multicoloredFilter = new FilterObject<>("multicolored"); + private static final FilterCreaturePermanent controlledCreatureWithP1P1CounterFilter = new FilterCreaturePermanent("creature you control"); + + static { + multicoloredFilter.add(MulticoloredPredicate.instance); + controlledCreatureWithP1P1CounterFilter.add(TargetController.YOU.getControllerPredicate()); + controlledCreatureWithP1P1CounterFilter.add(new CounterPredicate(CounterType.P1P1)); + } + + public BasrisLieutenant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Vigilance, protection from multicolored + this.addAbility(VigilanceAbility.getInstance()); + + // protection from multicolored + this.addAbility(new ProtectionAbility(multicoloredFilter)); + + // When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control. + Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + + // Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance. + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility( + new CreateTokenEffect(new KnightToken()).setText("if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance"), + false, + controlledCreatureWithP1P1CounterFilter + ).setApplyFilterOnSource(true)); + } + + private BasrisLieutenant(final BasrisLieutenant card) { + super(card); + } + + @Override + public BasrisLieutenant copy() { + return new BasrisLieutenant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BasrisSolidarity.java b/Mage.Sets/src/mage/cards/b/BasrisSolidarity.java new file mode 100644 index 0000000000..3af3b9c814 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BasrisSolidarity.java @@ -0,0 +1,34 @@ +package mage.cards.b; + +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BasrisSolidarity extends CardImpl { + + public BasrisSolidarity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}"); + + // Put a +1/+1 counter on each creature you control. + this.getSpellAbility().addEffect(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE + )); + } + + private BasrisSolidarity(final BasrisSolidarity card) { + super(card); + } + + @Override + public BasrisSolidarity copy() { + return new BasrisSolidarity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BastionOfRemembrance.java b/Mage.Sets/src/mage/cards/b/BastionOfRemembrance.java new file mode 100644 index 0000000000..4a41aaef06 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BastionOfRemembrance.java @@ -0,0 +1,45 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.HumanSoldierToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BastionOfRemembrance extends CardImpl { + + public BastionOfRemembrance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + // When Bastion of Remembrance enters the battlefield, create a 1/1 white Human Soldier creature token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken()))); + + // Whenever a creature you control dies, each opponent loses 1 life and you gain 1 life. + Ability ability = new DiesCreatureTriggeredAbility( + new LoseLifeOpponentsEffect(1), false, + StaticFilters.FILTER_CONTROLLED_A_CREATURE + ); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); + } + + private BastionOfRemembrance(final BastionOfRemembrance card) { + super(card); + } + + @Override + public BastionOfRemembrance copy() { + return new BastionOfRemembrance(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BatteringKrasis.java b/Mage.Sets/src/mage/cards/b/BatteringKrasis.java index fc6586c7df..a60e7d8531 100644 --- a/Mage.Sets/src/mage/cards/b/BatteringKrasis.java +++ b/Mage.Sets/src/mage/cards/b/BatteringKrasis.java @@ -1,5 +1,3 @@ - - package mage.cards.b; import mage.MageInt; @@ -13,28 +11,25 @@ import mage.constants.SubType; import java.util.UUID; /** - * * @author LevelX2 */ - - public final class BatteringKrasis extends CardImpl { - public BatteringKrasis (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); - this.subtype.add(SubType.FISH, SubType.BEAST); + public BatteringKrasis(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.subtype.add(SubType.SHARK, SubType.BEAST); this.power = new MageInt(2); this.toughness = new MageInt(1); // Trample this.addAbility(TrampleAbility.getInstance()); + // Evolve (Whenever a creature enters the battlefield under your control, if that creature has greater power or toughness than this creature, put a +1/+1 counter on this creature.) this.addAbility(new EvolveAbility()); - } - public BatteringKrasis (final BatteringKrasis card) { + private BatteringKrasis(final BatteringKrasis card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/b/BattlefieldPromotion.java b/Mage.Sets/src/mage/cards/b/BattlefieldPromotion.java index c86972f4c3..100716172b 100644 --- a/Mage.Sets/src/mage/cards/b/BattlefieldPromotion.java +++ b/Mage.Sets/src/mage/cards/b/BattlefieldPromotion.java @@ -25,7 +25,7 @@ public final class BattlefieldPromotion extends CardImpl { this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); this.getSpellAbility().addEffect(new GainAbilityTargetEffect( FirstStrikeAbility.getInstance(), Duration.EndOfTurn - ).setText("That creature gains first strike until end of turn.")); + ).setText("That creature gains first strike until end of turn")); this.getSpellAbility().addEffect(new GainLifeEffect(2)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/b/BattlefieldThaumaturge.java b/Mage.Sets/src/mage/cards/b/BattlefieldThaumaturge.java index 635027c5ad..9f6c37ff16 100644 --- a/Mage.Sets/src/mage/cards/b/BattlefieldThaumaturge.java +++ b/Mage.Sets/src/mage/cards/b/BattlefieldThaumaturge.java @@ -1,17 +1,15 @@ - package mage.cards.b; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.abilities.keyword.HeroicAbility; import mage.abilities.keyword.HexproofAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -22,8 +20,11 @@ import mage.game.stack.Spell; import mage.target.Target; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class BattlefieldThaumaturge extends CardImpl { @@ -37,6 +38,7 @@ public final class BattlefieldThaumaturge extends CardImpl { // Each instant and sorcery spell you cast costs 1 less to cast for each creature it targets. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BattlefieldThaumaturgeSpellsCostReductionEffect())); + // Heroic - Whenever you cast a spell that targets Battlefield Thaumaturge, Battlefield Thaumaturge gains hexproof until end of turn. this.addAbility(new HeroicAbility(new GainAbilitySourceEffect(HexproofAbility.getInstance(), Duration.EndOfTurn))); } @@ -64,25 +66,62 @@ class BattlefieldThaumaturgeSpellsCostReductionEffect extends CostModificationEf @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - Set creaturesTargeted = new HashSet<>(); - for (Target target : abilityToModify.getTargets()) { - for (UUID uuid : target.getTargets()) { - Permanent permanent = game.getPermanent(uuid); - if (permanent != null && permanent.isCreature()) { - creaturesTargeted.add(permanent.getId()); + int reduceAmount = 0; + if (game.inCheckPlayableState()) { + // checking state (search max possible targets) + reduceAmount = getMaxPossibleTargetCreatures(abilityToModify, game); + } else { + // real cast check + Set creaturesTargeted = new HashSet<>(); + for (Target target : abilityToModify.getAllSelectedTargets()) { + if (target.isNotTarget()) { + continue; + } + for (UUID uuid : target.getTargets()) { + Permanent permanent = game.getPermanent(uuid); + if (permanent != null && permanent.isCreature()) { + creaturesTargeted.add(permanent.getId()); + } } } + reduceAmount = creaturesTargeted.size(); } - CardUtil.reduceCost(abilityToModify, creaturesTargeted.size()); + CardUtil.reduceCost(abilityToModify, reduceAmount); return true; } + + private int getMaxPossibleTargetCreatures(Ability ability, Game game) { + // checks only one mode, so it can be wrong in rare use cases with multi-modes (example: mode one gives +2 and mode two gives another +1 -- total +3) + int maxAmount = 0; + for (Mode mode : ability.getModes().values()) { + for (Target target : mode.getTargets()) { + if (target.isNotTarget()) { + continue; + } + Set possibleList = target.possibleTargets(ability.getSourceId(), ability.getControllerId(), game); + possibleList.removeIf(id -> { + Permanent permanent = game.getPermanent(id); + return permanent == null || !permanent.isCreature(); + }); + int possibleAmount = Math.min(possibleList.size(), target.getMaxNumberOfTargets()); + maxAmount = Math.max(maxAmount, possibleAmount); + } + } + return maxAmount; + } + @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { if ((abilityToModify instanceof SpellAbility) && abilityToModify.isControlledBy(source.getControllerId())) { Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); - return spell != null && StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY.match(spell, game); + if (spell != null) { + return spell != null && StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY.match(spell, game); + } else { + Card sourceCard = game.getCard(abilityToModify.getSourceId()); + return sourceCard != null && StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY.match(sourceCard, game); + } } return false; } diff --git a/Mage.Sets/src/mage/cards/b/BazaarTrademage.java b/Mage.Sets/src/mage/cards/b/BazaarTrademage.java index e2cdeab207..847ce58f66 100644 --- a/Mage.Sets/src/mage/cards/b/BazaarTrademage.java +++ b/Mage.Sets/src/mage/cards/b/BazaarTrademage.java @@ -1,7 +1,7 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class BazaarTrademage extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Bazaar Trademage enters the battlefield, draw two cards, then discard three cards. - this.addAbility(new EntersBattlefieldAbility(new DrawDiscardControllerEffect(2, 3))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawDiscardControllerEffect(2, 3))); } private BazaarTrademage(final BazaarTrademage card) { diff --git a/Mage.Sets/src/mage/cards/b/BearerOfTheHeavens.java b/Mage.Sets/src/mage/cards/b/BearerOfTheHeavens.java index d935f234c4..0cdf4f2efb 100644 --- a/Mage.Sets/src/mage/cards/b/BearerOfTheHeavens.java +++ b/Mage.Sets/src/mage/cards/b/BearerOfTheHeavens.java @@ -3,7 +3,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -31,7 +31,7 @@ public final class BearerOfTheHeavens extends CardImpl { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new DestroyAllEffect(StaticFilters.FILTER_PERMANENT)); Effect effect = new CreateDelayedTriggeredAbilityEffect(delayedAbility); effect.setText("destroy all permanents at the beginning of the next end step"); - this.addAbility(new DiesTriggeredAbility(effect, false)); + this.addAbility(new DiesSourceTriggeredAbility(effect, false)); } public BearerOfTheHeavens(final BearerOfTheHeavens card) { diff --git a/Mage.Sets/src/mage/cards/b/BeastWithin.java b/Mage.Sets/src/mage/cards/b/BeastWithin.java index ed66eab7de..4446cc0592 100644 --- a/Mage.Sets/src/mage/cards/b/BeastWithin.java +++ b/Mage.Sets/src/mage/cards/b/BeastWithin.java @@ -41,7 +41,7 @@ public final class BeastWithin extends CardImpl { class BeastWithinEffect extends OneShotEffect { public BeastWithinEffect() { - super(Outcome.PutCreatureInPlay); + super(Outcome.Detriment); staticText = "Its controller creates a 3/3 green Beast creature token"; } @@ -59,7 +59,7 @@ class BeastWithinEffect extends OneShotEffect { // If the permanent is an illegal target when Beast Within tries to resolve, the spell won’t resolve and none // of its effects will happen. The permanent’s controller won’t get a Beast token. // (2011-06-01) - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); // must use LKI + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); // must use LKI if (permanent != null) { new BeastToken().putOntoBattlefield(1, game, source.getSourceId(), permanent.getControllerId()); } diff --git a/Mage.Sets/src/mage/cards/b/BedlamReveler.java b/Mage.Sets/src/mage/cards/b/BedlamReveler.java index 6f6175f7cc..5e4a2b29cc 100644 --- a/Mage.Sets/src/mage/cards/b/BedlamReveler.java +++ b/Mage.Sets/src/mage/cards/b/BedlamReveler.java @@ -1,4 +1,3 @@ - package mage.cards.b; import mage.MageInt; @@ -8,7 +7,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; import mage.abilities.effects.common.discard.DiscardHandControllerEffect; import mage.abilities.hint.ValueHint; import mage.abilities.keyword.ProwessAbility; @@ -26,27 +25,25 @@ import java.util.UUID; */ public final class BedlamReveler extends CardImpl { - private static final DynamicValue cardsCount = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); - public BedlamReveler(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{R}{R}"); this.subtype.add(SubType.DEVIL, SubType.HORROR); this.power = new MageInt(3); this.toughness = new MageInt(4); - // Bedlam Reveler costs {1} less to cast for each instant or sorcery card in your graveyard. - this.addAbility(new SimpleStaticAbility( - Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY) - ).addHint(new ValueHint("Instant and sorcery cards in your graveyard", cardsCount))); + // This spell costs {1} less to cast for each instant and sorcery card in your graveyard. + DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)); + ability.setRuleAtTheTop(true); + ability.addHint(new ValueHint("Instant or sourcery card in your graveyard", xValue)); + this.addAbility(ability); // Prowess this.addAbility(new ProwessAbility()); // When Bedlam Reveler enters the battlefield, discard your hand, then draw three cards. - Ability ability = new EntersBattlefieldTriggeredAbility( - new DiscardHandControllerEffect().setText("discard your hand,") - ); - ability.addEffect(new DrawCardSourceControllerEffect(3).setText("then draw three cards")); + ability = new EntersBattlefieldTriggeredAbility(new DiscardHandControllerEffect()); + ability.addEffect(new DrawCardSourceControllerEffect(3).concatBy(", then")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BelbesArmor.java b/Mage.Sets/src/mage/cards/b/BelbesArmor.java index d9cc57f6a2..f3b1409964 100644 --- a/Mage.Sets/src/mage/cards/b/BelbesArmor.java +++ b/Mage.Sets/src/mage/cards/b/BelbesArmor.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -13,11 +11,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class BelbesArmor extends CardImpl { @@ -26,20 +24,16 @@ public final class BelbesArmor extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {X}, {tap}: Target creature gets -X/+X until end of turn. - Ability ability = new SimpleActivatedAbility( - Zone.BATTLEFIELD, - new BoostTargetEffect( - new MultipliedValue(ManacostVariableValue.instance, -1), - ManacostVariableValue.instance, - Duration.EndOfTurn - ), - new ManaCostsImpl("{X}")); + Ability ability = new SimpleActivatedAbility(new BoostTargetEffect( + new MultipliedValue(ManacostVariableValue.instance, -1), + ManacostVariableValue.instance, Duration.EndOfTurn + ).setText("Target creature gets -X/+X until end of turn"), new ManaCostsImpl("{X}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } - public BelbesArmor(final BelbesArmor card) { + private BelbesArmor(final BelbesArmor card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/b/BellowingFiend.java b/Mage.Sets/src/mage/cards/b/BellowingFiend.java index 1e332e77b9..848f6afcfa 100644 --- a/Mage.Sets/src/mage/cards/b/BellowingFiend.java +++ b/Mage.Sets/src/mage/cards/b/BellowingFiend.java @@ -15,7 +15,6 @@ import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -69,7 +68,7 @@ class BellowingFiendEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { boolean applied = false; - Permanent damagedCreature = ((FixedTarget) targetPointer).getTargetedPermanentOrLKIBattlefield(game); + Permanent damagedCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (damagedCreature != null) { Player controller = game.getPlayer(damagedCreature.getControllerId()); if (controller != null) { diff --git a/Mage.Sets/src/mage/cards/b/BellowingSaddlebrute.java b/Mage.Sets/src/mage/cards/b/BellowingSaddlebrute.java index 10b298ca19..6859e2e29e 100644 --- a/Mage.Sets/src/mage/cards/b/BellowingSaddlebrute.java +++ b/Mage.Sets/src/mage/cards/b/BellowingSaddlebrute.java @@ -1,4 +1,3 @@ - package mage.cards.b; import mage.MageInt; @@ -7,8 +6,10 @@ import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.watchers.common.PlayerAttackedWatcher; @@ -16,13 +17,12 @@ import mage.watchers.common.PlayerAttackedWatcher; import java.util.UUID; /** - * * @author LevelX2 */ public final class BellowingSaddlebrute extends CardImpl { public BellowingSaddlebrute(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.ORC, SubType.WARRIOR); this.power = new MageInt(4); @@ -30,10 +30,12 @@ public final class BellowingSaddlebrute extends CardImpl { // Raid - When Bellowing Saddlebrute enters the battlefield, you lose 4 life unless you attacked with a creature this turn this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new LoseLifeSourceControllerEffect(4)), - new InvertCondition(RaidCondition.instance), - "Raid — When {this} enters the battlefield, you lose 4 life unless you attacked with a creature this turn" - ), new PlayerAttackedWatcher()); + new EntersBattlefieldTriggeredAbility(new LoseLifeSourceControllerEffect(4)), + new InvertCondition(RaidCondition.instance), + "Raid — When {this} enters the battlefield, you lose 4 life unless you attacked with a creature this turn") + .setAbilityWord(AbilityWord.RAID) + .addHint(RaidHint.instance), + new PlayerAttackedWatcher()); } public BellowingSaddlebrute(final BellowingSaddlebrute card) { diff --git a/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java b/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java index 4ef63a4281..ed65fe16d8 100644 --- a/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java +++ b/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java @@ -85,6 +85,6 @@ class BelltowerSphinxEffect extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a source deals damage to {this}, that source's controller puts that many cards from the top of their library into their graveyard."; + return "Whenever a source deals damage to {this}, that source's controller mills that many cards."; } } diff --git a/Mage.Sets/src/mage/cards/b/BenefactorsDraught.java b/Mage.Sets/src/mage/cards/b/BenefactorsDraught.java index e59c351045..5d5da26312 100644 --- a/Mage.Sets/src/mage/cards/b/BenefactorsDraught.java +++ b/Mage.Sets/src/mage/cards/b/BenefactorsDraught.java @@ -75,6 +75,6 @@ class BenefactorsDraughtTriggeredAbility extends DelayedTriggeredAbility { @Override public String getRule() { - return "Until end of turn, whenever a creature an opponent controls blocks, draw a card."; + return "Until end of turn, whenever a creature an opponent controls blocks, draw a card"; } } diff --git a/Mage.Sets/src/mage/cards/b/BenevolentOffering.java b/Mage.Sets/src/mage/cards/b/BenevolentOffering.java index 1a708b2343..1e4ea4310b 100644 --- a/Mage.Sets/src/mage/cards/b/BenevolentOffering.java +++ b/Mage.Sets/src/mage/cards/b/BenevolentOffering.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -19,8 +17,9 @@ import mage.target.Target; import mage.target.common.TargetOpponent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class BenevolentOffering extends CardImpl { @@ -69,10 +68,10 @@ class BenevolentOfferingEffect1 extends OneShotEffect { target.choose(Outcome.Sacrifice, source.getControllerId(), source.getSourceId(), game); Player opponent = game.getPlayer(target.getFirstTarget()); if (opponent != null) { - Effect effect = new CreateTokenTargetEffect(new SpiritWhiteToken("C14"), 3); + Effect effect = new CreateTokenTargetEffect(new SpiritWhiteToken(), 3); effect.setTargetPointer(new FixedTarget(opponent.getId())); effect.apply(game, source); - new CreateTokenEffect(new SpiritWhiteToken("C14"), 3).apply(game, source); + new CreateTokenEffect(new SpiritWhiteToken(), 3).apply(game, source); return true; } } diff --git a/Mage.Sets/src/mage/cards/b/BenthicExplorers.java b/Mage.Sets/src/mage/cards/b/BenthicExplorers.java index 8e9d18a989..89dd8b6202 100644 --- a/Mage.Sets/src/mage/cards/b/BenthicExplorers.java +++ b/Mage.Sets/src/mage/cards/b/BenthicExplorers.java @@ -1,247 +1,277 @@ -package mage.cards.b; - -import mage.MageInt; -import mage.Mana; -import mage.abilities.Abilities; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.common.ManaEffect; -import mage.abilities.mana.ActivatedManaAbilityImpl; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.choices.Choice; -import mage.choices.ChoiceColor; -import mage.constants.*; -import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.permanent.TappedPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetLandPermanent; -import mage.util.CardUtil; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class BenthicExplorers extends CardImpl { - - private static final FilterLandPermanent filter = new FilterLandPermanent("tapped land an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - filter.add(TappedPredicate.instance); - } - - public BenthicExplorers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); - - this.subtype.add(SubType.MERFOLK); - this.subtype.add(SubType.SCOUT); - this.power = new MageInt(2); - this.toughness = new MageInt(4); - - // {tap}, Untap a tapped land an opponent controls: Add one mana of any type that land could produce. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BenthicExplorersManaEffect(), new TapSourceCost()); - ability.addCost(new BenthicExplorersCost(new TargetLandPermanent(filter))); - this.addAbility(ability); - - } - - private BenthicExplorers(final BenthicExplorers card) { - super(card); - } - - @Override - public BenthicExplorers copy() { - return new BenthicExplorers(this); - } -} - -class BenthicExplorersCost extends CostImpl { - - TargetLandPermanent target; - - public BenthicExplorersCost(TargetLandPermanent target) { - this.target = target; - this.text = "Untap " + CardUtil.numberToText(target.getMaxNumberOfTargets(), "") + ' ' + target.getTargetName(); - } - - public BenthicExplorersCost(final BenthicExplorersCost cost) { - super(cost); - this.target = cost.target.copy(); - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { - if (target.choose(Outcome.Untap, controllerId, sourceId, game)) { - for (UUID targetId : target.getTargets()) { - Permanent permanent = game.getPermanent(targetId); - if (permanent == null) { - return false; - } - paid |= permanent.untap(game); - if (paid) { - game.getState().setValue("UntapTargetCost" + ability.getSourceId().toString(), permanent); // remember the untapped permanent - } - } - } - return paid; - } - - @Override - public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - return target.canChoose(controllerId, game); - } - - @Override - public BenthicExplorersCost copy() { - return new BenthicExplorersCost(this); - } - -} - -class BenthicExplorersManaEffect extends ManaEffect { - - public BenthicExplorersManaEffect() { - staticText = "Add one mana of any type that land could produce"; - } - - private BenthicExplorersManaEffect(final BenthicExplorersManaEffect effect) { - super(effect); - } - - @Override - public List getNetMana(Game game, Ability source) { - List netManas = new ArrayList<>(); - Mana types = getManaTypes(game, source); - if (types.getBlack() > 0) { - netManas.add(new Mana(ColoredManaSymbol.B)); - } - if (types.getRed() > 0) { - netManas.add(new Mana(ColoredManaSymbol.R)); - } - if (types.getBlue() > 0) { - netManas.add(new Mana(ColoredManaSymbol.U)); - } - if (types.getGreen() > 0) { - netManas.add(new Mana(ColoredManaSymbol.G)); - } - if (types.getWhite() > 0) { - netManas.add(new Mana(ColoredManaSymbol.W)); - } - if (types.getColorless() > 0) { - netManas.add(Mana.ColorlessMana(1)); - } - if (types.getAny() > 0) { - netManas.add(Mana.AnyMana(1)); - } - return netManas; - } - - @Override - public Mana produceMana(Game game, Ability source) { - Mana mana = new Mana(); - if (game == null) { - return mana; - } - Mana types = getManaTypes(game, source); - Choice choice = new ChoiceColor(true); - choice.getChoices().clear(); - choice.setMessage("Pick a mana color"); - if (types.getBlack() > 0) { - choice.getChoices().add("Black"); - } - if (types.getRed() > 0) { - choice.getChoices().add("Red"); - } - if (types.getBlue() > 0) { - choice.getChoices().add("Blue"); - } - if (types.getGreen() > 0) { - choice.getChoices().add("Green"); - } - if (types.getWhite() > 0) { - choice.getChoices().add("White"); - } - if (types.getColorless() > 0) { - choice.getChoices().add("Colorless"); - } - if (types.getAny() > 0) { - choice.getChoices().add("Black"); - choice.getChoices().add("Red"); - choice.getChoices().add("Blue"); - choice.getChoices().add("Green"); - choice.getChoices().add("White"); - choice.getChoices().add("Colorless"); - - } - if (!choice.getChoices().isEmpty()) { - Player player = game.getPlayer(source.getControllerId()); - if (choice.getChoices().size() == 1) { - choice.setChoice(choice.getChoices().iterator().next()); - } else { - if (player == null - || !player.choose(Outcome.Neutral, choice, game)) { - return mana; - } - } - if (choice.getChoice() != null) { - switch (choice.getChoice()) { - case "Black": - mana.setBlack(1); - break; - case "Blue": - mana.setBlue(1); - break; - case "Red": - mana.setRed(1); - break; - case "Green": - mana.setGreen(1); - break; - case "White": - mana.setWhite(1); - break; - case "Colorless": - mana.setColorless(1); - break; - } - } - } - return mana; - } - - private Mana getManaTypes(Game game, Ability source) { - Mana types = new Mana(); - if (game == null - || game.getPhase() == null) { - return types; - } - Permanent land = (Permanent) game.getState().getValue("UntapTargetCost" + source.getSourceId().toString()); - if (land != null) { - System.out.println("The land is " + land.getName()); - Abilities mana = land.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD); - for (ActivatedManaAbilityImpl ability : mana) { - if (ability.definesMana(game)) { - for (Mana netMana : ability.getNetMana(game)) { - types.add(netMana); - } - } - } - } - System.out.println("The types : " + types.toString()); - return types; - } - - @Override - public BenthicExplorersManaEffect copy() { - return new BenthicExplorersManaEffect(this); - } -} +package mage.cards.b; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Abilities; +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ManaEffect; +import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceColor; +import mage.constants.*; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetLandPermanent; +import mage.util.CardUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class BenthicExplorers extends CardImpl { + + private static final FilterLandPermanent filter = new FilterLandPermanent("tapped land an opponent controls"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(TappedPredicate.instance); + } + + public BenthicExplorers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.MERFOLK); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // {T}, Untap a tapped land an opponent controls: Add one mana of any type that land could produce. + Ability ability = new BenthicExplorersManaAbility(); + ability.addCost(new BenthicExplorersCost( + new TargetLandPermanent(1, 1, filter, true) + )); + this.addAbility(ability); + } + + private BenthicExplorers(final BenthicExplorers card) { + super(card); + } + + @Override + public BenthicExplorers copy() { + return new BenthicExplorers(this); + } +} + +class BenthicExplorersCost extends CostImpl { + + TargetLandPermanent target; + + public BenthicExplorersCost(TargetLandPermanent target) { + this.target = target; + this.text = "Untap " + CardUtil.numberToText(target.getMaxNumberOfTargets(), "") + ' ' + target.getTargetName(); + } + + public BenthicExplorersCost(final BenthicExplorersCost cost) { + super(cost); + this.target = cost.target.copy(); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + if (target.choose(Outcome.Untap, controllerId, sourceId, game)) { + for (UUID targetId : target.getTargets()) { + Permanent permanent = game.getPermanent(targetId); + if (permanent == null) { + return false; + } + paid |= permanent.untap(game); + if (paid) { + game.getState().setValue("UntapTargetCost" + ability.getSourceId().toString(), permanent); // remember the untapped permanent + } + } + } + return paid; + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + return target.canChoose(controllerId, game); + } + + @Override + public BenthicExplorersCost copy() { + return new BenthicExplorersCost(this); + } + +} + +class BenthicExplorersManaAbility extends ActivatedManaAbilityImpl { + + BenthicExplorersManaAbility() { + super(Zone.BATTLEFIELD, new BenthicExplorersManaEffect(), new TapSourceCost()); + } + + private BenthicExplorersManaAbility(BenthicExplorersManaAbility ability) { + super(ability); + } + + @Override + public BenthicExplorersManaAbility copy() { + return new BenthicExplorersManaAbility(this); + } +} + +class BenthicExplorersManaEffect extends ManaEffect { + + public BenthicExplorersManaEffect() { + staticText = "Add one mana of any type that land could produce"; + } + + private BenthicExplorersManaEffect(final BenthicExplorersManaEffect effect) { + super(effect); + } + + @Override + public List getNetMana(Game game, Ability source) { + List netManas = new ArrayList<>(); + if (game == null) { + return netManas; + } + + Mana types = new Mana(); + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(opponentId)) { + if (permanent.isLand() && permanent.isTapped()) { + for (ActivatedManaAbilityImpl ability : permanent.getAbilities(game).getActivatedManaAbilities(Zone.BATTLEFIELD)) { + for (Mana mana : ability.getNetMana(game)) { + types.add(mana); + } + } + } + } + } + + if (types.getBlack() > 0) { + netManas.add(new Mana(ColoredManaSymbol.B)); + } + if (types.getRed() > 0) { + netManas.add(new Mana(ColoredManaSymbol.R)); + } + if (types.getBlue() > 0) { + netManas.add(new Mana(ColoredManaSymbol.U)); + } + if (types.getGreen() > 0) { + netManas.add(new Mana(ColoredManaSymbol.G)); + } + if (types.getWhite() > 0) { + netManas.add(new Mana(ColoredManaSymbol.W)); + } + if (types.getColorless() > 0) { + netManas.add(Mana.ColorlessMana(1)); + } + if (types.getAny() > 0) { + netManas.add(Mana.AnyMana(1)); + } + return netManas; + } + + @Override + public Mana produceMana(Game game, Ability source) { + Mana mana = new Mana(); + if (game == null) { + return mana; + } + Mana types = getManaTypes(game, source); + Choice choice = new ChoiceColor(true); + choice.getChoices().clear(); + choice.setMessage("Pick a mana color"); + if (types.getBlack() > 0) { + choice.getChoices().add("Black"); + } + if (types.getRed() > 0) { + choice.getChoices().add("Red"); + } + if (types.getBlue() > 0) { + choice.getChoices().add("Blue"); + } + if (types.getGreen() > 0) { + choice.getChoices().add("Green"); + } + if (types.getWhite() > 0) { + choice.getChoices().add("White"); + } + if (types.getColorless() > 0) { + choice.getChoices().add("Colorless"); + } + if (types.getAny() > 0) { + choice.getChoices().add("Black"); + choice.getChoices().add("Red"); + choice.getChoices().add("Blue"); + choice.getChoices().add("Green"); + choice.getChoices().add("White"); + choice.getChoices().add("Colorless"); + + } + if (!choice.getChoices().isEmpty()) { + Player player = game.getPlayer(source.getControllerId()); + if (choice.getChoices().size() == 1) { + choice.setChoice(choice.getChoices().iterator().next()); + } else { + if (player == null + || !player.choose(Outcome.Neutral, choice, game)) { + return mana; + } + } + if (choice.getChoice() != null) { + switch (choice.getChoice()) { + case "Black": + mana.setBlack(1); + break; + case "Blue": + mana.setBlue(1); + break; + case "Red": + mana.setRed(1); + break; + case "Green": + mana.setGreen(1); + break; + case "White": + mana.setWhite(1); + break; + case "Colorless": + mana.setColorless(1); + break; + } + } + } + return mana; + } + + private Mana getManaTypes(Game game, Ability source) { + Mana types = new Mana(); + if (game == null + || game.getPhase() == null) { + return types; + } + Permanent land = (Permanent) game.getState().getValue("UntapTargetCost" + source.getSourceId().toString()); + if (land != null) { + Abilities mana = land.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD); + for (ActivatedManaAbilityImpl ability : mana) { + if (ability.definesMana(game)) { + for (Mana netMana : ability.getNetMana(game)) { + types.add(netMana); + } + } + } + } + return types; + } + + @Override + public BenthicExplorersManaEffect copy() { + return new BenthicExplorersManaEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BibFortuna.java b/Mage.Sets/src/mage/cards/b/BibFortuna.java index c2a33363c8..8ac710afa3 100644 --- a/Mage.Sets/src/mage/cards/b/BibFortuna.java +++ b/Mage.Sets/src/mage/cards/b/BibFortuna.java @@ -4,7 +4,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.effects.common.ShuffleLibrarySourceEffect; @@ -35,7 +35,7 @@ public final class BibFortuna extends CardImpl { this.addAbility(ability); // When Bib Fortuna dies shuffle your library. - this.addAbility(new DiesTriggeredAbility(new ShuffleLibrarySourceEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new ShuffleLibrarySourceEffect())); } public BibFortuna(final BibFortuna card) { diff --git a/Mage.Sets/src/mage/cards/b/BileBlight.java b/Mage.Sets/src/mage/cards/b/BileBlight.java index 0b23756190..77b3e4f0fb 100644 --- a/Mage.Sets/src/mage/cards/b/BileBlight.java +++ b/Mage.Sets/src/mage/cards/b/BileBlight.java @@ -61,7 +61,7 @@ class BileBlightEffect extends BoostAllEffect { } else { String name = target.getName(); for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - if (CardUtil.haveSameNames(perm.getName(), name)) { + if (CardUtil.haveSameNames(perm, name, game)) { affectedObjectList.add(new MageObjectReference(perm, game)); } } diff --git a/Mage.Sets/src/mage/cards/b/BiomancersFamiliar.java b/Mage.Sets/src/mage/cards/b/BiomancersFamiliar.java index ced7a58d5f..e6518e1d41 100644 --- a/Mage.Sets/src/mage/cards/b/BiomancersFamiliar.java +++ b/Mage.Sets/src/mage/cards/b/BiomancersFamiliar.java @@ -11,7 +11,6 @@ import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.choices.ChoiceImpl; import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; @@ -20,8 +19,6 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; -import java.util.LinkedHashSet; -import java.util.Set; import java.util.UUID; /** @@ -36,7 +33,8 @@ public final class BiomancersFamiliar extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // Activated abilities of creatures you control cost {2} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana. + // Activated abilities of creatures you control cost {2} less to activate. + // This effect can't reduce the amount of mana an ability costs to activate to less than one mana. this.addAbility(new SimpleStaticAbility(new BiomancersFamiliarCostReductionEffect())); // {T}: The next time target creature adapts this turn, it adapts as though it had no +1/+1 counters on it. @@ -57,8 +55,9 @@ public final class BiomancersFamiliar extends CardImpl { class BiomancersFamiliarCostReductionEffect extends CostModificationEffectImpl { - private static final String effectText = "Activated abilities of creatures you control cost {2} less to activate. " + - "This effect can't reduce the amount of mana an ability costs to activate to less than one mana"; + private static final String effectText = "Activated abilities of creatures " + + "you control cost {2} less to activate. " + + "This effect can't reduce the mana in that cost to less than one mana"; BiomancersFamiliarCostReductionEffect() { super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); @@ -86,19 +85,7 @@ class BiomancersFamiliarCostReductionEffect extends CostModificationEffectImpl { if (reduceMax <= 0) { return true; } - ChoiceImpl choice = new ChoiceImpl(true); - Set set = new LinkedHashSet<>(); - - for (int i = 0; i <= reduceMax; i++) { - set.add(String.valueOf(i)); - } - choice.setChoices(set); - choice.setMessage("Reduce ability cost"); - if (!controller.choose(Outcome.Benefit, choice, game)) { - return false; - } - int reduce = Integer.parseInt(choice.getChoice()); - CardUtil.reduceCost(abilityToModify, reduce); + CardUtil.reduceCost(abilityToModify, reduceMax); return true; } @@ -111,8 +98,9 @@ class BiomancersFamiliarCostReductionEffect extends CostModificationEffectImpl { return false; } //Activated abilities of creatures you control - Permanent permanent = game.getPermanent(abilityToModify.getSourceId()); - return permanent != null && permanent.isCreature() + Permanent permanent = game.getPermanentOrLKIBattlefield(abilityToModify.getSourceId()); + return permanent != null + && permanent.isCreature() && permanent.isControlledBy(source.getControllerId()); } @@ -126,8 +114,8 @@ class BiomancersFamiliarReplacementEffect extends ReplacementEffectImpl { BiomancersFamiliarReplacementEffect() { super(Duration.EndOfTurn, Outcome.Benefit); - staticText = "The next time target creature adapts this turn, " + - "it adapts as though it had no +1/+1 counters on it."; + staticText = "The next time target creature adapts this turn, " + + "it adapts as though it had no +1/+1 counters on it."; } private BiomancersFamiliarReplacementEffect(final BiomancersFamiliarReplacementEffect effect) { @@ -155,4 +143,4 @@ class BiomancersFamiliarReplacementEffect extends ReplacementEffectImpl { discard(); return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/b/BiomanticMastery.java b/Mage.Sets/src/mage/cards/b/BiomanticMastery.java index 7b12e0073c..c0c5d1f57d 100644 --- a/Mage.Sets/src/mage/cards/b/BiomanticMastery.java +++ b/Mage.Sets/src/mage/cards/b/BiomanticMastery.java @@ -63,7 +63,7 @@ class BiomanticMasteryEffect extends OneShotEffect { Player player = game.getPlayer(playerId); if (player != null) { int creatures = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, playerId, game); - controller.drawCards(creatures, game); + controller.drawCards(creatures, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/b/BirchloreRangers.java b/Mage.Sets/src/mage/cards/b/BirchloreRangers.java index 812fdb387b..1afa11758a 100644 --- a/Mage.Sets/src/mage/cards/b/BirchloreRangers.java +++ b/Mage.Sets/src/mage/cards/b/BirchloreRangers.java @@ -1,19 +1,26 @@ - package mage.cards.b; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; import mage.abilities.costs.common.TapTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; import mage.abilities.keyword.MorphAbility; -import mage.abilities.mana.AnyColorManaAbility; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -22,22 +29,22 @@ import mage.target.common.TargetControlledCreaturePermanent; */ public final class BirchloreRangers extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Elves you control"); - - static { - filter.add(Predicates.not(TappedPredicate.instance)); - filter.add(SubType.ELF.getPredicate()); - } - public BirchloreRangers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); this.subtype.add(SubType.ELF, SubType.DRUID); this.power = new MageInt(1); this.toughness = new MageInt(1); // Tap two untapped Elves you control: Add one mana of any color. - this.addAbility(new AnyColorManaAbility(new TapTargetCost(new TargetControlledCreaturePermanent(2, 2, filter, false)))); + FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Elves you control"); + filter.add(Predicates.not(TappedPredicate.instance)); + filter.add(SubType.ELF.getPredicate()); + this.addAbility(new SimpleManaAbility( + Zone.BATTLEFIELD, + new BirchloreRangersManaEffect(filter), + new TapTargetCost(new TargetControlledCreaturePermanent(2, 2, filter, false)))); + // Morph {G} this.addAbility(new MorphAbility(this, new ManaCostsImpl("{G}"))); } @@ -51,3 +58,37 @@ public final class BirchloreRangers extends CardImpl { return new BirchloreRangers(this); } } + +class BirchloreRangersManaEffect extends AddManaOfAnyColorEffect { + + private final FilterPermanent filter; + + public BirchloreRangersManaEffect(FilterPermanent filter) { + super(1); + this.filter = filter; + } + + public BirchloreRangersManaEffect(final BirchloreRangersManaEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + } + + @Override + public BirchloreRangersManaEffect copy() { + return new BirchloreRangersManaEffect(this); + } + + @Override + public List getNetMana(Game game, Ability source) { + if (game != null && game.inCheckPlayableState()) { + int count = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) / 2; + List netMana = new ArrayList<>(); + if (count > 0) { + netMana.add(Mana.AnyMana(count * 2)); + } + return netMana; + } + return super.getNetMana(game, source); + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BishopOfTheBloodstained.java b/Mage.Sets/src/mage/cards/b/BishopOfTheBloodstained.java index 7454450cfd..d36f35dd5a 100644 --- a/Mage.Sets/src/mage/cards/b/BishopOfTheBloodstained.java +++ b/Mage.Sets/src/mage/cards/b/BishopOfTheBloodstained.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -15,8 +13,9 @@ import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class BishopOfTheBloodstained extends CardImpl { @@ -36,7 +35,7 @@ public final class BishopOfTheBloodstained extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - // When Bishop of the Bloodstained enters the battlefield, target opponent loses 1 life for each vampire you control. + // When Bishop of the Bloodstained enters the battlefield, target opponent loses 1 life for each Vampire you control. Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(new PermanentsOnBattlefieldCount(filter))); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BitterheartWitch.java b/Mage.Sets/src/mage/cards/b/BitterheartWitch.java index 4527fc3c3a..2b4b0623a4 100644 --- a/Mage.Sets/src/mage/cards/b/BitterheartWitch.java +++ b/Mage.Sets/src/mage/cards/b/BitterheartWitch.java @@ -4,7 +4,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.cards.Card; @@ -36,7 +36,7 @@ public final class BitterheartWitch extends CardImpl { this.addAbility(DeathtouchAbility.getInstance()); // When Bitterheart Witch dies, you may search your library for a Curse card, put it onto the battlefield attached to target player, then shuffle your library. - Ability ability = new DiesTriggeredAbility(new BitterheartWitchEffect(), true); + Ability ability = new DiesSourceTriggeredAbility(new BitterheartWitchEffect(), true); ability.addTarget(new TargetPlayer()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BlackCat.java b/Mage.Sets/src/mage/cards/b/BlackCat.java index d6f263c907..f12607a99c 100644 --- a/Mage.Sets/src/mage/cards/b/BlackCat.java +++ b/Mage.Sets/src/mage/cards/b/BlackCat.java @@ -32,7 +32,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -54,7 +54,7 @@ public final class BlackCat extends CardImpl { this.toughness = new MageInt(1); // When Black Cat dies, target opponent discards a card at random. - Ability ability = new DiesTriggeredAbility(new DiscardTargetEffect(1, true),false); + Ability ability = new DiesSourceTriggeredAbility(new DiscardTargetEffect(1, true),false); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BlackManaBattery.java b/Mage.Sets/src/mage/cards/b/BlackManaBattery.java index 0aa099282e..72f4b37d46 100644 --- a/Mage.Sets/src/mage/cards/b/BlackManaBattery.java +++ b/Mage.Sets/src/mage/cards/b/BlackManaBattery.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -28,18 +27,21 @@ public final class BlackManaBattery extends CardImpl { public BlackManaBattery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {2}, {tap}: Put a charge counter on Black Mana Battery. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance(1)), new GenericManaCost(2)); + // {2}, {T}: Put a charge counter on Black Mana Battery. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)), + new GenericManaCost(2)); ability.addCost(new TapSourceCost()); this.addAbility(ability); - // {tap}, Remove any number of charge counters from Black Mana Battery: Add {B}, then add an additional {B} for each charge counter removed this way. + // {T}, Remove any number of charge counters from Black Mana Battery: + // Add {B}, then add an additional {B} for each charge counter removed this way. ability = new DynamicManaAbility( Mana.BlackMana(1), new IntPlusDynamicValue(1, RemovedCountersForCostValue.instance), new TapSourceCost(), "Add {B}, then add {B} for each charge counter removed this way", - true, new CountersSourceCount(CounterType.CHARGE)); + true, new IntPlusDynamicValue(1, new CountersSourceCount(CounterType.CHARGE))); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE.createInstance(), "Remove any number of charge counters from {this}")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BlackbladeReforged.java b/Mage.Sets/src/mage/cards/b/BlackbladeReforged.java index 5750ed8ae1..884afe2711 100644 --- a/Mage.Sets/src/mage/cards/b/BlackbladeReforged.java +++ b/Mage.Sets/src/mage/cards/b/BlackbladeReforged.java @@ -11,7 +11,6 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.keyword.EquipAbility; -import mage.abilities.keyword.EquipFilterAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -19,6 +18,7 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import java.util.UUID; +import mage.target.common.TargetControlledCreaturePermanent; /** * @author Rystan @@ -43,7 +43,7 @@ public final class BlackbladeReforged extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(count, count))); // Equip legendary creature (3) - this.addAbility(new EquipFilterAbility(filter, new GenericManaCost(3))); + this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(3), new TargetControlledCreaturePermanent(filter))); // Equip {7} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(7))); diff --git a/Mage.Sets/src/mage/cards/b/BladeBanish.java b/Mage.Sets/src/mage/cards/b/BladeBanish.java new file mode 100644 index 0000000000..8caceb0312 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BladeBanish.java @@ -0,0 +1,42 @@ +package mage.cards.b; + +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BladeBanish extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creature with power 4 or greater"); + + static { + filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); + } + + public BladeBanish(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); + + // Exile target creature with power 4 or greater. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + } + + private BladeBanish(final BladeBanish card) { + super(card); + } + + @Override + public BladeBanish copy() { + return new BladeBanish(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java b/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java index 53de13427a..f35ba8ab4c 100644 --- a/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java +++ b/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -9,15 +7,18 @@ import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author North */ public final class BladeTribeBerserkers extends CardImpl { @@ -25,7 +26,7 @@ public final class BladeTribeBerserkers extends CardImpl { private static final String effectText = "Metalcraft — When Blade-Tribe Berserkers enters the battlefield, if you control three or more artifacts, Blade-Tribe Berserkers gets +3/+3 and gains haste until end of turn."; public BladeTribeBerserkers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.HUMAN, SubType.BERSERKER); this.power = new MageInt(3); @@ -34,7 +35,10 @@ public final class BladeTribeBerserkers extends CardImpl { //Metalcraft — When Blade-Tribe Berserkers enters the battlefield, if you control three or more artifacts, Blade-Tribe Berserkers gets +3/+3 and gains haste until end of turn. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new BoostSourceEffect(3, 3, Duration.EndOfTurn), false); ability.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MetalcraftCondition.instance, effectText)); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MetalcraftCondition.instance, effectText) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); } public BladeTribeBerserkers(final BladeTribeBerserkers card) { diff --git a/Mage.Sets/src/mage/cards/b/BlastOfGenius.java b/Mage.Sets/src/mage/cards/b/BlastOfGenius.java index b18d881973..5fe18c9ded 100644 --- a/Mage.Sets/src/mage/cards/b/BlastOfGenius.java +++ b/Mage.Sets/src/mage/cards/b/BlastOfGenius.java @@ -1,5 +1,6 @@ package mage.cards.b; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -13,8 +14,6 @@ import mage.players.Player; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetDiscard; -import java.util.UUID; - /** * @author LevelX2 */ @@ -43,7 +42,7 @@ class BlastOfGeniusEffect extends OneShotEffect { public BlastOfGeniusEffect() { super(Outcome.Benefit); - this.staticText = "Choose any target. Draw three cards and discard a card. Blast of Genius deals damage equal to the converted mana cost of the discard card to that permanent or player"; + this.staticText = "Choose any target. Draw three cards and discard a card. {this} deals damage equal to the converted mana cost of the discard card to that permanent or player"; } public BlastOfGeniusEffect(final BlastOfGeniusEffect effect) { @@ -59,7 +58,7 @@ class BlastOfGeniusEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); TargetDiscard target = new TargetDiscard(player.getId()); if (target.canChoose(source.getSourceId(), player.getId(), game)) { player.choose(Outcome.Discard, target, source.getSourceId(), game); diff --git a/Mage.Sets/src/mage/cards/b/BlastZone.java b/Mage.Sets/src/mage/cards/b/BlastZone.java index 982b139f1c..5f33f3a11d 100644 --- a/Mage.Sets/src/mage/cards/b/BlastZone.java +++ b/Mage.Sets/src/mage/cards/b/BlastZone.java @@ -38,7 +38,8 @@ public final class BlastZone extends CardImpl { // Blast Zone enters the battlefield with a charge counter on it. this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)) - )); + ,"with a charge counter") + ); // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); diff --git a/Mage.Sets/src/mage/cards/b/BlazingEffigy.java b/Mage.Sets/src/mage/cards/b/BlazingEffigy.java index ddf098cd72..d429166ebe 100644 --- a/Mage.Sets/src/mage/cards/b/BlazingEffigy.java +++ b/Mage.Sets/src/mage/cards/b/BlazingEffigy.java @@ -7,7 +7,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; @@ -35,7 +35,7 @@ public final class BlazingEffigy extends CardImpl { this.toughness = new MageInt(3); // When Blazing Effigy dies, it deals X damage to target creature, where X is 3 plus the amount of damage dealt to Blazing Effigy this turn by other sources named Blazing Effigy. - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(BlazingEffigyCount.instance), false); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(BlazingEffigyCount.instance), false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability, new BlazingEffigyWatcher()); } diff --git a/Mage.Sets/src/mage/cards/b/BlazingHellhound.java b/Mage.Sets/src/mage/cards/b/BlazingHellhound.java index 65ed6aee7c..61d0e0f67e 100644 --- a/Mage.Sets/src/mage/cards/b/BlazingHellhound.java +++ b/Mage.Sets/src/mage/cards/b/BlazingHellhound.java @@ -25,7 +25,7 @@ public final class BlazingHellhound extends CardImpl { public BlazingHellhound(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); - this.subtype.add(SubType.ELEMENTAL, SubType.HOUND); + this.subtype.add(SubType.ELEMENTAL, SubType.DOG); this.power = new MageInt(4); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java b/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java index 61ec1d1667..181f01c128 100644 --- a/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java +++ b/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -9,15 +7,18 @@ import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.target.Target; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author North */ public final class BleakCovenVampires extends CardImpl { @@ -25,7 +26,7 @@ public final class BleakCovenVampires extends CardImpl { private static final String effectText = "Metalcraft — When Bleak Coven Vampires enters the battlefield, if you control three or more artifacts, target player loses 4 life and you gain 4 life."; public BleakCovenVampires(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.VAMPIRE, SubType.WARRIOR); this.power = new MageInt(4); @@ -37,7 +38,10 @@ public final class BleakCovenVampires extends CardImpl { Target target = new TargetPlayer(); ability.addTarget(target); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MetalcraftCondition.instance, effectText)); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MetalcraftCondition.instance, effectText) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); } public BleakCovenVampires(final BleakCovenVampires card) { diff --git a/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java b/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java index ac0276b9c5..5f4770a104 100644 --- a/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java +++ b/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java @@ -74,7 +74,7 @@ class BlessedReincarnationEffect extends OneShotEffect { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent != null && controller != null) { controller.moveCards(permanent, Zone.EXILED, source, game); - game.applyEffects(); + game.getState().processAction(game); Player permanentController = game.getPlayer(permanent.getControllerId()); if (permanentController != null) { diff --git a/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java b/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java new file mode 100644 index 0000000000..4cf9a6409d --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java @@ -0,0 +1,49 @@ +package mage.cards.b; + +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.PreventAllNonCombatDamageToAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.UnicornToken; + +import java.util.UUID; + +public class BlessedSanctuary extends CardImpl { + + private static final FilterPermanent filterYourCreatures = new FilterControlledCreaturePermanent("creatures you control"); + private static final FilterControlledCreaturePermanent filterNontoken = new FilterControlledCreaturePermanent("a nontoken creature"); + + static { + filterNontoken.add(Predicates.not(TokenPredicate.instance)); + } + + public BlessedSanctuary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}{W}"); + + //Prevent all noncombat damage that would be dealt to you and creatures you control. + this.addAbility(new SimpleStaticAbility(new PreventAllNonCombatDamageToAllEffect( + Duration.WhileOnBattlefield, filterYourCreatures, true))); + + //Whenever a nontoken creature enters the battlefield under your control, create a 2/2 white Unicorn creature token. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, + new CreateTokenEffect(new UnicornToken()), filterNontoken, false)); + } + + public BlessedSanctuary(final BlessedSanctuary card) { + super(card); + } + + @Override + public BlessedSanctuary copy() { + return new BlessedSanctuary(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlightKeeper.java b/Mage.Sets/src/mage/cards/b/BlightKeeper.java index 985e69d9ec..e72e5b1a9d 100644 --- a/Mage.Sets/src/mage/cards/b/BlightKeeper.java +++ b/Mage.Sets/src/mage/cards/b/BlightKeeper.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -18,8 +16,9 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class BlightKeeper extends CardImpl { @@ -37,7 +36,7 @@ public final class BlightKeeper extends CardImpl { // {7}{B}, {T}, Sacrifice Blight Keeper: Target opponent loses 4 life and you gain 4 life. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(4), new ManaCostsImpl("{7}{B}")); - ability.addEffect(new GainLifeEffect(4).setText("and you gain 4 life")); + ability.addEffect(new GainLifeEffect(4).concatBy("and")); ability.addTarget(new TargetOpponent()); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); diff --git a/Mage.Sets/src/mage/cards/b/BlightedWoodland.java b/Mage.Sets/src/mage/cards/b/BlightedWoodland.java index b69438215f..6bc7cc86d3 100644 --- a/Mage.Sets/src/mage/cards/b/BlightedWoodland.java +++ b/Mage.Sets/src/mage/cards/b/BlightedWoodland.java @@ -31,7 +31,7 @@ public final class BlightedWoodland extends CardImpl { // {3}{G}, {T}, Sacrifice Blighted Woodland: Search your library for up to two basic land cards and put them onto the battlefield tapped. Then shuffle your library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND), true, true), + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LANDS), true, true), new ManaCostsImpl<>("{3}{G}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); diff --git a/Mage.Sets/src/mage/cards/b/BlindFury.java b/Mage.Sets/src/mage/cards/b/BlindFury.java index cd7a9bb007..5f82b75538 100644 --- a/Mage.Sets/src/mage/cards/b/BlindFury.java +++ b/Mage.Sets/src/mage/cards/b/BlindFury.java @@ -30,7 +30,7 @@ public final class BlindFury extends CardImpl { this.getSpellAbility().addEffect(new LoseAbilityAllEffect( TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES - ).setText("All creatures lose trample until end of turn.")); + ).setText("All creatures lose trample until end of turn")); this.getSpellAbility().addEffect(new FurnaceOfRathEffect()); } diff --git a/Mage.Sets/src/mage/cards/b/BlindingFlare.java b/Mage.Sets/src/mage/cards/b/BlindingFlare.java index 3055e1ed58..8c9edf71a6 100644 --- a/Mage.Sets/src/mage/cards/b/BlindingFlare.java +++ b/Mage.Sets/src/mage/cards/b/BlindingFlare.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.common.combat.CantBlockTargetEffect; import mage.cards.CardImpl; @@ -10,18 +8,19 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class BlindingFlare extends CardImpl { public BlindingFlare(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); // Strive — Blinding Flare costs {R} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{R}")); + // Any number of target creatures can't block this turn. this.getSpellAbility().addEffect(new CantBlockTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE)); diff --git a/Mage.Sets/src/mage/cards/b/Blistergrub.java b/Mage.Sets/src/mage/cards/b/Blistergrub.java index 7519cb0dd5..198b9e0537 100644 --- a/Mage.Sets/src/mage/cards/b/Blistergrub.java +++ b/Mage.Sets/src/mage/cards/b/Blistergrub.java @@ -4,7 +4,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.abilities.keyword.SwampwalkAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class Blistergrub extends CardImpl { // Swampwalk (This creature can't be blocked as long as defending player controls a Swamp.) this.addAbility(new SwampwalkAbility()); // When Blistergrub dies, each opponent loses 2 life. - this.addAbility(new DiesTriggeredAbility(new LoseLifeOpponentsEffect(2), false)); + this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeOpponentsEffect(2), false)); } public Blistergrub (final Blistergrub card) { diff --git a/Mage.Sets/src/mage/cards/b/Blisterpod.java b/Mage.Sets/src/mage/cards/b/Blisterpod.java index b3644a8392..217161f196 100644 --- a/Mage.Sets/src/mage/cards/b/Blisterpod.java +++ b/Mage.Sets/src/mage/cards/b/Blisterpod.java @@ -3,7 +3,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DevoidAbility; @@ -30,7 +30,7 @@ public final class Blisterpod extends CardImpl { // When Blisterpod dies, create a 1/1 colorless Eldrazi Scion creature token. It has "Sacrifice this creature: Add {C}." Effect effect = new CreateTokenEffect(new EldraziScionToken()); effect.setText("Create a 1/1 colorless Eldrazi Scion creature token. It has \"Sacrifice this creature: Add {C}.\""); - this.addAbility(new DiesTriggeredAbility(effect, false)); + this.addAbility(new DiesSourceTriggeredAbility(effect, false)); } public Blisterpod(final Blisterpod card) { diff --git a/Mage.Sets/src/mage/cards/b/BlisterspitGremlin.java b/Mage.Sets/src/mage/cards/b/BlisterspitGremlin.java new file mode 100644 index 0000000000..1e50b96692 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlisterspitGremlin.java @@ -0,0 +1,53 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlisterspitGremlin extends CardImpl { + + public BlisterspitGremlin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.GREMLIN); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {1}, {T}: Blisterspit Gremlin deals 1 damage to each opponent. + Ability ability = new SimpleActivatedAbility( + new DamagePlayersEffect(1, TargetController.OPPONENT), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // Whenever you cast a noncreature spell, untap Blisterspit Gremlin. + this.addAbility(new SpellCastControllerTriggeredAbility( + new UntapSourceEffect(), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + )); + } + + private BlisterspitGremlin(final BlisterspitGremlin card) { + super(card); + } + + @Override + public BlisterspitGremlin copy() { + return new BlisterspitGremlin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlitzLeech.java b/Mage.Sets/src/mage/cards/b/BlitzLeech.java new file mode 100644 index 0000000000..55336f12e6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlitzLeech.java @@ -0,0 +1,84 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class BlitzLeech extends CardImpl { + + public BlitzLeech(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}"); + + this.subtype.add(SubType.LEECH); + this.power = new MageInt(5); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Blitz Leech enters the battlefield, target creature an opponent controls gets -2/-2 until end of turn. Remove all counters from that creature. + Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-2, -2)); + ability.addEffect(new BlitzLeechEffect()); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private BlitzLeech(final BlitzLeech card) { + super(card); + } + + @Override + public BlitzLeech copy() { + return new BlitzLeech(this); + } +} + +class BlitzLeechEffect extends OneShotEffect { + + BlitzLeechEffect() { + super(Outcome.Benefit); + staticText = "Remove all counters from that creature."; + } + + private BlitzLeechEffect(final BlitzLeechEffect effect) { + super(effect); + } + + @Override + public BlitzLeechEffect copy() { + return new BlitzLeechEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + Set counterTypes = permanent + .getCounters(game) + .keySet() + .stream() + .collect(Collectors.toSet()); + counterTypes.forEach(permanent.getCounters(game)::removeAllCounters); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BlitzOfTheThunderRaptor.java b/Mage.Sets/src/mage/cards/b/BlitzOfTheThunderRaptor.java new file mode 100644 index 0000000000..04d3efd1c1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlitzOfTheThunderRaptor.java @@ -0,0 +1,44 @@ +package mage.cards.b; + +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.ExileTargetIfDiesEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlitzOfTheThunderRaptor extends CardImpl { + + public static final FilterInstantOrSorceryCard filter + = new FilterInstantOrSorceryCard("instant and sorcery cards"); + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(filter); + + public BlitzOfTheThunderRaptor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Blitz of the Thunder-Raptor deals damage to target creature or planeswalker equal to the number of instant and sorcery cards in your graveyard. If that creature or planeswalker would die this turn, exile it instead. + this.getSpellAbility().addEffect(new DamageTargetEffect(xValue) + .setText("{this} deals damage to target creature or planeswalker " + + "equal to the number of instant and sorcery cards in your graveyard")); + this.getSpellAbility().addEffect(new ExileTargetIfDiesEffect() + .setText("If that creature or planeswalker would die this turn, exile it instead")); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private BlitzOfTheThunderRaptor(final BlitzOfTheThunderRaptor card) { + super(card); + } + + @Override + public BlitzOfTheThunderRaptor copy() { + return new BlitzOfTheThunderRaptor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlizzardStrix.java b/Mage.Sets/src/mage/cards/b/BlizzardStrix.java index aef99000ae..194b0b1629 100644 --- a/Mage.Sets/src/mage/cards/b/BlizzardStrix.java +++ b/Mage.Sets/src/mage/cards/b/BlizzardStrix.java @@ -94,7 +94,7 @@ class BlizzardStrixEffect extends OneShotEffect { if (controller != null && permanent != null && sourcePermanent != null) { if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/b/BloodCurdle.java b/Mage.Sets/src/mage/cards/b/BloodCurdle.java new file mode 100644 index 0000000000..aa8e71aea4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodCurdle.java @@ -0,0 +1,77 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodCurdle extends CardImpl { + + public BloodCurdle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}"); + + // Destroy target creature. Put a menace counter on a creature you control. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new BloodCurdleEffect()); + } + + private BloodCurdle(final BloodCurdle card) { + super(card); + } + + @Override + public BloodCurdle copy() { + return new BloodCurdle(this); + } +} + +class BloodCurdleEffect extends OneShotEffect { + + BloodCurdleEffect() { + super(Outcome.Benefit); + staticText = "Put a menace counter on a creature you control"; + } + + private BloodCurdleEffect(final BloodCurdleEffect effect) { + super(effect); + } + + @Override + public BloodCurdleEffect copy() { + return new BloodCurdleEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Target target = new TargetControlledCreaturePermanent(); + target.setNotTarget(true); + if (!player.choose(outcome, target, source.getSourceId(), game)) { + return false; + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + return false; + } + return permanent.addCounters(CounterType.MENACE.createInstance(), source, game); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BloodGlutton.java b/Mage.Sets/src/mage/cards/b/BloodGlutton.java new file mode 100644 index 0000000000..0936f5a04e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodGlutton.java @@ -0,0 +1,36 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodGlutton extends CardImpl { + + public BloodGlutton(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + } + + private BloodGlutton(final BloodGlutton card) { + super(card); + } + + @Override + public BloodGlutton copy() { + return new BloodGlutton(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodHound.java b/Mage.Sets/src/mage/cards/b/BloodHound.java index f182a4b5e3..98e7ef6b12 100644 --- a/Mage.Sets/src/mage/cards/b/BloodHound.java +++ b/Mage.Sets/src/mage/cards/b/BloodHound.java @@ -26,7 +26,7 @@ public final class BloodHound extends CardImpl { public BloodHound(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/b/BloodOperative.java b/Mage.Sets/src/mage/cards/b/BloodOperative.java index b3cde09e0f..d6358c900c 100644 --- a/Mage.Sets/src/mage/cards/b/BloodOperative.java +++ b/Mage.Sets/src/mage/cards/b/BloodOperative.java @@ -1,97 +1,97 @@ -package mage.cards.b; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; -import mage.abilities.keyword.LifelinkAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; -import mage.target.common.TargetCardInGraveyard; - -/** - * - * @author LevelX2 - */ -public final class BloodOperative extends CardImpl { - - public BloodOperative(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); - - this.subtype.add(SubType.VAMPIRE); - this.subtype.add(SubType.ASSASSIN); - this.power = new MageInt(3); - this.toughness = new MageInt(1); - - // Lifelink - this.addAbility(LifelinkAbility.getInstance()); - - // When Blood Operative enters the battlefield, you may exile target card from a graveyard. - Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect(), true); - ability.addTarget(new TargetCardInGraveyard()); - this.addAbility(ability); - - // Whenever you surveil, if Blood Operative is in your graveyard, you may pay 3 life. If you do, return Blood Operative to your hand. - this.addAbility(new BloodOperativeTriggeredAbility()); - } - - public BloodOperative(final BloodOperative card) { - super(card); - } - - @Override - public BloodOperative copy() { - return new BloodOperative(this); - } -} - -class BloodOperativeTriggeredAbility extends TriggeredAbilityImpl { - - public BloodOperativeTriggeredAbility() { - super(Zone.GRAVEYARD, new DoIfCostPaid(new ReturnSourceFromGraveyardToHandEffect(), new PayLifeCost(3)), false); - } - - public BloodOperativeTriggeredAbility(final BloodOperativeTriggeredAbility ability) { - super(ability); - } - - @Override - public BloodOperativeTriggeredAbility copy() { - return new BloodOperativeTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.SURVEILED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(getControllerId()); - } - - @Override - public boolean checkInterveningIfClause(Game game) { - Player controller = game.getPlayer(getControllerId()); - if (controller != null && controller.getGraveyard().contains(getSourceId())) { - return super.checkInterveningIfClause(game); - } - return false; - } - - @Override - public String getRule() { - return "Whenever you surveil, if {this} is in your graveyard, you may pay 3 life. If you do, return {this} to your hand."; - } -} +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; + +/** + * + * @author LevelX2 + */ +public final class BloodOperative extends CardImpl { + + public BloodOperative(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.ASSASSIN); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // When Blood Operative enters the battlefield, you may exile target card from a graveyard. + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect(), true); + ability.addTarget(new TargetCardInGraveyard()); + this.addAbility(ability); + + // Whenever you surveil, if Blood Operative is in your graveyard, you may pay 3 life. If you do, return Blood Operative to your hand. + this.addAbility(new BloodOperativeTriggeredAbility()); + } + + public BloodOperative(final BloodOperative card) { + super(card); + } + + @Override + public BloodOperative copy() { + return new BloodOperative(this); + } +} + +class BloodOperativeTriggeredAbility extends TriggeredAbilityImpl { + + public BloodOperativeTriggeredAbility() { + super(Zone.GRAVEYARD, new DoIfCostPaid(new ReturnSourceFromGraveyardToHandEffect(), new PayLifeCost(3)), false); + } + + public BloodOperativeTriggeredAbility(final BloodOperativeTriggeredAbility ability) { + super(ability); + } + + @Override + public BloodOperativeTriggeredAbility copy() { + return new BloodOperativeTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SURVEILED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals(getControllerId()); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + Player controller = game.getPlayer(getControllerId()); + if (controller != null && controller.getGraveyard().contains(getSourceId())) { + return super.checkInterveningIfClause(game); + } + return false; + } + + @Override + public String getRule() { + return "Whenever you surveil, if {this} is in your graveyard, you may pay 3 life. If you do, return {this} to your hand."; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodScrivener.java b/Mage.Sets/src/mage/cards/b/BloodScrivener.java index ee24474fbd..1b8a7a5ea5 100644 --- a/Mage.Sets/src/mage/cards/b/BloodScrivener.java +++ b/Mage.Sets/src/mage/cards/b/BloodScrivener.java @@ -70,7 +70,7 @@ class BloodScrivenerReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getPlayerId()); if (player != null) { - player.drawCards(2, game, event.getAppliedEffects()); + player.drawCards(2, event.getSourceId(), game, event.getAppliedEffects()); player.loseLife(1, game, false); } return true; diff --git a/Mage.Sets/src/mage/cards/b/BloodSun.java b/Mage.Sets/src/mage/cards/b/BloodSun.java index 227b656eaf..0a9e2dd853 100644 --- a/Mage.Sets/src/mage/cards/b/BloodSun.java +++ b/Mage.Sets/src/mage/cards/b/BloodSun.java @@ -1,6 +1,8 @@ package mage.cards.b; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -70,7 +72,13 @@ class BloodSunEffect extends ContinuousEffectImpl { for (Permanent permanent : game.getState().getBattlefield().getActivePermanents(StaticFilters.FILTER_LANDS, player.getId(), source.getSourceId(), game)) { switch (layer) { case AbilityAddingRemovingEffects_6: - permanent.getAbilities().removeIf(ability -> ability.getAbilityType() != AbilityType.MANA); + List toRemove = new ArrayList<>(); + permanent.getAbilities().forEach(a -> { + if (a.getAbilityType() != AbilityType.MANA) { + toRemove.add(a); + } + }); + permanent.removeAbilities(toRemove, source.getSourceId(), game); break; } } diff --git a/Mage.Sets/src/mage/cards/b/BloodTribute.java b/Mage.Sets/src/mage/cards/b/BloodTribute.java index 694c16ac06..5bc65b7e7e 100644 --- a/Mage.Sets/src/mage/cards/b/BloodTribute.java +++ b/Mage.Sets/src/mage/cards/b/BloodTribute.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.common.KickedCondition; import mage.abilities.costs.common.TapTargetCost; @@ -24,8 +22,9 @@ import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author North */ public final class BloodTribute extends CardImpl { @@ -41,8 +40,8 @@ public final class BloodTribute extends CardImpl { this.addAbility(new KickerAbility(new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); // Target opponent loses half their life, rounded up. - this.getSpellAbility().addTarget(new TargetOpponent()); this.getSpellAbility().addEffect(new BloodTributeLoseLifeEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); // If Blood Tribute was kicked, you gain life equal to the life lost this way. Effect effect = new ConditionalOneShotEffect( diff --git a/Mage.Sets/src/mage/cards/b/BloodbornScoundrels.java b/Mage.Sets/src/mage/cards/b/BloodbornScoundrels.java index 87837760ce..b55b53e9ba 100644 --- a/Mage.Sets/src/mage/cards/b/BloodbornScoundrels.java +++ b/Mage.Sets/src/mage/cards/b/BloodbornScoundrels.java @@ -1,21 +1,20 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; -import mage.constants.SubType; import mage.abilities.keyword.AssistAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class BloodbornScoundrels extends CardImpl { @@ -33,7 +32,7 @@ public final class BloodbornScoundrels extends CardImpl { // When Bloodborn Scoundrels enters the battlefield, target opponent loses 2 life and you gain 2 life. Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(2)); - ability.addEffect(new GainLifeEffect(2).setText("and you gain 2 life")); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BloodsoakedChampion.java b/Mage.Sets/src/mage/cards/b/BloodsoakedChampion.java index a2feaca2e7..a930f902be 100644 --- a/Mage.Sets/src/mage/cards/b/BloodsoakedChampion.java +++ b/Mage.Sets/src/mage/cards/b/BloodsoakedChampion.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CantBlockAbility; @@ -9,15 +7,18 @@ import mage.abilities.condition.common.RaidCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author emerald000 */ public final class BloodsoakedChampion extends CardImpl { @@ -32,13 +33,15 @@ public final class BloodsoakedChampion extends CardImpl { // Bloodstained Brave can't block. this.addAbility(new CantBlockAbility()); - // Raid — {1}{B}: Return Bloodstained Brave from your graveyard to the battlefield. Activate this ability only if you attacked with a creature this turn. + // Raid — {1}{B}: Return Bloodstained Brave from your graveyard to the battlefield. Activate this ability only if you attacked this turn. Ability ability = new ConditionalActivatedAbility( Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl<>("{1}{B}"), RaidCondition.instance, - "Raid — {1}{B}: Return {this} from your graveyard to the battlefield. Activate this ability only if you attacked with a creature this turn"); + "Raid — {1}{B}: Return {this} from your graveyard to the battlefield. Activate this ability only if you attacked this turn"); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/b/BloodthirstyBlade.java b/Mage.Sets/src/mage/cards/b/BloodthirstyBlade.java index 4e944b1aed..164776fc09 100644 --- a/Mage.Sets/src/mage/cards/b/BloodthirstyBlade.java +++ b/Mage.Sets/src/mage/cards/b/BloodthirstyBlade.java @@ -2,17 +2,16 @@ package mage.cards.b; import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.GoadAttachedAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.combat.AttacksIfAbleAttachedEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; import mage.target.common.TargetOpponentsCreaturePermanent; import java.util.UUID; @@ -28,16 +27,10 @@ public final class BloodthirstyBlade extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +2/+0 and is goaded. - Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 0)); - ability.addEffect(new AttacksIfAbleAttachedEffect( - Duration.WhileOnBattlefield, AttachmentType.EQUIPMENT) - .setText("")); - ability.addEffect(new BloodthirstyBladeAttackEffect() - .setText("goaded (It attacks each combat if able and attacks a player other than you if able.)").concatBy(" is")); - this.addAbility(ability); + this.addAbility(new GoadAttachedAbility(new BoostEquippedEffect(2, 0))); // {1}: Attach Bloodthirsty Blade to target creature an opponent controls. Active this ability only any time you could cast a sorcery. - ability = new ActivateAsSorceryActivatedAbility( + Ability ability = new ActivateAsSorceryActivatedAbility( Zone.BATTLEFIELD, new AttachEffect( Outcome.Detriment, "Attach {this} to target creature an opponent controls" @@ -56,34 +49,3 @@ public final class BloodthirstyBlade extends CardImpl { return new BloodthirstyBlade(this); } } - -class BloodthirstyBladeAttackEffect extends RestrictionEffect { - - BloodthirstyBladeAttackEffect() { - super(Duration.WhileOnBattlefield); - } - - private BloodthirstyBladeAttackEffect(final BloodthirstyBladeAttackEffect effect) { - super(effect); - } - - @Override - public BloodthirstyBladeAttackEffect copy() { - return new BloodthirstyBladeAttackEffect(this); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - Permanent attachment = game.getPermanent(source.getSourceId()); - return attachment != null && attachment.getAttachedTo() != null - && permanent.getId().equals(attachment.getAttachedTo()); - } - - @Override - public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { - if (defenderId == null) { - return true; - } - return !defenderId.equals(source.getControllerId()); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BlowflyInfestation.java b/Mage.Sets/src/mage/cards/b/BlowflyInfestation.java index 77782f57c3..7e0d0e41ab 100644 --- a/Mage.Sets/src/mage/cards/b/BlowflyInfestation.java +++ b/Mage.Sets/src/mage/cards/b/BlowflyInfestation.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -28,11 +27,11 @@ import mage.target.common.TargetCreaturePermanent; * */ public final class BlowflyInfestation extends CardImpl { - + private static final String rule = "Whenever a creature dies, if it had a -1/-1 counter on it, put a -1/-1 counter on target creature."; public BlowflyInfestation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); //Whenever a creature dies, if it had a -1/-1 counter on it, put a -1/-1 counter on target creature. Effect effect = new BlowflyInfestationEffect(); @@ -61,7 +60,7 @@ class BlowflyInfestationCondition implements Condition { public boolean apply(Game game, Ability source) { for (Effect effect : source.getEffects()) { if (effect.getTargetPointer().getFirst(game, source) != null) { - permanent = game.getPermanentOrLKIBattlefield(effect.getTargetPointer().getFirst(game, source)); + permanent = effect.getTargetPointer().getFirstTargetPermanentOrLKI(game, source); } } if (permanent != null) { diff --git a/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java b/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java index 5979a3dca0..71166b33c8 100644 --- a/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java +++ b/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java @@ -122,8 +122,8 @@ class BludgeonBrawlGainAbilityEffect extends ContinuousEffectImpl { Permanent permanent = game.getPermanent(permanentId); if (permanent != null) { int convertedManaCost = permanent.getConvertedManaCost(); - permanent.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(convertedManaCost)), game); - permanent.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(convertedManaCost, 0)), game); + permanent.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(convertedManaCost)), source.getSourceId(), game); + permanent.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(convertedManaCost, 0)), source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/b/BlueManaBattery.java b/Mage.Sets/src/mage/cards/b/BlueManaBattery.java index 05ad3bfc87..05733f41d8 100644 --- a/Mage.Sets/src/mage/cards/b/BlueManaBattery.java +++ b/Mage.Sets/src/mage/cards/b/BlueManaBattery.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -28,18 +27,20 @@ public final class BlueManaBattery extends CardImpl { public BlueManaBattery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {2}, {tap}: Put a charge counter on Blue Mana Battery. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance(1)), new GenericManaCost(2)); + // {2}, {T}: Put a charge counter on Blue Mana Battery. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); this.addAbility(ability); - // {tap}, Remove any number of charge counters from Blue Mana Battery: Add {U}, then add an additional {U} for each charge counter removed this way. + // {T}, Remove any number of charge counters from Blue Mana Battery: Add {U}, + // then add an additional {U} for each charge counter removed this way. ability = new DynamicManaAbility( Mana.BlueMana(1), new IntPlusDynamicValue(1, RemovedCountersForCostValue.instance), new TapSourceCost(), "Add {U}, then add {U} for each charge counter removed this way", - true, new CountersSourceCount(CounterType.CHARGE)); + true, new IntPlusDynamicValue(1, new CountersSourceCount(CounterType.CHARGE))); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE.createInstance(), "Remove any number of charge counters from {this}")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BlurredMongoose.java b/Mage.Sets/src/mage/cards/b/BlurredMongoose.java index 2ea3ba023c..675a16d753 100644 --- a/Mage.Sets/src/mage/cards/b/BlurredMongoose.java +++ b/Mage.Sets/src/mage/cards/b/BlurredMongoose.java @@ -3,7 +3,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.keyword.ShroudAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class BlurredMongoose extends CardImpl { this.toughness = new MageInt(1); // Blurred Mongoose can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); this.addAbility(ShroudAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/b/Blustersquall.java b/Mage.Sets/src/mage/cards/b/Blustersquall.java index 07fec0ff2c..91855c8f5f 100644 --- a/Mage.Sets/src/mage/cards/b/Blustersquall.java +++ b/Mage.Sets/src/mage/cards/b/Blustersquall.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; @@ -11,35 +9,28 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; /** - * * @author LevelX2 */ public final class Blustersquall extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public Blustersquall(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); // Tap target creature you don't control. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.getSpellAbility().addEffect(new TapTargetEffect()); // Overload {3}{U} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.") - this.addAbility(new OverloadAbility(this, new BlustersqallTapAllEffect(filter), new ManaCostsImpl("{3}{U}"))); + this.addAbility(new OverloadAbility(this, new BlustersqallTapAllEffect(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL), new ManaCostsImpl("{3}{U}"))); } @@ -57,13 +48,13 @@ class BlustersqallTapAllEffect extends OneShotEffect { protected FilterCreaturePermanent filter; - public BlustersqallTapAllEffect(FilterCreaturePermanent filter) { + BlustersqallTapAllEffect(FilterCreaturePermanent filter) { super(Outcome.Tap); this.filter = filter; staticText = "Tap each creature you don't control"; } - public BlustersqallTapAllEffect(final BlustersqallTapAllEffect effect) { + private BlustersqallTapAllEffect(final BlustersqallTapAllEffect effect) { super(effect); this.filter = effect.filter; } diff --git a/Mage.Sets/src/mage/cards/b/BodySnatcher.java b/Mage.Sets/src/mage/cards/b/BodySnatcher.java index 2f6e6aeb26..0bdc8fa855 100644 --- a/Mage.Sets/src/mage/cards/b/BodySnatcher.java +++ b/Mage.Sets/src/mage/cards/b/BodySnatcher.java @@ -4,7 +4,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.effects.Effect; @@ -38,7 +38,7 @@ public final class BodySnatcher extends CardImpl { // When Body Snatcher dies, exile Body Snatcher and return target creature card from your graveyard to the battlefield. Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); effect.setText("and return target creature card from your graveyard to the battlefield"); - Ability ability = new DiesTriggeredAbility(new ExileSourceEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect(), false); ability.addEffect(effect); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BogDown.java b/Mage.Sets/src/mage/cards/b/BogDown.java index b136f2e606..64fa2d7913 100644 --- a/Mage.Sets/src/mage/cards/b/BogDown.java +++ b/Mage.Sets/src/mage/cards/b/BogDown.java @@ -8,7 +8,7 @@ import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.StaticFilters; import mage.target.TargetPlayer; import mage.target.common.TargetControlledPermanent; @@ -24,12 +24,12 @@ public final class BogDown extends CardImpl { // Kicker-Sacrifice two lands. this.addAbility(new KickerAbility(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, - new FilterControlledLandPermanent("two lands"), true)))); + StaticFilters.FILTER_CONTROLLED_LAND_SHORT_TEXT, true)))); // Target player discards two cards. If Bog Down was kicked, that player discards three cards instead. this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DiscardTargetEffect(3), new DiscardTargetEffect(2), KickedCondition.instance, - "Target player discards two cards. if this spell was kicked, that player discards three cards instead.")); + "Target player discards two cards. If this spell was kicked, that player discards three cards instead.")); this.getSpellAbility().addTarget(new TargetPlayer()); } diff --git a/Mage.Sets/src/mage/cards/b/BogardanFirefiend.java b/Mage.Sets/src/mage/cards/b/BogardanFirefiend.java index dce2d9e915..9bcf88f9a7 100644 --- a/Mage.Sets/src/mage/cards/b/BogardanFirefiend.java +++ b/Mage.Sets/src/mage/cards/b/BogardanFirefiend.java @@ -4,7 +4,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class BogardanFirefiend extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(2), false); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2), false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java b/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java index e56829c19b..b724e00372 100644 --- a/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java +++ b/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java @@ -3,7 +3,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; @@ -36,7 +36,7 @@ public final class BogardanPhoenix extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Bogardan Phoenix dies, exile it if it had a death counter on it. Otherwise, return it to the battlefield under your control and put a death counter on it. - this.addAbility(new DiesTriggeredAbility(new BogardanPhoenixEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new BogardanPhoenixEffect(), false)); } public BogardanPhoenix(final BogardanPhoenix card) { diff --git a/Mage.Sets/src/mage/cards/b/BolassCitadel.java b/Mage.Sets/src/mage/cards/b/BolassCitadel.java index 2cb133e3d8..03c06175b4 100644 --- a/Mage.Sets/src/mage/cards/b/BolassCitadel.java +++ b/Mage.Sets/src/mage/cards/b/BolassCitadel.java @@ -11,9 +11,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.*; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; @@ -22,8 +20,10 @@ import mage.players.Player; import mage.target.common.TargetControlledPermanent; import mage.util.CardUtil; +import java.util.Objects; import java.util.UUID; + /** * @author jeffwadsworth */ @@ -69,7 +69,7 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl { BolassCitadelPlayTheTopCardEffect() { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.AIDontUseIt); // AI will need help with this - staticText = "You may play the top card of your library. If you cast a spell this way, " + staticText = "You may play lands and cast spells from the top of your library. If you cast a spell this way, " + "pay life equal to its converted mana cost rather than pay its mana cost."; } @@ -94,23 +94,47 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { - Card cardToCheck = game.getCard(objectId); - objectId = CardUtil.getMainCardId(game, objectId); // for split cards - - if (!isAbilityAppliedForAlternateCast(cardToCheck, affectedAbility, playerId, source)) { + if (!Objects.equals(source.getControllerId(), playerId)) { return false; } - Player controller = game.getPlayer(cardToCheck.getOwnerId()); - Card topCard = controller == null ? null : controller.getLibrary().getFromTop(game); - if (topCard != null && objectId.equals(topCard.getId())) { - // add the life cost first - PayLifeCost cost = new PayLifeCost(affectedAbility.getManaCosts().convertedManaCost()); - Costs costs = new CostsImpl(); - costs.add(cost); - costs.addAll(affectedAbility.getCosts()); - controller.setCastSourceIdWithAlternateMana(affectedAbility.getSourceId(), null, costs); + Player player = game.getPlayer(playerId); + if (player != null) { + Card topCard = player.getLibrary().getFromTop(game); + UUID objectIdToCast = CardUtil.getMainCardId(game, objectId); // for adventure cards + if (topCard == null || !topCard.getId().equals(objectIdToCast)) { + return false; + } + if (!topCard.isLand()) { + if (topCard instanceof SplitCard) { + SplitCardHalf leftCard = ((SplitCard) topCard).getLeftHalfCard(); + PayLifeCost lifeCost = new PayLifeCost(leftCard.getSpellAbility().getManaCosts().convertedManaCost()); + Costs leftCosts = new CostsImpl(); + leftCosts.add(lifeCost); + leftCosts.addAll(leftCard.getSpellAbility().getCosts()); + player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCosts); + + SplitCardHalf rightCard = ((SplitCard) topCard).getRightHalfCard(); + lifeCost = new PayLifeCost(rightCard.getSpellAbility().getManaCosts().convertedManaCost()); + Costs rightCosts = new CostsImpl(); + rightCosts.add(lifeCost); + rightCosts.addAll(rightCard.getSpellAbility().getCosts()); + player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCosts); + } else { + if (affectedAbility == null) { + affectedAbility = topCard.getSpellAbility(); + } else { + objectIdToCast = affectedAbility.getSourceId(); + } + PayLifeCost cost = new PayLifeCost(affectedAbility.getManaCosts().convertedManaCost()); + Costs costs = new CostsImpl(); + costs.add(cost); + costs.addAll(affectedAbility.getCosts()); + player.setCastSourceIdWithAlternateMana(objectIdToCast, null, costs); + } + } return true; + } return false; } diff --git a/Mage.Sets/src/mage/cards/b/BoltBend.java b/Mage.Sets/src/mage/cards/b/BoltBend.java index c48d40bee9..1f27c67d0a 100644 --- a/Mage.Sets/src/mage/cards/b/BoltBend.java +++ b/Mage.Sets/src/mage/cards/b/BoltBend.java @@ -1,5 +1,6 @@ package mage.cards.b; +import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.FerociousCondition; import mage.abilities.effects.common.ChooseNewTargetsTargetEffect; @@ -13,8 +14,6 @@ import mage.filter.FilterStackObject; import mage.filter.predicate.mageobject.NumberOfTargetsPredicate; import mage.target.TargetStackObject; -import java.util.UUID; - /** * @author TheElk801 */ @@ -31,7 +30,7 @@ public final class BoltBend extends CardImpl { // This spell costs {3} less to cast if you control a creature with power 4 or greater. this.addAbility(new SimpleStaticAbility( - Zone.STACK, new SpellCostReductionSourceEffect(3, FerociousCondition.instance) + Zone.ALL, new SpellCostReductionSourceEffect(3, FerociousCondition.instance) ).setRuleAtTheTop(true).addHint(FerociousHint.instance)); // Change the target of target spell or ability with a single target. diff --git a/Mage.Sets/src/mage/cards/b/BoltHound.java b/Mage.Sets/src/mage/cards/b/BoltHound.java new file mode 100644 index 0000000000..abda305699 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoltHound.java @@ -0,0 +1,45 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BoltHound extends CardImpl { + + public BoltHound(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.DOG); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn. + this.addAbility(new AttacksTriggeredAbility(new BoostControlledEffect( + 1, 0, Duration.EndOfTurn, true + ), false)); + } + + private BoltHound(final BoltHound card) { + super(card); + } + + @Override + public BoltHound copy() { + return new BoltHound(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BombSquad.java b/Mage.Sets/src/mage/cards/b/BombSquad.java index 4d7216ca2d..6605a03513 100644 --- a/Mage.Sets/src/mage/cards/b/BombSquad.java +++ b/Mage.Sets/src/mage/cards/b/BombSquad.java @@ -94,7 +94,7 @@ class BombSquadTriggeredAbility extends TriggeredAbilityImpl { if (permanent != null && filter.match(permanent, game)) { if (4 <= permanent.getCounters(game).getCount(CounterType.FUSE)) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/b/BondOfDiscipline.java b/Mage.Sets/src/mage/cards/b/BondOfDiscipline.java index d5980c6ca7..ebc07aa9de 100644 --- a/Mage.Sets/src/mage/cards/b/BondOfDiscipline.java +++ b/Mage.Sets/src/mage/cards/b/BondOfDiscipline.java @@ -19,7 +19,7 @@ import java.util.UUID; public final class BondOfDiscipline extends CardImpl { private static final FilterPermanent filter - = new FilterOpponentsCreaturePermanent("creatures your opponents control."); + = new FilterOpponentsCreaturePermanent("creatures your opponents control"); public BondOfDiscipline(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}"); diff --git a/Mage.Sets/src/mage/cards/b/BondOfInsight.java b/Mage.Sets/src/mage/cards/b/BondOfInsight.java index fcd7cdda7e..fbec825de1 100644 --- a/Mage.Sets/src/mage/cards/b/BondOfInsight.java +++ b/Mage.Sets/src/mage/cards/b/BondOfInsight.java @@ -1,5 +1,6 @@ package mage.cards.b; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSpellEffect; @@ -16,8 +17,6 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInYourGraveyard; -import java.util.UUID; - /** * @author TheElk801 */ @@ -45,8 +44,8 @@ class BondOfInsightEffect extends OneShotEffect { BondOfInsightEffect() { super(Outcome.Benefit); - staticText = "Each player puts the top four cards of their library into their graveyard. " + - "Return up to two instant and/or sorcery cards from your graveyard to your hand."; + staticText = "Each player mills four cards. " + + "Return up to two instant and/or sorcery cards from your graveyard to your hand."; } private BondOfInsightEffect(final BondOfInsightEffect effect) { @@ -65,7 +64,7 @@ class BondOfInsightEffect extends OneShotEffect { if (player == null) { continue; } - player.moveCards(player.getLibrary().getTopCards(game, 4), Zone.GRAVEYARD, source, game); + player.millCards(4, source, game); } Player player = game.getPlayer(source.getControllerId()); if (player == null) { @@ -80,4 +79,4 @@ class BondOfInsightEffect extends OneShotEffect { Cards cards = new CardsImpl(target.getTargets()); return player.moveCards(cards, Zone.HAND, source, game); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/b/BondersEnclave.java b/Mage.Sets/src/mage/cards/b/BondersEnclave.java new file mode 100644 index 0000000000..dee8f3a5d1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BondersEnclave.java @@ -0,0 +1,45 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.common.FerociousCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BondersEnclave extends CardImpl { + + public BondersEnclave(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {3}, {T}: Draw a card. Activate this ability only if you control a creature with power 4 or greater. + Ability ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), + new GenericManaCost(3), FerociousCondition.instance + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private BondersEnclave(final BondersEnclave card) { + super(card); + } + + @Override + public BondersEnclave copy() { + return new BondersEnclave(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BondersOrnament.java b/Mage.Sets/src/mage/cards/b/BondersOrnament.java new file mode 100644 index 0000000000..3408295c03 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BondersOrnament.java @@ -0,0 +1,80 @@ +package mage.cards.b; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BondersOrnament extends CardImpl { + + public BondersOrnament(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + + // {4}, {T}: Each player who controls a permanent named Bonder's Ornament draws a card. + Ability ability = new SimpleActivatedAbility(new BondersOrnamentEffect(), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private BondersOrnament(final BondersOrnament card) { + super(card); + } + + @Override + public BondersOrnament copy() { + return new BondersOrnament(this); + } +} + +class BondersOrnamentEffect extends OneShotEffect { + + BondersOrnamentEffect() { + super(Outcome.Benefit); + staticText = "Each player who controls a permanent named Bonder's Ornament draws a card."; + } + + private BondersOrnamentEffect(final BondersOrnamentEffect effect) { + super(effect); + } + + @Override + public BondersOrnamentEffect copy() { + return new BondersOrnamentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + if (game.getBattlefield() + .getAllActivePermanents(playerId) + .stream() + .map(MageObject::getName) + .noneMatch("Bonder's Ornament"::equals)) { + continue; + } + player.drawCards(1, source.getSourceId(), game); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BoneMask.java b/Mage.Sets/src/mage/cards/b/BoneMask.java index 77707ff083..43804bef48 100644 --- a/Mage.Sets/src/mage/cards/b/BoneMask.java +++ b/Mage.Sets/src/mage/cards/b/BoneMask.java @@ -1,94 +1,94 @@ -package mage.cards.b; - -import java.util.Set; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; -import mage.target.TargetSource; - -/** - * - * @author jeffwadsworth - */ -public final class BoneMask extends CardImpl { - - public BoneMask(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - - // {2}, {tap}: The next time a source of your choice would deal damage to you this turn, prevent that damage. Exile cards from the top of your library equal to the damage prevented this way. - Ability ability = new SimpleActivatedAbility(new BoneMaskEffect(), new ManaCostsImpl("{2}")); - ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetSource()); - this.addAbility(ability); - - } - - private BoneMask(final BoneMask card) { - super(card); - } - - @Override - public BoneMask copy() { - return new BoneMask(this); - } -} - -class BoneMaskEffect extends PreventionEffectImpl { - - public BoneMaskEffect() { - super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); - this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent that damage. " - + "Exile cards from the top of your library equal to the damage prevented this way."; - } - - public BoneMaskEffect(final BoneMaskEffect effect) { - super(effect); - } - - @Override - public BoneMaskEffect copy() { - return new BoneMaskEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used - && super.applies(event, source, game)) { - if (event.getTargetId().equals(source.getControllerId()) - && event.getSourceId().equals(source.getFirstTarget())) { - PreventionEffectData preventionData = preventDamageAction(event, source, game); - this.used = true; - this.discard(); - if (preventionData.getPreventedDamage() > 0) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Set cardsToMoveToExileFromTopOfLibrary = controller.getLibrary().getTopCards( - game, - preventionData.getPreventedDamage()); - controller.moveCards(cardsToMoveToExileFromTopOfLibrary, Zone.EXILED, source, game); - } - } - } - return true; - } - return false; - } -} +package mage.cards.b; + +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.PreventionEffectData; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.TargetSource; + +/** + * + * @author jeffwadsworth + */ +public final class BoneMask extends CardImpl { + + public BoneMask(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + // {2}, {tap}: The next time a source of your choice would deal damage to you this turn, prevent that damage. Exile cards from the top of your library equal to the damage prevented this way. + Ability ability = new SimpleActivatedAbility(new BoneMaskEffect(), new ManaCostsImpl("{2}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetSource()); + this.addAbility(ability); + + } + + private BoneMask(final BoneMask card) { + super(card); + } + + @Override + public BoneMask copy() { + return new BoneMask(this); + } +} + +class BoneMaskEffect extends PreventionEffectImpl { + + public BoneMaskEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); + this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent that damage. " + + "Exile cards from the top of your library equal to the damage prevented this way."; + } + + public BoneMaskEffect(final BoneMaskEffect effect) { + super(effect); + } + + @Override + public BoneMaskEffect copy() { + return new BoneMaskEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!this.used + && super.applies(event, source, game)) { + if (event.getTargetId().equals(source.getControllerId()) + && event.getSourceId().equals(source.getFirstTarget())) { + PreventionEffectData preventionData = preventDamageAction(event, source, game); + this.used = true; + this.discard(); + if (preventionData.getPreventedDamage() > 0) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Set cardsToMoveToExileFromTopOfLibrary = controller.getLibrary().getTopCards( + game, + preventionData.getPreventedDamage()); + controller.moveCards(cardsToMoveToExileFromTopOfLibrary, Zone.EXILED, source, game); + } + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BonePitBrute.java b/Mage.Sets/src/mage/cards/b/BonePitBrute.java new file mode 100644 index 0000000000..33c81ee740 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BonePitBrute.java @@ -0,0 +1,48 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BonePitBrute extends CardImpl { + + public BonePitBrute(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); + + this.subtype.add(SubType.CYCLOPS); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Menace + this.addAbility(new MenaceAbility()); + + // When Bone Pit Brute enters the battlefield, target creature gets +4/+0 until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility( + new BoostTargetEffect(4, 0, Duration.EndOfTurn) + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private BonePitBrute(final BonePitBrute card) { + super(card); + } + + @Override + public BonePitBrute copy() { + return new BonePitBrute(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java b/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java index 8a756691c2..3e62dc9af2 100644 --- a/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java +++ b/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java @@ -1,16 +1,16 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.BecomesTargetTriggeredAbility; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.DamageCantBePreventedEffect; import mage.cards.AdventureCard; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetAnyTarget; import java.util.UUID; @@ -34,7 +34,7 @@ public final class BonecrusherGiant extends AdventureCard { // Stomp // Damage can’t be prevented this turn. Stomp deals 2 damage to any target. - this.getSpellCard().getSpellAbility().addEffect(new StompEffect()); + this.getSpellCard().getSpellAbility().addEffect(new DamageCantBePreventedEffect(Duration.EndOfTurn, "Damage can't be prevented this turn", false, false)); this.getSpellCard().getSpellAbility().addEffect(new DamageTargetEffect(2)); this.getSpellCard().getSpellAbility().addTarget(new TargetAnyTarget()); } @@ -47,36 +47,4 @@ public final class BonecrusherGiant extends AdventureCard { public BonecrusherGiant copy() { return new BonecrusherGiant(this); } -} - -class StompEffect extends ReplacementEffectImpl { - - StompEffect() { - super(Duration.EndOfTurn, Outcome.Benefit); - staticText = "Damage can't be prevented this turn."; - } - - private StompEffect(final StompEffect effect) { - super(effect); - } - - @Override - public StompEffect copy() { - return new StompEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.PREVENT_DAMAGE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return true; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BoneyardLurker.java b/Mage.Sets/src/mage/cards/b/BoneyardLurker.java new file mode 100644 index 0000000000..ac41d1ddd5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoneyardLurker.java @@ -0,0 +1,51 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BoneyardLurker extends CardImpl { + + private static final FilterCard filter + = new FilterPermanentCard("permanent card from your graveyard"); + + public BoneyardLurker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}"); + + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Mutate {2}{B/G}{B/G} + this.addAbility(new MutateAbility(this, "{2}{B/G}{B/G}")); + + // Whenever this creature mutates, return target permanent card from your graveyard to your hand. + Ability ability = new MutatesSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private BoneyardLurker(final BoneyardLurker card) { + super(card); + } + + @Override + public BoneyardLurker copy() { + return new BoneyardLurker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BoneyardMycodrax.java b/Mage.Sets/src/mage/cards/b/BoneyardMycodrax.java new file mode 100644 index 0000000000..72e7776e2e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoneyardMycodrax.java @@ -0,0 +1,58 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.abilities.keyword.ScavengeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.permanent.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BoneyardMycodrax extends CardImpl { + + private static final FilterCard filter = new FilterCreatureCard("other creature cards"); + + static { + filter.add(AnotherPredicate.instance); + } + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(filter); + + public BoneyardMycodrax(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.FUNGUS); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Boneyard Mycodrax's power and toughness are each equal to the number of other creature cards in your graveyard. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(xValue, Duration.EndOfGame))); + + // Scavenge {4}{B} + this.addAbility(new ScavengeAbility(new ManaCostsImpl("{4}{B}"))); + + } + + private BoneyardMycodrax(final BoneyardMycodrax card) { + super(card); + } + + @Override + public BoneyardMycodrax copy() { + return new BoneyardMycodrax(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java b/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java index 79e561b86e..eac46899b4 100644 --- a/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java +++ b/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java @@ -37,16 +37,16 @@ public final class BontuTheGlorified extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(6); - //Menace - this.addAbility(new MenaceAbility()); + // Menace + this.addAbility(new MenaceAbility(false)); - //Indestructible + // Indestructible this.addAbility(IndestructibleAbility.getInstance()); - //Bontu the Glorified can't attack or block unless a creature died under your control this turn. + // Bontu the Glorified can't attack or block unless a creature died under your control this turn. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BontuTheGlorifiedRestrictionEffect()), new CreaturesDiedWatcher()); - //{1}{B}, Sacrifice another creature: Scry 1. Each opponent loses 1 life and you gain 1 life. + // {1}{B}, Sacrifice another creature: Scry 1. Each opponent loses 1 life and you gain 1 life. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1), new ManaCostsImpl("{1}{B}")); ability.addEffect(new LoseLifeOpponentsEffect(1)); Effect effect = new GainLifeEffect(1); diff --git a/Mage.Sets/src/mage/cards/b/BookBurning.java b/Mage.Sets/src/mage/cards/b/BookBurning.java index 6778bc4ee6..6678b6520f 100644 --- a/Mage.Sets/src/mage/cards/b/BookBurning.java +++ b/Mage.Sets/src/mage/cards/b/BookBurning.java @@ -41,7 +41,7 @@ class BookBurningMillEffect extends OneShotEffect { public BookBurningMillEffect() { super(Outcome.Detriment); - staticText = "Any player may have {source} deal 6 damage to them. If no one does, target player puts the top six cards of their library into their graveyard"; + staticText = "Any player may have {source} deal 6 damage to them. If no one does, target player mills six cards"; } public BookBurningMillEffect(final BookBurningMillEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BoonOfTheWishGiver.java b/Mage.Sets/src/mage/cards/b/BoonOfTheWishGiver.java new file mode 100644 index 0000000000..042af41cc4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoonOfTheWishGiver.java @@ -0,0 +1,35 @@ +package mage.cards.b; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BoonOfTheWishGiver extends CardImpl { + + public BoonOfTheWishGiver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}{U}"); + + // Draw four cards. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(4)); + + // Cycling {1} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}"))); + } + + private BoonOfTheWishGiver(final BoonOfTheWishGiver card) { + super(card); + } + + @Override + public BoonOfTheWishGiver copy() { + return new BoonOfTheWishGiver(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BootNipper.java b/Mage.Sets/src/mage/cards/b/BootNipper.java new file mode 100644 index 0000000000..be5e5d3df1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BootNipper.java @@ -0,0 +1,40 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.common.counter.AddCounterChoiceSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BootNipper extends CardImpl { + + public BootNipper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Boot Nipper enters the battlefield with your choice of a deathtouch counter or a lifelink counter on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCounterChoiceSourceEffect(CounterType.DEATHTOUCH, CounterType.LIFELINK) + )); + } + + private BootNipper(final BootNipper card) { + super(card); + } + + @Override + public BootNipper copy() { + return new BootNipper(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BorealElemental.java b/Mage.Sets/src/mage/cards/b/BorealElemental.java index bd0f8fcada..df54bda0db 100644 --- a/Mage.Sets/src/mage/cards/b/BorealElemental.java +++ b/Mage.Sets/src/mage/cards/b/BorealElemental.java @@ -1,20 +1,17 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.target.Target; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; -import java.util.Collection; import java.util.UUID; /** @@ -33,7 +30,9 @@ public final class BorealElemental extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Spells your opponents cast that target Boreal Elemental cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(new BorealElementalCostIncreaseEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT)) + ); } private BorealElemental(final BorealElemental card) { @@ -45,46 +44,3 @@ public final class BorealElemental extends CardImpl { return new BorealElemental(this); } } - -class BorealElementalCostIncreaseEffect extends CostModificationEffectImpl { - - BorealElementalCostIncreaseEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Spells your opponents cast that target {this} cost {2} more to cast"; - } - - private BorealElementalCostIncreaseEffect(BorealElementalCostIncreaseEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (!(abilityToModify instanceof SpellAbility) - || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - return false; - } - return abilityToModify - .getModes() - .getSelectedModes() - .stream() - .map(uuid -> abilityToModify.getModes().get(uuid)) - .map(Mode::getTargets) - .flatMap(Collection::stream) - .map(Target::getTargets) - .flatMap(Collection::stream) - .anyMatch(uuid -> uuid.equals(source.getSourceId())); - } - - @Override - public BorealElementalCostIncreaseEffect copy() { - return new BorealElementalCostIncreaseEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/b/BorosFuryShield.java b/Mage.Sets/src/mage/cards/b/BorosFuryShield.java index 9f15ea62fc..ea5b4233c3 100644 --- a/Mage.Sets/src/mage/cards/b/BorosFuryShield.java +++ b/Mage.Sets/src/mage/cards/b/BorosFuryShield.java @@ -1,5 +1,6 @@ package mage.cards.b; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.common.ManaWasSpentCondition; import mage.abilities.decorator.ConditionalOneShotEffect; @@ -18,12 +19,11 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; -import java.util.UUID; - /** * @author Dilnu */ public final class BorosFuryShield extends CardImpl { + private static final FilterAttackingOrBlockingCreature filter = new FilterAttackingOrBlockingCreature(); public BorosFuryShield(UUID ownerId, CardSetInfo setInfo) { @@ -49,6 +49,7 @@ public final class BorosFuryShield extends CardImpl { } static class BorosFuryShieldDamageEffect extends OneShotEffect { + BorosFuryShieldDamageEffect() { super(Outcome.Damage); staticText = "{this} deals damage to that creature's controller equal to the creature's power"; @@ -60,7 +61,7 @@ public final class BorosFuryShield extends CardImpl { @Override public boolean apply(Game game, Ability source) { - Permanent target = game.getPermanent(targetPointer.getFirst(game, source)); + Permanent target = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (target != null) { Player player = game.getPlayer(target.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/b/BorosMastiff.java b/Mage.Sets/src/mage/cards/b/BorosMastiff.java index 9d2e118ed8..35811ff779 100644 --- a/Mage.Sets/src/mage/cards/b/BorosMastiff.java +++ b/Mage.Sets/src/mage/cards/b/BorosMastiff.java @@ -23,7 +23,7 @@ public final class BorosMastiff extends CardImpl { public BorosMastiff (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java b/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java index 86f68cfe0b..95e22f7488 100644 --- a/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java +++ b/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java @@ -1,10 +1,10 @@ package mage.cards.b; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; + import mage.MageObject; +import mage.MageObjectReference; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTappedAbility; @@ -58,7 +58,7 @@ public final class BoseijuWhoSheltersAll extends CardImpl { class BoseijuWhoSheltersAllWatcher extends Watcher { - private List spells = new ArrayList<>(); + private final Set spells = new HashSet<>(); private final UUID originalId; public BoseijuWhoSheltersAllWatcher(UUID originalId) { @@ -69,17 +69,17 @@ class BoseijuWhoSheltersAllWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.MANA_PAID) { - if (event.getData() != null && event.getData().equals(originalId.toString())) { + if (event.getData() != null && event.getData().equals(originalId.toString()) && event.getTargetId() != null) { Card spell = game.getSpell(event.getTargetId()); if (spell != null && (spell.isInstant() || spell.isSorcery())) { - spells.add(event.getTargetId()); + spells.add(new MageObjectReference(game.getObject(event.getTargetId()), game)); } } } } - public boolean spellCantBeCountered(UUID spellId) { - return spells.contains(spellId); + public boolean spellCantBeCountered(MageObjectReference mor) { + return spells.contains(mor); } @Override @@ -128,6 +128,6 @@ class BoseijuWhoSheltersAllCantCounterEffect extends ContinuousRuleModifyingEffe public boolean applies(GameEvent event, Ability source, Game game) { BoseijuWhoSheltersAllWatcher watcher = game.getState().getWatcher(BoseijuWhoSheltersAllWatcher.class, source.getSourceId()); Spell spell = game.getStack().getSpell(event.getTargetId()); - return spell != null && watcher != null && watcher.spellCantBeCountered(spell.getId()); + return spell != null && watcher != null && watcher.spellCantBeCountered(new MageObjectReference(spell, game)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BoundDetermined.java b/Mage.Sets/src/mage/cards/b/BoundDetermined.java index cfb16ba517..86d2018d82 100644 --- a/Mage.Sets/src/mage/cards/b/BoundDetermined.java +++ b/Mage.Sets/src/mage/cards/b/BoundDetermined.java @@ -86,7 +86,7 @@ class BoundEffect extends OneShotEffect { Permanent toSacrifice = game.getPermanent(target.getFirstTarget()); if (toSacrifice != null) { toSacrifice.sacrifice(source.getSourceId(), game); - game.applyEffects(); + game.getState().processAction(game); int colors = toSacrifice.getColor(game).getColorCount(); if (colors > 0) { TargetCardInYourGraveyard targetCard = new TargetCardInYourGraveyard(0, colors, diff --git a/Mage.Sets/src/mage/cards/b/BountyOfTheLuxa.java b/Mage.Sets/src/mage/cards/b/BountyOfTheLuxa.java index 8a55e31f26..37e29df7f6 100644 --- a/Mage.Sets/src/mage/cards/b/BountyOfTheLuxa.java +++ b/Mage.Sets/src/mage/cards/b/BountyOfTheLuxa.java @@ -80,7 +80,7 @@ class BountyOfTheLuxaEffect extends OneShotEffect { if (bountyOfLuxa != null) { new AddCountersSourceEffect(CounterType.FLOOD.createInstance()).apply(game, source); } - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/b/BrainPry.java b/Mage.Sets/src/mage/cards/b/BrainPry.java index 2e75ebc832..8704271ed8 100644 --- a/Mage.Sets/src/mage/cards/b/BrainPry.java +++ b/Mage.Sets/src/mage/cards/b/BrainPry.java @@ -60,14 +60,14 @@ class BrainPryEffect extends OneShotEffect { if (targetPlayer != null && controller != null && sourceObject != null && cardName != null) { boolean hasDiscarded = false; for (Card card : targetPlayer.getHand().getCards(game)) { - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { targetPlayer.discard(card, source, game); hasDiscarded = true; break; } } if (!hasDiscarded) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } controller.lookAtCards(sourceObject.getName() + " Hand", targetPlayer.getHand(), game); } diff --git a/Mage.Sets/src/mage/cards/b/BrallinSkysharkRider.java b/Mage.Sets/src/mage/cards/b/BrallinSkysharkRider.java new file mode 100644 index 0000000000..25c5425b88 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BrallinSkysharkRider.java @@ -0,0 +1,66 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.DiscardCardControllerTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.PartnerWithAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BrallinSkysharkRider extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.SHARK, "Shark"); + + public BrallinSkysharkRider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Partner with Shabraz, the Skyshark + this.addAbility(new PartnerWithAbility("Shabraz, the Skyshark")); + + // Whenever you discard a card, put a +1/+1 counter on Brallin, Skyshark Rider and it deals 1 damage to each opponent. + Ability ability = new DiscardCardControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false + ); + ability.addEffect(new DamagePlayersEffect( + 1, TargetController.OPPONENT, "and it" + )); + this.addAbility(ability); + + // {R}: Target Shark gains trample until end of turn. + ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl("{R}")); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private BrallinSkysharkRider(final BrallinSkysharkRider card) { + super(card); + } + + @Override + public BrallinSkysharkRider copy() { + return new BrallinSkysharkRider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BrambleSovereign.java b/Mage.Sets/src/mage/cards/b/BrambleSovereign.java index caa98f8bf6..33c4ae111a 100644 --- a/Mage.Sets/src/mage/cards/b/BrambleSovereign.java +++ b/Mage.Sets/src/mage/cards/b/BrambleSovereign.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -9,12 +8,12 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.abilities.effects.common.DoIfCostPaid; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SetTargetPointer; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; @@ -81,7 +80,7 @@ class BrambleSovereignEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(permanent.getControllerId()); effect.setTargetPointer(targetPointer); diff --git a/Mage.Sets/src/mage/cards/b/BranchingEvolution.java b/Mage.Sets/src/mage/cards/b/BranchingEvolution.java new file mode 100644 index 0000000000..0e67487032 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BranchingEvolution.java @@ -0,0 +1,86 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BranchingEvolution extends CardImpl { + + public BranchingEvolution(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + // If one or more +1/+1 counters would be put a on a creature you control, twice that many +1/+1 counters are put on that creature instead. + this.addAbility(new SimpleStaticAbility(new BranchingEvolutionEffect())); + } + + private BranchingEvolution(final BranchingEvolution card) { + super(card); + } + + @Override + public BranchingEvolution copy() { + return new BranchingEvolution(this); + } +} + +class BranchingEvolutionEffect extends ReplacementEffectImpl { + + BranchingEvolutionEffect() { + super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false); + staticText = "If one or more +1/+1 counters would be put on a creature you control, " + + "twice that many +1/+1 counters are put on it instead"; + } + + private BranchingEvolutionEffect(final BranchingEvolutionEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmountForCounters(CardUtil.addWithOverflowCheck(event.getAmount(), event.getAmount()), true); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ADD_COUNTERS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getData().equals(CounterType.P1P1.getName()) && event.getAmount() > 0) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null) { + permanent = game.getPermanentEntering(event.getTargetId()); + } + return permanent != null && permanent.isControlledBy(source.getControllerId()) + && permanent.isCreature(); + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public BranchingEvolutionEffect copy() { + return new BranchingEvolutionEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BrashTaunter.java b/Mage.Sets/src/mage/cards/b/BrashTaunter.java new file mode 100644 index 0000000000..d3c35dfb42 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BrashTaunter.java @@ -0,0 +1,100 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealtDamageToSourceTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.FightTargetSourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author jmharmon + */ + +public final class BrashTaunter extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(AnotherPredicate.instance); + } + + public BrashTaunter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Indestructible + this.addAbility(IndestructibleAbility.getInstance()); + + // Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent. + Ability ability = new DealtDamageToSourceTriggeredAbility(new BrashTaunterEffect(), false, false, true); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + + // {2}{R}, {T}: Brash Taunter fights another target creature. + Ability ability1 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new FightTargetSourceEffect(), new ManaCostsImpl("{2}{R}")); + ability1.addCost(new TapSourceCost()); + ability1.addTarget(new TargetPermanent(filter)); + this.addAbility(ability1); + } + + private BrashTaunter(final BrashTaunter card) { + super(card); + } + + @Override + public BrashTaunter copy() { + return new BrashTaunter(this); + } +} + +class BrashTaunterEffect extends OneShotEffect { + + public BrashTaunterEffect() { + super(Outcome.Damage); + this.staticText = "it deals that much damage to target opponent"; + } + + public BrashTaunterEffect(final BrashTaunterEffect effect) { + super(effect); + } + + @Override + public BrashTaunterEffect copy() { + return new BrashTaunterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int amount = (Integer) getValue("damage"); + if (amount > 0) { + Player player = game.getPlayer(targetPointer.getFirst(game, source)); + if (player != null) { + player.damage(amount, source.getSourceId(), game); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java b/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java index 3171564a79..2c5230f397 100644 --- a/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java +++ b/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java @@ -8,7 +8,7 @@ import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -42,7 +42,7 @@ public final class BreachingLeviathan extends CardImpl { // When Breaching Leviathan enters the battlefield, if you cast it from your hand, tap all nonblue creatures. Those creatures don't untap during their controllers' next untap steps. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new BreachingLeviathanEffect(), false), - CastFromHandSourceCondition.instance, + CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, tap all nonblue creatures. Those creatures don't untap during their controllers' next untap steps."), new CastFromHandWatcher()); } diff --git a/Mage.Sets/src/mage/cards/b/BreakOpen.java b/Mage.Sets/src/mage/cards/b/BreakOpen.java index 60df5e30ab..5a5209b330 100644 --- a/Mage.Sets/src/mage/cards/b/BreakOpen.java +++ b/Mage.Sets/src/mage/cards/b/BreakOpen.java @@ -17,7 +17,7 @@ import mage.target.common.TargetCreaturePermanent; */ public final class BreakOpen extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Face-down creature an opponent controls"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("face-down creature an opponent controls"); static { filter.add(FaceDownPredicate.instance); diff --git a/Mage.Sets/src/mage/cards/b/Breakthrough.java b/Mage.Sets/src/mage/cards/b/Breakthrough.java index 15f6a890e9..b2392375b4 100644 --- a/Mage.Sets/src/mage/cards/b/Breakthrough.java +++ b/Mage.Sets/src/mage/cards/b/Breakthrough.java @@ -1,13 +1,11 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterCard; @@ -15,25 +13,24 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * * @author emerald000 */ public final class Breakthrough extends CardImpl { public Breakthrough(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}"); // Draw four cards, this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(4)); - + //then choose X cards in your hand and discard the rest. this.getSpellAbility().addEffect(new BreakthroughEffect()); - } - public Breakthrough(final Breakthrough card) { + private Breakthrough(final Breakthrough card) { super(card); } @@ -44,41 +41,38 @@ public final class Breakthrough extends CardImpl { } class BreakthroughEffect extends OneShotEffect { - + BreakthroughEffect() { super(Outcome.Discard); this.staticText = ", then choose X cards in your hand and discard the rest."; } - - BreakthroughEffect(final BreakthroughEffect effect) { + + private BreakthroughEffect(final BreakthroughEffect effect) { super(effect); } - + @Override public BreakthroughEffect copy() { return new BreakthroughEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - int amountToKeep = source.getManaCostsToPay().getX(); - if (amountToKeep == 0) { - player.discard(player.getHand().size(), false, source, game); - } - else if (amountToKeep < player.getHand().size()) { - TargetCardInHand target = new TargetCardInHand(amountToKeep, new FilterCard()); - target.setTargetName("cards to keep"); - target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game); - for (Card card : player.getHand().getCards(game)) { - if (!target.getTargets().contains(card.getId())) { - player.discard(card, source, game); - } - } - } - return true; + if (player == null) { + return false; } - return false; + int amountToKeep = source.getManaCostsToPay().getX(); + if (amountToKeep == 0) { + player.discard(player.getHand(), source, game); + } else if (amountToKeep < player.getHand().size()) { + TargetCardInHand target = new TargetCardInHand(amountToKeep, new FilterCard()); + target.setTargetName("cards to keep"); + target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game); + Cards cards = player.getHand().copy(); + cards.removeIf(target.getTargets()::contains); + player.discard(cards, source, game); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/b/BreathstealersCrypt.java b/Mage.Sets/src/mage/cards/b/BreathstealersCrypt.java index 520cd01f91..1141259d6b 100644 --- a/Mage.Sets/src/mage/cards/b/BreathstealersCrypt.java +++ b/Mage.Sets/src/mage/cards/b/BreathstealersCrypt.java @@ -68,7 +68,7 @@ class BreathstealersCryptEffect extends ReplacementEffectImpl { Player player = game.getPlayer(event.getPlayerId()); if (player != null) { Cards oldHand = player.getHand().copy(); - if (player.drawCards(1, game, event.getAppliedEffects()) > 0) { + if (player.drawCards(1, event.getSourceId(), game, event.getAppliedEffects()) > 0) { Cards drawnCards = player.getHand().copy(); drawnCards.removeAll(oldHand); player.revealCards(source, "The card drawn from " + player.getName() + "'s library.", drawnCards, game); diff --git a/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java b/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java index ba36808b69..b5339588b4 100644 --- a/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java +++ b/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java @@ -1,7 +1,6 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; @@ -22,6 +21,8 @@ import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetPlayerOrPlaneswalker; +import java.util.UUID; + /** * * @author fireshoes @@ -39,12 +40,12 @@ public final class BreyaEtheriumShaper extends CardImpl { // When Breya, Etherium Shaper enters the battlefield, create two 1/1 blue Thopter artifact creature tokens with flying. this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ThopterToken(), 2))); - // {2}, Sacrifice two artifacts: Choose one — Breya deals 3 damage to target player. + // {2}, Sacrifice two artifacts: Choose one — Breya deals 3 damage to target player or planeswalker. Ability ability = new SimpleActivatedAbility( Zone.BATTLEFIELD, new DamageTargetEffect(3), new GenericManaCost(2)); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledArtifactPermanent("two artifacts"), true))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledArtifactPermanent("artifacts"), true))); ability.addTarget(new TargetPlayerOrPlaneswalker()); // Target creature gets -4/-4 until end of turn. diff --git a/Mage.Sets/src/mage/cards/b/BrighthearthBanneret.java b/Mage.Sets/src/mage/cards/b/BrighthearthBanneret.java index 1d6c96aa68..24a0646982 100644 --- a/Mage.Sets/src/mage/cards/b/BrighthearthBanneret.java +++ b/Mage.Sets/src/mage/cards/b/BrighthearthBanneret.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -15,8 +13,9 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; +import java.util.UUID; + /** - * * @author North */ public final class BrighthearthBanneret extends CardImpl { @@ -30,7 +29,7 @@ public final class BrighthearthBanneret extends CardImpl { } public BrighthearthBanneret(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.ELEMENTAL, SubType.WARRIOR); this.power = new MageInt(1); @@ -38,6 +37,7 @@ public final class BrighthearthBanneret extends CardImpl { // Elemental spells and Warrior spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); + // Reinforce 1-{1}{R} this.addAbility(new ReinforceAbility(1, new ManaCostsImpl("{1}{R}"))); } diff --git a/Mage.Sets/src/mage/cards/b/Brightmare.java b/Mage.Sets/src/mage/cards/b/Brightmare.java new file mode 100644 index 0000000000..5dfc133b5d --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/Brightmare.java @@ -0,0 +1,76 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Brightmare extends CardImpl { + + public Brightmare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.UNICORN); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // When Brightmare enters the battlefield, tap up to one target creature. You gain life equal to that creature's power. + Ability ability = new EntersBattlefieldTriggeredAbility(new BrightmareEffect()); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + } + + private Brightmare(final Brightmare card) { + super(card); + } + + @Override + public Brightmare copy() { + return new Brightmare(this); + } +} + +class BrightmareEffect extends OneShotEffect { + + BrightmareEffect() { + super(Outcome.Benefit); + staticText = "tap up to one target creature. You gain life equal to that creature's power"; + } + + private BrightmareEffect(final BrightmareEffect effect) { + super(effect); + } + + @Override + public BrightmareEffect copy() { + return new BrightmareEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + permanent.tap(game); + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.gainLife(permanent.getPower().getValue(), game, source); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BrindleShoat.java b/Mage.Sets/src/mage/cards/b/BrindleShoat.java index 95f1ad2db2..5b98c191d6 100644 --- a/Mage.Sets/src/mage/cards/b/BrindleShoat.java +++ b/Mage.Sets/src/mage/cards/b/BrindleShoat.java @@ -3,7 +3,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class BrindleShoat extends CardImpl { this.toughness = new MageInt(1); // When Brindle Shoat dies, create a 3/3 green Boar creature token. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new BoarToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new BoarToken()))); } public BrindleShoat(final BrindleShoat card) { diff --git a/Mage.Sets/src/mage/cards/b/BrineGiant.java b/Mage.Sets/src/mage/cards/b/BrineGiant.java index 09942e2ab3..98d9ca61f4 100644 --- a/Mage.Sets/src/mage/cards/b/BrineGiant.java +++ b/Mage.Sets/src/mage/cards/b/BrineGiant.java @@ -1,18 +1,17 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.util.CardUtil; import java.util.UUID; @@ -21,8 +20,13 @@ import java.util.UUID; */ public final class BrineGiant extends CardImpl { - private static final DynamicValue xValue - = new PermanentsOnBattlefieldCount(BrineGiantCostReductionEffect.filter); + static final FilterControlledPermanent filter = new FilterControlledPermanent("enchantment you control"); + + static { + filter.add(CardType.ENCHANTMENT.getPredicate()); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); public BrineGiant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{U}"); @@ -33,7 +37,7 @@ public final class BrineGiant extends CardImpl { // This spell costs {1} less to cast for each enchantment you control. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new BrineGiantCostReductionEffect() + Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue) ).addHint(new ValueHint("Enchantments you control", xValue))); } @@ -45,41 +49,4 @@ public final class BrineGiant extends CardImpl { public BrineGiant copy() { return new BrineGiant(this); } -} - -class BrineGiantCostReductionEffect extends CostModificationEffectImpl { - - static final FilterControlledPermanent filter = new FilterControlledPermanent(); - - static { - filter.add(CardType.ENCHANTMENT.getPredicate()); - } - - BrineGiantCostReductionEffect() { - super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "This spell costs {1} less to cast for each enchantment you control"; - } - - private BrineGiantCostReductionEffect(final BrineGiantCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - int count = game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game).size(); - if (count > 0) { - CardUtil.reduceCost(abilityToModify, count); - } - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify.getSourceId().equals(source.getSourceId()); - } - - @Override - public BrineGiantCostReductionEffect copy() { - return new BrineGiantCostReductionEffect(this); - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BrineHag.java b/Mage.Sets/src/mage/cards/b/BrineHag.java index 5050409b69..608e176fc2 100644 --- a/Mage.Sets/src/mage/cards/b/BrineHag.java +++ b/Mage.Sets/src/mage/cards/b/BrineHag.java @@ -7,7 +7,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessAllEffect; import mage.cards.CardImpl; @@ -36,7 +36,7 @@ public final class BrineHag extends CardImpl { this.toughness = new MageInt(2); // When Brine Hag dies, change the base power and toughness of all creatures that dealt damage to it this turn to 0/2. - this.addAbility(new DiesTriggeredAbility(new BrineHagEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new BrineHagEffect())); } public BrineHag(final BrineHag card) { diff --git a/Mage.Sets/src/mage/cards/b/BringToLight.java b/Mage.Sets/src/mage/cards/b/BringToLight.java index d3d91d2389..88f7e78a9c 100644 --- a/Mage.Sets/src/mage/cards/b/BringToLight.java +++ b/Mage.Sets/src/mage/cards/b/BringToLight.java @@ -1,5 +1,6 @@ package mage.cards.b; +import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.ColorsOfManaSpentToCastCount; @@ -19,8 +20,6 @@ import mage.players.Player; import mage.target.common.TargetCardInLibrary; import org.apache.log4j.Logger; -import java.util.UUID; - /** * @author LevelX2 */ @@ -68,7 +67,8 @@ class BringToLightEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int numberColors = ColorsOfManaSpentToCastCount.getInstance().calculate(game, source, this); - FilterCard filter = new FilterCard(); + FilterCard filter = new FilterCard("a creature, instant, or sorcery card with converted mana " + + "cost less than or equal to " + numberColors); filter.add(Predicates.or(CardType.CREATURE.getPredicate(), CardType.INSTANT.getPredicate(), CardType.SORCERY.getPredicate())); filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, numberColors + 1)); @@ -86,9 +86,6 @@ class BringToLightEffect extends OneShotEffect { Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), game, true, new MageObjectReference(source.getSourceObject(game), game)); game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - if (!cardWasCast) { - Logger.getLogger(BringToLightEffect.class).error("Bring to Light: spellAbility == null " + card.getName()); - } } } return true; diff --git a/Mage.Sets/src/mage/cards/b/BringerOfTheRedDawn.java b/Mage.Sets/src/mage/cards/b/BringerOfTheRedDawn.java index de1966751c..1da0a6c4ee 100644 --- a/Mage.Sets/src/mage/cards/b/BringerOfTheRedDawn.java +++ b/Mage.Sets/src/mage/cards/b/BringerOfTheRedDawn.java @@ -41,11 +41,11 @@ public final class BringerOfTheRedDawn extends CardImpl { Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.YOU, true); effect = new GainControlTargetEffect(Duration.EndOfTurn); - effect.setText("and gain control of it until end of turn."); + effect.setText("and gain control of it until end of turn"); ability.addEffect(effect); effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setText("That creature gains haste until end of turn."); + effect.setText("That creature gains haste until end of turn"); ability.addEffect(effect); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java b/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java index fe17742f0b..58a37d6967 100644 --- a/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java +++ b/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java @@ -1,8 +1,5 @@ - package mage.cards.b; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -10,38 +7,33 @@ import mage.abilities.condition.common.CardsInHandCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SacrificeSourceEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author Plopman */ public final class BrinkOfMadness extends CardImpl { public BrinkOfMadness(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}"); // At the beginning of your upkeep, if you have no cards in hand, sacrifice Brink of Madness and target opponent discards their hand. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new SacrificeSourceEffect(), TargetController.YOU, false); + TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new SacrificeSourceEffect(), TargetController.YOU, false); ability.addEffect(new BrinkOfMadnessEffect()); ability.addTarget(new TargetOpponent()); CardsInHandCondition contition = new CardsInHandCondition(ComparisonType.EQUAL_TO, 0); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, contition, "At the beginning of your upkeep, if you have no cards in hand, sacrifice {this} and target opponent discards their hand.")); - + } - public BrinkOfMadness(final BrinkOfMadness card) { + private BrinkOfMadness(final BrinkOfMadness card) { super(card); } @@ -49,34 +41,27 @@ public final class BrinkOfMadness extends CardImpl { public BrinkOfMadness copy() { return new BrinkOfMadness(this); } - + static class BrinkOfMadnessEffect extends OneShotEffect { - public BrinkOfMadnessEffect() { - super(Outcome.Benefit); - this.staticText = "Target player discards their hand"; - } - - public BrinkOfMadnessEffect(final BrinkOfMadnessEffect effect) { - super(effect); - } - - @Override - public BrinkOfMadnessEffect copy() { - return new BrinkOfMadnessEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - Set cards = player.getHand().getCards(game); - for (Card card : cards) { - player.discard(card, source, game); - } - return true; + private BrinkOfMadnessEffect() { + super(Outcome.Benefit); + this.staticText = "Target player discards their hand"; + } + + private BrinkOfMadnessEffect(final BrinkOfMadnessEffect effect) { + super(effect); + } + + @Override + public BrinkOfMadnessEffect copy() { + return new BrinkOfMadnessEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + return player != null && !player.discard(player.getHand(), source, game).isEmpty(); } - return false; } } -} diff --git a/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java b/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java index 59228d09e5..297b9d1c58 100644 --- a/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java +++ b/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java @@ -11,7 +11,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; @@ -45,18 +44,21 @@ public final class BrokenAmbitions extends CardImpl { class BrokenAmbitionsEffect extends OneShotEffect { + private static final String effectText = "Counter target spell unless its controller pays {X}. Clash with an opponent. If you win, that spell's controller mills four cards"; + protected Cost cost; protected DynamicValue genericMana; public BrokenAmbitionsEffect(Cost cost) { super(Outcome.Benefit); this.cost = cost; - this.staticText = "Counter target spell unless its controller pays {X}. Clash with an opponent. If you win, that spell's controller puts the top four cards of their library into their graveyard"; + this.staticText = effectText; } public BrokenAmbitionsEffect(DynamicValue genericMana) { super(Outcome.Detriment); this.genericMana = genericMana; + this.staticText = effectText; } public BrokenAmbitionsEffect(final BrokenAmbitionsEffect effect) { @@ -67,6 +69,7 @@ class BrokenAmbitionsEffect extends OneShotEffect { if (effect.genericMana != null) { this.genericMana = effect.genericMana.copy(); } + this.staticText = effectText; } @Override @@ -103,7 +106,7 @@ class BrokenAmbitionsEffect extends OneShotEffect { game.informPlayers(player.getLogName() + " chooses to pay " + costValueMessage + " to prevent the counter effect"); if (ClashEffect.getInstance().apply(game, source)) { - player.moveCards(player.getLibrary().getTopCards(game, 4), Zone.GRAVEYARD, source, game); + player.millCards(4, source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/b/BrokenVisage.java b/Mage.Sets/src/mage/cards/b/BrokenVisage.java index 3505018f22..519cad5aef 100644 --- a/Mage.Sets/src/mage/cards/b/BrokenVisage.java +++ b/Mage.Sets/src/mage/cards/b/BrokenVisage.java @@ -69,7 +69,7 @@ class BrokenVisageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { permanent.destroy(source.getSourceId(), game, true); CreateTokenEffect effect = new CreateTokenEffect(new BrokenVisageSpiritToken(permanent.getPower().getValue(), permanent.getToughness().getValue())); diff --git a/Mage.Sets/src/mage/cards/b/BronzehideLion.java b/Mage.Sets/src/mage/cards/b/BronzehideLion.java index a1f7fa245b..98dd760b60 100644 --- a/Mage.Sets/src/mage/cards/b/BronzehideLion.java +++ b/Mage.Sets/src/mage/cards/b/BronzehideLion.java @@ -4,9 +4,8 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffectImpl; @@ -46,7 +45,7 @@ public final class BronzehideLion extends CardImpl { // When Bronzehide Lion dies, return it to the battlefield. // It's an Aura enchantment with enchant creature you control and // "{G}{W}: Enchanted creature gains indestructible until end of turn," and it loses all other abilities. - this.addAbility(new DiesTriggeredAbility(new BronzehideLionReturnEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new BronzehideLionReturnEffect())); } private BronzehideLion(final BronzehideLion card) { @@ -109,7 +108,7 @@ class BronzehideLionContinuousEffect extends ContinuousEffectImpl { private final int zoneChangeCounter; private final Ability activatedAbility = new SimpleActivatedAbility(new GainAbilityAttachedEffect( - IndestructibleAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield + IndestructibleAbility.getInstance(), AttachmentType.AURA, Duration.EndOfTurn ), new ManaCostsImpl("{G}{W}")); BronzehideLionContinuousEffect(int zoneChangeCounter) { @@ -133,17 +132,14 @@ class BronzehideLionContinuousEffect extends ContinuousEffectImpl { if (game.getState().getZoneChangeCounter(source.getSourceId()) > zoneChangeCounter) { discard(); } - MageObject sourceObject = game.getPermanent(source.getSourceId()); + Permanent sourceObject = game.getPermanent(source.getSourceId()); if (sourceObject == null) { sourceObject = game.getPermanentEntering(source.getSourceId()); } if (sourceObject == null) { return false; } - if (!(sourceObject instanceof Permanent)) { - return true; - } - Permanent lion = (Permanent) sourceObject; + Permanent lion = sourceObject; switch (layer) { case TypeChangingEffects_4: lion.getCardType().clear(); @@ -158,7 +154,7 @@ class BronzehideLionContinuousEffect extends ContinuousEffectImpl { toRemove.add(ability); } } - lion.getAbilities(game).removeAll(toRemove); + lion.removeAbilities(toRemove, source.getSourceId(), game); lion.getSpellAbility().getTargets().clear(); lion.getSpellAbility().getEffects().clear(); diff --git a/Mage.Sets/src/mage/cards/b/Browbeat.java b/Mage.Sets/src/mage/cards/b/Browbeat.java index e5f8377851..e96f9ae7bb 100644 --- a/Mage.Sets/src/mage/cards/b/Browbeat.java +++ b/Mage.Sets/src/mage/cards/b/Browbeat.java @@ -80,7 +80,7 @@ class BrowbeatDrawEffect extends OneShotEffect { if (targetPlayer != null) { Player player = game.getPlayer(targetPlayer); if (player != null) { - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/b/BrudicladTelchorEngineer.java b/Mage.Sets/src/mage/cards/b/BrudicladTelchorEngineer.java index ef8d9f5b94..1a643205f2 100644 --- a/Mage.Sets/src/mage/cards/b/BrudicladTelchorEngineer.java +++ b/Mage.Sets/src/mage/cards/b/BrudicladTelchorEngineer.java @@ -43,7 +43,7 @@ public final class BrudicladTelchorEngineer extends CardImpl { this.toughness = new MageInt(4); // Creature tokens you control have haste. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter, true))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter, false))); // At the beginning of combat on your turn, create a 2/1 blue Myr artifact creature token. Then you may choose a token you control. If you do, each other token you control becomes a copy of that token. this.addAbility(new BeginningOfCombatTriggeredAbility(new BrudicladTelchorEngineerEffect(), TargetController.YOU, false)); diff --git a/Mage.Sets/src/mage/cards/b/BrushWithDeath.java b/Mage.Sets/src/mage/cards/b/BrushWithDeath.java index f3a9b2da3e..78df808785 100644 --- a/Mage.Sets/src/mage/cards/b/BrushWithDeath.java +++ b/Mage.Sets/src/mage/cards/b/BrushWithDeath.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.keyword.BuybackAbility; @@ -10,17 +8,19 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author LoneFox */ public final class BrushWithDeath extends CardImpl { public BrushWithDeath(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Buyback {2}{B}{B} this.addAbility(new BuybackAbility("{2}{B}{B}")); + // Target opponent loses 2 life. You gain 2 life. this.getSpellAbility().addEffect(new LoseLifeTargetEffect(2)); this.getSpellAbility().addEffect(new GainLifeEffect(2)); diff --git a/Mage.Sets/src/mage/cards/b/Brushwagg.java b/Mage.Sets/src/mage/cards/b/Brushwagg.java index 147d23b284..d6c6e568b6 100644 --- a/Mage.Sets/src/mage/cards/b/Brushwagg.java +++ b/Mage.Sets/src/mage/cards/b/Brushwagg.java @@ -3,7 +3,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class Brushwagg extends CardImpl { this.toughness = new MageInt(2); // Whenever Brushwagg blocks or becomes blocked, it gets -2/+2 until end of turn. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(-2, 2, Duration.EndOfTurn), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(-2, 2, Duration.EndOfTurn), false)); } public Brushwagg(final Brushwagg card) { diff --git a/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java b/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java index de15f1581d..308ddc69c1 100644 --- a/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java +++ b/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java @@ -83,13 +83,9 @@ class BrutalizerExarchEffect2 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - Player player = game.getPlayer(permanent.getOwnerId()); - if (player == null) { - return false; - } - - return permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); + Player controller = game.getPlayer(source.getControllerId()); + if (permanent != null && controller != null) { + return controller.putCardsOnBottomOfLibrary(permanent, game, source, true); } return false; } diff --git a/Mage.Sets/src/mage/cards/b/BruvacTheGrandiloquent.java b/Mage.Sets/src/mage/cards/b/BruvacTheGrandiloquent.java new file mode 100644 index 0000000000..be1371b865 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BruvacTheGrandiloquent.java @@ -0,0 +1,80 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BruvacTheGrandiloquent extends CardImpl { + + public BruvacTheGrandiloquent(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // If an opponent would mill one or more cards, they mill twice that many cards instead. + this.addAbility(new SimpleStaticAbility(new BruvacTheGrandiloquentReplacementEffect())); + } + + private BruvacTheGrandiloquent(final BruvacTheGrandiloquent card) { + super(card); + } + + @Override + public BruvacTheGrandiloquent copy() { + return new BruvacTheGrandiloquent(this); + } +} + +class BruvacTheGrandiloquentReplacementEffect extends ReplacementEffectImpl { + + BruvacTheGrandiloquentReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + staticText = "If an opponent would mill one or more cards, they mill twice that many cards instead"; + } + + private BruvacTheGrandiloquentReplacementEffect(final BruvacTheGrandiloquentReplacementEffect effect) { + super(effect); + } + + @Override + public BruvacTheGrandiloquentReplacementEffect copy() { + return new BruvacTheGrandiloquentReplacementEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.MILL_CARDS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return game.getOpponents(source.getControllerId()).contains(event.getPlayerId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(CardUtil.addWithOverflowCheck(event.getAmount(), event.getAmount())); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BuildersBane.java b/Mage.Sets/src/mage/cards/b/BuildersBane.java index 98b8621a8d..d29d7db605 100644 --- a/Mage.Sets/src/mage/cards/b/BuildersBane.java +++ b/Mage.Sets/src/mage/cards/b/BuildersBane.java @@ -77,7 +77,7 @@ class BuildersBaneEffect extends OneShotEffect { Permanent permanent = game.getPermanent(targetID); if (permanent != null) { if (permanent.destroy(source.getSourceId(), game, false)) { - game.applyEffects(); + game.getState().processAction(game); if (permanent.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(permanent.getId()) && game.getState().getZone(permanent.getId()) != Zone.GRAVEYARD) { // A replacement effect has moved the card to another zone as grvayard diff --git a/Mage.Sets/src/mage/cards/b/BumpInTheNight.java b/Mage.Sets/src/mage/cards/b/BumpInTheNight.java index 9dc048b4cf..024b2619e4 100644 --- a/Mage.Sets/src/mage/cards/b/BumpInTheNight.java +++ b/Mage.Sets/src/mage/cards/b/BumpInTheNight.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.keyword.FlashbackAbility; @@ -11,14 +9,15 @@ import mage.constants.CardType; import mage.constants.TimingRule; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** * @author nantuko */ public final class BumpInTheNight extends CardImpl { public BumpInTheNight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}"); // Target opponent loses 3 life. this.getSpellAbility().addEffect(new LoseLifeTargetEffect(3)); diff --git a/Mage.Sets/src/mage/cards/b/BurlfistOak.java b/Mage.Sets/src/mage/cards/b/BurlfistOak.java new file mode 100644 index 0000000000..66bd617a80 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BurlfistOak.java @@ -0,0 +1,40 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.DrawCardControllerTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BurlfistOak extends CardImpl { + + public BurlfistOak(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + + this.subtype.add(SubType.TREEFOLK); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever you draw a card, Burlfist Oak gets +2/+2 until end of turn. + this.addAbility(new DrawCardControllerTriggeredAbility( + new BoostSourceEffect(2, 2, Duration.EndOfTurn), false + )); + } + + private BurlfistOak(final BurlfistOak card) { + super(card); + } + + @Override + public BurlfistOak copy() { + return new BurlfistOak(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java b/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java index e916504a73..d8d34f9dd0 100644 --- a/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java +++ b/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java @@ -4,7 +4,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; @@ -31,7 +31,7 @@ public final class BurningEyeZubera extends CardImpl { this.toughness = new MageInt(3); // When Burning-Eye Zubera dies, if 4 or more damage was dealt to it this turn, Burning-Eye Zubera deals 3 damage to any target. - Ability ability = new ConditionalInterveningIfTriggeredAbility(new DiesTriggeredAbility(new DamageTargetEffect(3)),new SourceGotFourDamage(), + Ability ability = new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new DamageTargetEffect(3)),new SourceGotFourDamage(), "When {this} dies, if 4 or more damage was dealt to it this turn, Burning-Eye Zubera deals 3 damage to any target"); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BurningInquiry.java b/Mage.Sets/src/mage/cards/b/BurningInquiry.java index 6691d8323b..b8f1670d93 100644 --- a/Mage.Sets/src/mage/cards/b/BurningInquiry.java +++ b/Mage.Sets/src/mage/cards/b/BurningInquiry.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardAllEffect; import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; @@ -9,24 +7,24 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import java.util.UUID; + /** - * * @author North */ public final class BurningInquiry extends CardImpl { public BurningInquiry(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); // Each player draws three cards, then discards three cards at random. this.getSpellAbility().addEffect(new DrawCardAllEffect(3)); Effect effect = new DiscardEachPlayerEffect(3, true); - effect.setText("then discards three cards at random"); + effect.setText(", then discards three cards at random"); this.getSpellAbility().addEffect(effect); } - public BurningInquiry(final BurningInquiry card) { + private BurningInquiry(final BurningInquiry card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java b/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java new file mode 100644 index 0000000000..50fd0a2a6e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java @@ -0,0 +1,103 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BushmeatPoacher extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("another creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public BushmeatPoacher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // {1}, {T}: Sacrifice another creature: You gain life equal to that creature's toughness. Draw a card. + Ability ability = new SimpleActivatedAbility(new BushmeatPoacherEffect(), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + this.addAbility(ability); + } + + private BushmeatPoacher(final BushmeatPoacher card) { + super(card); + } + + @Override + public BushmeatPoacher copy() { + return new BushmeatPoacher(this); + } +} + +class BushmeatPoacherEffect extends OneShotEffect { + + BushmeatPoacherEffect() { + super(Outcome.Benefit); + staticText = "you gain life equal to that creature's toughness. Draw a card"; + } + + private BushmeatPoacherEffect(final BushmeatPoacherEffect effect) { + super(effect); + } + + @Override + public BushmeatPoacherEffect copy() { + return new BushmeatPoacherEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + SacrificeTargetCost cost = source + .getCosts() + .stream() + .filter(SacrificeTargetCost.class::isInstance) + .map(SacrificeTargetCost.class::cast) + .findFirst() + .orElse(null); + if (cost == null) { + return false; + } + Permanent permanent = cost.getPermanents().get(0); + if (permanent == null) { + return false; + } + int amount = permanent.getToughness().getValue(); + if (amount > 0) { + player.gainLife(amount, game, source); + } + return player.drawCards(1, source.getSourceId(), game) > 0; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/ButcherOfMalakir.java b/Mage.Sets/src/mage/cards/b/ButcherOfMalakir.java index c790bfca65..01addd3e52 100644 --- a/Mage.Sets/src/mage/cards/b/ButcherOfMalakir.java +++ b/Mage.Sets/src/mage/cards/b/ButcherOfMalakir.java @@ -11,7 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; /** @@ -36,7 +36,7 @@ public final class ButcherOfMalakir extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Butcher of Malakir or another creature you control dies, each opponent sacrifices a creature. - this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility(new SacrificeOpponentsEffect(new FilterControlledCreaturePermanent("a creature")), false, filter)); + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility(new SacrificeOpponentsEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT), false, filter)); } public ButcherOfMalakir(final ButcherOfMalakir card) { diff --git a/Mage.Sets/src/mage/cards/b/BywayCourier.java b/Mage.Sets/src/mage/cards/b/BywayCourier.java index 6e66b6a06d..dbf1f1d5f0 100644 --- a/Mage.Sets/src/mage/cards/b/BywayCourier.java +++ b/Mage.Sets/src/mage/cards/b/BywayCourier.java @@ -3,7 +3,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.keyword.InvestigateEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class BywayCourier extends CardImpl { this.toughness = new MageInt(2); // When Byway Courier dies, investigate. - this.addAbility(new DiesTriggeredAbility(new InvestigateEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new InvestigateEffect(), false)); } public BywayCourier(final BywayCourier card) { diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapist.java b/Mage.Sets/src/mage/cards/c/CabalTherapist.java index 96978c84ee..66ed076a80 100644 --- a/Mage.Sets/src/mage/cards/c/CabalTherapist.java +++ b/Mage.Sets/src/mage/cards/c/CabalTherapist.java @@ -3,23 +3,23 @@ package mage.cards.c; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ChooseACardNameEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.SendOptionUsedEventEffect; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.keyword.MenaceAbility; import mage.cards.*; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetControlledPermanent; -import mage.target.common.TargetOpponent; import mage.util.CardUtil; import java.util.UUID; @@ -40,16 +40,17 @@ public final class CabalTherapist extends CardImpl { this.addAbility(new MenaceAbility()); // At the beginning of your precombat main phase, you may sacrifice a creature. When you do, choose a nonland card name, then target player reveals their hand and discards all cards with that name. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME), + false, "choose a nonland card name, then target player " + + "reveals their hand and discards all cards with that name" + ); + ability.addEffect(new CabalTherapistDiscardEffect()); + ability.addTarget(new TargetPlayer()); this.addAbility(new BeginningOfPreCombatMainTriggeredAbility( - new DoIfCostPaid( - new CabalTherapistCreateReflexiveTriggerEffect(), - new SacrificeTargetCost(new TargetControlledPermanent( - StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT - )), "Sacrifice a creature?" - ).setText("you may sacrifice a creature. When you do, " + - "choose a nonland card name, then target player reveals their hand " + - "and discards all cards with that name."), - TargetController.YOU, false + new DoWhenCostPaid(ability, new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + ), "Sacrifice a creature?"), TargetController.YOU, false )); } @@ -63,62 +64,6 @@ public final class CabalTherapist extends CardImpl { } } -class CabalTherapistCreateReflexiveTriggerEffect extends OneShotEffect { - - CabalTherapistCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - private CabalTherapistCreateReflexiveTriggerEffect(final CabalTherapistCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public CabalTherapistCreateReflexiveTriggerEffect copy() { - return new CabalTherapistCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new CabalTherapistReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class CabalTherapistReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - CabalTherapistReflexiveTriggeredAbility() { - super(new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME), Duration.OneUse, true); - this.addEffect(new CabalTherapistDiscardEffect()); - this.addTarget(new TargetPlayer()); - } - - private CabalTherapistReflexiveTriggeredAbility(final CabalTherapistReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public CabalTherapistReflexiveTriggeredAbility copy() { - return new CabalTherapistReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "Choose a nonland card name, then target player reveals their hand and discards all cards with that name."; - } -} - class CabalTherapistDiscardEffect extends OneShotEffect { CabalTherapistDiscardEffect() { @@ -138,22 +83,16 @@ class CabalTherapistDiscardEffect extends OneShotEffect { return false; } String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - Cards hand = targetPlayer.getHand(); - - for (Card card : hand.getCards(game)) { - if (card.isSplitCard()) { - SplitCard splitCard = (SplitCard) card; - if (CardUtil.haveSameNames(splitCard.getLeftHalfCard().getName(), cardName)) { - targetPlayer.discard(card, source, game); - } else if (CardUtil.haveSameNames(splitCard.getRightHalfCard().getName(), cardName)) { - targetPlayer.discard(card, source, game); - } + Cards hand = targetPlayer.getHand().copy(); + targetPlayer.revealCards(source, hand, game); + hand.removeIf(uuid -> { + Card card = hand.get(uuid, game); + if (card == null) { + return true; } - if (CardUtil.haveSameNames(card.getName(), cardName)) { - targetPlayer.discard(card, source, game); - } - } - targetPlayer.revealCards("Cabal Therapist", hand, game); + return !CardUtil.haveSameNames(card, cardName, game); + }); + targetPlayer.discard(hand, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapy.java b/Mage.Sets/src/mage/cards/c/CabalTherapy.java index ba03643b0a..a7740f6298 100644 --- a/Mage.Sets/src/mage/cards/c/CabalTherapy.java +++ b/Mage.Sets/src/mage/cards/c/CabalTherapy.java @@ -18,6 +18,8 @@ import mage.target.common.TargetControlledCreaturePermanent; import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.StaticAbility; +import mage.filter.StaticFilters; /** * @author jonubuu @@ -34,7 +36,7 @@ public final class CabalTherapy extends CardImpl { // Flashback-Sacrifice a creature. this.addAbility(new FlashbackAbility( - new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent("a creature"), true)), + new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, true)), TimingRule.SORCERY)); } @@ -64,25 +66,20 @@ class CabalTherapyEffect extends OneShotEffect { Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (targetPlayer != null && controller != null && sourceObject != null) { - String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - Cards hand = targetPlayer.getHand(); - - for (Card card : hand.getCards(game)) { - if (card.isSplitCard()) { - SplitCard splitCard = (SplitCard) card; - if (CardUtil.haveSameNames(splitCard.getLeftHalfCard().getName(), cardName)) { - targetPlayer.discard(card, source, game); - } else if (CardUtil.haveSameNames(splitCard.getRightHalfCard().getName(), cardName)) { - targetPlayer.discard(card, source, game); - } - } - if (CardUtil.haveSameNames(card.getName(), cardName)) { - targetPlayer.discard(card, source, game); - } - } - targetPlayer.revealCards("Cabal Therapy", hand, game); + if (targetPlayer == null || controller == null || sourceObject == null) { + return false; } + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + Cards hand = targetPlayer.getHand().copy(); + targetPlayer.revealCards(source, hand, game); + hand.removeIf(uuid -> { + Card card = hand.get(uuid, game); + if (card == null) { + return true; + } + return !CardUtil.haveSameNames(card, cardName, game); + }); + targetPlayer.discard(hand, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/c/CadaverousBloom.java b/Mage.Sets/src/mage/cards/c/CadaverousBloom.java index e9db6991a1..3b8ffb89a9 100644 --- a/Mage.Sets/src/mage/cards/c/CadaverousBloom.java +++ b/Mage.Sets/src/mage/cards/c/CadaverousBloom.java @@ -1,15 +1,22 @@ - package mage.cards.c; +import java.util.ArrayList; +import java.util.List; +import static java.util.Locale.filter; import java.util.UUID; import mage.Mana; +import mage.abilities.Ability; import mage.abilities.costs.common.ExileFromHandCost; +import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; import mage.constants.Zone; import mage.filter.common.FilterOwnedCard; +import mage.game.Game; +import mage.players.Player; import mage.target.common.TargetCardInHand; /** @@ -19,11 +26,15 @@ import mage.target.common.TargetCardInHand; public final class CadaverousBloom extends CardImpl { public CadaverousBloom(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{B}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{G}"); // Exile a card from your hand: Add {B}{B} or {G}{G}. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlackMana(2), new ExileFromHandCost(new TargetCardInHand(new FilterOwnedCard("a card from your hand"))))); - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana(2), new ExileFromHandCost(new TargetCardInHand(new FilterOwnedCard("a card from your hand"))))); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, + new CadaverousBloomManaEffect(Mana.BlackMana(2)), + new ExileFromHandCost(new TargetCardInHand(new FilterOwnedCard("a card from your hand"))))); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, + new CadaverousBloomManaEffect(Mana.GreenMana(2)), + new ExileFromHandCost(new TargetCardInHand(new FilterOwnedCard("a card from your hand"))))); } public CadaverousBloom(final CadaverousBloom card) { @@ -35,3 +46,48 @@ public final class CadaverousBloom extends CardImpl { return new CadaverousBloom(this); } } + +class CadaverousBloomManaEffect extends BasicManaEffect { + + public CadaverousBloomManaEffect(Mana mana) { + super(mana); + } + + public CadaverousBloomManaEffect(final CadaverousBloomManaEffect effect) { + super(effect); + } + + @Override + public CadaverousBloomManaEffect copy() { + return new CadaverousBloomManaEffect(this); + } + + @Override + public List getNetMana(Game game, Ability source) { + if (game != null && game.inCheckPlayableState()) { + List netMana = new ArrayList<>(); + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + int count = player.getHand().size(); + if (count > 0) { + Mana mana = new Mana( + getManaTemplate().getRed() * count, + getManaTemplate().getGreen() * count, + getManaTemplate().getBlue() * count, + getManaTemplate().getWhite() * count, + getManaTemplate().getBlack() * count, + getManaTemplate().getGeneric() * count, + getManaTemplate().getAny() * count, + getManaTemplate().getColorless() * count + ); + + if (count > 0) { + netMana.add(mana); + } + } + } + return netMana; + } + return super.getNetMana(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CagedSun.java b/Mage.Sets/src/mage/cards/c/CagedSun.java index 4567bca5d1..92d37d47ce 100644 --- a/Mage.Sets/src/mage/cards/c/CagedSun.java +++ b/Mage.Sets/src/mage/cards/c/CagedSun.java @@ -1,5 +1,8 @@ package mage.cards.c; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import mage.Mana; import mage.ObjectColor; import mage.abilities.Ability; @@ -18,8 +21,6 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; -import java.util.UUID; - /** * @author BetaSteward */ @@ -135,6 +136,19 @@ class CagedSunEffect extends ManaEffect { super(effect); } + @Override + public List getNetMana(Game game, Ability source) { + if (game != null && game.inCheckPlayableState()) { + ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color"); + if (color != null) { + List availableNetMana = new ArrayList<>(); + availableNetMana.add(new Mana(ColoredManaSymbol.lookup(color.toString().charAt(0)))); + return availableNetMana; + } + } + return super.getNetMana(game, source); + } + @Override public Mana produceMana(Game game, Ability source) { if (game != null) { diff --git a/Mage.Sets/src/mage/cards/c/CagedZombie.java b/Mage.Sets/src/mage/cards/c/CagedZombie.java new file mode 100644 index 0000000000..a505d648ad --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CagedZombie.java @@ -0,0 +1,48 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.condition.common.MorbidCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.watchers.common.MorbidWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CagedZombie extends CardImpl { + + public CagedZombie(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // {1}{B}, {T}: Each opponent loses 2 life. Activate this ability only if a creature died this turn. + Ability ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new LoseLifeOpponentsEffect(2), + new ManaCostsImpl("{1}{B}"), MorbidCondition.instance + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability, new MorbidWatcher()); + } + + private CagedZombie(final CagedZombie card) { + super(card); + } + + @Override + public CagedZombie copy() { + return new CagedZombie(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CalciformPools.java b/Mage.Sets/src/mage/cards/c/CalciformPools.java index 9d12369191..e5e46e936a 100644 --- a/Mage.Sets/src/mage/cards/c/CalciformPools.java +++ b/Mage.Sets/src/mage/cards/c/CalciformPools.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -7,9 +6,10 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; -import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -26,17 +26,18 @@ import mage.counters.CounterType; public final class CalciformPools extends CardImpl { public CalciformPools(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - // {tap}: Add {C}. + // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); - // {1}, {tap}: Put a storage counter on Calciform Pools. + // {1}, {T}: Put a storage counter on Calciform Pools. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); // {1}, Remove X storage counters from Calciform Pools: Add X mana in any combination of {W} and/or {U}. ability = new SimpleManaAbility(Zone.BATTLEFIELD, - new AddManaInAnyCombinationEffect(RemovedCountersForCostValue.instance, ColoredManaSymbol.W, ColoredManaSymbol.U), + new AddManaInAnyCombinationEffect(RemovedCountersForCostValue.instance, + new CountersSourceCount(CounterType.STORAGE), ColoredManaSymbol.W, ColoredManaSymbol.U), new GenericManaCost(1)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/CalixDestinysHand.java b/Mage.Sets/src/mage/cards/c/CalixDestinysHand.java index 82c27d022d..06862cc241 100644 --- a/Mage.Sets/src/mage/cards/c/CalixDestinysHand.java +++ b/Mage.Sets/src/mage/cards/c/CalixDestinysHand.java @@ -1,7 +1,5 @@ package mage.cards.c; -import java.util.UUID; -import java.util.stream.Collectors; import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; @@ -15,7 +13,6 @@ import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetE import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import static mage.constants.Outcome.Benefit; import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledEnchantmentPermanent; @@ -29,6 +26,11 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; +import java.util.stream.Collectors; + +import static mage.constants.Outcome.Benefit; + /** * @author TheElk801 */ @@ -155,7 +157,7 @@ class CalixDestinysHandDelayedTriggeredAbility extends DelayedTriggeredAbility { return false; } this.getEffects().clear(); - this.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect() + this.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false) .setTargetPointer(new FixedTarget(this.theirMor))); return true; } diff --git a/Mage.Sets/src/mage/cards/c/CallOfTheDeathDweller.java b/Mage.Sets/src/mage/cards/c/CallOfTheDeathDweller.java new file mode 100644 index 0000000000..ff7193bfc4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CallOfTheDeathDweller.java @@ -0,0 +1,152 @@ +package mage.cards.c; + +import mage.MageItem; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class CallOfTheDeathDweller extends CardImpl { + + public CallOfTheDeathDweller(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); + + // Return up to two target creature cards with total converted mana cost 3 or less from your graveyard to the battlefield. Put a deathtouch counter on either of them. Then put a menace counter on either of them. + this.getSpellAbility().addEffect(new CallOfTheDeathDwellerEffect()); + this.getSpellAbility().addTarget(new CallOfTheDeathDwellerTarget()); + } + + private CallOfTheDeathDweller(final CallOfTheDeathDweller card) { + super(card); + } + + @Override + public CallOfTheDeathDweller copy() { + return new CallOfTheDeathDweller(this); + } +} + +class CallOfTheDeathDwellerEffect extends OneShotEffect { + + CallOfTheDeathDwellerEffect() { + super(Outcome.Benefit); + staticText = "Return up to two target creature cards with total converted mana cost 3 or less " + + "from your graveyard to the battlefield. Put a deathtouch counter on either of them. " + + "Then put a menace counter on either of them."; + } + + private CallOfTheDeathDwellerEffect(final CallOfTheDeathDwellerEffect effect) { + super(effect); + } + + @Override + public CallOfTheDeathDwellerEffect copy() { + return new CallOfTheDeathDwellerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Cards cards = new CardsImpl( + source.getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .collect(Collectors.toSet()) + ); + if (player == null || cards.isEmpty() + || !player.moveCards(cards, Zone.BATTLEFIELD, source, game)) { + return false; + } + List predicates = cards + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .map(MageItem::getId) + .map(PermanentIdPredicate::new) + .collect(Collectors.toList()); + if (predicates.isEmpty()) { + return false; + } + FilterPermanent filter = new FilterPermanent("creature to put a deathtouch counter on"); + filter.add(Predicates.or(predicates)); + Target target = new TargetPermanent(0, 1, filter, true); + if (player.choose(outcome, target, source.getSourceId(), game)) { + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + permanent.addCounters(CounterType.DEATHTOUCH.createInstance(), source, game); + } + } + filter.setMessage("creature to put a menace counter on"); + target = new TargetPermanent(0, 1, filter, true); + if (player.choose(outcome, target, source.getSourceId(), game)) { + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + permanent.addCounters(CounterType.MENACE.createInstance(), source, game); + } + } + return true; + } +} + +class CallOfTheDeathDwellerTarget extends TargetCardInYourGraveyard { + + private static final FilterCard filter + = new FilterCreatureCard("creature cards with total converted mana cost 3 or less from your graveyard"); + + static { + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + } + + CallOfTheDeathDwellerTarget() { + super(0, 2, filter, false); + } + + private CallOfTheDeathDwellerTarget(final CallOfTheDeathDwellerTarget target) { + super(target); + } + + @Override + public CallOfTheDeathDwellerTarget copy() { + return new CallOfTheDeathDwellerTarget(this); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + if (!super.canTarget(controllerId, id, source, game)) { + return false; + } + Card card = game.getCard(id); + return card != null && + this.getTargets() + .stream() + .map(game::getCard) + .mapToInt(Card::getConvertedManaCost) + .sum() + card.getConvertedManaCost() <= 3; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CallTheCoppercoats.java b/Mage.Sets/src/mage/cards/c/CallTheCoppercoats.java new file mode 100644 index 0000000000..d4edb90ae1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CallTheCoppercoats.java @@ -0,0 +1,82 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.abilityword.StriveAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.FilterOpponent; +import mage.filter.FilterPlayer; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.HumanSoldierToken; +import mage.game.permanent.token.Token; +import mage.target.Target; +import mage.target.TargetPlayer; + +import java.util.Collection; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CallTheCoppercoats extends CardImpl { + + private static final FilterPlayer filter = new FilterOpponent(); + + public CallTheCoppercoats(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // Strive — This spell costs {1}{W} more to cast for each target beyond the first. + this.addAbility(new StriveAbility("{1}{W}")); + + // Choose any number of target opponents. Create X 1/1 white Human soldier creature tokens, where X is the number of creatures those opponents control. + this.getSpellAbility().addEffect(new CallTheCoppercoatsEffect()); + this.getSpellAbility().addTarget(new TargetPlayer(0, Integer.MAX_VALUE, false, filter)); + } + + private CallTheCoppercoats(final CallTheCoppercoats card) { + super(card); + } + + @Override + public CallTheCoppercoats copy() { + return new CallTheCoppercoats(this); + } +} + +class CallTheCoppercoatsEffect extends OneShotEffect { + + private static final Token token = new HumanSoldierToken(); + + CallTheCoppercoatsEffect() { + super(Outcome.Benefit); + staticText = "Choose any number of target opponents. Create X 1/1 white Human soldier creature tokens, " + + "where X is the number of creatures those opponents control."; + } + + private CallTheCoppercoatsEffect(final CallTheCoppercoatsEffect effect) { + super(effect); + } + + @Override + public CallTheCoppercoatsEffect copy() { + return new CallTheCoppercoatsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return token.putOntoBattlefield( + source.getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .mapToInt(uuid -> game.getBattlefield().countAll( + StaticFilters.FILTER_PERMANENT_CREATURE, uuid, game + )).sum(), + game, source.getSourceId(), source.getControllerId() + ); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CallToHeel.java b/Mage.Sets/src/mage/cards/c/CallToHeel.java index 4e9ad8d18d..e8127595fc 100644 --- a/Mage.Sets/src/mage/cards/c/CallToHeel.java +++ b/Mage.Sets/src/mage/cards/c/CallToHeel.java @@ -67,7 +67,7 @@ class CallToHeelEffect extends OneShotEffect { if (permanent != null) { Player controller = game.getPlayer(permanent.getControllerId()); if (controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CallapheBelovedOfTheSea.java b/Mage.Sets/src/mage/cards/c/CallapheBelovedOfTheSea.java index 447db9f811..1baa9de971 100644 --- a/Mage.Sets/src/mage/cards/c/CallapheBelovedOfTheSea.java +++ b/Mage.Sets/src/mage/cards/c/CallapheBelovedOfTheSea.java @@ -2,23 +2,18 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.DevotionCount; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.common.continuous.SetPowerSourceEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.target.Target; -import mage.util.CardUtil; -import java.util.Collection; import java.util.UUID; /** @@ -51,11 +46,13 @@ public final class CallapheBelovedOfTheSea extends CardImpl { ).addHint(DevotionCount.U.getHint())); // Creatures and enchantments you control have "Spells your opponents cast that target this permanent cost {1} more to cast". - this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - new SimpleStaticAbility( - new CallapheBelovedOfTheSeaEffect() - ), Duration.WhileOnBattlefield, filter - ))); + Ability gainAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(1, new FilterCard("Spells"), TargetController.OPPONENT) + .withTargetName("this permanent") + ); + this.addAbility(new SimpleStaticAbility( + new GainAbilityControlledEffect(gainAbility, Duration.WhileOnBattlefield, filter).withForceQuotes() + )); } private CallapheBelovedOfTheSea(final CallapheBelovedOfTheSea card) { @@ -67,45 +64,3 @@ public final class CallapheBelovedOfTheSea extends CardImpl { return new CallapheBelovedOfTheSea(this); } } - -class CallapheBelovedOfTheSeaEffect extends CostModificationEffectImpl { - - CallapheBelovedOfTheSeaEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Spells your opponents cast that target this permanent cost {1} more to cast"; - } - - private CallapheBelovedOfTheSeaEffect(CallapheBelovedOfTheSeaEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -1); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (!(abilityToModify instanceof SpellAbility) - || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - return false; - } - return abilityToModify - .getModes() - .getSelectedModes() - .stream() - .map(uuid -> abilityToModify.getModes().get(uuid)) - .map(Mode::getTargets) - .flatMap(Collection::stream) - .map(Target::getTargets) - .flatMap(Collection::stream) - .anyMatch(uuid -> uuid.equals(source.getSourceId())); - } - - @Override - public CallapheBelovedOfTheSeaEffect copy() { - return new CallapheBelovedOfTheSeaEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/c/Camaraderie.java b/Mage.Sets/src/mage/cards/c/Camaraderie.java index 1e0880911b..8bbc87a8c5 100644 --- a/Mage.Sets/src/mage/cards/c/Camaraderie.java +++ b/Mage.Sets/src/mage/cards/c/Camaraderie.java @@ -67,7 +67,7 @@ class CamaraderieEffect extends OneShotEffect { source.getSourceId(), source.getControllerId(), game ); player.gainLife(xValue, game, source); - player.drawCards(xValue, game); + player.drawCards(xValue, source.getSourceId(), game); game.addEffect(new BoostControlledEffect(1, 1, Duration.EndOfTurn), source); return true; } diff --git a/Mage.Sets/src/mage/cards/c/CanopyStalker.java b/Mage.Sets/src/mage/cards/c/CanopyStalker.java new file mode 100644 index 0000000000..364733b781 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CanopyStalker.java @@ -0,0 +1,48 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.CreaturesDiedThisTurnCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.watchers.common.CreaturesDiedWatcher; + +import java.util.UUID; +import mage.constants.Duration; + +/** + * @author TheElk801 + */ +public final class CanopyStalker extends CardImpl { + + public CanopyStalker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Canopy Stalker must be blocked if able. + this.addAbility(new SimpleStaticAbility(new MustBeBlockedByAtLeastOneSourceEffect(Duration.WhileOnBattlefield))); + + // When Canopy Stalker dies, you gain 1 life for each creature that died this turn. + this.addAbility(new DiesSourceTriggeredAbility( + new GainLifeEffect(CreaturesDiedThisTurnCount.instance) + .setText("you gain 1 life for each creature that died this turn") + ), new CreaturesDiedWatcher()); + } + + private CanopyStalker(final CanopyStalker card) { + super(card); + } + + @Override + public CanopyStalker copy() { + return new CanopyStalker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CapitalOffense.java b/Mage.Sets/src/mage/cards/c/CapitalOffense.java index 56a678cb46..43f2106fb4 100644 --- a/Mage.Sets/src/mage/cards/c/CapitalOffense.java +++ b/Mage.Sets/src/mage/cards/c/CapitalOffense.java @@ -1,8 +1,5 @@ - package mage.cards.c; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; @@ -11,15 +8,17 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.repository.CardInfo; -import mage.constants.CardType; import mage.cards.repository.CardRepository; +import mage.constants.CardType; import mage.constants.Duration; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.List; +import java.util.UUID; + /** - * * @author spjspj */ public final class CapitalOffense extends CardImpl { @@ -28,12 +27,15 @@ public final class CapitalOffense extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{B}"); // target creature gets -x/-x until end of turn, where x is the number of times a capital letter appears in its rules text. (ignore reminder text and flavor text.) - DynamicValue xValue = new NumberOfCapitalsInTextOfTargetCreatureCount(); - this.getSpellAbility().addEffect(new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn, true)); + this.getSpellAbility().addEffect(new BoostTargetEffect( + capitaloffensecount.instance, capitaloffensecount.instance, Duration.EndOfTurn, true + ).setText("target creature gets -x/-x until end of turn, where x is the number of times " + + "a capital letter appears in its rules text. (ignore reminder text and flavor text.)" + )); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } - public CapitalOffense(final CapitalOffense card) { + private CapitalOffense(final CapitalOffense card) { super(card); } @@ -43,39 +45,43 @@ public final class CapitalOffense extends CardImpl { } } -class NumberOfCapitalsInTextOfTargetCreatureCount implements DynamicValue { +enum capitaloffensecount implements DynamicValue { + instance; @Override - public NumberOfCapitalsInTextOfTargetCreatureCount copy() { - return new NumberOfCapitalsInTextOfTargetCreatureCount(); + public capitaloffensecount copy() { + return instance; } @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { Permanent permanent = game.getPermanent(sourceAbility.getTargets().get(0).getFirstTarget()); - if (permanent != null) { - int capitals = 0; - List cards = CardRepository.instance.findCards(permanent.getName()); + if (permanent == null) { + return 0; + } + int capitals = 0; + List cards = CardRepository.instance.findCards(permanent.getName()); - if (cards != null) { - for (CardInfo cardInfo : cards) { - Card dummy = cardInfo != null ? cardInfo.getCard() : null; - if (dummy != null) { - for (String line : dummy.getRules()) { - line = line.replaceAll("(?i)", ""); // Ignoring reminder text in italic - line = line.replaceAll("\\{this\\}", permanent.getName()); - capitals += line.length() - line.replaceAll("[A-Z]", "").length(); - } - } - return -1 * capitals; - } + if (cards == null) { + return 0; + } + for (CardInfo cardInfo : cards) { + Card dummy = cardInfo != null ? cardInfo.getCard() : null; + if (dummy == null) { + return -1 * capitals; } + for (String line : dummy.getRules()) { + line = line.replaceAll("(?i)", ""); // Ignoring reminder text in italic + line = line.replaceAll("\\{this\\}", permanent.getName()); + capitals += line.length() - line.replaceAll("[A-Z]", "").length(); + } + return -1 * capitals; } return 0; } @Override public String getMessage() { - return "target creature gets -x/-x until end of turn, where x is the number of times a capital letter appears in its rules text. (ignore reminder text and flavor text.)"; + return ""; } } diff --git a/Mage.Sets/src/mage/cards/c/Capricopian.java b/Mage.Sets/src/mage/cards/c/Capricopian.java new file mode 100644 index 0000000000..921f47e4e5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Capricopian.java @@ -0,0 +1,127 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbilityImpl; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.common.IsStepCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPlayer; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.other.PlayerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Capricopian extends CardImpl { + + public Capricopian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{G}"); + + this.subtype.add(SubType.GOAT); + this.subtype.add(SubType.HYDRA); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Capricopian enters the battlefield with X +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility( + new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()) + )); + + // {2}: Put a +1/+1 counter on Capricopian, then you may reselect which player Capricopian is attacking. Only the player Capricopian is attacking may activate this ability and only during the declare attackers step. + this.addAbility(new CapricopianActivatedAbility()); + } + + private Capricopian(final Capricopian card) { + super(card); + } + + @Override + public Capricopian copy() { + return new Capricopian(this); + } +} + +class CapricopianActivatedAbility extends ActivatedAbilityImpl { + + CapricopianActivatedAbility() { + super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(2)); + this.addEffect(new CapricopianEffect()); + this.setMayActivate(TargetController.ANY); + this.condition = new IsStepCondition(PhaseStep.DECLARE_ATTACKERS); + } + + private CapricopianActivatedAbility(final CapricopianActivatedAbility ability) { + super(ability); + } + + @Override + public CapricopianActivatedAbility copy() { + return new CapricopianActivatedAbility(this); + } + + @Override + public String getRule() { + return "{2}: Put a +1/+1 counter on {this}, then you may reselect which player {this} is attacking. " + + "Only the player {this} is attacking may activate this ability " + + "and only during the declare attackers step. (It can't attack its controller.)"; + } + + @Override + protected boolean checkTargetController(UUID playerId, Game game) { + return super.checkTargetController(playerId, game) + && playerId != null + && playerId.equals(game.getCombat().getDefenderId(this.getSourceId())); + } +} + +class CapricopianEffect extends OneShotEffect { + + CapricopianEffect() { + super(Outcome.Benefit); + } + + private CapricopianEffect(final CapricopianEffect effect) { + super(effect); + } + + @Override + public CapricopianEffect copy() { + return new CapricopianEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (player == null || permanent == null) { + return false; + } + if (!player.chooseUse(outcome, "Reselect attacker for " + permanent.getIdName() + "?", source, game)) { + return false; + } + FilterPlayer filterPlayer = new FilterPlayer(); + filterPlayer.add(Predicates.not(new PlayerIdPredicate(permanent.getControllerId()))); + filterPlayer.add(Predicates.not(new PlayerIdPredicate(game.getCombat().getDefenderId(permanent.getId())))); + TargetPlayer targetPlayer = new TargetPlayer(0, 1, true, filterPlayer); + player.choose(outcome, targetPlayer, source.getSourceId(), game); + Player newPlayer = game.getPlayer(targetPlayer.getFirstTarget()); + if (newPlayer == null) { + return false; + } + return game.getCombat().addAttackingCreature(permanent.getId(), game, newPlayer.getId()); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CapturedByTheConsulate.java b/Mage.Sets/src/mage/cards/c/CapturedByTheConsulate.java index ba7199bed2..d67b2804b2 100644 --- a/Mage.Sets/src/mage/cards/c/CapturedByTheConsulate.java +++ b/Mage.Sets/src/mage/cards/c/CapturedByTheConsulate.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.TriggeredAbilityImpl; @@ -14,7 +12,7 @@ import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -26,24 +24,19 @@ import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CapturedByTheConsulate extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public CapturedByTheConsulate(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); this.subtype.add(SubType.AURA); // Enchant creature you don't control - TargetPermanent auraTarget = new TargetCreaturePermanent(filter); + TargetPermanent auraTarget = new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.UnboostCreature)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); @@ -54,10 +47,9 @@ public final class CapturedByTheConsulate extends CardImpl { // Whenever an opponent casts a spell, if it has a single target, change the target to enchanted creature if able. this.addAbility(new CapturedByTheConsulateTriggeredAbility(Zone.BATTLEFIELD, new CapturedByTheConsulateEffect())); - } - public CapturedByTheConsulate(final CapturedByTheConsulate card) { + private CapturedByTheConsulate(final CapturedByTheConsulate card) { super(card); } @@ -70,15 +62,14 @@ public final class CapturedByTheConsulate extends CardImpl { class CapturedByTheConsulateTriggeredAbility extends TriggeredAbilityImpl { /** - * * @param zone * @param effect */ - public CapturedByTheConsulateTriggeredAbility(Zone zone, Effect effect) { + CapturedByTheConsulateTriggeredAbility(Zone zone, Effect effect) { super(zone, effect, false); } - public CapturedByTheConsulateTriggeredAbility(final CapturedByTheConsulateTriggeredAbility ability) { + private CapturedByTheConsulateTriggeredAbility(final CapturedByTheConsulateTriggeredAbility ability) { super(ability); } @@ -133,12 +124,12 @@ class CapturedByTheConsulateTriggeredAbility extends TriggeredAbilityImpl { class CapturedByTheConsulateEffect extends OneShotEffect { - public CapturedByTheConsulateEffect() { + CapturedByTheConsulateEffect() { super(Outcome.Benefit); this.staticText = "change the target to enchanted creature if able"; } - public CapturedByTheConsulateEffect(final CapturedByTheConsulateEffect effect) { + private CapturedByTheConsulateEffect(final CapturedByTheConsulateEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/c/Carapace.java b/Mage.Sets/src/mage/cards/c/Carapace.java index 9c36e39dd8..d315ba38a3 100644 --- a/Mage.Sets/src/mage/cards/c/Carapace.java +++ b/Mage.Sets/src/mage/cards/c/Carapace.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -16,15 +14,16 @@ import mage.constants.*; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Galatolol */ public final class Carapace extends CardImpl { public Carapace(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); - + this.subtype.add(SubType.AURA); // Enchant creature diff --git a/Mage.Sets/src/mage/cards/c/CarapaceForger.java b/Mage.Sets/src/mage/cards/c/CarapaceForger.java index 01d92bba75..1e8735e306 100644 --- a/Mage.Sets/src/mage/cards/c/CarapaceForger.java +++ b/Mage.Sets/src/mage/cards/c/CarapaceForger.java @@ -1,5 +1,3 @@ - - package mage.cards.c; import mage.MageInt; @@ -7,12 +5,10 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; import java.util.UUID; @@ -35,8 +31,9 @@ public final class CarapaceForger extends CardImpl { new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), MetalcraftCondition.instance, "Metalcraft — {this} gets " + "+2/+2 as long as you control three or more artifacts" - ) - )); + )) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance)); } public CarapaceForger(final CarapaceForger card) { diff --git a/Mage.Sets/src/mage/cards/c/CarelessCelebrant.java b/Mage.Sets/src/mage/cards/c/CarelessCelebrant.java index 6b97dd3bd2..c2fbb8e0b1 100644 --- a/Mage.Sets/src/mage/cards/c/CarelessCelebrant.java +++ b/Mage.Sets/src/mage/cards/c/CarelessCelebrant.java @@ -2,7 +2,7 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -40,7 +40,7 @@ public final class CarelessCelebrant extends CardImpl { this.toughness = new MageInt(1); // When Careless Celebrant dies, it deals 2 damage to target creature or planeswalker an opponent controls. - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it")); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2, "it")); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CarnageTyrant.java b/Mage.Sets/src/mage/cards/c/CarnageTyrant.java index d252b78390..ed47c44b28 100644 --- a/Mage.Sets/src/mage/cards/c/CarnageTyrant.java +++ b/Mage.Sets/src/mage/cards/c/CarnageTyrant.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.HexproofAbility; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class CarnageTyrant extends CardImpl { this.toughness = new MageInt(6); // Carnage Tyrant can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Trample this.addAbility(TrampleAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/c/CarrierThrall.java b/Mage.Sets/src/mage/cards/c/CarrierThrall.java index d6bc7cea23..94e3bf2813 100644 --- a/Mage.Sets/src/mage/cards/c/CarrierThrall.java +++ b/Mage.Sets/src/mage/cards/c/CarrierThrall.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class CarrierThrall extends CardImpl { // When Carrier Thrall dies, create a 1/1 colorless Eldrazi Scion creature token. It has "Sacrifice this creature. Add {C}." Effect effect = new CreateTokenEffect(new EldraziScionToken()); effect.setText("create a 1/1 colorless Eldrazi Scion creature token. It has \"Sacrifice this creature: Add {C}.\""); - this.addAbility(new DiesTriggeredAbility(effect, false)); + this.addAbility(new DiesSourceTriggeredAbility(effect, false)); } diff --git a/Mage.Sets/src/mage/cards/c/CarrionBeetles.java b/Mage.Sets/src/mage/cards/c/CarrionBeetles.java index beb63b6366..a8eb3bd570 100644 --- a/Mage.Sets/src/mage/cards/c/CarrionBeetles.java +++ b/Mage.Sets/src/mage/cards/c/CarrionBeetles.java @@ -1,40 +1,36 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInASingleGraveyard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class CarrionBeetles extends CardImpl { public CarrionBeetles(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); this.subtype.add(SubType.INSECT); this.power = new MageInt(1); this.toughness = new MageInt(1); - // {2}{B}, {tap}: Exile up to three target cards from a single graveyard. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CarrionBeetlesExileEffect(), new ManaCostsImpl("{2}{B}")); + // {2}{B}, {T}: Exile up to three target cards from a single graveyard. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new ManaCostsImpl("{2}{B}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("up to three target cards from a single graveyard"))); + ability.addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS)); this.addAbility(ability); } @@ -47,31 +43,3 @@ public final class CarrionBeetles extends CardImpl { return new CarrionBeetles(this); } } - -class CarrionBeetlesExileEffect extends OneShotEffect { - - public CarrionBeetlesExileEffect() { - super(Outcome.Exile); - this.staticText = "Exile up to three target cards from a single graveyard"; - } - - public CarrionBeetlesExileEffect(final CarrionBeetlesExileEffect effect) { - super(effect); - } - - @Override - public CarrionBeetlesExileEffect copy() { - return new CarrionBeetlesExileEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID targetID : source.getTargets().get(0).getTargets()) { - Card card = game.getCard(targetID); - if (card != null) { - card.moveToExile(null, "", source.getSourceId(), game); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CarrionGrub.java b/Mage.Sets/src/mage/cards/c/CarrionGrub.java new file mode 100644 index 0000000000..b0f52fc791 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CarrionGrub.java @@ -0,0 +1,89 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.game.Game; +import mage.players.Player; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CarrionGrub extends CardImpl { + + public CarrionGrub(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.INSECT); + this.power = new MageInt(0); + this.toughness = new MageInt(5); + + // Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + CarrionGrubValue.instance, StaticValue.get(0), Duration.WhileOnBattlefield + ))); + + // When Carrion Grub enters the battlefield, mill four cards. + this.addAbility(new EntersBattlefieldTriggeredAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(4))); + } + + private CarrionGrub(final CarrionGrub card) { + super(card); + } + + @Override + public CarrionGrub copy() { + return new CarrionGrub(this); + } +} + +enum CarrionGrubValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Player player = game.getPlayer(sourceAbility.getControllerId()); + if (player == null) { + return 0; + } + return player.getGraveyard() + .getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(MageObject::isCreature) + .map(MageObject::getPower) + .mapToInt(MageInt::getValue) + .max() + .orElse(0); + } + + @Override + public CarrionGrubValue copy() { + return instance; + } + + @Override + public String getMessage() { + return "the greatest power among creature cards in your graveyard"; + } + + @Override + public String toString() { + return "X"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/CarrionThrash.java b/Mage.Sets/src/mage/cards/c/CarrionThrash.java index 0bc6afc9f8..62b96da57e 100644 --- a/Mage.Sets/src/mage/cards/c/CarrionThrash.java +++ b/Mage.Sets/src/mage/cards/c/CarrionThrash.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.ReturnToHandTargetEffect; @@ -36,7 +36,7 @@ public final class CarrionThrash extends CardImpl { this.toughness = new MageInt(4); // When Carrion Thrash dies, you may pay {2}. If you do, return another target creature card from your graveyard to your hand. - DiesTriggeredAbility ability = new DiesTriggeredAbility(new DoIfCostPaid(new ReturnToHandTargetEffect(), new GenericManaCost(2)), false); + DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new ReturnToHandTargetEffect(), new GenericManaCost(2)), false); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CartographersHawk.java b/Mage.Sets/src/mage/cards/c/CartographersHawk.java new file mode 100644 index 0000000000..a9168c1635 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CartographersHawk.java @@ -0,0 +1,128 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CartographersHawk extends CardImpl { + + public CartographersHawk(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.BIRD); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Cartographer's Hawk deals combat damage to a player who controls more lands than you, return it to its owner's hand. If you do, you may search your library for a Plains card, put it onto the battlefield tapped, then shuffle your library. + this.addAbility(new CartographersHawkTriggeredAbility()); + } + + private CartographersHawk(final CartographersHawk card) { + super(card); + } + + @Override + public CartographersHawk copy() { + return new CartographersHawk(this); + } +} + +class CartographersHawkTriggeredAbility extends TriggeredAbilityImpl { + + CartographersHawkTriggeredAbility() { + super(Zone.BATTLEFIELD, new CartographersHawkEffect(), false); + } + + private CartographersHawkTriggeredAbility(final CartographersHawkTriggeredAbility ability) { + super(ability); + } + + @Override + public CartographersHawkTriggeredAbility copy() { + return new CartographersHawkTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getSourceId().equals(this.getSourceId()) + && game.getBattlefield().countAll( + StaticFilters.FILTER_LAND, this.getControllerId(), game + ) < game.getBattlefield().countAll( + StaticFilters.FILTER_LAND, event.getTargetId(), game + ); + } + + @Override + public String getRule() { + return "When {this} deals combat damage to a player who controls more lands than you, " + + "return it to its owner's hand. If you do, you may search your library for a Plains card, " + + "put it onto the battlefield tapped, then shuffle your library."; + } +} + +class CartographersHawkEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("a Plains card"); + + static { + filter.add(SubType.PLAINS.getPredicate()); + } + + CartographersHawkEffect() { + super(Outcome.Benefit); + } + + private CartographersHawkEffect(final CartographersHawkEffect effect) { + super(effect); + } + + @Override + public CartographersHawkEffect copy() { + return new CartographersHawkEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + Player player = game.getPlayer(source.getControllerId()); + if (permanent == null || player == null) { + return false; + } + if (!player.moveCards(permanent, Zone.HAND, source, game)) { + return false; + } + if (!player.chooseUse(Outcome.PutLandInPlay, "Search your library for Plains card?", source, game)) { + return true; + } + return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CastThroughTime.java b/Mage.Sets/src/mage/cards/c/CastThroughTime.java index 1693ae89d5..14b0171384 100644 --- a/Mage.Sets/src/mage/cards/c/CastThroughTime.java +++ b/Mage.Sets/src/mage/cards/c/CastThroughTime.java @@ -89,7 +89,7 @@ class GainReboundEffect extends ContinuousEffectImpl { private void addReboundAbility(Card card, Game game) { if (CastThroughTime.filter.match(card, game)) { - boolean found = card.getAbilities(game).stream().anyMatch(ability -> ability instanceof ReboundAbility); + boolean found = card.getAbilities(game).containsClass(ReboundAbility.class); if (!found) { Ability ability = new ReboundAbility(); game.getState().addOtherAbility(card, ability); diff --git a/Mage.Sets/src/mage/cards/c/CataclysmicGearhulk.java b/Mage.Sets/src/mage/cards/c/CataclysmicGearhulk.java index 2c76cf9d58..f3e58623e0 100644 --- a/Mage.Sets/src/mage/cards/c/CataclysmicGearhulk.java +++ b/Mage.Sets/src/mage/cards/c/CataclysmicGearhulk.java @@ -1,9 +1,6 @@ package mage.cards.c; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -13,8 +10,8 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.common.FilterControlledArtifactPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledEnchantmentPermanent; @@ -26,14 +23,17 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetControlledPermanent; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** - * * @author fireshoes */ public final class CataclysmicGearhulk extends CardImpl { public CataclysmicGearhulk(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{W}{W}"); this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(4); this.toughness = new MageInt(5); @@ -80,8 +80,8 @@ class CataclysmicGearhulkEffect extends OneShotEffect { public CataclysmicGearhulkEffect() { super(Outcome.DestroyPermanent); - staticText = "Each player chooses from among the non-land permanents they control an artifact, a creature, an enchantment, and a planeswalker, " - + "then sacrifices the rest"; + staticText = "each player chooses an artifact, a creature, an enchantment, and a planeswalker " + + "from among the nonland permanents they control then sacrifices the rest"; } public CataclysmicGearhulkEffect(CataclysmicGearhulkEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CatharsCompanion.java b/Mage.Sets/src/mage/cards/c/CatharsCompanion.java index d26614f24f..f42b71fd1e 100644 --- a/Mage.Sets/src/mage/cards/c/CatharsCompanion.java +++ b/Mage.Sets/src/mage/cards/c/CatharsCompanion.java @@ -27,7 +27,7 @@ public final class CatharsCompanion extends CardImpl { public CatharsCompanion(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/c/CathedralOfSerra.java b/Mage.Sets/src/mage/cards/c/CathedralOfSerra.java index 854dca56f9..98fefce3e9 100644 --- a/Mage.Sets/src/mage/cards/c/CathedralOfSerra.java +++ b/Mage.Sets/src/mage/cards/c/CathedralOfSerra.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -15,8 +13,9 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author L_J */ public final class CathedralOfSerra extends CardImpl { @@ -32,7 +31,10 @@ public final class CathedralOfSerra extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // White legendary creatures you control have "bands with other legendary creatures." - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(new BandsWithOtherAbility(SuperType.LEGENDARY), Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect( + new BandsWithOtherAbility(SuperType.LEGENDARY), Duration.WhileOnBattlefield, filter) + .withForceQuotes() + )); } public CathedralOfSerra(final CathedralOfSerra card) { diff --git a/Mage.Sets/src/mage/cards/c/Cathodion.java b/Mage.Sets/src/mage/cards/c/Cathodion.java index 7de6f730f6..212d3a6872 100644 --- a/Mage.Sets/src/mage/cards/c/Cathodion.java +++ b/Mage.Sets/src/mage/cards/c/Cathodion.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.Mana; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.mana.BasicManaEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -22,7 +22,7 @@ public final class Cathodion extends CardImpl { this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(3); this.toughness = new MageInt(3); - this.addAbility(new DiesTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(3)), false)); + this.addAbility(new DiesSourceTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(3)), false)); } public Cathodion(final Cathodion card) { diff --git a/Mage.Sets/src/mage/cards/c/CauldronsGift.java b/Mage.Sets/src/mage/cards/c/CauldronsGift.java index 923e9568a8..6d936425ff 100644 --- a/Mage.Sets/src/mage/cards/c/CauldronsGift.java +++ b/Mage.Sets/src/mage/cards/c/CauldronsGift.java @@ -47,8 +47,7 @@ class CauldronsGiftEffect extends OneShotEffect { CauldronsGiftEffect() { super(Outcome.Benefit); staticText = "Adamant — If at least three black mana was spent to cast this spell, " + - "put the top four cards of your library into your graveyard." + - "
You may choose a creature card in your graveyard. " + + "mill four cards.
You may choose a creature card in your graveyard. " + "If you do, return it to the battlefield with an additional +1/+1 counter on it."; } @@ -68,7 +67,7 @@ class CauldronsGiftEffect extends OneShotEffect { return false; } if (AdamantCondition.BLACK.apply(game, source)) { - player.moveCards(player.getLibrary().getTopCards(game, 4), Zone.GRAVEYARD, source, game); + player.millCards(4, source, game); } if (player.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game) == 0 || !player.chooseUse(outcome, "Choose a creature card in your graveyard to return to the battlefield?", source, game)) { diff --git a/Mage.Sets/src/mage/cards/c/CausticHound.java b/Mage.Sets/src/mage/cards/c/CausticHound.java index 021f30af70..f01e20fae4 100644 --- a/Mage.Sets/src/mage/cards/c/CausticHound.java +++ b/Mage.Sets/src/mage/cards/c/CausticHound.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeAllPlayersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -19,11 +19,11 @@ public final class CausticHound extends CardImpl { public CausticHound (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{B}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(4); this.toughness = new MageInt(4); - this.addAbility(new DiesTriggeredAbility(new LoseLifeAllPlayersEffect(4))); + this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeAllPlayersEffect(4))); } public CausticHound (final CausticHound card) { diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfDawn.java b/Mage.Sets/src/mage/cards/c/CavalierOfDawn.java index 2eba411769..a77ae3e3fd 100644 --- a/Mage.Sets/src/mage/cards/c/CavalierOfDawn.java +++ b/Mage.Sets/src/mage/cards/c/CavalierOfDawn.java @@ -2,7 +2,7 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -51,7 +51,7 @@ public final class CavalierOfDawn extends CardImpl { this.addAbility(ability); // When Cavalier of Dawn dies, return target artifact or enchantment card from your graveyard to your hand. - ability = new DiesTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java b/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java index 79dc225945..4de1d6c087 100644 --- a/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java +++ b/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java @@ -2,7 +2,7 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -16,6 +16,7 @@ import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; @@ -60,7 +61,7 @@ public final class CavalierOfFlame extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new CavalierOfFlameEffect())); // When Cavalier of Flame dies, it deals X damage to each opponent and each planeswalker they control, where X is the number of land cards in your graveyard. - ability = new DiesTriggeredAbility(new DamagePlayersEffect( + ability = new DiesSourceTriggeredAbility(new DamagePlayersEffect( Outcome.Damage, xValue, TargetController.OPPONENT ).setText("it deals X damage to each opponent")); ability.addEffect(new DamageAllEffect( @@ -103,14 +104,9 @@ class CavalierOfFlameEffect extends OneShotEffect { } TargetCardInHand target = new TargetCardInHand(0, player.getHand().size(), StaticFilters.FILTER_CARD); if (player.choose(Outcome.Discard, player.getHand(), target, game)) { - int counter = target - .getTargets() - .stream() - .map(uuid -> game.getCard(uuid)) - .mapToInt(card -> card != null && player.discard(card, source, game) ? 1 : 0) - .sum(); - player.drawCards(counter, game); + int counter = player.discard(new CardsImpl(target.getTargets()), source, game).size(); + player.drawCards(counter, source.getSourceId(), game); } return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfGales.java b/Mage.Sets/src/mage/cards/c/CavalierOfGales.java index e972f50c39..76f16388fa 100644 --- a/Mage.Sets/src/mage/cards/c/CavalierOfGales.java +++ b/Mage.Sets/src/mage/cards/c/CavalierOfGales.java @@ -2,7 +2,7 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.BrainstormEffect; import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; @@ -35,7 +35,7 @@ public final class CavalierOfGales extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new BrainstormEffect())); // When Cavalier of Gales dies, shuffle it into its owner's library, then scry 2. - Ability ability = new DiesTriggeredAbility(new ShuffleIntoLibrarySourceEffect()); + Ability ability = new DiesSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect()); ability.addEffect(new ScryEffect(2).concatBy(", then")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfNight.java b/Mage.Sets/src/mage/cards/c/CavalierOfNight.java index 47437215ba..2020c78e3d 100644 --- a/Mage.Sets/src/mage/cards/c/CavalierOfNight.java +++ b/Mage.Sets/src/mage/cards/c/CavalierOfNight.java @@ -2,27 +2,25 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetOpponentsCreaturePermanent; @@ -56,13 +54,17 @@ public final class CavalierOfNight extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // When Cavalier of Night enters the battlefield, you may sacrifice another creature. When you do, destroy target creature an opponent controls. - this.addAbility(new EntersBattlefieldTriggeredAbility(new DoIfCostPaid( - new CavalierOfNightCreateReflexiveTriggerEffect(), - new SacrificeTargetCost(new TargetControlledPermanent(filter)) - ).setText("you may sacrifice another creature. When you do, destroy target creature an opponent controls."))); + ReflexiveTriggeredAbility triggeredAbility = new ReflexiveTriggeredAbility( + new DestroyTargetEffect(), false, "Sacrifice a creature?" + ); + triggeredAbility.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DoWhenCostPaid( + triggeredAbility, new SacrificeTargetCost(new TargetControlledPermanent(filter)), + "destroy target creature an opponent controls" + ))); // When Cavalier of Night dies, return target creature card with converted mana cost 3 or less from your graveyard to the battlefield. - Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()); + Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()); ability.addTarget(new TargetCardInYourGraveyard(filter2)); this.addAbility(ability); } @@ -76,58 +78,3 @@ public final class CavalierOfNight extends CardImpl { return new CavalierOfNight(this); } } - -class CavalierOfNightCreateReflexiveTriggerEffect extends OneShotEffect { - - CavalierOfNightCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - private CavalierOfNightCreateReflexiveTriggerEffect(final CavalierOfNightCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public CavalierOfNightCreateReflexiveTriggerEffect copy() { - return new CavalierOfNightCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new CavalierOfNightReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class CavalierOfNightReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - CavalierOfNightReflexiveTriggeredAbility() { - super(new DestroyTargetEffect(), Duration.OneUse, true); - this.addTarget(new TargetOpponentsCreaturePermanent()); - } - - private CavalierOfNightReflexiveTriggeredAbility(final CavalierOfNightReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public CavalierOfNightReflexiveTriggeredAbility copy() { - return new CavalierOfNightReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "Destroy target creature an opponent controls"; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java b/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java index eabcf3643d..14b84ac4a1 100644 --- a/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java +++ b/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java @@ -2,7 +2,7 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.costs.common.ExileSourceFromGraveCost; import mage.abilities.effects.OneShotEffect; @@ -50,7 +50,7 @@ public final class CavalierOfThorns extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new CavalierOfThornsEffect())); // When Cavalier of Thorns dies, you may exile it. If you do, put another target card from your graveyard on top of your library. - Ability ability = new DiesTriggeredAbility(new DoIfCostPaid( + Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid( new PutOnLibraryTargetEffect(true), new ExileSourceFromGraveCost() ).setText("you may exile it. If you do, put another target card from your graveyard on top of your library.")); ability.addTarget(new TargetCardInYourGraveyard(filter)); diff --git a/Mage.Sets/src/mage/cards/c/CavernOfSouls.java b/Mage.Sets/src/mage/cards/c/CavernOfSouls.java index 1700ea17be..bea0f012e5 100644 --- a/Mage.Sets/src/mage/cards/c/CavernOfSouls.java +++ b/Mage.Sets/src/mage/cards/c/CavernOfSouls.java @@ -1,11 +1,11 @@ package mage.cards.c; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; + import mage.ConditionalMana; import mage.MageObject; +import mage.MageObjectReference; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; @@ -121,7 +121,7 @@ class CavernOfSoulsManaCondition extends CreatureCastManaCondition { class CavernOfSoulsWatcher extends Watcher { - private List spells = new ArrayList<>(); + private final Set spells = new HashSet<>(); private final UUID originalId; public CavernOfSoulsWatcher(UUID originalId) { @@ -132,14 +132,14 @@ class CavernOfSoulsWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.MANA_PAID) { - if (event.getData() != null && event.getData().equals(originalId.toString())) { - spells.add(event.getTargetId()); + if (event.getData() != null && event.getData().equals(originalId.toString()) && event.getTargetId() != null) { + spells.add(new MageObjectReference(game.getObject(event.getTargetId()), game)); } } } - public boolean spellCantBeCountered(UUID spellId) { - return spells.contains(spellId); + public boolean spellCantBeCountered(MageObjectReference mor) { + return spells.contains(mor); } @Override @@ -188,6 +188,6 @@ class CavernOfSoulsCantCounterEffect extends ContinuousRuleModifyingEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { CavernOfSoulsWatcher watcher = game.getState().getWatcher(CavernOfSoulsWatcher.class, source.getSourceId()); Spell spell = game.getStack().getSpell(event.getTargetId()); - return spell != null && watcher != null && watcher.spellCantBeCountered(spell.getId()); + return spell != null && watcher != null && watcher.spellCantBeCountered(new MageObjectReference(spell, game)); } } diff --git a/Mage.Sets/src/mage/cards/c/CavernWhisperer.java b/Mage.Sets/src/mage/cards/c/CavernWhisperer.java new file mode 100644 index 0000000000..ee090377e3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CavernWhisperer.java @@ -0,0 +1,46 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CavernWhisperer extends CardImpl { + + public CavernWhisperer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Mutate {3}{B} + this.addAbility(new MutateAbility(this, "{3}{B}")); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever this creature mutates, each opponent discards a card. + this.addAbility(new MutatesSourceTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT))); + } + + private CavernWhisperer(final CavernWhisperer card) { + super(card); + } + + @Override + public CavernWhisperer copy() { + return new CavernWhisperer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CazurRuthlessStalker.java b/Mage.Sets/src/mage/cards/c/CazurRuthlessStalker.java new file mode 100644 index 0000000000..9e7631b39b --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CazurRuthlessStalker.java @@ -0,0 +1,51 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CazurRuthlessStalker extends CardImpl { + + public CazurRuthlessStalker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Partner with Ukkima, Stalking Shadow + this.addAbility(new PartnerWithAbility("Ukkima, Stalking Shadow")); + + // Whenever a creature you control deals combat damage to a player, put a +1/+1 counter on that creature. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, false, + SetTargetPointer.PERMANENT, true + )); + } + + private CazurRuthlessStalker(final CazurRuthlessStalker card) { + super(card); + } + + @Override + public CazurRuthlessStalker copy() { + return new CazurRuthlessStalker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CelestialEnforcer.java b/Mage.Sets/src/mage/cards/c/CelestialEnforcer.java new file mode 100644 index 0000000000..5bf8ffdea1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CelestialEnforcer.java @@ -0,0 +1,63 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CelestialEnforcer extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("if you control a creature with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + + public CelestialEnforcer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // {1}{W}, {T}: Tap target creature. Activate this ability only if you control a creature with flying. + Ability ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new TapTargetEffect(), new ManaCostsImpl("{1}{W}"), condition + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private CelestialEnforcer(final CelestialEnforcer card) { + super(card); + } + + @Override + public CelestialEnforcer copy() { + return new CelestialEnforcer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CelestialGatekeeper.java b/Mage.Sets/src/mage/cards/c/CelestialGatekeeper.java index 4acadb8fc5..4a66a77b38 100644 --- a/Mage.Sets/src/mage/cards/c/CelestialGatekeeper.java +++ b/Mage.Sets/src/mage/cards/c/CelestialGatekeeper.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; @@ -47,7 +47,7 @@ public final class CelestialGatekeeper extends CardImpl { // When Celestial Gatekeeper dies, exile it, then return up to two target Bird and/or Cleric permanent cards from your graveyard to the battlefield. Effect effect = new ExileSourceEffect(); effect.setText(""); - DiesTriggeredAbility ability = new DiesTriggeredAbility(effect); + DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(effect); effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); effect.setText("exile it, then return up to two target Bird and/or Cleric permanent cards from your graveyard to the battlefield"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/c/CemeteryPuca.java b/Mage.Sets/src/mage/cards/c/CemeteryPuca.java index 37498a7238..3b70f6560f 100644 --- a/Mage.Sets/src/mage/cards/c/CemeteryPuca.java +++ b/Mage.Sets/src/mage/cards/c/CemeteryPuca.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -16,7 +15,6 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -30,7 +28,7 @@ import mage.util.functions.EmptyApplyToPermanent; public final class CemeteryPuca extends CardImpl { public CemeteryPuca(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U/B}{U/B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U/B}{U/B}"); this.subtype.add(SubType.SHAPESHIFTER); this.power = new MageInt(1); @@ -71,7 +69,7 @@ class CemeteryPucaEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent copyToCreature = game.getPermanent(source.getSourceId()); if (copyToCreature != null) { - Permanent copyFromCreature = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); + Permanent copyFromCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (copyFromCreature != null) { game.copyPermanent(Duration.WhileOnBattlefield, copyFromCreature, copyToCreature.getId(), source, new EmptyApplyToPermanent()); ContinuousEffect effect = new GainAbilityTargetEffect(new DiesCreatureTriggeredAbility(new DoIfCostPaid(new CemeteryPucaEffect(), new ManaCostsImpl("{1}")), false, new FilterCreaturePermanent("a creature"), true), Duration.WhileOnBattlefield); diff --git a/Mage.Sets/src/mage/cards/c/CemeteryRecruitment.java b/Mage.Sets/src/mage/cards/c/CemeteryRecruitment.java index a85dd2c4ab..d40353072d 100644 --- a/Mage.Sets/src/mage/cards/c/CemeteryRecruitment.java +++ b/Mage.Sets/src/mage/cards/c/CemeteryRecruitment.java @@ -64,7 +64,7 @@ class CemeteryRecruitmentEffect extends OneShotEffect { if (card != null) { if (controller.moveCards(card, Zone.HAND, source, game) && card.hasSubtype(SubType.ZOMBIE, game)) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/c/CentaurSafeguard.java b/Mage.Sets/src/mage/cards/c/CentaurSafeguard.java index 1ebeea7209..475abe297b 100644 --- a/Mage.Sets/src/mage/cards/c/CentaurSafeguard.java +++ b/Mage.Sets/src/mage/cards/c/CentaurSafeguard.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class CentaurSafeguard extends CardImpl { this.toughness = new MageInt(1); // When Centaur Safeguard dies, you may gain 3 life. - this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(3), true)); + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(3), true)); } public CentaurSafeguard(final CentaurSafeguard card) { diff --git a/Mage.Sets/src/mage/cards/c/CephalidVandal.java b/Mage.Sets/src/mage/cards/c/CephalidVandal.java index 780863a404..627b3c11a9 100644 --- a/Mage.Sets/src/mage/cards/c/CephalidVandal.java +++ b/Mage.Sets/src/mage/cards/c/CephalidVandal.java @@ -54,7 +54,7 @@ class CephalidVandalEffect extends OneShotEffect { public CephalidVandalEffect() { super(Outcome.Neutral); - staticText = "Then put the top card of your library into your graveyard for each shred counter on {this}"; + staticText = "Then mill a card for each shred counter on {this}"; } public CephalidVandalEffect(final CephalidVandalEffect effect) { @@ -72,7 +72,7 @@ class CephalidVandalEffect extends OneShotEffect { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null && controller != null) { int amount = permanent.getCounters(game).getCount(CounterType.SHRED); - controller.moveCards(controller.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game); + controller.millCards(amount, source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/ChainToMemory.java b/Mage.Sets/src/mage/cards/c/ChainToMemory.java index 9a58196956..4e3637b748 100644 --- a/Mage.Sets/src/mage/cards/c/ChainToMemory.java +++ b/Mage.Sets/src/mage/cards/c/ChainToMemory.java @@ -19,7 +19,7 @@ public final class ChainToMemory extends CardImpl { // Target creature gets -4/-0 until end of turn. Scry 2. this.getSpellAbility().addEffect(new BoostTargetEffect(-4, 0)); - this.getSpellAbility().addEffect(new ScryEffect(2).setText("Scry 2")); + this.getSpellAbility().addEffect(new ScryEffect(2, false)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/c/ChainedBrute.java b/Mage.Sets/src/mage/cards/c/ChainedBrute.java new file mode 100644 index 0000000000..6d3134d511 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChainedBrute.java @@ -0,0 +1,56 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChainedBrute extends CardImpl { + + public ChainedBrute(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.DEVIL); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Chained Brute doesn't untap during your untap step. + this.addAbility(new SimpleStaticAbility(new DontUntapInControllersUntapStepSourceEffect())); + + // {1}, Sacrifice another creature: Untap Chained Brute. Activate this ability only during your turn. + Ability ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new UntapSourceEffect(), + new GenericManaCost(1), MyTurnCondition.instance + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent( + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE + ))); + this.addAbility(ability); + } + + private ChainedBrute(final ChainedBrute card) { + super(card); + } + + @Override + public ChainedBrute copy() { + return new ChainedBrute(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java b/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java index 86a5d53c4f..6bf1188998 100644 --- a/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java +++ b/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java @@ -18,7 +18,6 @@ import mage.constants.*; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.events.GameEvent; @@ -42,11 +41,14 @@ public final class ChainerNightmareAdept extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - // Discard a card: You may cast a creature card from your graveyard this turn. Activate this ability only once each turn. - Ability ability = new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new ChainerNightmareAdeptContinuousEffect(), new DiscardCardCost()); + // Discard a card: You may cast a creature card from your graveyard this turn. + // Activate this ability only once each turn. + Ability ability = new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, + new ChainerNightmareAdeptContinuousEffect(), new DiscardCardCost()); this.addAbility(ability, new ChainerNightmareAdeptWatcher()); - // Whenever a nontoken creature enters the battlefield under your control, if you didn't cast it from your hand, it gains haste until your next turn. + // Whenever a nontoken creature enters the battlefield under your control, + // if you didn't cast it from your hand, it gains haste until your next turn. this.addAbility(new ChainerNightmareAdeptTriggeredAbility(), new CastFromHandWatcher()); } @@ -80,7 +82,9 @@ class ChainerNightmareAdeptContinuousEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player == null || game.getActivePlayerId() == null || !game.isActivePlayer(player.getId())) { + if (player == null + || game.getActivePlayerId() == null + || !game.isActivePlayer(player.getId())) { return false; } @@ -124,8 +128,10 @@ class ChainerNightmareAdeptCastFromGraveyardEffect extends AsThoughEffectImpl { && affectedControllerId != null && card.getSpellAbility().spellCanBeActivatedRegularlyNow(affectedControllerId, game) && affectedControllerId.equals(source.getControllerId())) { - ChainerNightmareAdeptWatcher watcher = game.getState().getWatcher(ChainerNightmareAdeptWatcher.class, source.getSourceId()); - return watcher != null && !watcher.isAbilityUsed(); + ChainerNightmareAdeptWatcher watcher = game.getState().getWatcher( + ChainerNightmareAdeptWatcher.class, source.getSourceId()); + return watcher != null + && !watcher.isAbilityUsed(); } return false; } @@ -141,9 +147,11 @@ class ChainerNightmareAdeptWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.SPELL_CAST && event.getZone() == Zone.GRAVEYARD) { + if (event.getType() == GameEvent.EventType.SPELL_CAST + && event.getZone() == Zone.GRAVEYARD) { Spell spell = (Spell) game.getObject(event.getTargetId()); - if (spell.isCreature()) { + if (spell.isCreature() + && abilityUsed == false) { abilityUsed = true; } } @@ -162,19 +170,31 @@ class ChainerNightmareAdeptWatcher extends Watcher { class ChainerNightmareAdeptTriggeredAbility extends EntersBattlefieldAllTriggeredAbility { - private final static String abilityText = "Whenever a nontoken creature enters the battlefield under your control, " + private final static String abilityText = "Whenever a nontoken creature " + + "enters the battlefield under your control, " + "if you didn't cast it from your hand, it gains haste until your next turn."; - private final static ContinuousEffect gainHasteUntilNextTurnEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.UntilYourNextTurn); - private final static FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another nontoken creature"); + private final static ContinuousEffect gainHasteUntilNextTurnEffect + = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.UntilYourNextTurn); + private final static FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent("nontoken creature"); static { filter.add(Predicates.not(TokenPredicate.instance)); filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(AnotherPredicate.instance); } public ChainerNightmareAdeptTriggeredAbility() { - super(Zone.BATTLEFIELD, gainHasteUntilNextTurnEffect, filter, false, SetTargetPointer.PERMANENT, abilityText); + super(Zone.BATTLEFIELD, gainHasteUntilNextTurnEffect, filter, false, + SetTargetPointer.PERMANENT, abilityText); + } + + ChainerNightmareAdeptTriggeredAbility(final ChainerNightmareAdeptTriggeredAbility effect) { + super(effect); + } + + @Override + public ChainerNightmareAdeptTriggeredAbility copy() { + return new ChainerNightmareAdeptTriggeredAbility(this); } @Override diff --git a/Mage.Sets/src/mage/cards/c/ChainsOfMephistopheles.java b/Mage.Sets/src/mage/cards/c/ChainsOfMephistopheles.java index 97210ba99b..d1e26d7750 100644 --- a/Mage.Sets/src/mage/cards/c/ChainsOfMephistopheles.java +++ b/Mage.Sets/src/mage/cards/c/ChainsOfMephistopheles.java @@ -43,7 +43,7 @@ class ChainsOfMephistophelesReplacementEffect extends ReplacementEffectImpl { public ChainsOfMephistophelesReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If a player would draw a card except the first one they draw in their draw step each turn, that player discards a card instead. If the player discards a card this way, they draw a card. If the player doesn't discard a card this way, they put the top card of their library into their graveyard"; + staticText = "If a player would draw a card except the first one they draw in their draw step each turn, that player discards a card instead. If the player discards a card this way, they draw a card. If the player doesn't discard a card this way, they mill a card"; } public ChainsOfMephistophelesReplacementEffect(final ChainsOfMephistophelesReplacementEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ChakramRetriever.java b/Mage.Sets/src/mage/cards/c/ChakramRetriever.java index 5f606092e5..19fc2daf8e 100644 --- a/Mage.Sets/src/mage/cards/c/ChakramRetriever.java +++ b/Mage.Sets/src/mage/cards/c/ChakramRetriever.java @@ -25,7 +25,7 @@ public final class ChakramRetriever extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); this.subtype.add(SubType.ELEMENTAL); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/c/ChamberedNautilus.java b/Mage.Sets/src/mage/cards/c/ChamberedNautilus.java index e3b917be6c..a4c20b19e1 100644 --- a/Mage.Sets/src/mage/cards/c/ChamberedNautilus.java +++ b/Mage.Sets/src/mage/cards/c/ChamberedNautilus.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class ChamberedNautilus extends CardImpl { this.toughness = new MageInt(2); // Whenever Chambered Nautilus becomes blocked, you may draw a card. - this.addAbility(new BecomesBlockedTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); } public ChamberedNautilus(final ChamberedNautilus card) { diff --git a/Mage.Sets/src/mage/cards/c/ChampionOfArashin.java b/Mage.Sets/src/mage/cards/c/ChampionOfArashin.java index 52d66a318c..a9941dacae 100644 --- a/Mage.Sets/src/mage/cards/c/ChampionOfArashin.java +++ b/Mage.Sets/src/mage/cards/c/ChampionOfArashin.java @@ -17,7 +17,7 @@ public final class ChampionOfArashin extends CardImpl { public ChampionOfArashin(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.WARRIOR); this.power = new MageInt(3); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/c/ChanceForGlory.java b/Mage.Sets/src/mage/cards/c/ChanceForGlory.java index dff4e834e2..13af041920 100644 --- a/Mage.Sets/src/mage/cards/c/ChanceForGlory.java +++ b/Mage.Sets/src/mage/cards/c/ChanceForGlory.java @@ -23,7 +23,7 @@ public final class ChanceForGlory extends CardImpl { this.getSpellAbility().addEffect(new GainAbilityAllEffect( IndestructibleAbility.getInstance(), Duration.EndOfGame, StaticFilters.FILTER_CONTROLLED_CREATURES - ).setText("Creatures you control gain indestructible.")); + ).setText("Creatures you control gain indestructible")); this.getSpellAbility().addEffect(new AddExtraTurnControllerEffect(true)); } diff --git a/Mage.Sets/src/mage/cards/c/ChancellorOfTheSpires.java b/Mage.Sets/src/mage/cards/c/ChancellorOfTheSpires.java index 689fd0bf19..d575eb23c7 100644 --- a/Mage.Sets/src/mage/cards/c/ChancellorOfTheSpires.java +++ b/Mage.Sets/src/mage/cards/c/ChancellorOfTheSpires.java @@ -1,36 +1,33 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.ChancellorAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.PlayTargetWithoutPayingManaEffect; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.TargetController; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; -import mage.players.Player; import mage.target.common.TargetCardInOpponentsGraveyard; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class ChancellorOfTheSpires extends CardImpl { - private static final String abilityText = "at the beginning of the first upkeep, each opponent puts the top seven cards of their library into their graveyard"; + private static final String abilityText = "at the beginning of the first upkeep, each opponent mills seven cards"; private static final FilterCard filter = new FilterCard("instant or sorcery card from an opponent's graveyard"); @@ -41,7 +38,7 @@ public final class ChancellorOfTheSpires extends CardImpl { } public ChancellorOfTheSpires(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}{U}"); this.subtype.add(SubType.SPHINX); this.power = new MageInt(5); @@ -58,7 +55,7 @@ public final class ChancellorOfTheSpires extends CardImpl { this.addAbility(ability); } - public ChancellorOfTheSpires(final ChancellorOfTheSpires card) { + private ChancellorOfTheSpires(final ChancellorOfTheSpires card) { super(card); } @@ -70,11 +67,11 @@ public final class ChancellorOfTheSpires extends CardImpl { class ChancellorOfTheSpiresDelayedTriggeredAbility extends DelayedTriggeredAbility { - ChancellorOfTheSpiresDelayedTriggeredAbility () { - super(new ChancellorOfTheSpiresEffect()); + ChancellorOfTheSpiresDelayedTriggeredAbility() { + super(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(7, TargetController.OPPONENT)); } - ChancellorOfTheSpiresDelayedTriggeredAbility(ChancellorOfTheSpiresDelayedTriggeredAbility ability) { + private ChancellorOfTheSpiresDelayedTriggeredAbility(ChancellorOfTheSpiresDelayedTriggeredAbility ability) { super(ability); } @@ -82,43 +79,14 @@ class ChancellorOfTheSpiresDelayedTriggeredAbility extends DelayedTriggeredAbili public boolean checkEventType(GameEvent event, Game game) { return event.getType() == EventType.UPKEEP_STEP_PRE; } - + @Override public boolean checkTrigger(GameEvent event, Game game) { return true; } + @Override public ChancellorOfTheSpiresDelayedTriggeredAbility copy() { return new ChancellorOfTheSpiresDelayedTriggeredAbility(this); } } - -class ChancellorOfTheSpiresEffect extends OneShotEffect { - - ChancellorOfTheSpiresEffect () { - super(Outcome.Benefit); - staticText = "each opponent puts the top seven cards of their library into their graveyard"; - } - - ChancellorOfTheSpiresEffect(ChancellorOfTheSpiresEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID opponentId : game.getOpponents(source.getControllerId())) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null) { - opponent.moveCards(opponent.getLibrary().getTopCards(game, 7), Zone.GRAVEYARD, source, game); - } - } - return true; - } - - @Override - public ChancellorOfTheSpiresEffect copy() { - return new ChancellorOfTheSpiresEffect(this); - } - -} - diff --git a/Mage.Sets/src/mage/cards/c/ChandraAblaze.java b/Mage.Sets/src/mage/cards/c/ChandraAblaze.java index 803758c703..4128d0687f 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraAblaze.java +++ b/Mage.Sets/src/mage/cards/c/ChandraAblaze.java @@ -172,7 +172,6 @@ class ChandraAblazeEffect5 extends OneShotEffect { Card card = game.getCard(target.getFirstTarget()); if (card != null) { player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game)); - player.getGraveyard().remove(card); cards.remove(card); } } diff --git a/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java b/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java index 635252fbf3..6187069669 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java +++ b/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java @@ -19,7 +19,6 @@ import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPlaneswalkerPermanent; -import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; @@ -27,8 +26,8 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; +import mage.game.permanent.token.RedElementalToken; import mage.game.permanent.token.Token; -import mage.game.permanent.token.YoungPyromancerElementalToken; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; @@ -100,7 +99,7 @@ class ChandraAcolyteOfFlameEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Token token = new YoungPyromancerElementalToken(); + Token token = new RedElementalToken(); token.putOntoBattlefield(2, game, source.getSourceId(), source.getControllerId()); token.getLastAddedTokenIds().stream().forEach(permId -> { @@ -148,7 +147,7 @@ class ChandraAcolyteOfFlameGraveyardEffect extends OneShotEffect { Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); if (card != null) { ContinuousEffect effect = new ChandraAcolyteOfFlameCastFromGraveyardEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); effect = new ChandraAcolyteOfFlameReplacementEffect(card.getId()); game.addEffect(effect, source); diff --git a/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java b/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java index c0f61cb557..8a28de03eb 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java +++ b/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java @@ -2,7 +2,7 @@ package mage.cards.c; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayVariableLoyaltyCost; @@ -46,7 +46,7 @@ public final class ChandraAwakenedInferno extends CardImpl { this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); // This spell can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // +2: Each opponent gets an emblem with "At the beginning of your upkeep, this emblem deals 1 damage to you." this.addAbility(new LoyaltyAbility(new ChandraAwakenedInfernoEffect(), 2)); diff --git a/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java b/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java index be87c242af..a4e54c3fd3 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java +++ b/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java @@ -10,7 +10,6 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DamageAllEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -19,10 +18,9 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.token.ElementalToken; +import mage.game.permanent.token.ElementalTokenWithHaste; import mage.players.Player; -import java.util.Set; import java.util.UUID; /** @@ -77,7 +75,7 @@ class ChandraElementalEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - CreateTokenEffect effect = new CreateTokenEffect(new ElementalToken("OGW", 2, true), 2); + CreateTokenEffect effect = new CreateTokenEffect(new ElementalTokenWithHaste(), 2); effect.apply(game, source); effect.exileTokensCreatedAtNextEndStep(game, source); return true; @@ -106,16 +104,12 @@ class ChandraDrawEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Set cardsInHand = player.getHand().getCards(game); - int amount = cardsInHand.size(); - for (Card card : cardsInHand) { - player.discard(card, source, game); - } - player.drawCards(amount + 1, game); - return true; + if (player == null) { + return false; } - return false; + int amount = player.discard(player.getHand(), source, game).size(); + player.drawCards(amount + 1, source.getSourceId(), game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java b/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java new file mode 100644 index 0000000000..9c78e245a3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java @@ -0,0 +1,75 @@ +package mage.cards.c; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.CastCardFromGraveyardThenExileItEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.CastFromHandWithoutPayingManaCostEffect; +import mage.abilities.effects.common.discard.DiscardHandControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class ChandraFlamesCatalyst extends CardImpl { + + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + filter.add(Predicates.or(CardType.INSTANT.getPredicate(), CardType.SORCERY.getPredicate())); + } + + public ChandraFlamesCatalyst(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CHANDRA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // +1: Chandra, Flame's Catalyst deals 3 damage to each opponent. + this.addAbility(new LoyaltyAbility(new DamagePlayersEffect(3, TargetController.OPPONENT), 1)); + + // −2: You may cast target red instant or sorcery card from your graveyard. If that spell would be put into your graveyard this turn, exile it instead. + CastCardFromGraveyardThenExileItEffect minusEffect = new CastCardFromGraveyardThenExileItEffect(); + minusEffect.setText("You may cast target red instant or sorcery card from your graveyard. If that spell would be put into your graveyard this turn, exile it instead"); + Ability ability = new LoyaltyAbility(minusEffect, -2); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + // −8: Discard your hand, then draw seven cards. Until end of turn, you may cast spells from your hand without paying their mana costs. + Effect discardHandEffect = new DiscardHandControllerEffect(); + Effect drawEffect = new DrawCardSourceControllerEffect(7); + drawEffect.setText(", then draw seven cards"); + Effect castSpellsFromHandEffect = new CastFromHandWithoutPayingManaCostEffect( + StaticFilters.FILTER_CARD_NON_LAND, true, Duration.EndOfTurn); + castSpellsFromHandEffect.setText("Until end of turn, you may cast spells from your hand without paying their mana costs"); + Ability ultimateAbility = new LoyaltyAbility(discardHandEffect, -8); + ultimateAbility.addEffect(drawEffect); + ultimateAbility.addEffect(castSpellsFromHandEffect); + this.addAbility(ultimateAbility); + } + + private ChandraFlamesCatalyst(final ChandraFlamesCatalyst card) { + super(card); + } + + @Override + public ChandraFlamesCatalyst copy() { + return new ChandraFlamesCatalyst(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java new file mode 100644 index 0000000000..169b6fcd4a --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java @@ -0,0 +1,134 @@ +package mage.cards.c; + +import mage.Mana; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.ExileTop3MayPlayUntilEndOfTurnEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.abilities.effects.common.discard.DiscardHandControllerEffect; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTargets; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author htrajan + */ +public final class ChandraHeartOfFire extends CardImpl { + + public ChandraHeartOfFire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CHANDRA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // +1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way. + Ability ability = new LoyaltyAbility(new DiscardHandControllerEffect(), 1); + ability.addEffect(new ExileTop3MayPlayUntilEndOfTurnEffect().concatBy(", then")); + this.addAbility(ability); + + // +1: Chandra, Heart of Fire deals 2 damage to any target. + Ability damageAbility = new LoyaltyAbility(new DamageTargetEffect(2), 1); + damageAbility.addTarget(new TargetAnyTarget()); + this.addAbility(damageAbility); + + // −9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}. + Ability ultimateAbility = new LoyaltyAbility(new ChandraHeartOfFireUltimateEffect(), -9); + ultimateAbility.addEffect(new BasicManaEffect(Mana.RedMana(6)).setText("Add six {R}")); + this.addAbility(ultimateAbility); + } + + private ChandraHeartOfFire(final ChandraHeartOfFire card) { + super(card); + } + + @Override + public ChandraHeartOfFire copy() { + return new ChandraHeartOfFire(this); + } +} + +class ChandraHeartOfFireUltimateEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("red instant or sorcery"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + filter.add(Predicates.or(CardType.INSTANT.getPredicate(), CardType.SORCERY.getPredicate())); + } + + ChandraHeartOfFireUltimateEffect() { + super(Outcome.Benefit); + staticText = "Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn"; + } + + private ChandraHeartOfFireUltimateEffect(ChandraHeartOfFireUltimateEffect effect) { + super(effect); + } + + @Override + public ChandraHeartOfFireUltimateEffect copy() { + return new ChandraHeartOfFireUltimateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Set exiledCards = new HashSet<>(); + + // from graveyard + Target target = new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, filter, true).withChooseHint("from graveyard"); + if (target.canChoose(source.getSourceId(), controller.getId(), game) + && target.choose(Outcome.AIDontUseIt, controller.getId(), source.getSourceId(), game)) { + Set cards = new CardsImpl(target.getTargets()).getCards(game); + controller.moveCards(cards, Zone.EXILED, source, game); + exiledCards.addAll(cards); + } + + // from library + target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter).withChooseHint("from library"); + if (target.canChoose(source.getSourceId(), controller.getId(), game) + && target.choose(Outcome.AIDontUseIt, controller.getId(), source.getSourceId(), game)) { + Set cards = new CardsImpl(target.getTargets()).getCards(game); + controller.moveCards(cards, Zone.EXILED, source, game); + exiledCards.addAll(cards); + } + controller.shuffleLibrary(source, game); + + exiledCards.removeIf(card -> !Zone.EXILED.equals(game.getState().getZone(card.getId()))); + + if (!exiledCards.isEmpty()) { + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTargets(exiledCards, game)); + game.addEffect(effect, source); + } + + return true; + } + return false; + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/ChandrasDefeat.java b/Mage.Sets/src/mage/cards/c/ChandrasDefeat.java index a8cff5456c..2915dac014 100644 --- a/Mage.Sets/src/mage/cards/c/ChandrasDefeat.java +++ b/Mage.Sets/src/mage/cards/c/ChandrasDefeat.java @@ -84,7 +84,7 @@ class ChandrasDefeatEffect extends OneShotEffect { if (filter.match(permanent, game) && controller != null && controller.chooseUse(outcome, "Discard a card and draw a card?", source, game)) { controller.discard(1, false, source, game); - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java b/Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java new file mode 100644 index 0000000000..0a87253ad9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java @@ -0,0 +1,49 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChandrasFiremaw extends CardImpl { + + private static final FilterCard filter = new FilterCard("Chandra, Flame's Catalyst"); + + static { + filter.add(new NamePredicate("Chandra, Flame's Catalyst")); + } + + public ChandrasFiremaw(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + + this.subtype.add(SubType.HELLION); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // When Chandra's Firemaw enters the battlefield, you may search your library and/or graveyard for a card named Chandra, Flame's Catalyst, reveal it, and put it into your hand. If you search your library this way, shuffle it. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter),true)); + } + + private ChandrasFiremaw(final ChandrasFiremaw card) { + super(card); + } + + @Override + public ChandrasFiremaw copy() { + return new ChandrasFiremaw(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java new file mode 100644 index 0000000000..5fd71a51ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java @@ -0,0 +1,169 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.target.TargetPermanent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChandrasIncinerator extends CardImpl { + + public ChandrasIncinerator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new ChandrasIncineratorCostReductionEffect() + ), new ChandrasIncineratorWatcher()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls. + this.addAbility(new ChandrasIncineratorTriggeredAbility()); + } + + private ChandrasIncinerator(final ChandrasIncinerator card) { + super(card); + } + + @Override + public ChandrasIncinerator copy() { + return new ChandrasIncinerator(this); + } +} + +class ChandrasIncineratorCostReductionEffect extends CostModificationEffectImpl { + + ChandrasIncineratorCostReductionEffect() { + super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn"; + } + + private ChandrasIncineratorCostReductionEffect(final ChandrasIncineratorCostReductionEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + ChandrasIncineratorWatcher watcher = game.getState().getWatcher(ChandrasIncineratorWatcher.class); + if (watcher == null) { + return true; + } + int reductionAmount = watcher.getDamage(source.getControllerId()); + CardUtil.reduceCost(abilityToModify, Math.max(0, reductionAmount)); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility + && abilityToModify.getSourceId().equals(source.getSourceId()) + && game.getCard(abilityToModify.getSourceId()) != null; + } + + @Override + public ChandrasIncineratorCostReductionEffect copy() { + return new ChandrasIncineratorCostReductionEffect(this); + } +} + +class ChandrasIncineratorWatcher extends Watcher { + + private final Map damageMap = new HashMap<>(); + + ChandrasIncineratorWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER + || ((DamagedPlayerEvent) event).isCombatDamage()) { + return; + } + for (UUID playerId : game.getOpponents(event.getTargetId())) { + damageMap.compute(playerId, ((u, i) -> i == null ? event.getAmount() : Integer.sum(i, event.getAmount()))); + } + } + + @Override + public void reset() { + damageMap.clear(); + super.reset(); + } + + int getDamage(UUID playerId) { + return damageMap.getOrDefault(playerId, 0); + } +} + +class ChandrasIncineratorTriggeredAbility extends TriggeredAbilityImpl { + + ChandrasIncineratorTriggeredAbility() { + super(Zone.BATTLEFIELD, null); + } + + private ChandrasIncineratorTriggeredAbility(final ChandrasIncineratorTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event; + if (dEvent.isCombatDamage() + || !game.getOpponents(event.getTargetId()).contains(getControllerId()) + || !game.getControllerId(event.getSourceId()).equals(getControllerId())) { + return false; + } + this.getEffects().clear(); + this.addEffect(new DamageTargetEffect(event.getAmount())); + FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker"); + filter.add(new ControllerIdPredicate(event.getTargetId())); + this.getTargets().clear(); + this.addTarget(new TargetPermanent(filter)); + return true; + } + + @Override + public ChandrasIncineratorTriggeredAbility copy() { + return new ChandrasIncineratorTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever a source you control deals noncombat damage to an opponent, " + + "{this} deals that much damage to target creature or planeswalker that player controls."; + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChandrasMagmutt.java b/Mage.Sets/src/mage/cards/c/ChandrasMagmutt.java new file mode 100644 index 0000000000..d4d0d86132 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChandrasMagmutt.java @@ -0,0 +1,43 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetPlayerOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChandrasMagmutt extends CardImpl { + + public ChandrasMagmutt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.DOG); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.< + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(1), new TapSourceCost()); + ability.addTarget(new TargetPlayerOrPlaneswalker()); + this.addAbility(ability); + } + + private ChandrasMagmutt(final ChandrasMagmutt card) { + super(card); + } + + @Override + public ChandrasMagmutt copy() { + return new ChandrasMagmutt(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChandrasPhoenix.java b/Mage.Sets/src/mage/cards/c/ChandrasPhoenix.java index af3ba402a7..5efc304390 100644 --- a/Mage.Sets/src/mage/cards/c/ChandrasPhoenix.java +++ b/Mage.Sets/src/mage/cards/c/ChandrasPhoenix.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -8,6 +7,7 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -26,7 +26,7 @@ import mage.game.stack.StackObject; public final class ChandrasPhoenix extends CardImpl { public ChandrasPhoenix(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); this.subtype.add(SubType.PHOENIX); this.power = new MageInt(2); @@ -34,9 +34,13 @@ public final class ChandrasPhoenix extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Haste (This creature can attack and as soon as it comes under your control.) this.addAbility(HasteAbility.getInstance()); - // Whenever an opponent is dealt damage by a red instant or sorcery spell you control or by a red planeswalker you control, return Chandra's Phoenix from your graveyard to your hand. + + // Whenever an opponent is dealt damage by a red instant or sorcery spell + // you control or by a red planeswalker you control, return Chandra's + // Phoenix from your graveyard to your hand. this.addAbility(new ChandrasPhoenixTriggeredAbility()); } @@ -72,7 +76,9 @@ class ChandrasPhoenixTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { + Card chandrasPhoenix = game.getCard(this.getSourceId()); + if (chandrasPhoenix != null + && game.getOpponents(chandrasPhoenix.getOwnerId()).contains(event.getPlayerId())) { StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); if (stackObject != null) { MageObject sourceObjectDamage; @@ -96,6 +102,7 @@ class ChandrasPhoenixTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever an opponent is dealt damage by a red instant or sorcery spell you control or by a red planeswalker you control, return {this} from your graveyard to your hand."; + return "Whenever an opponent is dealt damage by a red instant or sorcery spell " + + "you control or by a red planeswalker you control, return {this} from your graveyard to your hand."; } } diff --git a/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java b/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java new file mode 100644 index 0000000000..bca970e2a6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java @@ -0,0 +1,82 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author htrajan + */ +public final class ChandrasPyreling extends CardImpl { + + public ChandrasPyreling(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.LIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn. + this.addAbility(new ChandrasPyrelingAbility()); + } + + private ChandrasPyreling(final ChandrasPyreling card) { + super(card); + } + + @Override + public ChandrasPyreling copy() { + return new ChandrasPyreling(this); + } +} + +class ChandrasPyrelingAbility extends TriggeredAbilityImpl { + + ChandrasPyrelingAbility() { + super(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn)); + addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn)); + } + + private ChandrasPyrelingAbility(final ChandrasPyrelingAbility ability) { + super(ability); + } + + @Override + public ChandrasPyrelingAbility copy() { + return new ChandrasPyrelingAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; + return !damageEvent.isCombatDamage() + && game.getOpponents(controllerId).contains(event.getTargetId()) + && Objects.equals(controllerId, game.getControllerId(event.getSourceId())); + } + + @Override + public String getRule() { + return "Whenever a source you control deals noncombat damage to an opponent, {source} gets +1/+0 and gains double strike until end of turn."; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java b/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java index 29a5a4b730..53010c2af0 100644 --- a/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java +++ b/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java @@ -2,14 +2,12 @@ package mage.cards.c; import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.LoyaltyAbility; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; @@ -19,7 +17,6 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; import mage.players.Player; @@ -47,7 +44,7 @@ public final class ChandrasRegulator extends CardImpl { this.addSuperType(SuperType.LEGENDARY); // Whenever you activate a loyalty ability of a Chandra planeswalker, you may pay {1}. If you do, copy that ability. You may choose new targets for the copy. - this.addAbility(new ChandrasRegulatorTriggeredAbility()); + this.addAbility(new ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility(new ChandrasRegulatorEffect(), SubType.CHANDRA)); // {1}, {T}, Discard a Mountain card or a red card: Draw a card. Ability ability = new SimpleActivatedAbility( @@ -68,56 +65,11 @@ public final class ChandrasRegulator extends CardImpl { } } -class ChandrasRegulatorTriggeredAbility extends TriggeredAbilityImpl { - - ChandrasRegulatorTriggeredAbility() { - super(Zone.BATTLEFIELD, new ChandrasRegulatorEffect(), false); - } - - private ChandrasRegulatorTriggeredAbility(final ChandrasRegulatorTriggeredAbility ability) { - super(ability); - } - - @Override - public ChandrasRegulatorTriggeredAbility copy() { - return new ChandrasRegulatorTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getPlayerId().equals(getControllerId())) { - return false; - } - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); - if (stackAbility == null || !(stackAbility.getStackAbility() instanceof LoyaltyAbility)) { - return false; - } - Permanent permanent = stackAbility.getSourcePermanentOrLKI(game); - if (permanent == null || !permanent.isPlaneswalker() - || !permanent.hasSubtype(SubType.CHANDRA, game)) { - return false; - } - Effect effect = this.getEffects().get(0); - effect.setValue("stackAbility", stackAbility); - return true; - } - - @Override - public String getRule() { - return "Whenever you activate a loyalty ability of a Chandra planeswalker, you may pay {1}. " + - "If you do, copy that ability. You may choose new targets for the copy."; - } -} - class ChandrasRegulatorEffect extends OneShotEffect { ChandrasRegulatorEffect() { super(Outcome.Benefit); + staticText = "you may pay {1}. If you do, copy that ability. You may choose new targets for the copy"; } private ChandrasRegulatorEffect(final ChandrasRegulatorEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ChanneledForce.java b/Mage.Sets/src/mage/cards/c/ChanneledForce.java new file mode 100644 index 0000000000..0ffaf39118 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChanneledForce.java @@ -0,0 +1,78 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.costs.common.DiscardXTargetCost; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChanneledForce extends CardImpl { + + public ChanneledForce(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{R}"); + + // As an additional cost to cast this spell, discard X cards. + this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, false)); + + // Target player draws X cards. Channeled Force deals X damage to up to one target creature or planeswalker. + this.getSpellAbility().addEffect(new ChanneledForceEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private ChanneledForce(final ChanneledForce card) { + super(card); + } + + @Override + public ChanneledForce copy() { + return new ChanneledForce(this); + } +} + +class ChanneledForceEffect extends OneShotEffect { + + ChanneledForceEffect() { + super(Outcome.Benefit); + staticText = "Target player draws X cards. {this} deals X damage to up to one target creature or planeswalker."; + } + + private ChanneledForceEffect(final ChanneledForceEffect effect) { + super(effect); + } + + @Override + public ChanneledForceEffect copy() { + return new ChanneledForceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int xValue = GetXValue.instance.calculate(game, source, this); + if (xValue == 0) { + return false; + } + Player player = game.getPlayer(source.getTargets().get(0).getFirstTarget()); + if (player != null) { + player.drawCards(xValue, source.getSourceId(), game); + } + game.damagePlayerOrPlaneswalker( + source.getTargets().get(1).getFirstTarget(), xValue, + source.getSourceId(), game, false, true + ); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChaosLord.java b/Mage.Sets/src/mage/cards/c/ChaosLord.java index b7f2cca6ba..71736f0bd4 100644 --- a/Mage.Sets/src/mage/cards/c/ChaosLord.java +++ b/Mage.Sets/src/mage/cards/c/ChaosLord.java @@ -1,163 +1,163 @@ -package mage.cards.c; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; -import mage.abilities.condition.common.ControlsPermanentsComparedToOpponentsCondition; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.SubType; -import mage.abilities.keyword.FirstStrikeAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetOpponent; - -/** - * - * @author jeffwadsworth - */ -public final class ChaosLord extends CardImpl { - - public ChaosLord(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}{R}"); - - this.subtype.add(SubType.HUMAN); - this.power = new MageInt(7); - this.toughness = new MageInt(7); - - // First strike - this.addAbility(FirstStrikeAbility.getInstance()); - - // At the beginning of your upkeep, target opponent gains control of Chaos Lord if the number of permanents is even. - Ability ability = new ChaosLordTriggeredAbility(); - ability.addTarget(new TargetOpponent()); - this.addAbility(ability); - - // Chaos Lord can attack as though it had haste unless it entered the battlefield this turn. - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - new ChaosLordEffect())); - - } - - private ChaosLord(final ChaosLord card) { - super(card); - } - - @Override - public ChaosLord copy() { - return new ChaosLord(this); - } -} - -class ChaosLordTriggeredAbility extends BeginningOfUpkeepTriggeredAbility { - - public ChaosLordTriggeredAbility() { - super(Zone.BATTLEFIELD, - new GainControlSourceEffect(), - TargetController.YOU, - false); - } - - public ChaosLordTriggeredAbility(ChaosLordTriggeredAbility ability) { - super(ability); - } - - @Override - public BeginningOfUpkeepTriggeredAbility copy() { - return new ChaosLordTriggeredAbility(this); - } - - @Override - public boolean checkInterveningIfClause(Game game) { - Condition condition = new ControlsPermanentsComparedToOpponentsCondition( - ComparisonType.EQUAL_TO, - new FilterPermanent()); - Player controller = game.getPlayer(controllerId); - if (controller != null - && condition.apply(game, this)) { - return super.checkInterveningIfClause(game); - } - return false; - } - - @Override - public String getRule() { - return "At the beginning of your upkeep, target opponent gains control of {this} if the number of permanents is even."; - } - -} - -class GainControlSourceEffect extends ContinuousEffectImpl { - - public GainControlSourceEffect() { - super(Duration.Custom, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); - staticText = "target opponent gains control of {this}"; - } - - public GainControlSourceEffect(final GainControlSourceEffect effect) { - super(effect); - } - - @Override - public GainControlSourceEffect copy() { - return new GainControlSourceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); - if (permanent != null) { - return permanent.changeControllerId(source.getFirstTarget(), game); - } else { - discard(); - } - return false; - } -} - -class ChaosLordEffect extends AsThoughEffectImpl { - - public ChaosLordEffect() { - super(AsThoughEffectType.ATTACK_AS_HASTE, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Chaos Lord can attack as though it had haste unless it entered the battlefield this turn"; - } - - public ChaosLordEffect(final ChaosLordEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public ChaosLordEffect copy() { - return new ChaosLordEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - Permanent chaosLord = game.getPermanent(objectId); - return chaosLord != null - && objectId == source.getSourceId() - && chaosLord.getTurnsOnBattlefield() > 0; - } -} +package mage.cards.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.ControlsPermanentsComparedToOpponentsCondition; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.SubType; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AsThoughEffectType; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +/** + * + * @author jeffwadsworth + */ +public final class ChaosLord extends CardImpl { + + public ChaosLord(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}{R}"); + + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(7); + this.toughness = new MageInt(7); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // At the beginning of your upkeep, target opponent gains control of Chaos Lord if the number of permanents is even. + Ability ability = new ChaosLordTriggeredAbility(); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + + // Chaos Lord can attack as though it had haste unless it entered the battlefield this turn. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new ChaosLordEffect())); + + } + + private ChaosLord(final ChaosLord card) { + super(card); + } + + @Override + public ChaosLord copy() { + return new ChaosLord(this); + } +} + +class ChaosLordTriggeredAbility extends BeginningOfUpkeepTriggeredAbility { + + public ChaosLordTriggeredAbility() { + super(Zone.BATTLEFIELD, + new GainControlSourceEffect(), + TargetController.YOU, + false); + } + + public ChaosLordTriggeredAbility(ChaosLordTriggeredAbility ability) { + super(ability); + } + + @Override + public BeginningOfUpkeepTriggeredAbility copy() { + return new ChaosLordTriggeredAbility(this); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + Condition condition = new ControlsPermanentsComparedToOpponentsCondition( + ComparisonType.EQUAL_TO, + new FilterPermanent()); + Player controller = game.getPlayer(controllerId); + if (controller != null + && condition.apply(game, this)) { + return super.checkInterveningIfClause(game); + } + return false; + } + + @Override + public String getRule() { + return "At the beginning of your upkeep, target opponent gains control of {this} if the number of permanents is even."; + } + +} + +class GainControlSourceEffect extends ContinuousEffectImpl { + + public GainControlSourceEffect() { + super(Duration.Custom, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); + staticText = "target opponent gains control of {this}"; + } + + public GainControlSourceEffect(final GainControlSourceEffect effect) { + super(effect); + } + + @Override + public GainControlSourceEffect copy() { + return new GainControlSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); + if (permanent != null) { + return permanent.changeControllerId(source.getFirstTarget(), game); + } else { + discard(); + } + return false; + } +} + +class ChaosLordEffect extends AsThoughEffectImpl { + + public ChaosLordEffect() { + super(AsThoughEffectType.ATTACK_AS_HASTE, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Chaos Lord can attack as though it had haste unless it entered the battlefield this turn"; + } + + public ChaosLordEffect(final ChaosLordEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public ChaosLordEffect copy() { + return new ChaosLordEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + Permanent chaosLord = game.getPermanent(objectId); + return chaosLord != null + && objectId == source.getSourceId() + && chaosLord.getTurnsOnBattlefield() > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/c/Chaosphere.java b/Mage.Sets/src/mage/cards/c/Chaosphere.java index 12511380fc..73ed95605d 100644 --- a/Mage.Sets/src/mage/cards/c/Chaosphere.java +++ b/Mage.Sets/src/mage/cards/c/Chaosphere.java @@ -82,7 +82,7 @@ class ChaosphereEffect extends RestrictionEffect { if (attacker == null) { return true; } - return attacker.hasAbility(FlyingAbility.getInstance().getId(), game); + return attacker.hasAbility(FlyingAbility.getInstance(), game); } @Override diff --git a/Mage.Sets/src/mage/cards/c/ChargeOfTheForeverBeast.java b/Mage.Sets/src/mage/cards/c/ChargeOfTheForeverBeast.java new file mode 100644 index 0000000000..6e878e63de --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChargeOfTheForeverBeast.java @@ -0,0 +1,76 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChargeOfTheForeverBeast extends CardImpl { + + public ChargeOfTheForeverBeast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); + + // As an additional cost to cast this spell, reveal a creature card from your hand. + this.getSpellAbility().addCost(new RevealTargetFromHandCost( + new TargetCardInHand(StaticFilters.FILTER_CARD_CREATURE_YOUR_HAND) + )); + + // Charge of the Forever-Beast deals damage to target creature or planeswalker equal to the revealed card's power. + this.getSpellAbility().addEffect(new ChargeOfTheForeverBeastEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private ChargeOfTheForeverBeast(final ChargeOfTheForeverBeast card) { + super(card); + } + + @Override + public ChargeOfTheForeverBeast copy() { + return new ChargeOfTheForeverBeast(this); + } +} + +class ChargeOfTheForeverBeastEffect extends OneShotEffect { + + ChargeOfTheForeverBeastEffect() { + super(Outcome.Benefit); + staticText = "{this} deals damage to target creature or planeswalker equal to the revealed card's power"; + } + + private ChargeOfTheForeverBeastEffect(final ChargeOfTheForeverBeastEffect effect) { + super(effect); + } + + @Override + public ChargeOfTheForeverBeastEffect copy() { + return new ChargeOfTheForeverBeastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + RevealTargetFromHandCost cost = (RevealTargetFromHandCost) source.getCosts().get(0); + if (permanent == null || cost == null) { + return false; + } + Card card = cost.getRevealedCards().get(0); + if (card == null) { + return false; + } + return permanent.damage(card.getPower().getValue(), source.getSourceId(), game) > 0; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/Charisma.java b/Mage.Sets/src/mage/cards/c/Charisma.java index c2fe9091bc..235ab9e864 100644 --- a/Mage.Sets/src/mage/cards/c/Charisma.java +++ b/Mage.Sets/src/mage/cards/c/Charisma.java @@ -6,6 +6,7 @@ import mage.abilities.Ability; import mage.abilities.common.DealsDamageToACreatureAttachedTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceOnBattlefieldCondition; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; @@ -16,6 +17,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.Zone; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -40,8 +42,8 @@ public final class Charisma extends CardImpl { this.addAbility(ability); // Whenever enchanted creature deals damage to a creature, gain control of the other creature for as long as Charisma remains on the battlefield. - Condition condition = SourceOnBattlefieldCondition.instance; - ConditionalContinuousEffect conditionalEffect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), condition, rule); + ConditionalContinuousEffect conditionalEffect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), rule); this.addAbility(new DealsDamageToACreatureAttachedTriggeredAbility(conditionalEffect, false, "enchanted creature", false, true)); } diff --git a/Mage.Sets/src/mage/cards/c/CharmedGriffin.java b/Mage.Sets/src/mage/cards/c/CharmedGriffin.java index b2ce8b046a..92b7787a36 100644 --- a/Mage.Sets/src/mage/cards/c/CharmedGriffin.java +++ b/Mage.Sets/src/mage/cards/c/CharmedGriffin.java @@ -1,5 +1,7 @@ package mage.cards.c; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -68,6 +70,7 @@ class CharmedGriffinEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { + Set toBattlefield = new HashSet<>(); for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { if (!playerId.equals(controller.getId())) { Player player = game.getPlayer(playerId); @@ -78,13 +81,13 @@ class CharmedGriffinEffect extends OneShotEffect { && player.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { - player.moveCards(card, Zone.BATTLEFIELD, source, game); + toBattlefield.add(card); } } } } } - return true; + return controller.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game, false, false, true, null); // 101.4 } return false; } diff --git a/Mage.Sets/src/mage/cards/c/ChartACourse.java b/Mage.Sets/src/mage/cards/c/ChartACourse.java index 6b7b8a2520..a111a9d465 100644 --- a/Mage.Sets/src/mage/cards/c/ChartACourse.java +++ b/Mage.Sets/src/mage/cards/c/ChartACourse.java @@ -1,11 +1,10 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.common.RaidCondition; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -14,8 +13,9 @@ import mage.game.Game; import mage.players.Player; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ChartACourse extends CardImpl { @@ -27,9 +27,11 @@ public final class ChartACourse extends CardImpl { this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); this.getSpellAbility().addEffect(new ChartACourseEffect()); this.getSpellAbility().addWatcher(new PlayerAttackedWatcher()); + // this.getSpellAbility().setAbilityWord(AbilityWord.RAID); // no raid ability, only same conditional + this.getSpellAbility().addHint(RaidHint.instance); } - public ChartACourse(final ChartACourse card) { + private ChartACourse(final ChartACourse card) { super(card); } @@ -43,10 +45,10 @@ class ChartACourseEffect extends OneShotEffect { ChartACourseEffect() { super(Outcome.Neutral); - this.staticText = "Then discard a card unless you attacked with a creature this turn."; + this.staticText = "Then discard a card unless you attacked this turn."; } - ChartACourseEffect(final ChartACourseEffect effect) { + private ChartACourseEffect(final ChartACourseEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/c/ChasmSkulker.java b/Mage.Sets/src/mage/cards/c/ChasmSkulker.java index 880546b17c..8a86c45285 100644 --- a/Mage.Sets/src/mage/cards/c/ChasmSkulker.java +++ b/Mage.Sets/src/mage/cards/c/ChasmSkulker.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.DrawCardControllerTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -39,7 +39,7 @@ public final class ChasmSkulker extends CardImpl { this.addAbility(new DrawCardControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false)); // When Chasm Skulker dies, create X 1/1 blue Squid creature tokens with islandwalk, where X is the number of +1/+1 counters on Chasm Skulker. - this.addAbility(new DiesTriggeredAbility(new ChasmSkulkerEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new ChasmSkulkerEffect(), false)); } public ChasmSkulker(final ChasmSkulker card) { diff --git a/Mage.Sets/src/mage/cards/c/Chastise.java b/Mage.Sets/src/mage/cards/c/Chastise.java index 8a4d08e222..5bf683719e 100644 --- a/Mage.Sets/src/mage/cards/c/Chastise.java +++ b/Mage.Sets/src/mage/cards/c/Chastise.java @@ -58,7 +58,7 @@ class ChastiseEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { int power = permanent.getPower().getValue(); Player player = game.getPlayer(source.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/c/CheckpointOfficer.java b/Mage.Sets/src/mage/cards/c/CheckpointOfficer.java new file mode 100644 index 0000000000..c80db69705 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CheckpointOfficer.java @@ -0,0 +1,45 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CheckpointOfficer extends CardImpl { + + public CheckpointOfficer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {1}{W}, {T}: Tap target creature. + Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new ManaCostsImpl("{1}{W}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private CheckpointOfficer(final CheckpointOfficer card) { + super(card); + } + + @Override + public CheckpointOfficer copy() { + return new CheckpointOfficer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChemistersTrick.java b/Mage.Sets/src/mage/cards/c/ChemistersTrick.java index 06b57c3cdb..fcbaac6c6c 100644 --- a/Mage.Sets/src/mage/cards/c/ChemistersTrick.java +++ b/Mage.Sets/src/mage/cards/c/ChemistersTrick.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; @@ -14,42 +12,35 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ChemistersTrick extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public ChemistersTrick(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}{R}"); // Target creature you don't control gets -2/-0 until end of turn and attacks this turn if able. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); - this.getSpellAbility().addEffect(new BoostTargetEffect(-2,0, Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.getSpellAbility().addEffect(new BoostTargetEffect(-2, 0, Duration.EndOfTurn)); this.getSpellAbility().addEffect(new AttacksIfAbleTargetEffect(Duration.EndOfTurn)); // Overload {3}{U}{R} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.") - OverloadAbility ability = new OverloadAbility(this, new BoostAllEffect(-2,0, Duration.EndOfTurn,filter,false), new ManaCostsImpl("{3}{U}{R}")); - ability.addEffect(new ChemistersTrickEffect(filter)); + OverloadAbility ability = new OverloadAbility(this, new BoostAllEffect(-2, 0, Duration.EndOfTurn, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false), new ManaCostsImpl("{3}{U}{R}")); + ability.addEffect(new ChemistersTrickEffect()); this.addAbility(ability); } - public ChemistersTrick(final ChemistersTrick card) { + private ChemistersTrick(final ChemistersTrick card) { super(card); } @@ -61,22 +52,19 @@ public final class ChemistersTrick extends CardImpl { class ChemistersTrickEffect extends OneShotEffect { - private FilterCreaturePermanent filter; - public ChemistersTrickEffect(FilterCreaturePermanent filter) { + ChemistersTrickEffect() { super(Outcome.ReturnToHand); staticText = "each creature you don't control attacks this turn if able"; - this.filter = filter; } - public ChemistersTrickEffect(final ChemistersTrickEffect effect) { + private ChemistersTrickEffect(final ChemistersTrickEffect effect) { super(effect); - this.filter = effect.filter; } @Override public boolean apply(Game game, Ability source) { - for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, source.getControllerId(), source.getSourceId(), game)) { AttacksIfAbleTargetEffect effect = new AttacksIfAbleTargetEffect(Duration.EndOfTurn); effect.setTargetPointer(new FixedTarget(creature.getId())); game.addEffect(effect, source); @@ -88,5 +76,4 @@ class ChemistersTrickEffect extends OneShotEffect { public ChemistersTrickEffect copy() { return new ChemistersTrickEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/c/CherishedHatchling.java b/Mage.Sets/src/mage/cards/c/CherishedHatchling.java index 81c972d53f..f8c3c4835e 100644 --- a/Mage.Sets/src/mage/cards/c/CherishedHatchling.java +++ b/Mage.Sets/src/mage/cards/c/CherishedHatchling.java @@ -3,7 +3,7 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -45,7 +45,7 @@ public final class CherishedHatchling extends CardImpl { this.toughness = new MageInt(1); // When Cherished Hatchling dies, you may cast Dinosaur spells this turn as though they had flash, and whenever you cast a Dinosaur spell this turn, it gains "When this creature enters the battlefield, you may have it fight another target creature." - Ability ability = new DiesTriggeredAbility(new CastAsThoughItHadFlashAllEffect(Duration.EndOfTurn, filterCard, false)); + Ability ability = new DiesSourceTriggeredAbility(new CastAsThoughItHadFlashAllEffect(Duration.EndOfTurn, filterCard, false)); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new CherishedHatchlingTriggeredAbility())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ChevillBaneOfMonsters.java b/Mage.Sets/src/mage/cards/c/ChevillBaneOfMonsters.java new file mode 100644 index 0000000000..9e7b820336 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChevillBaneOfMonsters.java @@ -0,0 +1,90 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.permanent.CounterPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChevillBaneOfMonsters extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent(); + private static final FilterPermanent filter2 + = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker an opponent controls"); + private static final FilterPermanent filter3 + = new FilterPermanent("a permanent an opponent controls with a bounty counter on it"); + private static final Predicate predicate = new CounterPredicate(CounterType.BOUNTY); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(predicate); + + filter2.add(TargetController.OPPONENT.getControllerPredicate()); + + filter3.add(TargetController.OPPONENT.getControllerPredicate()); + filter3.add(predicate); + } + + private static final Condition condition + = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0, false); + + public ChevillBaneOfMonsters(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // At the beginning of your upkeep, if your opponents control no permanents with bounty counters on them, put a bounty counter on target creature or planeswalker an opponent controls. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new BeginningOfUpkeepTriggeredAbility( + new AddCountersTargetEffect(CounterType.BOUNTY.createInstance()), + TargetController.YOU, false + ), condition, "At the beginning of your upkeep, " + + "if your opponents control no permanents with bounty counters on them, " + + "put a bounty counter on target creature or planeswalker an opponent controls." + ); + ability.addTarget(new TargetPermanent(filter2)); + this.addAbility(ability); + + // Whenever a permanent an opponent controls with a bounty counter on it dies, you gain 3 life and draw card. + ability = new DiesCreatureTriggeredAbility(new GainLifeEffect(3), false, filter3); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and")); + this.addAbility(ability); + } + + private ChevillBaneOfMonsters(final ChevillBaneOfMonsters card) { + super(card); + } + + @Override + public ChevillBaneOfMonsters copy() { + return new ChevillBaneOfMonsters(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChickenALaKing.java b/Mage.Sets/src/mage/cards/c/ChickenALaKing.java index c9d9a0e0d9..e7e258a048 100644 --- a/Mage.Sets/src/mage/cards/c/ChickenALaKing.java +++ b/Mage.Sets/src/mage/cards/c/ChickenALaKing.java @@ -39,6 +39,7 @@ public final class ChickenALaKing extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.NOBLE); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/c/ChiefEngineer.java b/Mage.Sets/src/mage/cards/c/ChiefEngineer.java index b3874a1050..0ab1bec46f 100644 --- a/Mage.Sets/src/mage/cards/c/ChiefEngineer.java +++ b/Mage.Sets/src/mage/cards/c/ChiefEngineer.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect; @@ -11,10 +9,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterArtifactSpell; +import mage.filter.common.FilterArtifactCard; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ChiefEngineer extends CardImpl { @@ -28,7 +27,7 @@ public final class ChiefEngineer extends CardImpl { this.toughness = new MageInt(3); // Artifact spells you cast have convoke. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledSpellsEffect(new ConvokeAbility(), new FilterArtifactSpell("Artifact spells you cast")))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledSpellsEffect(new ConvokeAbility(), new FilterArtifactCard("Artifact spells you cast")))); } diff --git a/Mage.Sets/src/mage/cards/c/ChildOfAlara.java b/Mage.Sets/src/mage/cards/c/ChildOfAlara.java index d980f243a1..f739ddb60f 100644 --- a/Mage.Sets/src/mage/cards/c/ChildOfAlara.java +++ b/Mage.Sets/src/mage/cards/c/ChildOfAlara.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; @@ -31,7 +31,7 @@ public final class ChildOfAlara extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Child of Alara dies, destroy all nonland permanents. They can't be regenerated. - this.addAbility(new DiesTriggeredAbility(new DestroyAllEffect(new FilterNonlandPermanent("nonland permanents"), true))); + this.addAbility(new DiesSourceTriggeredAbility(new DestroyAllEffect(new FilterNonlandPermanent("nonland permanents"), true))); } diff --git a/Mage.Sets/src/mage/cards/c/Chill.java b/Mage.Sets/src/mage/cards/c/Chill.java index 557a0a2a47..083999cc7c 100644 --- a/Mage.Sets/src/mage/cards/c/Chill.java +++ b/Mage.Sets/src/mage/cards/c/Chill.java @@ -1,33 +1,34 @@ - package mage.cards.c; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Quercitron */ public final class Chill extends CardImpl { private static final FilterCard filter = new FilterCard("Red spells"); + static { filter.add(new ColorPredicate(ObjectColor.RED)); } public Chill(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); // Red spells cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(filter, 2))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(2, filter, TargetController.ANY))); } public Chill(final Chill card) { diff --git a/Mage.Sets/src/mage/cards/c/ChillOfForeboding.java b/Mage.Sets/src/mage/cards/c/ChillOfForeboding.java index 5f7d4323eb..98f9bbc67d 100644 --- a/Mage.Sets/src/mage/cards/c/ChillOfForeboding.java +++ b/Mage.Sets/src/mage/cards/c/ChillOfForeboding.java @@ -1,36 +1,32 @@ - package mage.cards.c; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect; import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.TimingRule; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; + +import java.util.UUID; /** - * * @author North */ public final class ChillOfForeboding extends CardImpl { public ChillOfForeboding(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); // Each player puts the top five cards of their library into their graveyard. - this.getSpellAbility().addEffect(new ChillOfForebodingEffect()); + this.getSpellAbility().addEffect(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(5, TargetController.ANY)); + // Flashback {7}{U} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{7}{U}"), TimingRule.SORCERY)); } - public ChillOfForeboding(final ChillOfForeboding card) { + private ChillOfForeboding(final ChillOfForeboding card) { super(card); } @@ -39,32 +35,3 @@ public final class ChillOfForeboding extends CardImpl { return new ChillOfForeboding(this); } } - -class ChillOfForebodingEffect extends OneShotEffect { - - public ChillOfForebodingEffect() { - super(Outcome.Detriment); - this.staticText = "Each player puts the top five cards of their library into their graveyard"; - } - - public ChillOfForebodingEffect(final ChillOfForebodingEffect effect) { - super(effect); - } - - @Override - public ChillOfForebodingEffect copy() { - return new ChillOfForebodingEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player sourcePlayer = game.getPlayer(source.getControllerId()); - for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.moveCards(player.getLibrary().getTopCards(game, 5), Zone.GRAVEYARD, source, game); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/c/ChimneyImp.java b/Mage.Sets/src/mage/cards/c/ChimneyImp.java index 6e9489b290..455b303dda 100644 --- a/Mage.Sets/src/mage/cards/c/ChimneyImp.java +++ b/Mage.Sets/src/mage/cards/c/ChimneyImp.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; @@ -35,7 +35,7 @@ public final class ChimneyImp extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Chimney Imp dies, target opponent puts a card from their hand on top of their library. - Ability ability = new DiesTriggeredAbility(new ChimneyImpEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new ChimneyImpEffect(), false); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/ChitteringHarvester.java b/Mage.Sets/src/mage/cards/c/ChitteringHarvester.java new file mode 100644 index 0000000000..fecca169ff --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChitteringHarvester.java @@ -0,0 +1,44 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.SacrificeOpponentsEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChitteringHarvester extends CardImpl { + + public ChitteringHarvester(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}"); + + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(4); + this.toughness = new MageInt(6); + + // Mutate {4}{B} + this.addAbility(new MutateAbility(this, "{4}{B}")); + + // Whenever this creature mutates, each opponent sacrifices a creature. + this.addAbility(new MutatesSourceTriggeredAbility( + new SacrificeOpponentsEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + )); + } + + private ChitteringHarvester(final ChitteringHarvester card) { + super(card); + } + + @Override + public ChitteringHarvester copy() { + return new ChitteringHarvester(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChokingVines.java b/Mage.Sets/src/mage/cards/c/ChokingVines.java new file mode 100644 index 0000000000..462c2c4ecf --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChokingVines.java @@ -0,0 +1,58 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.effects.common.BecomeBlockedTargetEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +import static mage.filter.StaticFilters.FILTER_ATTACKING_CREATURES; + +/** + * @author arcox + */ +public final class ChokingVines extends CardImpl { + + public ChokingVines(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{G}"); + + // Cast only during the declare blockers step. + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, + PhaseStep.DECLARE_BLOCKERS, null, "Cast this spell only during the declare blockers step")); + + // X target attacking creatures become blocked. Choking Vines deals 1 damage to each of those creatures. + this.getSpellAbility().addEffect(new BecomeBlockedTargetEffect() + .setText("X target attacking creatures become blocked.")); + this.getSpellAbility().addEffect(new DamageTargetEffect(1) + .setText("{this} deals 1 damage to each of those creatures")); + this.getSpellAbility().setTargetAdjuster(ChokingVinesAdjuster.instance); + } + + public ChokingVines(final ChokingVines card) { + super(card); + } + + @Override + public ChokingVines copy() { + return new ChokingVines(this); + } +} + +enum ChokingVinesAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + int x = ability.getManaCostsToPay().getX(); + ability.addTarget(new TargetCreaturePermanent(x, x, FILTER_ATTACKING_CREATURES, false)); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChromaticOrrery.java b/Mage.Sets/src/mage/cards/c/ChromaticOrrery.java new file mode 100644 index 0000000000..d69cbdcdff --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChromaticOrrery.java @@ -0,0 +1,84 @@ +package mage.cards.c; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.AsThoughManaEffect; +import mage.abilities.effects.common.DrawCardForEachColorAmongControlledPermanentsEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.players.ManaPoolItem; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class ChromaticOrrery extends CardImpl { + + public ChromaticOrrery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{7}"); + + this.addSuperType(SuperType.LEGENDARY); + + // You may spend mana as though it were mana of any color. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ChromaticOrreryEffect())); + + // {T}: Add {C}{C}{C}{C}{C}. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(5), new TapSourceCost())); + + // {5}, {T}: Draw a card for each color among permanents you control. + Ability ability = new SimpleActivatedAbility(new DrawCardForEachColorAmongControlledPermanentsEffect(), new GenericManaCost(5)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private ChromaticOrrery(final ChromaticOrrery card) { + super(card); + } + + @Override + public ChromaticOrrery copy() { + return new ChromaticOrrery(this); + } +} + +class ChromaticOrreryEffect extends AsThoughEffectImpl implements AsThoughManaEffect { + + ChromaticOrreryEffect() { + super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "You may spend mana as though it were mana of any color"; + } + + private ChromaticOrreryEffect(ChromaticOrreryEffect effect) { + super(effect); + } + + @Override + public ChromaticOrreryEffect copy() { + return new ChromaticOrreryEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) { + return mana.getFirstAvailable(); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/ChromeReplicator.java b/Mage.Sets/src/mage/cards/c/ChromeReplicator.java new file mode 100644 index 0000000000..8075551225 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChromeReplicator.java @@ -0,0 +1,80 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.token.Construct4Token; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChromeReplicator extends CardImpl { + + public ChromeReplicator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // When Chrome Replicator enters the battlefield, if you control two or more nonland, nontoken permanents with the same name as one another, create a 4/4 colorless Construct artifact creature token. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new Construct4Token())), + ChromeReplicatorCondition.instance, "When {this} enters the battlefield, " + + "if you control two or more nonland, nontoken permanents with the same name as one another, " + + "create a 4/4 colorless Construct artifact creature token." + )); + } + + private ChromeReplicator(final ChromeReplicator card) { + super(card); + } + + @Override + public ChromeReplicator copy() { + return new ChromeReplicator(this); + } +} + +enum ChromeReplicatorCondition implements Condition { + instance; + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(Predicates.not(CardType.LAND.getPredicate())); + filter.add(Predicates.not(TokenPredicate.instance)); + } + + @Override + public boolean apply(Game game, Ability source) { + Map nameMap = new HashMap<>(); + return game + .getBattlefield() + .getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .map(MageObject::getName) + .filter(Objects::nonNull) + .filter(s -> !"".equals(s)) + .anyMatch(s -> nameMap.compute(s, (x, i) -> i == null ? 1 : Integer.sum(i, 1)) >= 2); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChromeSteed.java b/Mage.Sets/src/mage/cards/c/ChromeSteed.java index 87b468f3c2..6a7f3aa3b3 100644 --- a/Mage.Sets/src/mage/cards/c/ChromeSteed.java +++ b/Mage.Sets/src/mage/cards/c/ChromeSteed.java @@ -1,5 +1,3 @@ - - package mage.cards.c; import mage.MageInt; @@ -7,12 +5,10 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; import java.util.UUID; @@ -33,8 +29,9 @@ public final class ChromeSteed extends CardImpl { new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), MetalcraftCondition.instance, "Metalcraft — {this} gets " + "+2/+2 as long as you control three or more artifacts" - ) - )); + )) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance)); } public ChromeSteed(final ChromeSteed card) { diff --git a/Mage.Sets/src/mage/cards/c/ChromiumTheMutable.java b/Mage.Sets/src/mage/cards/c/ChromiumTheMutable.java index 4ec39507c5..826d556786 100644 --- a/Mage.Sets/src/mage/cards/c/ChromiumTheMutable.java +++ b/Mage.Sets/src/mage/cards/c/ChromiumTheMutable.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; @@ -38,7 +38,7 @@ public final class ChromiumTheMutable extends CardImpl { this.addAbility(FlashAbility.getInstance()); // This spell can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Flying this.addAbility(FlyingAbility.getInstance()); @@ -50,12 +50,12 @@ public final class ChromiumTheMutable extends CardImpl { false, false, null, null, true ).setText("Until end of turn, {this} becomes " + "a Human with base power and toughness 1/1, " - + "loses all abilities, and gains hexproof."), + + "loses all abilities, and gains hexproof"), new DiscardCardCost() ); ability.addEffect( new CantBeBlockedSourceEffect(Duration.EndOfTurn) - .setText("It can't be blocked this turn.") + .setText("It can't be blocked this turn") ); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ChronicFlooding.java b/Mage.Sets/src/mage/cards/c/ChronicFlooding.java index 62f452388d..131327bd24 100644 --- a/Mage.Sets/src/mage/cards/c/ChronicFlooding.java +++ b/Mage.Sets/src/mage/cards/c/ChronicFlooding.java @@ -91,6 +91,6 @@ class ChronicFloodingAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever enchanted land becomes tapped, its controller puts the top three cards of their library into their graveyard."; + return "Whenever enchanted land becomes tapped, its controller mills three cards."; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/ChroniclerOfHeroes.java b/Mage.Sets/src/mage/cards/c/ChroniclerOfHeroes.java index 0915439a16..9d078b0271 100644 --- a/Mage.Sets/src/mage/cards/c/ChroniclerOfHeroes.java +++ b/Mage.Sets/src/mage/cards/c/ChroniclerOfHeroes.java @@ -75,7 +75,7 @@ class ChroniclerOfHeroesEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { if (new PermanentsOnTheBattlefieldCondition(filter).apply(game, source)) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/Chronozoa.java b/Mage.Sets/src/mage/cards/c/Chronozoa.java index 81a6153913..2ade15a2fe 100644 --- a/Mage.Sets/src/mage/cards/c/Chronozoa.java +++ b/Mage.Sets/src/mage/cards/c/Chronozoa.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.LastTimeCounterRemovedCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; @@ -46,7 +46,7 @@ public final class Chronozoa extends CardImpl { // When Chronozoa is put into a graveyard from play, if it had no time counters on it, create two tokens that are copies of it. Effect effect = new CreateTokenCopySourceEffect(2); effect.setText("create two tokens that are copies of it"); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesTriggeredAbility(effect, false), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(effect, false), LastTimeCounterRemovedCondition.instance, "When {this} dies, if it had no time counters on it, create two tokens that are copies of it.")); } diff --git a/Mage.Sets/src/mage/cards/c/ChubToad.java b/Mage.Sets/src/mage/cards/c/ChubToad.java index 8cc95ab155..ab57c1310b 100644 --- a/Mage.Sets/src/mage/cards/c/ChubToad.java +++ b/Mage.Sets/src/mage/cards/c/ChubToad.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class ChubToad extends CardImpl { // Whenever Chub Toad blocks or becomes blocked, it gets +2/+2 until end of turn. Effect effect = new BoostSourceEffect(+2, +2, Duration.EndOfTurn); effect.setText("it gets +2/+2 until end of turn"); - Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(effect, false); + Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CinderCloud.java b/Mage.Sets/src/mage/cards/c/CinderCloud.java index 95a401befd..a2208fc8ad 100644 --- a/Mage.Sets/src/mage/cards/c/CinderCloud.java +++ b/Mage.Sets/src/mage/cards/c/CinderCloud.java @@ -59,7 +59,7 @@ class CinderCloudEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent != null && permanent.destroy(source.getSourceId(), game, false) && permanent.getColor(game).equals(ObjectColor.WHITE)) { - game.applyEffects(); + game.getState().processAction(game); if (permanent.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(permanent.getId()) && game.getState().getZone(permanent.getId()) != Zone.GRAVEYARD) { // A replacement effect has moved the card to another zone as grvayard diff --git a/Mage.Sets/src/mage/cards/c/CirclingVultures.java b/Mage.Sets/src/mage/cards/c/CirclingVultures.java new file mode 100644 index 0000000000..bee9f4ca4b --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CirclingVultures.java @@ -0,0 +1,104 @@ +package mage.cards.c; + +import java.util.UUID; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpecialAction; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.common.ExileTopCreatureCardOfGraveyardCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; + +/** + * @author arcox + */ +public final class CirclingVultures extends CardImpl { + + public CirclingVultures(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + this.subtype.add(SubType.BIRD); + + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // You may discard Circling Vultures any time you could cast an instant. + this.addAbility(new CirclingVulturesSpecialAction()); + + // At the beginning of your upkeep, sacrifice Circling Vultures unless you exile the top creature card of your graveyard. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ExileTopCreatureCardOfGraveyardCost(1)), TargetController.YOU, false)); + } + + public CirclingVultures(final CirclingVultures card) { + super(card); + } + + @Override + public CirclingVultures copy() { + return new CirclingVultures(this); + } +} + +class CirclingVulturesSpecialAction extends SpecialAction { + + public CirclingVulturesSpecialAction() { + super(Zone.HAND); + this.setMayActivate(TargetController.YOU); + this.addEffect(new CirclingVulturesDiscardEffect()); + } + + public CirclingVulturesSpecialAction(final CirclingVulturesSpecialAction ability) { + super(ability); + } + + @Override + public CirclingVulturesSpecialAction copy() { + return new CirclingVulturesSpecialAction(this); + } + + @Override + public String getRule() { + return "You may discard {this} any time you could cast an instant."; + } +} + +class CirclingVulturesDiscardEffect extends OneShotEffect { + public CirclingVulturesDiscardEffect() { + super(Outcome.Neutral); + this.staticText = "discard {this}"; + } + + public CirclingVulturesDiscardEffect(final CirclingVulturesDiscardEffect effect) { + super(effect); + } + + @Override + public CirclingVulturesDiscardEffect copy() { + return new CirclingVulturesDiscardEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + + Card card = player.getHand().get(source.getSourceId(), game); + if (card == null) { + return false; + } + + return player.discard(card, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CitywatchSphinx.java b/Mage.Sets/src/mage/cards/c/CitywatchSphinx.java index 3bfe96bc8d..0fe4108f9a 100644 --- a/Mage.Sets/src/mage/cards/c/CitywatchSphinx.java +++ b/Mage.Sets/src/mage/cards/c/CitywatchSphinx.java @@ -2,7 +2,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.keyword.SurveilEffect; import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; @@ -27,7 +27,7 @@ public final class CitywatchSphinx extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Citywatch Sphinx dies, surveil 2. - this.addAbility(new DiesTriggeredAbility(new SurveilEffect(2))); + this.addAbility(new DiesSourceTriggeredAbility(new SurveilEffect(2))); } public CitywatchSphinx(final CitywatchSphinx card) { diff --git a/Mage.Sets/src/mage/cards/c/CivilizedScholar.java b/Mage.Sets/src/mage/cards/c/CivilizedScholar.java index 0f3f1eb7d2..480d3b6f5e 100644 --- a/Mage.Sets/src/mage/cards/c/CivilizedScholar.java +++ b/Mage.Sets/src/mage/cards/c/CivilizedScholar.java @@ -16,13 +16,10 @@ import mage.cards.h.HomicidalBruteWatcher; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.WatcherScope; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.watchers.Watcher; /** * @author nantuko @@ -77,7 +74,7 @@ class CivilizedScholarEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); Card card = player.discardOne(false, source, game); if (card != null && card.isCreature()) { Permanent permanent = game.getPermanent(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/c/ClackbridgeTroll.java b/Mage.Sets/src/mage/cards/c/ClackbridgeTroll.java index b3e3b9805e..fb16cf2153 100644 --- a/Mage.Sets/src/mage/cards/c/ClackbridgeTroll.java +++ b/Mage.Sets/src/mage/cards/c/ClackbridgeTroll.java @@ -114,7 +114,7 @@ class ClackbridgeTrollEffect extends OneShotEffect { sourcePerm.tap(game); } controller.gainLife(3, game, source); - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/ClashOfTitans.java b/Mage.Sets/src/mage/cards/c/ClashOfTitans.java new file mode 100644 index 0000000000..3c2b45967e --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ClashOfTitans.java @@ -0,0 +1,47 @@ +package mage.cards.c; + +import mage.abilities.effects.common.FightTargetsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ClashOfTitans extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(new AnotherTargetPredicate(2)); + } + + public ClashOfTitans(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}{R}"); + + // Target creature fights another target creature. + this.getSpellAbility().addEffect(new FightTargetsEffect()); + Target target = new TargetCreaturePermanent(); + target.setTargetTag(1); + this.getSpellAbility().addTarget(target); + + target = new TargetCreaturePermanent(filter); + target.setTargetTag(2); + this.getSpellAbility().addTarget(target); + } + + private ClashOfTitans(final ClashOfTitans card) { + super(card); + } + + @Override + public ClashOfTitans copy() { + return new ClashOfTitans(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ClearShot.java b/Mage.Sets/src/mage/cards/c/ClearShot.java index dd91a12153..3cd13c1c01 100644 --- a/Mage.Sets/src/mage/cards/c/ClearShot.java +++ b/Mage.Sets/src/mage/cards/c/ClearShot.java @@ -7,8 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -19,12 +18,6 @@ import java.util.UUID; */ public final class ClearShot extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public ClearShot(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); @@ -35,11 +28,11 @@ public final class ClearShot extends CardImpl { // It deals damage equal to its power to target creature you don't control. this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("It")); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); // second target + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); // second target } - public ClearShot(final ClearShot card) { + private ClearShot(final ClearShot card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/c/ClearTheMind.java b/Mage.Sets/src/mage/cards/c/ClearTheMind.java index b92b0ae932..77f82c9a27 100644 --- a/Mage.Sets/src/mage/cards/c/ClearTheMind.java +++ b/Mage.Sets/src/mage/cards/c/ClearTheMind.java @@ -44,7 +44,7 @@ class ClearTheMindEffect extends OneShotEffect { ClearTheMindEffect() { super(Outcome.Benefit); - staticText = "Target player shuffles their graveyard into their library."; + staticText = "Target player shuffles their graveyard into their library"; } private ClearTheMindEffect(final ClearTheMindEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ClergyOfTheHolyNimbus.java b/Mage.Sets/src/mage/cards/c/ClergyOfTheHolyNimbus.java index f366c54ab5..55257f19df 100644 --- a/Mage.Sets/src/mage/cards/c/ClergyOfTheHolyNimbus.java +++ b/Mage.Sets/src/mage/cards/c/ClergyOfTheHolyNimbus.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.ActivateOnlyByOpponentActivatedAbility; @@ -11,17 +9,14 @@ import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.CantBeRegeneratedSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class ClergyOfTheHolyNimbus extends CardImpl { @@ -66,7 +61,7 @@ class ClergyOfTheHolyNimbusReplacementEffect extends ReplacementEffectImpl { Permanent ClergyOfTheHolyNimbus = game.getPermanent(event.getTargetId()); if (ClergyOfTheHolyNimbus != null && event.getAmount() == 0) { // 1=noRegen - if (ClergyOfTheHolyNimbus.regenerate(source.getSourceId(), game)) { + if (ClergyOfTheHolyNimbus.regenerate(source, game)) { game.informPlayers(source.getSourceObject(game).getName() + " has been regenerated."); return true; } diff --git a/Mage.Sets/src/mage/cards/c/ClingToDust.java b/Mage.Sets/src/mage/cards/c/ClingToDust.java index 680f9ecbc3..b3b3246447 100644 --- a/Mage.Sets/src/mage/cards/c/ClingToDust.java +++ b/Mage.Sets/src/mage/cards/c/ClingToDust.java @@ -72,7 +72,7 @@ class ClingToDustEffect extends OneShotEffect { if (isCreature) { player.gainLife(3, game, source); } else { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java b/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java index 477ddbbc72..6a438c7573 100644 --- a/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java +++ b/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java @@ -1,186 +1,186 @@ -package mage.cards.c; - -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.combat.CombatGroup; -import mage.game.events.DamageEvent; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.TargetPermanent; -import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.targetpointer.FixedTarget; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class CloakOfConfusion extends CardImpl { - - public CloakOfConfusion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature you control - TargetPermanent auraTarget = new TargetControlledCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // Whenever enchanted creature attacks and isn't blocked, you may have it assign no combat damage this turn. - // If you do, defending player discards a card at random. - this.addAbility(new CloakOfConfusionTriggeredAbility()); - - } - - public CloakOfConfusion(final CloakOfConfusion card) { - super(card); - } - - @Override - public CloakOfConfusion copy() { - return new CloakOfConfusion(this); - } -} - -class CloakOfConfusionTriggeredAbility extends TriggeredAbilityImpl { - - public CloakOfConfusionTriggeredAbility() { - super(Zone.BATTLEFIELD, new CloakOfConfusionEffect(), true); - } - - public CloakOfConfusionTriggeredAbility(final CloakOfConfusionTriggeredAbility ability) { - super(ability); - } - - @Override - public CloakOfConfusionTriggeredAbility copy() { - return new CloakOfConfusionTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DECLARE_BLOCKERS_STEP; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent aura = game.getPermanentOrLKIBattlefield(getSourceId()); - if (aura != null) { - Permanent enchantedCreature = game.getPermanent(aura.getAttachedTo()); - if (enchantedCreature != null - && enchantedCreature.isAttacking()) { - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty() - && combatGroup.getAttackers().contains(enchantedCreature.getId())) { - this.getEffects().setTargetPointer( - new FixedTarget(game.getCombat().getDefendingPlayerId( - enchantedCreature.getId(), game))); - return true; - } - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever enchanted creature attacks and isn't blocked, " + super.getRule(); - } -} - -class CloakOfConfusionEffect extends OneShotEffect { - - public CloakOfConfusionEffect() { - super(Outcome.Neutral); - this.staticText = "you may have it assign no combat damage this turn. " - + "If you do, defending player discards a card at random"; - } - - public CloakOfConfusionEffect(final CloakOfConfusionEffect effect) { - super(effect); - } - - @Override - public CloakOfConfusionEffect copy() { - return new CloakOfConfusionEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent enchantedCreature = game.getPermanent(game.getPermanent(source.getSourceId()).getAttachedTo()); - if (controller != null && controller.chooseUse(outcome, "Do you wish to not assign combat damage from " - + enchantedCreature.getName() + " and have the defending player discard a card at random?", source, game)) { - ContinuousEffect effect = new AssignNoCombatDamageTargetEffect(); - effect.setTargetPointer(new FixedTarget(enchantedCreature.getId())); - game.addEffect(effect, source); - Player defendingPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (defendingPlayer != null) { - defendingPlayer.discard(1, true, source, game); - } - return true; - } - - return false; - } -} - -class AssignNoCombatDamageTargetEffect extends ReplacementEffectImpl { - - public AssignNoCombatDamageTargetEffect() { - super(Duration.EndOfTurn, Outcome.Neutral); - } - - public AssignNoCombatDamageTargetEffect(final AssignNoCombatDamageTargetEffect effect) { - super(effect); - } - - @Override - public AssignNoCombatDamageTargetEffect copy() { - return new AssignNoCombatDamageTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - switch (event.getType()) { - case DAMAGE_CREATURE: - case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: - return true; - default: - return false; - } - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - DamageEvent damageEvent = (DamageEvent) event; - return event.getSourceId().equals(targetPointer.getFirst(game, source)) - && damageEvent.isCombatDamage(); - } -} +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class CloakOfConfusion extends CardImpl { + + public CloakOfConfusion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature you control + TargetPermanent auraTarget = new TargetControlledCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Whenever enchanted creature attacks and isn't blocked, you may have it assign no combat damage this turn. + // If you do, defending player discards a card at random. + this.addAbility(new CloakOfConfusionTriggeredAbility()); + + } + + public CloakOfConfusion(final CloakOfConfusion card) { + super(card); + } + + @Override + public CloakOfConfusion copy() { + return new CloakOfConfusion(this); + } +} + +class CloakOfConfusionTriggeredAbility extends TriggeredAbilityImpl { + + public CloakOfConfusionTriggeredAbility() { + super(Zone.BATTLEFIELD, new CloakOfConfusionEffect(), true); + } + + public CloakOfConfusionTriggeredAbility(final CloakOfConfusionTriggeredAbility ability) { + super(ability); + } + + @Override + public CloakOfConfusionTriggeredAbility copy() { + return new CloakOfConfusionTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DECLARE_BLOCKERS_STEP; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent aura = game.getPermanentOrLKIBattlefield(getSourceId()); + if (aura != null) { + Permanent enchantedCreature = game.getPermanent(aura.getAttachedTo()); + if (enchantedCreature != null + && enchantedCreature.isAttacking()) { + for (CombatGroup combatGroup : game.getCombat().getGroups()) { + if (combatGroup.getBlockers().isEmpty() + && combatGroup.getAttackers().contains(enchantedCreature.getId())) { + this.getEffects().setTargetPointer( + new FixedTarget(game.getCombat().getDefendingPlayerId( + enchantedCreature.getId(), game))); + return true; + } + } + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever enchanted creature attacks and isn't blocked, " + super.getRule(); + } +} + +class CloakOfConfusionEffect extends OneShotEffect { + + public CloakOfConfusionEffect() { + super(Outcome.Neutral); + this.staticText = "you may have it assign no combat damage this turn. " + + "If you do, defending player discards a card at random"; + } + + public CloakOfConfusionEffect(final CloakOfConfusionEffect effect) { + super(effect); + } + + @Override + public CloakOfConfusionEffect copy() { + return new CloakOfConfusionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent enchantedCreature = game.getPermanent(game.getPermanent(source.getSourceId()).getAttachedTo()); + if (controller != null && controller.chooseUse(outcome, "Do you wish to not assign combat damage from " + + enchantedCreature.getName() + " and have the defending player discard a card at random?", source, game)) { + ContinuousEffect effect = new AssignNoCombatDamageTargetEffect(); + effect.setTargetPointer(new FixedTarget(enchantedCreature.getId())); + game.addEffect(effect, source); + Player defendingPlayer = game.getPlayer(targetPointer.getFirst(game, source)); + if (defendingPlayer != null) { + defendingPlayer.discard(1, true, source, game); + } + return true; + } + + return false; + } +} + +class AssignNoCombatDamageTargetEffect extends ReplacementEffectImpl { + + public AssignNoCombatDamageTargetEffect() { + super(Duration.EndOfTurn, Outcome.Neutral); + } + + public AssignNoCombatDamageTargetEffect(final AssignNoCombatDamageTargetEffect effect) { + super(effect); + } + + @Override + public AssignNoCombatDamageTargetEffect copy() { + return new AssignNoCombatDamageTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_CREATURE: + case DAMAGE_PLAYER: + case DAMAGE_PLANESWALKER: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + DamageEvent damageEvent = (DamageEvent) event; + return event.getSourceId().equals(targetPointer.getFirst(game, source)) + && damageEvent.isCombatDamage(); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CloneShell.java b/Mage.Sets/src/mage/cards/c/CloneShell.java index 84171a085b..7ce9a397b9 100644 --- a/Mage.Sets/src/mage/cards/c/CloneShell.java +++ b/Mage.Sets/src/mage/cards/c/CloneShell.java @@ -2,7 +2,7 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.*; @@ -36,7 +36,7 @@ public final class CloneShell extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new CloneShellEffect(), false)); // When Clone Shell dies, turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control. - this.addAbility(new DiesTriggeredAbility(new CloneShellDiesEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new CloneShellDiesEffect())); } public CloneShell(final CloneShell card) { diff --git a/Mage.Sets/src/mage/cards/c/ClotSliver.java b/Mage.Sets/src/mage/cards/c/ClotSliver.java index e7bd0f9039..d3ba912fbb 100644 --- a/Mage.Sets/src/mage/cards/c/ClotSliver.java +++ b/Mage.Sets/src/mage/cards/c/ClotSliver.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -16,6 +15,8 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; +import java.util.UUID; + /** * @author Loki */ @@ -33,7 +34,7 @@ public final class ClotSliver extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new GenericManaCost(2)), Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect("this permanent"), new GenericManaCost(2)), Duration.WhileOnBattlefield, filter, false))); } public ClotSliver(final ClotSliver card) { diff --git a/Mage.Sets/src/mage/cards/c/CloudKey.java b/Mage.Sets/src/mage/cards/c/CloudKey.java index 5a9bd8158e..ef63d2a9f7 100644 --- a/Mage.Sets/src/mage/cards/c/CloudKey.java +++ b/Mage.Sets/src/mage/cards/c/CloudKey.java @@ -1,42 +1,37 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.c; -import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.ChooseCardTypeEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionAllOfChosenCardTypeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.choices.ChoiceImpl; -import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; -import mage.players.Player; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; + +import java.util.Arrays; +import java.util.UUID; /** - * * @author nick.myers */ public final class CloudKey extends CardImpl { public CloudKey(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // As Cloud Key enters the battlefield, choose artifact, creature, enchantment, instant, or sorcery. - this.addAbility(new AsEntersBattlefieldAbility(new CloudKeyChooseTypeEffect())); + this.addAbility(new AsEntersBattlefieldAbility( + new ChooseCardTypeEffect(Outcome.Benefit, Arrays.asList(CardType.ARTIFACT, CardType.CREATURE, CardType.ENCHANTMENT, CardType.INSTANT, CardType.SORCERY)) + .setText("choose artifact, creature, enchantment, instant, or sorcery") + )); // Spells you cast of the chosen type cost {1} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CloudKeyCostModificationEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostReductionAllOfChosenCardTypeEffect(new FilterCard("Spells you cast of the chosen type"), 1, true) + )); } @Override @@ -48,84 +43,3 @@ public final class CloudKey extends CardImpl { super(card); } } - -class CloudKeyChooseTypeEffect extends OneShotEffect { - - public CloudKeyChooseTypeEffect() { - super(Outcome.Neutral); - this.staticText = "choose artifact, creature, enchantment, instant, or sorcery."; - } - - public CloudKeyChooseTypeEffect(final CloudKeyChooseTypeEffect effect) { - super(effect); - } - - @Override - public CloudKeyChooseTypeEffect copy() { - return new CloudKeyChooseTypeEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject mageObject = game.getPermanentEntering(source.getSourceId()); - if (mageObject == null) { - mageObject = game.getObject(source.getSourceId()); - } - if (mageObject != null && controller != null) { - ChoiceImpl choices = new ChoiceImpl(true); - choices.setMessage("Choose a spell type"); - choices.getChoices().add(CardType.ARTIFACT.toString()); - choices.getChoices().add(CardType.CREATURE.toString()); - choices.getChoices().add(CardType.ENCHANTMENT.toString()); - choices.getChoices().add(CardType.INSTANT.toString()); - choices.getChoices().add(CardType.SORCERY.toString()); - if (controller.choose(Outcome.Neutral, choices, game)) { - game.informPlayers(mageObject.getLogName() + ": chosen spell type is " + choices.getChoice()); - game.getState().setValue(source.getSourceId().toString() + "_CloudKey", choices.getChoice()); - if (mageObject instanceof Permanent) { - ((Permanent) mageObject).addInfo("chosenCardType", CardUtil.addToolTipMarkTags("Chosen card type: " + choices.getChoice()), game); - } - return true; - } - } - return false; - } - -} - -class CloudKeyCostModificationEffect extends CostModificationEffectImpl { - - public CloudKeyCostModificationEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); - this.staticText = "Spells you cast of the chosen type cost {1} less to cast."; - } - - public CloudKeyCostModificationEffect(final CloudKeyCostModificationEffect effect) { - super(effect); - } - - @Override - public CloudKeyCostModificationEffect copy() { - return new CloudKeyCostModificationEffect(this); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, 1); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility && abilityToModify.isControlledBy(source.getControllerId())) { - Spell spell = game.getStack().getSpell(abilityToModify.getSourceId()); - if (spell != null && spell.getCardType().toString().contains((String) game.getState().getValue(source.getSourceId().toString() + "_CloudKey"))) { - return true; - } - } - - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java b/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java index c0fc199a8c..ea015a23d3 100644 --- a/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java +++ b/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -9,11 +8,7 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.stack.Spell; @@ -21,8 +16,9 @@ import mage.players.Player; import mage.target.Target; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CloudhoofKirin extends CardImpl { @@ -44,7 +40,7 @@ public final class CloudhoofKirin extends CardImpl { this.addAbility(ability); } - public CloudhoofKirin(final CloudhoofKirin card) { + private CloudhoofKirin(final CloudhoofKirin card) { super(card); } @@ -56,12 +52,12 @@ public final class CloudhoofKirin extends CardImpl { class CloudhoofKirinEffect extends OneShotEffect { - public CloudhoofKirinEffect() { + CloudhoofKirinEffect() { super(Outcome.Detriment); - this.staticText = "you may have target player put the top X cards of their library into their graveyard, where X is that spell's converted mana cost"; + this.staticText = "have target player mill X cards, where X is that spell's converted mana cost"; } - public CloudhoofKirinEffect(final CloudhoofKirinEffect effect) { + private CloudhoofKirinEffect(final CloudhoofKirinEffect effect) { super(effect); } @@ -80,8 +76,10 @@ class CloudhoofKirinEffect extends OneShotEffect { targetPlayer = game.getPlayer(target.getFirstTarget()); } } - if (targetPlayer != null) { - return targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, spell.getConvertedManaCost()), Zone.GRAVEYARD, source, game); + int cmc = spell.getConvertedManaCost(); + if (targetPlayer != null && cmc > 0) { + targetPlayer.millCards(cmc, source, game); + return true; } } return false; diff --git a/Mage.Sets/src/mage/cards/c/Cloudpiercer.java b/Mage.Sets/src/mage/cards/c/Cloudpiercer.java new file mode 100644 index 0000000000..8261823742 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Cloudpiercer.java @@ -0,0 +1,49 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.MutateAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Cloudpiercer extends CardImpl { + + public Cloudpiercer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Mutate {3}{R} + this.addAbility(new MutateAbility(this, "{3}{R}")); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Whenever this creature mutates, you may discard a card. If you do, draw a card. + this.addAbility(new MutatesSourceTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), new DiscardCardCost() + ))); + } + + private Cloudpiercer(final Cloudpiercer card) { + super(card); + } + + @Override + public Cloudpiercer copy() { + return new Cloudpiercer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/Cloudshift.java b/Mage.Sets/src/mage/cards/c/Cloudshift.java index f960256b11..58df084895 100644 --- a/Mage.Sets/src/mage/cards/c/Cloudshift.java +++ b/Mage.Sets/src/mage/cards/c/Cloudshift.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderYourControlTargetEffect; @@ -10,8 +8,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author noxx */ public final class Cloudshift extends CardImpl { @@ -23,7 +22,7 @@ public final class Cloudshift extends CardImpl { this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); Effect effect = new ExileTargetForSourceEffect(); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(true)); + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(false)); } public Cloudshift(final Cloudshift card) { diff --git a/Mage.Sets/src/mage/cards/c/CloudstoneCurio.java b/Mage.Sets/src/mage/cards/c/CloudstoneCurio.java index e684313621..23bf56bcd2 100644 --- a/Mage.Sets/src/mage/cards/c/CloudstoneCurio.java +++ b/Mage.Sets/src/mage/cards/c/CloudstoneCurio.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.abilities.Ability; @@ -14,7 +13,6 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; import java.util.stream.Collectors; @@ -69,10 +67,7 @@ class CloudstoneCurioEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Permanent triggeringCreature = ((FixedTarget) getTargetPointer()).getTargetedPermanentOrLKIBattlefield(game); - if (triggeringCreature == null) { - triggeringCreature = (Permanent) game.getLastKnownInformation(getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD); - } + Permanent triggeringCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (triggeringCreature != null) { FilterPermanent filter = new FilterPermanent("another permanent you control that shares a permanent type with " + triggeringCreature.getName()); filter.add(Predicates.not(new PermanentIdPredicate(triggeringCreature.getId()))); diff --git a/Mage.Sets/src/mage/cards/c/CoalStoker.java b/Mage.Sets/src/mage/cards/c/CoalStoker.java index 17ead83cd5..26787c90d6 100644 --- a/Mage.Sets/src/mage/cards/c/CoalStoker.java +++ b/Mage.Sets/src/mage/cards/c/CoalStoker.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.mana.BasicManaEffect; import mage.cards.CardImpl; @@ -29,7 +29,7 @@ public final class CoalStoker extends CardImpl { // When Coal Stoker enters the battlefield, if you cast it from your hand, add {R}{R}{R}. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new BasicManaEffect(Mana.RedMana(3)), false), - CastFromHandSourceCondition.instance, + CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, add {R}{R}{R}."), new CastFromHandWatcher()); } diff --git a/Mage.Sets/src/mage/cards/c/Cockatrice.java b/Mage.Sets/src/mage/cards/c/Cockatrice.java index cd45ae14a3..d8cf0bb72d 100644 --- a/Mage.Sets/src/mage/cards/c/Cockatrice.java +++ b/Mage.Sets/src/mage/cards/c/Cockatrice.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -41,7 +41,7 @@ public final class Cockatrice extends CardImpl { // Whenever Cockatrice blocks or becomes blocked by a non-Wall creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false)); } public Cockatrice(final Cockatrice card) { diff --git a/Mage.Sets/src/mage/cards/c/CoercedConfession.java b/Mage.Sets/src/mage/cards/c/CoercedConfession.java index 2d727a4d8a..db45358213 100644 --- a/Mage.Sets/src/mage/cards/c/CoercedConfession.java +++ b/Mage.Sets/src/mage/cards/c/CoercedConfession.java @@ -1,10 +1,10 @@ - package mage.cards.c; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -12,22 +12,23 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.Objects; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CoercedConfession extends CardImpl { public CoercedConfession(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{U/B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U/B}"); // Target player puts the top four cards of their library into their graveyard. You draw a card for each creature card put into a graveyard this way. getSpellAbility().addEffect(new CoercedConfessionMillEffect()); getSpellAbility().addTarget(new TargetPlayer()); } - public CoercedConfession(final CoercedConfession card) { + private CoercedConfession(final CoercedConfession card) { super(card); } @@ -39,12 +40,12 @@ public final class CoercedConfession extends CardImpl { class CoercedConfessionMillEffect extends OneShotEffect { - public CoercedConfessionMillEffect() { + CoercedConfessionMillEffect() { super(Outcome.DrawCard); - this.staticText = "Target player puts the top four cards of their library into their graveyard. You draw a card for each creature card put into a graveyard this way"; + this.staticText = "Target player mills four cards. You draw a card for each creature card put into their graveyard this way"; } - public CoercedConfessionMillEffect(final CoercedConfessionMillEffect effect) { + private CoercedConfessionMillEffect(final CoercedConfessionMillEffect effect) { super(effect); } @@ -56,24 +57,26 @@ class CoercedConfessionMillEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - int foundCreatures = 0; - Cards cards = new CardsImpl(); - for(Card card: player.getLibrary().getTopCards(game, 4)) { - cards.add(card); - if (card.isCreature()) { - ++foundCreatures; - } - } - player.moveCards(cards, Zone.GRAVEYARD, source, game); - if (foundCreatures > 0) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.drawCards(foundCreatures, game); - } - } + if (player == null) { + return false; + } + int creaturesMilled = player + .millCards(4, source, game) + .getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> game.getState().getZone(card.getId()) == Zone.GRAVEYARD) + .filter(MageObject::isCreature) + .mapToInt(x -> 1) + .sum(); + if (creaturesMilled < 1) { return true; } - return false; + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return true; + } + controller.drawCards(creaturesMilled, source.getSourceId(), game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CoercivePortal.java b/Mage.Sets/src/mage/cards/c/CoercivePortal.java index 30d88f2034..fe92257372 100644 --- a/Mage.Sets/src/mage/cards/c/CoercivePortal.java +++ b/Mage.Sets/src/mage/cards/c/CoercivePortal.java @@ -77,7 +77,7 @@ class CoercivePortalEffect extends OneShotEffect { new SacrificeSourceEffect().apply(game, source); new DestroyAllEffect(new FilterNonlandPermanent()).apply(game, source); } else { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/CoilsOfTheMedusa.java b/Mage.Sets/src/mage/cards/c/CoilsOfTheMedusa.java new file mode 100644 index 0000000000..fe0bbe9c31 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CoilsOfTheMedusa.java @@ -0,0 +1,104 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * + * @author arcox + */ +public final class CoilsOfTheMedusa extends CardImpl { + public CoilsOfTheMedusa(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted creature gets +1/-1. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, -1, Duration.WhileOnBattlefield))); + + // Sacrifice Coils of the Medusa: Destroy all non-Wall creatures blocking enchanted creature. + this.addAbility(new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new CoilsOfTheMedusaDestroyEffect(), + new SacrificeSourceCost()) + ); + } + + public CoilsOfTheMedusa(final CoilsOfTheMedusa card) { + super(card); + } + + @Override + public CoilsOfTheMedusa copy() { + return new CoilsOfTheMedusa(this); + } +} + +class CoilsOfTheMedusaDestroyEffect extends OneShotEffect { + public CoilsOfTheMedusaDestroyEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "Destroy all non-Wall creatures blocking enchanted creature."; + } + + public CoilsOfTheMedusaDestroyEffect(final CoilsOfTheMedusaDestroyEffect effect) { + super(effect); + } + + @Override + public CoilsOfTheMedusaDestroyEffect copy() { + return new CoilsOfTheMedusaDestroyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent coils = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + List blockers = new ArrayList<>(); + + if (coils != null && coils.getAttachedTo() != null) { + // grab all creatures blocking enchanted creature + Permanent enchantedCreature = game.getPermanent(coils.getAttachedTo()); + if (enchantedCreature != null && enchantedCreature.isBlocked(game)) { + for (CombatGroup group : game.getCombat().getGroups()) { + if (group.getAttackers().contains(enchantedCreature.getId())) { + blockers = group.getBlockers(); + break; + } + } + + // filter out defenders, destroying the rest + while (!blockers.isEmpty()) { + Permanent blocker = game.getPermanent(blockers.remove(0)); + if (!blocker.hasAbility(DefenderAbility.getInstance(), game)) { + blocker.destroy(source.getSourceId(), game, false); + } + } + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/c/ColdEyedSelkie.java b/Mage.Sets/src/mage/cards/c/ColdEyedSelkie.java index 592581df02..118823d5c2 100644 --- a/Mage.Sets/src/mage/cards/c/ColdEyedSelkie.java +++ b/Mage.Sets/src/mage/cards/c/ColdEyedSelkie.java @@ -68,7 +68,7 @@ class ColdEyeSelkieEffect extends OneShotEffect { if (amount > 0) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(amount, game); + controller.drawCards(amount, source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CollapsingBorders.java b/Mage.Sets/src/mage/cards/c/CollapsingBorders.java index 1aa523897a..29793c618d 100644 --- a/Mage.Sets/src/mage/cards/c/CollapsingBorders.java +++ b/Mage.Sets/src/mage/cards/c/CollapsingBorders.java @@ -26,10 +26,10 @@ public final class CollapsingBorders extends CardImpl { // Domain - At the beginning of each player's upkeep, that player gains 1 life for each basic land type among lands they control. Then Collapsing Borders deals 3 damage to that player. Effect effect = new GainLifeTargetEffect(new DomainValue(true)); - effect.setText("that player gains 1 life for each basic land type among lands they control."); + effect.setText("that player gains 1 life for each basic land type among lands they control"); Ability ability = new BeginningOfUpkeepTriggeredAbility(effect, TargetController.ANY, false); effect = new DamageTargetEffect(3); - effect.setText("Then {this} deals 3 damage to that player."); + effect.setText("Then {this} deals 3 damage to that player"); ability.addEffect(effect); ability.setAbilityWord(AbilityWord.DOMAIN); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java b/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java index 9d60e693ab..985fbac516 100644 --- a/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java +++ b/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java @@ -3,7 +3,6 @@ package mage.cards.c; import mage.abilities.Mode; import mage.abilities.costs.Cost; import mage.abilities.costs.common.DiscardCardCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -15,11 +14,9 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.TargetController; import mage.filter.FilterCard; -import mage.filter.FilterPlayer; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.target.TargetPlayer; import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponent; import java.util.UUID; @@ -28,16 +25,11 @@ import java.util.UUID; */ public final class CollectiveBrutality extends CardImpl { - private static final FilterCard filter = new FilterCard("instant or sorcery card"); - private static final FilterPlayer filterDiscard = new FilterPlayer("opponent to discard"); - private static final FilterCreaturePermanent filterCreatureMinus = new FilterCreaturePermanent("creature to get -2/-2"); - private static final FilterPlayer filterLoseLife = new FilterPlayer("opponent to lose life"); + private static final FilterCard filter = new FilterCard("an instant or sorcery card"); static { filter.add(Predicates.or(CardType.INSTANT.getPredicate(), CardType.SORCERY.getPredicate())); - filterDiscard.add(TargetController.OPPONENT.getPlayerPredicate()); - filterLoseLife.add(TargetController.OPPONENT.getPlayerPredicate()); } public CollectiveBrutality(UUID ownerId, CardSetInfo setInfo) { @@ -52,28 +44,21 @@ public final class CollectiveBrutality extends CardImpl { this.getSpellAbility().getModes().setMinModes(1); this.getSpellAbility().getModes().setMaxModes(3); - // Target opponent reveals their hand. You choose an instant or sorcery card from it. That player discards that card.; - Effect effect = new DiscardCardYouChooseTargetEffect(filter, TargetController.ANY); - effect.setText("Target opponent reveals their hand. You choose an instant or sorcery card from it. That player discards that card"); - this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetPlayer(1, 1, false, filterDiscard).withChooseHint("reveals hand, you choose to discard")); + // Target opponent reveals their hand. You choose an instant or sorcery card from it. That player discards that card. + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter, TargetController.OPPONENT)); + this.getSpellAbility().addTarget(new TargetOpponent().withChooseHint("reveals hand, you choose to discard")); - // Target creature gets -2/-2 until end of turn.; + // Target creature gets -2/-2 until end of turn. Mode mode = new Mode(); - effect = new BoostTargetEffect(-2, -2, Duration.EndOfTurn); - effect.setText("Target creature gets -2/-2 until end of turn"); - mode.addEffect(effect); - mode.addTarget(new TargetCreaturePermanent(filterCreatureMinus).withChooseHint("gets -2/-2 until end of turn")); + mode.addEffect(new BoostTargetEffect(-2, -2, Duration.EndOfTurn)); + mode.addTarget(new TargetCreaturePermanent().withChooseHint("gets -2/-2 until end of turn")); this.getSpellAbility().addMode(mode); // Target opponent loses 2 life and you gain 2 life. mode = new Mode(); - effect = new LoseLifeTargetEffect(2); - effect.setText("Target opponent loses 2 life"); - mode.addEffect(effect); - mode.addTarget(new TargetPlayer(1, 1, false, filterLoseLife).withChooseHint("loses 2 life")); - effect = new GainLifeEffect(2); - mode.addEffect(effect.concatBy("and")); + mode.addEffect(new LoseLifeTargetEffect(2)); + mode.addEffect(new GainLifeEffect(2).concatBy("and")); + mode.addTarget(new TargetOpponent().withChooseHint("loses 2 life")); this.getSpellAbility().addMode(mode); } diff --git a/Mage.Sets/src/mage/cards/c/CollectiveDefiance.java b/Mage.Sets/src/mage/cards/c/CollectiveDefiance.java index ae5c57bb19..81f3abefa9 100644 --- a/Mage.Sets/src/mage/cards/c/CollectiveDefiance.java +++ b/Mage.Sets/src/mage/cards/c/CollectiveDefiance.java @@ -7,7 +7,6 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.EscalateAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -90,14 +89,11 @@ class CollectiveDefianceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (targetPlayer != null) { - int count = targetPlayer.getHand().size(); - for (Card card : targetPlayer.getHand().getCards(game)) { - targetPlayer.discard(card, source, game); - } - targetPlayer.drawCards(count, game); - return true; + if (targetPlayer == null) { + return false; } - return false; + int count = targetPlayer.discard(targetPlayer.getHand(), source, game).size(); + targetPlayer.drawCards(count, source.getSourceId(), game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/ColossalHeroics.java b/Mage.Sets/src/mage/cards/c/ColossalHeroics.java index 0f4ee4d943..061b0d6940 100644 --- a/Mage.Sets/src/mage/cards/c/ColossalHeroics.java +++ b/Mage.Sets/src/mage/cards/c/ColossalHeroics.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.UntapTargetEffect; @@ -12,8 +10,9 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ColossalHeroics extends CardImpl { @@ -23,9 +22,10 @@ public final class ColossalHeroics extends CardImpl { // Strive - Colossal Heroics costs {1}{G} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{1}{G}")); + // Any number of target creatures each get +2/+2 until end of turn. Untap those creatures. Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); - effect.setText("Any number of target creatures each get +2/+2 until end of turn."); + effect.setText("Any number of target creatures each get +2/+2 until end of turn"); this.getSpellAbility().addEffect(effect); effect = new UntapTargetEffect(); effect.setText("Untap those creatures"); diff --git a/Mage.Sets/src/mage/cards/c/Colossification.java b/Mage.Sets/src/mage/cards/c/Colossification.java new file mode 100644 index 0000000000..f76cc33b74 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Colossification.java @@ -0,0 +1,52 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.TapEnchantedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Colossification extends CardImpl { + + public Colossification(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{5}{G}{G}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // When Colossification enters the battlefield, tap enchanted creature. + this.addAbility(new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect())); + + // Enchanted creature gets +20/+20. + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(20, 20))); + } + + private Colossification(final Colossification card) { + super(card); + } + + @Override + public Colossification copy() { + return new Colossification(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java b/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java index f8d33deabe..dbc8f48801 100644 --- a/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java +++ b/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java @@ -1,24 +1,22 @@ package mage.cards.c; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FirstStrikeAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; -import java.util.Set; import java.util.UUID; /** @@ -97,7 +95,7 @@ class CombustibleGearhulkMillAndDamageEffect extends OneShotEffect { public CombustibleGearhulkMillAndDamageEffect() { super(Outcome.Damage); - staticText = "put the top three cards of your library into your graveyard, then {this} deals damage to that player equal to the total converted mana cost of those cards."; + staticText = "mill three cards, then {this} deals damage to that player equal to the total converted mana cost of those cards."; } public CombustibleGearhulkMillAndDamageEffect(final CombustibleGearhulkMillAndDamageEffect effect) { @@ -108,13 +106,12 @@ class CombustibleGearhulkMillAndDamageEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - int sumCMC = 0; - Set cardList = controller.getLibrary().getTopCards(game, 3); - for (Card card : cardList) { - int test = card.getConvertedManaCost(); - sumCMC += test; - } - controller.moveCards(cardList, Zone.GRAVEYARD, source, game); + int sumCMC = controller + .millCards(3, source, game) + .getCards(game) + .stream() + .mapToInt(MageObject::getConvertedManaCost) + .sum(); Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); if (targetPlayer != null) { targetPlayer.damage(sumCMC, source.getSourceId(), game); diff --git a/Mage.Sets/src/mage/cards/c/CometStorm.java b/Mage.Sets/src/mage/cards/c/CometStorm.java index 4ec4f69d54..97fbbb1fe4 100644 --- a/Mage.Sets/src/mage/cards/c/CometStorm.java +++ b/Mage.Sets/src/mage/cards/c/CometStorm.java @@ -58,7 +58,7 @@ class CometStormEffect extends OneShotEffect { public CometStormEffect() { super(Outcome.Damage); - staticText = "Choose any target, then choose another target for each time Comet Storm was kicked. Comet Storm deals X damage to each of them"; + staticText = "Choose any target, then choose another target for each time this spell was kicked. {this} deals X damage to each of them"; } public CometStormEffect(final CometStormEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CommanderCody.java b/Mage.Sets/src/mage/cards/c/CommanderCody.java index 371b118344..5340913a0c 100644 --- a/Mage.Sets/src/mage/cards/c/CommanderCody.java +++ b/Mage.Sets/src/mage/cards/c/CommanderCody.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -15,8 +13,9 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.TrooperToken; +import java.util.UUID; + /** - * * @author Styxo */ public final class CommanderCody extends CardImpl { @@ -29,7 +28,7 @@ public final class CommanderCody extends CardImpl { } public CommanderCody(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{U}{B}{R}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}{B}{R}{G}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.TROOPER); @@ -37,7 +36,11 @@ public final class CommanderCody extends CardImpl { this.toughness = new MageInt(7); // Non-token Trooper creatures you control have "At the beginning of your upkeep, create a 1/1 white Trooper creature token." - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(new BeginningOfUpkeepTriggeredAbility(new CreateTokenEffect(new TrooperToken()), TargetController.YOU, false), Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect( + new BeginningOfUpkeepTriggeredAbility(new CreateTokenEffect(new TrooperToken()), TargetController.YOU, false), + Duration.WhileOnBattlefield, filter, false) + .withForceQuotes() + )); } public CommanderCody(final CommanderCody card) { diff --git a/Mage.Sets/src/mage/cards/c/CommenceTheEndgame.java b/Mage.Sets/src/mage/cards/c/CommenceTheEndgame.java index 45eaf89629..1c58d22cf9 100644 --- a/Mage.Sets/src/mage/cards/c/CommenceTheEndgame.java +++ b/Mage.Sets/src/mage/cards/c/CommenceTheEndgame.java @@ -1,7 +1,7 @@ package mage.cards.c; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.keyword.AmassEffect; import mage.cards.CardImpl; @@ -22,7 +22,7 @@ public final class CommenceTheEndgame extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}{U}"); // This spell can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Draw two cards, then amass X, where X is the number of cards in your hand. this.getSpellAbility().addEffect(new CommenceTheEndgameEffect()); @@ -60,7 +60,7 @@ class CommenceTheEndgameEffect extends OneShotEffect { if (player == null) { return false; } - player.drawCards(2, game); + player.drawCards(2, source.getSourceId(), game); return new AmassEffect(player.getHand().size()).apply(game, source); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/CompellingDeterrence.java b/Mage.Sets/src/mage/cards/c/CompellingDeterrence.java index 4fb628199e..246c434497 100644 --- a/Mage.Sets/src/mage/cards/c/CompellingDeterrence.java +++ b/Mage.Sets/src/mage/cards/c/CompellingDeterrence.java @@ -63,7 +63,7 @@ class CompellingDeterrenceEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && player != null) { player.moveCards(target, Zone.HAND, source, game); - game.applyEffects(); + game.getState().processAction(game); FilterPermanent zombieFilter = new FilterPermanent(); zombieFilter.add(SubType.ZOMBIE.getPredicate()); if (game.getState().getBattlefield().countAll(zombieFilter, controller.getId(), game) > 0) { diff --git a/Mage.Sets/src/mage/cards/c/CompulsiveResearch.java b/Mage.Sets/src/mage/cards/c/CompulsiveResearch.java index d132be25f8..11e97e13c6 100644 --- a/Mage.Sets/src/mage/cards/c/CompulsiveResearch.java +++ b/Mage.Sets/src/mage/cards/c/CompulsiveResearch.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardTargetEffect; @@ -10,20 +8,21 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetDiscard; +import java.util.UUID; + /** - * * @author Plopman */ public final class CompulsiveResearch extends CardImpl { public CompulsiveResearch(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); // Target player draws three cards. Then that player discards two cards unless they discard a land card. this.getSpellAbility().addTarget(new TargetPlayer()); @@ -31,7 +30,7 @@ public final class CompulsiveResearch extends CardImpl { this.getSpellAbility().addEffect(new CompulsiveResearchDiscardEffect()); } - public CompulsiveResearch(final CompulsiveResearch card) { + private CompulsiveResearch(final CompulsiveResearch card) { super(card); } @@ -40,14 +39,15 @@ public final class CompulsiveResearch extends CardImpl { return new CompulsiveResearch(this); } } + class CompulsiveResearchDiscardEffect extends OneShotEffect { - public CompulsiveResearchDiscardEffect() { + CompulsiveResearchDiscardEffect() { super(Outcome.Discard); this.staticText = "Then that player discards two cards unless they discard a land card"; } - public CompulsiveResearchDiscardEffect(final CompulsiveResearchDiscardEffect effect) { + private CompulsiveResearchDiscardEffect(final CompulsiveResearchDiscardEffect effect) { super(effect); } @@ -59,18 +59,19 @@ class CompulsiveResearchDiscardEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (targetPlayer != null && !targetPlayer.getHand().isEmpty()) { - TargetDiscard target = new TargetDiscard(targetPlayer.getId()); - targetPlayer.choose(Outcome.Discard, target, source.getSourceId(), game); - Card card = targetPlayer.getHand().get(target.getFirstTarget(), game); - if (card != null) { - targetPlayer.discard(card, source, game); - if (!card.isLand() && !targetPlayer.getHand().isEmpty()) { - targetPlayer.discard(1, false, source, game); - } - return true; - } + if (targetPlayer == null || targetPlayer.getHand().isEmpty()) { + return false; } - return false; + if (targetPlayer.getHand().count(StaticFilters.FILTER_CARD_LAND, game) < 1 + || !targetPlayer.chooseUse(outcome, "Discard a land card?", source, game)) { + return !targetPlayer.discard(2, false, source, game).isEmpty(); + } + TargetDiscard target = new TargetDiscard(StaticFilters.FILTER_CARD_LAND_A, targetPlayer.getId()); + targetPlayer.choose(Outcome.Discard, target, source.getSourceId(), game); + Card card = targetPlayer.getHand().get(target.getFirstTarget(), game); + if (card != null && targetPlayer.discard(card, source, game)) { + return true; + } + return !targetPlayer.discard(2, false, source, game).isEmpty(); } } diff --git a/Mage.Sets/src/mage/cards/c/ConchHorn.java b/Mage.Sets/src/mage/cards/c/ConchHorn.java index b4622ce5ca..94d727ea62 100644 --- a/Mage.Sets/src/mage/cards/c/ConchHorn.java +++ b/Mage.Sets/src/mage/cards/c/ConchHorn.java @@ -64,7 +64,7 @@ class ConchHornEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(2, game); + player.drawCards(2, source.getSourceId(), game); putOnLibrary(player, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/c/ConclaveCavalier.java b/Mage.Sets/src/mage/cards/c/ConclaveCavalier.java index f8035d718c..144808dd33 100644 --- a/Mage.Sets/src/mage/cards/c/ConclaveCavalier.java +++ b/Mage.Sets/src/mage/cards/c/ConclaveCavalier.java @@ -2,7 +2,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.constants.SubType; import mage.abilities.keyword.VigilanceAbility; @@ -29,7 +29,7 @@ public final class ConclaveCavalier extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // When Conclave Cavalier dies, create two green and white 2/2 Elf Knight creature tokens with vigilance. - this.addAbility(new DiesTriggeredAbility( + this.addAbility(new DiesSourceTriggeredAbility( new CreateTokenEffect(new ElfKnightToken(), 2) )); } diff --git a/Mage.Sets/src/mage/cards/c/ConclaveMentor.java b/Mage.Sets/src/mage/cards/c/ConclaveMentor.java new file mode 100644 index 0000000000..28c1f1217a --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ConclaveMentor.java @@ -0,0 +1,101 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ConclaveMentor extends CardImpl { + + private static final DynamicValue xValue = new SourcePermanentPowerCount(false); + + public ConclaveMentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}"); + + this.subtype.add(SubType.CENTAUR); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on that creature instead. + this.addAbility(new SimpleStaticAbility(new ConclaveMentorEffect())); + + // When Conclave Mentor dies, you gain life equal to its power. + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(xValue, "you gain life equal to its power"))); + } + + private ConclaveMentor(final ConclaveMentor card) { + super(card); + } + + @Override + public ConclaveMentor copy() { + return new ConclaveMentor(this); + } +} + +class ConclaveMentorEffect extends ReplacementEffectImpl { + + ConclaveMentorEffect() { + super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false); + staticText = "If one or more +1/+1 counters would be put on a creature you control, " + + "that many plus one +1/+1 counters are put on that creature instead"; + } + + private ConclaveMentorEffect(final ConclaveMentorEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmountForCounters(event.getAmount() + 1, true); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ADD_COUNTERS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getData().equals(CounterType.P1P1.getName()) && event.getAmount() > 0) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null) { + permanent = game.getPermanentEntering(event.getTargetId()); + } + return permanent != null && permanent.isControlledBy(source.getControllerId()) + && permanent.isCreature(); + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public ConclaveMentorEffect copy() { + return new ConclaveMentorEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ConcussiveBolt.java b/Mage.Sets/src/mage/cards/c/ConcussiveBolt.java index 23c5c0e79d..7233e549a0 100644 --- a/Mage.Sets/src/mage/cards/c/ConcussiveBolt.java +++ b/Mage.Sets/src/mage/cards/c/ConcussiveBolt.java @@ -5,8 +5,10 @@ import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; @@ -28,9 +30,12 @@ public final class ConcussiveBolt extends CardImpl { // Concussive Bolt deals 4 damage to target player. this.getSpellAbility().addTarget(new TargetPlayerOrPlaneswalker()); this.getSpellAbility().addEffect(new DamageTargetEffect(4)); + // Metalcraft — If you control three or more artifacts, creatures that player controls can't block this turn. this.getSpellAbility().addEffect(new ConcussiveBoltEffect()); this.getSpellAbility().addEffect(new ConcussiveBoltRestrictionEffect()); + this.getSpellAbility().setAbilityWord(AbilityWord.METALCRAFT); + this.getSpellAbility().addHint(MetalcraftHint.instance); } public ConcussiveBolt(final ConcussiveBolt card) { diff --git a/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java b/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java index 3e857fc82d..7a86616f97 100644 --- a/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java +++ b/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java @@ -10,6 +10,7 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.CastSourceTriggeredAbility; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.abilities.effects.common.search.SearchLibraryPutOnLibraryEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -71,7 +72,7 @@ public final class ConduitOfRuin extends CardImpl { class ConduitOfRuinWatcher extends Watcher { - private Map playerCreatureSpells; + private final Map playerCreatureSpells; public ConduitOfRuinWatcher() { super(WatcherScope.GAME); @@ -103,8 +104,8 @@ class FirstCastCreatureSpellPredicate implements ObjectPlayerPredicate input, Game game) { - if (input.getObject() instanceof Spell - && ((Spell) input.getObject()).isCreature()) { + if (input.getObject() instanceof Card + && ((Card) input.getObject()).isCreature()) { ConduitOfRuinWatcher watcher = game.getState().getWatcher(ConduitOfRuinWatcher.class); return watcher != null && watcher.creatureSpellsCastThisTurn(input.getPlayerId()) == 0; } diff --git a/Mage.Sets/src/mage/cards/c/ConfrontTheAssault.java b/Mage.Sets/src/mage/cards/c/ConfrontTheAssault.java index 985bc5f919..2c45b4c15d 100644 --- a/Mage.Sets/src/mage/cards/c/ConfrontTheAssault.java +++ b/Mage.Sets/src/mage/cards/c/ConfrontTheAssault.java @@ -31,7 +31,7 @@ public final class ConfrontTheAssault extends CardImpl { this.addAbility(ability); // Create three 1/1 white Spirit creature tokens with flying. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken("ANA"), 3)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken(), 3)); } public ConfrontTheAssault(final ConfrontTheAssault card) { diff --git a/Mage.Sets/src/mage/cards/c/ConjurersBan.java b/Mage.Sets/src/mage/cards/c/ConjurersBan.java index f7624da9e8..ee03ad7253 100644 --- a/Mage.Sets/src/mage/cards/c/ConjurersBan.java +++ b/Mage.Sets/src/mage/cards/c/ConjurersBan.java @@ -1,7 +1,5 @@ package mage.cards.c; -import java.util.UUID; - import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; @@ -14,16 +12,17 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; import mage.game.events.GameEvent; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author noahg */ public final class ConjurersBan extends CardImpl { public ConjurersBan(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}{B}"); - // Choose a card name. Until your next turn, spells with the chosen name can’t be cast and lands with the chosen name can’t be played. this.getSpellAbility().addEffect(new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.ALL)); @@ -33,7 +32,7 @@ public final class ConjurersBan extends CardImpl { this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } - public ConjurersBan(final ConjurersBan card) { + private ConjurersBan(final ConjurersBan card) { super(card); } @@ -45,12 +44,12 @@ public final class ConjurersBan extends CardImpl { class ConjurersBanEffect extends ContinuousRuleModifyingEffectImpl { - public ConjurersBanEffect() { + ConjurersBanEffect() { super(Duration.UntilYourNextTurn, Outcome.Detriment, true, false); - this.staticText = "spells with the chosen name can't be cast and lands with the chosen name can't be played"; + this.staticText = "Until your next turn spells with the chosen name can't be cast and lands with the chosen name can't be played"; } - public ConjurersBanEffect(final ConjurersBanEffect effect) { + private ConjurersBanEffect(final ConjurersBanEffect effect) { super(effect); } @@ -68,18 +67,19 @@ class ConjurersBanEffect extends ContinuousRuleModifyingEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == GameEvent.EventType.CAST_SPELL || event.getType() == GameEvent.EventType.PLAY_LAND) { MageObject object = game.getObject(event.getSourceId()); - return object != null && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY)); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(object, cardName, game); } return false; } @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { - String namedCard = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); String playerName = game.getPlayer(source.getControllerId()).getName(); - if (namedCard == null || playerName == null || source.getSourceObject(game) == null){ + if (cardName == null || playerName == null || source.getSourceObject(game) == null) { return super.getInfoMessage(source, event, game); } - return "Until "+playerName+"'s next turn, spells named "+namedCard+" can't be cast and lands named "+namedCard+" can't be played ("+source.getSourceObject(game).getIdName()+")."; + return "Until " + playerName + "'s next turn, spells named " + cardName + " can't be cast and lands named " + cardName + " can't be played (" + source.getSourceObject(game).getIdName() + ")."; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/ConjurersCloset.java b/Mage.Sets/src/mage/cards/c/ConjurersCloset.java index 0783f1750c..2eda380a1f 100644 --- a/Mage.Sets/src/mage/cards/c/ConjurersCloset.java +++ b/Mage.Sets/src/mage/cards/c/ConjurersCloset.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; import mage.abilities.effects.common.ExileTargetForSourceEffect; @@ -11,17 +9,19 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** * @author noxx */ public final class ConjurersCloset extends CardImpl { public ConjurersCloset(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // At the beginning of your end step, you may exile target creature you control, then return that card to the battlefield under your control. Ability ability = new BeginningOfYourEndStepTriggeredAbility(new ExileTargetForSourceEffect(), true); - ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(true)); + ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(false)); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ConsignToDream.java b/Mage.Sets/src/mage/cards/c/ConsignToDream.java index 451c0819ea..32946ea4eb 100644 --- a/Mage.Sets/src/mage/cards/c/ConsignToDream.java +++ b/Mage.Sets/src/mage/cards/c/ConsignToDream.java @@ -11,6 +11,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.TargetPermanent; /** @@ -58,15 +59,15 @@ class ConsignToDreamEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - boolean applied = false; Permanent target = game.getPermanent(source.getFirstTarget()); - if (target != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (target != null && controller != null) { if (target.getColor(game).isRed() || target.getColor(game).isGreen()) { - applied = target.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + return controller.putCardsOnTopOfLibrary(target, game, source, true); } else { - applied = target.moveToZone(Zone.HAND, source.getSourceId(), game, false); + return controller.moveCards(target, Zone.HAND, source, game); } } - return applied; + return false; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/ConsignToDust.java b/Mage.Sets/src/mage/cards/c/ConsignToDust.java index 8703edfe45..37d04e9098 100644 --- a/Mage.Sets/src/mage/cards/c/ConsignToDust.java +++ b/Mage.Sets/src/mage/cards/c/ConsignToDust.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -13,8 +11,9 @@ import mage.filter.predicate.Predicates; import mage.target.Target; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ConsignToDust extends CardImpl { @@ -26,11 +25,11 @@ public final class ConsignToDust extends CardImpl { } public ConsignToDust(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); // Strive - Consign to Dust costs 2G more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{G}")); + // Destroy any number of target artifacts and/or enchantments. Effect effect = new DestroyTargetEffect(); effect.setText("Destroy any number of target artifacts and/or enchantments"); diff --git a/Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java b/Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java new file mode 100644 index 0000000000..1edb15357c --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java @@ -0,0 +1,54 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.GainActivatedAbilitiesOfTopCardEffect; +import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; +import mage.abilities.effects.common.continuous.PlayWithTheTopCardRevealedEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class ConspicuousSnoop extends CardImpl { + + private static final FilterCard filter = new FilterCard("cast Goblin spells"); + + static { + filter.add(SubType.GOBLIN.getPredicate()); + } + + public ConspicuousSnoop(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Play with the top card of your library revealed. + this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); + + // You may cast Goblin spells from the top of your library. + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + + // As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card. + this.addAbility(new SimpleStaticAbility(new GainActivatedAbilitiesOfTopCardEffect(filter.copy().withMessage("a Goblin card")))); + } + + private ConspicuousSnoop(final ConspicuousSnoop card) { + super(card); + } + + @Override + public ConspicuousSnoop copy() { + return new ConspicuousSnoop(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ConsumingVapors.java b/Mage.Sets/src/mage/cards/c/ConsumingVapors.java index 6653e4a7e5..61d9fbd7fa 100644 --- a/Mage.Sets/src/mage/cards/c/ConsumingVapors.java +++ b/Mage.Sets/src/mage/cards/c/ConsumingVapors.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -23,11 +22,8 @@ import mage.target.common.TargetControlledPermanent; */ public final class ConsumingVapors extends CardImpl { - - public ConsumingVapors(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); // Target player sacrifices a creature. You gain life equal to that creature's toughness. this.getSpellAbility().addEffect(new ConsumingVaporsEffect()); @@ -49,12 +45,12 @@ public final class ConsumingVapors extends CardImpl { class ConsumingVaporsEffect extends OneShotEffect { - ConsumingVaporsEffect ( ) { + ConsumingVaporsEffect() { super(Outcome.Sacrifice); staticText = "Target player sacrifices a creature. You gain life equal to that creature's toughness"; } - ConsumingVaporsEffect ( ConsumingVaporsEffect effect ) { + ConsumingVaporsEffect(ConsumingVaporsEffect effect) { super(effect); } @@ -68,15 +64,13 @@ class ConsumingVaporsEffect extends OneShotEffect { filter.add(TargetController.YOU.getControllerPredicate()); TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true); - //A spell or ability could have removed the only legal target this player - //had, if thats the case this ability should fizzle. if (player != null && target.canChoose(player.getId(), game)) { player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); Permanent permanent = game.getPermanent(target.getFirstTarget()); - if ( permanent != null ) { + if (permanent != null) { + permanent.sacrifice(source.getSourceId(), game); controller.gainLife(permanent.getToughness().getValue(), game, source); - return permanent.sacrifice(source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/Contempt.java b/Mage.Sets/src/mage/cards/c/Contempt.java index 1aa4800d05..be865d5c1e 100644 --- a/Mage.Sets/src/mage/cards/c/Contempt.java +++ b/Mage.Sets/src/mage/cards/c/Contempt.java @@ -1,93 +1,93 @@ -package mage.cards.c; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.AttacksAttachedTriggeredAbility; -import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.ReturnToHandSourceEffect; -import mage.abilities.effects.common.ReturnToHandTargetEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author jeffwadsworth - */ -public final class Contempt extends CardImpl { - - public Contempt(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // Whenever enchanted creature attacks, return it and Contempt to their owners' hands at end of combat. - this.addAbility(new AttacksAttachedTriggeredAbility(new ContemptEffect())); - - } - - public Contempt(final Contempt card) { - super(card); - } - - @Override - public Contempt copy() { - return new Contempt(this); - } -} - -class ContemptEffect extends OneShotEffect { - - ContemptEffect() { - super(Outcome.Detriment); - this.staticText = "return it and {this} to their owners' hands at end of combat."; - } - - ContemptEffect(final ContemptEffect effect) { - super(effect); - } - - @Override - public ContemptEffect copy() { - return new ContemptEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent contempt = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (contempt != null) { - Permanent attachedToPermanent = game.getPermanent(contempt.getAttachedTo()); - if (attachedToPermanent != null) { - Effect effect = new ReturnToHandTargetEffect(); - effect.setTargetPointer(new FixedTarget( - attachedToPermanent.getId())).setText("return " - + attachedToPermanent.getName() + " to owner's hand."); - AtTheEndOfCombatDelayedTriggeredAbility ability = new AtTheEndOfCombatDelayedTriggeredAbility(effect); - game.addDelayedTriggeredAbility(ability, source); - } - Effect effect = new ReturnToHandSourceEffect(); - AtTheEndOfCombatDelayedTriggeredAbility ability = new AtTheEndOfCombatDelayedTriggeredAbility(effect); - game.addDelayedTriggeredAbility(ability, source); - return true; - } - return false; - } -} +package mage.cards.c; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAttachedTriggeredAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jeffwadsworth + */ +public final class Contempt extends CardImpl { + + public Contempt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Whenever enchanted creature attacks, return it and Contempt to their owners' hands at end of combat. + this.addAbility(new AttacksAttachedTriggeredAbility(new ContemptEffect())); + + } + + public Contempt(final Contempt card) { + super(card); + } + + @Override + public Contempt copy() { + return new Contempt(this); + } +} + +class ContemptEffect extends OneShotEffect { + + ContemptEffect() { + super(Outcome.Detriment); + this.staticText = "return it and {this} to their owners' hands at end of combat."; + } + + ContemptEffect(final ContemptEffect effect) { + super(effect); + } + + @Override + public ContemptEffect copy() { + return new ContemptEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent contempt = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (contempt != null) { + Permanent attachedToPermanent = game.getPermanent(contempt.getAttachedTo()); + if (attachedToPermanent != null) { + Effect effect = new ReturnToHandTargetEffect(); + effect.setTargetPointer(new FixedTarget( + attachedToPermanent.getId())).setText("return " + + attachedToPermanent.getName() + " to owner's hand."); + AtTheEndOfCombatDelayedTriggeredAbility ability = new AtTheEndOfCombatDelayedTriggeredAbility(effect); + game.addDelayedTriggeredAbility(ability, source); + } + Effect effect = new ReturnToHandSourceEffect(); + AtTheEndOfCombatDelayedTriggeredAbility ability = new AtTheEndOfCombatDelayedTriggeredAbility(effect); + game.addDelayedTriggeredAbility(ability, source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java b/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java index 6baed0ceb0..4bddff8c30 100644 --- a/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java +++ b/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java @@ -85,7 +85,7 @@ class ConundrumSphinxEffect extends OneShotEffect { if (card != null) { Cards cards = new CardsImpl(card); player.revealCards(source, player.getName(), cards, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { player.moveCards(cards, Zone.HAND, source, game); } else { player.putCardsOnBottomOfLibrary(cards, game, source, false); diff --git a/Mage.Sets/src/mage/cards/c/CoordinatedBarrage.java b/Mage.Sets/src/mage/cards/c/CoordinatedBarrage.java index 33c621edc5..b2d04a8993 100644 --- a/Mage.Sets/src/mage/cards/c/CoordinatedBarrage.java +++ b/Mage.Sets/src/mage/cards/c/CoordinatedBarrage.java @@ -1,6 +1,6 @@ - package mage.cards.c; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -16,8 +16,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetAttackingOrBlockingCreature; -import java.util.UUID; - /** * * @author emerald000 @@ -25,7 +23,7 @@ import java.util.UUID; public final class CoordinatedBarrage extends CardImpl { public CoordinatedBarrage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); // Choose a creature type. Coordinated Barrage deals damage to target attacking or blocking creature equal to the number of permanents you control of the chosen type. this.getSpellAbility().addEffect(new CoordinatedBarrageEffect()); @@ -46,7 +44,7 @@ class CoordinatedBarrageEffect extends OneShotEffect { CoordinatedBarrageEffect() { super(Outcome.Damage); - this.staticText = "Choose a creature type. Coordinated Barrage deals damage to target attacking or blocking creature equal to the number of permanents you control of the chosen type"; + this.staticText = "Choose a creature type. {this} deals damage to target attacking or blocking creature equal to the number of permanents you control of the chosen type"; } CoordinatedBarrageEffect(final CoordinatedBarrageEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CoordinatedCharge.java b/Mage.Sets/src/mage/cards/c/CoordinatedCharge.java new file mode 100644 index 0000000000..e87486c55b --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CoordinatedCharge.java @@ -0,0 +1,36 @@ +package mage.cards.c; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CoordinatedCharge extends CardImpl { + + public CoordinatedCharge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{W}"); + + // Creatures you control get +2/+1 until end of turn. + this.getSpellAbility().addEffect(new BoostControlledEffect(2, 1, Duration.EndOfTurn)); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private CoordinatedCharge(final CoordinatedCharge card) { + super(card); + } + + @Override + public CoordinatedCharge copy() { + return new CoordinatedCharge(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CopyEnchantment.java b/Mage.Sets/src/mage/cards/c/CopyEnchantment.java index a17d5da526..ed3202936d 100644 --- a/Mage.Sets/src/mage/cards/c/CopyEnchantment.java +++ b/Mage.Sets/src/mage/cards/c/CopyEnchantment.java @@ -3,26 +3,12 @@ package mage.cards.c; import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.CopyPermanentEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentCard; -import mage.players.Player; -import mage.target.Target; -import mage.util.functions.EmptyApplyToPermanent; /** * @@ -34,7 +20,6 @@ public final class CopyEnchantment extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); // You may have Copy Enchantment enter the battlefield as a copy of any enchantment on the battlefield. - //this.addAbility(new EntersBattlefieldAbility(new CopyEnchantmentEffect(new FilterEnchantmentPermanent("any enchantment")), true)); this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), true)); } diff --git a/Mage.Sets/src/mage/cards/c/CoreProwler.java b/Mage.Sets/src/mage/cards/c/CoreProwler.java index 963b642bbe..708adb93a0 100644 --- a/Mage.Sets/src/mage/cards/c/CoreProwler.java +++ b/Mage.Sets/src/mage/cards/c/CoreProwler.java @@ -1,7 +1,7 @@ package mage.cards.c; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.ProliferateEffect; import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class CoreProwler extends CardImpl { this.addAbility(InfectAbility.getInstance()); // When Core Prowler dies, proliferate. (You choose any number of permanents and/or players with counters on them, then give each another counter of a kind already there.) - this.addAbility(new DiesTriggeredAbility(new ProliferateEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new ProliferateEffect())); } public CoreProwler(final CoreProwler card) { diff --git a/Mage.Sets/src/mage/cards/c/CorneredMarket.java b/Mage.Sets/src/mage/cards/c/CorneredMarket.java new file mode 100644 index 0000000000..3d6bf43e88 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CorneredMarket.java @@ -0,0 +1,116 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.util.CardUtil; + +/** + * + * @author jeffwadsworth + */ +public final class CorneredMarket extends CardImpl { + + public CorneredMarket(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // Players can't cast spells with the same name as a nontoken permanent. + // Players can't play nonbasic lands with the same name as a nontoken permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CorneredMarketReplacementEffect())); + + } + + private CorneredMarket(final CorneredMarket card) { + super(card); + } + + @Override + public CorneredMarket copy() { + return new CorneredMarket(this); + } +} + +class CorneredMarketReplacementEffect extends ContinuousRuleModifyingEffectImpl { + + private static final FilterPermanent filter = new FilterPermanent(); + + static { + filter.add(mage.filter.predicate.Predicates.not(TokenPredicate.instance)); + } + + public CorneredMarketReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral, true, true); + staticText = "Players can't cast spells with the same name as a nontoken permanent." + + "
Players can't play nonbasic lands with the same name as a nontoken permanent."; + } + + CorneredMarketReplacementEffect(final CorneredMarketReplacementEffect effect) { + super(effect); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL_LATE + || event.getType() == GameEvent.EventType.PLAY_LAND; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Card card = game.getCard(event.getSourceId()); + if (card != null) { + Spell spell = game.getState().getStack().getSpell(event.getSourceId()); + // Face Down cast spell (Morph creature) has no name + if (spell != null + && spell.isFaceDown(game)) { + return false; + } + // play land check + if (card.isLand() + && !card.isBasic()) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { + if (permanent != null) { + if (CardUtil.haveSameNames(card, permanent.getName(), game)) { + return true; + } + } + } + return false; + } + // cast spell check + if (spell != null) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { + if (permanent != null) { + if (CardUtil.haveSameNames(card, permanent.getName(), game)) { + return true; + } + } + } + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public CorneredMarketReplacementEffect copy() { + return new CorneredMarketReplacementEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CorpseAugur.java b/Mage.Sets/src/mage/cards/c/CorpseAugur.java index 01f0dea7d4..4cb6722c91 100644 --- a/Mage.Sets/src/mage/cards/c/CorpseAugur.java +++ b/Mage.Sets/src/mage/cards/c/CorpseAugur.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.CardsInTargetPlayersGraveyardCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -33,7 +33,7 @@ public final class CorpseAugur extends CardImpl { CardsInTargetPlayersGraveyardCount dynamicValue = new CardsInTargetPlayersGraveyardCount(new FilterCreatureCard("the number of creature cards")); Effect effect = new DrawCardSourceControllerEffect(dynamicValue); effect.setText("You draw X cards"); - Ability ability = new DiesTriggeredAbility(effect, false); + Ability ability = new DiesSourceTriggeredAbility(effect, false); effect = new LoseLifeSourceControllerEffect(dynamicValue); effect.setText("and you lose X life, where X is the number of creature cards in target player's graveyard"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/c/CorpseCur.java b/Mage.Sets/src/mage/cards/c/CorpseCur.java index 617c260215..79144ef43c 100644 --- a/Mage.Sets/src/mage/cards/c/CorpseCur.java +++ b/Mage.Sets/src/mage/cards/c/CorpseCur.java @@ -30,7 +30,7 @@ public final class CorpseCur extends CardImpl { public CorpseCur (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); this.addAbility(InfectAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java b/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java index d6845d3243..eaae709b7c 100644 --- a/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java +++ b/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java @@ -3,7 +3,7 @@ package mage.cards.c; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -44,7 +44,7 @@ public final class CorrosiveOoze extends CardImpl { // Whenever Corrosive Ooze blocks or becomes blocked by an equipped creature, destroy all Equipment attached to that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new CorrosiveOozeEffect()), true); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false), new CorrosiveOozeCombatWatcher()); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false), new CorrosiveOozeCombatWatcher()); } public CorrosiveOoze(final CorrosiveOoze card) { @@ -133,7 +133,7 @@ class CorrosiveOozeCombatWatcher extends Watcher { if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) { Permanent attacker = game.getPermanent(event.getTargetId()); Permanent blocker = game.getPermanent(event.getSourceId()); - if (attacker != null && CardUtil.haveSameNames(attacker.getName(), "Corrosive Ooze")) { // To check for name is not working if Ooze is copied but name changed + if (attacker != null && CardUtil.haveSameNames(attacker, "Corrosive Ooze", game)) { // To check for name is not working if Ooze is copied but name changed if (blocker != null && hasAttachedEquipment(game, blocker)) { MageObjectReference oozeMor = new MageObjectReference(attacker, game); Set relatedCreatures = oozeBlocksOrBlocked.getOrDefault(oozeMor, new HashSet<>()); @@ -141,7 +141,7 @@ class CorrosiveOozeCombatWatcher extends Watcher { oozeBlocksOrBlocked.put(oozeMor, relatedCreatures); } } - if (blocker != null && CardUtil.haveSameNames(blocker.getName(), "Corrosive Ooze")) { + if (blocker != null && CardUtil.haveSameNames(blocker, "Corrosive Ooze", game)) { if (attacker != null && hasAttachedEquipment(game, attacker)) { MageObjectReference oozeMor = new MageObjectReference(blocker, game); Set relatedCreatures = oozeBlocksOrBlocked.getOrDefault(oozeMor, new HashSet<>()); diff --git a/Mage.Sets/src/mage/cards/c/CorruptOfficial.java b/Mage.Sets/src/mage/cards/c/CorruptOfficial.java index 4c8aed08ec..c5cdd88794 100644 --- a/Mage.Sets/src/mage/cards/c/CorruptOfficial.java +++ b/Mage.Sets/src/mage/cards/c/CorruptOfficial.java @@ -1,10 +1,9 @@ - package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; @@ -16,6 +15,7 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; +import mage.game.combat.Combat; import mage.game.permanent.Permanent; import mage.players.Player; @@ -26,7 +26,7 @@ import mage.players.Player; public final class CorruptOfficial extends CardImpl { public CorruptOfficial(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.MINION); this.power = new MageInt(3); @@ -34,9 +34,9 @@ public final class CorruptOfficial extends CardImpl { // {2}{B}: Regenerate Corrupt Official. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{2}{B}"))); - + // Whenever Corrupt Official becomes blocked, defending player discards a card at random. - this.addAbility(new BecomesBlockedByCreatureTriggeredAbility(new CorruptOfficialDiscardEffect(), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new CorruptOfficialDiscardEffect(), false)); } public CorruptOfficial(final CorruptOfficial card) { @@ -67,11 +67,12 @@ class CorruptOfficialDiscardEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent blockingCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (blockingCreature != null) { - Player opponent = game.getPlayer(blockingCreature.getControllerId()); - if (opponent != null) { - opponent.discard(1, true, source, game); + Permanent corruptOfficial = game.getPermanent(source.getSourceId()); + if (corruptOfficial != null) { + Combat combat = game.getCombat(); + Player defendingPlayer = game.getPlayer(combat.getDefendingPlayerId(corruptOfficial.getId(), game)); + if (defendingPlayer != null) { + defendingPlayer.discard(1, true, source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CorsairCaptain.java b/Mage.Sets/src/mage/cards/c/CorsairCaptain.java new file mode 100644 index 0000000000..f23412279b --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CorsairCaptain.java @@ -0,0 +1,50 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CorsairCaptain extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.PIRATE, "Pirates"); + + public CorsairCaptain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.PIRATE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Corsair Captain enters the battlefield, create a treasure token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new TreasureToken()))); + + // Other Pirates you control get +1/+1. + this.addAbility(new SimpleStaticAbility( + new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, true) + )); + } + + private CorsairCaptain(final CorsairCaptain card) { + super(card); + } + + @Override + public CorsairCaptain copy() { + return new CorsairCaptain(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java b/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java index a6e289d8f4..483f058a50 100644 --- a/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java +++ b/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java @@ -78,7 +78,7 @@ class CouncilOfTheAbsoluteReplacementEffect extends ContinuousRuleModifyingEffec public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source.getSourceId()); if (mageObject != null) { - return "You can't cast a spell with that name (" + mageObject.getLogName() + " in play)."; + return "You can't cast a spell with that name (" + mageObject.getName() + " in play)."; } return null; } @@ -92,8 +92,8 @@ class CouncilOfTheAbsoluteReplacementEffect extends ContinuousRuleModifyingEffec public boolean applies(GameEvent event, Ability source, Game game) { if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { MageObject object = game.getObject(event.getSourceId()); - String needName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - return object != null && CardUtil.haveSameNames(object.getName(), needName); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return object != null && CardUtil.haveSameNames(object, cardName, game); } return false; } @@ -122,8 +122,8 @@ class CouncilOfTheAbsoluteCostReductionEffect extends CostModificationEffectImpl && abilityToModify.isControlledBy(source.getControllerId())) { Card card = game.getCard(abilityToModify.getSourceId()); if (card != null) { - String needName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - return CardUtil.haveSameNames(card.getName(), needName); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(card, cardName, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/c/Countermand.java b/Mage.Sets/src/mage/cards/c/Countermand.java index 5c20b25a40..d2dcff8740 100644 --- a/Mage.Sets/src/mage/cards/c/Countermand.java +++ b/Mage.Sets/src/mage/cards/c/Countermand.java @@ -1,28 +1,27 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.stack.StackObject; import mage.players.Player; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Countermand extends CardImpl { public Countermand(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{U}"); // Counter target spell. Its controller puts the top four cards of their library into their graveyard. @@ -39,6 +38,7 @@ public final class Countermand extends CardImpl { return new Countermand(this); } } + class CountermandEffect extends OneShotEffect { public CountermandEffect() { @@ -65,7 +65,7 @@ class CountermandEffect extends OneShotEffect { if (stackObject != null) { Player controller = game.getPlayer(stackObject.getControllerId()); if (controller != null) { - controller.moveCards(controller.getLibrary().getTopCards(game, 4), Zone.GRAVEYARD, source, game); + controller.millCards(4, source, game); } } return countered; diff --git a/Mage.Sets/src/mage/cards/c/CourserOfKruphix.java b/Mage.Sets/src/mage/cards/c/CourserOfKruphix.java index 9bc9fd077a..2dcbcb047f 100644 --- a/Mage.Sets/src/mage/cards/c/CourserOfKruphix.java +++ b/Mage.Sets/src/mage/cards/c/CourserOfKruphix.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -12,32 +10,37 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.common.FilterLandCard; -import mage.filter.common.FilterLandPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class CourserOfKruphix extends CardImpl { + private static final FilterCard filter = new FilterLandCard("play land cards"); + public CourserOfKruphix(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT,CardType.CREATURE},"{1}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{G}{G}"); this.subtype.add(SubType.CENTAUR); this.power = new MageInt(2); this.toughness = new MageInt(4); // Play with the top card of your library revealed. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); + this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); + // You may play the top card of your library if it's a land card. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(new FilterLandCard()))); + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + // Whenever a land enters the battlefield under your control, you gain 1 life. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new GainLifeEffect(1), new FilterLandPermanent("a land"))); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new GainLifeEffect(1), StaticFilters.FILTER_LAND_A)); } - public CourserOfKruphix(final CourserOfKruphix card) { + private CourserOfKruphix(final CourserOfKruphix card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/c/CovenantOfMinds.java b/Mage.Sets/src/mage/cards/c/CovenantOfMinds.java index 9c4ed372d4..bedea474a8 100644 --- a/Mage.Sets/src/mage/cards/c/CovenantOfMinds.java +++ b/Mage.Sets/src/mage/cards/c/CovenantOfMinds.java @@ -74,12 +74,12 @@ class CovenantOfMindsEffect extends OneShotEffect { player.moveCards(cards, Zone.HAND, source, game); } else { player.moveCards(cards, Zone.GRAVEYARD, source, game); - player.drawCards(5, game); + player.drawCards(5, source.getSourceId(), game); } } else { if (!opponent.chooseUse(Outcome.Benefit, player.getLogName() + "'s library is empty? Do you want them to draw five cards?", source, game)) { - player.drawCards(5, game); + player.drawCards(5, source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/cards/c/CovetedJewel.java b/Mage.Sets/src/mage/cards/c/CovetedJewel.java index 0e068ed2a5..8f09a76266 100644 --- a/Mage.Sets/src/mage/cards/c/CovetedJewel.java +++ b/Mage.Sets/src/mage/cards/c/CovetedJewel.java @@ -123,7 +123,7 @@ class CovetedJewelControlEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); Player newControllingPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (permanent == null || newControllingPlayer == null || !newControllingPlayer.isInGame()) { + if (permanent == null || newControllingPlayer == null || !newControllingPlayer.canRespond()) { this.discard(); return false; } diff --git a/Mage.Sets/src/mage/cards/c/CradleOfVitality.java b/Mage.Sets/src/mage/cards/c/CradleOfVitality.java index ac66054460..4640c6695a 100644 --- a/Mage.Sets/src/mage/cards/c/CradleOfVitality.java +++ b/Mage.Sets/src/mage/cards/c/CradleOfVitality.java @@ -1,40 +1,38 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.GainLifeControllerTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * - * @author Alvin + * @author TheElk801 */ public final class CradleOfVitality extends CardImpl { public CradleOfVitality(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); // Whenever you gain life, you may pay {1}{W}. If you do, put a +1/+1 counter on target creature for each 1 life you gained. - Ability ability = new CradleOfVitalityGainLifeTriggeredAbility(); + Ability ability = new GainLifeControllerTriggeredAbility(new DoIfCostPaid( + new CradleOfVitalityEffect(), new ManaCostsImpl("{1}{W}") + ), false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } - public CradleOfVitality(final CradleOfVitality card) { + private CradleOfVitality(final CradleOfVitality card) { super(card); } @@ -44,51 +42,14 @@ public final class CradleOfVitality extends CardImpl { } } - -class CradleOfVitalityGainLifeTriggeredAbility extends TriggeredAbilityImpl { - - public CradleOfVitalityGainLifeTriggeredAbility() { - super(Zone.BATTLEFIELD, new CradleOfVitalityEffect(), false); - addManaCost(new ManaCostsImpl("{1}{W}")); - } - - public CradleOfVitalityGainLifeTriggeredAbility(final CradleOfVitalityGainLifeTriggeredAbility ability) { - super(ability); - } - - @Override - public CradleOfVitalityGainLifeTriggeredAbility copy() { - return new CradleOfVitalityGainLifeTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.GAINED_LIFE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(this.controllerId)) { - this.getEffects().get(0).setValue("amount", event.getAmount()); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever you gain life, you may pay {1}{W}. If you do, put a +1/+1 counter on target creature for each 1 life you gained"; - } -} - class CradleOfVitalityEffect extends OneShotEffect { - public CradleOfVitalityEffect() { + CradleOfVitalityEffect() { super(Outcome.Benefit); - staticText = "Put a +1/+1 counter on target creature for each 1 life you gained"; + staticText = "put a +1/+1 counter on target creature for each 1 life you gained"; } - public CradleOfVitalityEffect(final CradleOfVitalityEffect effect) { + private CradleOfVitalityEffect(final CradleOfVitalityEffect effect) { super(effect); } @@ -99,13 +60,12 @@ class CradleOfVitalityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - int affectedTargets = 0; - Integer amount = (Integer) getValue("amount"); - for (UUID uuid : targetPointer.getTargets(game, source)) { - Permanent permanent = game.getPermanent(uuid); - permanent.addCounters(CounterType.P1P1.createInstance(amount), source, game); - affectedTargets ++; + Permanent permanent = game.getPermanent(source.getFirstTarget()); + int lifeGained = 0; + if (this.getValue("gainedLife") != null) { + lifeGained = (Integer) this.getValue("gainedLife"); } - return affectedTargets > 0; + return permanent != null && lifeGained > 0 + && permanent.addCounters(CounterType.P1P1.createInstance(lifeGained), source, game); } } diff --git a/Mage.Sets/src/mage/cards/c/CranialArchive.java b/Mage.Sets/src/mage/cards/c/CranialArchive.java index 6c0d7d4260..4f91a820ad 100644 --- a/Mage.Sets/src/mage/cards/c/CranialArchive.java +++ b/Mage.Sets/src/mage/cards/c/CranialArchive.java @@ -71,7 +71,7 @@ class CranialArchiveEffect extends OneShotEffect { } targetPlayer.shuffleLibrary(source, game); } - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/c/CranialPlating.java b/Mage.Sets/src/mage/cards/c/CranialPlating.java index 4ff75ee085..cdafd2c9ac 100644 --- a/Mage.Sets/src/mage/cards/c/CranialPlating.java +++ b/Mage.Sets/src/mage/cards/c/CranialPlating.java @@ -1,47 +1,44 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author Loki */ public final class CranialPlating extends CardImpl { - private static final FilterControlledPermanent filterCounted = new FilterControlledPermanent("artifacts you control"); - - static { - filterCounted.add(CardType.ARTIFACT.getPredicate()); - } public CranialPlating(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +1/+0 for each artifact you control. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(new PermanentsOnBattlefieldCount(filterCounted), StaticValue.get(0)))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(ArtifactYouControlCount.instance, StaticValue.get(0))) + .addHint(ArtifactYouControlHint.instance)); + // {B}{B}: Attach Cranial Plating to target creature you control. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AttachEffect(Outcome.BoostCreature, "Attach {this} to target creature you control"), new ManaCostsImpl("{B}{B}")); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); + // Equip {1} this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(1))); } diff --git a/Mage.Sets/src/mage/cards/c/CrawlingSensation.java b/Mage.Sets/src/mage/cards/c/CrawlingSensation.java index f3c2788771..83f3cd4b74 100644 --- a/Mage.Sets/src/mage/cards/c/CrawlingSensation.java +++ b/Mage.Sets/src/mage/cards/c/CrawlingSensation.java @@ -1,8 +1,5 @@ - - package mage.cards.c; -import java.util.Set; import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.OnEventTriggeredAbility; @@ -69,11 +66,8 @@ class CrawlingSensationTriggeredAbility extends TriggeredAbilityImpl { for (Card card : zEvent.getCards()) { if (card != null) { UUID cardOwnerId = card.getOwnerId(); - Set cardType = card.getCardType(); - if (cardOwnerId != null && card.isOwnedBy(getControllerId()) - && cardType != null && card.isLand()) { game.getState().setValue("usedOnTurn" + getControllerId() + getOriginalId(), game.getTurnNum()); return true; diff --git a/Mage.Sets/src/mage/cards/c/CrazedFirecat.java b/Mage.Sets/src/mage/cards/c/CrazedFirecat.java index a19b1e8233..aacbd36d13 100644 --- a/Mage.Sets/src/mage/cards/c/CrazedFirecat.java +++ b/Mage.Sets/src/mage/cards/c/CrazedFirecat.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.MageInt; @@ -68,6 +67,11 @@ class CrazedFirecatEffect extends OneShotEffect { int flipsWon = 0; while (controller.flipCoin(source, game, true)) { flipsWon++; + + // AI workaround to stop on good condition + if (!controller.isHuman() && !controller.isTestMode() && flipsWon >= 2) { + break; + } } sourceObject.addCounters(CounterType.P1P1.createInstance(flipsWon), source, game); return true; diff --git a/Mage.Sets/src/mage/cards/c/CreamOfTheCrop.java b/Mage.Sets/src/mage/cards/c/CreamOfTheCrop.java index dbf1c4d519..c09d904c45 100644 --- a/Mage.Sets/src/mage/cards/c/CreamOfTheCrop.java +++ b/Mage.Sets/src/mage/cards/c/CreamOfTheCrop.java @@ -72,7 +72,7 @@ class CreamOfTheCropEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = ((FixedTarget) getTargetPointer()).getTargetedPermanentOrLKIBattlefield(game); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && permanent != null) { Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, permanent.getPower().getValue())); if (!cards.isEmpty()) { diff --git a/Mage.Sets/src/mage/cards/c/CreditVoucher.java b/Mage.Sets/src/mage/cards/c/CreditVoucher.java index 1884b3b7f3..8abaae5f4a 100644 --- a/Mage.Sets/src/mage/cards/c/CreditVoucher.java +++ b/Mage.Sets/src/mage/cards/c/CreditVoucher.java @@ -79,7 +79,7 @@ class CreditVoucherEffect extends OneShotEffect { } controller.shuffleLibrary(source, game); if (amountShuffled > 0) { - controller.drawCards(amountShuffled, game); + controller.drawCards(amountShuffled, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/CribSwap.java b/Mage.Sets/src/mage/cards/c/CribSwap.java index b1388e1994..671bc13ae5 100644 --- a/Mage.Sets/src/mage/cards/c/CribSwap.java +++ b/Mage.Sets/src/mage/cards/c/CribSwap.java @@ -66,7 +66,7 @@ class CribSwapEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Permanent targetCreature = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent targetCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (targetCreature != null) { CribSwapShapeshifterWhiteToken token = new CribSwapShapeshifterWhiteToken(); return token.putOntoBattlefield(1, game, source.getSourceId(), targetCreature.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/c/CrimsonRoc.java b/Mage.Sets/src/mage/cards/c/CrimsonRoc.java index 6b7d68697c..1dab5eb0ff 100644 --- a/Mage.Sets/src/mage/cards/c/CrimsonRoc.java +++ b/Mage.Sets/src/mage/cards/c/CrimsonRoc.java @@ -71,7 +71,7 @@ class CrimsonRocTriggeredAbility extends TriggeredAbilityImpl { Permanent permanent = game.getPermanent(event.getTargetId()); return permanent != null && permanent.isCreature() - && !permanent.getAbilities(game).contains(FlyingAbility.getInstance()); + && !permanent.hasAbility(FlyingAbility.getInstance(), game); } @Override diff --git a/Mage.Sets/src/mage/cards/c/CrosisThePurger.java b/Mage.Sets/src/mage/cards/c/CrosisThePurger.java index b2f87e2d6a..02afdcfbcc 100644 --- a/Mage.Sets/src/mage/cards/c/CrosisThePurger.java +++ b/Mage.Sets/src/mage/cards/c/CrosisThePurger.java @@ -1,9 +1,5 @@ - package mage.cards.c; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; @@ -11,23 +7,23 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.choices.ChoiceColor; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.players.Player; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * * @author LoneFox - * */ public final class CrosisThePurger extends CardImpl { @@ -45,7 +41,7 @@ public final class CrosisThePurger extends CardImpl { new ManaCostsImpl("{2}{B}")), false, true)); } - public CrosisThePurger(final CrosisThePurger card) { + private CrosisThePurger(final CrosisThePurger card) { super(card); } @@ -62,7 +58,7 @@ class CrosisThePurgerEffect extends OneShotEffect { this.staticText = "choose a color, then that player reveals their hand and discards all cards of that color."; } - CrosisThePurgerEffect(final CrosisThePurgerEffect effect) { + private CrosisThePurgerEffect(final CrosisThePurgerEffect effect) { super(effect); } @@ -74,30 +70,28 @@ class CrosisThePurgerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - ChoiceColor choice = new ChoiceColor(); - player.choose(outcome, choice, game); - if (choice.isChosen()) { - game.informPlayers(player.getLogName() + " chooses " + choice.getColor()); - Player damagedPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if(damagedPlayer != null) { - damagedPlayer.revealCards("hand of " + damagedPlayer.getName(), damagedPlayer.getHand(), game); - FilterCard filter = new FilterCard(); - filter.add(new ColorPredicate(choice.getColor())); - List toDiscard = new ArrayList<>(); - for (UUID cardId : damagedPlayer.getHand()) { - Card card = game.getCard(cardId); - if (filter.match(card, game)) { - toDiscard.add(card); - } - } - for (Card card : toDiscard) { - damagedPlayer.discard(card, source, game); - } - return true; - } - } + if (player == null) { + return false; } - return false; + ChoiceColor choice = new ChoiceColor(); + player.choose(outcome, choice, game); + if (!choice.isChosen()) { + return false; + } + game.informPlayers(player.getLogName() + " chooses " + choice.getColor()); + Player damagedPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); + if (damagedPlayer == null) { + return false; + } + damagedPlayer.revealCards("hand of " + damagedPlayer.getName(), damagedPlayer.getHand(), game); + Cards cards = new CardsImpl( + damagedPlayer.getHand() + .getCards(game) + .stream() + .filter(card -> card.getColor(game).shares(choice.getColor())) + .collect(Collectors.toSet()) + ); + damagedPlayer.discard(cards, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CrosstownCourier.java b/Mage.Sets/src/mage/cards/c/CrosstownCourier.java index 093357a261..bb29e3e698 100644 --- a/Mage.Sets/src/mage/cards/c/CrosstownCourier.java +++ b/Mage.Sets/src/mage/cards/c/CrosstownCourier.java @@ -80,7 +80,7 @@ public final class CrosstownCourier extends CardImpl { @Override public String getRule() { - return "Whenever {this} deals combat damage to a player, that player puts that many cards from the top of their library into their graveyard."; + return "Whenever {this} deals combat damage to a player, that player mills that many cards."; } } } diff --git a/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java b/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java index f5bfddfe29..9089c535cf 100644 --- a/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java +++ b/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java @@ -61,9 +61,9 @@ class CrownOfEmpiresEffect extends OneShotEffect { boolean scepter = false; boolean throne = false; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { - if (CardUtil.haveSameNames(permanent.getName(), "Scepter of Empires")) { + if (CardUtil.haveSameNames(permanent, "Scepter of Empires", game)) { scepter = true; - } else if (CardUtil.haveSameNames(permanent.getName(), "Throne of Empires")) { + } else if (CardUtil.haveSameNames(permanent, "Throne of Empires", game)) { throne = true; } if (scepter && throne) break; diff --git a/Mage.Sets/src/mage/cards/c/CrucibleOfTheSpiritDragon.java b/Mage.Sets/src/mage/cards/c/CrucibleOfTheSpiritDragon.java index 0b8fe29b22..6d8c17dbbb 100644 --- a/Mage.Sets/src/mage/cards/c/CrucibleOfTheSpiritDragon.java +++ b/Mage.Sets/src/mage/cards/c/CrucibleOfTheSpiritDragon.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -11,6 +10,7 @@ import mage.abilities.condition.Condition; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.ColorlessManaAbility; @@ -31,13 +31,13 @@ import mage.game.Game; public final class CrucibleOfTheSpiritDragon extends CardImpl { public CrucibleOfTheSpiritDragon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); // {1}, {T}: Put a storage counter on Crucible of the Spirit Dragon. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()),new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); @@ -45,9 +45,10 @@ public final class CrucibleOfTheSpiritDragon extends CardImpl { ability = new ConditionalAnyColorManaAbility( new TapSourceCost(), RemovedCountersForCostValue.instance, + new CountersSourceCount(CounterType.STORAGE), new CrucibleOfTheSpiritDragonManaBuilder(), false - ); + ); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); this.addAbility(ability); } @@ -94,4 +95,4 @@ class CrucibleOfTheSpiritDragonManaCondition implements Condition { } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/CruelFate.java b/Mage.Sets/src/mage/cards/c/CruelFate.java index b225636297..34e6ba82bf 100644 --- a/Mage.Sets/src/mage/cards/c/CruelFate.java +++ b/Mage.Sets/src/mage/cards/c/CruelFate.java @@ -1,14 +1,8 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -18,8 +12,9 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 & L_J */ public final class CruelFate extends CardImpl { @@ -71,7 +66,7 @@ class CruelFateEffect extends OneShotEffect { // card to put into opponent's graveyard TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to put into target opponent's graveyard")); - if (targetOpponent.isInGame()) { + if (targetOpponent.canRespond()) { if (cards.size() > 1) { controller.choose(Outcome.Detriment, cards, target, game); Card card = cards.get(target.getFirstTarget(), game); diff --git a/Mage.Sets/src/mage/cards/c/CruelFeeding.java b/Mage.Sets/src/mage/cards/c/CruelFeeding.java index 4eeb5bc6f1..7a97e238bd 100644 --- a/Mage.Sets/src/mage/cards/c/CruelFeeding.java +++ b/Mage.Sets/src/mage/cards/c/CruelFeeding.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -13,26 +11,27 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CruelFeeding extends CardImpl { public CruelFeeding(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // Strive - Cruel Feeding costs {2}{B} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{B}")); + // Any number of target creatures each get +1/+0 and gain lifelink until end of turn. - Effect effect = new BoostTargetEffect(1,0,Duration.EndOfTurn); + Effect effect = new BoostTargetEffect(1, 0, Duration.EndOfTurn); effect.setText("Any number of target creatures each get +1/+0"); this.getSpellAbility().addEffect(effect); effect = new GainAbilityTargetEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn); effect.setText("and gain lifelink until end of turn"); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(0,Integer.MAX_VALUE)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE)); } public CruelFeeding(final CruelFeeding card) { diff --git a/Mage.Sets/src/mage/cards/c/Crumble.java b/Mage.Sets/src/mage/cards/c/Crumble.java index 90ead36ee1..00fedafe61 100644 --- a/Mage.Sets/src/mage/cards/c/Crumble.java +++ b/Mage.Sets/src/mage/cards/c/Crumble.java @@ -1,5 +1,6 @@ package mage.cards.c; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -12,8 +13,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetArtifactPermanent; -import java.util.UUID; - /** * @author fireshoes */ @@ -58,9 +57,7 @@ class CrumbleEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // If the target artifact becomes illegal before resolution, the player does not gain any life. - // (2004-10-04) - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); // must use LKI + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); // must use LKI if (permanent != null) { int cost = permanent.getConvertedManaCost(); Player player = game.getPlayer(permanent.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/c/CrusherZendikon.java b/Mage.Sets/src/mage/cards/c/CrusherZendikon.java index f8702b9551..474077c559 100644 --- a/Mage.Sets/src/mage/cards/c/CrusherZendikon.java +++ b/Mage.Sets/src/mage/cards/c/CrusherZendikon.java @@ -42,7 +42,7 @@ public final class CrusherZendikon extends CardImpl { this.addAbility(ability); // Enchanted land is a 4/2 red Beast creature with trample. It's still a land. Ability ability2 = new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesCreatureAttachedEffect( - new BeastToken(), "Enchanted land is a 4/2 red Beast creature with trample. It's still a land.", Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.COLOR)); + new CrusherZendikonToken(), "Enchanted land is a 4/2 red Beast creature with trample. It's still a land.", Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.COLOR)); this.addAbility(ability2); // When enchanted land dies, return that card to its owner's hand. Ability ability3 = new DiesAttachedTriggeredAbility(new ReturnToHandAttachedEffect(), "enchanted land", false); @@ -59,9 +59,9 @@ public final class CrusherZendikon extends CardImpl { } } -class BeastToken extends TokenImpl { +class CrusherZendikonToken extends TokenImpl { - BeastToken() { + CrusherZendikonToken() { super("", "4/2 red Beast creature with trample"); cardType.add(CardType.CREATURE); color.setRed(true); @@ -70,11 +70,11 @@ class BeastToken extends TokenImpl { toughness = new MageInt(2); this.addAbility(TrampleAbility.getInstance()); } - public BeastToken(final BeastToken token) { + public CrusherZendikonToken(final CrusherZendikonToken token) { super(token); } - public BeastToken copy() { - return new BeastToken(this); + public CrusherZendikonToken copy() { + return new CrusherZendikonToken(this); } } diff --git a/Mage.Sets/src/mage/cards/c/CryptLurker.java b/Mage.Sets/src/mage/cards/c/CryptLurker.java new file mode 100644 index 0000000000..153d25dbef --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CryptLurker.java @@ -0,0 +1,52 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.OrCost; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CryptLurker extends CardImpl { + + public CryptLurker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.HORROR); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When Crypt Lurker enters the battlefield, you may sacrifice a creature or discard a creature card. If you do, draw a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), + new OrCost( + new SacrificeTargetCost(new TargetControlledPermanent( + StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT + )), new DiscardTargetCost(new TargetCardInHand(StaticFilters.FILTER_CARD_CREATURE_A)), + "sacrifice a creature or discard a creature card" + ) + ))); + } + + private CryptLurker(final CryptLurker card) { + super(card); + } + + @Override + public CryptLurker copy() { + return new CryptLurker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CrypticCommand.java b/Mage.Sets/src/mage/cards/c/CrypticCommand.java index 0e18a518ba..53e291976e 100644 --- a/Mage.Sets/src/mage/cards/c/CrypticCommand.java +++ b/Mage.Sets/src/mage/cards/c/CrypticCommand.java @@ -1,9 +1,9 @@ - package mage.cards.c; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -27,18 +27,21 @@ import mage.target.TargetSpell; public final class CrypticCommand extends CardImpl { public CrypticCommand(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{U}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}{U}"); // Choose two - this.getSpellAbility().getModes().setMinModes(2); this.getSpellAbility().getModes().setMaxModes(2); // Counter target spell; - this.getSpellAbility().addEffect(new CounterTargetEffect()); + Effect effect1 = new CounterTargetEffect(); + effect1.setText("Counter target spell."); + this.getSpellAbility().addEffect(effect1); this.getSpellAbility().addTarget(new TargetSpell()); // or return target permanent to its owner's hand; Mode mode = new Mode(); - mode.addEffect(new ReturnToHandTargetEffect()); + Effect effect2 = new ReturnToHandTargetEffect(); + effect2.setText("Return target permanent to its owner's hand."); + mode.addEffect(effect2); mode.addTarget(new TargetPermanent()); this.getSpellAbility().getModes().addMode(mode); // or tap all creatures your opponents control; @@ -47,7 +50,9 @@ public final class CrypticCommand extends CardImpl { this.getSpellAbility().getModes().addMode(mode); // or draw a card. mode = new Mode(); - mode.addEffect(new DrawCardSourceControllerEffect(1)); + Effect effect3 = new DrawCardSourceControllerEffect(1); + mode.addEffect(effect3); + effect3.setText("Draw a card."); this.getSpellAbility().getModes().addMode(mode); } @@ -64,13 +69,14 @@ public final class CrypticCommand extends CardImpl { class CrypticCommandEffect extends OneShotEffect { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); + static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(TargetController.OPPONENT.getControllerPredicate()); } public CrypticCommandEffect() { super(Outcome.Tap); - staticText = "tap all creatures your opponents control"; + staticText = "Tap all creatures your opponents control"; } public CrypticCommandEffect(final CrypticCommandEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CrypticSerpent.java b/Mage.Sets/src/mage/cards/c/CrypticSerpent.java index fb28357301..bf49861457 100644 --- a/Mage.Sets/src/mage/cards/c/CrypticSerpent.java +++ b/Mage.Sets/src/mage/cards/c/CrypticSerpent.java @@ -1,10 +1,11 @@ package mage.cards.c; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; -import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -12,7 +13,6 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterInstantOrSorceryCard; import java.util.UUID; @@ -21,8 +21,6 @@ import java.util.UUID; */ public final class CrypticSerpent extends CardImpl { - private static final DynamicValue cardsCount = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); - public CrypticSerpent(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); @@ -31,8 +29,11 @@ public final class CrypticSerpent extends CardImpl { this.toughness = new MageInt(5); // Cryptic Serpent costs {1} less to cast for each instant and sorcery card in your graveyard. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(new FilterInstantOrSorceryCard())) - .addHint(new ValueHint("Instant and sorcery card in your graveyard", cardsCount))); + DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)); + ability.setRuleAtTheTop(true); + ability.addHint(new ValueHint("Instant and sorcery card in your graveyard", xValue)); + this.addAbility(ability); } public CrypticSerpent(final CrypticSerpent card) { diff --git a/Mage.Sets/src/mage/cards/c/CrypticTrilobite.java b/Mage.Sets/src/mage/cards/c/CrypticTrilobite.java new file mode 100644 index 0000000000..40bbb7587c --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrypticTrilobite.java @@ -0,0 +1,107 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.ConditionalMana; +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.mana.ConditionalColorlessManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.abilities.mana.conditional.ManaCondition; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AbilityType; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public final class CrypticTrilobite extends CardImpl { + + public CrypticTrilobite(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{X}"); + + this.subtype.add(SubType.TRILOBITE); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Cryptic Trilobite enters the battlefield with X +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility( + new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()) + )); + + // Remove a +1/+1 counter from Cryptic Trilobite: Add {C}{C}. Spend this mana only to activate abilities. + this.addAbility(new ConditionalColorlessManaAbility( + new RemoveCountersSourceCost(CounterType.P1P1.createInstance()), + 2, new CrypticTrilobiteManaBuilder() + )); + + // {1}, {T}: Put a +1/+1 counter on Cryptic Trilobite. + Ability ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private CrypticTrilobite(final CrypticTrilobite card) { + super(card); + } + + @Override + public CrypticTrilobite copy() { + return new CrypticTrilobite(this); + } +} + +class CrypticTrilobiteManaBuilder extends ConditionalManaBuilder { + + @Override + public ConditionalMana build(Object... options) { + return new CrypticTrilobiteConditionalMana(this.mana); + } + + @Override + public String getRule() { + return "Spend this mana only to activate abilities"; + } +} + +class CrypticTrilobiteConditionalMana extends ConditionalMana { + + CrypticTrilobiteConditionalMana(Mana mana) { + super(mana); + staticText = "Spend this mana only to activate abilities"; + addCondition(new CrypticTrilobiteManaCondition()); + } +} + +class CrypticTrilobiteManaCondition extends ManaCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + if (source != null) { + // ex: SimpleManaAbility is an ACTIVATED ability, but it is categorized as a MANA ability + return source.getAbilityType() == AbilityType.MANA + || source.getAbilityType() == AbilityType.ACTIVATED; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source, UUID originalId, Cost costsToPay) { + return apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/c/Crystacean.java b/Mage.Sets/src/mage/cards/c/Crystacean.java new file mode 100644 index 0000000000..8936071ac3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Crystacean.java @@ -0,0 +1,36 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Crystacean extends CardImpl { + + public Crystacean(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.CRAB); + this.power = new MageInt(1); + this.toughness = new MageInt(6); + + // Flash + this.addAbility(FlashAbility.getInstance()); + } + + private Crystacean(final Crystacean card) { + super(card); + } + + @Override + public Crystacean copy() { + return new Crystacean(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CrystallineCrawler.java b/Mage.Sets/src/mage/cards/c/CrystallineCrawler.java index fcf5ff90bc..3ea9e497e2 100644 --- a/Mage.Sets/src/mage/cards/c/CrystallineCrawler.java +++ b/Mage.Sets/src/mage/cards/c/CrystallineCrawler.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -8,6 +7,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.dynamicvalue.common.ColorsOfManaSpentToCastCount; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; @@ -30,13 +30,14 @@ public final class CrystallineCrawler extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - // Converge — Crystalline Crawler enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. + // Converge - Crystalline Crawler enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(), ColorsOfManaSpentToCastCount.getInstance(), true), null, "Converge — {this} enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.", null)); // Remove a +1/+1 counter from Crystalline Crawler: Add one mana of any color. - this.addAbility(new AnyColorManaAbility(new RemoveCountersSourceCost(CounterType.P1P1.createInstance(1)))); + this.addAbility(new AnyColorManaAbility(new RemoveCountersSourceCost(CounterType.P1P1.createInstance(1)), + new CountersSourceCount(CounterType.P1P1), false)); // {T}: Put a +1/+1 counter on Crystalline Crawler. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), new TapSourceCost())); diff --git a/Mage.Sets/src/mage/cards/c/CrystallineGiant.java b/Mage.Sets/src/mage/cards/c/CrystallineGiant.java new file mode 100644 index 0000000000..369d9b25c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrystallineGiant.java @@ -0,0 +1,102 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.counters.Counters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.util.RandomUtil; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CrystallineGiant extends CardImpl { + + public CrystallineGiant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.GIANT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant. + this.addAbility(new BeginningOfCombatTriggeredAbility( + new CrystallineGiantEffect(), TargetController.YOU, false + )); + } + + private CrystallineGiant(final CrystallineGiant card) { + super(card); + } + + @Override + public CrystallineGiant copy() { + return new CrystallineGiant(this); + } +} + +class CrystallineGiantEffect extends OneShotEffect { + + private static final EnumSet counterTypeSet = EnumSet.noneOf(CounterType.class); + + static { + counterTypeSet.add(CounterType.FLYING); + counterTypeSet.add(CounterType.FIRST_STRIKE); + counterTypeSet.add(CounterType.DEATHTOUCH); + counterTypeSet.add(CounterType.HEXPROOF); + counterTypeSet.add(CounterType.LIFELINK); + counterTypeSet.add(CounterType.MENACE); + counterTypeSet.add(CounterType.REACH); + counterTypeSet.add(CounterType.TRAMPLE); + counterTypeSet.add(CounterType.VIGILANCE); + counterTypeSet.add(CounterType.P1P1); + } + + CrystallineGiantEffect() { + super(Outcome.Benefit); + staticText = "choose a kind of counter at random that {this} doesn't have on it from among " + + "flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance or +1/+1. " + + "Put a counter of that kind on {this}"; + } + + private CrystallineGiantEffect(final CrystallineGiantEffect effect) { + super(effect); + } + + @Override + public CrystallineGiantEffect copy() { + return new CrystallineGiantEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return false; + } + Counters counters = permanent.getCounters(game); + List counterTypes = new ArrayList(); + counterTypes.addAll(counterTypeSet); + counterTypes.removeIf(counters::containsKey); + if (counterTypes.isEmpty()) { + return true; + } + return permanent.addCounters(counterTypes.get( + RandomUtil.nextInt(counterTypes.size()) + ).createInstance(), source, game); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/CrystallineResonance.java b/Mage.Sets/src/mage/cards/c/CrystallineResonance.java new file mode 100644 index 0000000000..a9f78614e6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrystallineResonance.java @@ -0,0 +1,76 @@ +package mage.cards.c; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.effects.common.CopyPermanentEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.util.functions.ApplyToPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CrystallineResonance extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("another permanent"); + + static { + filter.add(AnotherPredicate.instance); + } + + public CrystallineResonance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + // Whenever you cycle a card, you may have Crystalline Resonance become a copy of another target permanent until your next turn, except it has this ability. + this.addAbility(CrystallineResonance.createAbility()); + } + + private CrystallineResonance(final CrystallineResonance card) { + super(card); + } + + @Override + public CrystallineResonance copy() { + return new CrystallineResonance(this); + } + + static Ability createAbility() { + Ability ability = new CycleControllerTriggeredAbility( + new CopyPermanentEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, + new CrystallineResonanceApplier(), true + ).setDuration(Duration.UntilYourNextTurn).setText( + "have {this} become a copy of another target permanent until your next turn, " + + "except it has this ability" + ), true + ); + ability.addTarget(new TargetPermanent(filter)); + return ability; + } +} + +class CrystallineResonanceApplier extends ApplyToPermanent { + + @Override + public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { + permanent.getAbilities().add(CrystallineResonance.createAbility()); + return true; + } + + @Override + public boolean apply(Game game, MageObject mageObject, Ability source, UUID copyToObjectId) { + mageObject.getAbilities().add(CrystallineResonance.createAbility()); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/Cubwarden.java b/Mage.Sets/src/mage/cards/c/Cubwarden.java new file mode 100644 index 0000000000..0d5daa788d --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Cubwarden.java @@ -0,0 +1,46 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.CatToken2; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Cubwarden extends CardImpl { + + public Cubwarden(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Mutate {2}{W}{W} + this.addAbility(new MutateAbility(this, "{2}{W}{W}")); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Whenever this creature mutates, create two 1/1 white Cat creature tokens with lifelink. + this.addAbility(new MutatesSourceTriggeredAbility(new CreateTokenEffect(new CatToken2(), 2))); + } + + private Cubwarden(final Cubwarden card) { + super(card); + } + + @Override + public Cubwarden copy() { + return new Cubwarden(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CullingDais.java b/Mage.Sets/src/mage/cards/c/CullingDais.java index ba08cbdb22..7a26b00aa8 100644 --- a/Mage.Sets/src/mage/cards/c/CullingDais.java +++ b/Mage.Sets/src/mage/cards/c/CullingDais.java @@ -66,7 +66,7 @@ class CullingDaisEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); if (p != null && player != null) { int count = p.getCounters(game).getCount(CounterType.CHARGE); - player.drawCards(count, game); + player.drawCards(count, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/c/CulturalExchange.java b/Mage.Sets/src/mage/cards/c/CulturalExchange.java index 892f003901..c7b9c98b8f 100644 --- a/Mage.Sets/src/mage/cards/c/CulturalExchange.java +++ b/Mage.Sets/src/mage/cards/c/CulturalExchange.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -20,8 +18,9 @@ import mage.target.TargetPlayer; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class CulturalExchange extends CardImpl { diff --git a/Mage.Sets/src/mage/cards/c/Cunning.java b/Mage.Sets/src/mage/cards/c/Cunning.java index 505c10898a..3cace5396b 100644 --- a/Mage.Sets/src/mage/cards/c/Cunning.java +++ b/Mage.Sets/src/mage/cards/c/Cunning.java @@ -1,89 +1,89 @@ -package mage.cards.c; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.AttacksOrBlocksEnchantedTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.delayed.AtTheBeginOfNextCleanupDelayedTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.SacrificeSourceEffect; -import mage.abilities.effects.common.continuous.BoostEnchantedEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author jeffwadsworth - */ -public final class Cunning extends CardImpl { - - public Cunning(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // Enchanted creature gets +3/+3. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(3, 3))); - - // When enchanted creature attacks or blocks, sacrifice Cunning at the beginning of the next cleanup step. - this.addAbility(new AttacksOrBlocksEnchantedTriggeredAbility(Zone.BATTLEFIELD, - new SacrificeSourceBeginningCleanupStepEffect())); - } - - public Cunning(final Cunning card) { - super(card); - } - - @Override - public Cunning copy() { - return new Cunning(this); - } -} - -class SacrificeSourceBeginningCleanupStepEffect extends OneShotEffect { - - public SacrificeSourceBeginningCleanupStepEffect() { - super(Outcome.Sacrifice); - this.staticText = "sacrifice {this} at the beginning of the next cleanup step"; - } - - public SacrificeSourceBeginningCleanupStepEffect(final SacrificeSourceBeginningCleanupStepEffect effect) { - super(effect); - } - - @Override - public SacrificeSourceBeginningCleanupStepEffect copy() { - return new SacrificeSourceBeginningCleanupStepEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent cunning = game.getPermanent(source.getSourceId()); - if (cunning != null) { - DelayedTriggeredAbility delayedAbility - = new AtTheBeginOfNextCleanupDelayedTriggeredAbility( - new SacrificeSourceEffect()); - game.addDelayedTriggeredAbility(delayedAbility, source); - return true; - } - return false; - } -} +package mage.cards.c; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.AttacksOrBlocksEnchantedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextCleanupDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author jeffwadsworth + */ +public final class Cunning extends CardImpl { + + public Cunning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +3/+3. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(3, 3))); + + // When enchanted creature attacks or blocks, sacrifice Cunning at the beginning of the next cleanup step. + this.addAbility(new AttacksOrBlocksEnchantedTriggeredAbility(Zone.BATTLEFIELD, + new SacrificeSourceBeginningCleanupStepEffect())); + } + + public Cunning(final Cunning card) { + super(card); + } + + @Override + public Cunning copy() { + return new Cunning(this); + } +} + +class SacrificeSourceBeginningCleanupStepEffect extends OneShotEffect { + + public SacrificeSourceBeginningCleanupStepEffect() { + super(Outcome.Sacrifice); + this.staticText = "sacrifice {this} at the beginning of the next cleanup step"; + } + + public SacrificeSourceBeginningCleanupStepEffect(final SacrificeSourceBeginningCleanupStepEffect effect) { + super(effect); + } + + @Override + public SacrificeSourceBeginningCleanupStepEffect copy() { + return new SacrificeSourceBeginningCleanupStepEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent cunning = game.getPermanent(source.getSourceId()); + if (cunning != null) { + DelayedTriggeredAbility delayedAbility + = new AtTheBeginOfNextCleanupDelayedTriggeredAbility( + new SacrificeSourceEffect()); + game.addDelayedTriggeredAbility(delayedAbility, source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CunningNightbonder.java b/Mage.Sets/src/mage/cards/c/CunningNightbonder.java new file mode 100644 index 0000000000..ce35b6af8f --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CunningNightbonder.java @@ -0,0 +1,62 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CantBeCounteredControlledEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CunningNightbonder extends CardImpl { + + private static final FilterCard filter = new FilterCard(); + private static final FilterSpell filter2 = new FilterSpell(); + private static final Predicate predicate = new AbilityPredicate(FlashAbility.class); + + static { + filter.add(predicate); + filter2.add(predicate); + } + + public CunningNightbonder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U/B}{U/B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Spells you cast with flash cost {1} less to cast and can't be countered. + Ability ability = new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1) + .setText("spells with flash you cast cost {1} less to cast")); + ability.addEffect(new CantBeCounteredControlledEffect(filter2, Duration.WhileOnBattlefield) + .setText("and can't be countered")); + this.addAbility(ability); + } + + private CunningNightbonder(final CunningNightbonder card) { + super(card); + } + + @Override + public CunningNightbonder copy() { + return new CunningNightbonder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CuriousHerd.java b/Mage.Sets/src/mage/cards/c/CuriousHerd.java new file mode 100644 index 0000000000..e2812e6e37 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CuriousHerd.java @@ -0,0 +1,69 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.BeastToken; +import mage.game.permanent.token.Token; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CuriousHerd extends CardImpl { + + public CuriousHerd(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{G}"); + + // Choose target opponent. You create X 3/3 green Beast creature tokens, where X is the number of artifacts that player controls. + this.getSpellAbility().addEffect(new CuriousHerdEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + private CuriousHerd(final CuriousHerd card) { + super(card); + } + + @Override + public CuriousHerd copy() { + return new CuriousHerd(this); + } +} + +class CuriousHerdEffect extends OneShotEffect { + + private static final Token token = new BeastToken(); + + CuriousHerdEffect() { + super(Outcome.Benefit); + staticText = "Choose target opponent. You create X 3/3 green Beast creature tokens, " + + "where X is the number of artifacts that player controls."; + } + + private CuriousHerdEffect(final CuriousHerdEffect effect) { + super(effect); + } + + @Override + public CuriousHerdEffect copy() { + return new CuriousHerdEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int artifactCount = game.getBattlefield().countAll( + StaticFilters.FILTER_PERMANENT_ARTIFACT, source.getFirstTarget(), game + ); + if (artifactCount > 0) { + token.putOntoBattlefield(artifactCount, game, source.getSourceId(), source.getControllerId()); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/CurseOfChaos.java b/Mage.Sets/src/mage/cards/c/CurseOfChaos.java index d489242939..8031db361f 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfChaos.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfChaos.java @@ -115,7 +115,7 @@ class CurseOfChaosEffect extends OneShotEffect { if (attacker != null) { if (!attacker.getHand().isEmpty() && attacker.chooseUse(outcome, "Discard a card and draw a card?", source, game)) { attacker.discard(1, false, source, game); - attacker.drawCards(1, game); + attacker.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java b/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java index 1d12b1daf5..b1c0bd8ba6 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java @@ -88,6 +88,6 @@ class CurseOfTheBloodyTomeAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "At the beginning of enchanted player's upkeep, that player puts the top two cards of their library into their graveyard."; + return "At the beginning of enchanted player's upkeep, that player mills two cards."; } } diff --git a/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java b/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java index e12fb3e6da..fe1af03565 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java @@ -157,7 +157,7 @@ class CurseOfVengeanceDrawLifeEffect extends OneShotEffect { Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourceObject != null && controller != null) { if (sourceObject.getCounters(game).containsKey(CounterType.SPITE)) { - controller.drawCards(sourceObject.getCounters(game).getCount(CounterType.SPITE), game); + controller.drawCards(sourceObject.getCounters(game).getCount(CounterType.SPITE), source.getSourceId(), game); controller.gainLife(sourceObject.getCounters(game).getCount(CounterType.SPITE), game, source); } return true; diff --git a/Mage.Sets/src/mage/cards/c/CursedScroll.java b/Mage.Sets/src/mage/cards/c/CursedScroll.java index 70d1e5f048..6ed0c99568 100644 --- a/Mage.Sets/src/mage/cards/c/CursedScroll.java +++ b/Mage.Sets/src/mage/cards/c/CursedScroll.java @@ -70,7 +70,7 @@ class CursedScrollEffect extends OneShotEffect { } revealed.add(card); controller.revealCards(sourceObject.getIdName(), revealed, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { Permanent creature = game.getPermanent(targetPointer.getFirst(game, source)); if (creature != null) { creature.damage(2, source.getSourceId(), game, false, true); diff --git a/Mage.Sets/src/mage/cards/c/CurtainOfLight.java b/Mage.Sets/src/mage/cards/c/CurtainOfLight.java index 5f1e7056ef..027eeae339 100644 --- a/Mage.Sets/src/mage/cards/c/CurtainOfLight.java +++ b/Mage.Sets/src/mage/cards/c/CurtainOfLight.java @@ -1,34 +1,28 @@ - package mage.cards.c; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; import mage.abilities.condition.common.AfterBlockersAreDeclaredCondition; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.BecomeBlockedTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.TurnPhase; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.AttackingPredicate; import mage.filter.predicate.permanent.BlockedPredicate; -import mage.game.Game; -import mage.game.combat.CombatGroup; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CurtainOfLight extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("unblocked attacking creature"); + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("unblocked attacking creature"); static { filter.add(AttackingPredicate.instance); @@ -39,17 +33,19 @@ public final class CurtainOfLight extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); // Cast Curtain of Light only during combat after blockers are declared. - this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(TurnPhase.COMBAT, AfterBlockersAreDeclaredCondition.instance)); + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, AfterBlockersAreDeclaredCondition.instance + )); // Target unblocked attacking creature becomes blocked. - this.getSpellAbility().addEffect(new CurtainOfLightEffect()); + this.getSpellAbility().addEffect(new BecomeBlockedTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } - public CurtainOfLight(final CurtainOfLight card) { + private CurtainOfLight(final CurtainOfLight card) { super(card); } @@ -58,35 +54,3 @@ public final class CurtainOfLight extends CardImpl { return new CurtainOfLight(this); } } - -class CurtainOfLightEffect extends OneShotEffect { - - public CurtainOfLightEffect() { - super(Outcome.Benefit); - this.staticText = "Target unblocked attacking creature becomes blocked"; - } - - public CurtainOfLightEffect(final CurtainOfLightEffect effect) { - super(effect); - } - - @Override - public CurtainOfLightEffect copy() { - return new CurtainOfLightEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (controller != null && permanent != null) { - CombatGroup combatGroup = game.getCombat().findGroup(permanent.getId()); - if (combatGroup != null) { - combatGroup.setBlocked(true, game); - game.informPlayers(permanent.getLogName() + " has become blocked"); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java b/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java index ab23d88f7d..968e1360af 100644 --- a/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java +++ b/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; @@ -21,19 +19,21 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** - * * @author emerald000 */ public final class CustodiSoulbinders extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("other creatures"); + static { filter.add(AnotherPredicate.instance); } public CustodiSoulbinders(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.CLERIC); this.power = new MageInt(0); @@ -42,14 +42,14 @@ public final class CustodiSoulbinders extends CardImpl { // Custodi Soulbinders enters the battlefield with X +1/+1 counters on it, where X is the number of other creatures on the battlefield. this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect( - CounterType.P1P1.createInstance(), - new PermanentsOnBattlefieldCount(filter), - false), + CounterType.P1P1.createInstance(), + new PermanentsOnBattlefieldCount(filter), + false), "with X +1/+1 counters on it, where X is the number of other creatures on the battlefield")); - + // {2}{W}, Remove a +1/+1 counter from Custodi Soulbinders: Create a 1/1 white Spirit creature token with flying. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken("CNS")), new ManaCostsImpl<>("{2}{W}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken()), new ManaCostsImpl<>("{2}{W}")); ability.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CycloneSire.java b/Mage.Sets/src/mage/cards/c/CycloneSire.java index ea97032c91..10b665840e 100644 --- a/Mage.Sets/src/mage/cards/c/CycloneSire.java +++ b/Mage.Sets/src/mage/cards/c/CycloneSire.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -34,7 +34,7 @@ public final class CycloneSire extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Cyclone Sire dies, you may put three +1/+1 counters on target land you control. If you do, that land becomes a 0/0 Elemental creature with haste that's still a land. - Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance(3)), true); + Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance(3)), true); Effect effect = new BecomesCreatureTargetEffect(new WallOfResurgenceToken(), false, true, Duration.Custom); effect.setText("If you do, that land becomes a 0/0 Elemental creature with haste that's still a land"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/c/CyclopeanGiant.java b/Mage.Sets/src/mage/cards/c/CyclopeanGiant.java index 1c04cdb2a6..0842dcaa94 100644 --- a/Mage.Sets/src/mage/cards/c/CyclopeanGiant.java +++ b/Mage.Sets/src/mage/cards/c/CyclopeanGiant.java @@ -2,7 +2,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; import mage.constants.Duration; @@ -27,7 +27,7 @@ public final class CyclopeanGiant extends CardImpl { this.toughness = new MageInt(2); // When Cyclopean Giant dies, target land becomes a Swamp. Exile Cyclopean Giant. - DiesTriggeredAbility ability = new DiesTriggeredAbility(new BecomesBasicLandTargetEffect(Duration.EndOfGame, SubType.SWAMP)); + DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(new BecomesBasicLandTargetEffect(Duration.EndOfGame, SubType.SWAMP)); ability.addEffect(new ExileSourceEffect()); ability.addTarget(new TargetLandPermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/CyclopeanMummy.java b/Mage.Sets/src/mage/cards/c/CyclopeanMummy.java index aa72a16878..e8f6458b48 100644 --- a/Mage.Sets/src/mage/cards/c/CyclopeanMummy.java +++ b/Mage.Sets/src/mage/cards/c/CyclopeanMummy.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ExileSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class CyclopeanMummy extends CardImpl { this.toughness = new MageInt(1); // When Cyclopean Mummy dies, exile it. - this.addAbility(new DiesTriggeredAbility(new ExileSourceEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new ExileSourceEffect())); } public CyclopeanMummy(final CyclopeanMummy card) { diff --git a/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java b/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java index 95643caea0..6b637fda4c 100644 --- a/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java +++ b/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java @@ -120,7 +120,7 @@ class CyclopeanTombCreateTriggeredEffect extends OneShotEffect { DelayedTriggeredAbility ability = new AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility(new CyclopeanTombEffect(), Duration.EndOfGame, false); ability.setControllerId(source.getControllerId()); ability.setSourceId(source.getSourceId()); - game.addDelayedTriggeredAbility(ability); + game.addDelayedTriggeredAbility(ability, source); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java b/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java index fa4db9dfc2..4954ae6e4a 100644 --- a/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java +++ b/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java @@ -6,6 +6,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.common.SourceOnBattlefieldCondition; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -47,7 +48,7 @@ public final class CytoplastManipulator extends CardImpl { // {U}, {tap}: Gain control of target creature with a +1/+1 counter on it for as long as Cytoplast Manipulator remains on the battlefield. ConditionalContinuousEffect effect = new ConditionalContinuousEffect( new GainControlTargetEffect(Duration.Custom, true), - SourceOnBattlefieldCondition.instance, + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), "gain control of target creature with a +1/+1 counter on it for as long as {this} remains on the battlefield"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{U}")); ability.addCost(new TapSourceCost()); diff --git a/Mage.Sets/src/mage/cards/d/DacksDuplicate.java b/Mage.Sets/src/mage/cards/d/DacksDuplicate.java index 646597f79c..ef7b040837 100644 --- a/Mage.Sets/src/mage/cards/d/DacksDuplicate.java +++ b/Mage.Sets/src/mage/cards/d/DacksDuplicate.java @@ -56,8 +56,8 @@ class DacksDuplicateApplyToPermanent extends ApplyToPermanent { * 29/05/2014 The ability of Dack's Duplicate doesn't target the * creature. */ - permanent.addAbility(new DethroneAbility(), game); - permanent.addAbility(HasteAbility.getInstance(), game); + permanent.addAbility(new DethroneAbility(), source.getSourceId(), game); + permanent.addAbility(HasteAbility.getInstance(), source.getSourceId(), game); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DakmorGhoul.java b/Mage.Sets/src/mage/cards/d/DakmorGhoul.java index 8afa10f66a..34e559e459 100644 --- a/Mage.Sets/src/mage/cards/d/DakmorGhoul.java +++ b/Mage.Sets/src/mage/cards/d/DakmorGhoul.java @@ -1,11 +1,8 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; @@ -14,24 +11,23 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class DakmorGhoul extends CardImpl { public DakmorGhoul(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(2); this.toughness = new MageInt(2); // When Dakmor Ghoul enters the battlefield, target opponent loses 2 life and you gain 2 life. - Effect effect = new GainLifeEffect(2); - effect.setText("and you gain 2 life"); Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(2), false); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); ability.addTarget(new TargetOpponent()); - ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DamnablePact.java b/Mage.Sets/src/mage/cards/d/DamnablePact.java index 71d91680b4..33b8f0b960 100644 --- a/Mage.Sets/src/mage/cards/d/DamnablePact.java +++ b/Mage.Sets/src/mage/cards/d/DamnablePact.java @@ -52,7 +52,7 @@ class DamnablePactEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); if (targetPlayer != null) { - targetPlayer.drawCards(source.getManaCostsToPay().getX(), game); + targetPlayer.drawCards(source.getManaCostsToPay().getX(), source.getSourceId(), game); targetPlayer.loseLife(source.getManaCostsToPay().getX(), game, false); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DampingEngine.java b/Mage.Sets/src/mage/cards/d/DampingEngine.java index 573f507d55..18546501f4 100644 --- a/Mage.Sets/src/mage/cards/d/DampingEngine.java +++ b/Mage.Sets/src/mage/cards/d/DampingEngine.java @@ -1,207 +1,207 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ -package mage.cards.d; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.SpecialAction; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; -import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetControlledPermanent; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public class DampingEngine extends CardImpl { - - public DampingEngine(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - - // A player who controls more permanents than each other player can't play lands or cast artifact, creature, or enchantment spells. That player may sacrifice a permanent for that player to ignore this effect until end of turn. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DampingEngineEffect())); - this.addAbility(new DampingEngineSpecialAction()); - - } - - public DampingEngine(final DampingEngine card) { - super(card); - } - - @Override - public DampingEngine copy() { - return new DampingEngine(this); - } -} - -class DampingEngineEffect extends ContinuousRuleModifyingEffectImpl { - - public DampingEngineEffect() { - super(Duration.WhileOnBattlefield, Outcome.AIDontUseIt); - staticText = "A player who controls more permanents than each other player can't play lands or cast artifact, creature, or enchantment spells" - + "That player may sacrifice a permanent for that player to ignore this effect until end of turn.

"; - } - - public DampingEngineEffect(final DampingEngineEffect effect) { - super(effect); - } - - @Override - public DampingEngineEffect copy() { - return new DampingEngineEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public String getInfoMessage(Ability source, GameEvent event, Game game) { - MageObject mageObject = game.getObject(source.getSourceId()); - if (mageObject != null) { - return "You can't play the land or cast the spell (" + mageObject.getName() + " in play)."; - } - return null; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.PLAY_LAND - || event.getType() == GameEvent.EventType.CAST_SPELL; - - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - Permanent dampingEngine = game.getPermanent(source.getSourceId()); - final Card card = game.getCard(event.getSourceId()); - if (player != null || card != null) { - // check type of spell - if (card.isCreature() - || card.isArtifact() - || card.isEnchantment() - || card.isLand()) { - // check to see if the player has more permanents - if (new ControlsMorePermanentsThanEachOtherPlayer(player).apply(game, source)) { - // check to see if the player choose to ignore the effect - return game.getState().getValue("ignoreEffect") == null - || dampingEngine == null - || !game.getState().getValue("ignoreEffect").equals - (dampingEngine.getId() + "ignoreEffect" + game.getState().getPriorityPlayerId() + game.getState().getTurnNum()); - } - } - } - return false; - } -} - -class DampingEngineSpecialAction extends SpecialAction { - - public DampingEngineSpecialAction() { - super(Zone.BATTLEFIELD); - this.addCost(new SacrificeTargetCost(new TargetControlledPermanent(), true)); - this.addEffect(new DampingEngineIgnoreEffect()); - this.setMayActivate(TargetController.ANY); - } - - public DampingEngineSpecialAction(final DampingEngineSpecialAction ability) { - super(ability); - } - - @Override - public DampingEngineSpecialAction copy() { - return new DampingEngineSpecialAction(this); - } -} - -class DampingEngineIgnoreEffect extends OneShotEffect { - - public DampingEngineIgnoreEffect() { - super(Outcome.AIDontUseIt); - this.staticText = "That player may sacrifice a permanent for that player to ignore this effect until end of turn"; - } - - public DampingEngineIgnoreEffect(final DampingEngineIgnoreEffect effect) { - super(effect); - } - - @Override - public DampingEngineIgnoreEffect copy() { - return new DampingEngineIgnoreEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - String key = permanent.getId() + "ignoreEffect" + game.getState().getPriorityPlayerId() + game.getState().getTurnNum(); - if (key != null) { - game.getState().setValue("ignoreEffect", key); - } - return true; - } -} - -class ControlsMorePermanentsThanEachOtherPlayer implements Condition { - - Player player; - - public ControlsMorePermanentsThanEachOtherPlayer(Player player) { - this.player = player; - } - - @Override - public boolean apply(Game game, Ability source) { - int numPermanents = game.getBattlefield().countAll(new FilterPermanent(), player.getId(), game); - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - if (numPermanents > game.getBattlefield().countAll(new FilterPermanent(), playerId, game)) { - return true; - } - } - return false; - } - - @Override - public String toString() { - return "a player controls less permanents than you"; - } -} +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.d; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.SpecialAction; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public class DampingEngine extends CardImpl { + + public DampingEngine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + // A player who controls more permanents than each other player can't play lands or cast artifact, creature, or enchantment spells. That player may sacrifice a permanent for that player to ignore this effect until end of turn. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DampingEngineEffect())); + this.addAbility(new DampingEngineSpecialAction()); + + } + + public DampingEngine(final DampingEngine card) { + super(card); + } + + @Override + public DampingEngine copy() { + return new DampingEngine(this); + } +} + +class DampingEngineEffect extends ContinuousRuleModifyingEffectImpl { + + public DampingEngineEffect() { + super(Duration.WhileOnBattlefield, Outcome.AIDontUseIt); + staticText = "A player who controls more permanents than each other player can't play lands or cast artifact, creature, or enchantment spells" + + "That player may sacrifice a permanent for that player to ignore this effect until end of turn.

"; + } + + public DampingEngineEffect(final DampingEngineEffect effect) { + super(effect); + } + + @Override + public DampingEngineEffect copy() { + return new DampingEngineEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public String getInfoMessage(Ability source, GameEvent event, Game game) { + MageObject mageObject = game.getObject(source.getSourceId()); + if (mageObject != null) { + return "You can't play the land or cast the spell (" + mageObject.getName() + " in play)."; + } + return null; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PLAY_LAND + || event.getType() == GameEvent.EventType.CAST_SPELL; + + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(event.getPlayerId()); + Permanent dampingEngine = game.getPermanent(source.getSourceId()); + final Card card = game.getCard(event.getSourceId()); + if (player != null || card != null) { + // check type of spell + if (card.isCreature() + || card.isArtifact() + || card.isEnchantment() + || card.isLand()) { + // check to see if the player has more permanents + if (new ControlsMorePermanentsThanEachOtherPlayer(player).apply(game, source)) { + // check to see if the player choose to ignore the effect + return game.getState().getValue("ignoreEffect") == null + || dampingEngine == null + || !game.getState().getValue("ignoreEffect").equals + (dampingEngine.getId() + "ignoreEffect" + game.getState().getPriorityPlayerId() + game.getState().getTurnNum()); + } + } + } + return false; + } +} + +class DampingEngineSpecialAction extends SpecialAction { + + public DampingEngineSpecialAction() { + super(Zone.BATTLEFIELD); + this.addCost(new SacrificeTargetCost(new TargetControlledPermanent(), true)); + this.addEffect(new DampingEngineIgnoreEffect()); + this.setMayActivate(TargetController.ANY); + } + + public DampingEngineSpecialAction(final DampingEngineSpecialAction ability) { + super(ability); + } + + @Override + public DampingEngineSpecialAction copy() { + return new DampingEngineSpecialAction(this); + } +} + +class DampingEngineIgnoreEffect extends OneShotEffect { + + public DampingEngineIgnoreEffect() { + super(Outcome.AIDontUseIt); + this.staticText = "That player may sacrifice a permanent for that player to ignore this effect until end of turn"; + } + + public DampingEngineIgnoreEffect(final DampingEngineIgnoreEffect effect) { + super(effect); + } + + @Override + public DampingEngineIgnoreEffect copy() { + return new DampingEngineIgnoreEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + String key = permanent.getId() + "ignoreEffect" + game.getState().getPriorityPlayerId() + game.getState().getTurnNum(); + if (key != null) { + game.getState().setValue("ignoreEffect", key); + } + return true; + } +} + +class ControlsMorePermanentsThanEachOtherPlayer implements Condition { + + Player player; + + public ControlsMorePermanentsThanEachOtherPlayer(Player player) { + this.player = player; + } + + @Override + public boolean apply(Game game, Ability source) { + int numPermanents = game.getBattlefield().countAll(new FilterPermanent(), player.getId(), game); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + if (numPermanents > game.getBattlefield().countAll(new FilterPermanent(), playerId, game)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "a player controls less permanents than you"; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DampingSphere.java b/Mage.Sets/src/mage/cards/d/DampingSphere.java index b4780d80d7..c2177cc690 100644 --- a/Mage.Sets/src/mage/cards/d/DampingSphere.java +++ b/Mage.Sets/src/mage/cards/d/DampingSphere.java @@ -1,19 +1,15 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageObject; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; +import mage.filter.FilterCard; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -21,8 +17,9 @@ import mage.game.events.ManaEvent; import mage.util.CardUtil; import mage.watchers.common.CastSpellLastTurnWatcher; +import java.util.UUID; + /** - * * @author L_J */ public final class DampingSphere extends CardImpl { @@ -90,10 +87,10 @@ class DampingSphereReplacementEffect extends ReplacementEffectImpl { } } -class DampingSphereIncreasementAllEffect extends SpellsCostIncreasementAllEffect { +class DampingSphereIncreasementAllEffect extends SpellsCostIncreasingAllEffect { DampingSphereIncreasementAllEffect() { - super(0); + super(1, new FilterCard(), TargetController.ANY); this.staticText = "Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn"; } diff --git a/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java b/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java index e5c70512b6..53dd4fb364 100644 --- a/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java +++ b/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java @@ -240,9 +240,7 @@ class DanceOfTheDeadChangeAbilityEffect extends ContinuousEffectImpl implements abilityToRemove = ability; } } - if (abilityToRemove != null) { - permanent.getAbilities().remove(abilityToRemove); - } + permanent.removeAbility(abilityToRemove, source.getSourceId(), game); permanent.addAbility(newAbility, source.getSourceId(), game); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DangerousWager.java b/Mage.Sets/src/mage/cards/d/DangerousWager.java index 1d328c8c06..1e98390b3e 100644 --- a/Mage.Sets/src/mage/cards/d/DangerousWager.java +++ b/Mage.Sets/src/mage/cards/d/DangerousWager.java @@ -1,34 +1,27 @@ - package mage.cards.d; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.cards.Card; +import mage.abilities.effects.common.discard.DiscardHandControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; + +import java.util.UUID; /** - * * @author North */ public final class DangerousWager extends CardImpl { public DangerousWager(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // Discard your hand, then draw two cards. - this.getSpellAbility().addEffect(new DangerousWagerEffect()); - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); + this.getSpellAbility().addEffect(new DiscardHandControllerEffect()); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText(", then draw two cards")); } - public DangerousWager(final DangerousWager card) { + private DangerousWager(final DangerousWager card) { super(card); } @@ -37,32 +30,3 @@ public final class DangerousWager extends CardImpl { return new DangerousWager(this); } } - -class DangerousWagerEffect extends OneShotEffect { - - public DangerousWagerEffect() { - super(Outcome.Discard); - this.staticText = "Discard your hand"; - } - - public DangerousWagerEffect(final DangerousWagerEffect effect) { - super(effect); - } - - @Override - public DangerousWagerEffect copy() { - return new DangerousWagerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - for (Card card : player.getHand().getCards(game)) { - player.discard(card, source, game); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DaredevilDragster.java b/Mage.Sets/src/mage/cards/d/DaredevilDragster.java index 112af1122a..d0dcc2315c 100644 --- a/Mage.Sets/src/mage/cards/d/DaredevilDragster.java +++ b/Mage.Sets/src/mage/cards/d/DaredevilDragster.java @@ -79,7 +79,7 @@ class DaredevilDragsterEffect extends OneShotEffect { permanent.addCounters(CounterType.VELOCITY.createInstance(), source, game); if (permanent.getCounters(game).getCount(CounterType.VELOCITY) >= 2) { permanent.sacrifice(source.getSourceId(), game); - controller.drawCards(2, game); + controller.drawCards(2, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java b/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java index 85a367404c..7f04fb3a77 100644 --- a/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java +++ b/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; @@ -14,12 +12,8 @@ import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffec import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.cards.CardsImpl; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterArtifactCard; import mage.filter.common.FilterControlledArtifactPermanent; @@ -33,8 +27,9 @@ import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetDiscard; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DarettiScrapSavant extends CardImpl { @@ -93,15 +88,8 @@ class DarettiDiscardDrawEffect extends OneShotEffect { if (controller != null) { TargetDiscard target = new TargetDiscard(0, 2, new FilterCard(), controller.getId()); target.choose(outcome, controller.getId(), source.getSourceId(), game); - int count = 0; - for (UUID cardId : target.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - controller.discard(card, source, game); - count++; - } - } - controller.drawCards(count, game); + int count = controller.discard(new CardsImpl(target.getTargets()), source, game).size(); + controller.drawCards(count, source.getSourceId(), game); return true; } return false; @@ -166,7 +154,7 @@ class DarettiScrapSavantEffect extends OneShotEffect { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return that card to the battlefield at the beginning of the next end step"); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.COMMAND, effect, TargetController.ANY), source); return true; diff --git a/Mage.Sets/src/mage/cards/d/DaringFiendbonder.java b/Mage.Sets/src/mage/cards/d/DaringFiendbonder.java new file mode 100644 index 0000000000..4c1682ae17 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DaringFiendbonder.java @@ -0,0 +1,59 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.AttacksEachCombatStaticAbility; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DaringFiendbonder extends CardImpl { + + public DaringFiendbonder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(5); + this.toughness = new MageInt(1); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Daring Fiendbonder attacks each combat if able. + this.addAbility(new AttacksEachCombatStaticAbility()); + + // {1}{B}, Exile Daring Fiendbonder from your graveyard: Put an indestructible counter on target creature. Activate this ability only any time you could cast a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + Zone.GRAVEYARD, + new AddCountersTargetEffect(CounterType.INDESTRUCTIBLE.createInstance()), + new ManaCostsImpl("{1}{B}") + ); + ability.addCost(new ExileSourceFromGraveCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private DaringFiendbonder(final DaringFiendbonder card) { + super(card); + } + + @Override + public DaringFiendbonder copy() { + return new DaringFiendbonder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DarkBargain.java b/Mage.Sets/src/mage/cards/d/DarkBargain.java index 769842cc37..f6fee752e7 100644 --- a/Mage.Sets/src/mage/cards/d/DarkBargain.java +++ b/Mage.Sets/src/mage/cards/d/DarkBargain.java @@ -44,7 +44,7 @@ class DarkBargainEffect extends OneShotEffect { public DarkBargainEffect() { super(Outcome.Benefit); - this.staticText = "Look at the top three cards of your library. Put two of them into your hand and the rest into your graveyard"; + this.staticText = "Look at the top three cards of your library. Put two of them into your hand and the other into your graveyard"; } public DarkBargainEffect(final DarkBargainEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DarkDabbling.java b/Mage.Sets/src/mage/cards/d/DarkDabbling.java index 5d60a57d60..a336174ebe 100644 --- a/Mage.Sets/src/mage/cards/d/DarkDabbling.java +++ b/Mage.Sets/src/mage/cards/d/DarkDabbling.java @@ -66,7 +66,7 @@ class DarkDabblingEffect extends OneShotEffect { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { if (!permanent.getId().equals(getTargetPointer().getFirst(game, source))) { RegenerateTargetEffect regenEffect = new RegenerateTargetEffect(); - regenEffect.setTargetPointer(new FixedTarget(permanent.getId())); + regenEffect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(regenEffect, source); } } diff --git a/Mage.Sets/src/mage/cards/d/DarkDeal.java b/Mage.Sets/src/mage/cards/d/DarkDeal.java index 1c81482bc1..e3d836b24c 100644 --- a/Mage.Sets/src/mage/cards/d/DarkDeal.java +++ b/Mage.Sets/src/mage/cards/d/DarkDeal.java @@ -5,7 +5,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; -import com.j256.ormlite.stmt.query.In; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -72,7 +71,7 @@ class DarkDealEffect extends OneShotEffect { for (Map.Entry toDrawByPlayer : cardsToDraw.entrySet()) { Player player = game.getPlayer(toDrawByPlayer.getKey()); if (player != null) { - player.drawCards(toDrawByPlayer.getValue(), game); + player.drawCards(toDrawByPlayer.getValue(), source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/d/DarkImpostor.java b/Mage.Sets/src/mage/cards/d/DarkImpostor.java index 268e22d04a..412a01f2a2 100644 --- a/Mage.Sets/src/mage/cards/d/DarkImpostor.java +++ b/Mage.Sets/src/mage/cards/d/DarkImpostor.java @@ -72,7 +72,7 @@ class DarkImpostorExileTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); MageObject sourceObject = source.getSourceObject(game); if (permanent != null) { permanent.moveToExile(null, null, source.getSourceId(), game); diff --git a/Mage.Sets/src/mage/cards/d/DarkIntimations.java b/Mage.Sets/src/mage/cards/d/DarkIntimations.java index d01a05305b..7c2973c477 100644 --- a/Mage.Sets/src/mage/cards/d/DarkIntimations.java +++ b/Mage.Sets/src/mage/cards/d/DarkIntimations.java @@ -128,7 +128,7 @@ class DarkIntimationsEffect extends OneShotEffect { } controller.moveCards(card, Zone.HAND, source, game); } - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DarkRevenant.java b/Mage.Sets/src/mage/cards/d/DarkRevenant.java index 6741b40a2b..58206923ac 100644 --- a/Mage.Sets/src/mage/cards/d/DarkRevenant.java +++ b/Mage.Sets/src/mage/cards/d/DarkRevenant.java @@ -4,12 +4,13 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; @@ -34,7 +35,7 @@ public final class DarkRevenant extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Dark Revenant dies, put it on top of its owner's library. - this.addAbility(new DiesTriggeredAbility(new DarkRevenantEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new DarkRevenantEffect())); } public DarkRevenant(final DarkRevenant card) { @@ -67,10 +68,9 @@ class DarkRevenantEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getSourceId()); if (card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { - Player owner = game.getPlayer(card.getOwnerId()); - if(owner != null) { - owner.getGraveyard().remove(card); - return card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + Player controller = game.getPlayer(source.getControllerId()); + if(controller != null) { + return controller.putCardsOnTopOfLibrary(card, game, source, true); } } return true; diff --git a/Mage.Sets/src/mage/cards/d/DarkslickDrake.java b/Mage.Sets/src/mage/cards/d/DarkslickDrake.java index 33d29cf7df..a47db43bfc 100644 --- a/Mage.Sets/src/mage/cards/d/DarkslickDrake.java +++ b/Mage.Sets/src/mage/cards/d/DarkslickDrake.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class DarkslickDrake extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(4); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } public DarkslickDrake (final DarkslickDrake card) { diff --git a/Mage.Sets/src/mage/cards/d/DaruWarchief.java b/Mage.Sets/src/mage/cards/d/DaruWarchief.java index 211b0173aa..abee47c84f 100644 --- a/Mage.Sets/src/mage/cards/d/DaruWarchief.java +++ b/Mage.Sets/src/mage/cards/d/DaruWarchief.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; @@ -15,8 +13,9 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class DaruWarchief extends CardImpl { @@ -30,7 +29,7 @@ public final class DaruWarchief extends CardImpl { } public DaruWarchief(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); @@ -39,6 +38,7 @@ public final class DaruWarchief extends CardImpl { // Soldier spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); + // Soldier creatures you control get +1/+2. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 2, Duration.WhileOnBattlefield, filterCreatures, false))); } diff --git a/Mage.Sets/src/mage/cards/d/DaybreakCharger.java b/Mage.Sets/src/mage/cards/d/DaybreakCharger.java new file mode 100644 index 0000000000..21bca3ec70 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DaybreakCharger.java @@ -0,0 +1,44 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DaybreakCharger extends CardImpl { + + public DaybreakCharger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.UNICORN); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // When Daybreak Charger enters the battlefield, target creature gets +2/+0 until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility( + new BoostTargetEffect(2, 0, Duration.EndOfTurn) + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private DaybreakCharger(final DaybreakCharger card) { + super(card); + } + + @Override + public DaybreakCharger copy() { + return new DaybreakCharger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DaybreakChimera.java b/Mage.Sets/src/mage/cards/d/DaybreakChimera.java index 32eeb17e45..b8035bf7db 100644 --- a/Mage.Sets/src/mage/cards/d/DaybreakChimera.java +++ b/Mage.Sets/src/mage/cards/d/DaybreakChimera.java @@ -1,17 +1,15 @@ package mage.cards.d; import mage.MageInt; -import mage.Mana; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.DevotionCount; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; import java.util.UUID; @@ -28,7 +26,9 @@ public final class DaybreakChimera extends CardImpl { this.toughness = new MageInt(3); // This spell costs {X} less to cast, where X is your devotion to white. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new DaybreakChimeraEffect()).addHint(DevotionCount.W.getHint())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(DevotionCount.W)) + .addHint(DevotionCount.W.getHint()) + ); // Flying this.addAbility(FlyingAbility.getInstance()); @@ -43,40 +43,3 @@ public final class DaybreakChimera extends CardImpl { return new DaybreakChimera(this); } } - -class DaybreakChimeraEffect extends CostModificationEffectImpl { - - DaybreakChimeraEffect() { - super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "This spell costs {X} less to cast, where X is your devotion to white. " + - "(Each {W} in the mana costs of permanents you control counts toward your devotion to white.)"; - } - - private DaybreakChimeraEffect(final DaybreakChimeraEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - Mana mana = spellAbility.getManaCostsToPay().getMana(); - if (mana.getGeneric() == 0) { - return false; - } - int count = DevotionCount.W.calculate(game, source, this); - mana.setGeneric(Math.max(mana.getGeneric() - count, 0)); - spellAbility.getManaCostsToPay().load(mana.toString()); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify instanceof SpellAbility - && abilityToModify.getSourceId().equals(source.getSourceId()); - } - - @Override - public DaybreakChimeraEffect copy() { - return new DaybreakChimeraEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/d/DaysquadMarshal.java b/Mage.Sets/src/mage/cards/d/DaysquadMarshal.java new file mode 100644 index 0000000000..02cf08416c --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DaysquadMarshal.java @@ -0,0 +1,39 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.HumanSoldierToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DaysquadMarshal extends CardImpl { + + public DaysquadMarshal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Daysquad Marshal enters the battlefield, create a 1/1 white Human Soldier creature token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken()))); + } + + private DaysquadMarshal(final DaysquadMarshal card) { + super(card); + } + + @Override + public DaysquadMarshal copy() { + return new DaysquadMarshal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DazzlingBeauty.java b/Mage.Sets/src/mage/cards/d/DazzlingBeauty.java index 88bb01666e..1341174f42 100644 --- a/Mage.Sets/src/mage/cards/d/DazzlingBeauty.java +++ b/Mage.Sets/src/mage/cards/d/DazzlingBeauty.java @@ -1,30 +1,23 @@ - package mage.cards.d; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.BecomeBlockedTargetEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.AttackingPredicate; import mage.filter.predicate.permanent.BlockedPredicate; -import mage.game.Game; -import mage.game.combat.CombatGroup; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 & L_J */ public final class DazzlingBeauty extends CardImpl { @@ -40,17 +33,20 @@ public final class DazzlingBeauty extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); // Cast Dazzling Beauty only during the declare blockers step. - this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, PhaseStep.DECLARE_BLOCKERS, null, "Cast Dazzling Beauty only during the declare blockers step")); + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility( + null, PhaseStep.DECLARE_BLOCKERS, null, + "Cast this spell only during the declare blockers step" + )); // Target unblocked attacking creature becomes blocked. - this.getSpellAbility().addEffect(new DazzlingBeautyEffect()); + this.getSpellAbility().addEffect(new BecomeBlockedTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); // Draw a card at the beginning of the next turn's upkeep. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false)); } - public DazzlingBeauty(final DazzlingBeauty card) { + private DazzlingBeauty(final DazzlingBeauty card) { super(card); } @@ -59,35 +55,3 @@ public final class DazzlingBeauty extends CardImpl { return new DazzlingBeauty(this); } } - -class DazzlingBeautyEffect extends OneShotEffect { - - public DazzlingBeautyEffect() { - super(Outcome.Benefit); - this.staticText = "Target unblocked attacking creature becomes blocked"; - } - - public DazzlingBeautyEffect(final DazzlingBeautyEffect effect) { - super(effect); - } - - @Override - public DazzlingBeautyEffect copy() { - return new DazzlingBeautyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (controller != null && permanent != null) { - CombatGroup combatGroup = game.getCombat().findGroup(permanent.getId()); - if (combatGroup != null) { - combatGroup.setBlocked(true, game); - game.informPlayers(permanent.getLogName() + " has become blocked"); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DazzlingReflection.java b/Mage.Sets/src/mage/cards/d/DazzlingReflection.java index c125dcfc32..01de779624 100644 --- a/Mage.Sets/src/mage/cards/d/DazzlingReflection.java +++ b/Mage.Sets/src/mage/cards/d/DazzlingReflection.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -25,7 +24,7 @@ import mage.target.targetpointer.FixedTarget; public final class DazzlingReflection extends CardImpl { public DazzlingReflection(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); // You gain life equal to target creature's power. The next time that creature would deal damage this turn, prevent that damage. getSpellAbility().addEffect(new DazzlingReflectionEffect()); @@ -62,12 +61,14 @@ class DazzlingReflectionEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Permanent targetCreature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); - controller.gainLife(targetCreature.getPower().getValue(), game, source); - ContinuousEffect effect = new DazzlingReflectionPreventEffect(); - effect.setTargetPointer(new FixedTarget(targetCreature, game)); - game.addEffect(effect, source); - return true; + Permanent targetCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (targetCreature != null) { + controller.gainLife(targetCreature.getPower().getValue(), game, source); + ContinuousEffect effect = new DazzlingReflectionPreventEffect(); + effect.setTargetPointer(new FixedTarget(targetCreature, game)); + game.addEffect(effect, source); + return true; + } } return false; } diff --git a/Mage.Sets/src/mage/cards/d/DeadGone.java b/Mage.Sets/src/mage/cards/d/DeadGone.java index ac7f2c2828..d42b2109ac 100644 --- a/Mage.Sets/src/mage/cards/d/DeadGone.java +++ b/Mage.Sets/src/mage/cards/d/DeadGone.java @@ -1,6 +1,5 @@ package mage.cards.d; -import java.util.UUID; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.Card; @@ -8,36 +7,31 @@ import mage.cards.CardSetInfo; import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.SpellAbilityType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author dustinconrad */ public final class DeadGone extends SplitCard { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public DeadGone(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}", "{2}{R}", SpellAbilityType.SPLIT); // Dead // Dead deals 2 damage to target creature. - getLeftHalfCard().getSpellAbility().addEffect(new DeadDamageEffect()); + getLeftHalfCard().getSpellAbility().addEffect(new DamageTargetEffect(2, "Dead")); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Gone // Return target creature you don't control to its owner's hand. getRightHalfCard().getSpellAbility().addEffect(new ReturnToHandTargetEffect()); - getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public DeadGone(final DeadGone card) { + private DeadGone(final DeadGone card) { super(card); } @@ -46,12 +40,3 @@ public final class DeadGone extends SplitCard { return new DeadGone(this); } } - -class DeadDamageEffect extends DamageTargetEffect { - - public DeadDamageEffect() { - super(2); - // Full name of split card was displaying using DamageTargetEffect - staticText = "Dead deals 2 damage to target creature."; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DeadIronSledge.java b/Mage.Sets/src/mage/cards/d/DeadIronSledge.java index 00073a826a..6c59f00502 100644 --- a/Mage.Sets/src/mage/cards/d/DeadIronSledge.java +++ b/Mage.Sets/src/mage/cards/d/DeadIronSledge.java @@ -2,14 +2,12 @@ package mage.cards.d; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; @@ -19,14 +17,13 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTargets; /** * - * @author jerekwilson + * @author jerekwilson, ciaccona007 */ public final class DeadIronSledge extends CardImpl { @@ -35,7 +32,7 @@ public final class DeadIronSledge extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Whenever equipped creature blocks or becomes blocked by a creature, destroy both creatures. - this.addAbility(new DeadIronSledgeTriggeredAbility()); + this.addAbility(new DeadIronSledgeTriggeredAbility(new DeadIronSledgeDestroyEffect(), false)); // Equip {2} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2))); @@ -54,70 +51,52 @@ public final class DeadIronSledge extends CardImpl { class DeadIronSledgeTriggeredAbility extends TriggeredAbilityImpl { - private Set possibleTargets = new HashSet<>(); - - DeadIronSledgeTriggeredAbility() { - super(Zone.BATTLEFIELD, new DeadIronSledgeDestroyEffect(), false); + public DeadIronSledgeTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); } - DeadIronSledgeTriggeredAbility(final DeadIronSledgeTriggeredAbility ability) { + public DeadIronSledgeTriggeredAbility(final DeadIronSledgeTriggeredAbility ability) { super(ability); } @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_BLOCKERS; + return event.getType() == GameEvent.EventType.BLOCKER_DECLARED; } @Override public boolean checkTrigger(GameEvent event, Game game) { - List targetPermanents = new ArrayList<>(); - Permanent equipment = game.getPermanentOrLKIBattlefield((this.getSourceId())); + Permanent equipment = game.getPermanent(sourceId); if (equipment != null && equipment.getAttachedTo() != null) { - Permanent equippedPermanent = game.getPermanentOrLKIBattlefield((equipment.getAttachedTo())); - if (equippedPermanent != null) { - possibleTargets.clear(); - if (equippedPermanent.isBlocked(game)) { - possibleTargets.add(equippedPermanent.getId()); //add equipped creature to target list - } - if (equippedPermanent.isAttacking()) { - for (CombatGroup group : game.getCombat().getGroups()) { - if (group.getAttackers().contains(equippedPermanent.getId())) { - possibleTargets.addAll(group.getBlockers()); - } - } - } else if (equippedPermanent.getBlocking() > 0) { - for (CombatGroup group : game.getCombat().getGroups()) { - if (group.getBlockers().contains(equippedPermanent.getId())) { - possibleTargets.addAll(group.getAttackers()); - } - } - } - if (!possibleTargets.isEmpty()) { - this.getTargets().clear(); + Permanent equipped = game.getPermanent(equipment.getAttachedTo()); + if (equipped.getId().equals(event.getTargetId()) || equipped.getId().equals(event.getSourceId())) { + List targets = new ArrayList<>(); - for (UUID creatureId : possibleTargets) { - Permanent target = game.getPermanentOrLKIBattlefield(creatureId); - targetPermanents.add(target); - } - - this.getEffects().get(0).setTargetPointer(new FixedTargets(targetPermanents, game)); - - return true; + Permanent blocker = game.getPermanent(event.getSourceId()); + if (blocker != null) { + targets.add(blocker); } + + Permanent blocked = game.getPermanent(event.getTargetId()); + if (blocked != null) { + targets.add(blocked); + } + this.getEffects().get(0).setTargetPointer(new FixedTargets(targets, game)); + + return true; } } return false; } @Override - public TriggeredAbility copy() { - return new DeadIronSledgeTriggeredAbility(this); + public String getRule() { + return "Whenever equipped creature blocks or becomes blocked by a creature, " + super.getRule(); } @Override - public String getRule() { - return "Whenever equipped creature blocks or becomes blocked by a creature, destroy both creatures."; + public DeadIronSledgeTriggeredAbility copy() { + return new DeadIronSledgeTriggeredAbility(this); } } diff --git a/Mage.Sets/src/mage/cards/d/DeadReckoning.java b/Mage.Sets/src/mage/cards/d/DeadReckoning.java index 1e3843ae00..a12b0bd82f 100644 --- a/Mage.Sets/src/mage/cards/d/DeadReckoning.java +++ b/Mage.Sets/src/mage/cards/d/DeadReckoning.java @@ -7,6 +7,7 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -59,22 +60,22 @@ class DeadReckoningEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); TargetCardInYourGraveyard target1 = new TargetCardInYourGraveyard(new FilterCreatureCard("creature card in your graveyard")); TargetCreaturePermanent target2 = new TargetCreaturePermanent(); - if (you != null) { + if (controller != null) { if (target1.canChoose(source.getControllerId(), game) - && you.choose(Outcome.Benefit, target1, source.getSourceId(), game) + && controller.choose(Outcome.Benefit, target1, source.getSourceId(), game) && target2.canChoose(source.getControllerId(), game) - && you.choose(Outcome.Damage, target2, source.getSourceId(), game)) { + && controller.choose(Outcome.Damage, target2, source.getSourceId(), game)) { Card creatureInGraveyard = game.getCard(target1.getFirstTarget()); if (creatureInGraveyard != null) { - if (creatureInGraveyard.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true)) { + if (controller.putCardsOnTopOfLibrary(creatureInGraveyard, game, source, true)) { int power = creatureInGraveyard.getPower().getValue(); Permanent creature = game.getPermanent(target2.getFirstTarget()); if (creature != null) { - creature.damage(power, source.getSourceId(), game, true, true); + creature.damage(power, source.getSourceId(), game, false, true); return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DeadbridgeShaman.java b/Mage.Sets/src/mage/cards/d/DeadbridgeShaman.java index 8c8f834a9b..473fed7dfa 100644 --- a/Mage.Sets/src/mage/cards/d/DeadbridgeShaman.java +++ b/Mage.Sets/src/mage/cards/d/DeadbridgeShaman.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class DeadbridgeShaman extends CardImpl { this.toughness = new MageInt(1); // When Deadbridge Shaman dies, target opponent discards a card. - Ability ability = new DiesTriggeredAbility(new DiscardTargetEffect(1)); + Ability ability = new DiesSourceTriggeredAbility(new DiscardTargetEffect(1)); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DeadeyePlunderers.java b/Mage.Sets/src/mage/cards/d/DeadeyePlunderers.java index 121c950190..180c7cf2b4 100644 --- a/Mage.Sets/src/mage/cards/d/DeadeyePlunderers.java +++ b/Mage.Sets/src/mage/cards/d/DeadeyePlunderers.java @@ -1,37 +1,28 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; +import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterPermanent; import mage.game.permanent.token.TreasureToken; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class DeadeyePlunderers extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact you control"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - filter.add(TargetController.YOU.getControllerPredicate()); - } - public DeadeyePlunderers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}"); @@ -41,8 +32,9 @@ public final class DeadeyePlunderers extends CardImpl { this.toughness = new MageInt(3); // Deadeye Plunderers gets +1/+1 for each artifact you control. - PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(count, count, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new BoostSourceEffect(ArtifactYouControlCount.instance, ArtifactYouControlCount.instance, Duration.WhileOnBattlefield) + ).addHint(ArtifactYouControlHint.instance)); // {2}{U}{B}: Create a colorless artifact token named Treasure with "{T}, Sacrifice this artifact: Add one mana of any color." this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken()), new ManaCostsImpl("{2}{U}{B}"))); diff --git a/Mage.Sets/src/mage/cards/d/DeadeyeRigHauler.java b/Mage.Sets/src/mage/cards/d/DeadeyeRigHauler.java index 96cba582c9..568a9f911e 100644 --- a/Mage.Sets/src/mage/cards/d/DeadeyeRigHauler.java +++ b/Mage.Sets/src/mage/cards/d/DeadeyeRigHauler.java @@ -1,22 +1,23 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DeadeyeRigHauler extends CardImpl { @@ -29,10 +30,12 @@ public final class DeadeyeRigHauler extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - // Raid— When Deadeye Rig-Hauler enters the battlefield, if you attacked with a creature this turn, you may return target creature to its owner's hand. + // Raid— When Deadeye Rig-Hauler enters the battlefield, if you attacked this turn, you may return target creature to its owner's hand. Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), true), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, you may return target creature to its owner's hand."); + "Raid — When {this} enters the battlefield, if you attacked this turn, you may return target creature to its owner's hand."); ability.addTarget(new TargetCreaturePermanent()); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/d/DeadeyeTormentor.java b/Mage.Sets/src/mage/cards/d/DeadeyeTormentor.java index 2a7df4690c..3a68af76ed 100644 --- a/Mage.Sets/src/mage/cards/d/DeadeyeTormentor.java +++ b/Mage.Sets/src/mage/cards/d/DeadeyeTormentor.java @@ -1,38 +1,41 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetOpponent; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class DeadeyeTormentor extends CardImpl { public DeadeyeTormentor(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); - + this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.PIRATE); this.power = new MageInt(2); this.toughness = new MageInt(2); - // Raid — When Deadeye Tormentor enters the battlefield, if you attacked with a creature this turn, target opponent discards a card. + // Raid — When Deadeye Tormentor enters the battlefield, if you attacked this turn, target opponent discards a card. Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new DiscardTargetEffect(1)), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, target opponent discards a card."); + "Raid — When {this} enters the battlefield, if you attacked this turn, target opponent discards a card."); ability.addTarget(new TargetOpponent()); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/d/DeadlyGrub.java b/Mage.Sets/src/mage/cards/d/DeadlyGrub.java index ddf8afee2d..1e836e6731 100644 --- a/Mage.Sets/src/mage/cards/d/DeadlyGrub.java +++ b/Mage.Sets/src/mage/cards/d/DeadlyGrub.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.LastTimeCounterRemovedCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; @@ -38,7 +38,7 @@ public final class DeadlyGrub extends CardImpl { this.addAbility(new VanishingUpkeepAbility(3)); this.addAbility(new VanishingSacrificeAbility()); // When Deadly Grub dies, if it had no time counters on it, create a 6/1 green Insect creature token with shroud. - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesTriggeredAbility(new CreateTokenEffect(new DeadlyGrubToken(), 1)), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new DeadlyGrubToken(), 1)), LastTimeCounterRemovedCondition.instance, "When {this} dies, if it had no time counters on it, create a 6/1 green Insect creature token with shroud.")); } diff --git a/Mage.Sets/src/mage/cards/d/DeadlyRecluse.java b/Mage.Sets/src/mage/cards/d/DeadlyRecluse.java index b39c37d9dc..4c5d0420b4 100644 --- a/Mage.Sets/src/mage/cards/d/DeadlyRecluse.java +++ b/Mage.Sets/src/mage/cards/d/DeadlyRecluse.java @@ -22,8 +22,12 @@ public final class DeadlyRecluse extends CardImpl { this.subtype.add(SubType.SPIDER); this.power = new MageInt(1); - this.toughness = new MageInt(2); + this.toughness = new MageInt(2); + + // Reach (This creature can block creatures with flying.) this.addAbility(ReachAbility.getInstance()); + + // Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.) this.addAbility(DeathtouchAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/d/DeadlyRollick.java b/Mage.Sets/src/mage/cards/d/DeadlyRollick.java new file mode 100644 index 0000000000..1d259b9544 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeadlyRollick.java @@ -0,0 +1,37 @@ +package mage.cards.d; + +import mage.abilities.condition.common.ControlACommanderCondition; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DeadlyRollick extends CardImpl { + + public DeadlyRollick(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}"); + + // If you control a commander, you may cast this card without paying its mana cost + this.addAbility(new AlternativeCostSourceAbility(null, ControlACommanderCondition.instance)); + + // Exile target creature. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private DeadlyRollick(final DeadlyRollick card) { + super(card); + } + + @Override + public DeadlyRollick copy() { + return new DeadlyRollick(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/Deadshot.java b/Mage.Sets/src/mage/cards/d/Deadshot.java index 88a3d88040..6e2db1af86 100644 --- a/Mage.Sets/src/mage/cards/d/Deadshot.java +++ b/Mage.Sets/src/mage/cards/d/Deadshot.java @@ -73,7 +73,7 @@ class DeadshotDamageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent ownCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent ownCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (ownCreature != null) { int damage = ownCreature.getPower().getValue(); Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); diff --git a/Mage.Sets/src/mage/cards/d/DeathBomb.java b/Mage.Sets/src/mage/cards/d/DeathBomb.java index 2331d0e70b..319c8bc873 100644 --- a/Mage.Sets/src/mage/cards/d/DeathBomb.java +++ b/Mage.Sets/src/mage/cards/d/DeathBomb.java @@ -9,7 +9,7 @@ import mage.abilities.effects.common.LoseLifeTargetControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; @@ -31,7 +31,7 @@ public final class DeathBomb extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}"); // As an additional cost to cast Death Bomb, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1, new FilterControlledCreaturePermanent("a creature"), true))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, true))); // Destroy target nonblack creature. It can't be regenerated. Its controller loses 2 life. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); this.getSpellAbility().addEffect(new LoseLifeTargetControllerEffect(2)); diff --git a/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java b/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java index 18824247a4..1655ec9108 100644 --- a/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java +++ b/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java @@ -72,6 +72,6 @@ class DeathbellowWarCryTarget extends TargetCardInLibrary { .map(game::getCard) .filter(Objects::nonNull) .map(Card::getName) - .noneMatch(n -> CardUtil.haveSameNames(n, card.getName())); + .noneMatch(n -> CardUtil.haveSameNames(card, n, game)); } } diff --git a/Mage.Sets/src/mage/cards/d/DeathbloomThallid.java b/Mage.Sets/src/mage/cards/d/DeathbloomThallid.java index 31edb39de0..b8b8a048a1 100644 --- a/Mage.Sets/src/mage/cards/d/DeathbloomThallid.java +++ b/Mage.Sets/src/mage/cards/d/DeathbloomThallid.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.constants.SubType; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class DeathbloomThallid extends CardImpl { this.toughness = new MageInt(2); // When Deathbloom Thallid dies, create a 1/1 green Saproling creature token. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SaprolingToken()), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SaprolingToken()), false)); } public DeathbloomThallid(final DeathbloomThallid card) { diff --git a/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java b/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java index 023015b9e0..d306c5e7a1 100644 --- a/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java +++ b/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java @@ -6,7 +6,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.keyword.FlyingAbility; @@ -63,7 +63,7 @@ class DeathbringerRegentCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - return CastFromHandSourceCondition.instance.apply(game, source) + return CastFromHandSourcePermanentCondition.instance.apply(game, source) && game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, game).size() >= 6; } } diff --git a/Mage.Sets/src/mage/cards/d/DeathcurseOgre.java b/Mage.Sets/src/mage/cards/d/DeathcurseOgre.java index d6fd4f0370..6d5c1ba31f 100644 --- a/Mage.Sets/src/mage/cards/d/DeathcurseOgre.java +++ b/Mage.Sets/src/mage/cards/d/DeathcurseOgre.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeAllPlayersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class DeathcurseOgre extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - this.addAbility(new DiesTriggeredAbility(new LoseLifeAllPlayersEffect(3))); + this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeAllPlayersEffect(3))); } public DeathcurseOgre (final DeathcurseOgre card) { diff --git a/Mage.Sets/src/mage/cards/d/Deathgazer.java b/Mage.Sets/src/mage/cards/d/Deathgazer.java index ac8463fba9..4ff33fe235 100644 --- a/Mage.Sets/src/mage/cards/d/Deathgazer.java +++ b/Mage.Sets/src/mage/cards/d/Deathgazer.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -39,7 +39,7 @@ public final class Deathgazer extends CardImpl { // Whenever Deathgazer blocks or becomes blocked by a nonblack creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false)); } diff --git a/Mage.Sets/src/mage/cards/d/DeathpactAngel.java b/Mage.Sets/src/mage/cards/d/DeathpactAngel.java index ebcca304ea..8b6378a8f7 100644 --- a/Mage.Sets/src/mage/cards/d/DeathpactAngel.java +++ b/Mage.Sets/src/mage/cards/d/DeathpactAngel.java @@ -2,7 +2,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class DeathpactAngel extends CardImpl { //Flying this.addAbility(FlyingAbility.getInstance()); //When Deathpact Angel dies, create a 1/1 white and black Cleric creature token. It has "{3}{W}{B}{B}, {T}, Sacrifice this creature: Return a card named Deathpact Angel from your graveyard to the battlefield." - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new DeathpactAngelToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new DeathpactAngelToken()))); } public DeathpactAngel(final DeathpactAngel card) { diff --git a/Mage.Sets/src/mage/cards/d/DeathriteShaman.java b/Mage.Sets/src/mage/cards/d/DeathriteShaman.java index 154c9a6c78..d2899fc272 100644 --- a/Mage.Sets/src/mage/cards/d/DeathriteShaman.java +++ b/Mage.Sets/src/mage/cards/d/DeathriteShaman.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -7,16 +6,19 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.abilities.dynamicvalue.LimitedDynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterLandCard; import mage.filter.predicate.Predicates; @@ -37,17 +39,18 @@ public final class DeathriteShaman extends CardImpl { } public DeathriteShaman(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B/G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B/G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.SHAMAN); - this.power = new MageInt(1); this.toughness = new MageInt(2); // {T}: Exile target land card from a graveyard. Add one mana of any color. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new TapSourceCost()); - ability.addEffect(new AddManaOfAnyColorEffect()); + // Because this is no mana ability, this mana will not be calculated during available mana calculation + ability.addEffect(new AddManaOfAnyColorEffect(1, new LimitedDynamicValue(1, + new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_LAND)), false)); ability.addTarget(new TargetCardInGraveyard(new FilterLandCard("land card from a graveyard"))); this.addAbility(ability); @@ -74,4 +77,4 @@ public final class DeathriteShaman extends CardImpl { public DeathriteShaman copy() { return new DeathriteShaman(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/d/DeathsHeadBuzzard.java b/Mage.Sets/src/mage/cards/d/DeathsHeadBuzzard.java index 91f87019c4..93d6785fec 100644 --- a/Mage.Sets/src/mage/cards/d/DeathsHeadBuzzard.java +++ b/Mage.Sets/src/mage/cards/d/DeathsHeadBuzzard.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class DeathsHeadBuzzard extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Death's-Head Buzzard dies, all creatures get -1/-1 until end of turn. - this.addAbility(new DiesTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn))); + this.addAbility(new DiesSourceTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn))); } public DeathsHeadBuzzard(final DeathsHeadBuzzard card) { diff --git a/Mage.Sets/src/mage/cards/d/DeathsOasis.java b/Mage.Sets/src/mage/cards/d/DeathsOasis.java new file mode 100644 index 0000000000..216ce01ade --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeathsOasis.java @@ -0,0 +1,170 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DeathsOasis extends CardImpl { + + public DeathsOasis(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{B}{G}"); + + // Whenever a nontoken creature you control dies, put the top two cards of your library into your graveyard. Then return a creature card with lesser converted mana cost than the creature that died from the graveyard to your hand. + this.addAbility(new DeathsOasisTriggeredAbility()); + + // {1}, Sacrifice Death's Oasis: You gain life equal to the greatest converted mana cost among creatures you control. + Ability ability = new SimpleActivatedAbility( + new GainLifeEffect(DeathsOasisValue.instance) + .setText("you gain life equal to the greatest converted mana cost among creatures you control"), + new GenericManaCost(1) + ); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private DeathsOasis(final DeathsOasis card) { + super(card); + } + + @Override + public DeathsOasis copy() { + return new DeathsOasis(this); + } +} + +class DeathsOasisTriggeredAbility extends DiesCreatureTriggeredAbility { + + private static final FilterPermanent defaultFilter = new FilterControlledCreaturePermanent(); + + static { + defaultFilter.add(Predicates.not(TokenPredicate.instance)); + } + + DeathsOasisTriggeredAbility() { + super(Zone.BATTLEFIELD, null, false, defaultFilter, false); + } + + private DeathsOasisTriggeredAbility(final DeathsOasisTriggeredAbility ability) { + super(ability); + } + + @Override + public DeathsOasisTriggeredAbility copy() { + return new DeathsOasisTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!super.checkTrigger(event, game)) { + return false; + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getTarget() == null) { + return false; + } + this.getEffects().clear(); + this.addEffect(new PutTopCardOfLibraryIntoGraveControllerEffect(2)); + this.addEffect(new DeathsOasisEffect(zEvent.getTarget().getConvertedManaCost())); + return true; + } + + @Override + public String getRule() { + return "Whenever a nontoken creature you control dies, put the top two cards of your library " + + "into your graveyard. Then return a creature card with lesser converted mana cost " + + "than the creature that died from your graveyard to your hand."; + } +} + +class DeathsOasisEffect extends OneShotEffect { + + private final FilterCard filter; + + DeathsOasisEffect(int cmc) { + super(Outcome.Benefit); + this.filter = new FilterCreatureCard("creature card in your graveyard with converted mana cost " + (cmc - 1) + " or less"); + this.filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, cmc)); + } + + private DeathsOasisEffect(final DeathsOasisEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + } + + @Override + public DeathsOasisEffect copy() { + return new DeathsOasisEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || player.getGraveyard().count(filter, game) == 0) { + return false; + } + TargetCard target = new TargetCardInYourGraveyard(filter); + target.setNotTarget(true); + if (!player.choose(outcome, player.getGraveyard(), target, game)) { + return false; + } + return player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game); + } +} + +enum DeathsOasisValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game + .getBattlefield() + .getAllActivePermanents(sourceAbility.getControllerId()) + .stream() + .filter(Permanent::isCreature) + .mapToInt(Permanent::getConvertedManaCost) + .max() + .orElse(0); + } + + @Override + public DynamicValue copy() { + return this; + } + + @Override + public String getMessage() { + return "1"; + } +} diff --git a/Mage.Sets/src/mage/cards/d/Deathsprout.java b/Mage.Sets/src/mage/cards/d/Deathsprout.java index e7a64af570..8c3502a96f 100644 --- a/Mage.Sets/src/mage/cards/d/Deathsprout.java +++ b/Mage.Sets/src/mage/cards/d/Deathsprout.java @@ -20,7 +20,7 @@ public final class Deathsprout extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}{B}{G}"); // Destroy target creature. Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. - this.getSpellAbility().addEffect(new DestroyTargetEffect().setText("Destroy target creature.")); + this.getSpellAbility().addEffect(new DestroyTargetEffect().setText("Destroy target creature")); this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect( new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true )); diff --git a/Mage.Sets/src/mage/cards/d/DecayingSoil.java b/Mage.Sets/src/mage/cards/d/DecayingSoil.java index 6e8c872d77..42318087da 100644 --- a/Mage.Sets/src/mage/cards/d/DecayingSoil.java +++ b/Mage.Sets/src/mage/cards/d/DecayingSoil.java @@ -101,7 +101,7 @@ class DecayingSoilTriggeredAbility extends TriggeredAbilityImpl { if (zEvent.isDiesEvent()) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (permanent != null && filter.match(permanent, this.getSourceId(), this.getControllerId(), game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DeclarationInStone.java b/Mage.Sets/src/mage/cards/d/DeclarationInStone.java index 45cfb5b117..c7edeb0cca 100644 --- a/Mage.Sets/src/mage/cards/d/DeclarationInStone.java +++ b/Mage.Sets/src/mage/cards/d/DeclarationInStone.java @@ -88,7 +88,7 @@ class DeclarationInStoneEffect extends OneShotEffect { } } controller.moveCards(cardsToExile, Zone.EXILED, source, game); - game.applyEffects(); + game.getState().processAction(game); if (nonTokenCount > 0) { new ClueArtifactToken().putOntoBattlefield(nonTokenCount, game, source.getSourceId(), targetPermanent.getControllerId(), false, false); } diff --git a/Mage.Sets/src/mage/cards/d/Decompose.java b/Mage.Sets/src/mage/cards/d/Decompose.java index c3cf1b5d0c..4989f1075a 100644 --- a/Mage.Sets/src/mage/cards/d/Decompose.java +++ b/Mage.Sets/src/mage/cards/d/Decompose.java @@ -1,26 +1,25 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInASingleGraveyard; +import java.util.UUID; + /** - * * @author ilcartographer */ public final class Decompose extends CardImpl { public Decompose(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Exile up to three target cards from a single graveyard. this.getSpellAbility().addEffect(new ExileTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("cards from a single graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS)); } public Decompose(final Decompose card) { diff --git a/Mage.Sets/src/mage/cards/d/DecoyGambit.java b/Mage.Sets/src/mage/cards/d/DecoyGambit.java new file mode 100644 index 0000000000..d238558379 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DecoyGambit.java @@ -0,0 +1,155 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.TargetAdjuster; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; +import mage.abilities.condition.Condition; +import mage.constants.Zone; + +/** + * @author TheElk801 + */ +public final class DecoyGambit extends CardImpl { + + public DecoyGambit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // For each opponent, choose up to one target creature that player controls, + // then return that creature to its owner's hand unless its controller has you draw a card. + this.getSpellAbility().addEffect(new DecoyGambitEffect()); + this.getSpellAbility().setTargetAdjuster(DecoyGambitAdjuster.instance); + } + + private DecoyGambit(final DecoyGambit card) { + super(card); + } + + @Override + public DecoyGambit copy() { + return new DecoyGambit(this); + } +} + +enum DecoyGambitAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + game.getOpponents(ability.getControllerId()) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .forEachOrdered(player -> { + FilterPermanent filter = new FilterCreaturePermanent( + "creature controlled by " + player.getName() + ); + filter.add(new ControllerIdPredicate(player.getId())); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + }); + } +} + +class DecoyGambitEffect extends OneShotEffect { + + DecoyGambitEffect() { + super(Outcome.Benefit); + staticText = "For each opponent, choose up to one target creature that player controls, " + + "then return that creature to its owner's hand unless its controller has you draw a card."; + } + + private DecoyGambitEffect(final DecoyGambitEffect effect) { + super(effect); + } + + @Override + public DecoyGambitEffect copy() { + return new DecoyGambitEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + HashSet permanentToHand = new HashSet(); + int numberOfCardsToDraw = 0; + if (controller == null) { + return false; + } + List permanents = source + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + for (Permanent permanent : permanents) { + // If a creature targeted by Decoy Gambit changes controller, it’s no longer a legal target. + new DecoyGambitCondition(permanent).apply(game, source); // save current controller + Player player = game.getPlayer(permanent.getControllerId()); + if (player == null) { + continue; + } + if (player.chooseUse(outcome, "Have " + controller.getName() + " draw a card? If you don't, " + + permanent.getName() + " will be returned to its owner's hand.", source, game)) { + game.informPlayers(player.getName() + " chose to have " + controller.getName() + " draw a card."); + numberOfCardsToDraw += 1; + } else { + game.informPlayers(player.getName() + " chose to have their creature returned to their hand."); + permanentToHand.add(permanent); + } + } + /* + As the Decoy Gambit resolves, first the next opponent in turn order (or, if it’s an opponent’s + turn, the opponent whose turn it is) chooses whether you’ll draw a card or return their creature + that was targeted to its owner’s hand, then each other opponent in turn order does so knowing + the choices made before them. After all choices are made, you draw the appropriate number of + cards. After you’ve drawn, the appropriate creatures are all simultaneously returned to their owners’ hands. + */ + controller.drawCards(numberOfCardsToDraw, source.getSourceId(), game); + for (Permanent creature : permanentToHand) { + if (creature != null + && new DecoyGambitCondition(creature).apply(game, source)) { // same controller required + creature.moveToZone(Zone.HAND, source.getSourceId(), game, false); + } + } + return true; + } +} + +class DecoyGambitCondition implements Condition { + + private UUID controllerId; + private final Permanent permanent; + + DecoyGambitCondition(Permanent permanent) { + this.permanent = permanent; + } + + @Override + public boolean apply(Game game, Ability source) { + if (controllerId == null) { // is the original controller set + controllerId = permanent.getControllerId(); // original controller set + } + return (permanent != null + && Objects.equals(controllerId, permanent.getControllerId())); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DecreeOfJustice.java b/Mage.Sets/src/mage/cards/d/DecreeOfJustice.java index 397039c60e..6255aa4521 100644 --- a/Mage.Sets/src/mage/cards/d/DecreeOfJustice.java +++ b/Mage.Sets/src/mage/cards/d/DecreeOfJustice.java @@ -14,7 +14,6 @@ import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.token.AngelToken; import mage.game.permanent.token.SoldierToken; -import mage.game.permanent.token.Token; import mage.players.Player; import mage.util.ManaUtil; @@ -35,11 +34,11 @@ public final class DecreeOfJustice extends CardImpl { this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}{W}"))); // When you cycle Decree of Justice, you may pay {X}. If you do, create X 1/1 white Soldier creature tokens. - Ability ability = new CycleTriggeredAbility(new DecreeOfJusticeCycleEffect(), true); + Ability ability = new CycleTriggeredAbility(new DecreeOfJusticeCycleEffect()); this.addAbility(ability); } - public DecreeOfJustice(final DecreeOfJustice card) { + private DecreeOfJustice(final DecreeOfJustice card) { super(card); } @@ -56,7 +55,7 @@ class DecreeOfJusticeCycleEffect extends OneShotEffect { this.staticText = "you may pay {X}. If you do, create X 1/1 white Soldier creature tokens"; } - DecreeOfJusticeCycleEffect(final DecreeOfJusticeCycleEffect effect) { + private DecreeOfJusticeCycleEffect(final DecreeOfJusticeCycleEffect effect) { super(effect); } @@ -68,14 +67,12 @@ class DecreeOfJusticeCycleEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null && player.chooseUse(Outcome.Benefit, "Do you want to pay {X} to create X tokens?", source, game)) { - int payCount = ManaUtil.playerPaysXGenericMana(true, "Decree of Justice", player, source, game); - if (payCount > 0) { - Token token = new SoldierToken(); - token.putOntoBattlefield(payCount, game, source.getSourceId(), source.getControllerId()); - return true; - } - + if (player == null || !player.chooseUse(Outcome.Benefit, "Pay {X} to create X tokens?", source, game)) { + return false; + } + int payCount = ManaUtil.playerPaysXGenericMana(true, "Decree of Justice", player, source, game); + if (payCount > 0) { + return new SoldierToken().putOntoBattlefield(payCount, game, source.getSourceId(), source.getControllerId()); } return false; } diff --git a/Mage.Sets/src/mage/cards/d/DecreeOfPain.java b/Mage.Sets/src/mage/cards/d/DecreeOfPain.java index 0176b88846..1930d1889f 100644 --- a/Mage.Sets/src/mage/cards/d/DecreeOfPain.java +++ b/Mage.Sets/src/mage/cards/d/DecreeOfPain.java @@ -73,7 +73,7 @@ class DecreeOfPainEffect extends OneShotEffect { } } if (destroyedCreature > 0) { - controller.drawCards(destroyedCreature, game); + controller.drawCards(destroyedCreature, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/d/DeepSpawn.java b/Mage.Sets/src/mage/cards/d/DeepSpawn.java index 1c54645f9c..3a78baa9fb 100644 --- a/Mage.Sets/src/mage/cards/d/DeepSpawn.java +++ b/Mage.Sets/src/mage/cards/d/DeepSpawn.java @@ -39,7 +39,7 @@ public final class DeepSpawn extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // At the beginning of your upkeep, sacrifice Deep Spawn unless you put the top two cards of your library into your graveyard. Effect effect = new SacrificeSourceUnlessPaysEffect(new PutTopCardOfYourLibraryToGraveyardCost(2)); - effect.setText("sacrifice {this} unless you put the top two cards of your library into your graveyard"); + effect.setText("sacrifice {this} unless you mill two cards"); this.addAbility(new BeginningOfUpkeepTriggeredAbility(effect, TargetController.YOU, false)); // {U}: Deep Spawn gains shroud until end of turn and doesn't untap during your next untap step. Tap Deep Spawn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect( diff --git a/Mage.Sets/src/mage/cards/d/DeepWater.java b/Mage.Sets/src/mage/cards/d/DeepWater.java index f169edd52f..fc41ee608b 100644 --- a/Mage.Sets/src/mage/cards/d/DeepWater.java +++ b/Mage.Sets/src/mage/cards/d/DeepWater.java @@ -20,7 +20,6 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.events.ManaEvent; import mage.game.permanent.Permanent; -import mage.players.Player; /** * diff --git a/Mage.Sets/src/mage/cards/d/DeeprootWarrior.java b/Mage.Sets/src/mage/cards/d/DeeprootWarrior.java index 42fea81ea0..e55b432804 100644 --- a/Mage.Sets/src/mage/cards/d/DeeprootWarrior.java +++ b/Mage.Sets/src/mage/cards/d/DeeprootWarrior.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; @@ -29,7 +29,7 @@ public final class DeeprootWarrior extends CardImpl { // Whenever Deeproot Warrior becomes blocked, it gets +1/+1 until end of turn. Effect effect = new BoostSourceEffect(1, 1, Duration.EndOfTurn); effect.setText("it gets +1/+1 until end of turn"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public DeeprootWarrior(final DeeprootWarrior card) { diff --git a/Mage.Sets/src/mage/cards/d/DeepwoodTantiv.java b/Mage.Sets/src/mage/cards/d/DeepwoodTantiv.java index b8f265102f..a1e0b9d32a 100644 --- a/Mage.Sets/src/mage/cards/d/DeepwoodTantiv.java +++ b/Mage.Sets/src/mage/cards/d/DeepwoodTantiv.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class DeepwoodTantiv extends CardImpl { this.toughness = new MageInt(4); // Whenever Deepwood Tantiv becomes blocked, you gain 2 life. - this.addAbility(new BecomesBlockedTriggeredAbility(new GainLifeEffect(2), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new GainLifeEffect(2), false)); } public DeepwoodTantiv(final DeepwoodTantiv card) { diff --git a/Mage.Sets/src/mage/cards/d/DeepwoodWolverine.java b/Mage.Sets/src/mage/cards/d/DeepwoodWolverine.java index f7e364dcff..7ed887cf9d 100644 --- a/Mage.Sets/src/mage/cards/d/DeepwoodWolverine.java +++ b/Mage.Sets/src/mage/cards/d/DeepwoodWolverine.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class DeepwoodWolverine extends CardImpl { // Whenever Deepwood Wolverine becomes blocked, it gets +2/+0 until end of turn. Effect effect = new BoostSourceEffect(2, 0, Duration.EndOfTurn); effect.setText("it gets +2/+0 until end of turn"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public DeepwoodWolverine(final DeepwoodWolverine card) { diff --git a/Mage.Sets/src/mage/cards/d/DeflectingSwat.java b/Mage.Sets/src/mage/cards/d/DeflectingSwat.java new file mode 100644 index 0000000000..616b942e7e --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeflectingSwat.java @@ -0,0 +1,37 @@ +package mage.cards.d; + +import mage.abilities.condition.common.ControlACommanderCondition; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.effects.common.ChooseNewTargetsTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.TargetStackObject; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DeflectingSwat extends CardImpl { + + public DeflectingSwat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // If you control a commander, you may cast this spell without paying its mana cost. + this.addAbility(new AlternativeCostSourceAbility(null, ControlACommanderCondition.instance)); + + // You may chose new targets for target spell or ability. + this.getSpellAbility().addTarget(new TargetStackObject()); + this.getSpellAbility().addEffect(new ChooseNewTargetsTargetEffect()); + } + + private DeflectingSwat(final DeflectingSwat card) { + super(card); + } + + @Override + public DeflectingSwat copy() { + return new DeflectingSwat(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DefyDeath.java b/Mage.Sets/src/mage/cards/d/DefyDeath.java index 3a2f5783d2..92fa3bbd0c 100644 --- a/Mage.Sets/src/mage/cards/d/DefyDeath.java +++ b/Mage.Sets/src/mage/cards/d/DefyDeath.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; @@ -16,8 +14,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author North */ public final class DefyDeath extends CardImpl { @@ -26,7 +25,7 @@ public final class DefyDeath extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}"); // Return target creature card from your graveyard to the battlefield. If it's an Angel, put two +1/+1 counters on it. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); this.getSpellAbility().addEffect(new DefyDeathEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); } diff --git a/Mage.Sets/src/mage/cards/d/Deglamer.java b/Mage.Sets/src/mage/cards/d/Deglamer.java index 35c2351eb2..081301752a 100644 --- a/Mage.Sets/src/mage/cards/d/Deglamer.java +++ b/Mage.Sets/src/mage/cards/d/Deglamer.java @@ -1,6 +1,6 @@ - package mage.cards.d; +import java.util.UUID; import mage.abilities.effects.common.ShuffleIntoLibraryTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -8,8 +8,6 @@ import mage.constants.CardType; import mage.filter.StaticFilters; import mage.target.TargetPermanent; -import java.util.UUID; - /** * * @author LevelX2 @@ -17,7 +15,7 @@ import java.util.UUID; public final class Deglamer extends CardImpl { public Deglamer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Choose target artifact or enchantment. Its owner shuffles it into their library. this.getSpellAbility().addEffect(new ShuffleIntoLibraryTargetEffect()); @@ -32,4 +30,4 @@ public final class Deglamer extends CardImpl { public Deglamer copy() { return new Deglamer(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/d/DementiaSliver.java b/Mage.Sets/src/mage/cards/d/DementiaSliver.java index 686a33a1ce..1610c825ae 100644 --- a/Mage.Sets/src/mage/cards/d/DementiaSliver.java +++ b/Mage.Sets/src/mage/cards/d/DementiaSliver.java @@ -86,7 +86,7 @@ class DementiaSliverEffect extends OneShotEffect { if (card != null) { revealed.add(card); opponent.revealCards(sourceObject.getName(), revealed, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { opponent.discard(card, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/DemonOfWailingAgonies.java b/Mage.Sets/src/mage/cards/d/DemonOfWailingAgonies.java index c17c13dffb..469e70a9b3 100644 --- a/Mage.Sets/src/mage/cards/d/DemonOfWailingAgonies.java +++ b/Mage.Sets/src/mage/cards/d/DemonOfWailingAgonies.java @@ -14,7 +14,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -33,7 +33,7 @@ public final class DemonOfWailingAgonies extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Lieutenant - As long as you control your commander, Demon of Wailing Agonies gets +2/+2 and has "Whenever Demon of Wailing Agonies deals combat damage to a player, that player sacrifices a creature." - Ability gainedAbility = new DealsCombatDamageToAPlayerTriggeredAbility(new SacrificeEffect(new FilterControlledCreaturePermanent("a creature"), 1, "that player"), false, true); + Ability gainedAbility = new DealsCombatDamageToAPlayerTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, 1, "that player"), false, true); ContinuousEffect effect = new GainAbilitySourceEffect(gainedAbility); effect.setText("and has \"Whenever {this} deals combat damage to a player, that player sacrifices a creature.\""); this.addAbility(new LieutenantAbility(effect)); diff --git a/Mage.Sets/src/mage/cards/d/DemonicEmbrace.java b/Mage.Sets/src/mage/cards/d/DemonicEmbrace.java new file mode 100644 index 0000000000..a206bc9f76 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DemonicEmbrace.java @@ -0,0 +1,108 @@ +package mage.cards.d; + +import java.util.UUID; + +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; + +/** + * @author arcox + */ +public final class DemonicEmbrace extends CardImpl { + + public DemonicEmbrace(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{B}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +3/+1, has flying, and is a Demon in addition to its other types. + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(3, 1, Duration.WhileOnBattlefield)); + Effect effect = new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA); + effect.setText(", has flying"); + ability.addEffect(effect); + effect = new AddCardSubtypeAttachedEffect(SubType.DEMON, Duration.WhileOnBattlefield, AttachmentType.AURA); + effect.setText(", and is a Demon in addition to its other types"); + ability.addEffect(effect); + this.addAbility(ability); + + // You may cast Demonic Embrace from your graveyard by paying 3 life and discarding a card in addition to paying its other costs. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new DemonicEmbracePlayEffect())); + } + + public DemonicEmbrace(final DemonicEmbrace card) { + super(card); + } + + @Override + public DemonicEmbrace copy() { + return new DemonicEmbrace(this); + } +} + + +class DemonicEmbracePlayEffect extends AsThoughEffectImpl { + + public DemonicEmbracePlayEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit); + staticText = "You may cast {this} from your graveyard by paying 3 life and discarding a card in addition to paying its other costs"; + } + + public DemonicEmbracePlayEffect(final DemonicEmbracePlayEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public DemonicEmbracePlayEffect copy() { + return new DemonicEmbracePlayEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + if (sourceId.equals(source.getSourceId()) && source.isControlledBy(affectedControllerId)) { + if (game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { + Player player = game.getPlayer(affectedControllerId); + if (player != null) { + Costs costs = new CostsImpl<>(); + costs.add(new PayLifeCost(3)); + costs.add(new DiscardCardCost()); + player.setCastSourceIdWithAlternateMana(sourceId, new ManaCostsImpl<>("{1}{B}{B}"), costs); + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/d/Denied.java b/Mage.Sets/src/mage/cards/d/Denied.java index b5825c8b1e..b8d60aec7a 100644 --- a/Mage.Sets/src/mage/cards/d/Denied.java +++ b/Mage.Sets/src/mage/cards/d/Denied.java @@ -58,12 +58,11 @@ class DeniedEffect extends OneShotEffect { return true; } Player player = game.getPlayer(targetSpell.getControllerId()); - Object object = game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - if (player != null && object instanceof String) { + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + if (player != null && cardName != null) { player.revealCards("Denied!", player.getHand(), game, true); - String namedCard = (String) object; for (Card card : player.getHand().getCards(game)) { - if (card != null && CardUtil.haveSameNames(card.getName(), namedCard)) { + if (card != null && CardUtil.haveSameNames(card, cardName, game)) { game.getStack().counter(targetSpell.getId(), source.getSourceId(), game); break; } diff --git a/Mage.Sets/src/mage/cards/d/DenseCanopy.java b/Mage.Sets/src/mage/cards/d/DenseCanopy.java index 9a43da5da0..3226e49efa 100644 --- a/Mage.Sets/src/mage/cards/d/DenseCanopy.java +++ b/Mage.Sets/src/mage/cards/d/DenseCanopy.java @@ -66,7 +66,7 @@ class DenseCanopyCantBlockEffect extends RestrictionEffect { if (attacker == null) { return true; } - return attacker.hasAbility(FlyingAbility.getInstance().getId(), game); + return attacker.hasAbility(FlyingAbility.getInstance(), game); } @Override diff --git a/Mage.Sets/src/mage/cards/d/DerangedAssistant.java b/Mage.Sets/src/mage/cards/d/DerangedAssistant.java index 45a12b1e3f..1b4b3d76b0 100644 --- a/Mage.Sets/src/mage/cards/d/DerangedAssistant.java +++ b/Mage.Sets/src/mage/cards/d/DerangedAssistant.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -17,14 +16,14 @@ import mage.constants.SubType; public final class DerangedAssistant extends CardImpl { public DerangedAssistant(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); this.power = new MageInt(1); this.toughness = new MageInt(1); - // {T}, Put the top card of your library into your graveyard: Add {C}. + // {T}, Mill a card: Add {C}. ColorlessManaAbility ability = new ColorlessManaAbility(); ability.addCost(new PutTopCardOfYourLibraryToGraveyardCost()); ability.setUndoPossible(false); diff --git a/Mage.Sets/src/mage/cards/d/Derelor.java b/Mage.Sets/src/mage/cards/d/Derelor.java index 6d1ddddc00..e6984a51e6 100644 --- a/Mage.Sets/src/mage/cards/d/Derelor.java +++ b/Mage.Sets/src/mage/cards/d/Derelor.java @@ -1,41 +1,41 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Quercitron */ public final class Derelor extends CardImpl { private static final FilterCard filter = new FilterCard("Black spells"); - + static { filter.add(new ColorPredicate(ObjectColor.BLACK)); } - + public Derelor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.THRULL); this.power = new MageInt(4); this.toughness = new MageInt(4); // Black spells you cast cost {B} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl<>("{B}")))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(new ManaCostsImpl<>("{B}"), filter, TargetController.YOU))); } public Derelor(final Derelor card) { diff --git a/Mage.Sets/src/mage/cards/d/DesecratedEarth.java b/Mage.Sets/src/mage/cards/d/DesecratedEarth.java index 43361ca127..0ffa994598 100644 --- a/Mage.Sets/src/mage/cards/d/DesecratedEarth.java +++ b/Mage.Sets/src/mage/cards/d/DesecratedEarth.java @@ -58,7 +58,7 @@ class DesecratedEarthEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/d/DesecratedTomb.java b/Mage.Sets/src/mage/cards/d/DesecratedTomb.java index b597b3e36d..0ca91ef33b 100644 --- a/Mage.Sets/src/mage/cards/d/DesecratedTomb.java +++ b/Mage.Sets/src/mage/cards/d/DesecratedTomb.java @@ -1,6 +1,5 @@ package mage.cards.d; -import java.util.Set; import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.CreateTokenEffect; @@ -60,13 +59,9 @@ class DesecratedTombTriggeredAbility extends TriggeredAbilityImpl { && zEvent.getCards() != null) { for (Card card : zEvent.getCards()) { if (card != null) { - UUID cardOwnerId = card.getOwnerId(); - Set cardType = card.getCardType(); - if (cardOwnerId != null && card.isOwnedBy(getControllerId()) - && cardType != null && card.isCreature()) { return true; } diff --git a/Mage.Sets/src/mage/cards/d/Desertion.java b/Mage.Sets/src/mage/cards/d/Desertion.java index e7ef89c2a1..5758bc61f3 100644 --- a/Mage.Sets/src/mage/cards/d/Desertion.java +++ b/Mage.Sets/src/mage/cards/d/Desertion.java @@ -2,17 +2,20 @@ package mage.cards.d; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CounterTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.stack.Spell; -import mage.players.Player; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; import mage.target.TargetSpell; /** @@ -24,9 +27,12 @@ public final class Desertion extends CardImpl { public Desertion(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); - // Counter target spell. If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard. - this.getSpellAbility().addEffect(new DesertionEffect()); + // Counter target spell. + this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addTarget(new TargetSpell()); + + // If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard. + this.addAbility(new SimpleStaticAbility(Zone.STACK, new DesertionReplacementEffect())); } public Desertion(final Desertion card) { @@ -39,38 +45,48 @@ public final class Desertion extends CardImpl { } } -class DesertionEffect extends OneShotEffect { +class DesertionReplacementEffect extends ReplacementEffectImpl { - public DesertionEffect() { - super(Outcome.Detriment); - this.staticText = "Counter target spell. If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard"; + DesertionReplacementEffect() { + super(Duration.WhileOnStack, Outcome.PutCardInPlay); + staticText = "If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard"; } - public DesertionEffect(final DesertionEffect effect) { + private DesertionReplacementEffect(final DesertionReplacementEffect effect) { super(effect); } @Override - public DesertionEffect copy() { - return new DesertionEffect(this); + public DesertionReplacementEffect copy() { + return new DesertionReplacementEffect(this); } @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Spell targetSpell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); - if (targetSpell != null) { - if (game.getStack().counter(targetSpell.getId(), source.getSourceId(), game)) { - game.applyEffects(); - if (targetSpell.isArtifact() || targetSpell.isCreature()) { - Card card = game.getCard(targetSpell.getSourceId()); - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - } - } - } - return true; - } + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zce = (ZoneChangeEvent) event; + zce.setToZone(Zone.BATTLEFIELD); + zce.setPlayerId(source.getControllerId()); return false; } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!event.getSourceId().equals(source.getSourceId()) + || !(((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD)) { + return false; + } + MageObject mageObject = game.getObject(event.getTargetId()); + return mageObject != null + && (mageObject.isArtifact() || mageObject.isCreature()); + } } diff --git a/Mage.Sets/src/mage/cards/d/DesiccatedNaga.java b/Mage.Sets/src/mage/cards/d/DesiccatedNaga.java index a9fbe8b25f..b15a00889d 100644 --- a/Mage.Sets/src/mage/cards/d/DesiccatedNaga.java +++ b/Mage.Sets/src/mage/cards/d/DesiccatedNaga.java @@ -1,4 +1,3 @@ - package mage.cards.d; import mage.MageInt; @@ -6,7 +5,6 @@ import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; @@ -20,7 +18,6 @@ import mage.target.common.TargetOpponent; import java.util.UUID; /** - * * @author fireshoes */ public final class DesiccatedNaga extends CardImpl { @@ -44,10 +41,8 @@ public final class DesiccatedNaga extends CardImpl { new LoseLifeTargetEffect(2), new ManaCostsImpl("{3}{B}"), new PermanentsOnTheBattlefieldCondition(filter)); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); ability.addTarget(new TargetOpponent()); - Effect effect = new GainLifeEffect(2); - effect.setText("and you gain 2 life"); - ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/Desolation.java b/Mage.Sets/src/mage/cards/d/Desolation.java index e0e13efc7a..55447a30ab 100644 --- a/Mage.Sets/src/mage/cards/d/Desolation.java +++ b/Mage.Sets/src/mage/cards/d/Desolation.java @@ -53,7 +53,7 @@ class DesolationEffect extends OneShotEffect { public DesolationEffect() { super(Outcome.Damage); - this.staticText = "each player who tapped a land for mana this turn sacrifices a land. Desolation deals 2 damage to each player who sacrificed a Plains this way"; + this.staticText = "each player who tapped a land for mana this turn sacrifices a land. {this} deals 2 damage to each player who sacrificed a Plains this way"; } public DesolationEffect(DesolationEffect copy) { @@ -108,7 +108,8 @@ class DesolationWatcher extends Watcher { if (event.getType() == GameEvent.EventType.UNTAP_STEP_PRE) { reset(); } - if (event.getType() == GameEvent.EventType.TAPPED_FOR_MANA) { + if (event.getType() == GameEvent.EventType.TAPPED_FOR_MANA + && !game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA UUID playerId = event.getPlayerId(); if (playerId != null) { Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/d/DesperateLunge.java b/Mage.Sets/src/mage/cards/d/DesperateLunge.java index ba5f659bde..0a75a06760 100644 --- a/Mage.Sets/src/mage/cards/d/DesperateLunge.java +++ b/Mage.Sets/src/mage/cards/d/DesperateLunge.java @@ -26,7 +26,7 @@ public final class DesperateLunge extends CardImpl { ).setText("Target creature gets +2/+2")); this.getSpellAbility().addEffect(new GainAbilityTargetEffect( FlyingAbility.getInstance(), Duration.EndOfTurn - ).setText("and gains flying until end of turn.")); + ).setText("and gains flying until end of turn")); this.getSpellAbility().addEffect(new GainLifeEffect(2)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/d/DesperateRavings.java b/Mage.Sets/src/mage/cards/d/DesperateRavings.java index c439c8e282..bcafe7eb9f 100644 --- a/Mage.Sets/src/mage/cards/d/DesperateRavings.java +++ b/Mage.Sets/src/mage/cards/d/DesperateRavings.java @@ -62,7 +62,7 @@ class DesperateRavingsEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(2, game); + player.drawCards(2, source.getSourceId(), game); Cards hand = player.getHand(); Card card = hand.getRandom(game); player.discard(card, source, game); diff --git a/Mage.Sets/src/mage/cards/d/DesperateSentry.java b/Mage.Sets/src/mage/cards/d/DesperateSentry.java index 28dc4fe5c5..0cb69932d4 100644 --- a/Mage.Sets/src/mage/cards/d/DesperateSentry.java +++ b/Mage.Sets/src/mage/cards/d/DesperateSentry.java @@ -4,7 +4,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.DeliriumCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -32,7 +32,7 @@ public final class DesperateSentry extends CardImpl { this.toughness = new MageInt(2); // When Desperate Sentry dies, create a 3/2 colorless Eldrazi Horror creature token. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new EldraziHorrorToken()), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new EldraziHorrorToken()), false)); // Delirium — Desperate Sentry gets +3/+0 as long as there are four or more card types among cards in your graveyard. ConditionalContinuousEffect effect = new ConditionalContinuousEffect( diff --git a/Mage.Sets/src/mage/cards/d/DesperateStand.java b/Mage.Sets/src/mage/cards/d/DesperateStand.java index 94d44cbb7c..94feb97de5 100644 --- a/Mage.Sets/src/mage/cards/d/DesperateStand.java +++ b/Mage.Sets/src/mage/cards/d/DesperateStand.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -14,26 +12,27 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DesperateStand extends CardImpl { public DesperateStand(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{R}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}{W}"); // Strive - Desperate Stand costs RW more to cast for each target beyond the first. this.addAbility(new StriveAbility("{R}{W}")); + // Any number of target creatures each get +2/+0 and gain first strike and vigilance until end of turn. - Effect effect = new BoostTargetEffect(2,0,Duration.EndOfTurn); + Effect effect = new BoostTargetEffect(2, 0, Duration.EndOfTurn); effect.setText("Any number of target creatures each get +2/+0"); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, "and gain first strike")); this.getSpellAbility().addEffect(new GainAbilityTargetEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, "and vigilance until end of turn")); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE)); - + } public DesperateStand(final DesperateStand card) { diff --git a/Mage.Sets/src/mage/cards/d/DestroyTheEvidence.java b/Mage.Sets/src/mage/cards/d/DestroyTheEvidence.java index 93820f75f0..7de58bdad8 100644 --- a/Mage.Sets/src/mage/cards/d/DestroyTheEvidence.java +++ b/Mage.Sets/src/mage/cards/d/DestroyTheEvidence.java @@ -59,7 +59,7 @@ class DestroyTheEvidenceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent landPermanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent landPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (landPermanent != null) { Player player = game.getPlayer(landPermanent.getControllerId()); if (player == null) { diff --git a/Mage.Sets/src/mage/cards/d/DestructorDragon.java b/Mage.Sets/src/mage/cards/d/DestructorDragon.java index cb37ba3c4b..94a3ca7b08 100644 --- a/Mage.Sets/src/mage/cards/d/DestructorDragon.java +++ b/Mage.Sets/src/mage/cards/d/DestructorDragon.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -35,7 +35,7 @@ public final class DestructorDragon extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Destructor Dragon dies, destroy target noncreature permanent. - Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), false); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DetentionSphere.java b/Mage.Sets/src/mage/cards/d/DetentionSphere.java index 64943101ae..c7a99d41a5 100644 --- a/Mage.Sets/src/mage/cards/d/DetentionSphere.java +++ b/Mage.Sets/src/mage/cards/d/DetentionSphere.java @@ -85,7 +85,7 @@ class DetentionSphereEntersEffect extends OneShotEffect { } else { String name = targetPermanent.getName(); for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - if (permanent != null && CardUtil.haveSameNames(permanent.getName(), name)) { + if (permanent != null && CardUtil.haveSameNames(permanent, name, game)) { controller.moveCardToExileWithInfo(permanent, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true); } } diff --git a/Mage.Sets/src/mage/cards/d/DevourFlesh.java b/Mage.Sets/src/mage/cards/d/DevourFlesh.java index 985f35f554..c8b64b6004 100644 --- a/Mage.Sets/src/mage/cards/d/DevourFlesh.java +++ b/Mage.Sets/src/mage/cards/d/DevourFlesh.java @@ -73,7 +73,7 @@ class DevourFleshSacrificeEffect extends OneShotEffect { if (permanent != null) { int gainLife = permanent.getToughness().getValue(); permanent.sacrifice(source.getSourceId(), game); - game.applyEffects(); + game.getState().processAction(game); player.gainLife(gainLife, game, source); } else { return false; diff --git a/Mage.Sets/src/mage/cards/d/DevourInShadow.java b/Mage.Sets/src/mage/cards/d/DevourInShadow.java index 75dcbe367d..db3c330200 100644 --- a/Mage.Sets/src/mage/cards/d/DevourInShadow.java +++ b/Mage.Sets/src/mage/cards/d/DevourInShadow.java @@ -61,7 +61,7 @@ class DevourInShadowEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Permanent target = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent target = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (player != null && target != null) { player.loseLife(target.getToughness().getValue(), game, false); return true; diff --git a/Mage.Sets/src/mage/cards/d/DiamondKaleidoscope.java b/Mage.Sets/src/mage/cards/d/DiamondKaleidoscope.java index fd21f23373..8b8ce93800 100644 --- a/Mage.Sets/src/mage/cards/d/DiamondKaleidoscope.java +++ b/Mage.Sets/src/mage/cards/d/DiamondKaleidoscope.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -7,6 +6,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; @@ -33,7 +33,7 @@ public final class DiamondKaleidoscope extends CardImpl { } public DiamondKaleidoscope(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // {3}, {tap}: Create a 0/1 colorless Prism artifact creature token. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new PrismToken(), 1), new GenericManaCost(3)); @@ -41,7 +41,9 @@ public final class DiamondKaleidoscope extends CardImpl { this.addAbility(ability); // Sacrifice a Prism token: Add one mana of any color. - ability = new AnyColorManaAbility(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + ability = new AnyColorManaAbility(new SacrificeTargetCost(new TargetControlledPermanent(filter)), + new PermanentsOnBattlefieldCount(filter), + false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DidntSayPlease.java b/Mage.Sets/src/mage/cards/d/DidntSayPlease.java index f4e6a186b5..96f5edc363 100644 --- a/Mage.Sets/src/mage/cards/d/DidntSayPlease.java +++ b/Mage.Sets/src/mage/cards/d/DidntSayPlease.java @@ -8,7 +8,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetSpell; @@ -44,8 +43,7 @@ class DidntSayPleaseEffect extends OneShotEffect { DidntSayPleaseEffect() { super(Outcome.Benefit); - staticText = "Counter target spell. Its controller puts " + - "the top three cards of their library into their graveyard."; + staticText = "Counter target spell. Its controller mills three cards."; } private DidntSayPleaseEffect(final DidntSayPleaseEffect effect) { @@ -63,7 +61,7 @@ class DidntSayPleaseEffect extends OneShotEffect { if (player == null) { return false; } - player.moveCards(player.getLibrary().getTopCards(game, 3), Zone.GRAVEYARD, source, game); + player.millCards(3, source, game); return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/d/DigThroughTime.java b/Mage.Sets/src/mage/cards/d/DigThroughTime.java index fbc8168103..5a652e5ceb 100644 --- a/Mage.Sets/src/mage/cards/d/DigThroughTime.java +++ b/Mage.Sets/src/mage/cards/d/DigThroughTime.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; import mage.abilities.keyword.DelveAbility; @@ -11,19 +9,19 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.filter.FilterCard; +import java.util.UUID; + /** - * * @author emerald000 */ public final class DigThroughTime extends CardImpl { public DigThroughTime(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{6}{U}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{6}{U}{U}"); // Delve this.addAbility(new DelveAbility()); - + // Look at the top seven cards of your library. Put two of them into your hand and the rest on the bottom of your library in any order. this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect(StaticValue.get(7), false, StaticValue.get(2), new FilterCard(), Zone.LIBRARY, false, false)); } diff --git a/Mage.Sets/src/mage/cards/d/DiminishingReturns.java b/Mage.Sets/src/mage/cards/d/DiminishingReturns.java index e6b2d8ecdf..f1c69c6f8c 100644 --- a/Mage.Sets/src/mage/cards/d/DiminishingReturns.java +++ b/Mage.Sets/src/mage/cards/d/DiminishingReturns.java @@ -4,7 +4,8 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.DrawCardAllEffect; +import mage.abilities.effects.common.ShuffleHandGraveyardAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -23,7 +24,8 @@ public final class DiminishingReturns extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}{U}"); // Each player shuffles their hand and graveyard into their library. You exile the top ten cards of your library. Then each player draws up to seven cards. - this.getSpellAbility().addEffect(new DiminishingReturnsEffect()); + this.getSpellAbility().addEffect(new ShuffleHandGraveyardAllEffect()); + this.getSpellAbility().addEffect(new DiminishingReturnsEffect()); } public DiminishingReturns(final DiminishingReturns card) { @@ -40,7 +42,7 @@ class DiminishingReturnsEffect extends OneShotEffect { public DiminishingReturnsEffect() { super(Outcome.Neutral); - staticText = "Each player shuffles their hand and graveyard into their library. You exile the top ten cards of your library. Then each player draws up to seven cards."; + staticText = "You exile the top ten cards of your library. Then each player draws up to seven cards."; } public DiminishingReturnsEffect(final DiminishingReturnsEffect effect) { @@ -51,28 +53,13 @@ class DiminishingReturnsEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { + controller.moveCards(controller.getLibrary().getTopCards(game, 10), Zone.EXILED, source, game); + game.getState().processAction(game); for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - for (Card card: player.getHand().getCards(game)) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - for (Card card: player.getGraveyard().getCards(game)) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - player.shuffleLibrary(source, game); - } - } - - for (Card card: controller.getLibrary().getTopCards(game, 10)) { - controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.LIBRARY, true); - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - int cardsToDrawCount = player.getAmount(0, 7, "How many cards to draw (up to 7)?", game); - player.drawCards(cardsToDrawCount, game); + player.drawCards(player.getAmount(0, 7, "How many cards to draw (up to 7)?", game), + source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/d/DimirCutpurse.java b/Mage.Sets/src/mage/cards/d/DimirCutpurse.java index 5d6beefa3b..07f119e1db 100644 --- a/Mage.Sets/src/mage/cards/d/DimirCutpurse.java +++ b/Mage.Sets/src/mage/cards/d/DimirCutpurse.java @@ -59,7 +59,7 @@ class DimirCutpurseEffect extends OneShotEffect { damagedPlayer.discard(1, false,source, game); } if (you != null) { - you.drawCards(1, game); + you.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java b/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java index eee9cd1434..664420628b 100644 --- a/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java +++ b/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java @@ -49,7 +49,9 @@ public final class DireFleetDaredevil extends CardImpl { // First strike this.addAbility(FirstStrikeAbility.getInstance()); - // When this enters the battlefield, exile target instant or sorcery card from an opponent's graveyard. You may cast that card this turn and you may spend mana as though it were mana of any color. If that card would be put into a graveyard this turn, exile it instead. + // When Dire Fleet Daredevil enters the battlefield, exile target instant or sorcery card from an opponent's graveyard. + // You may cast it this turn, and you may spend mana as though it were mana of any type to cast that spell. + // If that spell would be put into a graveyard this turn, exile it instead. Ability ability = new EntersBattlefieldTriggeredAbility(new DireFleetDaredevilEffect()); ability.addTarget(new TargetCardInOpponentsGraveyard(filter)); this.addAbility(ability); @@ -167,8 +169,9 @@ class DireFleetDaredevilReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Card card = game.getCard(event.getTargetId()); - if (card != null) { - return card.moveToZone(Zone.EXILED, source.getSourceId(), game, false); + Player controller = game.getPlayer(source.getControllerId()); + if (card != null && controller != null) { + return controller.moveCards(card, Zone.EXILED, source, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/d/DireFleetHoarder.java b/Mage.Sets/src/mage/cards/d/DireFleetHoarder.java index b23b13831b..1c40d69139 100644 --- a/Mage.Sets/src/mage/cards/d/DireFleetHoarder.java +++ b/Mage.Sets/src/mage/cards/d/DireFleetHoarder.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.constants.SubType; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class DireFleetHoarder extends CardImpl { this.toughness = new MageInt(1); // When Dire Fleet Hoarder dies, create a colorless Treasure artifact token with "{t}, Sacrifice this artifact: Add one mana of any color." - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new TreasureToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new TreasureToken()))); } public DireFleetHoarder(final DireFleetHoarder card) { diff --git a/Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java b/Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java new file mode 100644 index 0000000000..b43fafe14e --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java @@ -0,0 +1,53 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DireFleetWarmonger extends CardImpl { + + public DireFleetWarmonger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{R}"); + + this.subtype.add(SubType.ORC); + this.subtype.add(SubType.PIRATE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // At the beginning of combat on your turn, you may sacrifice another creature. If you do, Dire Fleet Warmonger gets +2/+2 and gains trample until end of turn. + this.addAbility(new BeginningOfCombatTriggeredAbility(new DoIfCostPaid( + new BoostSourceEffect(2, 2, Duration.EndOfTurn), + new SacrificeTargetCost(new TargetControlledPermanent( + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE + )) + ).addEffect(new GainAbilitySourceEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + )), TargetController.YOU, false)); + } + + private DireFleetWarmonger(final DireFleetWarmonger card) { + super(card); + } + + @Override + public DireFleetWarmonger copy() { + return new DireFleetWarmonger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DireTactics.java b/Mage.Sets/src/mage/cards/d/DireTactics.java new file mode 100644 index 0000000000..b441c376e5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DireTactics.java @@ -0,0 +1,75 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DireTactics extends CardImpl { + + public DireTactics(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}{B}"); + + // Exile target creature. If you don't control a Human, you lose life equal to that creature's toughness. + this.getSpellAbility().addEffect(new DireTacticsEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private DireTactics(final DireTactics card) { + super(card); + } + + @Override + public DireTactics copy() { + return new DireTactics(this); + } +} + +class DireTacticsEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent(SubType.HUMAN, ""); + + DireTacticsEffect() { + super(Outcome.Benefit); + staticText = "Exile target creature. If you don't control a Human, " + + "you lose life equal to that creature's toughness."; + } + + private DireTacticsEffect(final DireTacticsEffect effect) { + super(effect); + } + + @Override + public DireTacticsEffect copy() { + return new DireTacticsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + Player player = game.getPlayer(source.getControllerId()); + if (permanent == null || player == null) { + return false; + } + int toughness = permanent.getToughness().getValue(); + player.moveCards(permanent, Zone.EXILED, source, game); + if (game.getBattlefield().countAll(filter, player.getId(), game) < 1) { + player.loseLife(toughness, game, false); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DiregrafCaptain.java b/Mage.Sets/src/mage/cards/d/DiregrafCaptain.java index 845221f6d8..52ee1b1f96 100644 --- a/Mage.Sets/src/mage/cards/d/DiregrafCaptain.java +++ b/Mage.Sets/src/mage/cards/d/DiregrafCaptain.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleStaticAbility; @@ -22,8 +20,9 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author Loki */ public final class DiregrafCaptain extends CardImpl { @@ -35,16 +34,18 @@ public final class DiregrafCaptain extends CardImpl { } public DiregrafCaptain(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.SOLDIER); - this.power = new MageInt(2); this.toughness = new MageInt(2); + // Deathtouch this.addAbility(DeathtouchAbility.getInstance()); + // Other Zombie creatures you control get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, true))); + // Whenever another Zombie you control dies, target opponent loses 1 life. this.addAbility(new DiregrafCaptainTriggeredAbility()); } @@ -84,12 +85,10 @@ class DiregrafCaptainTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (!event.getTargetId().equals(this.getSourceId())) { - ZoneChangeEvent zEvent = (ZoneChangeEvent)event; + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; if (zEvent.isDiesEvent()) { Permanent p = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (p != null && p.isControlledBy(this.controllerId) && filter.match(p, game)) { - return true; - } + return p != null && p.isControlledBy(this.controllerId) && filter.match(p, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/d/DirgeBat.java b/Mage.Sets/src/mage/cards/d/DirgeBat.java new file mode 100644 index 0000000000..0afe778660 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DirgeBat.java @@ -0,0 +1,63 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DirgeBat extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker an opponent controls"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public DirgeBat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.subtype.add(SubType.BAT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Mutate {4}{B}{B} + this.addAbility(new MutateAbility(this, "{4}{B}{B}")); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever this creature mutates, destroy target creature or planeswalker an opponent controls. + Ability ability = new MutatesSourceTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private DirgeBat(final DirgeBat card) { + super(card); + } + + @Override + public DirgeBat copy() { + return new DirgeBat(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/Disappear.java b/Mage.Sets/src/mage/cards/d/Disappear.java index a02d0bf128..4ce17915e6 100644 --- a/Mage.Sets/src/mage/cards/d/Disappear.java +++ b/Mage.Sets/src/mage/cards/d/Disappear.java @@ -1,87 +1,87 @@ -package mage.cards.d; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.AttachEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * - * @author jeffwadsworth - */ -public final class Disappear extends CardImpl { - - public Disappear(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // {U}: Return enchanted creature and Disappear to their owners' hands. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DisappearEffect(), new ManaCostsImpl("{U}"))); - - } - - public Disappear(final Disappear card) { - super(card); - } - - @Override - public Disappear copy() { - return new Disappear(this); - } -} - -class DisappearEffect extends OneShotEffect { - - public DisappearEffect() { - super(Outcome.ReturnToHand); - staticText = "Return enchanted creature and {this} to their owners' hands"; - } - - public DisappearEffect(final DisappearEffect effect) { - super(effect); - } - - @Override - public DisappearEffect copy() { - return new DisappearEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent aura = game.getPermanentOrLKIBattlefield(source.getSourceId()); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null - && aura != null - && aura.getAttachedTo() != null) { - Permanent enchantedCreature = game.getPermanent(aura.getAttachedTo()); - controller.moveCards(aura, Zone.HAND, source, game); - if (enchantedCreature != null) { - controller.moveCards(enchantedCreature, Zone.HAND, source, game); - } - return true; - } - return false; - } -} +package mage.cards.d; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author jeffwadsworth + */ +public final class Disappear extends CardImpl { + + public Disappear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // {U}: Return enchanted creature and Disappear to their owners' hands. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DisappearEffect(), new ManaCostsImpl("{U}"))); + + } + + public Disappear(final Disappear card) { + super(card); + } + + @Override + public Disappear copy() { + return new Disappear(this); + } +} + +class DisappearEffect extends OneShotEffect { + + public DisappearEffect() { + super(Outcome.ReturnToHand); + staticText = "Return enchanted creature and {this} to their owners' hands"; + } + + public DisappearEffect(final DisappearEffect effect) { + super(effect); + } + + @Override + public DisappearEffect copy() { + return new DisappearEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent aura = game.getPermanentOrLKIBattlefield(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null + && aura != null + && aura.getAttachedTo() != null) { + Permanent enchantedCreature = game.getPermanent(aura.getAttachedTo()); + controller.moveCards(aura, Zone.HAND, source, game); + if (enchantedCreature != null) { + controller.moveCards(enchantedCreature, Zone.HAND, source, game); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DiscipleOfBolas.java b/Mage.Sets/src/mage/cards/d/DiscipleOfBolas.java index 001725d167..67fd8e63d3 100644 --- a/Mage.Sets/src/mage/cards/d/DiscipleOfBolas.java +++ b/Mage.Sets/src/mage/cards/d/DiscipleOfBolas.java @@ -75,7 +75,7 @@ class DiscipleOfBolasEffect extends OneShotEffect { sacrificed.sacrifice(source.getSourceId(), game); int power = sacrificed.getPower().getValue(); controller.gainLife(power, game, source); - controller.drawCards(power, game); + controller.drawCards(power, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/d/Discontinuity.java b/Mage.Sets/src/mage/cards/d/Discontinuity.java new file mode 100644 index 0000000000..87f82cc923 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/Discontinuity.java @@ -0,0 +1,42 @@ +package mage.cards.d; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.EndTurnEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.common.MyTurnHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Discontinuity extends CardImpl { + + public Discontinuity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}{U}"); + + // As long as it's your turn, this spell costs {2}{U}{U} less to cast. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect( + new ManaCostsImpl("{2}{U}{U}"), MyTurnCondition.instance + ).setText("as long as it's your turn, this spell costs {2}{U}{U} less to cast")) + .addHint(MyTurnHint.instance)); + + // End the turn. + this.getSpellAbility().addEffect(new EndTurnEffect()); + } + + private Discontinuity(final Discontinuity card) { + super(card); + } + + @Override + public Discontinuity copy() { + return new Discontinuity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DiscordantDirge.java b/Mage.Sets/src/mage/cards/d/DiscordantDirge.java index 4d2c5f9405..cc5d30ea2a 100644 --- a/Mage.Sets/src/mage/cards/d/DiscordantDirge.java +++ b/Mage.Sets/src/mage/cards/d/DiscordantDirge.java @@ -1,99 +1,97 @@ -package mage.cards.d; - -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.TargetCard; -import mage.target.common.TargetOpponent; - -import java.util.Objects; -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class DiscordantDirge extends CardImpl { - - private static final String rule = "Look at target opponent's hand and choose up to X cards from it, where X is the number of verse counters on {this}. That player discards those cards.."; - - public DiscordantDirge(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); - - // At the beginning of your upkeep, you may put a verse counter on Discordant Dirge. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.VERSE.createInstance(), true), TargetController.YOU, true)); - - // {B}, Sacrifice Discordant Dirge: Look at target opponent's hand and choose up to X cards from it, where X is the number of verse counters on Discordant Dirge. That player discards those cards. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new DiscordantDirgeEffect(), - new ManaCostsImpl("{B}")); - ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetOpponent()); - this.addAbility(ability); - } - - public DiscordantDirge(final DiscordantDirge card) { - super(card); - } - - @Override - public DiscordantDirge copy() { - return new DiscordantDirge(this); - } -} - -class DiscordantDirgeEffect extends OneShotEffect { - - public DiscordantDirgeEffect() { - super(Outcome.Benefit); - staticText = "Look at target opponent's hand and choose up to X cards from it, where X is the number of verse counters on {this}. That player discards those card."; - } - - public DiscordantDirgeEffect(final DiscordantDirgeEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent discordantDirge = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (discordantDirge != null) { - int verseCounters = discordantDirge.getCounters(game).getCount(CounterType.VERSE); - Player targetOpponent = game.getPlayer(source.getFirstTarget()); - Player controller = game.getPlayer(source.getControllerId()); - if (targetOpponent != null - && controller != null) { - controller.lookAtCards(targetOpponent.getName() + " hand", targetOpponent.getHand(), game); - TargetCard target = new TargetCard(0, verseCounters, Zone.HAND, new FilterCard()); - target.setNotTarget(true); - if (controller.choose(Outcome.Benefit, targetOpponent.getHand(), target, game)) { - target.getTargets().stream().map(game::getCard).filter(Objects::nonNull).filter((card) -> (card != null - && targetOpponent.getHand().contains(card.getId()))).forEachOrdered((card) -> { - targetOpponent.discard(card, source, game); - }); - return true; - } - } - } - return false; - } - - @Override - public DiscordantDirgeEffect copy() { - return new DiscordantDirgeEffect(this); - } -} +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class DiscordantDirge extends CardImpl { + + public DiscordantDirge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); + + // At the beginning of your upkeep, you may put a verse counter on Discordant Dirge. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.VERSE.createInstance(), true), TargetController.YOU, true)); + + // {B}, Sacrifice Discordant Dirge: Look at target opponent's hand and choose up to X cards from it, where X is the number of verse counters on Discordant Dirge. That player discards those cards. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new DiscordantDirgeEffect(), + new ManaCostsImpl("{B}")); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private DiscordantDirge(final DiscordantDirge card) { + super(card); + } + + @Override + public DiscordantDirge copy() { + return new DiscordantDirge(this); + } +} + +class DiscordantDirgeEffect extends OneShotEffect { + + DiscordantDirgeEffect() { + super(Outcome.Benefit); + staticText = "Look at target opponent's hand and choose up to X cards from it, " + + "where X is the number of verse counters on {this}. That player discards those cards"; + } + + private DiscordantDirgeEffect(final DiscordantDirgeEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent discordantDirge = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (discordantDirge == null) { + return false; + } + int verseCounters = discordantDirge.getCounters(game).getCount(CounterType.VERSE); + Player targetOpponent = game.getPlayer(source.getFirstTarget()); + Player controller = game.getPlayer(source.getControllerId()); + if (targetOpponent == null + || controller == null) { + return false; + } + controller.lookAtCards(targetOpponent.getName() + " hand", targetOpponent.getHand(), game); + TargetCard target = new TargetCard(0, verseCounters, Zone.HAND, new FilterCard()); + target.setNotTarget(true); + if (!controller.choose(Outcome.Benefit, targetOpponent.getHand(), target, game)) { + return false; + } + targetOpponent.discard(new CardsImpl(target.getTargets()), source, game); + return true; + } + + @Override + public DiscordantDirgeEffect copy() { + return new DiscordantDirgeEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DiscordantPiper.java b/Mage.Sets/src/mage/cards/d/DiscordantPiper.java index 2e1a8797da..ce3d17a092 100644 --- a/Mage.Sets/src/mage/cards/d/DiscordantPiper.java +++ b/Mage.Sets/src/mage/cards/d/DiscordantPiper.java @@ -1,7 +1,7 @@ package mage.cards.d; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class DiscordantPiper extends CardImpl { this.toughness = new MageInt(1); // When Discordant Piper dies, create a 0/1 white Goat creature token. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new GoatToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new GoatToken()))); } private DiscordantPiper(final DiscordantPiper card) { diff --git a/Mage.Sets/src/mage/cards/d/DiseaseCarriers.java b/Mage.Sets/src/mage/cards/d/DiseaseCarriers.java index 1c94958523..0e926a6ad8 100644 --- a/Mage.Sets/src/mage/cards/d/DiseaseCarriers.java +++ b/Mage.Sets/src/mage/cards/d/DiseaseCarriers.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class DiseaseCarriers extends CardImpl { this.toughness = new MageInt(2); // When Disease Carriers dies, target creature gets -2/-2 until end of turn. - Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), false); + Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DiseasedVermin.java b/Mage.Sets/src/mage/cards/d/DiseasedVermin.java index 8cb88d9f1b..b9d61ec3ff 100644 --- a/Mage.Sets/src/mage/cards/d/DiseasedVermin.java +++ b/Mage.Sets/src/mage/cards/d/DiseasedVermin.java @@ -1,155 +1,155 @@ -package mage.cards.d; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.WatcherScope; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.FilterOpponent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.TargetPlayer; -import mage.watchers.Watcher; - -/** - * - * @author jeffwadsworth - */ -public final class DiseasedVermin extends CardImpl { - - public DiseasedVermin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); - - this.subtype.add(SubType.RAT); - this.power = new MageInt(1); - this.toughness = new MageInt(1); - - // Whenever Diseased Vermin deals combat damage to a player, put an infection counter on it. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( - new AddCountersSourceEffect( - CounterType.INFECTION.createInstance()), - false)); - - // At the beginning of your upkeep, Diseased Vermin deals X damage to target opponent previously dealt damage by it, where X is the number of infection counters on it. - Ability ability = new BeginningOfUpkeepTriggeredAbility( - Zone.BATTLEFIELD, - new DiseasedVerminEffect(), - TargetController.YOU, - false); - ability.addWatcher(new DiseasedVerminWatcher()); - this.addAbility(ability); - - } - - private DiseasedVermin(final DiseasedVermin card) { - super(card); - } - - @Override - public DiseasedVermin copy() { - return new DiseasedVermin(this); - } -} - -class DiseasedVerminEffect extends OneShotEffect { - - static final FilterOpponent filter = new FilterOpponent("player previously dealt damage by {this}"); - - static { - filter.add(new DiseasedVerminPredicate()); - } - - public DiseasedVerminEffect() { - super(Outcome.Benefit); - this.staticText = "{this} deals X damage to target opponent previously dealt damage by it, where X is the number of infection counters on it"; - } - - public DiseasedVerminEffect(final DiseasedVerminEffect effect) { - super(effect); - } - - @Override - public DiseasedVerminEffect copy() { - return new DiseasedVerminEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent != null - && controller != null) { - TargetPlayer targetOpponent = new TargetPlayer(1, 1, false, filter); - if (targetOpponent.canChoose(controller.getId(), game) - && controller.choose(Outcome.Damage, targetOpponent, source.getSourceId(), game)) { - Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); - if (opponent != null - && sourcePermanent.getCounters(game).getCount(CounterType.INFECTION) > 0) { - opponent.damage( - sourcePermanent.getCounters(game).getCount(CounterType.INFECTION), - source.getSourceId(), game, false, true); - return true; - } - } - } - return false; - } -} - -class DiseasedVerminPredicate implements ObjectSourcePlayerPredicate> { - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - DiseasedVerminWatcher watcher = game.getState().getWatcher(DiseasedVerminWatcher.class); - if (watcher != null) { - return watcher.hasSourceDoneDamage(input.getObject().getId(), game); - } - return false; - } - - @Override - public String toString() { - return "(Player previously dealt damage by {source})"; - } -} - -class DiseasedVerminWatcher extends Watcher { - - // does not reset!! - private final Set damagedPlayers; - - public DiseasedVerminWatcher() { - super(WatcherScope.GAME); - damagedPlayers = new HashSet<>(); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == EventType.DAMAGED_PLAYER - && event.getSourceId() == sourceId) { - damagedPlayers.add(event.getTargetId()); - } - } - - public boolean hasSourceDoneDamage(UUID playerId, Game game) { - return damagedPlayers.contains(playerId); - } -} +package mage.cards.d; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterOpponent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.watchers.Watcher; + +/** + * + * @author jeffwadsworth + */ +public final class DiseasedVermin extends CardImpl { + + public DiseasedVermin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.RAT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever Diseased Vermin deals combat damage to a player, put an infection counter on it. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new AddCountersSourceEffect( + CounterType.INFECTION.createInstance()), + false)); + + // At the beginning of your upkeep, Diseased Vermin deals X damage to target opponent previously dealt damage by it, where X is the number of infection counters on it. + Ability ability = new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, + new DiseasedVerminEffect(), + TargetController.YOU, + false); + ability.addWatcher(new DiseasedVerminWatcher()); + this.addAbility(ability); + + } + + private DiseasedVermin(final DiseasedVermin card) { + super(card); + } + + @Override + public DiseasedVermin copy() { + return new DiseasedVermin(this); + } +} + +class DiseasedVerminEffect extends OneShotEffect { + + static final FilterOpponent filter = new FilterOpponent("player previously dealt damage by {this}"); + + static { + filter.add(new DiseasedVerminPredicate()); + } + + public DiseasedVerminEffect() { + super(Outcome.Benefit); + this.staticText = "{this} deals X damage to target opponent previously dealt damage by it, where X is the number of infection counters on it"; + } + + public DiseasedVerminEffect(final DiseasedVerminEffect effect) { + super(effect); + } + + @Override + public DiseasedVerminEffect copy() { + return new DiseasedVerminEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + if (sourcePermanent != null + && controller != null) { + TargetPlayer targetOpponent = new TargetPlayer(1, 1, false, filter); + if (targetOpponent.canChoose(controller.getId(), game) + && controller.choose(Outcome.Damage, targetOpponent, source.getSourceId(), game)) { + Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); + if (opponent != null + && sourcePermanent.getCounters(game).getCount(CounterType.INFECTION) > 0) { + opponent.damage( + sourcePermanent.getCounters(game).getCount(CounterType.INFECTION), + source.getSourceId(), game, false, true); + return true; + } + } + } + return false; + } +} + +class DiseasedVerminPredicate implements ObjectSourcePlayerPredicate> { + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + DiseasedVerminWatcher watcher = game.getState().getWatcher(DiseasedVerminWatcher.class); + if (watcher != null) { + return watcher.hasSourceDoneDamage(input.getObject().getId(), game); + } + return false; + } + + @Override + public String toString() { + return "(Player previously dealt damage by {source})"; + } +} + +class DiseasedVerminWatcher extends Watcher { + + // does not reset!! + private final Set damagedPlayers; + + public DiseasedVerminWatcher() { + super(WatcherScope.GAME); + damagedPlayers = new HashSet<>(); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == EventType.DAMAGED_PLAYER + && event.getSourceId() == sourceId) { + damagedPlayers.add(event.getTargetId()); + } + } + + public boolean hasSourceDoneDamage(UUID playerId, Game game) { + return damagedPlayers.contains(playerId); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DismantlingWave.java b/Mage.Sets/src/mage/cards/d/DismantlingWave.java new file mode 100644 index 0000000000..f38f154db1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DismantlingWave.java @@ -0,0 +1,73 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DismantlingWave extends CardImpl { + + private static final FilterPermanent filter + = new FilterArtifactOrEnchantmentPermanent("artifacts and enchantments"); + + public DismantlingWave(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}"); + + // For each opponent, destroy up to one target artifact or enchantment that player controls. + this.getSpellAbility().addEffect(new DestroyTargetEffect(false, true) + .setText("For each opponent, destroy up to one target artifact or enchantment that player controls.")); + this.getSpellAbility().setTargetAdjuster(DismantlingWaveAdjuster.instance); + + // Cycling {6}{W}{W} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{6}{W}{W}"))); + + // When you cycle Dismantling Wave, destroy all artifacts and enchantments. + this.addAbility(new CycleTriggeredAbility(new DestroyAllEffect(filter))); + } + + private DismantlingWave(final DismantlingWave card) { + super(card); + } + + @Override + public DismantlingWave copy() { + return new DismantlingWave(this); + } +} + +enum DismantlingWaveAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + game.getOpponents(ability.getControllerId()) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .forEachOrdered(player -> { + FilterPermanent filter = new FilterArtifactOrEnchantmentPermanent( + "artifact or enchantment controlled by " + player.getName() + ); + filter.add(new ControllerIdPredicate(player.getId())); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + }); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DismissIntoDream.java b/Mage.Sets/src/mage/cards/d/DismissIntoDream.java index 16a2addbe1..2c2a2fcd09 100644 --- a/Mage.Sets/src/mage/cards/d/DismissIntoDream.java +++ b/Mage.Sets/src/mage/cards/d/DismissIntoDream.java @@ -3,13 +3,15 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.common.BecomesTargetTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.continuous.CreaturesBecomeOtherTypeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -20,13 +22,18 @@ import mage.game.permanent.Permanent; */ public final class DismissIntoDream extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Each creature your opponents control"); + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + public DismissIntoDream(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{6}{U}"); // Each creature your opponents control is an Illusion in addition to its other types // and has "When this creature becomes the target of a spell or ability, sacrifice it." - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DismissIntoDreamEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DismissIntoDreamEffect(filter))); } public DismissIntoDream(final DismissIntoDream card) { @@ -39,16 +46,11 @@ public final class DismissIntoDream extends CardImpl { } } -class DismissIntoDreamEffect extends ContinuousEffectImpl { +class DismissIntoDreamEffect extends CreaturesBecomeOtherTypeEffect { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - - DismissIntoDreamEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); - this.staticText = "Each creature your opponents control is an Illusion in addition to its other types and has \"When this creature becomes the target of a spell or ability, sacrifice it.\""; + DismissIntoDreamEffect(FilterPermanent filter) { + super(filter, SubType.ILLUSION, Duration.WhileOnBattlefield); + this.outcome = Outcome.Detriment; } DismissIntoDreamEffect(final DismissIntoDreamEffect effect) { @@ -67,23 +69,24 @@ class DismissIntoDreamEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (Permanent object: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - switch (layer) { - case AbilityAddingRemovingEffects_6: - object.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), source.getSourceId(), game); - break; - case TypeChangingEffects_4: - if (!object.hasSubtype(SubType.ILLUSION, game)) { - object.getSubtype(game).add(SubType.ILLUSION); - } - break; + super.apply(layer, sublayer, source, game); + + if (layer == Layer.AbilityAddingRemovingEffects_6) { + for (Permanent object: game.getBattlefield().getActivePermanents(this.filter, source.getControllerId(), game)) { + object.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), source.getSourceId(), game); } } + return true; } @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + return super.hasLayer(layer) || layer == Layer.AbilityAddingRemovingEffects_6; + } + + @Override + public String getText(Mode mode) { + return super.getText(mode) + " and has \"When this creature becomes the target of a spell or ability, sacrifice it.\""; } } diff --git a/Mage.Sets/src/mage/cards/d/Dispatch.java b/Mage.Sets/src/mage/cards/d/Dispatch.java index 6fd5815ce9..5ecd0f6348 100644 --- a/Mage.Sets/src/mage/cards/d/Dispatch.java +++ b/Mage.Sets/src/mage/cards/d/Dispatch.java @@ -1,32 +1,37 @@ - - package mage.cards.d; -import java.util.UUID; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Loki */ public final class Dispatch extends CardImpl { - public Dispatch (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}"); + public Dispatch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); + // Tap target creature. this.getSpellAbility().addEffect(new TapTargetEffect()); + + // Metalcraft — If you control three or more artifacts, exile that creature. this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ExileTargetEffect(), MetalcraftCondition.instance, "Metalcraft — If you control three or more artifacts, exile that creature")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().setAbilityWord(AbilityWord.METALCRAFT); + this.getSpellAbility().addHint(MetalcraftHint.instance); } - public Dispatch (final Dispatch card) { + public Dispatch(final Dispatch card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/d/DispenseJustice.java b/Mage.Sets/src/mage/cards/d/DispenseJustice.java index 98da926193..fbafe60573 100644 --- a/Mage.Sets/src/mage/cards/d/DispenseJustice.java +++ b/Mage.Sets/src/mage/cards/d/DispenseJustice.java @@ -1,35 +1,38 @@ - - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SacrificeEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.common.FilterAttackingCreature; import mage.game.Game; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author maurer.it_at_gmail.com */ public final class DispenseJustice extends CardImpl { - public DispenseJustice (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}"); - + public DispenseJustice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + // Target player sacrifices an attacking creature. + // Metalcraft — That player sacrifices two attacking creatures instead if you control three or more artifacts. this.getSpellAbility().addEffect(new DispenseJusticeEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().setAbilityWord(AbilityWord.METALCRAFT); + this.getSpellAbility().addHint(MetalcraftHint.instance); } - public DispenseJustice (final DispenseJustice card) { + public DispenseJustice(final DispenseJustice card) { super(card); } @@ -47,21 +50,20 @@ class DispenseJusticeEffect extends OneShotEffect { private static final FilterAttackingCreature filter = new FilterAttackingCreature(); - DispenseJusticeEffect ( ) { + DispenseJusticeEffect() { super(Outcome.Sacrifice); staticText = effectText; } - DispenseJusticeEffect ( DispenseJusticeEffect effect ) { + DispenseJusticeEffect(DispenseJusticeEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - if ( MetalcraftCondition.instance.apply(game, source) ) { + if (MetalcraftCondition.instance.apply(game, source)) { return new SacrificeEffect(filter, 2, effectText).apply(game, source); - } - else { + } else { return new SacrificeEffect(filter, 1, effectText).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/d/Displace.java b/Mage.Sets/src/mage/cards/d/Displace.java index e7cfe8012d..87ab48e600 100644 --- a/Mage.Sets/src/mage/cards/d/Displace.java +++ b/Mage.Sets/src/mage/cards/d/Displace.java @@ -23,7 +23,7 @@ public final class Displace extends CardImpl { this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, 2, new FilterControlledCreaturePermanent("creatures you control"), false)); Effect effect = new ExileTargetForSourceEffect(); this.getSpellAbility().addEffect(effect); - effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect() + effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false) .withReturnNames("those cards", "their owner's").concatBy(", then"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/d/DistantMemories.java b/Mage.Sets/src/mage/cards/d/DistantMemories.java index f93aabe1e4..3833a540a9 100644 --- a/Mage.Sets/src/mage/cards/d/DistantMemories.java +++ b/Mage.Sets/src/mage/cards/d/DistantMemories.java @@ -65,7 +65,7 @@ class DistantMemoriesEffect extends OneShotEffect { if (controller.searchLibrary(target, source, game)) { Card card = controller.getLibrary().remove(target.getFirstTarget(), game); if (card != null) { - card.moveToZone(Zone.EXILED, source.getSourceId(), game, false); + controller.moveCards(card, Zone.EXILED, source, game); controller.shuffleLibrary(source, game); StringBuilder sb = new StringBuilder(); @@ -76,16 +76,20 @@ class DistantMemoriesEffect extends OneShotEffect { Set opponents = game.getOpponents(source.getControllerId()); for (UUID opponentUuid : opponents) { Player opponent = game.getPlayer(opponentUuid); - if (opponent != null - && opponent.chooseUse(Outcome.Detriment, sb.toString(), source, game)) { - putInHand = true; + if (opponent != null) { + if (opponent.chooseUse(Outcome.Detriment, sb.toString(), source, game)) { + putInHand = true; + game.informPlayers(opponent.getName() + " decides to put the selected card into the player's hand."); + } else { + game.informPlayers(opponent.getName() + " decides to leave the card in exile."); + } } } if (putInHand) { controller.moveCards(card, Zone.HAND, source, game); } else { - controller.drawCards(3, game); + controller.drawCards(3, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java index 13500ac144..8b6c5813b8 100644 --- a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java +++ b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java @@ -1,38 +1,38 @@ - package mage.cards.d; -import java.util.List; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CastSourceTriggeredAbility; import mage.abilities.keyword.EmergeAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; import mage.target.common.TargetOpponent; +import java.util.UUID; +import mage.constants.Zone; + /** - * * @author fireshoes */ public final class DistendedMindbender extends CardImpl { public DistendedMindbender(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{8}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{8}"); this.subtype.add(SubType.ELDRAZI); this.subtype.add(SubType.INSECT); this.power = new MageInt(5); @@ -88,30 +88,20 @@ class DistendedMindbenderEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player opponent = game.getPlayer(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); - if (opponent != null && controller != null) { - opponent.revealCards("Distended Mindbender", opponent.getHand(), game); - TargetCard targetThreeOrLess = new TargetCard(1, Zone.HAND, filterThreeOrLess); - TargetCard targetFourOrGreater = new TargetCard(1, Zone.HAND, filterFourOrGreater); - if (controller.choose(Outcome.Benefit, opponent.getHand(), targetThreeOrLess, game)) { - List targets = targetThreeOrLess.getTargets(); - for (UUID targetId : targets) { - Card card = opponent.getHand().get(targetId, game); - if (card != null) { - opponent.discard(card, source, game); - } - } - } - if (controller.choose(Outcome.Benefit, opponent.getHand(), targetFourOrGreater, game)) { - List targets = targetFourOrGreater.getTargets(); - for (UUID targetId : targets) { - Card card = opponent.getHand().get(targetId, game); - if (card != null) { - opponent.discard(card, source, game); - } - } - } - return true; + if (opponent == null || controller == null) { + return false; } - return false; + opponent.revealCards(source, opponent.getHand(), game); + TargetCard targetThreeOrLess = new TargetCard(1, Zone.HAND, filterThreeOrLess); + TargetCard targetFourOrGreater = new TargetCard(1, Zone.HAND, filterFourOrGreater); + Cards toDiscard = new CardsImpl(); + if (controller.chooseTarget(Outcome.Benefit, opponent.getHand(), targetThreeOrLess, source, game)) { + toDiscard.addAll(targetThreeOrLess.getTargets()); + } + if (controller.chooseTarget(Outcome.Benefit, opponent.getHand(), targetFourOrGreater, source, game)) { + toDiscard.addAll(targetFourOrGreater.getTargets()); + } + opponent.discard(toDiscard, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DivinerSpirit.java b/Mage.Sets/src/mage/cards/d/DivinerSpirit.java index 0b844df343..0796e31099 100644 --- a/Mage.Sets/src/mage/cards/d/DivinerSpirit.java +++ b/Mage.Sets/src/mage/cards/d/DivinerSpirit.java @@ -64,8 +64,8 @@ class DivinerSpiritEffect extends OneShotEffect { if (sourceController != null && damagedPlayer != null) { int amount = (Integer) getValue("damage"); if (amount > 0) { - sourceController.drawCards(amount, game); - damagedPlayer.drawCards(amount, game); + sourceController.drawCards(amount, source.getSourceId(), game); + damagedPlayer.drawCards(amount, source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DivinersLockbox.java b/Mage.Sets/src/mage/cards/d/DivinersLockbox.java index 488626d2c4..ce8c46dbd8 100644 --- a/Mage.Sets/src/mage/cards/d/DivinersLockbox.java +++ b/Mage.Sets/src/mage/cards/d/DivinersLockbox.java @@ -84,7 +84,7 @@ class DivinersLockboxEffect extends OneShotEffect { player.revealCards(source, new CardsImpl(card), game); if (choice.getChoice().equals(card.getName())) { sacEffect.apply(game, source); - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/d/DiviningWitch.java b/Mage.Sets/src/mage/cards/d/DiviningWitch.java index 39e231aa20..a1a2f862bb 100644 --- a/Mage.Sets/src/mage/cards/d/DiviningWitch.java +++ b/Mage.Sets/src/mage/cards/d/DiviningWitch.java @@ -92,7 +92,7 @@ public final class DiviningWitch extends CardImpl { if (card != null) { cardsToReaveal.add(card); // Put that card into your hand - if (CardUtil.haveSameNames(card.getName(), name)) { + if (CardUtil.haveSameNames(card, name, game)) { cardToHand = card; break; } diff --git a/Mage.Sets/src/mage/cards/d/DizzyingGaze.java b/Mage.Sets/src/mage/cards/d/DizzyingGaze.java index 83f24c04ec..38ab4fa84e 100644 --- a/Mage.Sets/src/mage/cards/d/DizzyingGaze.java +++ b/Mage.Sets/src/mage/cards/d/DizzyingGaze.java @@ -1,71 +1,71 @@ -package mage.cards.d; - -import java.util.UUID; -import mage.constants.SubType; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.constants.Outcome; -import mage.abilities.keyword.EnchantAbility; -import mage.abilities.keyword.FlyingAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.target.TargetPermanent; -import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.common.TargetCreaturePermanent; - -/** - * - * @author jeffwadsworth - */ -public final class DizzyingGaze extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("with flying"); - - static { - filter.add(new AbilityPredicate(FlyingAbility.class)); - } - - public DizzyingGaze(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature you control - TargetPermanent auraTarget = new TargetControlledCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // {R}: Enchanted creature deals 1 damage to target creature with flying. - Ability ability2 = new SimpleActivatedAbility(new DamageTargetEffect(1), new ManaCostsImpl("{R}")); - ability2.addTarget(new TargetCreaturePermanent(filter)); - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - new GainAbilityAttachedEffect( - ability2, - AttachmentType.AURA, - Duration.WhileOnBattlefield))); - - } - - public DizzyingGaze(final DizzyingGaze card) { - super(card); - } - - @Override - public DizzyingGaze copy() { - return new DizzyingGaze(this); - } -} +package mage.cards.d; + +import java.util.UUID; +import mage.constants.SubType; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.constants.Outcome; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author jeffwadsworth + */ +public final class DizzyingGaze extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public DizzyingGaze(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature you control + TargetPermanent auraTarget = new TargetControlledCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // {R}: Enchanted creature deals 1 damage to target creature with flying. + Ability ability2 = new SimpleActivatedAbility(new DamageTargetEffect(1), new ManaCostsImpl("{R}")); + ability2.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityAttachedEffect( + ability2, + AttachmentType.AURA, + Duration.WhileOnBattlefield))); + + } + + public DizzyingGaze(final DizzyingGaze card) { + super(card); + } + + @Override + public DizzyingGaze copy() { + return new DizzyingGaze(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DomriAnarchOfBolas.java b/Mage.Sets/src/mage/cards/d/DomriAnarchOfBolas.java index c7d33f04a5..295ccb1645 100644 --- a/Mage.Sets/src/mage/cards/d/DomriAnarchOfBolas.java +++ b/Mage.Sets/src/mage/cards/d/DomriAnarchOfBolas.java @@ -12,9 +12,7 @@ import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.players.Player; import mage.target.TargetPermanent; @@ -27,13 +25,6 @@ import java.util.UUID; */ public final class DomriAnarchOfBolas extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public DomriAnarchOfBolas(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{R}{G}"); @@ -52,7 +43,7 @@ public final class DomriAnarchOfBolas extends CardImpl { // -2: Target creature you control fights target creature you don't control. Ability ability = new LoyaltyAbility(new FightTargetsEffect(), -2); ability.addTarget(new TargetControlledCreaturePermanent()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); } @@ -98,4 +89,4 @@ class DomriAnarchOfBolasEffect extends OneShotEffect { game.addEffect(new CantBeCounteredControlledEffect(StaticFilters.FILTER_SPELL_A_CREATURE, Duration.EndOfTurn), source); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/d/DomriChaosBringer.java b/Mage.Sets/src/mage/cards/d/DomriChaosBringer.java index 5ed58faaa1..b0b518583c 100644 --- a/Mage.Sets/src/mage/cards/d/DomriChaosBringer.java +++ b/Mage.Sets/src/mage/cards/d/DomriChaosBringer.java @@ -17,7 +17,7 @@ import mage.abilities.keyword.RiotAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterSpell; +import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.CardIdPredicate; import mage.game.Game; @@ -26,6 +26,7 @@ import mage.game.events.GameEvent; import mage.players.Player; import java.util.UUID; +import mage.game.stack.StackObject; /** * @author TheElk801 @@ -42,19 +43,22 @@ public final class DomriChaosBringer extends CardImpl { // +1: Add {R} or {G}. If that mana is spent on a creature spell, it gains riot. this.addAbility(new LoyaltyAbility(new DomriChaosBringerEffect(), 1)); - // −3: Look at the top four cards of your library. You may reveal up to two creature cards from among them and put them into your hand. Put the rest on the bottom of your library in a random order. + // −3: Look at the top four cards of your library. You may reveal up to two + // creature cards from among them and put them into your hand. Put the rest + // on the bottom of your library in a random order. this.addAbility(new LoyaltyAbility(new LookLibraryAndPickControllerEffect( StaticValue.get(4), false, StaticValue.get(2), StaticFilters.FILTER_CARD_CREATURE, Zone.LIBRARY, false, true, true, Zone.HAND, false, false, false ).setText( - "Look at the top four cards of your library. " + - "You may reveal up to two creature cards from among them " + - "and put them into your hand. Put the rest on the bottom of your library " + - "in a random order." + "Look at the top four cards of your library. " + + "You may reveal up to two creature cards from among them " + + "and put them into your hand. Put the rest on the bottom of your library " + + "in a random order." ), -3)); - // −8: You get an emblem with "At the beginning of each end step, create a 4/4 red and green Beast creature token with trample." + // −8: You get an emblem with "At the beginning of each end step, create a 4/4 red + // and green Beast creature token with trample." this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new DomriChaosBringerEmblem()), -8)); } @@ -136,9 +140,15 @@ class DomriChaosBringerTriggeredAbility extends DelayedTriggeredAbility { if (mo == null || !mo.isCreature()) { return false; } + + StackObject stackObject = game.getStack().getStackObject(event.getTargetId()); + + if (stackObject == null) { + return false; + } this.getEffects().clear(); - FilterSpell filter = new FilterSpell(); - filter.add(new CardIdPredicate(event.getTargetId())); + FilterCard filter = new FilterCard(); + filter.add(new CardIdPredicate(stackObject.getSourceId())); this.addEffect(new GainAbilityControlledSpellsEffect(new RiotAbility(), filter)); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DomrisAmbush.java b/Mage.Sets/src/mage/cards/d/DomrisAmbush.java index f1c177cb97..99eeb781a0 100644 --- a/Mage.Sets/src/mage/cards/d/DomrisAmbush.java +++ b/Mage.Sets/src/mage/cards/d/DomrisAmbush.java @@ -1,19 +1,14 @@ package mage.cards.d; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -52,33 +47,3 @@ public final class DomrisAmbush extends CardImpl { return new DomrisAmbush(this); } } - -class DomrisAmbushEffect extends OneShotEffect { - - DomrisAmbushEffect() { - super(Outcome.Benefit); - staticText = "Put a +1/+1 counter on target creature you control. " + - "Then that creature deals damage equal to its power " + - "to target creature or planeswalker you don't control."; - } - - private DomrisAmbushEffect(final DomrisAmbushEffect effect) { - super(effect); - } - - @Override - public DomrisAmbushEffect copy() { - return new DomrisAmbushEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent == null) { - return false; - } - permanent.addCounters(CounterType.P1P1.createInstance(), source, game); - game.applyEffects(); - return new DamageWithPowerFromOneToAnotherTargetEffect().apply(game, source); - } -} diff --git a/Mage.Sets/src/mage/cards/d/DoomForetold.java b/Mage.Sets/src/mage/cards/d/DoomForetold.java index 2a997df0c6..97b9126f8e 100644 --- a/Mage.Sets/src/mage/cards/d/DoomForetold.java +++ b/Mage.Sets/src/mage/cards/d/DoomForetold.java @@ -96,7 +96,7 @@ class DoomForetoldEffect extends OneShotEffect { } player.discard(1, false, source, game); player.loseLife(2, game, false); - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); controller.gainLife(2, game, source); effect1.apply(game, source); effect2.apply(game, source); diff --git a/Mage.Sets/src/mage/cards/d/DoomedDissenter.java b/Mage.Sets/src/mage/cards/d/DoomedDissenter.java index 04e3dd2cce..08c87cbdc9 100644 --- a/Mage.Sets/src/mage/cards/d/DoomedDissenter.java +++ b/Mage.Sets/src/mage/cards/d/DoomedDissenter.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class DoomedDissenter extends CardImpl { this.toughness = new MageInt(1); // When Doomed Dissenter dies, create a 2/2 black Zombie creature token. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ZombieToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ZombieToken()))); } public DoomedDissenter(final DoomedDissenter card) { diff --git a/Mage.Sets/src/mage/cards/d/DoomedTraveler.java b/Mage.Sets/src/mage/cards/d/DoomedTraveler.java index d1d89ccfa3..cbdbfefeaf 100644 --- a/Mage.Sets/src/mage/cards/d/DoomedTraveler.java +++ b/Mage.Sets/src/mage/cards/d/DoomedTraveler.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class DoomedTraveler extends CardImpl { this.toughness = new MageInt(1); // When Doomed Traveler dies, create a 1/1 white Spirit creature token with flying. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken()))); } public DoomedTraveler(final DoomedTraveler card) { diff --git a/Mage.Sets/src/mage/cards/d/Doomsday.java b/Mage.Sets/src/mage/cards/d/Doomsday.java index 59f04c1df2..15ed6ab58a 100644 --- a/Mage.Sets/src/mage/cards/d/Doomsday.java +++ b/Mage.Sets/src/mage/cards/d/Doomsday.java @@ -58,47 +58,24 @@ class DoomsdayEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); - if (player != null) { + if (controller != null) { //Search your library and graveyard for five cards Cards allCards = new CardsImpl(); - Cards cards = new CardsImpl(); - allCards.addAll(player.getLibrary().getCardList()); - allCards.addAll(player.getGraveyard()); + allCards.addAll(controller.getLibrary().getCardList()); + allCards.addAll(controller.getGraveyard()); int number = Math.min(5, allCards.size()); TargetCard target = new TargetCard(number, number, Zone.ALL, new FilterCard()); - if (player.choose(Outcome.Benefit, allCards, target, game)) { - // exile the rest - for (UUID uuid : allCards) { - if (!target.getTargets().contains(uuid)) { - Card card = game.getCard(uuid); - if (card != null) { - card.moveToExile(null, "Doomsday", source.getSourceId(), game); - } - } else { - cards.add(uuid); - } - - } + if (controller.choose(Outcome.Benefit, allCards, target, game)) { + Cards toLibrary = new CardsImpl(target.getTargets()); + allCards.removeAll(toLibrary); + // Exile the rest + controller.moveCards(allCards, Zone.EXILED, source, game); //Put the chosen cards on top of your library in any order - target = new TargetCard(Zone.ALL, new FilterCard("Card to put on top")); - while (cards.size() > 1 && player.canRespond()) { - player.choose(Outcome.Neutral, cards, target, game); - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - cards.remove(card); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - target.clearChosen(); - } - if (cards.size() == 1) { - Card card = cards.get(cards.iterator().next(), game); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } + controller.putCardsOnTopOfLibrary(toLibrary, game, source, true); } - return true; } return false; diff --git a/Mage.Sets/src/mage/cards/d/DoubleVision.java b/Mage.Sets/src/mage/cards/d/DoubleVision.java new file mode 100644 index 0000000000..f71749174e --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DoubleVision.java @@ -0,0 +1,86 @@ +package mage.cards.d; + +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.CopyTargetSpellEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author htrajan + */ +public final class DoubleVision extends CardImpl { + + public DoubleVision(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}"); + + // Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy. + this.addAbility(new DoubleVisionCopyTriggeredAbility(), new SpellsCastWatcher()); + } + + private DoubleVision(final DoubleVision card) { + super(card); + } + + @Override + public DoubleVision copy() { + return new DoubleVision(this); + } +} + +class DoubleVisionCopyTriggeredAbility extends SpellCastControllerTriggeredAbility { + + DoubleVisionCopyTriggeredAbility() { + super(new CopyTargetSpellEffect(true), new FilterInstantOrSorcerySpell(), false); + } + + DoubleVisionCopyTriggeredAbility(DoubleVisionCopyTriggeredAbility ability) { + super(ability); + } + + @Override + public DoubleVisionCopyTriggeredAbility copy() { + return new DoubleVisionCopyTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (super.checkTrigger(event, game)) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (isFirstInstantOrSorceryCastByPlayerOnTurn(spell, game)) { + this.getEffects().get(0).setTargetPointer(new FixedTarget(spell.getId())); + return true; + } + } + return false; + } + + private boolean isFirstInstantOrSorceryCastByPlayerOnTurn(Spell spell, Game game) { + if (spell != null) { + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + if (watcher != null) { + List eligibleSpells = watcher.getSpellsCastThisTurn(this.getControllerId()) + .stream() + .filter(s -> s.isInstant() || s.isSorcery()) + .collect(Collectors.toList()); + return eligibleSpells.size() == 1 && eligibleSpells.get(0).getId().equals(spell.getId()); + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy."; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java b/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java index 34eaf023bc..dc14c627a5 100644 --- a/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java +++ b/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java @@ -2,30 +2,35 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.SpellAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.PreventDamageByTargetEffect; import mage.abilities.effects.common.PreventDamageToTargetEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.cards.Card; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.game.Game; +import mage.filter.predicate.Predicates; import mage.target.TargetPermanent; -import mage.util.CardUtil; import java.util.UUID; -import mage.abilities.mana.ManaAbility; -import mage.game.stack.Spell; /** * @author TheElk801 */ public final class DovinHandOfControl extends CardImpl { + private static final FilterCard filter = new FilterCard("Artifact, instant, and sorcery spells"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.INSTANT.getPredicate(), + CardType.SORCERY.getPredicate())); + } + public DovinHandOfControl(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{W/U}"); @@ -34,7 +39,7 @@ public final class DovinHandOfControl extends CardImpl { this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); // Artifact, instant, and sorcery spells your opponents cast cost {1} more to cast. - this.addAbility(new SimpleStaticAbility(new DovinHandOfControlEffect())); + this.addAbility(new SimpleStaticAbility(new SpellsCostIncreasingAllEffect(1, filter, TargetController.OPPONENT))); // -1: Until your next turn, prevent all damage that would be dealt to and dealt by target permanent an opponent controls. Ability ability = new LoyaltyAbility(new PreventDamageToTargetEffect( @@ -55,40 +60,4 @@ public final class DovinHandOfControl extends CardImpl { public DovinHandOfControl copy() { return new DovinHandOfControl(this); } -} - -class DovinHandOfControlEffect extends CostModificationEffectImpl { - - DovinHandOfControlEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Artifact, instant, and sorcery spells your opponents cast cost {1} more to cast"; - } - - private DovinHandOfControlEffect(DovinHandOfControlEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -1); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - Card card = game.getCard(abilityToModify.getSourceId()); - if (!(abilityToModify instanceof SpellAbility)) { - return false; - } - return card != null - && (card.isInstantOrSorcery() - || card.isArtifact()) - && game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId()); - } - - @Override - public DovinHandOfControlEffect copy() { - return new DovinHandOfControlEffect(this); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/Downsize.java b/Mage.Sets/src/mage/cards/d/Downsize.java index be1526a164..e91c97ca90 100644 --- a/Mage.Sets/src/mage/cards/d/Downsize.java +++ b/Mage.Sets/src/mage/cards/d/Downsize.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -10,36 +8,29 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Downsize extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public Downsize(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); // Target creature you don't control gets -4/-0 until end of turn. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); - this.getSpellAbility().addEffect(new BoostTargetEffect(-4,0, Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.getSpellAbility().addEffect(new BoostTargetEffect(-4, 0, Duration.EndOfTurn)); // Overload {2}{U} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.") - this.addAbility(new OverloadAbility(this, new BoostAllEffect(-4,0, Duration.EndOfTurn,filter,false), new ManaCostsImpl("{2}{U}"))); + this.addAbility(new OverloadAbility(this, new BoostAllEffect(-4, 0, Duration.EndOfTurn, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false), new ManaCostsImpl("{2}{U}"))); } - public Downsize(final Downsize card) { + private Downsize(final Downsize card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/d/Draco.java b/Mage.Sets/src/mage/cards/d/Draco.java index 973d2433ef..736a234e7a 100644 --- a/Mage.Sets/src/mage/cards/d/Draco.java +++ b/Mage.Sets/src/mage/cards/d/Draco.java @@ -35,7 +35,7 @@ public final class Draco extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Domain - Draco costs {2} less to cast for each basic land type among lands you control. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new DracoCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new DracoCostReductionEffect())); // Domain - At the beginning of your upkeep, sacrifice Draco unless you pay {10}. This cost is reduced by {2} for each basic land type among lands you control. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DracoSacrificeUnlessPaysEffect(), TargetController.YOU, false)); diff --git a/Mage.Sets/src/mage/cards/d/DraconicRoar.java b/Mage.Sets/src/mage/cards/d/DraconicRoar.java index b9baa020cf..b020519c61 100644 --- a/Mage.Sets/src/mage/cards/d/DraconicRoar.java +++ b/Mage.Sets/src/mage/cards/d/DraconicRoar.java @@ -92,7 +92,7 @@ class DraconicRoarEffect extends OneShotEffect { if (controller != null) { DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = game.getState().getWatcher(DragonOnTheBattlefieldWhileSpellWasCastWatcher.class); if (watcher != null && watcher.castWithConditionTrue(source.getId())) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java b/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java index e8c6687c80..bc91ad64c3 100644 --- a/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java +++ b/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java @@ -1,16 +1,13 @@ package mage.cards.d; -import mage.Mana; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.DevotionCount; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; +import mage.constants.CardType; +import mage.constants.Zone; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -24,7 +21,7 @@ public final class DragToTheUnderworld extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{B}"); // This spell costs {X} less to cast, where X is your devotion to black. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new DragToTheUnderworldEffect()) + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(DevotionCount.B)) .addHint(DevotionCount.B.getHint()) .setRuleAtTheTop(true)); @@ -41,41 +38,4 @@ public final class DragToTheUnderworld extends CardImpl { public DragToTheUnderworld copy() { return new DragToTheUnderworld(this); } -} - -class DragToTheUnderworldEffect extends CostModificationEffectImpl { - - DragToTheUnderworldEffect() { - super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "This spell costs {X} less to cast, where X is your devotion to black. " + - "(Each {B} in the mana costs of permanents you control counts toward your devotion to black.) "; - } - - private DragToTheUnderworldEffect(final DragToTheUnderworldEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - Mana mana = spellAbility.getManaCostsToPay().getMana(); - if (mana.getGeneric() == 0) { - return false; - } - int count = DevotionCount.B.calculate(game, source, this); - mana.setGeneric(Math.max(mana.getGeneric() - count, 0)); - spellAbility.getManaCostsToPay().load(mana.toString()); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify instanceof SpellAbility - && abilityToModify.getSourceId().equals(source.getSourceId()); - } - - @Override - public DragToTheUnderworldEffect copy() { - return new DragToTheUnderworldEffect(this); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DragonEgg.java b/Mage.Sets/src/mage/cards/d/DragonEgg.java index 7758179a67..defc2e95f8 100644 --- a/Mage.Sets/src/mage/cards/d/DragonEgg.java +++ b/Mage.Sets/src/mage/cards/d/DragonEgg.java @@ -2,7 +2,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class DragonEgg extends CardImpl { this.addAbility(DefenderAbility.getInstance()); // When Dragon Egg dies, create a 2/2 red Dragon creature token with flying. It has "{R}: This creature gets +1/+0 until end of turn". - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new DragonEggDragonToken()), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new DragonEggDragonToken()), false)); } diff --git a/Mage.Sets/src/mage/cards/d/DragonGrip.java b/Mage.Sets/src/mage/cards/d/DragonGrip.java index 9280f0c4a2..6637c97a21 100644 --- a/Mage.Sets/src/mage/cards/d/DragonGrip.java +++ b/Mage.Sets/src/mage/cards/d/DragonGrip.java @@ -32,9 +32,9 @@ public final class DragonGrip extends CardImpl { // Ferocious - If you control a creature with power 4 or greater, you may cast Dragon Grip as though it had flash. AsThoughEffect effect = new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame); - effect.setText("Ferocious — If you control a creature with power 4 or greater, you may cast Dragon Grip as though it had flash"); this.addAbility(new SimpleStaticAbility(Zone.ALL, new ConditionalAsThoughEffect(effect, - FerociousCondition.instance)) + FerociousCondition.instance).setText("Ferocious — If you control a creature with power 4 or greater, " + + "you may cast Dragon Grip as though it had flash")) .addHint(FerociousHint.instance)); // Enchant creature diff --git a/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java b/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java index 68e4876378..68e0371562 100644 --- a/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java +++ b/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -30,7 +29,7 @@ public final class DragonlordAtarka extends CardImpl { } public DragonlordAtarka(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{R}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{G}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ELDER); this.subtype.add(SubType.DRAGON); @@ -45,7 +44,10 @@ public final class DragonlordAtarka extends CardImpl { // When Dragonlord Atarka enters the battlefield, it deals 5 damage divided as you choose among any number of target creatures and/or planeswalkers your opponents control. Ability ability = new EntersBattlefieldTriggeredAbility(new DamageMultiEffect(5, "it"), false); - ability.addTarget(new TargetCreatureOrPlaneswalkerAmount(5, filter)); + TargetCreatureOrPlaneswalkerAmount target = new TargetCreatureOrPlaneswalkerAmount(5, filter); + target.setMinNumberOfTargets(1); + target.setMaxNumberOfTargets(5); + ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java b/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java index c24d6985bc..36d69f9f50 100644 --- a/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java +++ b/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.keyword.FlyingAbility; @@ -30,7 +30,7 @@ public final class DragonlordDromoka extends CardImpl { this.toughness = new MageInt(7); // Dragonlord Dromoka can't be countered - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Flying this.addAbility(FlyingAbility.getInstance()); // Lifelink diff --git a/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java b/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java index e4b4464ed3..ccf9f82913 100644 --- a/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java +++ b/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -16,9 +15,9 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.SuperType; import mage.game.Game; import mage.game.permanent.Permanent; @@ -26,6 +25,7 @@ import mage.players.Player; import mage.target.common.TargetCreatureOrPlaneswalker; import mage.util.CardUtil; import mage.util.GameLog; +import mage.watchers.common.LostControlWatcher; /** * @@ -50,6 +50,7 @@ public final class DragonlordSilumgar extends CardImpl { // When Dragonlord Silumgar enters the battlefield, gain control of target creature or planeswalker for as long as you control Dragonlord Silumgar. Ability ability = new EntersBattlefieldTriggeredAbility(new DragonlordSilumgarEffect(), false); ability.addTarget(new TargetCreatureOrPlaneswalker()); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DrainpipeVermin.java b/Mage.Sets/src/mage/cards/d/DrainpipeVermin.java index 65991fd0e4..23be4152f1 100644 --- a/Mage.Sets/src/mage/cards/d/DrainpipeVermin.java +++ b/Mage.Sets/src/mage/cards/d/DrainpipeVermin.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ColoredManaCost; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.discard.DiscardTargetEffect; @@ -29,7 +29,7 @@ public final class DrainpipeVermin extends CardImpl { this.toughness = new MageInt(1); // When Drainpipe Vermin dies, you may pay {B}. If you do, target player discards a card. - Ability ability = new DiesTriggeredAbility(new DoIfCostPaid(new DiscardTargetEffect(1), new ColoredManaCost(ColoredManaSymbol.B)), false); + Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new DiscardTargetEffect(1), new ColoredManaCost(ColoredManaSymbol.B)), false); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DrannithHealer.java b/Mage.Sets/src/mage/cards/d/DrannithHealer.java new file mode 100644 index 0000000000..a1bbe65de5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DrannithHealer.java @@ -0,0 +1,43 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DrannithHealer extends CardImpl { + + public DrannithHealer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever you cycle another card, you gain 1 life. + this.addAbility(new CycleControllerTriggeredAbility(new GainLifeEffect(1), false, true)); + + // Cycling {1} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}"))); + } + + private DrannithHealer(final DrannithHealer card) { + super(card); + } + + @Override + public DrannithHealer copy() { + return new DrannithHealer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DrannithMagistrate.java b/Mage.Sets/src/mage/cards/d/DrannithMagistrate.java new file mode 100644 index 0000000000..583b757833 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DrannithMagistrate.java @@ -0,0 +1,75 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DrannithMagistrate extends CardImpl { + + public DrannithMagistrate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Your opponents can't cast spells from anywhere other than their hands. + this.addAbility(new SimpleStaticAbility(new DrannithMagistrateEffect())); + } + + private DrannithMagistrate(final DrannithMagistrate card) { + super(card); + } + + @Override + public DrannithMagistrate copy() { + return new DrannithMagistrate(this); + } +} + +class DrannithMagistrateEffect extends ContinuousRuleModifyingEffectImpl { + + DrannithMagistrateEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Your opponents can't cast spells from anywhere other than their hands"; + } + + private DrannithMagistrateEffect(final DrannithMagistrateEffect effect) { + super(effect); + } + + @Override + public DrannithMagistrateEffect copy() { + return new DrannithMagistrateEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + return false; + } + Card card = game.getCard(event.getSourceId()); + if (card == null) { + return false; + } + return game.getState().getZone(card.getId()) != Zone.HAND; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DrannithStinger.java b/Mage.Sets/src/mage/cards/d/DrannithStinger.java new file mode 100644 index 0000000000..03c72c6350 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DrannithStinger.java @@ -0,0 +1,46 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DrannithStinger extends CardImpl { + + public DrannithStinger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever you cycle another card, Drannith Stinger deals 1 damage to each opponent. + this.addAbility(new CycleControllerTriggeredAbility( + new DamagePlayersEffect(1, TargetController.OPPONENT), false, true + )); + + // Cycling {1} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}"))); + } + + private DrannithStinger(final DrannithStinger card) { + super(card); + } + + @Override + public DrannithStinger copy() { + return new DrannithStinger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DrasticRevelation.java b/Mage.Sets/src/mage/cards/d/DrasticRevelation.java index faec9cf5da..df6a334e9a 100644 --- a/Mage.Sets/src/mage/cards/d/DrasticRevelation.java +++ b/Mage.Sets/src/mage/cards/d/DrasticRevelation.java @@ -2,10 +2,8 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; @@ -25,7 +23,7 @@ public final class DrasticRevelation extends CardImpl { this.getSpellAbility().addEffect(new DrasticRevelationEffect()); } - public DrasticRevelation(final DrasticRevelation card) { + private DrasticRevelation(final DrasticRevelation card) { super(card); } @@ -42,24 +40,19 @@ class DrasticRevelationEffect extends OneShotEffect { staticText = "Discard your hand. Draw seven cards, then discard three cards at random"; } - DrasticRevelationEffect(final DrasticRevelationEffect effect) { + private DrasticRevelationEffect(final DrasticRevelationEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player you = game.getPlayer(source.getControllerId()); - if (you != null) { - you.discard(you.getHand().size(), false, source, game); - you.drawCards(7, game); - Cards hand = you.getHand(); - for (int i = 0; i < 3; i++) { - Card card = hand.getRandom(game); - if (card != null) { - you.discard(card, source, game); - } - } + if (you == null) { + return false; } + you.discard(you.getHand(), source, game); + you.drawCards(7, source.getSourceId(), game); + you.discard(3, true, source, game); return false; } @@ -67,4 +60,4 @@ class DrasticRevelationEffect extends OneShotEffect { public DrasticRevelationEffect copy() { return new DrasticRevelationEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/d/DreadCacodemon.java b/Mage.Sets/src/mage/cards/d/DreadCacodemon.java index cb2b37606c..ab005dde42 100644 --- a/Mage.Sets/src/mage/cards/d/DreadCacodemon.java +++ b/Mage.Sets/src/mage/cards/d/DreadCacodemon.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.TapAllEffect; @@ -44,7 +44,7 @@ public final class DreadCacodemon extends CardImpl { // if you cast it from your hand, destroy all creatures your opponents control, then tap all other creatures you control. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(opponentsCreatures, false)); ability.addEffect(new TapAllEffect(otherCreaturesYouControl)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourceCondition.instance, + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, destroy all creatures your opponents control, then tap all other creatures you control."), new CastFromHandWatcher()); } diff --git a/Mage.Sets/src/mage/cards/d/DreadSpecter.java b/Mage.Sets/src/mage/cards/d/DreadSpecter.java index c3cb1d8e77..50e5aca675 100644 --- a/Mage.Sets/src/mage/cards/d/DreadSpecter.java +++ b/Mage.Sets/src/mage/cards/d/DreadSpecter.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -38,7 +38,7 @@ public final class DreadSpecter extends CardImpl { // Whenever Dread Specter blocks or becomes blocked by a nonblack creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false)); } public DreadSpecter(final DreadSpecter card) { diff --git a/Mage.Sets/src/mage/cards/d/DreadSummons.java b/Mage.Sets/src/mage/cards/d/DreadSummons.java index 0367d259c0..8a2812efbf 100644 --- a/Mage.Sets/src/mage/cards/d/DreadSummons.java +++ b/Mage.Sets/src/mage/cards/d/DreadSummons.java @@ -1,23 +1,22 @@ - package mage.cards.d; -import java.util.Set; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateTokenEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; +import mage.game.permanent.token.Token; import mage.game.permanent.token.ZombieToken; import mage.players.Player; +import java.util.Objects; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DreadSummons extends CardImpl { @@ -26,10 +25,10 @@ public final class DreadSummons extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{B}{B}"); // Each player puts the top X cards of their library into their graveyard. For each creature card put into a graveyard this way, you create a tapped 2/2 black Zombie creature token. - getSpellAbility().addEffect(new DreadSummonsEffect()); + this.getSpellAbility().addEffect(new DreadSummonsEffect()); } - public DreadSummons(final DreadSummons card) { + private DreadSummons(final DreadSummons card) { super(card); } @@ -41,12 +40,15 @@ public final class DreadSummons extends CardImpl { class DreadSummonsEffect extends OneShotEffect { - public DreadSummonsEffect() { + private static final Token token = new ZombieToken(); + + DreadSummonsEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Each player puts the top X cards of their library into their graveyard. For each creature card put into a graveyard this way, you create a tapped 2/2 black Zombie creature token"; + this.staticText = "each player mills X cards. For each creature card put into a graveyard this way, " + + "you create a tapped 2/2 black Zombie creature token"; } - public DreadSummonsEffect(final DreadSummonsEffect effect) { + private DreadSummonsEffect(final DreadSummonsEffect effect) { super(effect); } @@ -57,28 +59,24 @@ class DreadSummonsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int numberOfCards = source.getManaCostsToPay().getX(); - if (numberOfCards > 0) { - int numberOfCreatureCards = 0; - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Set movedCards = player.moveCardsToGraveyardWithInfo(player.getLibrary().getTopCards(game, numberOfCards), source, game, Zone.LIBRARY); - for (Card card : movedCards) { - if (card.isCreature()) { - numberOfCreatureCards++; - } - } - } - } - if (numberOfCreatureCards > 0) { - return new CreateTokenEffect(new ZombieToken(), numberOfCreatureCards, true, false).apply(game, source); - } + int creatureCount = 0; + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; } - return true; + creatureCount += player + .millCards(source.getManaCostsToPay().getX(), source, game) + .getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> game.getState().getZone(card.getId()) == Zone.GRAVEYARD) + .filter(MageObject::isCreature) + .count(); } - return false; + if (creatureCount > 0) { + token.putOntoBattlefield(creatureCount, game, source.getSourceId(), source.getControllerId()); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DreadWight.java b/Mage.Sets/src/mage/cards/d/DreadWight.java index e3225c405f..0483bcaebe 100644 --- a/Mage.Sets/src/mage/cards/d/DreadWight.java +++ b/Mage.Sets/src/mage/cards/d/DreadWight.java @@ -1,233 +1,233 @@ -package mage.cards.d; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; -import mage.abilities.condition.Condition; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.ContinuousRuleModifyingEffect; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author jeffwadsworth - */ -public final class DreadWight extends CardImpl { - - public DreadWight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); - - this.subtype.add(SubType.ZOMBIE); - this.power = new MageInt(3); - this.toughness = new MageInt(4); - - // At end of combat, put a paralyzation counter on each creature blocking or blocked by Dread Wight and tap those creatures. Each of those creatures doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it. Each of those creatures gains "{4}: Remove a paralyzation counter from this creature." - this.addAbility(new DreadWightTriggeredAbility( - new CreateDelayedTriggeredAbilityEffect( - new AtTheEndOfCombatDelayedTriggeredAbility( - new DreadWightEffect())))); - - } - - public DreadWight(final DreadWight card) { - super(card); - } - - @Override - public DreadWight copy() { - return new DreadWight(this); - } -} - -class DreadWightTriggeredAbility extends TriggeredAbilityImpl { - - DreadWightTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - this.usesStack = false; - } - - DreadWightTriggeredAbility(final DreadWightTriggeredAbility ability) { - super(ability); - } - - @Override - public DreadWightTriggeredAbility copy() { - return new DreadWightTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return (event.getType() == EventType.BLOCKER_DECLARED); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(getSourceId())) { // Dread Wight is the blocker - getAllEffects().setTargetPointer(new FixedTarget(event.getTargetId())); - return true; - } - if (event.getTargetId().equals(getSourceId())) { // Dread Wight is the attacker - getAllEffects().setTargetPointer(new FixedTarget(event.getSourceId())); - return true; - } - return false; - } - -} - -class DreadWightEffect extends OneShotEffect { - - String rule = "doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it."; - - public DreadWightEffect() { - super(Outcome.Detriment); - this.staticText = "put a paralyzation counter on each creature blocking or blocked by {this} and tap those creatures. " - + "Each of those creatures doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it. " - + "Each of those creatures gains \"{4}: Remove a paralyzation counter from this creature.\""; - } - - public DreadWightEffect(final DreadWightEffect effect) { - super(effect); - } - - @Override - public DreadWightEffect copy() { - return new DreadWightEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); - if (permanent != null) { - // add paralyzation counter - Effect effect = new AddCountersTargetEffect(CounterType.PARALYZATION.createInstance()); - effect.setTargetPointer(new FixedTarget(permanent.getId())); - effect.apply(game, source); - // tap permanent - permanent.tap(game); - // does not untap while paralyzation counter is on it - ContinuousRuleModifyingEffect effect2 = new DreadWightDoNotUntapEffect( - Duration.WhileOnBattlefield, - permanent.getId()); - effect2.setText("This creature doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it"); - Condition condition = new DreadWightCounterCondition(permanent.getId()); - ConditionalContinuousRuleModifyingEffect conditionalEffect = new ConditionalContinuousRuleModifyingEffect( - effect2, - condition); - Ability ability = new SimpleStaticAbility( - Zone.BATTLEFIELD, - conditionalEffect); - ContinuousEffect effect3 = new GainAbilityTargetEffect( - ability, - Duration.WhileOnBattlefield); - ability.setRuleVisible(true); - effect3.setTargetPointer(new FixedTarget(permanent.getId())); - game.addEffect(effect3, source); - // each gains 4: remove paralyzation counter - Ability activatedAbility = new SimpleActivatedAbility( - Zone.BATTLEFIELD, - new RemoveCounterSourceEffect(CounterType.PARALYZATION.createInstance()), - new ManaCostsImpl("{4}")); - ContinuousEffect effect4 = new GainAbilityTargetEffect( - activatedAbility, - Duration.WhileOnBattlefield); - effect4.setTargetPointer(new FixedTarget(permanent.getId())); - game.addEffect(effect4, source); - return true; - } - return false; - } -} - -class DreadWightDoNotUntapEffect extends ContinuousRuleModifyingEffectImpl { - - UUID permanentId; - - public DreadWightDoNotUntapEffect(Duration duration, UUID permanentId) { - super(duration, Outcome.Detriment); - this.permanentId = permanentId; - } - - public DreadWightDoNotUntapEffect(final DreadWightDoNotUntapEffect effect) { - super(effect); - this.permanentId = effect.permanentId; - } - - @Override - public DreadWightDoNotUntapEffect copy() { - return new DreadWightDoNotUntapEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public String getInfoMessage(Ability source, GameEvent event, Game game) { - Permanent permanentToUntap = game.getPermanent((event.getTargetId())); - if (permanentToUntap != null) { - return permanentToUntap.getLogName() + " doesn't untap."; - } - return null; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UNTAP; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getTargetId() == permanentId - && game.isActivePlayer(game.getPermanent(permanentId).getControllerId()); - } -} - -class DreadWightCounterCondition implements Condition { - - UUID permanentId; - - public DreadWightCounterCondition(UUID permanentId) { - this.permanentId = permanentId; - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(permanentId); - if (permanent != null) { - return permanent.getCounters(game).getCount(CounterType.PARALYZATION) > 0; - } - return false; - } - - @Override - public String toString() { - return "has counter on it"; - } -} +package mage.cards.d; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousRuleModifyingEffect; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jeffwadsworth + */ +public final class DreadWight extends CardImpl { + + public DreadWight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // At end of combat, put a paralyzation counter on each creature blocking or blocked by Dread Wight and tap those creatures. Each of those creatures doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it. Each of those creatures gains "{4}: Remove a paralyzation counter from this creature." + this.addAbility(new DreadWightTriggeredAbility( + new CreateDelayedTriggeredAbilityEffect( + new AtTheEndOfCombatDelayedTriggeredAbility( + new DreadWightEffect())))); + + } + + public DreadWight(final DreadWight card) { + super(card); + } + + @Override + public DreadWight copy() { + return new DreadWight(this); + } +} + +class DreadWightTriggeredAbility extends TriggeredAbilityImpl { + + DreadWightTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect); + this.usesStack = false; + } + + DreadWightTriggeredAbility(final DreadWightTriggeredAbility ability) { + super(ability); + } + + @Override + public DreadWightTriggeredAbility copy() { + return new DreadWightTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return (event.getType() == EventType.BLOCKER_DECLARED); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getSourceId().equals(getSourceId())) { // Dread Wight is the blocker + getAllEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + return true; + } + if (event.getTargetId().equals(getSourceId())) { // Dread Wight is the attacker + getAllEffects().setTargetPointer(new FixedTarget(event.getSourceId())); + return true; + } + return false; + } + +} + +class DreadWightEffect extends OneShotEffect { + + String rule = "doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it."; + + public DreadWightEffect() { + super(Outcome.Detriment); + this.staticText = "put a paralyzation counter on each creature blocking or blocked by {this} and tap those creatures. " + + "Each of those creatures doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it. " + + "Each of those creatures gains \"{4}: Remove a paralyzation counter from this creature.\""; + } + + public DreadWightEffect(final DreadWightEffect effect) { + super(effect); + } + + @Override + public DreadWightEffect copy() { + return new DreadWightEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); + if (permanent != null) { + // add paralyzation counter + Effect effect = new AddCountersTargetEffect(CounterType.PARALYZATION.createInstance()); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + // tap permanent + permanent.tap(game); + // does not untap while paralyzation counter is on it + ContinuousRuleModifyingEffect effect2 = new DreadWightDoNotUntapEffect( + Duration.WhileOnBattlefield, + permanent.getId()); + effect2.setText("This creature doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it"); + Condition condition = new DreadWightCounterCondition(permanent.getId()); + ConditionalContinuousRuleModifyingEffect conditionalEffect = new ConditionalContinuousRuleModifyingEffect( + effect2, + condition); + Ability ability = new SimpleStaticAbility( + Zone.BATTLEFIELD, + conditionalEffect); + ContinuousEffect effect3 = new GainAbilityTargetEffect( + ability, + Duration.WhileOnBattlefield); + ability.setRuleVisible(true); + effect3.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect3, source); + // each gains 4: remove paralyzation counter + Ability activatedAbility = new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new RemoveCounterSourceEffect(CounterType.PARALYZATION.createInstance()), + new ManaCostsImpl("{4}")); + ContinuousEffect effect4 = new GainAbilityTargetEffect( + activatedAbility, + Duration.WhileOnBattlefield); + effect4.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect4, source); + return true; + } + return false; + } +} + +class DreadWightDoNotUntapEffect extends ContinuousRuleModifyingEffectImpl { + + UUID permanentId; + + public DreadWightDoNotUntapEffect(Duration duration, UUID permanentId) { + super(duration, Outcome.Detriment); + this.permanentId = permanentId; + } + + public DreadWightDoNotUntapEffect(final DreadWightDoNotUntapEffect effect) { + super(effect); + this.permanentId = effect.permanentId; + } + + @Override + public DreadWightDoNotUntapEffect copy() { + return new DreadWightDoNotUntapEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public String getInfoMessage(Ability source, GameEvent event, Game game) { + Permanent permanentToUntap = game.getPermanent((event.getTargetId())); + if (permanentToUntap != null) { + return permanentToUntap.getLogName() + " doesn't untap."; + } + return null; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UNTAP; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getTargetId() == permanentId + && game.isActivePlayer(game.getPermanent(permanentId).getControllerId()); + } +} + +class DreadWightCounterCondition implements Condition { + + UUID permanentId; + + public DreadWightCounterCondition(UUID permanentId) { + this.permanentId = permanentId; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(permanentId); + if (permanent != null) { + return permanent.getCounters(game).getCount(CounterType.PARALYZATION) > 0; + } + return false; + } + + @Override + public String toString() { + return "has counter on it"; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DreadhordeButcher.java b/Mage.Sets/src/mage/cards/d/DreadhordeButcher.java index 6e99a3b55f..bd8699396c 100644 --- a/Mage.Sets/src/mage/cards/d/DreadhordeButcher.java +++ b/Mage.Sets/src/mage/cards/d/DreadhordeButcher.java @@ -3,7 +3,7 @@ package mage.cards.d; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -39,7 +39,7 @@ public final class DreadhordeButcher extends CardImpl { ), false).setOrPlaneswalker(true)); // When Dreadhorde Butcher dies, it deals damage equal to its power to any target. - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect( + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect( new SourcePermanentPowerCount() ).setText("it deals damage equal to its power to any target")); ability.addTarget(new TargetAnyTarget()); diff --git a/Mage.Sets/src/mage/cards/d/DreadshipReef.java b/Mage.Sets/src/mage/cards/d/DreadshipReef.java index 53a75729b9..504c0f04f5 100644 --- a/Mage.Sets/src/mage/cards/d/DreadshipReef.java +++ b/Mage.Sets/src/mage/cards/d/DreadshipReef.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -7,9 +6,10 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; -import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ import mage.counters.CounterType; public final class DreadshipReef extends CardImpl { public DreadshipReef(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {tap}: Add {C}. this.addAbility(new ColorlessManaAbility()); @@ -36,7 +36,8 @@ public final class DreadshipReef extends CardImpl { this.addAbility(ability); // {1}, Remove X storage counters from Dreadship Reef: Add X mana in any combination of {U} and/or {B}. ability = new SimpleManaAbility(Zone.BATTLEFIELD, - new AddManaInAnyCombinationEffect(RemovedCountersForCostValue.instance, ColoredManaSymbol.U, ColoredManaSymbol.B), + new AddManaInAnyCombinationEffect(RemovedCountersForCostValue.instance, + new CountersSourceCount(CounterType.STORAGE), ColoredManaSymbol.U, ColoredManaSymbol.B), new GenericManaCost(1)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/d/DreamCache.java b/Mage.Sets/src/mage/cards/d/DreamCache.java index cebb4bd18c..f971d3826f 100644 --- a/Mage.Sets/src/mage/cards/d/DreamCache.java +++ b/Mage.Sets/src/mage/cards/d/DreamCache.java @@ -58,7 +58,7 @@ class DreamCacheEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(3, game); + controller.drawCards(3, source.getSourceId(), game); boolean putOnTop = controller.chooseUse(Outcome.Neutral, "Put cards on top?", source, game); TargetCardInHand target = new TargetCardInHand(2, 2, new FilterCard()); controller.chooseTarget(Outcome.Detriment, target, source, game); diff --git a/Mage.Sets/src/mage/cards/d/DreamChisel.java b/Mage.Sets/src/mage/cards/d/DreamChisel.java index 8508e9b24b..22503b4d2a 100644 --- a/Mage.Sets/src/mage/cards/d/DreamChisel.java +++ b/Mage.Sets/src/mage/cards/d/DreamChisel.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.cards.CardImpl; @@ -9,10 +7,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.other.FaceDownCastablePredicate; + +import java.util.UUID; /** - * * @author North */ public final class DreamChisel extends CardImpl { @@ -20,11 +19,11 @@ public final class DreamChisel extends CardImpl { private static final FilterCreatureCard filter = new FilterCreatureCard("Face-down creature spells"); static { - filter.add(FaceDownPredicate.instance); + filter.add(FaceDownCastablePredicate.instance); } public DreamChisel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Face-down creature spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); diff --git a/Mage.Sets/src/mage/cards/d/DreamCoat.java b/Mage.Sets/src/mage/cards/d/DreamCoat.java index ee5585f44b..3f88c6d44d 100644 --- a/Mage.Sets/src/mage/cards/d/DreamCoat.java +++ b/Mage.Sets/src/mage/cards/d/DreamCoat.java @@ -109,7 +109,7 @@ class BecomesColorOrColorsEnchantedEffect extends OneShotEffect { String colors = new String(sb); ObjectColor chosenColors = new ObjectColor(colors); ContinuousEffect effect = new BecomesColorTargetEffect(chosenColors, Duration.Custom); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); return true; diff --git a/Mage.Sets/src/mage/cards/d/DreamEater.java b/Mage.Sets/src/mage/cards/d/DreamEater.java index de2fe37416..5c72856648 100644 --- a/Mage.Sets/src/mage/cards/d/DreamEater.java +++ b/Mage.Sets/src/mage/cards/d/DreamEater.java @@ -1,30 +1,28 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.abilities.effects.keyword.SurveilEffect; -import mage.constants.SubType; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class DreamEater extends CardImpl { @@ -44,14 +42,12 @@ public final class DreamEater extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Dream Eater enters the battlefield, surveil 4. When you do, you may return target nonland permanent an opponent controls to its owner's hand. - Ability ability = new EntersBattlefieldTriggeredAbility( - new SurveilEffect(4) - ); - ability.addEffect(new DreamEaterCreateReflexiveTriggerEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new SurveilEffect(4)); + ability.addEffect(new DreamEaterEffect()); this.addAbility(ability); } - public DreamEater(final DreamEater card) { + private DreamEater(final DreamEater card) { super(card); } @@ -61,31 +57,7 @@ public final class DreamEater extends CardImpl { } } -class DreamEaterCreateReflexiveTriggerEffect extends OneShotEffect { - - public DreamEaterCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - this.staticText = "When you do, you may return target " - + "nonland permanent an opponent controls to its owner's hand."; - } - - public DreamEaterCreateReflexiveTriggerEffect(final DreamEaterCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public DreamEaterCreateReflexiveTriggerEffect copy() { - return new DreamEaterCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new DreamEaterReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class DreamEaterReflexiveTriggeredAbility extends DelayedTriggeredAbility { +class DreamEaterEffect extends OneShotEffect { private static final FilterPermanent filter = new FilterNonlandPermanent("nonland permanent an opponent controls"); @@ -94,35 +66,29 @@ class DreamEaterReflexiveTriggeredAbility extends DelayedTriggeredAbility { filter.add(TargetController.OPPONENT.getControllerPredicate()); } - public DreamEaterReflexiveTriggeredAbility() { - super(new ReturnToHandTargetEffect()); - this.addTarget(new TargetPermanent(filter)); - this.optional = true; + DreamEaterEffect() { + super(Outcome.Benefit); + this.staticText = "When you do, you may return target " + + "nonland permanent an opponent controls to its owner's hand."; } - public DreamEaterReflexiveTriggeredAbility(final DreamEaterReflexiveTriggeredAbility ability) { - super(ability); + private DreamEaterEffect(final DreamEaterEffect effect) { + super(effect); } @Override - public DreamEaterReflexiveTriggeredAbility copy() { - return new DreamEaterReflexiveTriggeredAbility(this); + public DreamEaterEffect copy() { + return new DreamEaterEffect(this); } @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you surveil 4, you may return target nonland permanent " - + "an opponent controls to its owner's hand."; + public boolean apply(Game game, Ability source) { + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ReturnToHandTargetEffect(), true, + "you may return target nonland permanent an opponent controls to its owner's hand" + ); + ability.addTarget(new TargetPermanent(filter)); + game.fireReflexiveTriggeredAbility(ability, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DreamFracture.java b/Mage.Sets/src/mage/cards/d/DreamFracture.java index d5aaa50294..2148a395a3 100644 --- a/Mage.Sets/src/mage/cards/d/DreamFracture.java +++ b/Mage.Sets/src/mage/cards/d/DreamFracture.java @@ -70,7 +70,7 @@ class DreamFractureEffect extends OneShotEffect { countered = true; } if (controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return countered; } diff --git a/Mage.Sets/src/mage/cards/d/DreamLeash.java b/Mage.Sets/src/mage/cards/d/DreamLeash.java index bccc18cffd..f0f15cdb1b 100644 --- a/Mage.Sets/src/mage/cards/d/DreamLeash.java +++ b/Mage.Sets/src/mage/cards/d/DreamLeash.java @@ -1,21 +1,21 @@ package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.ControlEnchantedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.constants.SubType; import mage.target.TargetPermanent; +import mage.target.common.TargetTappedPermanentAsYouCast; + +import java.util.UUID; /** * @@ -28,15 +28,18 @@ public final class DreamLeash extends CardImpl { this.subtype.add(SubType.AURA); // Enchant permanent - // You can't choose an untapped permanent as Dream Leash's target as you cast Dream Leash. - TargetPermanent auraTarget = new DreamLeashTarget(); + TargetPermanent auraTarget = new TargetTappedPermanentAsYouCast(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.GainControl)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); + // You can't choose an untapped permanent as Dream Leash's target as you cast Dream Leash. + Effect effect = new ControlEnchantedEffect("permanent"); + effect.setText("You can't choose an untapped permanent as {this}'s target as you cast {this}.
" + effect.getText(null)); + // You control enchanted permanent. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ControlEnchantedEffect("permanent"))); + this.addAbility(new SimpleStaticAbility(effect)); } private DreamLeash(final DreamLeash card) { @@ -47,19 +50,4 @@ public final class DreamLeash extends CardImpl { public DreamLeash copy() { return new DreamLeash(this); } -} - - -class DreamLeashTarget extends TargetPermanent { - - @Override - public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { - - if(super.canTarget(controllerId, id, source, game)){ - Permanent permanent = game.getPermanent(id); - return permanent.isTapped(); - } - return false; - } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DreamSalvage.java b/Mage.Sets/src/mage/cards/d/DreamSalvage.java index a62f2ea0c5..397ef520cb 100644 --- a/Mage.Sets/src/mage/cards/d/DreamSalvage.java +++ b/Mage.Sets/src/mage/cards/d/DreamSalvage.java @@ -3,7 +3,6 @@ package mage.cards.d; import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -97,7 +96,7 @@ class DreamSalvageEffect extends OneShotEffect { && controller != null && watcher != null && watcher.getAmountCardsDiscarded(targetOpponent.getId()) > 0) { - controller.drawCards(watcher.getAmountCardsDiscarded(targetOpponent.getId()), game); + controller.drawCards(watcher.getAmountCardsDiscarded(targetOpponent.getId()), source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/d/DreambornMuse.java b/Mage.Sets/src/mage/cards/d/DreambornMuse.java index 169107a55c..1ea48de2e1 100644 --- a/Mage.Sets/src/mage/cards/d/DreambornMuse.java +++ b/Mage.Sets/src/mage/cards/d/DreambornMuse.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.dynamicvalue.common.CardsInTargetPlayerHandCount; @@ -12,26 +10,26 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; +import java.util.UUID; + /** - * * @author cbrianhill */ public final class DreambornMuse extends CardImpl { public DreambornMuse(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); this.subtype.add(SubType.SPIRIT); this.power = new MageInt(2); this.toughness = new MageInt(2); - + // At the beginning of each player's upkeep, that player puts the top X cards of their library into their graveyard, where X is the number of cards in their hand. PutLibraryIntoGraveTargetEffect effect = new PutLibraryIntoGraveTargetEffect(CardsInTargetPlayerHandCount.instance); - effect.setText("that player puts the top X cards of their library into their graveyard, where X is the number of cards in their hand."); + effect.setText("that player mills X cards, where X is the number of cards in their hand"); this.addAbility(new BeginningOfUpkeepTriggeredAbility(effect, TargetController.ANY, false)); - } - public DreambornMuse(final DreambornMuse card) { + private DreambornMuse(final DreambornMuse card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/d/DreamtailHeron.java b/Mage.Sets/src/mage/cards/d/DreamtailHeron.java new file mode 100644 index 0000000000..6b0891091c --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DreamtailHeron.java @@ -0,0 +1,46 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DreamtailHeron extends CardImpl { + + public DreamtailHeron(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.BIRD); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Mutate {3}{U} + this.addAbility(new MutateAbility(this, "{3}{U}")); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever this creature mutates, draw a card. + this.addAbility(new MutatesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1))); + } + + private DreamtailHeron(final DreamtailHeron card) { + super(card); + } + + @Override + public DreamtailHeron copy() { + return new DreamtailHeron(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DredgeTheMire.java b/Mage.Sets/src/mage/cards/d/DredgeTheMire.java new file mode 100644 index 0000000000..59b99dbc23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DredgeTheMire.java @@ -0,0 +1,80 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DredgeTheMire extends CardImpl { + + public DredgeTheMire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); + + // Each opponent chooses a creature card in their graveyard. Put those cards onto the battlefield under your control. + this.getSpellAbility().addEffect(new DredgeTheMireEffect()); + } + + private DredgeTheMire(final DredgeTheMire card) { + super(card); + } + + @Override + public DredgeTheMire copy() { + return new DredgeTheMire(this); + } +} + +class DredgeTheMireEffect extends OneShotEffect { + + DredgeTheMireEffect() { + super(Outcome.Benefit); + staticText = "Each opponent chooses a creature card in their graveyard. " + + "Put those cards onto the battlefield under your control."; + } + + private DredgeTheMireEffect(final DredgeTheMireEffect effect) { + super(effect); + } + + @Override + public DredgeTheMireEffect copy() { + return new DredgeTheMireEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(opponentId); + if (player == null || player.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game) == 0) { + continue; + } + TargetCard target = new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD); + target.setNotTarget(true); + if (!player.choose(outcome, player.getGraveyard(), target, game)) { + continue; + } + cards.add(target.getFirstTarget()); + } + return controller.moveCards(cards, Zone.BATTLEFIELD, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/d/Drelnoch.java b/Mage.Sets/src/mage/cards/d/Drelnoch.java index 21b7ae3e2d..c82c191d86 100644 --- a/Mage.Sets/src/mage/cards/d/Drelnoch.java +++ b/Mage.Sets/src/mage/cards/d/Drelnoch.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class Drelnoch extends CardImpl { this.toughness = new MageInt(3); // Whenever Drelnoch becomes blocked, you may draw two cards. - this.addAbility(new BecomesBlockedTriggeredAbility(new DrawCardSourceControllerEffect(2), true)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(2), true)); } public Drelnoch(final Drelnoch card) { diff --git a/Mage.Sets/src/mage/cards/d/DriftingMeadow.java b/Mage.Sets/src/mage/cards/d/DriftingMeadow.java index 0aad8815f3..a980ba89dc 100644 --- a/Mage.Sets/src/mage/cards/d/DriftingMeadow.java +++ b/Mage.Sets/src/mage/cards/d/DriftingMeadow.java @@ -22,8 +22,8 @@ public final class DriftingMeadow extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.LAND},""); this.addAbility(new EntersBattlefieldTappedAbility()); - this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); this.addAbility(new WhiteManaAbility()); + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); } public DriftingMeadow(final DriftingMeadow card) { diff --git a/Mage.Sets/src/mage/cards/d/DrippingTongueZubera.java b/Mage.Sets/src/mage/cards/d/DrippingTongueZubera.java index ed36a32932..ba93772a8b 100644 --- a/Mage.Sets/src/mage/cards/d/DrippingTongueZubera.java +++ b/Mage.Sets/src/mage/cards/d/DrippingTongueZubera.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.ZuberasDiedDynamicValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class DrippingTongueZubera extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritToken(), ZuberasDiedDynamicValue.instance), false), new ZuberasDiedWatcher()); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SpiritToken(), ZuberasDiedDynamicValue.instance), false), new ZuberasDiedWatcher()); } public DrippingTongueZubera (final DrippingTongueZubera card) { diff --git a/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java b/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java index 67b28222df..debff34c88 100644 --- a/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java +++ b/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -21,7 +21,7 @@ import mage.target.common.TargetCardInYourGraveyard; */ public final class DriverOfTheDead extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard to the battlefield"); + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard"); static { filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); @@ -35,7 +35,7 @@ public final class DriverOfTheDead extends CardImpl { this.toughness = new MageInt(2); // When Driver of the Dead dies, return target creature card with converted mana cost 2 or less from your graveyard to the battlefield. - Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), false); Target target = new TargetCardInYourGraveyard(filter); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/d/DroidCommando.java b/Mage.Sets/src/mage/cards/d/DroidCommando.java index 981a61d28a..2c86fd7f72 100644 --- a/Mage.Sets/src/mage/cards/d/DroidCommando.java +++ b/Mage.Sets/src/mage/cards/d/DroidCommando.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.keyword.RepairAbility; @@ -27,7 +27,7 @@ public final class DroidCommando extends CardImpl { this.toughness = new MageInt(2); // When Droid Commando dies, target player loses 2 life and you gain 2 life. - Ability ability = new DiesTriggeredAbility(new LoseLifeTargetEffect(2)); + Ability ability = new DiesSourceTriggeredAbility(new LoseLifeTargetEffect(2)); ability.addEffect(new GainLifeEffect(2)); ability.addTarget(new TargetPlayer()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/d/DromokasCommand.java b/Mage.Sets/src/mage/cards/d/DromokasCommand.java index cfe17d2161..6d8b427d44 100644 --- a/Mage.Sets/src/mage/cards/d/DromokasCommand.java +++ b/Mage.Sets/src/mage/cards/d/DromokasCommand.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Mode; import mage.abilities.effects.Effect; import mage.abilities.effects.common.FightTargetsEffect; @@ -12,8 +10,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterEnchantmentPermanent; import mage.filter.common.FilterInstantOrSorcerySpell; @@ -22,19 +20,15 @@ import mage.target.TargetSpell; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class DromokasCommand extends CardImpl { private static final FilterEnchantmentPermanent filterEnchantment = new FilterEnchantmentPermanent("an enchantment"); private static final FilterCreaturePermanent filterCreature = new FilterCreaturePermanent("creature to put a +1/+1 counter on it"); - private static final FilterCreaturePermanent filterUncontrolledCreature = new FilterCreaturePermanent("creature you don't control"); - - static { - filterUncontrolledCreature.add(TargetController.NOT_YOU.getControllerPredicate()); - } public DromokasCommand(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}{W}"); @@ -69,12 +63,12 @@ public final class DromokasCommand extends CardImpl { effect.setText("Target creature you control fights target creature you don't control"); mode.addEffect(effect); mode.addTarget(new TargetControlledCreaturePermanent()); - mode.addTarget(new TargetCreaturePermanent(filterUncontrolledCreature)); + mode.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.getSpellAbility().getModes().addMode(mode); } - public DromokasCommand(final DromokasCommand card) { + private DromokasCommand(final DromokasCommand card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/d/Dromosaur.java b/Mage.Sets/src/mage/cards/d/Dromosaur.java index edc3d67690..99cac6e8ce 100644 --- a/Mage.Sets/src/mage/cards/d/Dromosaur.java +++ b/Mage.Sets/src/mage/cards/d/Dromosaur.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class Dromosaur extends CardImpl { this.toughness = new MageInt(3); // Whenever Dromosaur blocks or becomes blocked by a creature, it gets +2/-2 until end of turn. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(2, -2, Duration.EndOfTurn), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(2, -2, Duration.EndOfTurn), false)); } public Dromosaur(final Dromosaur card) { diff --git a/Mage.Sets/src/mage/cards/d/DrossRipper.java b/Mage.Sets/src/mage/cards/d/DrossRipper.java index d9867e585f..836f431631 100644 --- a/Mage.Sets/src/mage/cards/d/DrossRipper.java +++ b/Mage.Sets/src/mage/cards/d/DrossRipper.java @@ -22,7 +22,7 @@ public final class DrossRipper extends CardImpl { public DrossRipper (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{2}{B}"))); diff --git a/Mage.Sets/src/mage/cards/d/Drought.java b/Mage.Sets/src/mage/cards/d/Drought.java index 201ab8d18d..090b050191 100644 --- a/Mage.Sets/src/mage/cards/d/Drought.java +++ b/Mage.Sets/src/mage/cards/d/Drought.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -11,12 +10,12 @@ import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.AbilityType; +import mage.constants.CardType; import mage.constants.CostModificationType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; @@ -54,9 +53,10 @@ public final class Drought extends CardImpl { class DroughtAdditionalCostEffect extends CostModificationEffectImpl { - private boolean appliesToSpells; + private final boolean appliesToSpells; private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Swamp"); - static{ + + static { filter.add(SubType.SWAMP.getPredicate()); } diff --git a/Mage.Sets/src/mage/cards/d/DrownInFilth.java b/Mage.Sets/src/mage/cards/d/DrownInFilth.java index c9befad3dd..5513828419 100644 --- a/Mage.Sets/src/mage/cards/d/DrownInFilth.java +++ b/Mage.Sets/src/mage/cards/d/DrownInFilth.java @@ -28,7 +28,7 @@ public final class DrownInFilth extends CardImpl { // Choose target creature. Put the top four cards of your library into your graveyard, then that creature gets -1/-1 until end of turn for each land card in your graveyard. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); Effect effect = new PutTopCardOfLibraryIntoGraveControllerEffect(4); - effect.setText("Choose target creature. Put the top four cards of your library into your graveyard"); + effect.setText("Choose target creature. Mill four cards"); this.getSpellAbility().addEffect(effect); DynamicValue landCards = new SignInversionDynamicValue(new CardsInControllerGraveyardCount(new FilterLandCard())); this.getSpellAbility().addEffect(new BoostTargetEffect(landCards, landCards, Duration.EndOfTurn)); diff --git a/Mage.Sets/src/mage/cards/d/DrowsingTyrannodon.java b/Mage.Sets/src/mage/cards/d/DrowsingTyrannodon.java new file mode 100644 index 0000000000..0635f947dd --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DrowsingTyrannodon.java @@ -0,0 +1,49 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.FerociousCondition; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DrowsingTyrannodon extends CardImpl { + + public DrowsingTyrannodon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // As long as you control a creature with power 4 or greater, Drowsing Tyrannodon can attack as though it didn't have defender. + this.addAbility(new SimpleStaticAbility(new ConditionalAsThoughEffect( + new CanAttackAsThoughItDidntHaveDefenderSourceEffect( + Duration.WhileOnBattlefield + ), FerociousCondition.instance + ).setText("as long as you control a creature with power 4 or greater, " + + "{this} can attack as though it didn't have defender"))); + } + + private DrowsingTyrannodon(final DrowsingTyrannodon card) { + super(card); + } + + @Override + public DrowsingTyrannodon copy() { + return new DrowsingTyrannodon(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DruidsRepository.java b/Mage.Sets/src/mage/cards/d/DruidsRepository.java index 55d3333580..b02e250d6d 100644 --- a/Mage.Sets/src/mage/cards/d/DruidsRepository.java +++ b/Mage.Sets/src/mage/cards/d/DruidsRepository.java @@ -2,9 +2,11 @@ package mage.cards.d; import java.util.UUID; +import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.mana.AddManaOfAnyColorEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.SimpleManaAbility; @@ -29,8 +31,9 @@ public final class DruidsRepository extends CardImpl { this.addAbility(new AttacksCreatureYouControlTriggeredAbility(new AddCountersSourceEffect(CounterType.CHARGE.createInstance()))); // Remove a charge counter from Druids' Repository: Add one mana of any color. - Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(), new RemoveCountersSourceCost(CounterType.CHARGE.createInstance())); - this.addAbility(ability); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, + new AddManaOfAnyColorEffect(1, new CountersSourceCount(CounterType.CHARGE), false), + new RemoveCountersSourceCost(CounterType.CHARGE.createInstance()))); } public DruidsRepository(final DruidsRepository card) { diff --git a/Mage.Sets/src/mage/cards/d/DryadOfTheIlysianGrove.java b/Mage.Sets/src/mage/cards/d/DryadOfTheIlysianGrove.java index 21b0902844..7e81a8956d 100644 --- a/Mage.Sets/src/mage/cards/d/DryadOfTheIlysianGrove.java +++ b/Mage.Sets/src/mage/cards/d/DryadOfTheIlysianGrove.java @@ -21,6 +21,7 @@ public final class DryadOfTheIlysianGrove extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{2}{G}"); this.subtype.add(SubType.NYMPH); + this.subtype.add(SubType.DRYAD); this.power = new MageInt(2); this.toughness = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/d/DualNature.java b/Mage.Sets/src/mage/cards/d/DualNature.java index 2105105790..7842ff9c64 100644 --- a/Mage.Sets/src/mage/cards/d/DualNature.java +++ b/Mage.Sets/src/mage/cards/d/DualNature.java @@ -86,7 +86,7 @@ class DualNatureCreateTokenEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(permanent.getControllerId()); effect.setTargetPointer(targetPointer); @@ -127,7 +127,7 @@ class DualNatureCreatureLeavesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent creature = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (creature != null) { FilterPermanent filter = new FilterPermanent(); filter.add(TokenPredicate.instance); diff --git a/Mage.Sets/src/mage/cards/d/DuelistsHeritage.java b/Mage.Sets/src/mage/cards/d/DuelistsHeritage.java index 1ea887e0d1..b7c1e3cbe6 100644 --- a/Mage.Sets/src/mage/cards/d/DuelistsHeritage.java +++ b/Mage.Sets/src/mage/cards/d/DuelistsHeritage.java @@ -1,15 +1,16 @@ - package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterAttackingCreature; import mage.game.Game; @@ -26,9 +27,13 @@ public final class DuelistsHeritage extends CardImpl { public DuelistsHeritage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); - - // Whenever one or more creatures attack, you may have target attacking creature gain double strike until end of turn. - Ability ability = new DuelistsHeritageTriggeredAbility(); + // Whenever one or more creatures attack, you may have target attacking + // creature gain double strike until end of turn. + Effect effect = new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), + Duration.EndOfTurn); + effect.setOutcome(Outcome.Benefit); + Ability ability = new DuelistsHeritageTriggeredAbility( + Zone.BATTLEFIELD, effect); ability.addTarget(new TargetCreaturePermanent(new FilterAttackingCreature())); this.addAbility(ability); } @@ -45,8 +50,8 @@ public final class DuelistsHeritage extends CardImpl { class DuelistsHeritageTriggeredAbility extends TriggeredAbilityImpl { - public DuelistsHeritageTriggeredAbility() { - super(Zone.BATTLEFIELD, new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn), true); + public DuelistsHeritageTriggeredAbility(Zone zone, Effect effect) { + super(zone, effect, true); } public DuelistsHeritageTriggeredAbility(final DuelistsHeritageTriggeredAbility ability) { @@ -65,6 +70,12 @@ class DuelistsHeritageTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { + // AI workaround to disable it on opponent's attacks - JayDi85 + if (game.getCombat().getAttackingPlayerId().equals(this.getControllerId())) { + this.addCustomOutcome(Outcome.Benefit); + } else { + this.addCustomOutcome(Outcome.AIDontUseIt); + } return !game.getCombat().getAttackers().isEmpty(); } diff --git a/Mage.Sets/src/mage/cards/d/DurableCoilbug.java b/Mage.Sets/src/mage/cards/d/DurableCoilbug.java new file mode 100644 index 0000000000..135c5b7437 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DurableCoilbug.java @@ -0,0 +1,41 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DurableCoilbug extends CardImpl { + + public DurableCoilbug(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.INSECT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {4}{B}: Return Durable Coilbug from your graveyard to your hand. + this.addAbility(new SimpleActivatedAbility( + Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), new ManaCostsImpl("{4}{B}") + )); + } + + private DurableCoilbug(final DurableCoilbug card) { + super(card); + } + + @Override + public DurableCoilbug copy() { + return new DurableCoilbug(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DuskFeaster.java b/Mage.Sets/src/mage/cards/d/DuskFeaster.java index 96c7fbaea3..4075027397 100644 --- a/Mage.Sets/src/mage/cards/d/DuskFeaster.java +++ b/Mage.Sets/src/mage/cards/d/DuskFeaster.java @@ -1,36 +1,38 @@ - package mage.cards.d; -import java.util.EnumSet; -import java.util.UUID; import mage.MageInt; -import mage.Mana; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.condition.common.DeliriumCondition; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.common.DeliriumHint; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.players.Player; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class DuskFeaster extends CardImpl { public DuskFeaster(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}"); this.subtype.add(SubType.VAMPIRE); this.power = new MageInt(4); this.toughness = new MageInt(5); - // Delirium — Dusk Feaster costs {2} less to cast if there are four or more card types among cards in your graveyard. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new DuskFeasterCostReductionEffect())); + // Delirium — This spell costs {2} less to cast if there are four or more card types among cards in your graveyard. + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, DeliriumCondition.instance)); + ability.setRuleAtTheTop(true); + ability.setAbilityWord(AbilityWord.DELIRIUM); + ability.addHint(DeliriumHint.instance); + this.addAbility(ability); // Flying this.addAbility(FlyingAbility.getInstance()); @@ -44,56 +46,4 @@ public final class DuskFeaster extends CardImpl { public DuskFeaster copy() { return new DuskFeaster(this); } -} - -class DuskFeasterCostReductionEffect extends CostModificationEffectImpl { - - DuskFeasterCostReductionEffect() { - super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "Delirium — {this} costs {2} less to cast if there are four or more card types among cards in your graveyard"; - } - - DuskFeasterCostReductionEffect(final DuskFeasterCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - Mana mana = spellAbility.getManaCostsToPay().getMana(); - if (mana.getGeneric() > 0) { - int newCount = mana.getGeneric() - 2; - if (newCount < 0) { - newCount = 0; - } - mana.setGeneric(newCount); - spellAbility.getManaCostsToPay().load(mana.toString()); - return true; - } - return false; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - - boolean hasDelirium = false; - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - EnumSet foundCardTypes = EnumSet.noneOf(CardType.class); - for (Card card : controller.getGraveyard().getCards(game)) { - foundCardTypes.addAll(card.getCardType()); - } - int number = foundCardTypes.size(); - hasDelirium = number > 3; - } - - return abilityToModify.getSourceId().equals(source.getSourceId()) - && (abilityToModify instanceof SpellAbility) - && hasDelirium; - } - - @Override - public DuskFeasterCostReductionEffect copy() { - return new DuskFeasterCostReductionEffect(this); - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DuskUrchins.java b/Mage.Sets/src/mage/cards/d/DuskUrchins.java index 81047b88e2..728636cabf 100644 --- a/Mage.Sets/src/mage/cards/d/DuskUrchins.java +++ b/Mage.Sets/src/mage/cards/d/DuskUrchins.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksOrBlocksTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -31,7 +31,7 @@ public final class DuskUrchins extends CardImpl { this.addAbility(new AttacksOrBlocksTriggeredAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance()), false)); // When Dusk Urchins dies, draw a card for each -1/-1 counter on it. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.M1M1)))); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.M1M1)))); } diff --git a/Mage.Sets/src/mage/cards/d/DuskfangMentor.java b/Mage.Sets/src/mage/cards/d/DuskfangMentor.java new file mode 100644 index 0000000000..cc4f35141c --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DuskfangMentor.java @@ -0,0 +1,71 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DuskfangMentor extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("non-Human creature you control"); + private static final FilterPermanent filter2 + = new FilterControlledCreaturePermanent("creature you control with lifelink"); + + static { + filter.add(Predicates.not(SubType.HUMAN.getPredicate())); + filter2.add(new AbilityPredicate(LifelinkAbility.class)); + } + + public DuskfangMentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // When Duskfang Mentor enters the battlefield, put a lifelink counter on target non-Human creature you control. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.LIFELINK.createInstance()) + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {1}{B}, {T}: Put a +1/+1 counter on each creature you control with lifelink. + ability = new SimpleActivatedAbility( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter2), new ManaCostsImpl("{1}{B}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private DuskfangMentor(final DuskfangMentor card) { + super(card); + } + + @Override + public DuskfangMentor copy() { + return new DuskfangMentor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/Duskworker.java b/Mage.Sets/src/mage/cards/d/Duskworker.java index 4dbd6d6713..dade86eb09 100644 --- a/Mage.Sets/src/mage/cards/d/Duskworker.java +++ b/Mage.Sets/src/mage/cards/d/Duskworker.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.RegenerateSourceEffect; @@ -26,7 +26,7 @@ public final class Duskworker extends CardImpl { this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(2); this.toughness = new MageInt(2); - this.addAbility(new BecomesBlockedTriggeredAbility(new RegenerateSourceEffect(), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new RegenerateSourceEffect(), false)); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new GenericManaCost(3))); } diff --git a/Mage.Sets/src/mage/cards/d/DutifulAttendant.java b/Mage.Sets/src/mage/cards/d/DutifulAttendant.java index 8262b7050c..46d73e16dc 100644 --- a/Mage.Sets/src/mage/cards/d/DutifulAttendant.java +++ b/Mage.Sets/src/mage/cards/d/DutifulAttendant.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -34,7 +34,7 @@ public final class DutifulAttendant extends CardImpl { this.toughness = new MageInt(2); // When Dutiful Ateendant dies, return another target creature card from your graveyard to your hand. - Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), false); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DwarvenBerserker.java b/Mage.Sets/src/mage/cards/d/DwarvenBerserker.java index 998ff1a509..21fe183f8c 100644 --- a/Mage.Sets/src/mage/cards/d/DwarvenBerserker.java +++ b/Mage.Sets/src/mage/cards/d/DwarvenBerserker.java @@ -4,7 +4,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; @@ -31,7 +31,7 @@ public final class DwarvenBerserker extends CardImpl { // Whenever Dwarven Berserker becomes blocked, it gets +3/+0 and gains trample until end of turn. Effect effect = new BoostSourceEffect(3, 0, Duration.EndOfTurn); effect.setText("it gets +3/+0"); - Ability ability = new BecomesBlockedTriggeredAbility(effect, false); + Ability ability = new BecomesBlockedSourceTriggeredAbility(effect, false); effect = new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); effect.setText("and gains trample until end of turn"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/d/DwellOnThePast.java b/Mage.Sets/src/mage/cards/d/DwellOnThePast.java index efa62fddb7..f57594e87a 100644 --- a/Mage.Sets/src/mage/cards/d/DwellOnThePast.java +++ b/Mage.Sets/src/mage/cards/d/DwellOnThePast.java @@ -8,6 +8,7 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -61,24 +62,9 @@ class DwellOnThePastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - List targets = source.getTargets().get(1).getTargets(); - boolean shuffle = false; - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - if (player.getGraveyard().contains(card.getId())) { - player.getGraveyard().remove(card); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - shuffle = true; - } - } - } - if (shuffle) { - player.shuffleLibrary(source, game); - } - return true; + Player controller = game.getPlayer(source.getFirstTarget()); + if (controller != null) { + return controller.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/e/EarwigSquad.java b/Mage.Sets/src/mage/cards/e/EarwigSquad.java index 006d6bf757..d132407b99 100644 --- a/Mage.Sets/src/mage/cards/e/EarwigSquad.java +++ b/Mage.Sets/src/mage/cards/e/EarwigSquad.java @@ -1,21 +1,19 @@ - package mage.cards.e; -import java.util.List; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.ProwlCondition; +import mage.abilities.condition.common.ProwlCostWasPaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.common.ProwlCostWasPaidHint; import mage.abilities.keyword.ProwlAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; @@ -23,14 +21,16 @@ import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; +import java.util.List; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class EarwigSquad extends CardImpl { public EarwigSquad(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.GOBLIN); this.subtype.add(SubType.ROGUE); @@ -39,11 +39,13 @@ public final class EarwigSquad extends CardImpl { // Prowl {2}{B} this.addAbility(new ProwlAbility(this, "{2}{B}")); + // When Earwig Squad enters the battlefield, if its prowl cost was paid, search target opponent's library for three cards and exile them. Then that player shuffles their library. EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new EarwigSquadEffect(), false); ability.addTarget(new TargetOpponent()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, ProwlCondition.instance, - "When {this} enters the battlefield, if its prowl cost was paid, search target opponent's library for three cards and exile them. Then that player shuffles their library.")); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, ProwlCostWasPaidCondition.instance, + "When {this} enters the battlefield, if its prowl cost was paid, search target opponent's library for three cards and exile them. Then that player shuffles their library.") + .addHint(ProwlCostWasPaidHint.instance)); } @@ -78,7 +80,7 @@ class EarwigSquadEffect extends OneShotEffect { Player opponent = game.getPlayer(source.getFirstTarget()); Player player = game.getPlayer(source.getControllerId()); if (player != null && opponent != null) { - TargetCardInLibrary target = new TargetCardInLibrary(0, 3, new FilterCard("cards from opponents library to exile")); + TargetCardInLibrary target = new TargetCardInLibrary(0, 3, new FilterCard("cards from opponents library to exile")); if (player.searchLibrary(target, source, game, opponent.getId())) { List targets = target.getTargets(); for (UUID targetId : targets) { diff --git a/Mage.Sets/src/mage/cards/e/EasyPrey.java b/Mage.Sets/src/mage/cards/e/EasyPrey.java new file mode 100644 index 0000000000..988b57063c --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EasyPrey.java @@ -0,0 +1,48 @@ +package mage.cards.e; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EasyPrey extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature with converted mana cost 2 or less"); + + static { + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + } + + public EasyPrey(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Destroy target creature with converted mana cost 2 or less. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private EasyPrey(final EasyPrey card) { + super(card); + } + + @Override + public EasyPrey copy() { + return new EasyPrey(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EbonyCharm.java b/Mage.Sets/src/mage/cards/e/EbonyCharm.java index f3ed1de7c0..b6f9245471 100644 --- a/Mage.Sets/src/mage/cards/e/EbonyCharm.java +++ b/Mage.Sets/src/mage/cards/e/EbonyCharm.java @@ -1,26 +1,23 @@ package mage.cards.e; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.FearAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInASingleGraveyard; import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class EbonyCharm extends CardImpl { @@ -29,13 +26,14 @@ public final class EbonyCharm extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // Choose one - Target opponent loses 1 life and you gain 1 life; - this.getSpellAbility().addEffect(new EbonyCharmDrainEffect()); + this.getSpellAbility().addEffect(new LoseLifeTargetEffect(1)); + this.getSpellAbility().addEffect(new GainLifeEffect(1).concatBy("and")); this.getSpellAbility().addTarget(new TargetOpponent()); // or exile up to three target cards from a single graveyard; Mode mode = new Mode(); - mode.addEffect(new EbonyCharmExileEffect()); - mode.addTarget((new TargetCardInASingleGraveyard(0, 3, new FilterCard("up to three target cards from a single graveyard")))); + mode.addEffect(new ExileTargetEffect()); + mode.addTarget((new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS))); this.getSpellAbility().addMode(mode); // or target creature gains fear until end of turn. @@ -54,62 +52,3 @@ public final class EbonyCharm extends CardImpl { return new EbonyCharm(this); } } - -class EbonyCharmDrainEffect extends OneShotEffect { - - EbonyCharmDrainEffect() { - super(Outcome.Damage); - staticText = "target opponent loses 1 life and you gain 1 life"; - } - - EbonyCharmDrainEffect(final EbonyCharmDrainEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player targetOpponent = game.getPlayer(source.getFirstTarget()); - Player controller = game.getPlayer(source.getControllerId()); - if (targetOpponent != null - && controller != null) { - targetOpponent.loseLife(1, game, false); - controller.gainLife(1, game, source); - return true; - } - return false; - } - - @Override - public EbonyCharmDrainEffect copy() { - return new EbonyCharmDrainEffect(this); - } - -} - -class EbonyCharmExileEffect extends OneShotEffect { - - public EbonyCharmExileEffect() { - super(Outcome.Exile); - this.staticText = "Exile up to three target cards from a single graveyard"; - } - - public EbonyCharmExileEffect(final EbonyCharmExileEffect effect) { - super(effect); - } - - @Override - public EbonyCharmExileEffect copy() { - return new EbonyCharmExileEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID targetID : source.getTargets().get(0).getTargets()) { - Card card = game.getCard(targetID); - if (card != null) { - card.moveToExile(null, "", source.getSourceId(), game); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/e/EchoMage.java b/Mage.Sets/src/mage/cards/e/EchoMage.java index 28acd87fd5..e5a635d78c 100644 --- a/Mage.Sets/src/mage/cards/e/EchoMage.java +++ b/Mage.Sets/src/mage/cards/e/EchoMage.java @@ -1,7 +1,6 @@ package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; @@ -16,22 +15,23 @@ import mage.abilities.keyword.LevelerCardBuilder; import mage.cards.CardSetInfo; import mage.cards.LevelerCard; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.stack.Spell; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author North */ public final class EchoMage extends LevelerCard { public EchoMage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); @@ -94,8 +94,7 @@ class EchoMageEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); if (spell != null) { - spell.createCopyOnStack(game, source, source.getControllerId(), true); - spell.createCopyOnStack(game, source, source.getControllerId(), true); + spell.createCopyOnStack(game, source, source.getControllerId(), true, 2); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/e/EerieInterlude.java b/Mage.Sets/src/mage/cards/e/EerieInterlude.java index 0d5578c90a..f09d6b529c 100644 --- a/Mage.Sets/src/mage/cards/e/EerieInterlude.java +++ b/Mage.Sets/src/mage/cards/e/EerieInterlude.java @@ -1,9 +1,5 @@ - package mage.cards.e; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -23,14 +19,17 @@ import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class EerieInterlude extends CardImpl { public EerieInterlude(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); // Exile any number of target creatures you control. Return those cards to the battlefield under their owner's control at the beginning of the next end step. this.getSpellAbility().addEffect(new EerieInterludeEffect()); @@ -86,12 +85,11 @@ class EerieInterludeEffect extends OneShotEffect { if (bottomCard.getZoneChangeCounter(game) == meldCard.getBottomLastZoneChangeCounter()) { cardsToReturn.add(bottomCard); } - } - else if (exiled.getZoneChangeCounter(game) == game.getState().getZoneChangeCounter(exiled.getId()) - 1) { + } else if (exiled.getZoneChangeCounter(game) == game.getState().getZoneChangeCounter(exiled.getId()) - 1) { cardsToReturn.add(exiled); } } - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setTargetPointer(new FixedTargets(cardsToReturn, game)); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/e/EerieUltimatum.java b/Mage.Sets/src/mage/cards/e/EerieUltimatum.java new file mode 100644 index 0000000000..2e851e7cb5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EerieUltimatum.java @@ -0,0 +1,110 @@ +package mage.cards.e; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class EerieUltimatum extends CardImpl { + + public EerieUltimatum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}{W}{B}{B}{B}{G}{G}"); + + // Return any number of permanent cards with different names from your graveyard to the battlefield + this.getSpellAbility().addEffect(new EerieUltimatumEffect()); + } + + private EerieUltimatum(final EerieUltimatum card) { + super(card); + } + + @Override + public EerieUltimatum copy() { + return new EerieUltimatum(this); + } +} + +class EerieUltimatumEffect extends OneShotEffect { + + EerieUltimatumEffect() { + super(Outcome.Benefit); + staticText = "return any number of permanent cards with different names from your graveyard to the battlefield"; + } + + private EerieUltimatumEffect(final EerieUltimatumEffect effect) { + super(effect); + } + + @Override + public EerieUltimatumEffect copy() { + return new EerieUltimatumEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCard target = new EerieUltimatumTarget(); + if (!player.choose(outcome, player.getGraveyard(), target, game)) { + return false; + } + return player.moveCards(new CardsImpl(target.getTargets()), Zone.BATTLEFIELD, source, game); + } +} + +class EerieUltimatumTarget extends TargetCardInYourGraveyard { + + private static final FilterCard filter + = new FilterPermanentCard("permanent cards with different names"); + + EerieUltimatumTarget() { + super(0, Integer.MAX_VALUE, filter, true); + } + + private EerieUltimatumTarget(final EerieUltimatumTarget target) { + super(target); + } + + @Override + public EerieUltimatumTarget copy() { + return new EerieUltimatumTarget(this); + } + + @Override + public Set possibleTargets(UUID sourceId, UUID playerId, Game game) { + Set possibleTargets = super.possibleTargets(sourceId, playerId, game); + Set names = this.getTargets() + .stream() + .map(game::getCard) + .map(MageObject::getName) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + possibleTargets.removeIf(uuid -> { + Card card = game.getCard(uuid); + return card != null && names.contains(card.getName()); + }); + return possibleTargets; + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElderCathar.java b/Mage.Sets/src/mage/cards/e/ElderCathar.java index 4a68c92c63..e285ed05aa 100644 --- a/Mage.Sets/src/mage/cards/e/ElderCathar.java +++ b/Mage.Sets/src/mage/cards/e/ElderCathar.java @@ -4,7 +4,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -31,7 +31,7 @@ public final class ElderCathar extends CardImpl { this.toughness = new MageInt(2); // When Elder Cathar dies, put a +1/+1 counter on target creature you control. If that creature is a Human, put two +1/+1 counters on it instead. - Ability ability = new DiesTriggeredAbility(new ElderCatharAddCountersTargetEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new ElderCatharAddCountersTargetEffect(), false); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/ElderGargaroth.java b/Mage.Sets/src/mage/cards/e/ElderGargaroth.java new file mode 100644 index 0000000000..3722172846 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ElderGargaroth.java @@ -0,0 +1,62 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.AttacksOrBlocksTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.BeastToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ElderGargaroth extends CardImpl { + + public ElderGargaroth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever Elder Gargaroth attacks or blocks, choose one — + // • Create a 3/3 green Beast creature token. + Ability ability = new AttacksOrBlocksTriggeredAbility(new CreateTokenEffect(new BeastToken()), false); + + // • You gain 3 life. + ability.addMode(new Mode(new GainLifeEffect(3))); + + // • Draw a card. + ability.addMode(new Mode(new DrawCardSourceControllerEffect(1))); + this.addAbility(ability); + } + + private ElderGargaroth(final ElderGargaroth card) { + super(card); + } + + @Override + public ElderGargaroth copy() { + return new ElderGargaroth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElderwoodScion.java b/Mage.Sets/src/mage/cards/e/ElderwoodScion.java index 778ffb55e5..eceff73b4f 100644 --- a/Mage.Sets/src/mage/cards/e/ElderwoodScion.java +++ b/Mage.Sets/src/mage/cards/e/ElderwoodScion.java @@ -1,24 +1,21 @@ - package mage.cards.e; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.target.Target; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ElderwoodScion extends CardImpl { @@ -32,12 +29,19 @@ public final class ElderwoodScion extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); + // Lifelink this.addAbility(LifelinkAbility.getInstance()); + // Spells you cast that target Elderwood Scion cost {2} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ElderwoodScionCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(-2, new FilterCard("Spells"), TargetController.YOU)) + ); + // Spells your opponents cast that target Elderwood Scion cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ElderwoodScionCostReductionEffect2())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT)) + ); } public ElderwoodScion(final ElderwoodScion card) { @@ -49,92 +53,3 @@ public final class ElderwoodScion extends CardImpl { return new ElderwoodScion(this); } } - -class ElderwoodScionCostReductionEffect extends CostModificationEffectImpl { - - private static final String effectText = "Spells you cast that target {this} cost {2} less to cast"; - - ElderwoodScionCostReductionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = effectText; - } - - ElderwoodScionCostReductionEffect(ElderwoodScionCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, 2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - if (abilityToModify.isControlledBy(source.getControllerId())) { - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - if (targetUUID.equals(source.getSourceId())) { - return true; - } - } - } - } - } - } - return false; - } - - @Override - public ElderwoodScionCostReductionEffect copy() { - return new ElderwoodScionCostReductionEffect(this); - } - -} - -class ElderwoodScionCostReductionEffect2 extends CostModificationEffectImpl { - - private static final String effectText = "Spells your opponents cast that target {this} cost {2} more to cast"; - - ElderwoodScionCostReductionEffect2() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = effectText; - } - - ElderwoodScionCostReductionEffect2(ElderwoodScionCostReductionEffect2 effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.increaseCost(spellAbility, 2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - for (Target target : abilityToModify.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - if (targetUUID.equals(source.getSourceId())) { - return true; - } - } - } - } - } - return false; - } - - @Override - public ElderwoodScionCostReductionEffect2 copy() { - return new ElderwoodScionCostReductionEffect2(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java b/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java index 1ccfa5e162..48310f862c 100644 --- a/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java +++ b/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java @@ -42,7 +42,7 @@ public final class EldraziDisplacer extends CardImpl { // {2}{C}: Exile another target creature, then return it to the battlefield tapped under its owner's control. Effect effect = new ExileTargetForSourceEffect(); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl<>("{2}{C}")); - effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(true) + effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(true, false) .withReturnNames("it", "its owner's").concatBy(", then"); ability.addEffect(effect); ability.addTarget(new TargetCreaturePermanent(FILTER)); diff --git a/Mage.Sets/src/mage/cards/e/EldraziMimic.java b/Mage.Sets/src/mage/cards/e/EldraziMimic.java index 657a7a8565..b3a2ed8129 100644 --- a/Mage.Sets/src/mage/cards/e/EldraziMimic.java +++ b/Mage.Sets/src/mage/cards/e/EldraziMimic.java @@ -73,7 +73,7 @@ class EldraziMimicEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Permanent permanent = ((FixedTarget) getTargetPointer()).getTargetedPermanentOrLKIBattlefield(game); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { ContinuousEffect effect = new SetPowerToughnessTargetEffect(permanent.getPower().getValue(), permanent.getToughness().getValue(), Duration.EndOfTurn); effect.setTargetPointer(new FixedTarget(source.getSourceId())); diff --git a/Mage.Sets/src/mage/cards/e/EldritchEvolution.java b/Mage.Sets/src/mage/cards/e/EldritchEvolution.java index 9a46e46b89..05d7fa7e64 100644 --- a/Mage.Sets/src/mage/cards/e/EldritchEvolution.java +++ b/Mage.Sets/src/mage/cards/e/EldritchEvolution.java @@ -15,7 +15,7 @@ import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -33,7 +33,7 @@ public final class EldritchEvolution extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{G}{G}"); // As an additional cost to cast Eldritch Evolution, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent("a creature"), true))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, true))); // Search your library for a creature card with converted mana cost X or less, where X is 2 plus the sacrificed creature's converted mana cost. // Put that card onto the battlefield, then shuffle your library. Exile Eldritch Evolution. diff --git a/Mage.Sets/src/mage/cards/e/Electrickery.java b/Mage.Sets/src/mage/cards/e/Electrickery.java index 6ba7ccd480..6714614509 100644 --- a/Mage.Sets/src/mage/cards/e/Electrickery.java +++ b/Mage.Sets/src/mage/cards/e/Electrickery.java @@ -1,7 +1,5 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -9,32 +7,25 @@ import mage.abilities.keyword.OverloadAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Electrickery extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public Electrickery(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); // Electrickery deals 1 damage to target creature you don't control. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.getSpellAbility().addEffect(new DamageTargetEffect(1)); // Overload {1}{R} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.") - this.addAbility(new OverloadAbility(this, new DamageAllEffect(1, filter), new ManaCostsImpl("{1}{R}"))); + this.addAbility(new OverloadAbility(this, new DamageAllEffect(1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL), new ManaCostsImpl("{1}{R}"))); } public Electrickery(final Electrickery card) { @@ -45,4 +36,4 @@ public final class Electrickery extends CardImpl { public Electrickery copy() { return new Electrickery(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/e/ElementalAppeal.java b/Mage.Sets/src/mage/cards/e/ElementalAppeal.java index d7346d75ce..bfffb2e6b2 100644 --- a/Mage.Sets/src/mage/cards/e/ElementalAppeal.java +++ b/Mage.Sets/src/mage/cards/e/ElementalAppeal.java @@ -1,9 +1,5 @@ - package mage.cards.e; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.condition.common.KickedCondition; @@ -21,10 +17,13 @@ import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardIdPredicate; import mage.game.Game; -import mage.game.permanent.token.ElementalAppealElementalToken; +import mage.game.permanent.token.RedElementalWithTrampleAndHaste; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; /** - * * @author North */ public final class ElementalAppeal extends CardImpl { @@ -69,7 +68,7 @@ class ElementalAppealEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - CreateTokenEffect effect = new CreateTokenEffect(new ElementalAppealElementalToken()); + CreateTokenEffect effect = new CreateTokenEffect(new RedElementalWithTrampleAndHaste()); if (effect.apply(game, source)) { effect.exileTokensCreatedAtNextEndStep(game, source); if (KickedCondition.instance.apply(game, source)) { diff --git a/Mage.Sets/src/mage/cards/e/ElendaTheDuskRose.java b/Mage.Sets/src/mage/cards/e/ElendaTheDuskRose.java index f6caea4489..69b841e09c 100644 --- a/Mage.Sets/src/mage/cards/e/ElendaTheDuskRose.java +++ b/Mage.Sets/src/mage/cards/e/ElendaTheDuskRose.java @@ -4,7 +4,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; import mage.abilities.common.DiesCreatureTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -38,7 +38,7 @@ public final class ElendaTheDuskRose extends CardImpl { this.addAbility(new DiesCreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, true)); // When Elenda dies, create X 1/1 white Vampire creature tokens with lifelink, where X is Elenda's power. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new IxalanVampireToken(), new SourcePermanentPowerCount()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new IxalanVampireToken(), new SourcePermanentPowerCount()))); } public ElendaTheDuskRose(final ElendaTheDuskRose card) { diff --git a/Mage.Sets/src/mage/cards/e/ElgaudInquisitor.java b/Mage.Sets/src/mage/cards/e/ElgaudInquisitor.java index 97f1e20c73..dae094242e 100644 --- a/Mage.Sets/src/mage/cards/e/ElgaudInquisitor.java +++ b/Mage.Sets/src/mage/cards/e/ElgaudInquisitor.java @@ -3,7 +3,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class ElgaudInquisitor extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // When Elgaud Inquisitor dies, create a 1/1 white Spirit creature token with flying. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken()))); } public ElgaudInquisitor(final ElgaudInquisitor card) { diff --git a/Mage.Sets/src/mage/cards/e/Eliminate.java b/Mage.Sets/src/mage/cards/e/Eliminate.java new file mode 100644 index 0000000000..39423122e3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/Eliminate.java @@ -0,0 +1,44 @@ +package mage.cards.e; + +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Eliminate extends CardImpl { + + private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent( + "creature or planeswalker with converted mana cost 3 or less" + ); + + static { + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + } + + public Eliminate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Destroy target creature or planeswalker with converted mana cost 3 or less. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + } + + private Eliminate(final Eliminate card) { + super(card); + } + + @Override + public Eliminate copy() { + return new Eliminate(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EliteScaleguard.java b/Mage.Sets/src/mage/cards/e/EliteScaleguard.java index b2c5911d85..02a7c48ef0 100644 --- a/Mage.Sets/src/mage/cards/e/EliteScaleguard.java +++ b/Mage.Sets/src/mage/cards/e/EliteScaleguard.java @@ -1,10 +1,11 @@ - package mage.cards.e; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.effects.keyword.BolsterEffect; import mage.cards.CardImpl; @@ -14,13 +15,12 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.CounterPredicate; -import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; - -import java.util.UUID; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.FirstTargetPointer; /** * @author emerald000 @@ -28,11 +28,9 @@ import java.util.UUID; public final class EliteScaleguard extends CardImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature defending player controls"); static { filter.add(new CounterPredicate(CounterType.P1P1)); - filter2.add(DefendingPlayerControlsPredicate.instance); } public EliteScaleguard(UUID ownerId, CardSetInfo setInfo) { @@ -46,8 +44,9 @@ public final class EliteScaleguard extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new BolsterEffect(2))); // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls. - Ability ability = new AttacksCreatureYouControlTriggeredAbility(new EliteScaleguardTapEffect(), false, filter, true); - ability.addTarget(new TargetCreaturePermanent(filter2)); + Ability ability = new AttacksCreatureYouControlTriggeredAbility(new TapTargetEffect(), false, filter, true); + ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature defending player controls"))); + ability.setTargetAdjuster(EliteScaleguardTargetAdjuster.instance); this.addAbility(ability); } @@ -61,28 +60,21 @@ public final class EliteScaleguard extends CardImpl { } } -class EliteScaleguardTapEffect extends TapTargetEffect { - - EliteScaleguardTapEffect() { - super(); - } - - EliteScaleguardTapEffect(final EliteScaleguardTapEffect effect) { - super(effect); - } +enum EliteScaleguardTargetAdjuster implements TargetAdjuster { + instance; @Override - public EliteScaleguardTapEffect copy() { - return new EliteScaleguardTapEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - permanent.tap(game); - return true; + public void adjustTargets(Ability ability, Game game) { + FilterCreaturePermanent filterDefender = new FilterCreaturePermanent("creature defending player controls"); + for (Effect effect : ability.getEffects()) { + if (effect instanceof TapTargetEffect) { + filterDefender.add(new ControllerIdPredicate(game.getCombat().getDefendingPlayerId(effect.getTargetPointer().getFirst(game, ability), game))); + effect.setTargetPointer(new FirstTargetPointer());// reset target pointer to first target to tap correct target + break; + } } - return false; + ability.getTargets().clear(); + TargetCreaturePermanent target = new TargetCreaturePermanent(filterDefender); + ability.addTarget(target); } } diff --git a/Mage.Sets/src/mage/cards/e/ElshaOfTheInfinite.java b/Mage.Sets/src/mage/cards/e/ElshaOfTheInfinite.java index 91b80c7af8..5d4f9255c2 100644 --- a/Mage.Sets/src/mage/cards/e/ElshaOfTheInfinite.java +++ b/Mage.Sets/src/mage/cards/e/ElshaOfTheInfinite.java @@ -24,7 +24,7 @@ import java.util.UUID; */ public final class ElshaOfTheInfinite extends CardImpl { - private static final FilterCard filter = new FilterNonlandCard(); + private static final FilterCard filter = new FilterNonlandCard("cast noncreature spells"); static { filter.add(Predicates.not(CardType.CREATURE.getPredicate())); @@ -46,14 +46,10 @@ public final class ElshaOfTheInfinite extends CardImpl { this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); // You may cast the top card of your library if it's a noncreature, nonland card, and you may cast it as though it had flash. - Ability ability = new SimpleStaticAbility( - new PlayTheTopCardEffect(filter).setText( - "you may cast the top card of your library if it's a noncreature, nonland card," - ) - ); + Ability ability = new SimpleStaticAbility(new PlayTheTopCardEffect(filter)); ability.addEffect(new CastAsThoughItHadFlashAllEffect( Duration.WhileOnBattlefield, filter - ).setText("and you may cast it as though it had flash")); + ).setText("If you cast a spell this way, you may cast it as though it had flash.")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java b/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java index 0dbe61b9b8..9ea54575cf 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java +++ b/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java @@ -1,5 +1,6 @@ package mage.cards.e; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.SagaAbility; @@ -23,8 +24,6 @@ import mage.target.TargetPermanent; import mage.target.common.TargetCardInYourGraveyard; import mage.util.CardUtil; -import java.util.UUID; - /** * @author TheElk801 */ @@ -99,12 +98,14 @@ class ElspethConquersDeathCostEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (!(abilityToModify instanceof SpellAbility) || - !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - return false; + if ((abilityToModify instanceof SpellAbility) + && game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + if (spellCard != null) { + return !spellCard.isCreature(); + } } - Card card = game.getCard(abilityToModify.getSourceId()); - return card != null && !card.isCreature(); + return false; } @Override @@ -117,8 +118,8 @@ class ElspethConquersDeathReturnEffect extends OneShotEffect { ElspethConquersDeathReturnEffect() { super(Outcome.Benefit); - staticText = "Return target creature or planeswalker card from your graveyard to the battlefield. " + - "Put a +1/+1 counter or a loyalty counter on it"; + staticText = "Return target creature or planeswalker card from your graveyard to the battlefield. " + + "Put a +1/+1 counter or a loyalty counter on it"; } private ElspethConquersDeathReturnEffect(final ElspethConquersDeathReturnEffect effect) { @@ -149,4 +150,5 @@ class ElspethConquersDeathReturnEffect extends OneShotEffect { permanent.addCounters(counter, source, game); return true; } -} \ No newline at end of file + +} diff --git a/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java b/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java index 445548bb21..d04129b353 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java +++ b/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java @@ -28,7 +28,7 @@ import java.util.UUID; */ public final class ElspethUndauntedHero extends CardImpl { - private static final FilterCard filter = new FilterCard("Sunlit Hoplite"); + private static final FilterCard filter = new FilterCard("card named Sunlit Hoplite"); static { filter.add(new NamePredicate("Sunlit Hoplite")); diff --git a/Mage.Sets/src/mage/cards/e/ElvenWarhounds.java b/Mage.Sets/src/mage/cards/e/ElvenWarhounds.java index e34c197214..6809e3f232 100644 --- a/Mage.Sets/src/mage/cards/e/ElvenWarhounds.java +++ b/Mage.Sets/src/mage/cards/e/ElvenWarhounds.java @@ -19,7 +19,7 @@ public final class ElvenWarhounds extends CardImpl { public ElvenWarhounds(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/e/ElvishBerserker.java b/Mage.Sets/src/mage/cards/e/ElvishBerserker.java index 48aa62ef48..a85d7e9e76 100644 --- a/Mage.Sets/src/mage/cards/e/ElvishBerserker.java +++ b/Mage.Sets/src/mage/cards/e/ElvishBerserker.java @@ -3,7 +3,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.BlockedCreatureCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -30,7 +30,7 @@ public final class ElvishBerserker extends CardImpl { BlockedCreatureCount value = new BlockedCreatureCount(); Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true); effect.setText("it gets +1/+1 until end of turn for each creature blocking it"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public ElvishBerserker(final ElvishBerserker card) { diff --git a/Mage.Sets/src/mage/cards/e/ElvishSoultiller.java b/Mage.Sets/src/mage/cards/e/ElvishSoultiller.java index a7153fbaa5..20cd16aa9c 100644 --- a/Mage.Sets/src/mage/cards/e/ElvishSoultiller.java +++ b/Mage.Sets/src/mage/cards/e/ElvishSoultiller.java @@ -6,7 +6,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -34,7 +34,7 @@ public final class ElvishSoultiller extends CardImpl { this.toughness = new MageInt(4); // When Elvish Soultiller dies, choose a creature type. Shuffle all creature cards of that type from your graveyard into your library. - addAbility(new DiesTriggeredAbility(new ElvishSoultillerEffect())); + addAbility(new DiesSourceTriggeredAbility(new ElvishSoultillerEffect())); } diff --git a/Mage.Sets/src/mage/cards/e/EmbalmersTools.java b/Mage.Sets/src/mage/cards/e/EmbalmersTools.java index bb7c1284c1..59fa7e2e9a 100644 --- a/Mage.Sets/src/mage/cards/e/EmbalmersTools.java +++ b/Mage.Sets/src/mage/cards/e/EmbalmersTools.java @@ -1,8 +1,5 @@ - package mage.cards.e; -import java.util.UUID; -import mage.Mana; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -24,8 +21,9 @@ import mage.target.TargetPlayer; import mage.target.common.TargetControlledPermanent; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author fireshoes */ public final class EmbalmersTools extends CardImpl { @@ -81,10 +79,7 @@ class EmbalmersToolsEffect extends CostModificationEffectImpl { public boolean apply(Game game, Ability source, Ability abilityToModify) { Player controller = game.getPlayer(abilityToModify.getControllerId()); if (controller != null) { - Mana mana = abilityToModify.getManaCostsToPay().getMana(); - if (mana.count() > 1 && mana.getGeneric() > 0) { - CardUtil.reduceCost(abilityToModify, 1); - } + CardUtil.reduceCost(abilityToModify, 1); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/e/EmberFistZubera.java b/Mage.Sets/src/mage/cards/e/EmberFistZubera.java index acfe8a486c..921ad4e910 100644 --- a/Mage.Sets/src/mage/cards/e/EmberFistZubera.java +++ b/Mage.Sets/src/mage/cards/e/EmberFistZubera.java @@ -5,7 +5,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.ZuberasDiedDynamicValue; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class EmberFistZubera extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(ZuberasDiedDynamicValue.instance)); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(ZuberasDiedDynamicValue.instance)); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability, new ZuberasDiedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/e/Embercleave.java b/Mage.Sets/src/mage/cards/e/Embercleave.java index 2c0d2baa7d..35851640f0 100644 --- a/Mage.Sets/src/mage/cards/e/Embercleave.java +++ b/Mage.Sets/src/mage/cards/e/Embercleave.java @@ -1,13 +1,15 @@ package mage.cards.e; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.AttackingCreatureCount; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.EquipAbility; import mage.abilities.keyword.FlashAbility; @@ -15,12 +17,8 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AttackingPredicate; -import mage.game.Game; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; -import mage.util.CardUtil; import java.util.UUID; @@ -39,7 +37,10 @@ public final class Embercleave extends CardImpl { this.addAbility(FlashAbility.getInstance()); // This spell costs {1} less to cast for each attacking creature you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new EmbercleaveCostReductionEffect())); + DynamicValue xValue = new AttackingCreatureCount(StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED); + this.addAbility(new SimpleStaticAbility(Zone.ALL, + new SpellCostReductionForEachSourceEffect(1, xValue) + ).addHint(new ValueHint("Attacking creature you control", xValue))); // When Embercleave enters the battlefield, attach it to target creature you control. Ability ability = new EntersBattlefieldTriggeredAbility(new AttachEffect( @@ -71,41 +72,3 @@ public final class Embercleave extends CardImpl { return new Embercleave(this); } } - -class EmbercleaveCostReductionEffect extends CostModificationEffectImpl { - - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); - - static { - filter.add(AttackingPredicate.instance); - } - - EmbercleaveCostReductionEffect() { - super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "This spell costs {1} less to cast for each attacking creature you control"; - } - - private EmbercleaveCostReductionEffect(EmbercleaveCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - int reductionAmount = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); - CardUtil.reduceCost(abilityToModify, reductionAmount); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if ((abilityToModify instanceof SpellAbility) && abilityToModify.getSourceId().equals(source.getSourceId())) { - return game.getCard(abilityToModify.getSourceId()) != null; - } - return false; - } - - @Override - public EmbercleaveCostReductionEffect copy() { - return new EmbercleaveCostReductionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java b/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java new file mode 100644 index 0000000000..004297319d --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java @@ -0,0 +1,161 @@ +package mage.cards.e; + +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.MonocoloredPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInExile; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EmergentUltimatum extends CardImpl { + + public EmergentUltimatum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}{B}{G}{G}{G}{U}{U}"); + + // Search your library for up to three monocolored cards with different names and exile them. An opponent chooses one of those cards. Shuffle that card into your library. You may cast the other cards without paying their mana costs. Exile Emergent Ultimatum. + this.getSpellAbility().addEffect(new EmergentUltimatumEffect()); + this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + } + + private EmergentUltimatum(final EmergentUltimatum card) { + super(card); + } + + @Override + public EmergentUltimatum copy() { + return new EmergentUltimatum(this); + } +} + +class EmergentUltimatumEffect extends OneShotEffect { + + EmergentUltimatumEffect() { + super(Outcome.Benefit); + staticText = "Search your library for up to three monocolored cards with different names and exile them. " + + "An opponent chooses one of those cards. Shuffle that card into your library. " + + "You may cast the other cards without paying their mana costs"; + } + + private EmergentUltimatumEffect(final EmergentUltimatumEffect effect) { + super(effect); + } + + @Override + public EmergentUltimatumEffect copy() { + return new EmergentUltimatumEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCardInLibrary targetCardInLibrary = new EmergentUltimatumTarget(); + boolean searched = player.searchLibrary(targetCardInLibrary, source, game); + Cards cards = new CardsImpl(targetCardInLibrary.getTargets()); + player.moveCards(cards, Zone.EXILED, source, game); + if (cards.isEmpty()) { + if (searched) { + player.shuffleLibrary(source, game); + } + return false; + } + TargetOpponent targetOpponent = new TargetOpponent(); + targetOpponent.setNotTarget(true); + player.choose(outcome, targetOpponent, source.getSourceId(), game); + Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); + if (opponent == null) { + if (searched) { + player.shuffleLibrary(source, game); + } + return true; + } + TargetCardInExile targetCardInExile = new TargetCardInExile(StaticFilters.FILTER_CARD); + opponent.choose(outcome, cards, targetCardInExile, game); + Card toShuffle = game.getCard(targetCardInExile.getFirstTarget()); + if (toShuffle != null) { + player.putCardsOnBottomOfLibrary(toShuffle, game, source, false); + player.shuffleLibrary(source, game); + cards.remove(toShuffle); + } + while (!cards.isEmpty()) { + if (!player.chooseUse(Outcome.PlayForFree, "Cast an exiled card without paying its mana cost?", source, game)) { + break; + } + targetCardInExile.clearChosen(); + if (!player.choose(Outcome.PlayForFree, cards, targetCardInExile, game)) { + continue; + } + Card card = game.getCard(targetCardInExile.getFirstTarget()); + if (card == null) { + continue; + } + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + Boolean cardWasCast = player.cast(player.chooseAbilityForCast(card, game, true), + game, true, new MageObjectReference(source.getSourceObject(game), game)); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + cards.remove(card); // remove on non cast too (infinite freeze fix) + if (cardWasCast) { + cards.remove(card); + } else { + game.informPlayer(player, "You're not able to cast " + + card.getIdName() + " or you canceled the casting."); + } + } + return true; + } +} + +class EmergentUltimatumTarget extends TargetCardInLibrary { + + private static final FilterCard filter + = new FilterCard("monocolored cards with different names"); + + static { + filter.add(MonocoloredPredicate.instance); + } + + EmergentUltimatumTarget() { + super(0, 3, filter); + } + + private EmergentUltimatumTarget(final EmergentUltimatumTarget target) { + super(target); + } + + @Override + public EmergentUltimatumTarget copy() { + return new EmergentUltimatumTarget(this); + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + if (!super.canTarget(playerId, id, source, game)) { + return false; + } + Card card = game.getCard(id); + return card != null + && this.getTargets() + .stream() + .map(game::getCard) + .map(MageObject::getName) + .noneMatch(card.getName()::equals); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java b/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java new file mode 100644 index 0000000000..f750ef9d4a --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java @@ -0,0 +1,97 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ExileTargetForSourceEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EmielTheBlessed extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another target creature you control"); + + static { + filter.add(AnotherPredicate.instance); + } + + public EmielTheBlessed(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.UNICORN); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // {3}: Exile another target creature you control, then return it to the battlefield under its owner's control. + Ability ability = new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new GenericManaCost(3)); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // Whenever another creature enters the battlefield under your control, you may pay {G/W}. + // If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + Zone.BATTLEFIELD, new DoIfCostPaid(new EmielTheBlessedEffect(), new ManaCostsImpl<>("{G/W}")), + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, false, SetTargetPointer.PERMANENT, + "Whenever another creature enters the battlefield under your control, you may pay {G/W}. " + + "If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead." + )); + } + + private EmielTheBlessed(final EmielTheBlessed card) { + super(card); + } + + @Override + public EmielTheBlessed copy() { + return new EmielTheBlessed(this); + } +} + +class EmielTheBlessedEffect extends OneShotEffect { + + EmielTheBlessedEffect() { + super(Outcome.Benefit); + } + + private EmielTheBlessedEffect(final EmielTheBlessedEffect effect) { + super(effect); + } + + @Override + public EmielTheBlessedEffect copy() { + return new EmielTheBlessedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + return false; + } + int counters = permanent.hasSubtype(SubType.UNICORN, game) ? 2 : 1; + return permanent.addCounters(CounterType.P1P1.createInstance(counters), source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java b/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java index e8560d4270..a53fb3e2ca 100644 --- a/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java +++ b/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java @@ -88,7 +88,7 @@ class EmpoweredAutogeneratorManaEffect extends ManaEffect { if (game == null) { return mana; } - game.applyEffects(); + game.getState().processAction(game); Permanent sourcePermanent = game.getState().getPermanent(source.getSourceId()); if (sourcePermanent == null) { return mana; diff --git a/Mage.Sets/src/mage/cards/e/EmrakulTheAeonsTorn.java b/Mage.Sets/src/mage/cards/e/EmrakulTheAeonsTorn.java index b08775abcb..91caa63e32 100644 --- a/Mage.Sets/src/mage/cards/e/EmrakulTheAeonsTorn.java +++ b/Mage.Sets/src/mage/cards/e/EmrakulTheAeonsTorn.java @@ -3,7 +3,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.PutIntoGraveFromAnywhereSourceTriggeredAbility; import mage.abilities.effects.common.CastSourceTriggeredAbility; import mage.abilities.effects.common.ShuffleIntoLibraryGraveOfSourceOwnerEffect; @@ -39,7 +39,7 @@ public final class EmrakulTheAeonsTorn extends CardImpl { this.toughness = new MageInt(15); // Emrakul, the Aeons Torn can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // When you cast Emrakul, take an extra turn after this one. this.addAbility(new CastSourceTriggeredAbility(new AddExtraTurnControllerEffect())); diff --git a/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java b/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java index d8338673a1..bebd901a61 100644 --- a/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java +++ b/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java @@ -2,21 +2,21 @@ package mage.cards.e; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; -import mage.util.CardUtil; import java.util.UUID; @@ -35,7 +35,9 @@ public final class EmryLurkerOfTheLoch extends CardImpl { this.toughness = new MageInt(2); // This spell costs {1} less to cast for each artifact you control. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new EmryLurkerOfTheLochCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, + new SpellCostReductionForEachSourceEffect(1, ArtifactYouControlCount.instance) + ).addHint(ArtifactYouControlHint.instance)); // When Emry, Lurker of the Loch enters the battlefield, put the top four cards of your library into your graveyard. this.addAbility(new EntersBattlefieldTriggeredAbility( @@ -58,40 +60,6 @@ public final class EmryLurkerOfTheLoch extends CardImpl { } } -class EmryLurkerOfTheLochCostReductionEffect extends CostModificationEffectImpl { - - EmryLurkerOfTheLochCostReductionEffect() { - super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "This spell costs {1} less to cast for each artifact you control"; - } - - private EmryLurkerOfTheLochCostReductionEffect(final EmryLurkerOfTheLochCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - int reductionAmount = game.getBattlefield().count( - StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, - source.getSourceId(), source.getControllerId(), game - ); - CardUtil.reduceCost(abilityToModify, reductionAmount); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify instanceof SpellAbility - && abilityToModify.getSourceId().equals(source.getSourceId()) - && game.getCard(abilityToModify.getSourceId()) != null; - } - - @Override - public EmryLurkerOfTheLochCostReductionEffect copy() { - return new EmryLurkerOfTheLochCostReductionEffect(this); - } -} - class EmryLurkerOfTheLochPlayEffect extends AsThoughEffectImpl { EmryLurkerOfTheLochPlayEffect() { diff --git a/Mage.Sets/src/mage/cards/e/EnatuGolem.java b/Mage.Sets/src/mage/cards/e/EnatuGolem.java index aba9da3ec1..b6f6f92caf 100644 --- a/Mage.Sets/src/mage/cards/e/EnatuGolem.java +++ b/Mage.Sets/src/mage/cards/e/EnatuGolem.java @@ -3,7 +3,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class EnatuGolem extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(5); - this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(4), false)); + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(4), false)); } public EnatuGolem(final EnatuGolem card) { diff --git a/Mage.Sets/src/mage/cards/e/EncampmentKeeper.java b/Mage.Sets/src/mage/cards/e/EncampmentKeeper.java index 13fb82d3cb..d2c401c54f 100644 --- a/Mage.Sets/src/mage/cards/e/EncampmentKeeper.java +++ b/Mage.Sets/src/mage/cards/e/EncampmentKeeper.java @@ -26,7 +26,7 @@ public final class EncampmentKeeper extends CardImpl { public EncampmentKeeper(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/e/EnchantmentAlteration.java b/Mage.Sets/src/mage/cards/e/EnchantmentAlteration.java index 8b611fb6ac..7a53b0138f 100644 --- a/Mage.Sets/src/mage/cards/e/EnchantmentAlteration.java +++ b/Mage.Sets/src/mage/cards/e/EnchantmentAlteration.java @@ -1,144 +1,144 @@ -package mage.cards.e; - -import java.util.UUID; -import mage.MageItem; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.AttachmentAttachedToCardTypePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; -import mage.players.Player; -import mage.target.Target; -import mage.target.TargetPermanent; - -/** - * - * @author jeffwadsworth - */ -public final class EnchantmentAlteration extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("aura attached to a creature or land"); - private static final FilterPermanent filter2 = new FilterPermanent("another target permanent that shares that type of creature or land"); - - static { - filter.add(SubType.AURA.getPredicate()); - filter.add(Predicates.or(new AttachmentAttachedToCardTypePredicate(CardType.CREATURE), - new AttachmentAttachedToCardTypePredicate(CardType.LAND))); - filter2.add(new SharesEnchantedCardTypePredicate()); - // another target permanent is handled in this predicate - } - - public EnchantmentAlteration(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); - - // Attach target Aura attached to a creature or land to another permanent of that type. - this.getSpellAbility().addEffect(new EnchantmentAlterationEffect()); - TargetPermanent targetAura = new TargetPermanent(filter); - this.getSpellAbility().addTarget(targetAura); - - TargetPermanent targetCreatureOrLandThatSharesTheEnchantedCardType = new TargetPermanent(filter2); - this.getSpellAbility().addTarget(targetCreatureOrLandThatSharesTheEnchantedCardType); - - } - - public EnchantmentAlteration(final EnchantmentAlteration card) { - super(card); - } - - @Override - public EnchantmentAlteration copy() { - return new EnchantmentAlteration(this); - } - -} - -class SharesEnchantedCardTypePredicate implements ObjectSourcePlayerPredicate> { - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - StackObject source = game.getStack().getStackObject(input.getSourceId()); - Permanent auraIsAttachedToThisPermanent = null; - Permanent newPermanentToAttachAuraTo; - if (source != null) { - if (source.getStackAbility().getTargets().isEmpty() - || source.getStackAbility().getTargets().get(0).getTargets().isEmpty()) { - return true; - } - Permanent auraPermanent = game.getPermanent( - source.getStackAbility().getTargets().get(0).getTargets().get(0)); // targeted aura enchanting land or creature - if (auraPermanent != null) { - auraIsAttachedToThisPermanent = game.getPermanent(auraPermanent.getAttachedTo()); - } - if (auraIsAttachedToThisPermanent == null) { // the original permanent the aura is attached to - return false; - } - newPermanentToAttachAuraTo = game.getPermanent(input.getObject().getId()); // the new target creature or land to enchant - if (newPermanentToAttachAuraTo == auraIsAttachedToThisPermanent) { - return false; // must be another permanent - } - if (auraIsAttachedToThisPermanent.isCreature() - && newPermanentToAttachAuraTo.isCreature()) { - return true; - } - if (auraIsAttachedToThisPermanent.isLand() - && newPermanentToAttachAuraTo.isLand()) { - return true; - } - return false; - } - return true; - } - -} - -class EnchantmentAlterationEffect extends OneShotEffect { - - public EnchantmentAlterationEffect() { - super(Outcome.BoostCreature); - this.staticText = "Attach target Aura attached to a creature or land to another permanent of that type"; - } - - public EnchantmentAlterationEffect(final EnchantmentAlterationEffect effect) { - super(effect); - } - - @Override - public EnchantmentAlterationEffect copy() { - return new EnchantmentAlterationEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent aura = game.getPermanent(source.getFirstTarget()); - Permanent permanentToBeAttachedTo = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (aura != null - && permanentToBeAttachedTo != null) { - Permanent oldPermanent = game.getPermanent(aura.getAttachedTo()); - if (oldPermanent != null - && !oldPermanent.equals(permanentToBeAttachedTo)) { - Target auraTarget = aura.getSpellAbility().getTargets().get(0); - if (!auraTarget.canTarget(permanentToBeAttachedTo.getId(), game)) { - game.informPlayers(aura.getLogName() + " was not attched to " + permanentToBeAttachedTo.getLogName() + " because it's no legal target for the aura"); - } else if (oldPermanent.removeAttachment(aura.getId(), game)) { - game.informPlayers(aura.getLogName() + " was unattached from " + oldPermanent.getLogName() + " and attached to " + permanentToBeAttachedTo.getLogName()); - permanentToBeAttachedTo.addAttachment(aura.getId(), game); - } - } - } - return true; - } - return false; - } -} +package mage.cards.e; + +import java.util.UUID; +import mage.MageItem; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AttachmentAttachedToCardTypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; + +/** + * + * @author jeffwadsworth + */ +public final class EnchantmentAlteration extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("aura attached to a creature or land"); + private static final FilterPermanent filter2 = new FilterPermanent("another target permanent that shares that type of creature or land"); + + static { + filter.add(SubType.AURA.getPredicate()); + filter.add(Predicates.or(new AttachmentAttachedToCardTypePredicate(CardType.CREATURE), + new AttachmentAttachedToCardTypePredicate(CardType.LAND))); + filter2.add(new SharesEnchantedCardTypePredicate()); + // another target permanent is handled in this predicate + } + + public EnchantmentAlteration(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // Attach target Aura attached to a creature or land to another permanent of that type. + this.getSpellAbility().addEffect(new EnchantmentAlterationEffect()); + TargetPermanent targetAura = new TargetPermanent(filter); + this.getSpellAbility().addTarget(targetAura); + + TargetPermanent targetCreatureOrLandThatSharesTheEnchantedCardType = new TargetPermanent(filter2); + this.getSpellAbility().addTarget(targetCreatureOrLandThatSharesTheEnchantedCardType); + + } + + public EnchantmentAlteration(final EnchantmentAlteration card) { + super(card); + } + + @Override + public EnchantmentAlteration copy() { + return new EnchantmentAlteration(this); + } + +} + +class SharesEnchantedCardTypePredicate implements ObjectSourcePlayerPredicate> { + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + StackObject source = game.getStack().getStackObject(input.getSourceId()); + Permanent auraIsAttachedToThisPermanent = null; + Permanent newPermanentToAttachAuraTo; + if (source != null) { + if (source.getStackAbility().getTargets().isEmpty() + || source.getStackAbility().getTargets().get(0).getTargets().isEmpty()) { + return true; + } + Permanent auraPermanent = game.getPermanent( + source.getStackAbility().getTargets().get(0).getTargets().get(0)); // targeted aura enchanting land or creature + if (auraPermanent != null) { + auraIsAttachedToThisPermanent = game.getPermanent(auraPermanent.getAttachedTo()); + } + if (auraIsAttachedToThisPermanent == null) { // the original permanent the aura is attached to + return false; + } + newPermanentToAttachAuraTo = game.getPermanent(input.getObject().getId()); // the new target creature or land to enchant + if (newPermanentToAttachAuraTo == auraIsAttachedToThisPermanent) { + return false; // must be another permanent + } + if (auraIsAttachedToThisPermanent.isCreature() + && newPermanentToAttachAuraTo.isCreature()) { + return true; + } + if (auraIsAttachedToThisPermanent.isLand() + && newPermanentToAttachAuraTo.isLand()) { + return true; + } + return false; + } + return true; + } + +} + +class EnchantmentAlterationEffect extends OneShotEffect { + + public EnchantmentAlterationEffect() { + super(Outcome.BoostCreature); + this.staticText = "Attach target Aura attached to a creature or land to another permanent of that type"; + } + + public EnchantmentAlterationEffect(final EnchantmentAlterationEffect effect) { + super(effect); + } + + @Override + public EnchantmentAlterationEffect copy() { + return new EnchantmentAlterationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent aura = game.getPermanent(source.getFirstTarget()); + Permanent permanentToBeAttachedTo = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + if (aura != null + && permanentToBeAttachedTo != null) { + Permanent oldPermanent = game.getPermanent(aura.getAttachedTo()); + if (oldPermanent != null + && !oldPermanent.equals(permanentToBeAttachedTo)) { + Target auraTarget = aura.getSpellAbility().getTargets().get(0); + if (!auraTarget.canTarget(permanentToBeAttachedTo.getId(), game)) { + game.informPlayers(aura.getLogName() + " was not attched to " + permanentToBeAttachedTo.getLogName() + " because it's no legal target for the aura"); + } else if (oldPermanent.removeAttachment(aura.getId(), game)) { + game.informPlayers(aura.getLogName() + " was unattached from " + oldPermanent.getLogName() + " and attached to " + permanentToBeAttachedTo.getLogName()); + permanentToBeAttachedTo.addAttachment(aura.getId(), game); + } + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EndlessCockroaches.java b/Mage.Sets/src/mage/cards/e/EndlessCockroaches.java index ef402fd92d..97b1e014e3 100644 --- a/Mage.Sets/src/mage/cards/e/EndlessCockroaches.java +++ b/Mage.Sets/src/mage/cards/e/EndlessCockroaches.java @@ -3,7 +3,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class EndlessCockroaches extends CardImpl { this.toughness = new MageInt(1); // When Endless Cockroaches dies, return it to its owner's hand. - this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect())); } public EndlessCockroaches(final EndlessCockroaches card) { diff --git a/Mage.Sets/src/mage/cards/e/EndlessWhispers.java b/Mage.Sets/src/mage/cards/e/EndlessWhispers.java index 0eab041fec..4cec197aea 100644 --- a/Mage.Sets/src/mage/cards/e/EndlessWhispers.java +++ b/Mage.Sets/src/mage/cards/e/EndlessWhispers.java @@ -4,7 +4,7 @@ package mage.cards.e; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.Effect; @@ -36,7 +36,7 @@ public final class EndlessWhispers extends CardImpl { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceToBattlefieldEffect()); Effect effect = new CreateDelayedTriggeredAbilityEffect(delayedAbility, true); effect.setText("choose target opponent. That player puts this card from its owner's graveyard onto the battlefield under their control at the beginning of the next end step"); - Ability gainAbility = new DiesTriggeredAbility(effect); + Ability gainAbility = new DiesSourceTriggeredAbility(effect); gainAbility.addTarget(new TargetOpponent()); effect = new GainAbilityAllEffect(gainAbility, Duration.WhileOnBattlefield, new FilterCreaturePermanent("Each creature")); diff --git a/Mage.Sets/src/mage/cards/e/EnfysNest.java b/Mage.Sets/src/mage/cards/e/EnfysNest.java index a1c387a804..661b8856c6 100644 --- a/Mage.Sets/src/mage/cards/e/EnfysNest.java +++ b/Mage.Sets/src/mage/cards/e/EnfysNest.java @@ -65,7 +65,7 @@ class EnfysNestEffect extends ExileTargetEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if(permanent != null) { // you may exile target creature an opponent controls if(!super.apply(game, source)) { diff --git a/Mage.Sets/src/mage/cards/e/EngulfingSlagwurm.java b/Mage.Sets/src/mage/cards/e/EngulfingSlagwurm.java index 7a9919f7c4..2b7acd48cc 100644 --- a/Mage.Sets/src/mage/cards/e/EngulfingSlagwurm.java +++ b/Mage.Sets/src/mage/cards/e/EngulfingSlagwurm.java @@ -4,7 +4,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; @@ -30,7 +30,7 @@ public final class EngulfingSlagwurm extends CardImpl { this.toughness = new MageInt(7); // Whenever Engulfing Slagwurm blocks or becomes blocked by a creature, destroy that creature. You gain life equal to that creature's toughness. - Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(new DestroyTargetEffect(), false); + Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(new DestroyTargetEffect(), false); ability.addEffect(new EngulfingSlagwurmEffect()); this.addAbility(ability); } @@ -60,7 +60,7 @@ class EngulfingSlagwurmEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent creature = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (creature != null && controller != null) { controller.gainLife(creature.getPower().getValue(), game, source); } diff --git a/Mage.Sets/src/mage/cards/e/EnigmaticIncarnation.java b/Mage.Sets/src/mage/cards/e/EnigmaticIncarnation.java index 1270759484..d08212116b 100644 --- a/Mage.Sets/src/mage/cards/e/EnigmaticIncarnation.java +++ b/Mage.Sets/src/mage/cards/e/EnigmaticIncarnation.java @@ -1,5 +1,6 @@ package mage.cards.e; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -22,8 +23,6 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; -import java.util.UUID; - /** * @author TheElk801 */ @@ -59,10 +58,10 @@ class EnigmaticIncarnationEffect extends OneShotEffect { EnigmaticIncarnationEffect() { super(Outcome.Benefit); - staticText = "you may sacrifice another enchantment. If you do, " + - "search your library for a creature card with converted mana cost " + - "equal to 1 plus the sacrificed enchantment's converted mana cost, " + - "put that card onto the battlefield, then shuffle your library."; + staticText = "you may sacrifice another enchantment. If you do, " + + "search your library for a creature card with converted mana cost " + + "equal to 1 plus the sacrificed enchantment's converted mana cost, " + + "put that card onto the battlefield, then shuffle your library."; } private EnigmaticIncarnationEffect(final EnigmaticIncarnationEffect effect) { @@ -88,6 +87,7 @@ class EnigmaticIncarnationEffect extends OneShotEffect { if (permanent == null) { return false; } + game.getState().processAction(game); int cmc = permanent.getConvertedManaCost(); if (!permanent.sacrifice(source.getSourceId(), game)) { return false; @@ -96,4 +96,4 @@ class EnigmaticIncarnationEffect extends OneShotEffect { filterCard.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, cmc + 1)); return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filterCard)).apply(game, source); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/e/EnterTheGodEternals.java b/Mage.Sets/src/mage/cards/e/EnterTheGodEternals.java index fe6bdebf9b..352c2812c8 100644 --- a/Mage.Sets/src/mage/cards/e/EnterTheGodEternals.java +++ b/Mage.Sets/src/mage/cards/e/EnterTheGodEternals.java @@ -1,5 +1,6 @@ package mage.cards.e; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.keyword.AmassEffect; @@ -7,7 +8,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -15,8 +15,6 @@ import mage.target.Target; import mage.target.TargetPlayer; import mage.target.common.TargetCreaturePermanent; -import java.util.UUID; - /** * @author TheElk801 */ @@ -45,8 +43,8 @@ class EnterTheGodEternalsEffect extends OneShotEffect { EnterTheGodEternalsEffect() { super(Outcome.Benefit); - staticText = "{this} deals 4 damage to target creature and you gain life equal to the damage dealt this way. " + - "Target player puts the top four cards of their library into their graveyard. Amass 4."; + staticText = "{this} deals 4 damage to target creature and you gain life equal to the damage dealt this way. " + + "Target player mills four cards. Amass 4."; } private EnterTheGodEternalsEffect(final EnterTheGodEternalsEffect effect) { @@ -73,10 +71,10 @@ class EnterTheGodEternalsEffect extends OneShotEffect { } Player player = game.getPlayer(targetId); if (player != null) { - player.moveCards(player.getLibrary().getTopCards(game, 4), Zone.GRAVEYARD, source, game); + player.millCards(4, source, game); } } } return new AmassEffect(4).apply(game, source); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/e/EnthrallingHold.java b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java new file mode 100644 index 0000000000..94ff85687b --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java @@ -0,0 +1,54 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.ControlEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetTappedPermanentAsYouCast; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class EnthrallingHold extends CardImpl { + + public EnthrallingHold(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetTappedPermanentAsYouCast(StaticFilters.FILTER_PERMANENT_CREATURE); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.GainControl)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // You can't choose an untapped creature as this spell's target as you cast it. + Effect effect = new ControlEnchantedEffect(); + effect.setText("You can't choose an untapped creature as this spell's target as you cast it.
" + effect.getText(null)); + + // You control enchanted creature. + this.addAbility(new SimpleStaticAbility(effect)); + } + + private EnthrallingHold(final EnthrallingHold card) { + super(card); + } + + @Override + public EnthrallingHold copy() { + return new EnthrallingHold(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/EonFrolicker.java b/Mage.Sets/src/mage/cards/e/EonFrolicker.java new file mode 100644 index 0000000000..09815576b6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EonFrolicker.java @@ -0,0 +1,100 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.CastFromEverywhereSourceCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPlayer; +import mage.filter.StaticFilters; +import mage.filter.predicate.other.PlayerIdPredicate; +import mage.game.Game; +import mage.game.turn.TurnMod; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EonFrolicker extends CardImpl { + + public EonFrolicker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.OTTER); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Eon Frolicker enters the battlefield, if you cast it, target opponent takes an extra turn after this one. Until your next turn, you and planeswalkers you control gain protection from that player. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new EonFrolickerEffect()), + CastFromEverywhereSourceCondition.instance, "When {this} enters the battlefield, " + + "if you cast it, target opponent takes an extra turn after this one. Until your next turn, " + + "you and planeswalkers you control gain protection from that player." + ); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private EonFrolicker(final EonFrolicker card) { + super(card); + } + + @Override + public EonFrolicker copy() { + return new EonFrolicker(this); + } +} + +class EonFrolickerEffect extends OneShotEffect { + + EonFrolickerEffect() { + super(Outcome.Benefit); + } + + private EonFrolickerEffect(final EonFrolickerEffect effect) { + super(effect); + } + + @Override + public EonFrolickerEffect copy() { + return new EonFrolickerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + game.getState().getTurnMods().add(new TurnMod(player.getId(), false)); + FilterPlayer filter = new FilterPlayer(player.getName()); + filter.add(new PlayerIdPredicate(player.getId())); + Ability ability = new ProtectionAbility(filter); + game.addEffect(new GainAbilityControlledEffect( + ability, Duration.UntilYourNextTurn, + StaticFilters.FILTER_PERMANENT_PLANESWALKER + ), source); + game.addEffect(new GainAbilityControllerEffect( + ability, Duration.UntilYourNextTurn + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/Ephemerate.java b/Mage.Sets/src/mage/cards/e/Ephemerate.java index b1fa057447..871ae3e169 100644 --- a/Mage.Sets/src/mage/cards/e/Ephemerate.java +++ b/Mage.Sets/src/mage/cards/e/Ephemerate.java @@ -21,7 +21,7 @@ public final class Ephemerate extends CardImpl { // Exile target creature you control, then return it to the battlefield under its owner's control. this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addEffect(new ExileTargetForSourceEffect()); - this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); // Rebound this.addAbility(new ReboundAbility()); diff --git a/Mage.Sets/src/mage/cards/e/EpicConfrontation.java b/Mage.Sets/src/mage/cards/e/EpicConfrontation.java index 0dcd2c6ff8..8fe1d32f2e 100644 --- a/Mage.Sets/src/mage/cards/e/EpicConfrontation.java +++ b/Mage.Sets/src/mage/cards/e/EpicConfrontation.java @@ -1,7 +1,5 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.FightTargetsEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -9,24 +7,18 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class EpicConfrontation extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public EpicConfrontation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); @@ -37,11 +29,11 @@ public final class EpicConfrontation extends CardImpl { effect.setText("It fights target creature you don't control"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - Target target = new TargetCreaturePermanent(filter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL); this.getSpellAbility().addTarget(target); } - public EpicConfrontation(final EpicConfrontation card) { + private EpicConfrontation(final EpicConfrontation card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/e/EpitaphGolem.java b/Mage.Sets/src/mage/cards/e/EpitaphGolem.java index 9751cffa5b..5d747a640c 100644 --- a/Mage.Sets/src/mage/cards/e/EpitaphGolem.java +++ b/Mage.Sets/src/mage/cards/e/EpitaphGolem.java @@ -7,7 +7,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -15,6 +15,7 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; +import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; /** @@ -32,7 +33,7 @@ public final class EpitaphGolem extends CardImpl { // {2}: Put target card from your graveyard on the bottom of your library. Ability ability = new SimpleActivatedAbility( Zone.BATTLEFIELD, - new EpitaphGolemGraveyardToLibraryEffect(), + new PutOnLibraryTargetEffect(false), new ManaCostsImpl("{2}")); ability.addTarget(new TargetCardInYourGraveyard()); this.addAbility(ability); @@ -66,9 +67,9 @@ class EpitaphGolemGraveyardToLibraryEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { - return card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + return controller.putCardsOnBottomOfLibrary(game.getCard(getTargetPointer().getFirst(game, source)), game, source, true); } return false; } diff --git a/Mage.Sets/src/mage/cards/e/Epochrasite.java b/Mage.Sets/src/mage/cards/e/Epochrasite.java index 439e042ddf..f0c0c3cdd0 100644 --- a/Mage.Sets/src/mage/cards/e/Epochrasite.java +++ b/Mage.Sets/src/mage/cards/e/Epochrasite.java @@ -5,10 +5,10 @@ import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.InvertCondition; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainSuspendEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -41,12 +41,12 @@ public final class Epochrasite extends CardImpl { // Epochrasite enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand. this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), - new InvertCondition(CastFromHandSourceCondition.instance), + new InvertCondition(CastFromHandSourcePermanentCondition.instance), "{this} enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand",""), new CastFromHandWatcher()); // When Epochrasite dies, exile it with three time counters on it and it gains suspend. - this.addAbility(new DiesTriggeredAbility(new EpochrasiteEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new EpochrasiteEffect())); } public Epochrasite(final Epochrasite card) { diff --git a/Mage.Sets/src/mage/cards/e/ErrantMinion.java b/Mage.Sets/src/mage/cards/e/ErrantMinion.java index 0650304ef2..f6d80ffc9e 100644 --- a/Mage.Sets/src/mage/cards/e/ErrantMinion.java +++ b/Mage.Sets/src/mage/cards/e/ErrantMinion.java @@ -1,100 +1,100 @@ -package mage.cards.e; - -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.PreventDamageToTargetEffect; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.TargetPermanent; -import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; -import mage.util.ManaUtil; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class ErrantMinion extends CardImpl { - - public ErrantMinion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // At the beginning of the upkeep of enchanted creature's controller, that player may pay any amount of mana. Errant Minion deals 2 damage to that player. Prevent X of that damage, where X is the amount of mana that player paid this way. - this.addAbility(new BeginningOfUpkeepTriggeredAbility( - Zone.BATTLEFIELD, - new ErrantMinionEffect(), - TargetController.CONTROLLER_ATTACHED_TO, - false)); - - } - - private ErrantMinion(final ErrantMinion card) { - super(card); - } - - @Override - public ErrantMinion copy() { - return new ErrantMinion(this); - } -} - -class ErrantMinionEffect extends OneShotEffect { - - public ErrantMinionEffect() { - super(Outcome.Damage); - this.staticText = "that player may pay any amount of mana. Errant Minion deals 2 damage to that player. Prevent X of that damage, where X is the amount of mana that player paid this way"; - } - - public ErrantMinionEffect(final ErrantMinionEffect effect) { - super(effect); - } - - @Override - public ErrantMinionEffect copy() { - return new ErrantMinionEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent errantMinion = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (errantMinion == null) { - return false; - } - Permanent enchantedCreature = game.getPermanentOrLKIBattlefield(errantMinion.getAttachedTo()); - if (enchantedCreature == null) { - return false; - } - Player controllerOfEnchantedCreature = game.getPlayer(enchantedCreature.getControllerId()); - if (controllerOfEnchantedCreature != null && controllerOfEnchantedCreature.canRespond()) { - int manaPaid = ManaUtil.playerPaysXGenericMana(false, "Errant Minion", controllerOfEnchantedCreature, source, game); - if (manaPaid > 0) { - PreventDamageToTargetEffect effect = new PreventDamageToTargetEffect(Duration.OneUse, manaPaid); - effect.setTargetPointer(new FixedTarget(controllerOfEnchantedCreature.getId())); - game.addEffect(effect, source); - DamageTargetEffect effect2 = new DamageTargetEffect(2); - effect2.setTargetPointer(new FixedTarget(controllerOfEnchantedCreature.getId())); - effect2.apply(game, source); - return true; - } - } - return false; - } -} +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.PreventDamageToTargetEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.ManaUtil; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class ErrantMinion extends CardImpl { + + public ErrantMinion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of the upkeep of enchanted creature's controller, that player may pay any amount of mana. Errant Minion deals 2 damage to that player. Prevent X of that damage, where X is the amount of mana that player paid this way. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, + new ErrantMinionEffect(), + TargetController.CONTROLLER_ATTACHED_TO, + false)); + + } + + private ErrantMinion(final ErrantMinion card) { + super(card); + } + + @Override + public ErrantMinion copy() { + return new ErrantMinion(this); + } +} + +class ErrantMinionEffect extends OneShotEffect { + + public ErrantMinionEffect() { + super(Outcome.Damage); + this.staticText = "that player may pay any amount of mana. Errant Minion deals 2 damage to that player. Prevent X of that damage, where X is the amount of mana that player paid this way"; + } + + public ErrantMinionEffect(final ErrantMinionEffect effect) { + super(effect); + } + + @Override + public ErrantMinionEffect copy() { + return new ErrantMinionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent errantMinion = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (errantMinion == null) { + return false; + } + Permanent enchantedCreature = game.getPermanentOrLKIBattlefield(errantMinion.getAttachedTo()); + if (enchantedCreature == null) { + return false; + } + Player controllerOfEnchantedCreature = game.getPlayer(enchantedCreature.getControllerId()); + if (controllerOfEnchantedCreature != null && controllerOfEnchantedCreature.canRespond()) { + int manaPaid = ManaUtil.playerPaysXGenericMana(false, "Errant Minion", controllerOfEnchantedCreature, source, game); + if (manaPaid > 0) { + PreventDamageToTargetEffect effect = new PreventDamageToTargetEffect(Duration.OneUse, manaPaid); + effect.setTargetPointer(new FixedTarget(controllerOfEnchantedCreature.getId())); + game.addEffect(effect, source); + DamageTargetEffect effect2 = new DamageTargetEffect(2); + effect2.setTargetPointer(new FixedTarget(controllerOfEnchantedCreature.getId())); + effect2.apply(game, source); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EscapeProtocol.java b/Mage.Sets/src/mage/cards/e/EscapeProtocol.java new file mode 100644 index 0000000000..4aa3a6b602 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EscapeProtocol.java @@ -0,0 +1,58 @@ +package mage.cards.e; + +import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.ExileTargetForSourceEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EscapeProtocol extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent("artifact or creature you control"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + + public EscapeProtocol(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + // Whenever you cycle a card, you may pay {1}. When you do, exile target artifact or creature you control, then return it to the battlefield under its owner's control. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ExileTargetForSourceEffect(), false, + "exile target artifact or creature you control, " + + "then return it to the battlefield under its owner's control." + ); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(new CycleControllerTriggeredAbility(new DoWhenCostPaid( + ability, new GenericManaCost(1), "Pay {1}?" + ))); + } + + private EscapeProtocol(final EscapeProtocol card) { + super(card); + } + + @Override + public EscapeProtocol copy() { + return new EscapeProtocol(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EscapedNull.java b/Mage.Sets/src/mage/cards/e/EscapedNull.java index fdfcb2db58..2a72bd2b0f 100644 --- a/Mage.Sets/src/mage/cards/e/EscapedNull.java +++ b/Mage.Sets/src/mage/cards/e/EscapedNull.java @@ -3,7 +3,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class EscapedNull extends CardImpl { this.toughness = new MageInt(2); this.addAbility(LifelinkAbility.getInstance()); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(5, 0, Duration.EndOfTurn), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(5, 0, Duration.EndOfTurn), false)); } public EscapedNull(final EscapedNull card) { diff --git a/Mage.Sets/src/mage/cards/e/EsperSojourners.java b/Mage.Sets/src/mage/cards/e/EsperSojourners.java index ebbaca1dcb..46f72cfd8f 100644 --- a/Mage.Sets/src/mage/cards/e/EsperSojourners.java +++ b/Mage.Sets/src/mage/cards/e/EsperSojourners.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CycleTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.MayTapOrUntapTargetEffect; import mage.abilities.keyword.CyclingAbility; @@ -34,7 +34,7 @@ public final class EsperSojourners extends CardImpl { // When you cycle Esper Sojourners or it dies, you may tap or untap target permanent. Ability ability1 = new CycleTriggeredAbility(new MayTapOrUntapTargetEffect()); - Ability ability2 = new DiesTriggeredAbility(new MayTapOrUntapTargetEffect()); + Ability ability2 = new DiesSourceTriggeredAbility(new MayTapOrUntapTargetEffect()); ability1.addTarget(new TargetPermanent()); ability2.addTarget(new TargetPermanent()); this.addAbility(ability1); diff --git a/Mage.Sets/src/mage/cards/e/EssenceBacklash.java b/Mage.Sets/src/mage/cards/e/EssenceBacklash.java index 0fcc6e2e70..50410118aa 100644 --- a/Mage.Sets/src/mage/cards/e/EssenceBacklash.java +++ b/Mage.Sets/src/mage/cards/e/EssenceBacklash.java @@ -41,7 +41,7 @@ class EssenceBacklashEffect extends OneShotEffect { public EssenceBacklashEffect() { super(Outcome.Damage); - staticText = "Counter target creature spell. Essence Backlash deals damage equal to that spell's power to its controller"; + staticText = "Counter target creature spell. {this} deals damage equal to that spell's power to its controller"; } public EssenceBacklashEffect(final EssenceBacklashEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EssenceDepleter.java b/Mage.Sets/src/mage/cards/e/EssenceDepleter.java index 6f54e14200..185570dfd4 100644 --- a/Mage.Sets/src/mage/cards/e/EssenceDepleter.java +++ b/Mage.Sets/src/mage/cards/e/EssenceDepleter.java @@ -1,12 +1,9 @@ - package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.keyword.DevoidAbility; @@ -17,14 +14,15 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class EssenceDepleter extends CardImpl { public EssenceDepleter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.ELDRAZI); this.subtype.add(SubType.DRONE); this.power = new MageInt(2); @@ -35,9 +33,7 @@ public final class EssenceDepleter extends CardImpl { // {1}{C}: Target opponent loses 1 life and you gain 1 life. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), new ManaCostsImpl("{1}{C}")); - Effect effect = new GainLifeEffect(1); - effect.setText("and you gain 1 life"); - ability.addEffect(effect); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/e/EssenceFlux.java b/Mage.Sets/src/mage/cards/e/EssenceFlux.java index 8364d113e6..907217250c 100644 --- a/Mage.Sets/src/mage/cards/e/EssenceFlux.java +++ b/Mage.Sets/src/mage/cards/e/EssenceFlux.java @@ -1,7 +1,5 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -13,16 +11,15 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class EssenceFlux extends CardImpl { @@ -68,30 +65,25 @@ class EssenceFluxEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Cards cardsToBattlefield = new CardsImpl(); - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - if (exileZoneId != null) { - ExileZone exileZone = game.getExile().getExileZone(exileZoneId); - if (exileZone != null) { - for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { - if (exileZone.contains(targetId)) { - cardsToBattlefield.add(targetId); - } else { - Card card = game.getCard(targetId); - if (card instanceof MeldCard) { - MeldCard meldCard = (MeldCard) card; - Card topCard = meldCard.getTopHalfCard(); - Card bottomCard = meldCard.getBottomHalfCard(); - if (topCard.getZoneChangeCounter(game) == meldCard.getTopLastZoneChangeCounter() && exileZone.contains(topCard.getId())) { - cardsToBattlefield.add(topCard); - } - if (bottomCard.getZoneChangeCounter(game) == meldCard.getBottomLastZoneChangeCounter() && exileZone.contains(bottomCard.getId())) { - cardsToBattlefield.add(bottomCard); - } - } + for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { + if (game.getExile().containsId(targetId, game)) { + cardsToBattlefield.add(targetId); + } else { + Card card = game.getCard(targetId); + if (card instanceof MeldCard) { + MeldCard meldCard = (MeldCard) card; + Card topCard = meldCard.getTopHalfCard(); + Card bottomCard = meldCard.getBottomHalfCard(); + if (topCard.getZoneChangeCounter(game) == meldCard.getTopLastZoneChangeCounter() && game.getExile().containsId(topCard.getId(), game)) { + cardsToBattlefield.add(topCard); + } + if (bottomCard.getZoneChangeCounter(game) == meldCard.getBottomLastZoneChangeCounter() && game.getExile().containsId(bottomCard.getId(), game)) { + cardsToBattlefield.add(bottomCard); } } } } + if (!cardsToBattlefield.isEmpty()) { controller.moveCards(cardsToBattlefield.getCards(game), Zone.BATTLEFIELD, source, game, false, false, true, null); for (UUID cardId : cardsToBattlefield) { diff --git a/Mage.Sets/src/mage/cards/e/EssenceOfTheWild.java b/Mage.Sets/src/mage/cards/e/EssenceOfTheWild.java index eb64cf137c..c2dab424d6 100644 --- a/Mage.Sets/src/mage/cards/e/EssenceOfTheWild.java +++ b/Mage.Sets/src/mage/cards/e/EssenceOfTheWild.java @@ -1,10 +1,10 @@ package mage.cards.e; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.CopyEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -14,8 +14,6 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; -import java.util.UUID; - /** * @author BetaSteward */ @@ -68,7 +66,7 @@ class EssenceOfTheWildEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourceObject != null) { - game.addEffect(new CopyEffect(Duration.Custom, sourceObject, event.getTargetId()), source); + game.copyPermanent(sourceObject, event.getTargetId(), source, null); } return false; } diff --git a/Mage.Sets/src/mage/cards/e/EssenceSymbiote.java b/Mage.Sets/src/mage/cards/e/EssenceSymbiote.java new file mode 100644 index 0000000000..6dc3cd672b --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EssenceSymbiote.java @@ -0,0 +1,118 @@ +package mage.cards.e; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.keyword.MutateAbility; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.abilities.Ability; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author gp66 + */ +public final class EssenceSymbiote extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("creature you control mutates"); + + static { + filter.add(new AbilityPredicate(MutateAbility.class)); + } + + public EssenceSymbiote(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever a creature you control mutates, put a +1/+1 counter on that creature and you gain 2 life. + Ability ability = new EssenceSymbioteTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersTargetEffect(CounterType.P1P1.createInstance()).setText("put a +1/+1 counter on that creature"), + filter); + // You gain 2 life when Essence Symbiote’s ability resolves, even if you can’t put a +1/+1 counter on the mutated creature + ability.addEffect(new GainLifeEffect(2).concatBy("and")); + this.addAbility(ability); + } + + private EssenceSymbiote(final EssenceSymbiote card) { + super(card); + } + + @Override + public EssenceSymbiote copy() { + return new EssenceSymbiote(this); + } +} + +class EssenceSymbioteTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterPermanent filter; + + public EssenceSymbioteTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter) { + super(zone, effect, false); + this.filter = filter; + } + + public EssenceSymbioteTriggeredAbility(final mage.cards.e.EssenceSymbioteTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + } + + @Override + public mage.cards.e.EssenceSymbioteTriggeredAbility copy() { + return new mage.cards.e.EssenceSymbioteTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + // TODO: Implement this + //return event.getType() == GameEvent.EventType.CREATURE_MUTATED; + return false; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + // TODO: Implement this + /* + Permanent sourcePermanent = game.getPermanent(event.getSourceId()); + Permanent targetPermanent = game.getPermanent(event.getTargetId()); + if (sourcePermanent != null && targetPermanent != null) { + Player controller = game.getPlayer(targetPermanent.getControllerId()); + if (controller != null + && event.getTargetId().equals(targetPermanent.getId()) + && controller.getId().equals(sourcePermanent.getControllerId()) + && this.isControlledBy(controller.getId())) { + for (Effect effect : this.getEffects()) { + effect.setValue("targetId", targetPermanent.getId()); + effect.setTargetPointer(new FixedTarget(targetPermanent.getId(), targetPermanent.getZoneChangeCounter(game))); + } + return true; + } + } + */ + return false; + } + + @Override + public String getRule() { + return "Whenever a creature you control mutates, " + super.getRule(); + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/EstridsInvocation.java b/Mage.Sets/src/mage/cards/e/EstridsInvocation.java index 079f53e723..37406898aa 100644 --- a/Mage.Sets/src/mage/cards/e/EstridsInvocation.java +++ b/Mage.Sets/src/mage/cards/e/EstridsInvocation.java @@ -34,7 +34,7 @@ public final class EstridsInvocation extends CardImpl { // You may have Estrid's Invocation enter the battlefield as a copy of any enchantment you control, except it gains "At the beginning of your upkeep, you may exile this enchantment. If you do, return it to the battlefield under its owner's control." this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect( filter, new EstridsInvocationApplier() - ).setText("as a copy of any enchantment you control, except it gains " + ).setText("as a copy of an enchantment you control, except it gains " + "\"At the beginning of your upkeep, " + "you may exile this enchantment. " + "If you do, return it to the battlefield " diff --git a/Mage.Sets/src/mage/cards/e/EtchedChampion.java b/Mage.Sets/src/mage/cards/e/EtchedChampion.java index a4fd2efd09..11aa9777da 100644 --- a/Mage.Sets/src/mage/cards/e/EtchedChampion.java +++ b/Mage.Sets/src/mage/cards/e/EtchedChampion.java @@ -1,7 +1,5 @@ - package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; @@ -9,19 +7,18 @@ import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author North */ public final class EtchedChampion extends CardImpl { @@ -39,14 +36,19 @@ public final class EtchedChampion extends CardImpl { } public EtchedChampion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); this.subtype.add(SubType.SOLDIER); this.power = new MageInt(2); this.toughness = new MageInt(2); + // Metalcraft — Etched Champion has protection from all colors as long as you control three or more artifacts. ContinuousEffect effect = new GainAbilitySourceEffect(new ProtectionAbility(filter), Duration.WhileOnBattlefield); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect, MetalcraftCondition.instance, ruleText))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(effect, MetalcraftCondition.instance, ruleText)) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); } public EtchedChampion(final EtchedChampion card) { diff --git a/Mage.Sets/src/mage/cards/e/EtherealForager.java b/Mage.Sets/src/mage/cards/e/EtherealForager.java new file mode 100644 index 0000000000..d39ba59c8c --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EtherealForager.java @@ -0,0 +1,96 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.DelveAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInExile; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EtherealForager extends CardImpl { + + public EtherealForager(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.WHALE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Delve + this.addAbility(new DelveAbility()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Ethereal Forager attacks, you may return an instant or sorcery card exiled with Ethereal Forager to its owner's hand. + this.addAbility(new AttacksTriggeredAbility(new EtherealForagerEffect(), true)); + } + + private EtherealForager(final EtherealForager card) { + super(card); + } + + @Override + public EtherealForager copy() { + return new EtherealForager(this); + } +} + +class EtherealForagerEffect extends OneShotEffect { + + EtherealForagerEffect() { + super(Outcome.Benefit); + staticText = "return an instant or sorcery card exiled with {this} to its owner's hand"; + } + + private EtherealForagerEffect(final EtherealForagerEffect effect) { + super(effect); + } + + @Override + public EtherealForagerEffect copy() { + return new EtherealForagerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + String keyString = CardUtil.getCardZoneString("delvedCards", source.getSourceId(), game, true); + Cards delvedCards = (Cards) game.getState().getValue(keyString); + if (delvedCards == null || delvedCards.count(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY, game) < 1) { + return false; + } + TargetCard targetCard = new TargetCardInExile(0, 1, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, null, true); + ; + player.choose(Outcome.DrawCard, delvedCards, targetCard, game); + Card card = game.getCard(targetCard.getFirstTarget()); + if (card == null || !player.moveCards(card, Zone.HAND, source, game)) { + return false; + } + delvedCards.remove(card); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EverAfter.java b/Mage.Sets/src/mage/cards/e/EverAfter.java index 31b8dedeea..272d0aeda7 100644 --- a/Mage.Sets/src/mage/cards/e/EverAfter.java +++ b/Mage.Sets/src/mage/cards/e/EverAfter.java @@ -23,7 +23,7 @@ public final class EverAfter extends CardImpl { // Return up to two target creature cards from your graveyard to the battlefield. Each of those creatures is a black Zombie in addition // to its other colors and types. Put Ever After on the bottom of its owner's library. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); Effect effect = new BecomesBlackZombieAdditionEffect(); effect.setText("Each of those creatures is a black Zombie in addition to its other colors and types"); diff --git a/Mage.Sets/src/mage/cards/e/EverquillPhoenix.java b/Mage.Sets/src/mage/cards/e/EverquillPhoenix.java new file mode 100644 index 0000000000..7e5cc20e5d --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EverquillPhoenix.java @@ -0,0 +1,46 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.FeatherToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EverquillPhoenix extends CardImpl { + + public EverquillPhoenix(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.PHOENIX); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Mutate {3}{R} + this.addAbility(new MutateAbility(this, "{3}{R}")); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever this creature mutates, create a red artifact token named Feather with "{1}, Sacrifice Feather: Return target Phoenix card from your graveyard to the battlefield tapped." + this.addAbility(new MutatesSourceTriggeredAbility(new CreateTokenEffect(new FeatherToken()))); + } + + private EverquillPhoenix(final EverquillPhoenix card) { + super(card); + } + + @Override + public EverquillPhoenix copy() { + return new EverquillPhoenix(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EverythingamajigE.java b/Mage.Sets/src/mage/cards/e/EverythingamajigE.java index bad33f9ba5..6edc1f5d7e 100644 --- a/Mage.Sets/src/mage/cards/e/EverythingamajigE.java +++ b/Mage.Sets/src/mage/cards/e/EverythingamajigE.java @@ -11,9 +11,11 @@ import mage.abilities.costs.Cost; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.Card; import mage.cards.CardImpl; @@ -47,9 +49,11 @@ public final class EverythingamajigE extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(2), new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_LAND_SHORT_TEXT)))); // Ashnod's Altar - // Sacrifice a creature: Add CC to your mana pool. + // Sacrifice a creature: Add {C}{C} to your mana pool. SacrificeTargetCost cost = new SacrificeTargetCost(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT)); - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(2), cost)); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, + new BasicManaEffect(Mana.ColorlessMana(2), CreaturesYouControlCount.instance), + cost)); // Urza's Hot Tub // 2, Discard a card: Search your library for a card that shares a complete word in its name with the name of the discarded card, reveal it, put it into your hand, then shuffle your library. diff --git a/Mage.Sets/src/mage/cards/e/EvilTwin.java b/Mage.Sets/src/mage/cards/e/EvilTwin.java index cda27f37ff..f08e6c3402 100644 --- a/Mage.Sets/src/mage/cards/e/EvilTwin.java +++ b/Mage.Sets/src/mage/cards/e/EvilTwin.java @@ -69,7 +69,7 @@ class EvilTwinApplyToPermanent extends ApplyToPermanent { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{U}{B}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(filter)); - permanent.addAbility(ability, game); + permanent.addAbility(ability, source.getSourceId(), game); return true; } diff --git a/Mage.Sets/src/mage/cards/e/EvolutionCharm.java b/Mage.Sets/src/mage/cards/e/EvolutionCharm.java index d552e8ff70..b8ed70aac1 100644 --- a/Mage.Sets/src/mage/cards/e/EvolutionCharm.java +++ b/Mage.Sets/src/mage/cards/e/EvolutionCharm.java @@ -1,8 +1,7 @@ - package mage.cards.e; import mage.abilities.Mode; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.abilities.keyword.FlyingAbility; @@ -18,7 +17,6 @@ import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * * @author fireshoes */ public final class EvolutionCharm extends CardImpl { @@ -27,11 +25,11 @@ public final class EvolutionCharm extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Choose one - Search your library for a basic land card, reveal it, put it into your hand, then shuffle your library; - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_BASIC_LAND), true, true)); + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_BASIC_LAND), true, true).setText("Search your library for a basic land card, reveal it, put it into your hand, then shuffle your library")); // or return target creature card from your graveyard to your hand; Mode mode = new Mode(); - mode.addEffect(new ReturnToHandTargetEffect()); + mode.addEffect(new ReturnFromGraveyardToHandTargetEffect()); mode.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); this.getSpellAbility().addMode(mode); diff --git a/Mage.Sets/src/mage/cards/e/Excavation.java b/Mage.Sets/src/mage/cards/e/Excavation.java index 5bf86b2e01..4f140eaa1f 100644 --- a/Mage.Sets/src/mage/cards/e/Excavation.java +++ b/Mage.Sets/src/mage/cards/e/Excavation.java @@ -67,7 +67,7 @@ class ExcavationEffect extends OneShotEffect { if (source instanceof ActivatedAbilityImpl) { Player activator = game.getPlayer(((ActivatedAbilityImpl) source).getActivatorId()); if (activator != null) { - activator.drawCards(1, game); + activator.drawCards(1, source.getSourceId(), game); return true; } diff --git a/Mage.Sets/src/mage/cards/e/ExcavationMole.java b/Mage.Sets/src/mage/cards/e/ExcavationMole.java new file mode 100644 index 0000000000..05e75b59db --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExcavationMole.java @@ -0,0 +1,43 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExcavationMole extends CardImpl { + + public ExcavationMole(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.MOLE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Excavation Mole enters the battlefield, put the top three cards of your library into your graveyard. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new PutTopCardOfLibraryIntoGraveControllerEffect(3) + )); + } + + private ExcavationMole(final ExcavationMole card) { + super(card); + } + + @Override + public ExcavationMole copy() { + return new ExcavationMole(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/Excavator.java b/Mage.Sets/src/mage/cards/e/Excavator.java index 827f0d2c21..9516f6212c 100644 --- a/Mage.Sets/src/mage/cards/e/Excavator.java +++ b/Mage.Sets/src/mage/cards/e/Excavator.java @@ -112,7 +112,7 @@ class ExcavatorEffect extends ContinuousEffectImpl implements SourceEffect { if (permanent != null) { for(Ability ability : abilities) { - permanent.addAbility(ability, source.getSourceId(), game, false); + permanent.addAbility(ability, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/e/ExiledBoggart.java b/Mage.Sets/src/mage/cards/e/ExiledBoggart.java index 3d1703ba0d..d78998ea27 100644 --- a/Mage.Sets/src/mage/cards/e/ExiledBoggart.java +++ b/Mage.Sets/src/mage/cards/e/ExiledBoggart.java @@ -3,7 +3,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.discard.DiscardControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class ExiledBoggart extends CardImpl { this.toughness = new MageInt(2); // When Exiled Boggart dies, discard a card. - this.addAbility(new DiesTriggeredAbility(new DiscardControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DiscardControllerEffect(1), false)); } public ExiledBoggart(final ExiledBoggart card) { diff --git a/Mage.Sets/src/mage/cards/e/ExpansionExplosion.java b/Mage.Sets/src/mage/cards/e/ExpansionExplosion.java index 0b2039c094..e59b36cf07 100644 --- a/Mage.Sets/src/mage/cards/e/ExpansionExplosion.java +++ b/Mage.Sets/src/mage/cards/e/ExpansionExplosion.java @@ -1,6 +1,5 @@ package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; @@ -23,13 +22,15 @@ import mage.target.TargetSpell; import mage.target.common.TargetAnyTarget; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ExpansionExplosion extends SplitCard { - private static final FilterSpell filter = new FilterInstantOrSorcerySpell("instant or sorcery spell with converted mana cost 4 or less"); + private static final FilterSpell filter + = new FilterInstantOrSorcerySpell("instant or sorcery spell with converted mana cost 4 or less"); static { filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 5)); @@ -46,11 +47,11 @@ public final class ExpansionExplosion extends SplitCard { // Explosion // Explosion deals X damage to any target. Target player draws X cards. this.getRightHalfCard().getSpellAbility().addEffect(new ExplosionEffect()); - this.getRightHalfCard().getSpellAbility().addTarget(new TargetAnyTarget()); - this.getRightHalfCard().getSpellAbility().addTarget(new TargetPlayer()); + this.getRightHalfCard().getSpellAbility().addTarget(new TargetAnyTarget().withChooseHint("To deal damage")); + this.getRightHalfCard().getSpellAbility().addTarget(new TargetPlayer().withChooseHint("To draw cards")); } - public ExpansionExplosion(final ExpansionExplosion card) { + private ExpansionExplosion(final ExpansionExplosion card) { super(card); } @@ -62,13 +63,13 @@ public final class ExpansionExplosion extends SplitCard { class ExplosionEffect extends OneShotEffect { - public ExplosionEffect() { + ExplosionEffect() { super(Outcome.Benefit); this.staticText = "{this} deals X damage to any target. " + "Target player draws X cards."; } - public ExplosionEffect(final ExplosionEffect effect) { + private ExplosionEffect(final ExplosionEffect effect) { super(effect); } @@ -85,7 +86,7 @@ class ExplosionEffect extends OneShotEffect { effect.apply(game, source); Player player = game.getPlayer(source.getTargets().get(1).getFirstTarget()); if (player != null) { - player.drawCards(xValue, game); + player.drawCards(xValue, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/e/ExperimentalOverload.java b/Mage.Sets/src/mage/cards/e/ExperimentalOverload.java new file mode 100644 index 0000000000..ae7d41f907 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExperimentalOverload.java @@ -0,0 +1,78 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.WeirdToken2; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExperimentalOverload extends CardImpl { + + public ExperimentalOverload(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{R}"); + + // Create an X/X blue and red Weird creature token, where X is the number of instant and sorcery cards in your graveyard. Then you may return an instant or sorcery card from your graveyard to your hand. Exile Experimental Overload. + this.getSpellAbility().addEffect(new ExperimentalOverloadEffect()); + this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + } + + private ExperimentalOverload(final ExperimentalOverload card) { + super(card); + } + + @Override + public ExperimentalOverload copy() { + return new ExperimentalOverload(this); + } +} + +class ExperimentalOverloadEffect extends OneShotEffect { + + ExperimentalOverloadEffect() { + super(Outcome.Benefit); + staticText = "Create an X/X blue and red Weird creature token, " + + "where X is the number of instant and sorcery cards in your graveyard. " + + "Then you may return an instant or sorcery card from your graveyard to your hand."; + } + + private ExperimentalOverloadEffect(final ExperimentalOverloadEffect effect) { + super(effect); + } + + @Override + public ExperimentalOverloadEffect copy() { + return new ExperimentalOverloadEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int spellCount = player.getGraveyard().count(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game); + new WeirdToken2(spellCount).putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + if (spellCount < 1) { + return true; + } + TargetCard target = new TargetCardInYourGraveyard( + 0, 1, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, true + ); + player.choose(outcome, player.getGraveyard(), target, game); + return player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/ExtinctionEvent.java b/Mage.Sets/src/mage/cards/e/ExtinctionEvent.java new file mode 100644 index 0000000000..ac43a229a1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExtinctionEvent.java @@ -0,0 +1,81 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ConvertedManaCostParityPredicate; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class ExtinctionEvent extends CardImpl { + + public ExtinctionEvent(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); + + // Choose odd or even. Exile each creature with converted mana cost of the chosen value. + this.getSpellAbility().addEffect(new ExtinctionEventEffect()); + } + + private ExtinctionEvent(final ExtinctionEvent card) { + super(card); + } + + @Override + public ExtinctionEvent copy() { + return new ExtinctionEvent(this); + } +} + +class ExtinctionEventEffect extends OneShotEffect { + + private static final FilterPermanent evenFilter = new FilterCreaturePermanent(); + private static final FilterPermanent oddFilter = new FilterCreaturePermanent(); + + static { + evenFilter.add(ConvertedManaCostParityPredicate.EVEN); + oddFilter.add(ConvertedManaCostParityPredicate.ODD); + } + + ExtinctionEventEffect() { + super(Outcome.Benefit); + staticText = "Choose odd or even. Exile each creature with converted mana cost of the chosen value. (Zero is even.)"; + } + + private ExtinctionEventEffect(final ExtinctionEventEffect effect) { + super(effect); + } + + @Override + public ExtinctionEventEffect copy() { + return new ExtinctionEventEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + FilterPermanent filter = player.chooseUse( + outcome, "Odd or even?", null, + "Odd", "Even", source, game + ) ? oddFilter : evenFilter; + return player.moveCards( + game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + ).stream().collect(Collectors.toSet()), Zone.EXILED, source, game + ); + } +} diff --git a/Mage.Sets/src/mage/cards/e/Extortion.java b/Mage.Sets/src/mage/cards/e/Extortion.java index 46d5163ae0..86c210dc31 100644 --- a/Mage.Sets/src/mage/cards/e/Extortion.java +++ b/Mage.Sets/src/mage/cards/e/Extortion.java @@ -1,19 +1,18 @@ - package mage.cards.e; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPlayer; +import mage.target.common.TargetCardInHand; import java.util.UUID; @@ -30,7 +29,7 @@ public final class Extortion extends CardImpl { this.getSpellAbility().addTarget(new TargetPlayer()); } - public Extortion(final Extortion card) { + private Extortion(final Extortion card) { super(card); } @@ -42,12 +41,12 @@ public final class Extortion extends CardImpl { class ExtortionEffect extends OneShotEffect { - public ExtortionEffect() { + ExtortionEffect() { super(Outcome.Discard); - staticText = "Look at target player's hand and choose up to two cards from it. That player discards that card."; + staticText = "Look at target player's hand and choose up to two cards from it. That player discards those cards."; } - public ExtortionEffect(final ExtortionEffect effect) { + private ExtortionEffect(final ExtortionEffect effect) { super(effect); } @@ -55,18 +54,15 @@ class ExtortionEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(source.getFirstTarget()); Player you = game.getPlayer(source.getControllerId()); - if (targetPlayer != null && you != null) { - you.lookAtCards("Discard", targetPlayer.getHand(), game); - TargetCard target = new TargetCard(0, 2, Zone.HAND, new FilterCard()); - target.setNotTarget(true); - if (you.choose(Outcome.Benefit, targetPlayer.getHand(), target, game)) { - Card card = targetPlayer.getHand().get(target.getFirstTarget(), game); - return targetPlayer.discard(card, source, game); - - } - + if (targetPlayer == null || you == null) { + return false; } - return false; + you.lookAtCards("Discard", targetPlayer.getHand(), game); + TargetCard target = new TargetCardInHand(0, 2, StaticFilters.FILTER_CARD_CARDS); + target.setNotTarget(true); + you.choose(Outcome.Discard, targetPlayer.getHand(), target, game); + targetPlayer.discard(new CardsImpl(target.getTargets()), source, game); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/e/ExtractFromDarkness.java b/Mage.Sets/src/mage/cards/e/ExtractFromDarkness.java index 60fa0cc682..92e77183b5 100644 --- a/Mage.Sets/src/mage/cards/e/ExtractFromDarkness.java +++ b/Mage.Sets/src/mage/cards/e/ExtractFromDarkness.java @@ -1,12 +1,13 @@ package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; @@ -14,8 +15,9 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInGraveyard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class ExtractFromDarkness extends CardImpl { @@ -24,12 +26,13 @@ public final class ExtractFromDarkness extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}{B}"); // Each player puts the top two cards of their library into their graveyard. - this.getSpellAbility().addEffect(new ExtractFromDarknessMillEffect()); + this.getSpellAbility().addEffect(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(2, TargetController.ANY)); + // Then put a creature card from a graveyard onto the battlefield under your control. - this.getSpellAbility().addEffect(new ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect()); + this.getSpellAbility().addEffect(new ExtractFromDarknessEffect()); } - public ExtractFromDarkness(final ExtractFromDarkness card) { + private ExtractFromDarkness(final ExtractFromDarkness card) { super(card); } @@ -39,48 +42,20 @@ public final class ExtractFromDarkness extends CardImpl { } } -class ExtractFromDarknessMillEffect extends OneShotEffect { +class ExtractFromDarknessEffect extends OneShotEffect { - ExtractFromDarknessMillEffect() { - super(Outcome.Detriment); - staticText = "Each player puts the top two cards of their library into their graveyard"; - } - - ExtractFromDarknessMillEffect(final ExtractFromDarknessMillEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.moveCards(player.getLibrary().getTopCards(game, 2), Zone.GRAVEYARD, source, game); - } - } - return true; - } - - @Override - public ExtractFromDarknessMillEffect copy() { - return new ExtractFromDarknessMillEffect(this); - } -} - -class ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect extends OneShotEffect { - - public ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect() { + ExtractFromDarknessEffect() { super(Outcome.PutCreatureInPlay); staticText = "Put a creature card from a graveyard onto the battlefield under your control"; } - public ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect(final ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect effect) { + private ExtractFromDarknessEffect(final ExtractFromDarknessEffect effect) { super(effect); } @Override - public ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect copy() { - return new ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect(this); + public ExtractFromDarknessEffect copy() { + return new ExtractFromDarknessEffect(this); } @Override diff --git a/Mage.Sets/src/mage/cards/e/ExuberantWolfbear.java b/Mage.Sets/src/mage/cards/e/ExuberantWolfbear.java new file mode 100644 index 0000000000..7b2432fd80 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExuberantWolfbear.java @@ -0,0 +1,84 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExuberantWolfbear extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.HUMAN, "Human you control"); + + public ExuberantWolfbear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.WOLF); + this.subtype.add(SubType.BEAR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever Exuberant Wolfbear attacks, you may change the base power and toughness of target Human you control to Exuberant Wolfbear's power and toughness until end of turn. + Ability ability = new AttacksTriggeredAbility(new ExuberantWolfbearEffect(), true); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private ExuberantWolfbear(final ExuberantWolfbear card) { + super(card); + } + + @Override + public ExuberantWolfbear copy() { + return new ExuberantWolfbear(this); + } +} + +class ExuberantWolfbearEffect extends OneShotEffect { + + ExuberantWolfbearEffect() { + super(Outcome.Benefit); + staticText = "change the base power and toughness of target Human you control " + + "to {this}'s power and toughness until end of turn"; + } + + private ExuberantWolfbearEffect(final ExuberantWolfbearEffect effect) { + super(effect); + } + + @Override + public ExuberantWolfbearEffect copy() { + return new ExuberantWolfbearEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (permanent == null) { + return false; + } + game.addEffect(new SetPowerToughnessTargetEffect( + permanent.getPower().getValue(), + permanent.getToughness().getValue(), + Duration.EndOfTurn + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExultantCultist.java b/Mage.Sets/src/mage/cards/e/ExultantCultist.java index 5011b0c40e..babb9bf196 100644 --- a/Mage.Sets/src/mage/cards/e/ExultantCultist.java +++ b/Mage.Sets/src/mage/cards/e/ExultantCultist.java @@ -3,7 +3,7 @@ package mage.cards.e; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class ExultantCultist extends CardImpl { this.toughness = new MageInt(2); // When Exultant Cultist dies, draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } public ExultantCultist(final ExultantCultist card) { diff --git a/Mage.Sets/src/mage/cards/e/EyeCollector.java b/Mage.Sets/src/mage/cards/e/EyeCollector.java index 708c9bec08..757aa98757 100644 --- a/Mage.Sets/src/mage/cards/e/EyeCollector.java +++ b/Mage.Sets/src/mage/cards/e/EyeCollector.java @@ -1,22 +1,16 @@ package mage.cards.e; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; +import mage.constants.TargetController; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 @@ -34,7 +28,7 @@ public final class EyeCollector extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Eye Collector deals combat damage to a player, each player puts the top card of their library into their graveyard. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new EyeCollectorEffect(), false)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(1, TargetController.ANY), false)); } private EyeCollector(final EyeCollector card) { @@ -46,37 +40,3 @@ public final class EyeCollector extends CardImpl { return new EyeCollector(this); } } - -class EyeCollectorEffect extends OneShotEffect { - - EyeCollectorEffect() { - super(Outcome.Benefit); - staticText = "each player puts the top card of their library into their graveyard"; - } - - private EyeCollectorEffect(final EyeCollectorEffect effect) { - super(effect); - } - - @Override - public EyeCollectorEffect copy() { - return new EyeCollectorEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - return controller.moveCards(new CardsImpl(game.getState() - .getPlayersInRange(controller.getId(), game) - .stream() - .map(game::getPlayer) - .filter(player -> player != null) - .map(Player::getLibrary) - .map(library -> library.getFromTop(game)) - .collect(Collectors.toSet()) - ), Zone.GRAVEYARD, source, game); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/EyeOfRamos.java b/Mage.Sets/src/mage/cards/e/EyeOfRamos.java index 54a2e54f42..e0f56cada8 100644 --- a/Mage.Sets/src/mage/cards/e/EyeOfRamos.java +++ b/Mage.Sets/src/mage/cards/e/EyeOfRamos.java @@ -1,4 +1,3 @@ - package mage.cards.e; import java.util.UUID; @@ -18,11 +17,11 @@ import mage.constants.Zone; public final class EyeOfRamos extends CardImpl { public EyeOfRamos(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - // {tap}: Add {U}. + // {T}: Add {U}. this.addAbility(new BlueManaAbility()); - + // Sacrifice Eye of Ramos: Add {U}. this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlueMana(1), new SacrificeSourceCost())); } diff --git a/Mage.Sets/src/mage/cards/e/EyeOfSingularity.java b/Mage.Sets/src/mage/cards/e/EyeOfSingularity.java index c3ba613992..160ee560df 100644 --- a/Mage.Sets/src/mage/cards/e/EyeOfSingularity.java +++ b/Mage.Sets/src/mage/cards/e/EyeOfSingularity.java @@ -159,7 +159,7 @@ class EyeOfSingularityTriggeredEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Map toDestroy = new HashMap<>(); - Permanent etbPermanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent etbPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (etbPermanent == null) { return false; diff --git a/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java b/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java index 563fb12b5c..86e8ba47bf 100644 --- a/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java +++ b/Mage.Sets/src/mage/cards/e/EyeOfTheStorm.java @@ -137,7 +137,7 @@ class EyeOfTheStormEffect1 extends OneShotEffect { } boolean continueCasting = true; - while (spellController.isInGame() && continueCasting) { + while (spellController.canRespond() && continueCasting) { continueCasting = copiedCards.size() > 1 && spellController.chooseUse(outcome, "Cast one of the copied cards without paying its mana cost?", source, game); Card cardToCopy; diff --git a/Mage.Sets/src/mage/cards/e/EyeOfUgin.java b/Mage.Sets/src/mage/cards/e/EyeOfUgin.java index 5aa63ff887..d2aec862fd 100644 --- a/Mage.Sets/src/mage/cards/e/EyeOfUgin.java +++ b/Mage.Sets/src/mage/cards/e/EyeOfUgin.java @@ -1,8 +1,5 @@ - - package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -21,6 +18,8 @@ import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ColorlessPredicate; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** * @author maurer.it_at_gmail.com */ @@ -41,6 +40,7 @@ public final class EyeOfUgin extends CardImpl { // Colorless Eldrazi spells you cast cost {2} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filterSpells, 2))); + // {7}, {tap}: Search your library for a colorless creature card, reveal it, and put it into your hand. Then shuffle your library. Ability searchAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true), diff --git a/Mage.Sets/src/mage/cards/e/EzurisBrigade.java b/Mage.Sets/src/mage/cards/e/EzurisBrigade.java index 28bd5cf374..3d130cd6e2 100644 --- a/Mage.Sets/src/mage/cards/e/EzurisBrigade.java +++ b/Mage.Sets/src/mage/cards/e/EzurisBrigade.java @@ -1,8 +1,5 @@ - - package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -11,36 +8,38 @@ import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; + +import java.util.UUID; /** - * * @author Loki */ public final class EzurisBrigade extends CardImpl { private static final String rule = "Metalcraft — As long as you control three or more artifacts, Ezuri's Brigade gets +4/+4 and has trample"; - public EzurisBrigade (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}"); + public EzurisBrigade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.WARRIOR); - this.power = new MageInt(4); this.toughness = new MageInt(4); + + // Metalcraft — As long as you control three or more artifacts, Ezuri’s Brigade gets +4/+4 and has trample. ContinuousEffect boostSource = new BoostSourceEffect(4, 4, Duration.WhileOnBattlefield); ConditionalContinuousEffect effect = new ConditionalContinuousEffect(boostSource, MetalcraftCondition.instance, rule); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); ability.addEffect(new ConditionalContinuousEffect(new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield), MetalcraftCondition.instance, "")); + ability.setAbilityWord(AbilityWord.METALCRAFT); + ability.addHint(MetalcraftHint.instance); this.addAbility(ability); } - public EzurisBrigade (final EzurisBrigade card) { + public EzurisBrigade(final EzurisBrigade card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/e/EzurisPredation.java b/Mage.Sets/src/mage/cards/e/EzurisPredation.java index ef1c18b4c1..12f004e6fa 100644 --- a/Mage.Sets/src/mage/cards/e/EzurisPredation.java +++ b/Mage.Sets/src/mage/cards/e/EzurisPredation.java @@ -1,8 +1,7 @@ package mage.cards.e; -import java.util.List; -import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -13,18 +12,23 @@ import mage.constants.Outcome; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; +import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.BeastToken2; import mage.players.Player; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class EzurisPredation extends CardImpl { public EzurisPredation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{G}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{G}{G}{G}"); // For each creature your opponents control, create a 4/4 green Beast creature token. Each of those Beasts fights a different one of those creatures. this.getSpellAbility().addEffect(new EzurisPredationEffect()); @@ -75,6 +79,7 @@ class EzurisPredationEffect extends OneShotEffect { FilterCreaturePermanent filterCreature = new FilterCreaturePermanent(); filterCreature.add(TargetController.OPPONENT.getControllerPredicate()); List creaturesOfOpponents = game.getBattlefield().getActivePermanents(filterCreature, source.getControllerId(), source.getSourceId(), game); + Set morSet = new HashSet<>(); if (!creaturesOfOpponents.isEmpty()) { CreateTokenEffect effect = new CreateTokenEffect(new BeastToken2(), creaturesOfOpponents.size()); effect.apply(game, source); @@ -86,10 +91,15 @@ class EzurisPredationEffect extends OneShotEffect { } Permanent opponentCreature = creaturesOfOpponents.iterator().next(); creaturesOfOpponents.remove(opponentCreature); - token.fight(opponentCreature, source, game); + token.fight(opponentCreature, source, game, false); + morSet.add(new MageObjectReference(token, game)); + morSet.add(new MageObjectReference(opponentCreature, game)); game.informPlayers(token.getLogName() + " fights " + opponentCreature.getLogName()); } } + String data = UUID.randomUUID().toString(); + game.getState().setValue("batchFight_" + data, morSet); + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BATCH_FIGHT, getId(), getId(), source.getControllerId(), data, 0)); } return true; } diff --git a/Mage.Sets/src/mage/cards/f/FaadiyahSeer.java b/Mage.Sets/src/mage/cards/f/FaadiyahSeer.java index 3b917169e5..d85c5ef6a5 100644 --- a/Mage.Sets/src/mage/cards/f/FaadiyahSeer.java +++ b/Mage.Sets/src/mage/cards/f/FaadiyahSeer.java @@ -71,7 +71,7 @@ class FaadiyahSeerEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Card card = controller.getLibrary().getFromTop(game); - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); controller.revealCards("Fa'adiyah Seer", new CardsImpl(card), game); if (!filter.match(card, game)) { controller.discard(card, source, game); diff --git a/Mage.Sets/src/mage/cards/f/FacetReader.java b/Mage.Sets/src/mage/cards/f/FacetReader.java new file mode 100644 index 0000000000..70fc36c178 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FacetReader.java @@ -0,0 +1,45 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FacetReader extends CardImpl { + + public FacetReader(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {1}, {T}: Draw a card, then discard a card. + Ability ability = new SimpleActivatedAbility( + new DrawDiscardControllerEffect(1, 1), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private FacetReader(final FacetReader card) { + super(card); + } + + @Override + public FacetReader copy() { + return new FacetReader(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FaerieArtisans.java b/Mage.Sets/src/mage/cards/f/FaerieArtisans.java index 8eb37e5af9..c9924321fb 100644 --- a/Mage.Sets/src/mage/cards/f/FaerieArtisans.java +++ b/Mage.Sets/src/mage/cards/f/FaerieArtisans.java @@ -1,5 +1,7 @@ package mage.cards.f; +import java.util.StringTokenizer; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; @@ -19,9 +21,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; -import java.util.StringTokenizer; -import java.util.UUID; - /** * @author LevelX2 */ @@ -80,7 +79,7 @@ class FaerieArtisansEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanentToCopy = game.getPermanentOrLKIBattlefield(targetPointer.getFixedTarget(game, source).getTarget()); + Permanent permanentToCopy = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && permanentToCopy != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.ARTIFACT, false); diff --git a/Mage.Sets/src/mage/cards/f/FalconerAdept.java b/Mage.Sets/src/mage/cards/f/FalconerAdept.java new file mode 100644 index 0000000000..c9f342a026 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FalconerAdept.java @@ -0,0 +1,41 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.BirdToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FalconerAdept extends CardImpl { + + public FalconerAdept(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever Falconer Adept attacks, create a 1/1 white Bird creature token with flying that's tapped and attacking. + this.addAbility(new AttacksTriggeredAbility( + new CreateTokenEffect(new BirdToken(), 1, true, true), false + )); + } + + private FalconerAdept(final FalconerAdept card) { + super(card); + } + + @Override + public FalconerAdept copy() { + return new FalconerAdept(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FallenShinobi.java b/Mage.Sets/src/mage/cards/f/FallenShinobi.java index bc6cf45403..6405ab0686 100644 --- a/Mage.Sets/src/mage/cards/f/FallenShinobi.java +++ b/Mage.Sets/src/mage/cards/f/FallenShinobi.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.NinjutsuAbility; @@ -12,9 +11,10 @@ import mage.cards.*; import mage.constants.*; import mage.game.Game; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.target.targetpointer.FixedTargets; /** * @author TheElk801 @@ -52,8 +52,8 @@ class FallenShinobiEffect extends OneShotEffect { FallenShinobiEffect() { super(Outcome.Benefit); - staticText = "that player exiles the top two cards of their library. " + - "Until end of turn, you may play those cards without paying their mana cost."; + staticText = "that player exiles the top two cards of their library. " + + "Until end of turn, you may play those cards without paying their mana cost."; } private FallenShinobiEffect(final FallenShinobiEffect effect) { @@ -71,53 +71,7 @@ class FallenShinobiEffect extends OneShotEffect { if (player == null) { return false; } - Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 2)); - player.moveCards(cards, Zone.EXILED, source, game); - for (Card card : cards.getCards(game)) { - ContinuousEffect effect = new UrzaLordHighArtificerFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card, game)); - game.addEffect(effect, source); - } - return true; - } -} - -class UrzaLordHighArtificerFromExileEffect extends AsThoughEffectImpl { - - UrzaLordHighArtificerFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "you may play that card without paying its mana cost"; - } - - private UrzaLordHighArtificerFromExileEffect(final UrzaLordHighArtificerFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public UrzaLordHighArtificerFromExileEffect copy() { - return new UrzaLordHighArtificerFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!affectedControllerId.equals(source.getControllerId()) - || !getTargetPointer().getTargets(game, source).contains(objectId)) { - return false; - } - Card card = game.getCard(objectId); - if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) { - return true; - } - Player player = game.getPlayer(affectedControllerId); - if (player == null) { - return false; - } - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, player.getLibrary().getTopCards(game, 2), + TargetController.YOU, Duration.EndOfTurn, true); } } diff --git a/Mage.Sets/src/mage/cards/f/FalseOrders.java b/Mage.Sets/src/mage/cards/f/FalseOrders.java index 28d324d5f9..c1658256f7 100644 --- a/Mage.Sets/src/mage/cards/f/FalseOrders.java +++ b/Mage.Sets/src/mage/cards/f/FalseOrders.java @@ -1,10 +1,6 @@ - package mage.cards.f; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; import mage.abilities.effects.Effect; @@ -30,8 +26,9 @@ import mage.target.TargetPermanent; import mage.target.common.TargetAttackingCreature; import mage.watchers.common.BlockedByOnlyOneCreatureThisCombatWatcher; +import java.util.*; + /** - * * @author L_J */ public final class FalseOrders extends CardImpl { @@ -40,7 +37,7 @@ public final class FalseOrders extends CardImpl { static { filter.add(CardType.CREATURE.getPredicate()); - filter.add(new FalseOrdersDefendingPlayerControlsPredicate()); + filter.add(FalseOrdersDefendingPlayerControlsPredicate.instance); } public FalseOrders(UUID ownerId, CardSetInfo setInfo) { @@ -55,7 +52,7 @@ public final class FalseOrders extends CardImpl { this.getSpellAbility().addWatcher(new BlockedByOnlyOneCreatureThisCombatWatcher()); } - public FalseOrders(final FalseOrders card) { + private FalseOrders(final FalseOrders card) { super(card); } @@ -66,7 +63,8 @@ public final class FalseOrders extends CardImpl { } -class FalseOrdersDefendingPlayerControlsPredicate implements ObjectPlayerPredicate> { +enum FalseOrdersDefendingPlayerControlsPredicate implements ObjectPlayerPredicate> { + instance; @Override public boolean apply(ObjectPlayer input, Game game) { @@ -76,12 +74,12 @@ class FalseOrdersDefendingPlayerControlsPredicate implements ObjectPlayerPredica class FalseOrdersUnblockEffect extends OneShotEffect { - public FalseOrdersUnblockEffect() { + FalseOrdersUnblockEffect() { super(Outcome.Benefit); this.staticText = "Remove target creature defending player controls from combat. Creatures it was blocking that had become blocked by only that creature this combat become unblocked. You may have it block an attacking creature of your choice"; } - public FalseOrdersUnblockEffect(final FalseOrdersUnblockEffect effect) { + private FalseOrdersUnblockEffect(final FalseOrdersUnblockEffect effect) { super(effect); } @@ -161,12 +159,22 @@ class FalseOrdersUnblockEffect extends OneShotEffect { game.getCombat().addBlockingGroup(permanent.getId(), chosenPermanent.getId(), controller.getId(), game); // 702.21h if (notYetBlocked) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null)); + Set morSet = new HashSet<>(); + morSet.add(new MageObjectReference(chosenPermanent, game)); for (UUID bandedId : chosenPermanent.getBandedCards()) { CombatGroup bandedGroup = game.getCombat().findGroup(bandedId); if (bandedGroup != null && chosenGroup.getBlockers().size() == 1) { + morSet.add(new MageObjectReference(bandedId, game)); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, bandedId, null)); } } + String key = UUID.randomUUID().toString(); + game.getState().setValue("becameBlocked_" + key, morSet); + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.BATCH_BLOCK_NONCOMBAT, + source.getSourceId(), source.getSourceId(), + source.getControllerId(), key, 0) + ); } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); } diff --git a/Mage.Sets/src/mage/cards/f/FalseProphet.java b/Mage.Sets/src/mage/cards/f/FalseProphet.java index 968b9c3455..6db36bfe8d 100644 --- a/Mage.Sets/src/mage/cards/f/FalseProphet.java +++ b/Mage.Sets/src/mage/cards/f/FalseProphet.java @@ -3,7 +3,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ExileAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class FalseProphet extends CardImpl { this.toughness = new MageInt(2); // When False Prophet dies, exile all creatures. - this.addAbility(new DiesTriggeredAbility(new ExileAllEffect(new FilterCreaturePermanent()))); + this.addAbility(new DiesSourceTriggeredAbility(new ExileAllEffect(new FilterCreaturePermanent()))); } public FalseProphet(final FalseProphet card) { diff --git a/Mage.Sets/src/mage/cards/f/FamishedGhoul.java b/Mage.Sets/src/mage/cards/f/FamishedGhoul.java index 984bafd610..83277c2064 100644 --- a/Mage.Sets/src/mage/cards/f/FamishedGhoul.java +++ b/Mage.Sets/src/mage/cards/f/FamishedGhoul.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -13,17 +11,18 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInASingleGraveyard; +import java.util.UUID; + /** - * * @author cbt33 */ public final class FamishedGhoul extends CardImpl { public FamishedGhoul(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(3); @@ -32,7 +31,7 @@ public final class FamishedGhoul extends CardImpl { // {1}{B}, Sacrifice Famished Ghoul: Exile up to two target cards from a single graveyard. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new ManaCostsImpl("{1}{B}")); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetCardInASingleGraveyard(0, 2, new FilterCard("cards from a single graveyard"))); + ability.addTarget(new TargetCardInASingleGraveyard(0, 2, StaticFilters.FILTER_CARD_CARDS)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/Farfinder.java b/Mage.Sets/src/mage/cards/f/Farfinder.java new file mode 100644 index 0000000000..02595becd9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/Farfinder.java @@ -0,0 +1,45 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Farfinder extends CardImpl { + + public Farfinder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.FOX); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // When Farfinder etners the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true + ), true)); + } + + private Farfinder(final Farfinder card) { + super(card); + } + + @Override + public Farfinder copy() { + return new Farfinder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FatalFrenzy.java b/Mage.Sets/src/mage/cards/f/FatalFrenzy.java index a5147f18f1..9728eeaec7 100644 --- a/Mage.Sets/src/mage/cards/f/FatalFrenzy.java +++ b/Mage.Sets/src/mage/cards/f/FatalFrenzy.java @@ -36,7 +36,7 @@ public final class FatalFrenzy extends CardImpl { .setText("Until end of turn, target creature you control gains trample") ); this.getSpellAbility().addEffect(new BoostTargetEffect(TargetPermanentPowerCount.instance, StaticValue.get(0), Duration.EndOfTurn, true) - .setText("and gets +X/+0, where X is its power.") + .setText("and gets +X/+0, where X is its power") ); this.getSpellAbility().addEffect(new FatalFrenzyEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/f/FatalLore.java b/Mage.Sets/src/mage/cards/f/FatalLore.java index 9978b96de5..e551a767b3 100644 --- a/Mage.Sets/src/mage/cards/f/FatalLore.java +++ b/Mage.Sets/src/mage/cards/f/FatalLore.java @@ -1,91 +1,91 @@ -package mage.cards.f; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.DrawCardTargetEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetCreaturePermanent; -import mage.target.common.TargetOpponent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author jeffwadsworth - */ -public final class FatalLore extends CardImpl { - - public FatalLore(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); - - // An opponent chooses one - You draw three cards; or you destroy up to two target creatures that opponent controls and that player draws up to three cards. Those creatures can't be regenerated. - this.getSpellAbility().addEffect(new FatalLoreEffect()); - this.getSpellAbility().addTarget(new TargetOpponent(true)); - - } - - private FatalLore(final FatalLore card) { - super(card); - } - - @Override - public FatalLore copy() { - return new FatalLore(this); - } -} - -class FatalLoreEffect extends OneShotEffect { - - public FatalLoreEffect() { - super(Outcome.Neutral); - staticText = "An opponent chooses one - You draw three cards; or you destroy up to two target creatures that opponent controls and that player draws up to three cards. Those creatures can't be regenerated"; - } - - public FatalLoreEffect(final FatalLoreEffect effect) { - super(effect); - } - - @Override - public FatalLoreEffect copy() { - return new FatalLoreEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Player chosenOpponent = game.getPlayer(targetPointer.getFirst(game, source)); - if (controller != null - && chosenOpponent != null) { - if (chosenOpponent.chooseUse(Outcome.Neutral, "If you choose Yes, the controller draws three cards. If no, the controller gets to destroy up to two target creatures that you control and you get to draw up to 3 cards. Those creatures can't be regenerated.", source, game)) { - controller.drawCards(3, game); - } else { - FilterCreaturePermanent filter = new FilterCreaturePermanent("chosen opponent's creature"); - filter.add(new ControllerIdPredicate(chosenOpponent.getId())); - TargetCreaturePermanent target = new TargetCreaturePermanent(0, 2, filter, false); - if (target.canChoose(controller.getId(), game) - && controller.choose(Outcome.DestroyPermanent, target, source.getSourceId(), game)) { - for (UUID targetId : target.getTargets()) { - Effect destroyCreature = new DestroyTargetEffect(true); - destroyCreature.setTargetPointer(new FixedTarget(targetId)); - destroyCreature.apply(game, source); - } - Effect opponentDrawsCards = new DrawCardTargetEffect(StaticValue.get(3), false, true); - opponentDrawsCards.setTargetPointer(new FixedTarget(chosenOpponent.getId())); - opponentDrawsCards.apply(game, source); - return true; - } - } - } - return false; - } -} +package mage.cards.f; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jeffwadsworth + */ +public final class FatalLore extends CardImpl { + + public FatalLore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); + + // An opponent chooses one - You draw three cards; or you destroy up to two target creatures that opponent controls and that player draws up to three cards. Those creatures can't be regenerated. + this.getSpellAbility().addEffect(new FatalLoreEffect()); + this.getSpellAbility().addTarget(new TargetOpponent(true)); + + } + + private FatalLore(final FatalLore card) { + super(card); + } + + @Override + public FatalLore copy() { + return new FatalLore(this); + } +} + +class FatalLoreEffect extends OneShotEffect { + + public FatalLoreEffect() { + super(Outcome.Neutral); + staticText = "An opponent chooses one - You draw three cards; or you destroy up to two target creatures that opponent controls and that player draws up to three cards. Those creatures can't be regenerated"; + } + + public FatalLoreEffect(final FatalLoreEffect effect) { + super(effect); + } + + @Override + public FatalLoreEffect copy() { + return new FatalLoreEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player chosenOpponent = game.getPlayer(targetPointer.getFirst(game, source)); + if (controller != null + && chosenOpponent != null) { + if (chosenOpponent.chooseUse(Outcome.Neutral, "If you choose Yes, the controller draws three cards. If no, the controller gets to destroy up to two target creatures that you control and you get to draw up to 3 cards. Those creatures can't be regenerated.", source, game)) { + controller.drawCards(3, source.getSourceId(), game); + } else { + FilterCreaturePermanent filter = new FilterCreaturePermanent("chosen opponent's creature"); + filter.add(new ControllerIdPredicate(chosenOpponent.getId())); + TargetCreaturePermanent target = new TargetCreaturePermanent(0, 2, filter, false); + if (target.canChoose(controller.getId(), game) + && controller.choose(Outcome.DestroyPermanent, target, source.getSourceId(), game)) { + for (UUID targetId : target.getTargets()) { + Effect destroyCreature = new DestroyTargetEffect(true); + destroyCreature.setTargetPointer(new FixedTarget(targetId)); + destroyCreature.apply(game, source); + } + Effect opponentDrawsCards = new DrawCardTargetEffect(StaticValue.get(3), false, true); + opponentDrawsCards.setTargetPointer(new FixedTarget(chosenOpponent.getId())); + opponentDrawsCards.apply(game, source); + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FatedConflagration.java b/Mage.Sets/src/mage/cards/f/FatedConflagration.java index be6f34b0b6..7c1b4b9a8d 100644 --- a/Mage.Sets/src/mage/cards/f/FatedConflagration.java +++ b/Mage.Sets/src/mage/cards/f/FatedConflagration.java @@ -24,7 +24,7 @@ public final class FatedConflagration extends CardImpl { // Fated Conflagration deals 5 damage to target creature or planewalker. If it's your turn, scry 2. this.getSpellAbility().addEffect(new DamageTargetEffect(5)); this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2), MyTurnCondition.instance, "If it's your turn, scry 2")); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2, false), MyTurnCondition.instance, "If it's your turn, scry 2")); this.getSpellAbility().addHint(MyTurnHint.instance); } diff --git a/Mage.Sets/src/mage/cards/f/FatedIntervention.java b/Mage.Sets/src/mage/cards/f/FatedIntervention.java index 92d6442d13..2b1ea48ef0 100644 --- a/Mage.Sets/src/mage/cards/f/FatedIntervention.java +++ b/Mage.Sets/src/mage/cards/f/FatedIntervention.java @@ -20,10 +20,9 @@ public final class FatedIntervention extends CardImpl { public FatedIntervention(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}{G}{G}"); - // Create two 3/3 green Centaur enchantment creature tokens. If it's your turn, scry 2. this.getSpellAbility().addEffect(new CreateTokenEffect(new CentaurEnchantmentCreatureToken(), 2)); - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2), MyTurnCondition.instance, "If it's your turn, scry 2")); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2, false), MyTurnCondition.instance, "If it's your turn, scry 2")); this.getSpellAbility().addHint(MyTurnHint.instance); } diff --git a/Mage.Sets/src/mage/cards/f/FatedRetribution.java b/Mage.Sets/src/mage/cards/f/FatedRetribution.java index d0a35a9ed8..23654106e7 100644 --- a/Mage.Sets/src/mage/cards/f/FatedRetribution.java +++ b/Mage.Sets/src/mage/cards/f/FatedRetribution.java @@ -32,7 +32,7 @@ public final class FatedRetribution extends CardImpl { // Destroy all creatures and planeswalkers. If it's your turn, scry 2. this.getSpellAbility().addEffect(new DestroyAllEffect(filter, false)); - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2), MyTurnCondition.instance, "If it's your turn, scry 2")); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2, false), MyTurnCondition.instance, "If it's your turn, scry 2")); this.getSpellAbility().addHint(MyTurnHint.instance); } diff --git a/Mage.Sets/src/mage/cards/f/FatefulEnd.java b/Mage.Sets/src/mage/cards/f/FatefulEnd.java index 1b107f271f..22813579de 100644 --- a/Mage.Sets/src/mage/cards/f/FatefulEnd.java +++ b/Mage.Sets/src/mage/cards/f/FatefulEnd.java @@ -18,7 +18,7 @@ public final class FatefulEnd extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); // Fateful End deals 3 damage to any target. Scry 1. - this.getSpellAbility().addEffect(new DamageTargetEffect(3, true, "any target.")); + this.getSpellAbility().addEffect(new DamageTargetEffect(3, true, "any target")); this.getSpellAbility().addTarget(new TargetAnyTarget()); this.getSpellAbility().addEffect(new ScryEffect(1)); } diff --git a/Mage.Sets/src/mage/cards/f/Fatigue.java b/Mage.Sets/src/mage/cards/f/Fatigue.java index 9cdd6176c3..0ddc96756a 100644 --- a/Mage.Sets/src/mage/cards/f/Fatigue.java +++ b/Mage.Sets/src/mage/cards/f/Fatigue.java @@ -1,36 +1,36 @@ -package mage.cards.f; - -import java.util.UUID; -import mage.abilities.effects.common.SkipNextDrawStepTargetEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.target.TargetPlayer; - -/** - * - * @author jeffwadsworth - */ -public final class Fatigue extends CardImpl { - - private static final String rule = "Target player skips their next draw step."; - - public Fatigue(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}"); - - - // Target player skips their next draw step. - this.getSpellAbility().addEffect(new SkipNextDrawStepTargetEffect().setText(rule)); - this.getSpellAbility().addTarget(new TargetPlayer()); - - } - - public Fatigue(final Fatigue card) { - super(card); - } - - @Override - public Fatigue copy() { - return new Fatigue(this); - } -} +package mage.cards.f; + +import java.util.UUID; +import mage.abilities.effects.common.SkipNextDrawStepTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.TargetPlayer; + +/** + * + * @author jeffwadsworth + */ +public final class Fatigue extends CardImpl { + + private static final String rule = "Target player skips their next draw step."; + + public Fatigue(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}"); + + + // Target player skips their next draw step. + this.getSpellAbility().addEffect(new SkipNextDrawStepTargetEffect().setText(rule)); + this.getSpellAbility().addTarget(new TargetPlayer()); + + } + + public Fatigue(final Fatigue card) { + super(card); + } + + @Override + public Fatigue copy() { + return new Fatigue(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java b/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java index af752d5c84..5e021705ba 100644 --- a/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java +++ b/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java @@ -3,7 +3,7 @@ package mage.cards.f; import mage.MageInt; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; @@ -48,7 +48,7 @@ public final class FeastingTrollKing extends CardImpl { // When Feasting Troll King enters the battlefield, if you cast it from your hand, create three Food tokens. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new FoodToken(), 3)), - CastFromHandSourceCondition.instance, "When {this} enters the battlefield, " + + CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, " + "if you cast it from your hand, create three Food tokens." ), new CastFromHandWatcher()); diff --git a/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java b/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java index ebb28bb5b9..20305147a3 100644 --- a/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java +++ b/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java @@ -41,7 +41,9 @@ public final class FeatherTheRedeemed extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Whenever you cast an instant or sorcery spell that targets a creature you control, exile that card instead of putting it into your graveyard as it resolves. If you do, return it to your hand at the beginning of the next end step. + // Whenever you cast an instant or sorcery spell that targets a creature you control, + // exile that card instead of putting it into your graveyard as it resolves. + // If you do, return it to your hand at the beginning of the next end step. this.addAbility(new FeatherTheRedeemedTriggeredAbility()); } diff --git a/Mage.Sets/src/mage/cards/f/Fecundity.java b/Mage.Sets/src/mage/cards/f/Fecundity.java index 2c87820a40..e59f570461 100644 --- a/Mage.Sets/src/mage/cards/f/Fecundity.java +++ b/Mage.Sets/src/mage/cards/f/Fecundity.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -21,7 +20,7 @@ import mage.players.Player; public final class Fecundity extends CardImpl { public Fecundity(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // Whenever a creature dies, that creature's controller may draw a card. this.addAbility(new DiesCreatureTriggeredAbility(new FecundityEffect(), false, false, true)); @@ -55,12 +54,14 @@ class FecundityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) game.getLastKnownInformation(this.getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD); + Permanent permanent = (Permanent) game.getLastKnownInformation(this.getTargetPointer() + // Card can be moved again (e.g. commander replacement) so we need the row id from fixed target to check + .getFixedTarget(game, source).getTarget(), Zone.BATTLEFIELD); if (permanent != null) { Player controller = game.getPlayer(permanent.getControllerId()); if (controller != null) { if (controller.chooseUse(outcome, "Draw a card?", source, game)) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/f/Feedback.java b/Mage.Sets/src/mage/cards/f/Feedback.java index 68489eded5..df3bc7c272 100644 --- a/Mage.Sets/src/mage/cards/f/Feedback.java +++ b/Mage.Sets/src/mage/cards/f/Feedback.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -24,7 +23,7 @@ import mage.target.common.TargetEnchantmentPermanent; public final class Feedback extends CardImpl { public Feedback(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); this.subtype.add(SubType.AURA); // Enchant enchantment @@ -32,11 +31,13 @@ public final class Feedback extends CardImpl { this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + // At the beginning of the upkeep of enchanted enchantment's controller, Feedback deals 1 damage to that player. Effect effect = new DamageTargetEffect(1); effect.setText("{this} deals 1 damage to that player"); this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, - TargetController.CONTROLLER_ATTACHED_TO, false, true)); + TargetController.CONTROLLER_ATTACHED_TO, false, true, + "At the beginning of the upkeep of enchanted enchantment's controller, ")); } public Feedback(final Feedback card) { diff --git a/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java b/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java index 4b7c656183..291e56ad8b 100644 --- a/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java +++ b/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java @@ -78,7 +78,7 @@ class FelhideSpiritbinderEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.ENCHANTMENT, true); effect.setTargetPointer(getTargetPointer()); diff --git a/Mage.Sets/src/mage/cards/f/FelidarGuardian.java b/Mage.Sets/src/mage/cards/f/FelidarGuardian.java index cbd687eea8..1312c76e69 100644 --- a/Mage.Sets/src/mage/cards/f/FelidarGuardian.java +++ b/Mage.Sets/src/mage/cards/f/FelidarGuardian.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -16,8 +14,9 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author Styxo */ public final class FelidarGuardian extends CardImpl { @@ -39,7 +38,7 @@ public final class FelidarGuardian extends CardImpl { // When Felidar Guardian enters the battlefield, you may exile another target permanent you control, then return that card to the battlefield under its owner's control. Effect effect = new ExileTargetForSourceEffect(); Ability ability = new EntersBattlefieldTriggeredAbility(effect, true); - ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); ability.addTarget(new TargetControlledPermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/FelineSovereign.java b/Mage.Sets/src/mage/cards/f/FelineSovereign.java new file mode 100644 index 0000000000..7b84e45e8b --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FelineSovereign.java @@ -0,0 +1,131 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public final class FelineSovereign extends CardImpl { + + private static final FilterCreaturePermanent filterCat = new FilterCreaturePermanent("Cats"); + + static { + filterCat.add(SubType.CAT.getPredicate()); + filterCat.add(TargetController.YOU.getControllerPredicate()); + } + + private static final FilterCard filterProtectionFromDogs = new FilterCard("Dogs"); + + static { + filterProtectionFromDogs.add(SubType.DOG.getPredicate()); + } + + public FelineSovereign(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.subtype.add(SubType.CAT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Other Cats you control get +1/+1 and have protection from Dogs. + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filterCat, true)); + // + Effect effect = new GainAbilityAllEffect(new ProtectionAbility(filterProtectionFromDogs), Duration.WhileOnBattlefield, filterCat, true); + effect.setText("and have protection from Dogs"); + ability.addEffect(effect); + this.addAbility(ability); + + // Whenever one or more Cats you control deal combat damage to a player, destroy up to one target artifact or enchantment that player controls. + this.addAbility(new FelineSovereignTriggeredAbility()); + } + + public FelineSovereign(final FelineSovereign card) { + super(card); + } + + @Override + public FelineSovereign copy() { + return new FelineSovereign(this); + } +} + +class FelineSovereignTriggeredAbility extends TriggeredAbilityImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Cat you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(SubType.CAT.getPredicate()); + } + + private final Set damagedPlayerIds = new HashSet<>(); + + public FelineSovereignTriggeredAbility() { + super(Zone.BATTLEFIELD, new DestroyTargetEffect(), false); + } + + public FelineSovereignTriggeredAbility(final FelineSovereignTriggeredAbility ability) { + super(ability); + } + + @Override + public FelineSovereignTriggeredAbility copy() { + return new FelineSovereignTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGED_PLAYER + || event.getType() == EventType.COMBAT_DAMAGE_STEP_PRIORITY + || event.getType() == EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == EventType.DAMAGED_PLAYER) { + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; + Permanent p = game.getPermanent(event.getSourceId()); + if (damageEvent.isCombatDamage() && p != null && p.isControlledBy(this.getControllerId()) && + filter.match(p, getSourceId(), getControllerId(), game) && + !damagedPlayerIds.contains(event.getPlayerId())) { + damagedPlayerIds.add(event.getPlayerId()); + this.getTargets().clear(); + FilterArtifactOrEnchantmentPermanent filter = new FilterArtifactOrEnchantmentPermanent(); + filter.add(new ControllerIdPredicate(event.getPlayerId())); + this.addTarget(new TargetPermanent(0, 1, filter, false)); + return true; + } + } + if (event.getType() == EventType.COMBAT_DAMAGE_STEP_PRIORITY || + (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(getSourceId()))) { + damagedPlayerIds.clear(); + } + return false; + } + + @Override + public String getRule() { + return "Whenever one or more Cats you control deal combat damage to a player, destroy up to one target artifact or enchantment that player controls"; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FellTheMighty.java b/Mage.Sets/src/mage/cards/f/FellTheMighty.java index a53a034e48..ac6085360b 100644 --- a/Mage.Sets/src/mage/cards/f/FellTheMighty.java +++ b/Mage.Sets/src/mage/cards/f/FellTheMighty.java @@ -58,7 +58,7 @@ class FellTheMightyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent targetCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent targetCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && targetCreature != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, controller.getId(), source.getSourceId(), game)) { if (permanent.getPower().getValue() > targetCreature.getPower().getValue()) { diff --git a/Mage.Sets/src/mage/cards/f/FellThePheasant.java b/Mage.Sets/src/mage/cards/f/FellThePheasant.java index 14c4c01826..788685d81a 100644 --- a/Mage.Sets/src/mage/cards/f/FellThePheasant.java +++ b/Mage.Sets/src/mage/cards/f/FellThePheasant.java @@ -20,7 +20,7 @@ import java.util.UUID; public final class FellThePheasant extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creature with flying."); + private static final FilterPermanent filter = new FilterCreaturePermanent("creature with flying"); static { filter.add(new AbilityPredicate(FlyingAbility.class)); diff --git a/Mage.Sets/src/mage/cards/f/FendOff.java b/Mage.Sets/src/mage/cards/f/FendOff.java index 045d166fe7..1061c527cc 100644 --- a/Mage.Sets/src/mage/cards/f/FendOff.java +++ b/Mage.Sets/src/mage/cards/f/FendOff.java @@ -1,41 +1,41 @@ -package mage.cards.f; - -import java.util.UUID; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.PreventDamageByTargetEffect; -import mage.abilities.keyword.CyclingAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.target.common.TargetCreaturePermanent; - -/** - * - * @author jeffwadsworth - */ -public final class FendOff extends CardImpl { - - private static final String rule = "Prevent all combat damage that would be dealt by target creature this turn."; - - public FendOff(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); - - // Prevent all combat damage that would be dealt by target creature this turn. - this.getSpellAbility().addEffect(new PreventDamageByTargetEffect(Duration.EndOfTurn, true).setText(rule)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - - // Cycling {2} - this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); - - } - - public FendOff(final FendOff card) { - super(card); - } - - @Override - public FendOff copy() { - return new FendOff(this); - } -} +package mage.cards.f; + +import java.util.UUID; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.PreventDamageByTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author jeffwadsworth + */ +public final class FendOff extends CardImpl { + + private static final String rule = "Prevent all combat damage that would be dealt by target creature this turn."; + + public FendOff(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Prevent all combat damage that would be dealt by target creature this turn. + this.getSpellAbility().addEffect(new PreventDamageByTargetEffect(Duration.EndOfTurn, true).setText(rule)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + + } + + public FendOff(final FendOff card) { + super(card); + } + + @Override + public FendOff copy() { + return new FendOff(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FeralLightning.java b/Mage.Sets/src/mage/cards/f/FeralLightning.java index e15766daf2..d87cf22bad 100644 --- a/Mage.Sets/src/mage/cards/f/FeralLightning.java +++ b/Mage.Sets/src/mage/cards/f/FeralLightning.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -10,17 +8,18 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; -import mage.game.permanent.token.ElementalToken; +import mage.game.permanent.token.ElementalTokenWithHaste; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class FeralLightning extends CardImpl { public FeralLightning(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{R}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}{R}"); // Create three 3/1 red Elemental creature tokens with haste. Exile them at the beginning of the next end step. this.getSpellAbility().addEffect(new FeralLightningEffect()); @@ -57,7 +56,7 @@ class FeralLightningEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - CreateTokenEffect effect = new CreateTokenEffect(new ElementalToken("CON", 1, true), 3); + CreateTokenEffect effect = new CreateTokenEffect(new ElementalTokenWithHaste(), 3); effect.apply(game, source); effect.exileTokensCreatedAtNextEndStep(game, source); return true; diff --git a/Mage.Sets/src/mage/cards/f/FeralProwler.java b/Mage.Sets/src/mage/cards/f/FeralProwler.java index 35935d2fcb..5636aa30e8 100644 --- a/Mage.Sets/src/mage/cards/f/FeralProwler.java +++ b/Mage.Sets/src/mage/cards/f/FeralProwler.java @@ -2,7 +2,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -18,7 +18,7 @@ public final class FeralProwler extends CardImpl { power = new MageInt(1); toughness = new MageInt(3); - addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } public FeralProwler(final FeralProwler feralProwler) { diff --git a/Mage.Sets/src/mage/cards/f/FerociousTigorilla.java b/Mage.Sets/src/mage/cards/f/FerociousTigorilla.java new file mode 100644 index 0000000000..32764e2b4f --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FerociousTigorilla.java @@ -0,0 +1,41 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.common.counter.AddCounterChoiceSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FerociousTigorilla extends CardImpl { + + public FerociousTigorilla(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.APE); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Ferocious Tigorilla enters the battlefield with your choice of a trample counter or a menace counter on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCounterChoiceSourceEffect(CounterType.TRAMPLE, CounterType.MENACE) + )); + } + + private FerociousTigorilla(final FerociousTigorilla card) { + super(card); + } + + @Override + public FerociousTigorilla copy() { + return new FerociousTigorilla(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FerozsBan.java b/Mage.Sets/src/mage/cards/f/FerozsBan.java index bda78ea60a..52a6ed4995 100644 --- a/Mage.Sets/src/mage/cards/f/FerozsBan.java +++ b/Mage.Sets/src/mage/cards/f/FerozsBan.java @@ -1,29 +1,29 @@ package mage.cards.f; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.StaticFilters; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; + +import java.util.UUID; /** - * * @author LoneFox - * */ public final class FerozsBan extends CardImpl { + private static final FilterCard filter = new FilterCreatureCard("Creature spells"); + public FerozsBan(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}"); // Creature spells cost {2} more to cast. - Effect effect = new SpellsCostIncreasementAllEffect(StaticFilters.FILTER_CARD_CREATURE, 2); - effect.setText("Creature spells cost {2} more to cast."); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(2, filter, TargetController.ANY))); } public FerozsBan(final FerozsBan card) { diff --git a/Mage.Sets/src/mage/cards/f/FerventChampion.java b/Mage.Sets/src/mage/cards/f/FerventChampion.java index e6122045a4..3dc095ea40 100644 --- a/Mage.Sets/src/mage/cards/f/FerventChampion.java +++ b/Mage.Sets/src/mage/cards/f/FerventChampion.java @@ -90,14 +90,22 @@ class FerventChampionEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify instanceof EquipAbility - && abilityToModify.isControlledBy(source.getControllerId()) - && abilityToModify + if (abilityToModify instanceof EquipAbility + && abilityToModify.isControlledBy(source.getControllerId())) { + if (game != null && game.inCheckPlayableState()) { + return !abilityToModify.getTargets().isEmpty() && + abilityToModify.getTargets().get(0).canTarget(source.getSourceId(), abilityToModify, game); + } else { + return abilityToModify .getTargets() .stream() .map(Target::getTargets) .flatMap(Collection::stream) - .anyMatch(source.getSourceId()::equals); + .anyMatch(source.getSourceId()::equals); + } + + } + return false; } @Override diff --git a/Mage.Sets/src/mage/cards/f/FesteringGoblin.java b/Mage.Sets/src/mage/cards/f/FesteringGoblin.java index 846165c4b5..a5d737259b 100644 --- a/Mage.Sets/src/mage/cards/f/FesteringGoblin.java +++ b/Mage.Sets/src/mage/cards/f/FesteringGoblin.java @@ -4,7 +4,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class FesteringGoblin extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false); + Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FesteringMummy.java b/Mage.Sets/src/mage/cards/f/FesteringMummy.java index ce2d4cc4ba..9278a6c6a9 100644 --- a/Mage.Sets/src/mage/cards/f/FesteringMummy.java +++ b/Mage.Sets/src/mage/cards/f/FesteringMummy.java @@ -4,7 +4,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -27,7 +27,7 @@ public final class FesteringMummy extends CardImpl { this.toughness = new MageInt(1); // When Festering Mummy dies, you may put a -1/-1 counter on target creature. - Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance()), true); + Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance()), true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FesteringNewt.java b/Mage.Sets/src/mage/cards/f/FesteringNewt.java index d0219fe32c..21b616b3b6 100644 --- a/Mage.Sets/src/mage/cards/f/FesteringNewt.java +++ b/Mage.Sets/src/mage/cards/f/FesteringNewt.java @@ -4,7 +4,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.LockedInCondition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -45,7 +45,7 @@ public final class FesteringNewt extends CardImpl { new BoostTargetEffect(-1,-1, Duration.EndOfTurn), new LockedInCondition(new PermanentsOnTheBattlefieldCondition(filterBogbrewWitch)), "target creature an opponent controls gets -1/-1 until end of turn. That creature gets -4/-4 instead if you control a creature named Bogbrew Witch"); - Ability ability = new DiesTriggeredAbility(effect); + Ability ability = new DiesSourceTriggeredAbility(effect); ability.addTarget(new TargetCreaturePermanent(filterCreature)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FeveredVisions.java b/Mage.Sets/src/mage/cards/f/FeveredVisions.java index f14b2a5262..9b100ce1fc 100644 --- a/Mage.Sets/src/mage/cards/f/FeveredVisions.java +++ b/Mage.Sets/src/mage/cards/f/FeveredVisions.java @@ -54,7 +54,7 @@ class FeveredVisionsEffect extends OneShotEffect { UUID activePlayerId = game.getActivePlayerId(); Player player = game.getPlayer(activePlayerId); if (controller != null && player != null) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); Set opponents = game.getOpponents(source.getControllerId()); if (opponents.contains(player.getId()) && player.getHand().size() > 3) { player.damage(2, source.getSourceId(), game); diff --git a/Mage.Sets/src/mage/cards/f/FieldOfTheDead.java b/Mage.Sets/src/mage/cards/f/FieldOfTheDead.java index e4eca676bd..f224145654 100644 --- a/Mage.Sets/src/mage/cards/f/FieldOfTheDead.java +++ b/Mage.Sets/src/mage/cards/f/FieldOfTheDead.java @@ -1,8 +1,8 @@ package mage.cards.f; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -33,8 +33,8 @@ public final class FieldOfTheDead extends CardImpl { // Whenever Field of the Dead or another land enters the battlefield under your control, if you control seven or more lands with different names, create a 2/2 black Zombie creature token. this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldControlledTriggeredAbility( - new CreateTokenEffect(new ZombieToken()), StaticFilters.FILTER_LAND + new EntersBattlefieldThisOrAnotherTriggeredAbility( + new CreateTokenEffect(new ZombieToken()), StaticFilters.FILTER_LAND, false, true ), FieldOfTheDeadCondition.instance, "Whenever {this} or another land " + "enters the battlefield under your control, if you control seven or more lands with different names, " + "create a 2/2 black Zombie creature token." diff --git a/Mage.Sets/src/mage/cards/f/FiendArtisan.java b/Mage.Sets/src/mage/cards/f/FiendArtisan.java new file mode 100644 index 0000000000..fed489f0ac --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FiendArtisan.java @@ -0,0 +1,100 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FiendArtisan extends CardImpl { + + private static final DynamicValue xValue + = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE); + private static final FilterControlledPermanent filter + = new FilterControlledCreaturePermanent("another creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public FiendArtisan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B/G}{B/G}"); + + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Fiend Artisan gets +1/+1 for each creature card in your graveyard. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + xValue, xValue, Duration.WhileOnBattlefield + ).setText("{this} gets +1/+1 for each creature card in your graveyard"))); + + // {X}{B/G}, {T}, Sacrifice another creature: Search your library for a creature card with converted mana cost X or less, put it onto the battlefield, then shuffle your library. Activate this ability only any time you could cast a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + Zone.BATTLEFIELD, new FiendArtisanEffect(), new ManaCostsImpl("{X}{B/G}") + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + this.addAbility(ability); + } + + private FiendArtisan(final FiendArtisan card) { + super(card); + } + + @Override + public FiendArtisan copy() { + return new FiendArtisan(this); + } +} + +class FiendArtisanEffect extends OneShotEffect { + + FiendArtisanEffect() { + super(Outcome.Benefit); + staticText = "search your library for a creature card with converted mana cost X or less, " + + "put it onto the battlefield, then shuffle your library"; + } + + private FiendArtisanEffect(final FiendArtisanEffect effect) { + super(effect); + } + + @Override + public FiendArtisanEffect copy() { + return new FiendArtisanEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int xValue = source.getManaCostsToPay().getX(); + FilterCard filter = new FilterCreatureCard("creature card with converted mana cost " + xValue + " or less"); + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)).apply(game, source); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/FierceGuardianship.java b/Mage.Sets/src/mage/cards/f/FierceGuardianship.java new file mode 100644 index 0000000000..4e96a94fe8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FierceGuardianship.java @@ -0,0 +1,38 @@ +package mage.cards.f; + +import mage.abilities.condition.common.ControlACommanderCondition; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FierceGuardianship extends CardImpl { + + public FierceGuardianship(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // If you control a commander, you may cast this spell without paying its mana cost. + this.addAbility(new AlternativeCostSourceAbility(null, ControlACommanderCondition.instance)); + + // Counter target noncreature spell. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); + } + + private FierceGuardianship(final FierceGuardianship card) { + super(card); + } + + @Override + public FierceGuardianship copy() { + return new FierceGuardianship(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FierceInvocation.java b/Mage.Sets/src/mage/cards/f/FierceInvocation.java index c50291390a..3b88a89853 100644 --- a/Mage.Sets/src/mage/cards/f/FierceInvocation.java +++ b/Mage.Sets/src/mage/cards/f/FierceInvocation.java @@ -67,7 +67,7 @@ class FierceInvocationEffect extends OneShotEffect { Permanent permanent = game.getPermanent(card.getId()); if (permanent != null) { Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/f/FieryEmancipation.java b/Mage.Sets/src/mage/cards/f/FieryEmancipation.java new file mode 100644 index 0000000000..0c6009ef96 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FieryEmancipation.java @@ -0,0 +1,77 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FieryEmancipation extends CardImpl { + + public FieryEmancipation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}{R}"); + + // If a source you control would deal damage to a permanent or player, it deals triple that damage to that permanent or player instead. + this.addAbility(new SimpleStaticAbility(new FieryEmancipationEffect())); + } + + private FieryEmancipation(final FieryEmancipation card) { + super(card); + } + + @Override + public FieryEmancipation copy() { + return new FieryEmancipation(this); + } +} + +class FieryEmancipationEffect extends ReplacementEffectImpl { + + FieryEmancipationEffect() { + super(Duration.WhileOnBattlefield, Outcome.Damage); + staticText = "If a source you control would deal damage to a permanent or player, " + + "it deals triple that damage to that permanent or player instead"; + } + + private FieryEmancipationEffect(final FieryEmancipationEffect effect) { + super(effect); + } + + @Override + public FieryEmancipationEffect copy() { + return new FieryEmancipationEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType().equals(GameEvent.EventType.DAMAGE_PLAYER) + || event.getType().equals(GameEvent.EventType.DAMAGE_CREATURE) + || event.getType().equals(GameEvent.EventType.DAMAGE_PLANESWALKER); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return game.getControllerId(event.getSourceId()).equals(source.getControllerId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(CardUtil.addWithOverflowCheck( + CardUtil.addWithOverflowCheck( + event.getAmount(), event.getAmount() + ), event.getAmount() + )); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FieryGambit.java b/Mage.Sets/src/mage/cards/f/FieryGambit.java index 36177cf86d..748885ddad 100644 --- a/Mage.Sets/src/mage/cards/f/FieryGambit.java +++ b/Mage.Sets/src/mage/cards/f/FieryGambit.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -17,14 +15,15 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class FieryGambit extends CardImpl { public FieryGambit(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); // Flip a coin until you lose a flip or choose to stop flipping. If you lose a flip, Fiery Gambit has no effect. If you win one or more flips, Fiery Gambit deals 3 damage to target creature. If you win two or more flips, Fiery Gambit deals 6 damage to each opponent. If you win three or more flips, draw nine cards and untap all lands you control. this.getSpellAbility().addEffect(new FieryGambitEffect()); @@ -71,6 +70,12 @@ class FieryGambitEffect extends OneShotEffect { controllerStopped = true; break; } + + // AI workaround to stop flips on good result + if (!controller.isHuman() && !controller.isTestMode() && flipsWon >= 3) { + controllerStopped = true; + break; + } } if (controllerStopped) { Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); @@ -81,7 +86,7 @@ class FieryGambitEffect extends OneShotEffect { new DamagePlayersEffect(6, TargetController.OPPONENT).apply(game, source); } if (flipsWon > 2) { - controller.drawCards(9, game); + controller.drawCards(9, source.getSourceId(), game); new UntapAllLandsControllerEffect().apply(game, source); } } else { diff --git a/Mage.Sets/src/mage/cards/f/FieryHellhound.java b/Mage.Sets/src/mage/cards/f/FieryHellhound.java index 62815a4ed5..8381f487ef 100644 --- a/Mage.Sets/src/mage/cards/f/FieryHellhound.java +++ b/Mage.Sets/src/mage/cards/f/FieryHellhound.java @@ -23,7 +23,7 @@ public final class FieryHellhound extends CardImpl { public FieryHellhound(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}{R}"); this.subtype.add(SubType.ELEMENTAL); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/f/FightAsOne.java b/Mage.Sets/src/mage/cards/f/FightAsOne.java new file mode 100644 index 0000000000..7fd124c15e --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FightAsOne.java @@ -0,0 +1,64 @@ +package mage.cards.f; + +import mage.abilities.Mode; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FightAsOne extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.HUMAN); + private static final FilterPermanent filter2 = new FilterControlledCreaturePermanent("non-Human creature you control"); + + static { + filter2.add(Predicates.not(SubType.HUMAN.getPredicate())); + } + + public FightAsOne(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); + + // Choose one or both— + this.getSpellAbility().getModes().setMinModes(1); + this.getSpellAbility().getModes().setMaxModes(2); + + // • Target Human creature you control gets +1/+1 and gains indestructible until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 1) + .setText("Target Human creature you control gets +1/+1")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains indestructible until end of turn")); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + // • Target non-Human creature you control gets +1/+1 and gains indestructible until end of turn. + Mode mode = new Mode(new BoostTargetEffect(1, 1) + .setText("Target non-Human creature you control gets +1/+1")); + mode.addEffect(new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains indestructible until end of turn")); + mode.addTarget(new TargetPermanent(filter2)); + this.getSpellAbility().addMode(mode); + } + + private FightAsOne(final FightAsOne card) { + super(card); + } + + @Override + public FightAsOne copy() { + return new FightAsOne(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FightOrFlight.java b/Mage.Sets/src/mage/cards/f/FightOrFlight.java index 6e88e228ce..219d9d307e 100644 --- a/Mage.Sets/src/mage/cards/f/FightOrFlight.java +++ b/Mage.Sets/src/mage/cards/f/FightOrFlight.java @@ -95,7 +95,7 @@ class FightOrFlightEffect extends OneShotEffect { if (permanent != null) { RestrictionEffect effect = new CantAttackTargetEffect(Duration.EndOfTurn); effect.setText(""); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/f/FiligreeAngel.java b/Mage.Sets/src/mage/cards/f/FiligreeAngel.java index 638f78ff10..2f06035903 100644 --- a/Mage.Sets/src/mage/cards/f/FiligreeAngel.java +++ b/Mage.Sets/src/mage/cards/f/FiligreeAngel.java @@ -1,34 +1,30 @@ - - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledPermanent; +import mage.constants.SubType; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author Loki */ public final class FiligreeAngel extends CardImpl { - public FiligreeAngel (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}{W}{W}{U}"); + public FiligreeAngel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}{W}{W}{U}"); this.subtype.add(SubType.ANGEL); - - this.power = new MageInt(4); this.toughness = new MageInt(4); @@ -36,10 +32,10 @@ public final class FiligreeAngel extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Filigree Angel enters the battlefield, you gain 3 life for each artifact you control. - this.addAbility(new EntersBattlefieldTriggeredAbility(new FiligreeAngelEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new FiligreeAngelEffect()).addHint(ArtifactYouControlHint.instance)); } - public FiligreeAngel (final FiligreeAngel card) { + public FiligreeAngel(final FiligreeAngel card) { super(card); } @@ -50,12 +46,6 @@ public final class FiligreeAngel extends CardImpl { } class FiligreeAngelEffect extends OneShotEffect { - - private static final FilterPermanent filter = new FilterControlledPermanent(); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } public FiligreeAngelEffect() { super(Outcome.GainLife); @@ -70,7 +60,7 @@ class FiligreeAngelEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - int life = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) * 3; + int life = ArtifactYouControlCount.instance.calculate(game, source, this) * 3; player.gainLife(life, game, source); } return true; diff --git a/Mage.Sets/src/mage/cards/f/FiligreeCrawler.java b/Mage.Sets/src/mage/cards/f/FiligreeCrawler.java index 3edcd09dfa..c0ab6710dc 100644 --- a/Mage.Sets/src/mage/cards/f/FiligreeCrawler.java +++ b/Mage.Sets/src/mage/cards/f/FiligreeCrawler.java @@ -3,7 +3,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class FiligreeCrawler extends CardImpl { this.toughness = new MageInt(2); // When Filigree Crawler dies, create a 1/1 colorless Thopter artifact creature token with flying. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken()))); } public FiligreeCrawler(final FiligreeCrawler card) { diff --git a/Mage.Sets/src/mage/cards/f/FiligreeFamiliar.java b/Mage.Sets/src/mage/cards/f/FiligreeFamiliar.java index 854c79187c..50a977f31f 100644 --- a/Mage.Sets/src/mage/cards/f/FiligreeFamiliar.java +++ b/Mage.Sets/src/mage/cards/f/FiligreeFamiliar.java @@ -3,7 +3,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -28,7 +28,7 @@ public final class FiligreeFamiliar extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(2), false)); // When Filigree Familiar dies, draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } public FiligreeFamiliar(final FiligreeFamiliar card) { diff --git a/Mage.Sets/src/mage/cards/f/FiligreeFracture.java b/Mage.Sets/src/mage/cards/f/FiligreeFracture.java index 5a9e5b1f52..98eb2eb6ed 100644 --- a/Mage.Sets/src/mage/cards/f/FiligreeFracture.java +++ b/Mage.Sets/src/mage/cards/f/FiligreeFracture.java @@ -61,9 +61,9 @@ class FiligreeFractureEffect extends OneShotEffect { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (player != null && permanent != null) { permanent.destroy(source.getSourceId(), game, true); - game.applyEffects(); + game.getState().processAction(game); if (permanent.getColor(game).isBlack() || permanent.getColor(game).isBlue()) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/f/FilthyCur.java b/Mage.Sets/src/mage/cards/f/FilthyCur.java index 9864aec012..e47525388a 100644 --- a/Mage.Sets/src/mage/cards/f/FilthyCur.java +++ b/Mage.Sets/src/mage/cards/f/FilthyCur.java @@ -23,7 +23,7 @@ public final class FilthyCur extends CardImpl { public FilthyCur(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/f/FinaleOfPromise.java b/Mage.Sets/src/mage/cards/f/FinaleOfPromise.java index 9ebf263dde..444affb50c 100644 --- a/Mage.Sets/src/mage/cards/f/FinaleOfPromise.java +++ b/Mage.Sets/src/mage/cards/f/FinaleOfPromise.java @@ -125,8 +125,8 @@ class FinaleOfPromiseEffect extends OneShotEffect { .filter(Objects::nonNull) .map(Card::getName) .collect(Collectors.joining(" -> ")); - if (!controller.chooseUse(Outcome.Detriment, "Cast cards by choose order: " - + cardsOrder + "?", "Finale of Promise", + if (!controller.chooseUse(Outcome.Detriment, "Cast cards by choose order: " + + cardsOrder + "?", "Finale of Promise", "Use that order", "Reverse", source, game)) { Collections.reverse(cardsToCast); } @@ -154,8 +154,7 @@ class FinaleOfPromiseEffect extends OneShotEffect { if (card != null) { Spell spell = game.getStack().getSpell(card.getId()); if (spell != null) { - spell.createCopyOnStack(game, source, controller.getId(), true); - spell.createCopyOnStack(game, source, controller.getId(), true); + spell.createCopyOnStack(game, source, controller.getId(), true, 2); game.informPlayers(controller.getLogName() + " copies " + spell.getName() + " twice."); } } diff --git a/Mage.Sets/src/mage/cards/f/FinaleOfRevelation.java b/Mage.Sets/src/mage/cards/f/FinaleOfRevelation.java index afaf5aee13..ff1251cdee 100644 --- a/Mage.Sets/src/mage/cards/f/FinaleOfRevelation.java +++ b/Mage.Sets/src/mage/cards/f/FinaleOfRevelation.java @@ -45,7 +45,7 @@ class FinaleOfRevelationEffect extends OneShotEffect { FinaleOfRevelationEffect() { super(Outcome.Benefit); staticText = "Draw X cards. If X is 10 or more, instead shuffle your graveyard into your library, " + - "draw X cards, untap up to five lands, and you have no maximum hand size for the rest of the game."; + "draw X cards, untap up to five lands, and you have no maximum hand size for the rest of the game"; } private FinaleOfRevelationEffect(final FinaleOfRevelationEffect effect) { @@ -66,11 +66,11 @@ class FinaleOfRevelationEffect extends OneShotEffect { int xValue = source.getManaCostsToPay().getX(); if (xValue < 10) { - player.drawCards(xValue, game); + player.drawCards(xValue, source.getSourceId(), game); } else { player.putCardsOnTopOfLibrary(player.getGraveyard(), game, source, false); player.shuffleLibrary(source, game); - player.drawCards(xValue, game); + player.drawCards(xValue, source.getSourceId(), game); new UntapLandsEffect(5).apply(game, source); game.addEffect(new MaximumHandSizeControllerEffect( Integer.MAX_VALUE, Duration.EndOfGame, diff --git a/Mage.Sets/src/mage/cards/f/FinishingBlow.java b/Mage.Sets/src/mage/cards/f/FinishingBlow.java new file mode 100644 index 0000000000..8376ef033d --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FinishingBlow.java @@ -0,0 +1,32 @@ +package mage.cards.f; + +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FinishingBlow extends CardImpl { + + public FinishingBlow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}"); + + // Destroy target creature or planeswalker. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private FinishingBlow(final FinishingBlow card) { + super(card); + } + + @Override + public FinishingBlow copy() { + return new FinishingBlow(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FireJuggler.java b/Mage.Sets/src/mage/cards/f/FireJuggler.java index 87cd678002..d2ede0bfae 100644 --- a/Mage.Sets/src/mage/cards/f/FireJuggler.java +++ b/Mage.Sets/src/mage/cards/f/FireJuggler.java @@ -3,7 +3,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.DoIfClashWonEffect; import mage.cards.CardImpl; @@ -30,7 +30,7 @@ public final class FireJuggler extends CardImpl { // Whenever Fire Juggler becomes blocked, clash with an opponent. If you win, Fire Juggler deals 4 damage to each creature blocking it. FilterPermanent filter = new FilterPermanent("each creature blocking it"); filter.add(new BlockingAttackerIdPredicate(this.getId())); - this.addAbility(new BecomesBlockedTriggeredAbility(new DoIfClashWonEffect(new DamageAllEffect(4,filter)),false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DoIfClashWonEffect(new DamageAllEffect(4,filter)),false)); } public FireJuggler(final FireJuggler card) { diff --git a/Mage.Sets/src/mage/cards/f/FireProphecy.java b/Mage.Sets/src/mage/cards/f/FireProphecy.java new file mode 100644 index 0000000000..8801cd8059 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FireProphecy.java @@ -0,0 +1,81 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FireProphecy extends CardImpl { + + public FireProphecy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Fire Prophecy deals 3 damage to target creature. You may put a card from your hand on the bottom of your library. If you do, draw a card. + this.getSpellAbility().addEffect(new DamageTargetEffect(3)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new FireProphecyEffect()); + } + + private FireProphecy(final FireProphecy card) { + super(card); + } + + @Override + public FireProphecy copy() { + return new FireProphecy(this); + } +} + +class FireProphecyEffect extends OneShotEffect { + + FireProphecyEffect() { + super(Outcome.Benefit); + staticText = "You may put a card from your hand on the bottom of your library. If you do, draw a card."; + } + + private FireProphecyEffect(final FireProphecyEffect effect) { + super(effect); + } + + @Override + public FireProphecyEffect copy() { + return new FireProphecyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null + || player.getHand().isEmpty() + || !player.chooseUse( + outcome, "Put a card from your hand " + + "on the bottom of your library?", source, game + )) { + return false; + } + TargetCard target = new TargetCardInHand(); + player.choose(outcome, player.getHand(), target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; + } + if (player.putCardsOnBottomOfLibrary(card, game, source, false)) { + player.drawCards(1, source.getSourceId(), game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FireSnake.java b/Mage.Sets/src/mage/cards/f/FireSnake.java index 2f6dca506d..1a04253391 100644 --- a/Mage.Sets/src/mage/cards/f/FireSnake.java +++ b/Mage.Sets/src/mage/cards/f/FireSnake.java @@ -4,7 +4,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class FireSnake extends CardImpl { this.toughness = new MageInt(1); // When Fire Snake dies, destroy target land. - Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), false); ability.addTarget(new TargetLandPermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FirebladeArtist.java b/Mage.Sets/src/mage/cards/f/FirebladeArtist.java index e11b412cc7..5568d06ee3 100644 --- a/Mage.Sets/src/mage/cards/f/FirebladeArtist.java +++ b/Mage.Sets/src/mage/cards/f/FirebladeArtist.java @@ -1,21 +1,18 @@ package mage.cards.f; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.SendOptionUsedEventEffect; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetOpponentOrPlaneswalker; @@ -38,15 +35,15 @@ public final class FirebladeArtist extends CardImpl { this.addAbility(HasteAbility.getInstance()); // At the beginning of your upkeep, you may sacrifice a creature. When you do, Fireblade Artist deals 2 damage to target opponent or planeswalker. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(2), false, + "{this} deals 2 damage to target opponent or planeswalker" + ); + ability.addTarget(new TargetOpponentOrPlaneswalker()); this.addAbility(new BeginningOfUpkeepTriggeredAbility( - new DoIfCostPaid( - new FirebladeArtistCreateReflexiveTriggerEffect(), - new SacrificeTargetCost(new TargetControlledPermanent( - StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT - )), "Sacrifice a creature to deal 2 damage to an opponent or planeswalker?" - ).setText("you may sacrifice a creature. When you do, " + - "{this} deals 2 damage to target opponent or planeswalker."), - TargetController.YOU, false + new DoWhenCostPaid(ability, new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + ), "Sacrifice a creature?"), TargetController.YOU, false )); } @@ -59,58 +56,3 @@ public final class FirebladeArtist extends CardImpl { return new FirebladeArtist(this); } } - -class FirebladeArtistCreateReflexiveTriggerEffect extends OneShotEffect { - - FirebladeArtistCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - private FirebladeArtistCreateReflexiveTriggerEffect(final FirebladeArtistCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public FirebladeArtistCreateReflexiveTriggerEffect copy() { - return new FirebladeArtistCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new FirebladeArtistReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class FirebladeArtistReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - FirebladeArtistReflexiveTriggeredAbility() { - super(new DamageTargetEffect(2), Duration.OneUse, true); - this.addTarget(new TargetOpponentOrPlaneswalker()); - } - - private FirebladeArtistReflexiveTriggeredAbility(final FirebladeArtistReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public FirebladeArtistReflexiveTriggeredAbility copy() { - return new FirebladeArtistReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "{this} deals 2 damage to target opponent or planeswalker."; - } -} diff --git a/Mage.Sets/src/mage/cards/f/FirecannonBlast.java b/Mage.Sets/src/mage/cards/f/FirecannonBlast.java index ae1daf3461..6e7a4d2aff 100644 --- a/Mage.Sets/src/mage/cards/f/FirecannonBlast.java +++ b/Mage.Sets/src/mage/cards/f/FirecannonBlast.java @@ -1,12 +1,12 @@ - package mage.cards.f; -import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.PlayerAttackedWatcher; @@ -22,14 +22,16 @@ public final class FirecannonBlast extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}{R}"); // Firecannon Blast deals 3 damage to target creature. - // Raid - Firecannon Blast deals 6 damage to that creature instead if you attacked with a creature this turn. + // Raid - Firecannon Blast deals 6 damage to that creature instead if you attacked this turn. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new DamageTargetEffect(6), new DamageTargetEffect(3), RaidCondition.instance, - "{this} deals 3 damage to target creature.
Raid — {this} deals 6 damage instead if you attacked with a creature this turn")); + "{this} deals 3 damage to target creature.
Raid — {this} deals 6 damage instead if you attacked this turn")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addWatcher(new PlayerAttackedWatcher()); + this.getSpellAbility().setAbilityWord(AbilityWord.RAID); + this.getSpellAbility().addHint(RaidHint.instance); } public FirecannonBlast(final FirecannonBlast card) { diff --git a/Mage.Sets/src/mage/cards/f/FirefluxSquad.java b/Mage.Sets/src/mage/cards/f/FirefluxSquad.java new file mode 100644 index 0000000000..85ac79b014 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FirefluxSquad.java @@ -0,0 +1,112 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FirefluxSquad extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another attacking creature you control"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(AttackingPredicate.instance); + } + + public FirefluxSquad(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Fireflux Squad attacks, you may exile another target attacking creature you control. If you do, reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield tapped and attacking and the rest on the bottom of your library in a random order. + Ability ability = new AttacksTriggeredAbility(new FirefluxSquadEffect(), true); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private FirefluxSquad(final FirefluxSquad card) { + super(card); + } + + @Override + public FirefluxSquad copy() { + return new FirefluxSquad(this); + } +} + +class FirefluxSquadEffect extends OneShotEffect { + + FirefluxSquadEffect() { + super(Outcome.Benefit); + staticText = "exile another target attacking creature you control. If you do, " + + "reveal cards from the top of your library until you reveal a creature card. " + + "Put that card onto the battlefield tapped and attacking " + + "and the rest on the bottom of your library in a random order."; + } + + private FirefluxSquadEffect(final FirefluxSquadEffect effect) { + super(effect); + } + + @Override + public FirefluxSquadEffect copy() { + return new FirefluxSquadEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (player == null || permanent == null) { + return false; + } + player.moveCards(permanent, Zone.EXILED, source, game); + Card toBattlefield = null; + Cards cards = new CardsImpl(); + for (Card card : player.getLibrary().getCards(game)) { + cards.add(card); + if (card != null && card.isCreature()) { + toBattlefield = card; + break; + } + } + player.revealCards(source, cards, game); + if (toBattlefield == null) { + return player.putCardsOnBottomOfLibrary(cards, game, source, false); + } + player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game, true, false, true, null); + permanent = game.getPermanent(toBattlefield.getId()); + if (permanent != null) { + cards.remove(toBattlefield); + game.getCombat().addAttackingCreature(permanent.getId(), game); + } + return player.putCardsOnBottomOfLibrary(cards, game, source, false); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/Firestorm.java b/Mage.Sets/src/mage/cards/f/Firestorm.java index 5e55fb73a0..39338f6e86 100644 --- a/Mage.Sets/src/mage/cards/f/Firestorm.java +++ b/Mage.Sets/src/mage/cards/f/Firestorm.java @@ -27,7 +27,8 @@ public final class Firestorm extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); // As an additional cost to cast Firestorm, discard X cards. - this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), true)); + this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), false)); + // Firestorm deals X damage to each of X target creatures and/or players. this.getSpellAbility().addEffect(new FirestormEffect()); this.getSpellAbility().setTargetAdjuster(FirestormAdjuster.instance); diff --git a/Mage.Sets/src/mage/cards/f/FlailingDrake.java b/Mage.Sets/src/mage/cards/f/FlailingDrake.java index 3083f1c918..db8dd6a855 100644 --- a/Mage.Sets/src/mage/cards/f/FlailingDrake.java +++ b/Mage.Sets/src/mage/cards/f/FlailingDrake.java @@ -4,7 +4,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.FlyingAbility; @@ -32,7 +32,7 @@ public final class FlailingDrake extends CardImpl { // Whenever Flailing Drake blocks or becomes blocked by a creature, that creature gets +1/+1 until end of turn. Effect effect = new BoostTargetEffect(+1, +1, Duration.EndOfTurn); effect.setText("that creature gets +1/+1 until end of turn"); - Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(effect, new FilterCreaturePermanent("a creature"), false, null, true); + Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, new FilterCreaturePermanent("a creature"), false, null, true); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FlameSpill.java b/Mage.Sets/src/mage/cards/f/FlameSpill.java new file mode 100644 index 0000000000..50707d477d --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FlameSpill.java @@ -0,0 +1,79 @@ +package mage.cards.f; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +import static mage.game.combat.CombatGroup.getLethalDamage; + +/** + * @author TheElk801 + */ +public final class FlameSpill extends CardImpl { + + public FlameSpill(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Flame Spill deals 4 damage to target creature. Excess damage is dealt to that creature's controller instead. + this.getSpellAbility().addEffect(new FlameSpillEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private FlameSpill(final FlameSpill card) { + super(card); + } + + @Override + public FlameSpill copy() { + return new FlameSpill(this); + } +} + +class FlameSpillEffect extends OneShotEffect { + + FlameSpillEffect() { + super(Outcome.Benefit); + staticText = "{this} deals 4 damage to target creature. " + + "Excess damage is dealt to that creature's controller instead."; + } + + private FlameSpillEffect(final FlameSpillEffect effect) { + super(effect); + } + + @Override + public FlameSpillEffect copy() { + return new FlameSpillEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + MageObject sourceObject = source.getSourceObject(game); + if (permanent == null || sourceObject == null) { + return false; + } + int lethal = getLethalDamage(permanent, game); + if (sourceObject.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { + lethal = Math.min(lethal, 1); + } + lethal = Math.min(lethal, 4); + permanent.damage(lethal, source.getSourceId(), game); + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null && lethal < 4) { + player.damage(4 - lethal, source.getSourceId(), game); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/FlameSweep.java b/Mage.Sets/src/mage/cards/f/FlameSweep.java index 5fb3d7b6c2..6b27948005 100644 --- a/Mage.Sets/src/mage/cards/f/FlameSweep.java +++ b/Mage.Sets/src/mage/cards/f/FlameSweep.java @@ -51,8 +51,6 @@ enum FlameSweepPredicate implements ObjectPlayerPredicate ability.getClass().equals(FlyingAbility.class) - )); + && object.getAbilities(game).containsClass(FlyingAbility.class)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/FlameWreathedPhoenix.java b/Mage.Sets/src/mage/cards/f/FlameWreathedPhoenix.java index e3a7203845..fda65cb18d 100644 --- a/Mage.Sets/src/mage/cards/f/FlameWreathedPhoenix.java +++ b/Mage.Sets/src/mage/cards/f/FlameWreathedPhoenix.java @@ -4,7 +4,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.TributeNotPaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; @@ -39,7 +39,7 @@ public final class FlameWreathedPhoenix extends CardImpl { this.addAbility(new TributeAbility(2)); // When Flame-Wreathed Phoenix enters the battlefield, if its tribute wasn't paid, it gains haste and "When this creature dies, return it to its owner's hand." TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield)); - Effect effect = new GainAbilitySourceEffect(new DiesTriggeredAbility(new ReturnToHandSourceEffect())); + Effect effect = new GainAbilitySourceEffect(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect())); ability.addEffect(effect); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TributeNotPaidCondition.instance, "When {this} enters the battlefield, if its tribute wasn't paid, it gains haste and \"When this creature dies, return it to its owner's hand.\"")); diff --git a/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java b/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java index 05ebb2bfe6..10fcf26864 100644 --- a/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java +++ b/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; @@ -36,7 +36,7 @@ public final class FlameheartWerewolf extends CardImpl { this.transformable = true; // Whenever Flameheart Werewolf blocks or becomes blocked by a creature, Flameheart Werewolf deals 2 damage to that creature. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new DamageTargetEffect(2, true, "that creature"), + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(2, true, "that creature"), StaticFilters.FILTER_PERMANENT_CREATURE, false, null, true)); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Flameheart Werewolf. diff --git a/Mage.Sets/src/mage/cards/f/FlamerushRider.java b/Mage.Sets/src/mage/cards/f/FlamerushRider.java index be2c67e9fd..2596f076f6 100644 --- a/Mage.Sets/src/mage/cards/f/FlamerushRider.java +++ b/Mage.Sets/src/mage/cards/f/FlamerushRider.java @@ -84,7 +84,7 @@ class FlamerushRiderEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true, 1, true, true); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java b/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java index 9db0cd3935..2b87176bb7 100644 --- a/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java +++ b/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java @@ -77,7 +77,7 @@ class FlameshadowConjuringEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = ((FixedTarget) getTargetPointer()).getTargetedPermanentOrLKIBattlefield(game); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true); effect.setTargetPointer(getTargetPointer()); diff --git a/Mage.Sets/src/mage/cards/f/FlawlessManeuver.java b/Mage.Sets/src/mage/cards/f/FlawlessManeuver.java new file mode 100644 index 0000000000..511d2639f9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FlawlessManeuver.java @@ -0,0 +1,42 @@ +package mage.cards.f; + +import mage.abilities.condition.common.ControlACommanderCondition; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FlawlessManeuver extends CardImpl { + + public FlawlessManeuver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // If you control a commander, you may cast this spell without paying its mana cost. + this.addAbility(new AlternativeCostSourceAbility(null, ControlACommanderCondition.instance)); + + // Creatures you control gain indestructible until end of turn. + this.getSpellAbility().addEffect(new GainAbilityControlledEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURES + )); + } + + private FlawlessManeuver(final FlawlessManeuver card) { + super(card); + } + + @Override + public FlawlessManeuver copy() { + return new FlawlessManeuver(this); + } +} +// outstanding move! diff --git a/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java b/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java index 105e7d16c0..c64ba61968 100644 --- a/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java +++ b/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java @@ -1,24 +1,17 @@ package mage.cards.f; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.SendOptionUsedEventEffect; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.cards.AdventureCard; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.token.BearToken; import mage.target.TargetPermanent; @@ -38,9 +31,13 @@ public final class FlaxenIntruder extends AdventureCard { this.toughness = new MageInt(2); // Whenever Flaxen Intruder deals combat damage to a player, you may sacrifice it. When you do, destroy target artifact or enchantment. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DoIfCostPaid( - new FlaxenIntruderCreateReflexiveTriggerEffect(), new SacrificeSourceCost() - ).setText("you may sacrifice it. When you do, destroy target artifact or enchantment."), false)); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DestroyTargetEffect(), false, "destroy target artifact or enchantment" + ); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DoWhenCostPaid( + ability, new SacrificeSourceCost(), "Sacrifice {this}?" + ), false)); // Welcome Home // Create three 2/2 green Bear creature tokens. @@ -56,58 +53,3 @@ public final class FlaxenIntruder extends AdventureCard { return new FlaxenIntruder(this); } } - -class FlaxenIntruderCreateReflexiveTriggerEffect extends OneShotEffect { - - FlaxenIntruderCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - private FlaxenIntruderCreateReflexiveTriggerEffect(final FlaxenIntruderCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public FlaxenIntruderCreateReflexiveTriggerEffect copy() { - return new FlaxenIntruderCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new FlaxenIntruderReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class FlaxenIntruderReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - FlaxenIntruderReflexiveTriggeredAbility() { - super(new DestroyTargetEffect(), Duration.OneUse, true); - this.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); - } - - private FlaxenIntruderReflexiveTriggeredAbility(final FlaxenIntruderReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public FlaxenIntruderReflexiveTriggeredAbility copy() { - return new FlaxenIntruderReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you do, destroy target artifact or enchantment."; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/FlayedNim.java b/Mage.Sets/src/mage/cards/f/FlayedNim.java index 336542943e..81c51ecdd1 100644 --- a/Mage.Sets/src/mage/cards/f/FlayedNim.java +++ b/Mage.Sets/src/mage/cards/f/FlayedNim.java @@ -67,7 +67,7 @@ class FlayedNimEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent creature = ((FixedTarget) targetPointer).getTargetedPermanentOrLKIBattlefield(game); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (creature == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java b/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java index b22d7cd689..c25c1cc6fb 100644 --- a/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java +++ b/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java @@ -73,7 +73,7 @@ class FlayerTriggeredAbility extends TriggeredAbilityImpl { if (permanent != null && ((EntersTheBattlefieldEvent) event).getFromZone() == Zone.GRAVEYARD && permanent.isOwnedBy(controllerId) - && permanent.isCreature()) { + && (permanent.isCreature() || permanent.getId().equals(getSourceId()))) { Effect effect = this.getEffects().get(0); effect.setValue("damageSource", event.getTargetId()); return true; diff --git a/Mage.Sets/src/mage/cards/f/FleshAllergy.java b/Mage.Sets/src/mage/cards/f/FleshAllergy.java index 7ac5f4227f..9134edaaf4 100644 --- a/Mage.Sets/src/mage/cards/f/FleshAllergy.java +++ b/Mage.Sets/src/mage/cards/f/FleshAllergy.java @@ -101,7 +101,7 @@ class FleshAllergyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { FleshAllergyWatcher watcher = game.getState().getWatcher(FleshAllergyWatcher.class); - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null && watcher != null) { Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/f/FleshCarver.java b/Mage.Sets/src/mage/cards/f/FleshCarver.java index 96e7e22f3a..1ef0f7f28a 100644 --- a/Mage.Sets/src/mage/cards/f/FleshCarver.java +++ b/Mage.Sets/src/mage/cards/f/FleshCarver.java @@ -4,7 +4,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -63,7 +63,7 @@ public final class FleshCarver extends CardImpl { } } -class FleshCarverAbility extends DiesTriggeredAbility { +class FleshCarverAbility extends DiesSourceTriggeredAbility { public FleshCarverAbility() { super(new FleshCarverEffect(), false); diff --git a/Mage.Sets/src/mage/cards/f/Flicker.java b/Mage.Sets/src/mage/cards/f/Flicker.java index dd16b1d1cb..baa9add859 100644 --- a/Mage.Sets/src/mage/cards/f/Flicker.java +++ b/Mage.Sets/src/mage/cards/f/Flicker.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; import mage.cards.CardImpl; @@ -12,26 +10,27 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Flicker extends CardImpl { - + private static final FilterPermanent filter = new FilterPermanent("nontoken permanent"); - + static { filter.add(Predicates.not(TokenPredicate.instance)); } - + public Flicker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}"); // Exile target nontoken permanent, then return it to the battlefield under its owner's control. this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addEffect(new ExileTargetForSourceEffect()); - this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); - + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); + } public Flicker(final Flicker card) { diff --git a/Mage.Sets/src/mage/cards/f/FlickerOfFate.java b/Mage.Sets/src/mage/cards/f/FlickerOfFate.java index 4cd69eb49a..f28e304584 100644 --- a/Mage.Sets/src/mage/cards/f/FlickerOfFate.java +++ b/Mage.Sets/src/mage/cards/f/FlickerOfFate.java @@ -30,7 +30,7 @@ public final class FlickerOfFate extends CardImpl { // Exile target creature or enchantment, then return it to the battlefield under its owner's control. this.getSpellAbility().addEffect(new ExileTargetForSourceEffect()); - this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false)); + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); this.getSpellAbility().addTarget(new TargetPermanent(filter)); } diff --git a/Mage.Sets/src/mage/cards/f/Flickerwisp.java b/Mage.Sets/src/mage/cards/f/Flickerwisp.java index b4af63cae7..c67632972d 100644 --- a/Mage.Sets/src/mage/cards/f/Flickerwisp.java +++ b/Mage.Sets/src/mage/cards/f/Flickerwisp.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -13,8 +11,8 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.permanent.AnotherPredicate; @@ -24,8 +22,9 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author Plopman */ public final class Flickerwisp extends CardImpl { @@ -81,7 +80,7 @@ class FlickerwispEffect extends OneShotEffect { if (controller != null && permanent != null && sourcePermanent != null) { if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/f/FlightSpellbomb.java b/Mage.Sets/src/mage/cards/f/FlightSpellbomb.java index 2d5368f786..35162b4f3a 100644 --- a/Mage.Sets/src/mage/cards/f/FlightSpellbomb.java +++ b/Mage.Sets/src/mage/cards/f/FlightSpellbomb.java @@ -4,7 +4,7 @@ package mage.cards.f; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -32,7 +32,7 @@ public final class FlightSpellbomb extends CardImpl { ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); - this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{U}")), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{U}")), false)); } public FlightSpellbomb (final FlightSpellbomb card) { diff --git a/Mage.Sets/src/mage/cards/f/FlintGolem.java b/Mage.Sets/src/mage/cards/f/FlintGolem.java index f78c9e2e67..f8377e4402 100644 --- a/Mage.Sets/src/mage/cards/f/FlintGolem.java +++ b/Mage.Sets/src/mage/cards/f/FlintGolem.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -11,7 +10,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -23,7 +21,7 @@ import mage.players.Player; public final class FlintGolem extends CardImpl { public FlintGolem(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); this.subtype.add(SubType.GOLEM); this.power = new MageInt(2); this.toughness = new MageInt(3); @@ -46,7 +44,7 @@ class FlintGolemEffect extends OneShotEffect { public FlintGolemEffect() { super(Outcome.Detriment); - this.staticText = "defending player puts the top three cards of their library into their graveyard"; + this.staticText = "defending player mills three cards"; } public FlintGolemEffect(final FlintGolemEffect effect) { @@ -64,7 +62,7 @@ class FlintGolemEffect extends OneShotEffect { if (blockingCreature != null) { Player opponent = game.getPlayer(blockingCreature.getControllerId()); if (opponent != null) { - opponent.moveCards(opponent.getLibrary().getTopCards(game, 3), Zone.GRAVEYARD, source, game); + opponent.millCards(3, source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/f/FloatingDreamZubera.java b/Mage.Sets/src/mage/cards/f/FloatingDreamZubera.java index f2e77e0b50..ad4c7fd252 100644 --- a/Mage.Sets/src/mage/cards/f/FloatingDreamZubera.java +++ b/Mage.Sets/src/mage/cards/f/FloatingDreamZubera.java @@ -4,7 +4,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.ZuberasDiedDynamicValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class FloatingDreamZubera extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(ZuberasDiedDynamicValue.instance)), new ZuberasDiedWatcher()); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(ZuberasDiedDynamicValue.instance)), new ZuberasDiedWatcher()); } public FloatingDreamZubera(final FloatingDreamZubera card) { diff --git a/Mage.Sets/src/mage/cards/f/FlourishingFox.java b/Mage.Sets/src/mage/cards/f/FlourishingFox.java new file mode 100644 index 0000000000..11b3a45add --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FlourishingFox.java @@ -0,0 +1,45 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FlourishingFox extends CardImpl { + + public FlourishingFox(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.FOX); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever you cycle another card, put a +1/+1 counter on Flourishing Fox. + this.addAbility(new CycleControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, true + )); + + // Cycling {1} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}"))); + } + + private FlourishingFox(final FlourishingFox card) { + super(card); + } + + @Override + public FlourishingFox copy() { + return new FlourishingFox(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/Fluctuator.java b/Mage.Sets/src/mage/cards/f/Fluctuator.java index c0a1e52d08..29433b881d 100644 --- a/Mage.Sets/src/mage/cards/f/Fluctuator.java +++ b/Mage.Sets/src/mage/cards/f/Fluctuator.java @@ -1,8 +1,5 @@ package mage.cards.f; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -16,8 +13,11 @@ import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Fluctuator extends CardImpl { @@ -69,7 +69,7 @@ class FluctuatorEffect extends CostModificationEffectImpl { } if (reduceMax > 0) { int reduce; - if (game.inCheckPlayableState()) { + if (game.inCheckPlayableState() || !controller.isHuman()) { reduce = reduceMax; } else { ChoiceImpl choice = new ChoiceImpl(true); diff --git a/Mage.Sets/src/mage/cards/f/Flux.java b/Mage.Sets/src/mage/cards/f/Flux.java index 8c981be444..21be11d74d 100644 --- a/Mage.Sets/src/mage/cards/f/Flux.java +++ b/Mage.Sets/src/mage/cards/f/Flux.java @@ -61,7 +61,7 @@ class FluxEffect extends OneShotEffect { if (player != null) { int numToDiscard = player.getAmount(0, player.getHand().size(), "Discard how many cards?", game); player.discard(numToDiscard, false, source, game); - player.drawCards(numToDiscard, game); + player.drawCards(numToDiscard, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/f/FlycatcherGiraffid.java b/Mage.Sets/src/mage/cards/f/FlycatcherGiraffid.java new file mode 100644 index 0000000000..3329d84e23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FlycatcherGiraffid.java @@ -0,0 +1,41 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.common.counter.AddCounterChoiceSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FlycatcherGiraffid extends CardImpl { + + public FlycatcherGiraffid(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.ANTELOPE); + this.subtype.add(SubType.LIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Flycatcher Giraffid enters the battlefield with your choice of a vigilance counter or a reach counter on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCounterChoiceSourceEffect(CounterType.VIGILANCE, CounterType.REACH) + )); + } + + private FlycatcherGiraffid(final FlycatcherGiraffid card) { + super(card); + } + + @Override + public FlycatcherGiraffid copy() { + return new FlycatcherGiraffid(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FoeRazerRegent.java b/Mage.Sets/src/mage/cards/f/FoeRazerRegent.java index efad58f974..a6375acc09 100644 --- a/Mage.Sets/src/mage/cards/f/FoeRazerRegent.java +++ b/Mage.Sets/src/mage/cards/f/FoeRazerRegent.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; @@ -16,10 +14,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -27,18 +24,13 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class FoeRazerRegent extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public FoeRazerRegent(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}"); this.subtype.add(SubType.DRAGON); @@ -50,14 +42,14 @@ public final class FoeRazerRegent extends CardImpl { // When Foe-Razer Regent enters the battlefield, you may have it fight target creature you don't control. Ability ability = new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect(), true); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); // Whenever a creature you control fights, put two +1/+1 counters on it at the beginning of the next end step. this.addAbility(new FoeRazerRegentTriggeredAbility()); } - public FoeRazerRegent(final FoeRazerRegent card) { + private FoeRazerRegent(final FoeRazerRegent card) { super(card); } @@ -69,11 +61,11 @@ public final class FoeRazerRegent extends CardImpl { class FoeRazerRegentTriggeredAbility extends TriggeredAbilityImpl { - public FoeRazerRegentTriggeredAbility() { + FoeRazerRegentTriggeredAbility() { super(Zone.BATTLEFIELD, new CreateDelayedTriggeredAbilityEffect(new FoeRazerRegentDelayedTriggeredAbility(), true), false); } - public FoeRazerRegentTriggeredAbility(final FoeRazerRegentTriggeredAbility ability) { + private FoeRazerRegentTriggeredAbility(final FoeRazerRegentTriggeredAbility ability) { super(ability); } @@ -107,11 +99,11 @@ class FoeRazerRegentTriggeredAbility extends TriggeredAbilityImpl { class FoeRazerRegentDelayedTriggeredAbility extends DelayedTriggeredAbility { - public FoeRazerRegentDelayedTriggeredAbility() { + FoeRazerRegentDelayedTriggeredAbility() { super(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2))); } - public FoeRazerRegentDelayedTriggeredAbility(final FoeRazerRegentDelayedTriggeredAbility ability) { + private FoeRazerRegentDelayedTriggeredAbility(final FoeRazerRegentDelayedTriggeredAbility ability) { super(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FogPatch.java b/Mage.Sets/src/mage/cards/f/FogPatch.java index 7c62a88119..dc9decca0d 100644 --- a/Mage.Sets/src/mage/cards/f/FogPatch.java +++ b/Mage.Sets/src/mage/cards/f/FogPatch.java @@ -1,22 +1,22 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.BecomeBlockedTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.PhaseStep; +import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.combat.CombatGroup; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; /** - * * @author L_J */ public final class FogPatch extends CardImpl { @@ -25,13 +25,16 @@ public final class FogPatch extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Cast Fog Patch only during the declare blockers step. - this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, PhaseStep.DECLARE_BLOCKERS, null, "Cast this spell only during the declare blockers step")); + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility( + null, PhaseStep.DECLARE_BLOCKERS, null, + "Cast this spell only during the declare blockers step" + )); // Attacking creatures become blocked. this.getSpellAbility().addEffect(new FogPatchEffect()); } - public FogPatch(final FogPatch card) { + private FogPatch(final FogPatch card) { super(card); } @@ -43,12 +46,12 @@ public final class FogPatch extends CardImpl { class FogPatchEffect extends OneShotEffect { - public FogPatchEffect() { + FogPatchEffect() { super(Outcome.Benefit); this.staticText = "Attacking creatures become blocked"; } - public FogPatchEffect(final FogPatchEffect effect) { + private FogPatchEffect(final FogPatchEffect effect) { super(effect); } @@ -59,19 +62,10 @@ class FogPatchEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID attackers : game.getCombat().getAttackers()) { - Permanent attacker = game.getPermanent(attackers); - if (attacker != null) { - CombatGroup combatGroup = game.getCombat().findGroup(attacker.getId()); - if (combatGroup != null) { - combatGroup.setBlocked(true); - } - } - } - return true; - } - return false; + Effect effect = new BecomeBlockedTargetEffect(); + effect.setTargetPointer(new FixedTargets(game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_ATTACKING_CREATURES, source.getSourceId(), game + ), game)); + return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/f/FolioOfFancies.java b/Mage.Sets/src/mage/cards/f/FolioOfFancies.java index 191fe2c06d..1f81cc0be1 100644 --- a/Mage.Sets/src/mage/cards/f/FolioOfFancies.java +++ b/Mage.Sets/src/mage/cards/f/FolioOfFancies.java @@ -9,18 +9,16 @@ import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardAllEffect; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; import mage.game.Game; import mage.players.Player; -import java.util.Collection; -import java.util.Objects; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 @@ -63,8 +61,7 @@ class FolioOfFanciesEffect extends OneShotEffect { FolioOfFanciesEffect() { super(Outcome.Benefit); - staticText = "Each opponent puts a number of cards equal to the number of cards in their hand " + - "from the top of their library into their graveyard."; + staticText = "each opponent mills cards equal to the number of cards in their hand"; } private FolioOfFanciesEffect(final FolioOfFanciesEffect effect) { @@ -82,14 +79,13 @@ class FolioOfFanciesEffect extends OneShotEffect { if (controller == null) { return false; } - Set cards = game.getOpponents(source.getControllerId()) - .stream() - .map(game::getPlayer) - .filter(Objects::nonNull) - .filter(player -> !player.getHand().isEmpty()) - .map(player -> player.getLibrary().getTopCards(game, player.getHand().size())) - .flatMap(Collection::stream) - .collect(Collectors.toSet()); - return controller.moveCards(cards, Zone.GRAVEYARD, source, game); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + player.millCards(player.getHand().size(), source, game); + } + return true; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/FootfallCrater.java b/Mage.Sets/src/mage/cards/f/FootfallCrater.java new file mode 100644 index 0000000000..1c82ac48fd --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FootfallCrater.java @@ -0,0 +1,66 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetLandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FootfallCrater extends CardImpl { + + public FootfallCrater(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); + + this.subtype.add(SubType.AURA); + + // Enchant land + TargetPermanent auraTarget = new TargetLandPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted land has "{T}: Target creature gains trample and haste until end of turn." + ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ).setText("Target creature gains trample"), new TapSourceCost()); + ability.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setText("and haste until end of turn")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + ability, AttachmentType.AURA, Duration.WhileOnBattlefield, + "enchanted land has \"{T}: Target creature gains trample and haste until end of turn.\"" + ))); + + // Cycling {1} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}"))); + } + + private FootfallCrater(final FootfallCrater card) { + super(card); + } + + @Override + public FootfallCrater copy() { + return new FootfallCrater(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FootlightFiend.java b/Mage.Sets/src/mage/cards/f/FootlightFiend.java index 6d7d41f979..08d0071347 100644 --- a/Mage.Sets/src/mage/cards/f/FootlightFiend.java +++ b/Mage.Sets/src/mage/cards/f/FootlightFiend.java @@ -2,7 +2,7 @@ package mage.cards.f; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class FootlightFiend extends CardImpl { this.toughness = new MageInt(1); // When Footlight Fiend dies, it deals 1 damage to any target. - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(1, "it")); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(1, "it")); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/ForbiddenFriendship.java b/Mage.Sets/src/mage/cards/f/ForbiddenFriendship.java new file mode 100644 index 0000000000..e1e0487a66 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/ForbiddenFriendship.java @@ -0,0 +1,34 @@ +package mage.cards.f; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.DinosaurHasteToken; +import mage.game.permanent.token.HumanSoldierToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ForbiddenFriendship extends CardImpl { + + public ForbiddenFriendship(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); + + // Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token. + this.getSpellAbility().addEffect(new CreateTokenEffect(new DinosaurHasteToken())); + this.getSpellAbility().addEffect(new CreateTokenEffect(new HumanSoldierToken()) + .setText("and a 1/1 white Human Soldier creature token")); + } + + private ForbiddenFriendship(final ForbiddenFriendship card) { + super(card); + } + + @Override + public ForbiddenFriendship copy() { + return new ForbiddenFriendship(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/ForbiddenOrchard.java b/Mage.Sets/src/mage/cards/f/ForbiddenOrchard.java index ebf3b78537..65895bb145 100644 --- a/Mage.Sets/src/mage/cards/f/ForbiddenOrchard.java +++ b/Mage.Sets/src/mage/cards/f/ForbiddenOrchard.java @@ -86,7 +86,7 @@ class ForbiddenOrchardTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getSourceId().equals(getSourceId()); + return event.getSourceId().equals(getSourceId()) && !game.inCheckPlayableState(); } @Override diff --git a/Mage.Sets/src/mage/cards/f/ForceDenial.java b/Mage.Sets/src/mage/cards/f/ForceDenial.java index 18b9a9d074..d1d54761c1 100644 --- a/Mage.Sets/src/mage/cards/f/ForceDenial.java +++ b/Mage.Sets/src/mage/cards/f/ForceDenial.java @@ -28,13 +28,13 @@ public final class ForceDenial extends CardImpl { this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new CounterUnlessPaysEffect(new GenericManaCost(1)), new InvertCondition(HateCondition.instance), - "Counter target spell unless its controller pays {1}.")); + "Counter target spell unless its controller pays {1}")); // Hate — If an opponent lost life from a source other then combat damage this turn, counter that spell instead. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new CounterTargetEffect(), HateCondition.instance, - "
Hate — If an opponent lost life from a source other than combat damage this turn, counter that spell instead.")); + "
Hate — If an opponent lost life from a source other than combat damage this turn, counter that spell instead")); this.getSpellAbility().addTarget(new TargetSpell()); this.getSpellAbility().addWatcher(new LifeLossOtherFromCombatWatcher()); diff --git a/Mage.Sets/src/mage/cards/f/ForceProjection.java b/Mage.Sets/src/mage/cards/f/ForceProjection.java index 6770afe6c7..d696d95331 100644 --- a/Mage.Sets/src/mage/cards/f/ForceProjection.java +++ b/Mage.Sets/src/mage/cards/f/ForceProjection.java @@ -68,7 +68,7 @@ class ForceProjectionEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && permanent != null) { // Create a token that is a copy of target creature CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); diff --git a/Mage.Sets/src/mage/cards/f/Foresee.java b/Mage.Sets/src/mage/cards/f/Foresee.java index 52b112e3a0..d081d3acc7 100644 --- a/Mage.Sets/src/mage/cards/f/Foresee.java +++ b/Mage.Sets/src/mage/cards/f/Foresee.java @@ -16,8 +16,9 @@ public final class Foresee extends CardImpl { public Foresee(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); - this.getSpellAbility().addEffect(new ScryEffect(4).setText("scry 4,")); - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText("then draw two cards")); + // Scry 4, then draw two cards. (To scry 4, look at the top four cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.) + this.getSpellAbility().addEffect(new ScryEffect(4, false)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).concatBy(", then")); } public Foresee(final Foresee card) { diff --git a/Mage.Sets/src/mage/cards/f/Foreshadow.java b/Mage.Sets/src/mage/cards/f/Foreshadow.java index 01fabc683e..2ac311b6f3 100644 --- a/Mage.Sets/src/mage/cards/f/Foreshadow.java +++ b/Mage.Sets/src/mage/cards/f/Foreshadow.java @@ -11,7 +11,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; @@ -49,12 +48,12 @@ public final class Foreshadow extends CardImpl { class ForeshadowEffect extends OneShotEffect { - public ForeshadowEffect() { + ForeshadowEffect() { super(Outcome.DrawCard); - this.staticText = "target opponent puts the top card of their library into their graveyard. If that card has the chosen name, you draw a card"; + this.staticText = "target opponent mills a card. If that card has the chosen name, you draw a card"; } - public ForeshadowEffect(final ForeshadowEffect effect) { + private ForeshadowEffect(final ForeshadowEffect effect) { super(effect); } @@ -68,17 +67,16 @@ class ForeshadowEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(source.getFirstTarget()); String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - if (controller != null && targetPlayer != null && cardName != null && !cardName.isEmpty()) { - Card card = targetPlayer.getLibrary().getFromTop(game); - if (card != null) { - controller.moveCards(card, Zone.GRAVEYARD, source, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { - controller.drawCards(1, game); - } - } - return true; + if (controller == null || targetPlayer == null || cardName == null || cardName.isEmpty()) { + return false; } - return false; + for (Card card : targetPlayer.millCards(1, source, game).getCards(game)) { + if (CardUtil.haveSameNames(card, cardName, game)) { + controller.drawCards(1, source.getSourceId(), game); + break; + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/f/Foresight.java b/Mage.Sets/src/mage/cards/f/Foresight.java index 296793c6bb..15f1bb3a7d 100644 --- a/Mage.Sets/src/mage/cards/f/Foresight.java +++ b/Mage.Sets/src/mage/cards/f/Foresight.java @@ -48,7 +48,7 @@ class ForesightEffect extends SearchEffect { ForesightEffect() { super(new TargetCardInLibrary(3, new FilterCard()), Outcome.Benefit); - staticText = "Search your library for three cards, exile them, then shuffle your library."; + staticText = "Search your library for three cards, exile them, then shuffle your library"; } ForesightEffect(final ForesightEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/ForeverYoung.java b/Mage.Sets/src/mage/cards/f/ForeverYoung.java index 3ffce98390..144e81ebf0 100644 --- a/Mage.Sets/src/mage/cards/f/ForeverYoung.java +++ b/Mage.Sets/src/mage/cards/f/ForeverYoung.java @@ -51,7 +51,7 @@ class ForeverYoungEffect extends OneShotEffect { ForeverYoungEffect() { super(Outcome.Benefit); - staticText = "Put any number of target creature cards from your graveyard on top of your library."; + staticText = "Put any number of target creature cards from your graveyard on top of your library"; } private ForeverYoungEffect(final ForeverYoungEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/Forget.java b/Mage.Sets/src/mage/cards/f/Forget.java index eb1030c7da..43b8b685ea 100644 --- a/Mage.Sets/src/mage/cards/f/Forget.java +++ b/Mage.Sets/src/mage/cards/f/Forget.java @@ -57,7 +57,7 @@ class ForgetEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(source.getFirstTarget()); if (targetPlayer != null) { - targetPlayer.drawCards(targetPlayer.discard(2, false, source, game).size(), game); + targetPlayer.drawCards(targetPlayer.discard(2, false, source, game).size(), source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/f/ForgottenCreation.java b/Mage.Sets/src/mage/cards/f/ForgottenCreation.java index 8fb1b4587d..fd93164daf 100644 --- a/Mage.Sets/src/mage/cards/f/ForgottenCreation.java +++ b/Mage.Sets/src/mage/cards/f/ForgottenCreation.java @@ -67,7 +67,7 @@ class ForgottenCreationEffect extends OneShotEffect { if (controller != null) { int cardsInHand = controller.getHand().size(); controller.discard(cardsInHand, false, source, game); - controller.drawCards(cardsInHand, game); + controller.drawCards(cardsInHand, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/f/ForgottenSentinel.java b/Mage.Sets/src/mage/cards/f/ForgottenSentinel.java new file mode 100644 index 0000000000..cfdcd62362 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/ForgottenSentinel.java @@ -0,0 +1,36 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ForgottenSentinel extends CardImpl { + + public ForgottenSentinel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + + this.subtype.add(SubType.GOLEM); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Forgotten Sentinel enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + } + + private ForgottenSentinel(final ForgottenSentinel card) { + super(card); + } + + @Override + public ForgottenSentinel copy() { + return new ForgottenSentinel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/ForkedBolt.java b/Mage.Sets/src/mage/cards/f/ForkedBolt.java index cf57dc2b75..83db5716c5 100644 --- a/Mage.Sets/src/mage/cards/f/ForkedBolt.java +++ b/Mage.Sets/src/mage/cards/f/ForkedBolt.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; @@ -9,22 +7,21 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetAnyTargetAmount; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class ForkedBolt extends CardImpl { public ForkedBolt(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); // Forked Bolt deals 2 damage divided as you choose among one or two target creatures and/or players. Effect effect = new DamageMultiEffect(2); effect.setText("{this} deals 2 damage divided as you choose among one or two target creatures and/or players"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); - } public ForkedBolt(final ForkedBolt card) { diff --git a/Mage.Sets/src/mage/cards/f/ForsakenDrifters.java b/Mage.Sets/src/mage/cards/f/ForsakenDrifters.java index cf624e8dce..822954776d 100644 --- a/Mage.Sets/src/mage/cards/f/ForsakenDrifters.java +++ b/Mage.Sets/src/mage/cards/f/ForsakenDrifters.java @@ -3,7 +3,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class ForsakenDrifters extends CardImpl { this.toughness = new MageInt(2); // When Forsaken Drifters dies, put the top four cards of your library into your graveyard. - this.addAbility(new DiesTriggeredAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(4))); + this.addAbility(new DiesSourceTriggeredAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(4))); } public ForsakenDrifters(final ForsakenDrifters card) { diff --git a/Mage.Sets/src/mage/cards/f/FoulTongueShriek.java b/Mage.Sets/src/mage/cards/f/FoulTongueShriek.java index fda2829170..725a760e57 100644 --- a/Mage.Sets/src/mage/cards/f/FoulTongueShriek.java +++ b/Mage.Sets/src/mage/cards/f/FoulTongueShriek.java @@ -1,28 +1,26 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.AttackingFilterCreatureCount; +import mage.abilities.dynamicvalue.common.AttackingCreatureCount; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class FoulTongueShriek extends CardImpl { public FoulTongueShriek(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // Target opponent loses 1 life for each attacking creature you control. You gain that much life. this.getSpellAbility().addEffect(new FoulTongueShriekEffect()); @@ -41,12 +39,7 @@ public final class FoulTongueShriek extends CardImpl { } class FoulTongueShriekEffect extends OneShotEffect { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - + public FoulTongueShriekEffect() { super(Outcome.Benefit); this.staticText = "Target opponent loses 1 life for each attacking creature you control. You gain that much life"; @@ -66,7 +59,7 @@ class FoulTongueShriekEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Player targetOpponent = game.getPlayer(getTargetPointer().getFirst(game, source)); if (controller != null && targetOpponent != null) { - int amount = new AttackingFilterCreatureCount(filter).calculate(game, source, this); + int amount = new AttackingCreatureCount(StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED).calculate(game, source, this); if (amount > 0) { targetOpponent.loseLife(amount, game, false); controller.gainLife(amount, game, source); diff --git a/Mage.Sets/src/mage/cards/f/FracturingGust.java b/Mage.Sets/src/mage/cards/f/FracturingGust.java index c54bb8b1f1..4da56870ae 100644 --- a/Mage.Sets/src/mage/cards/f/FracturingGust.java +++ b/Mage.Sets/src/mage/cards/f/FracturingGust.java @@ -67,7 +67,7 @@ class FracturingGustDestroyEffect extends OneShotEffect { ++destroyedPermanents; } } - game.applyEffects(); // needed in case a destroyed permanent did prevent life gain + game.getState().processAction(game); // needed in case a destroyed permanent did prevent life gain if (destroyedPermanents > 0) { controller.gainLife(2 * destroyedPermanents, game, source); } diff --git a/Mage.Sets/src/mage/cards/f/FranticInventory.java b/Mage.Sets/src/mage/cards/f/FranticInventory.java new file mode 100644 index 0000000000..7ab40c6030 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FranticInventory.java @@ -0,0 +1,44 @@ +package mage.cards.f; + +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FranticInventory extends CardImpl { + + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(new NamePredicate("Frantic Inventory")); + } + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(filter); + + public FranticInventory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(xValue) + .setText(", then draw cards equal to the number of cards named Frantic Inventory in your graveyard")); + } + + private FranticInventory(final FranticInventory card) { + super(card); + } + + @Override + public FranticInventory copy() { + return new FranticInventory(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FrayingSanity.java b/Mage.Sets/src/mage/cards/f/FrayingSanity.java index 56f444ed24..92ea0d9933 100644 --- a/Mage.Sets/src/mage/cards/f/FrayingSanity.java +++ b/Mage.Sets/src/mage/cards/f/FrayingSanity.java @@ -83,7 +83,7 @@ class FrayingSanityTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "At the beginning of each end step, enchanted player puts the top X cards of their library into their graveyard, where X is the total number of cards put into their graveyard from anywhere this turn."; + return "At the beginning of each end step, enchanted player mills X cards, where X is the total number of cards put into their graveyard from anywhere this turn."; } } @@ -117,8 +117,8 @@ class FrayingSanityEffect extends OneShotEffect { if (watcher != null) { xAmount = watcher.getAmountCardsPutToGraveyard(enchantedPlayer.getId()); } - Set topXCardsFromLibrary = enchantedPlayer.getLibrary().getTopCards(game, xAmount); - return enchantedPlayer.moveCards(topXCardsFromLibrary, Zone.GRAVEYARD, source, game, false, false, true, null); + enchantedPlayer.millCards(xAmount, source, game); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/f/FrenzySliver.java b/Mage.Sets/src/mage/cards/f/FrenzySliver.java index 5f3772a05a..cd6dd8ce27 100644 --- a/Mage.Sets/src/mage/cards/f/FrenzySliver.java +++ b/Mage.Sets/src/mage/cards/f/FrenzySliver.java @@ -45,7 +45,7 @@ public final class FrenzySliver extends CardImpl { // All Sliver creatures have frenzy 1. (Whenever a Sliver attacks and isn't blocked, it gets +1/+0 until end of turn.) this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( new FrenzyAbility(), Duration.WhileOnBattlefield, - filter, "All Sliver creatures have frenzy 1. (Whenever a Sliver attacks and isn't blocked, it gets +1/+0 until end of turn.)\""))); + filter, "All Sliver creatures have frenzy 1. (Whenever a Sliver attacks and isn't blocked, it gets +1/+0 until end of turn.)"))); } public FrenzySliver(final FrenzySliver card) { diff --git a/Mage.Sets/src/mage/cards/f/FreyaliseSupplicant.java b/Mage.Sets/src/mage/cards/f/FreyaliseSupplicant.java index 3a0d18ab5c..92d53fad91 100644 --- a/Mage.Sets/src/mage/cards/f/FreyaliseSupplicant.java +++ b/Mage.Sets/src/mage/cards/f/FreyaliseSupplicant.java @@ -1,61 +1,61 @@ -package mage.cards.f; - -import java.util.UUID; -import mage.MageInt; -import mage.ObjectColor; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.dynamicvalue.common.HalfValue; -import mage.abilities.dynamicvalue.common.SacrificeCostCreaturesPower; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.target.common.TargetAnyTarget; -import mage.target.common.TargetControlledCreaturePermanent; - -/** - * - * @author jeffwadsworth - */ -public final class FreyaliseSupplicant extends CardImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("red or white creature"); - - static { - filter.add(Predicates.or(new ColorPredicate(ObjectColor.RED), - new ColorPredicate(ObjectColor.WHITE))); - } - - public FreyaliseSupplicant(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); - - this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.CLERIC); - this.power = new MageInt(1); - this.toughness = new MageInt(1); - - // {tap}, Sacrifice a red or white creature: Freyalise Supplicant deals damage to any target equal to half the sacrificed creature's power, rounded down. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new HalfValue(SacrificeCostCreaturesPower.instance, false)), new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(filter))); - ability.addTarget(new TargetAnyTarget()); - this.addAbility(ability); - - } - - private FreyaliseSupplicant(final FreyaliseSupplicant card) { - super(card); - } - - @Override - public FreyaliseSupplicant copy() { - return new FreyaliseSupplicant(this); - } -} +package mage.cards.f; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.HalfValue; +import mage.abilities.dynamicvalue.common.SacrificeCostCreaturesPower; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author jeffwadsworth + */ +public final class FreyaliseSupplicant extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("red or white creature"); + + static { + filter.add(Predicates.or(new ColorPredicate(ObjectColor.RED), + new ColorPredicate(ObjectColor.WHITE))); + } + + public FreyaliseSupplicant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {tap}, Sacrifice a red or white creature: Freyalise Supplicant deals damage to any target equal to half the sacrificed creature's power, rounded down. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new HalfValue(SacrificeCostCreaturesPower.instance, false)), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(filter))); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + + } + + private FreyaliseSupplicant(final FreyaliseSupplicant card) { + super(card); + } + + @Override + public FreyaliseSupplicant copy() { + return new FreyaliseSupplicant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FreyalisesWinds.java b/Mage.Sets/src/mage/cards/f/FreyalisesWinds.java index 3065e087af..1db467c484 100644 --- a/Mage.Sets/src/mage/cards/f/FreyalisesWinds.java +++ b/Mage.Sets/src/mage/cards/f/FreyalisesWinds.java @@ -1,92 +1,92 @@ -package mage.cards.f; - -import mage.abilities.Ability; -import mage.abilities.common.BecomesTappedTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class FreyalisesWinds extends CardImpl { - - public FreyalisesWinds(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}"); - - // Whenever a permanent becomes tapped, put a wind counter on it. - Effect effect = new AddCountersTargetEffect(CounterType.WIND.createInstance()); - effect.setText("put a wind counter on it."); - this.addAbility(new BecomesTappedTriggeredAbility(effect, false, new FilterPermanent("a permanent"), true)); - - // If a permanent with a wind counter on it would untap during its controller's untap step, remove all wind counters from it instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new FreyalisesWindsReplacementEffect())); - - } - - private FreyalisesWinds(final FreyalisesWinds card) { - super(card); - } - - @Override - public FreyalisesWinds copy() { - return new FreyalisesWinds(this); - } -} - -class FreyalisesWindsReplacementEffect extends ReplacementEffectImpl { - - FreyalisesWindsReplacementEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If a permanent with a wind counter on it would untap during its controller's untap step, remove all wind counters from it instead"; - } - - FreyalisesWindsReplacementEffect(final FreyalisesWindsReplacementEffect effect) { - super(effect); - } - - @Override - public FreyalisesWindsReplacementEffect copy() { - return new FreyalisesWindsReplacementEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent permanentUntapping = game.getPermanent(event.getTargetId()); - if (permanentUntapping != null) { - permanentUntapping.removeCounters(CounterType.WIND.createInstance(), game); - return true; - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return (event.getType() == GameEvent.EventType.UNTAP - && game.getPhase().getStep().getType() == PhaseStep.UNTAP); - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Permanent permanentUntapping = game.getPermanent(event.getTargetId()); - return (permanentUntapping != null - && event.getPlayerId().equals(permanentUntapping.getControllerId()) - && permanentUntapping.getCounters(game).getCount(CounterType.WIND) > 0); - } -} +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.common.BecomesTappedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class FreyalisesWinds extends CardImpl { + + public FreyalisesWinds(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}"); + + // Whenever a permanent becomes tapped, put a wind counter on it. + Effect effect = new AddCountersTargetEffect(CounterType.WIND.createInstance()); + effect.setText("put a wind counter on it."); + this.addAbility(new BecomesTappedTriggeredAbility(effect, false, new FilterPermanent("a permanent"), true)); + + // If a permanent with a wind counter on it would untap during its controller's untap step, remove all wind counters from it instead. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new FreyalisesWindsReplacementEffect())); + + } + + private FreyalisesWinds(final FreyalisesWinds card) { + super(card); + } + + @Override + public FreyalisesWinds copy() { + return new FreyalisesWinds(this); + } +} + +class FreyalisesWindsReplacementEffect extends ReplacementEffectImpl { + + FreyalisesWindsReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If a permanent with a wind counter on it would untap during its controller's untap step, remove all wind counters from it instead"; + } + + FreyalisesWindsReplacementEffect(final FreyalisesWindsReplacementEffect effect) { + super(effect); + } + + @Override + public FreyalisesWindsReplacementEffect copy() { + return new FreyalisesWindsReplacementEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent permanentUntapping = game.getPermanent(event.getTargetId()); + if (permanentUntapping != null) { + permanentUntapping.removeCounters(CounterType.WIND.createInstance(), game); + return true; + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return (event.getType() == GameEvent.EventType.UNTAP + && game.getPhase().getStep().getType() == PhaseStep.UNTAP); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent permanentUntapping = game.getPermanent(event.getTargetId()); + return (permanentUntapping != null + && event.getPlayerId().equals(permanentUntapping.getControllerId()) + && permanentUntapping.getCounters(game).getCount(CounterType.WIND) > 0); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FrillscareMentor.java b/Mage.Sets/src/mage/cards/f/FrillscareMentor.java new file mode 100644 index 0000000000..87e3d85b73 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FrillscareMentor.java @@ -0,0 +1,71 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FrillscareMentor extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("non-Human creature you control"); + private static final FilterPermanent filter2 + = new FilterControlledCreaturePermanent("creature you control with menace"); + + static { + filter.add(Predicates.not(SubType.HUMAN.getPredicate())); + filter2.add(new AbilityPredicate(MenaceAbility.class)); + } + + public FrillscareMentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Frillscare Mentor enters the battlefield, put a menace counter on target non-Human creature you control. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.MENACE.createInstance()) + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {2}{R}, {T}: Put a +1/+1 counter on each creature you control with menace. + ability = new SimpleActivatedAbility( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter2), new ManaCostsImpl("{2}{R}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private FrillscareMentor(final FrillscareMentor card) { + super(card); + } + + @Override + public FrillscareMentor copy() { + return new FrillscareMentor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FrogtosserBanneret.java b/Mage.Sets/src/mage/cards/f/FrogtosserBanneret.java index 886577ba81..dd107b833f 100644 --- a/Mage.Sets/src/mage/cards/f/FrogtosserBanneret.java +++ b/Mage.Sets/src/mage/cards/f/FrogtosserBanneret.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; @@ -14,8 +12,9 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; +import java.util.UUID; + /** - * * @author North */ public final class FrogtosserBanneret extends CardImpl { @@ -29,7 +28,7 @@ public final class FrogtosserBanneret extends CardImpl { } public FrogtosserBanneret(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.GOBLIN); this.subtype.add(SubType.ROGUE); @@ -38,6 +37,7 @@ public final class FrogtosserBanneret extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); + // Goblin spells and Rogue spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); } diff --git a/Mage.Sets/src/mage/cards/f/FromTheAshes.java b/Mage.Sets/src/mage/cards/f/FromTheAshes.java index 77d8390df9..8dd0b42910 100644 --- a/Mage.Sets/src/mage/cards/f/FromTheAshes.java +++ b/Mage.Sets/src/mage/cards/f/FromTheAshes.java @@ -76,7 +76,7 @@ class FromTheAshesEffect extends OneShotEffect { playerAmount.put(playerId, amount); } } - game.applyEffects(); + game.getState().processAction(game); for (Map.Entry entry : playerAmount.entrySet()) { Player player = game.getPlayer(entry.getKey()); if (player != null && player.chooseUse(outcome, "Search your library for up to " + entry.getValue() + " basic land card(s) to put it onto the battlefield?", source, game)) { @@ -90,7 +90,7 @@ class FromTheAshesEffect extends OneShotEffect { entry.setValue(0); // no search no shuffling } } - game.applyEffects(); + game.getState().processAction(game); for (Map.Entry entry : playerAmount.entrySet()) { Player player = game.getPlayer(entry.getKey()); if (player != null && entry.getValue() > 0) { diff --git a/Mage.Sets/src/mage/cards/f/FromUnderTheFloorboards.java b/Mage.Sets/src/mage/cards/f/FromUnderTheFloorboards.java index d628f69dff..450d0a34fe 100644 --- a/Mage.Sets/src/mage/cards/f/FromUnderTheFloorboards.java +++ b/Mage.Sets/src/mage/cards/f/FromUnderTheFloorboards.java @@ -34,7 +34,7 @@ public final class FromUnderTheFloorboards extends CardImpl { // If From Under the Floorboards's madness cost was paid, instead create X of those tokens tapped and you gain X life. DynamicValue xValue = new FromUnderTheFloorboardsManacostVariableValue(); Effect effect = new CreateTokenEffect(new ZombieToken(), xValue, true, false); - effect.setText("Create three 2/2 black Zombie creature tokens tapped and you gain 3 life. If {this} madness cost was paid, instead create X of those tokens tapped and you gain X life."); + effect.setText("Create three 2/2 black Zombie creature tokens tapped and you gain 3 life. If {this} madness cost was paid, instead create X of those tokens tapped and you gain X life"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new GainLifeEffect(xValue)); } diff --git a/Mage.Sets/src/mage/cards/f/FrondlandFelidar.java b/Mage.Sets/src/mage/cards/f/FrondlandFelidar.java new file mode 100644 index 0000000000..996235df65 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FrondlandFelidar.java @@ -0,0 +1,66 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.DependencyType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FrondlandFelidar extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("creatures you control with vigilance"); + + static { + filter.add(new AbilityPredicate(VigilanceAbility.class)); + } + + public FrondlandFelidar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}"); + + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Creatures you control with vigilance have "{1}, {T}: Tap target creature." + Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + ContinuousEffect effect = new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, filter); + effect.setDependedToType(DependencyType.AddingAbility); + this.addAbility(new SimpleStaticAbility(effect)); + } + + private FrondlandFelidar(final FrondlandFelidar card) { + super(card); + } + + @Override + public FrondlandFelidar copy() { + return new FrondlandFelidar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FrontierSiege.java b/Mage.Sets/src/mage/cards/f/FrontierSiege.java index 7110ac9dde..3049ef6ec7 100644 --- a/Mage.Sets/src/mage/cards/f/FrontierSiege.java +++ b/Mage.Sets/src/mage/cards/f/FrontierSiege.java @@ -1,4 +1,3 @@ - package mage.cards.f; import mage.Mana; @@ -15,6 +14,7 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; @@ -25,24 +25,22 @@ import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * * @author LevelX2 */ public final class FrontierSiege extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with flying"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("a creature you don't control"); static { filter.add(TargetController.YOU.getControllerPredicate()); filter.add(new AbilityPredicate(FlyingAbility.class)); - filter2.add(TargetController.NOT_YOU.getControllerPredicate()); } + private static final String ruleTrigger1 = "&bull Khans — At the beginning of each of your main phases, add {G}{G}."; private static final String ruleTrigger2 = "&bull Dragons — Whenever a creature with flying enters the battlefield under your control, you may have it fight target creature you don't control."; public FrontierSiege(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); // As Frontier Siege enters the battlefield, choose Khans or Dragons. this.addAbility(new EntersBattlefieldAbility(new ChooseModeEffect("Khans or Dragons?", "Khans", "Dragons"), null, @@ -59,12 +57,12 @@ public final class FrontierSiege extends CardImpl { new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new FrontierSiegeFightEffect(), filter, true, SetTargetPointer.PERMANENT, ""), new ModeChoiceSourceCondition("Dragons"), ruleTrigger2); - ability2.addTarget(new TargetCreaturePermanent(filter2)); + ability2.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability2); } - public FrontierSiege(final FrontierSiege card) { + private FrontierSiege(final FrontierSiege card) { super(card); } @@ -76,12 +74,12 @@ public final class FrontierSiege extends CardImpl { class FrontierSiegeKhansTriggeredAbility extends TriggeredAbilityImpl { - public FrontierSiegeKhansTriggeredAbility() { + FrontierSiegeKhansTriggeredAbility() { super(Zone.BATTLEFIELD, new AddManaToManaPoolSourceControllerEffect(Mana.GreenMana(2)), false); } - public FrontierSiegeKhansTriggeredAbility(final FrontierSiegeKhansTriggeredAbility ability) { + private FrontierSiegeKhansTriggeredAbility(final FrontierSiegeKhansTriggeredAbility ability) { super(ability); } @@ -114,7 +112,7 @@ class FrontierSiegeFightEffect extends OneShotEffect { super(Outcome.Damage); } - FrontierSiegeFightEffect(final FrontierSiegeFightEffect effect) { + private FrontierSiegeFightEffect(final FrontierSiegeFightEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/f/FrontierWarmonger.java b/Mage.Sets/src/mage/cards/f/FrontierWarmonger.java new file mode 100644 index 0000000000..b2ebba4908 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FrontierWarmonger.java @@ -0,0 +1,96 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTargets; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class FrontierWarmonger extends CardImpl { + + public FrontierWarmonger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever one or more creatures attack an opponent or a planeswalker an opponent controls, those creatures gain menace until end of turn. + this.addAbility(new FrontierWarmongerTriggeredAbility()); + } + + private FrontierWarmonger(final FrontierWarmonger card) { + super(card); + } + + @Override + public FrontierWarmonger copy() { + return new FrontierWarmonger(this); + } +} + +class FrontierWarmongerTriggeredAbility extends TriggeredAbilityImpl { + + FrontierWarmongerTriggeredAbility() { + super(Zone.BATTLEFIELD, new GainAbilityTargetEffect(new MenaceAbility(), Duration.EndOfTurn), false); + } + + private FrontierWarmongerTriggeredAbility(final FrontierWarmongerTriggeredAbility ability) { + super(ability); + } + + @Override + public FrontierWarmongerTriggeredAbility copy() { + return new FrontierWarmongerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Set opponents = game.getOpponents(this.getControllerId()); + Predicate predicate = uuid -> opponents.contains(game.getCombat().getDefendingPlayerId(uuid, game)); + if (game.getCombat().getAttackers().stream().noneMatch(predicate)) { + return false; + } + List permanents = game + .getCombat() + .getAttackers() + .stream() + .filter(predicate) + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + this.getEffects().setTargetPointer(new FixedTargets(permanents, game)); + return true; + } + + @Override + public String getRule() { + return "Whenever one or more creatures attack an opponent or a planeswalker an opponent controls, " + + "those creatures gain menace until end of turn."; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FrostveilAmbush.java b/Mage.Sets/src/mage/cards/f/FrostveilAmbush.java new file mode 100644 index 0000000000..3bf757d753 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FrostveilAmbush.java @@ -0,0 +1,39 @@ +package mage.cards.f; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FrostveilAmbush extends CardImpl { + + public FrostveilAmbush(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); + + // Tap up to two target creatures. Those creatures don't untap during their controller's next untap step. + this.getSpellAbility().addEffect(new TapTargetEffect()); + this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect("Those creatures")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); + + // Cycling {1} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}"))); + } + + private FrostveilAmbush(final FrostveilAmbush card) { + super(card); + } + + @Override + public FrostveilAmbush copy() { + return new FrostveilAmbush(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FruitOfTheFirstTree.java b/Mage.Sets/src/mage/cards/f/FruitOfTheFirstTree.java index b7cd53ad79..6a9c0c6af8 100644 --- a/Mage.Sets/src/mage/cards/f/FruitOfTheFirstTree.java +++ b/Mage.Sets/src/mage/cards/f/FruitOfTheFirstTree.java @@ -71,7 +71,7 @@ class FruitOfTheFirstTreeEffect extends OneShotEffect { Permanent creature = (Permanent) getValue("attachedTo"); if (controller != null && creature != null) { controller.gainLife(creature.getToughness().getValue(), game, source); - controller.drawCards(creature.getToughness().getValue(), game); + controller.drawCards(creature.getToughness().getValue(), source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/f/Fry.java b/Mage.Sets/src/mage/cards/f/Fry.java index c9c9f7eca3..faa409a226 100644 --- a/Mage.Sets/src/mage/cards/f/Fry.java +++ b/Mage.Sets/src/mage/cards/f/Fry.java @@ -1,7 +1,7 @@ package mage.cards.f; import mage.ObjectColor; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -33,7 +33,7 @@ public final class Fry extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // This spell can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Fry deals 5 damage to target creature or planeswalker that's white or blue. this.getSpellAbility().addEffect(new DamageTargetEffect(5)); diff --git a/Mage.Sets/src/mage/cards/f/FullMoonsRise.java b/Mage.Sets/src/mage/cards/f/FullMoonsRise.java index c1c0c06ebb..3093568217 100644 --- a/Mage.Sets/src/mage/cards/f/FullMoonsRise.java +++ b/Mage.Sets/src/mage/cards/f/FullMoonsRise.java @@ -82,7 +82,7 @@ class FullMoonsRiseEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { ReplacementEffect effect = new RegenerateTargetEffect(); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/f/FullyGrown.java b/Mage.Sets/src/mage/cards/f/FullyGrown.java new file mode 100644 index 0000000000..6e45f4f429 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FullyGrown.java @@ -0,0 +1,36 @@ +package mage.cards.f; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FullyGrown extends CardImpl { + + public FullyGrown(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // Target creature gets +3/+3 until end of turn. Put a trample counter on it. + this.getSpellAbility().addEffect(new BoostTargetEffect(3, 3)); + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.TRAMPLE.createInstance()) + .setText("Put a trample counter on it")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private FullyGrown(final FullyGrown card) { + super(card); + } + + @Override + public FullyGrown copy() { + return new FullyGrown(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/Fumigate.java b/Mage.Sets/src/mage/cards/f/Fumigate.java index 54a1d5cfee..b74ef2339e 100644 --- a/Mage.Sets/src/mage/cards/f/Fumigate.java +++ b/Mage.Sets/src/mage/cards/f/Fumigate.java @@ -62,7 +62,7 @@ class FumigateEffect extends OneShotEffect { destroyedCreature++; } } - game.applyEffects(); + game.getState().processAction(game); if (destroyedCreature > 0) { controller.gainLife(destroyedCreature, game, source); } diff --git a/Mage.Sets/src/mage/cards/f/FumikoTheLowblood.java b/Mage.Sets/src/mage/cards/f/FumikoTheLowblood.java index 148f68f513..e79ecc0ae2 100644 --- a/Mage.Sets/src/mage/cards/f/FumikoTheLowblood.java +++ b/Mage.Sets/src/mage/cards/f/FumikoTheLowblood.java @@ -33,7 +33,7 @@ public final class FumikoTheLowblood extends CardImpl { this.toughness = new MageInt(2); // Fumiko the Lowblood has bushido X, where X is the number of attacking creatures. - this.addAbility(new BushidoAbility(new AttackingCreatureCount("the number of attacking creatures"))); + this.addAbility(new BushidoAbility(new AttackingCreatureCount("the number of attacking creatures."))); // Creatures your opponents control attack each turn if able. FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures your opponents control"); diff --git a/Mage.Sets/src/mage/cards/f/FungalReaches.java b/Mage.Sets/src/mage/cards/f/FungalReaches.java index 69f0a3cbe6..c94f030bbc 100644 --- a/Mage.Sets/src/mage/cards/f/FungalReaches.java +++ b/Mage.Sets/src/mage/cards/f/FungalReaches.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -7,9 +6,10 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; -import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ import mage.counters.CounterType; public final class FungalReaches extends CardImpl { public FungalReaches(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {tap}: Add {C}. this.addAbility(new ColorlessManaAbility()); @@ -38,7 +38,8 @@ public final class FungalReaches extends CardImpl { // {1}, Remove X storage counters from Fungal Reaches: Add X mana in any combination of {R} and/or {G}. ability = new SimpleManaAbility(Zone.BATTLEFIELD, - new AddManaInAnyCombinationEffect(RemovedCountersForCostValue.instance, ColoredManaSymbol.R, ColoredManaSymbol.G), + new AddManaInAnyCombinationEffect(RemovedCountersForCostValue.instance, + new CountersSourceCount(CounterType.STORAGE), ColoredManaSymbol.R, ColoredManaSymbol.G), new GenericManaCost(1)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/FungalRebirth.java b/Mage.Sets/src/mage/cards/f/FungalRebirth.java new file mode 100644 index 0000000000..801d277449 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FungalRebirth.java @@ -0,0 +1,46 @@ +package mage.cards.f; + +import mage.abilities.condition.common.MorbidCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.SaprolingToken; +import mage.target.common.TargetCardInYourGraveyard; +import mage.watchers.common.MorbidWatcher; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class FungalRebirth extends CardImpl { + + public FungalRebirth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // Return target permanent card from your graveyard to your hand. If a creature died this turn, create two 1/1 green Saproling creature tokens. + getSpellAbility().addEffect( + new ReturnFromGraveyardToHandTargetEffect().setText("Return target permanent card from your graveyard to your hand") + ); + getSpellAbility().addWatcher(new MorbidWatcher()); + getSpellAbility().addEffect(new ConditionalOneShotEffect( + new CreateTokenEffect(new SaprolingToken(), 2), + MorbidCondition.instance, + "If a creature died this turn, create two 1/1 green Saproling creature tokens")); + getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_PERMANENT)); + } + + private FungalRebirth(final FungalRebirth card) { + super(card); + } + + @Override + public FungalRebirth copy() { + return new FungalRebirth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FurnaceDragon.java b/Mage.Sets/src/mage/cards/f/FurnaceDragon.java index 8f508a93bb..92a267b0db 100644 --- a/Mage.Sets/src/mage/cards/f/FurnaceDragon.java +++ b/Mage.Sets/src/mage/cards/f/FurnaceDragon.java @@ -4,7 +4,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ExileAllEffect; import mage.abilities.keyword.AffinityForArtifactsAbility; @@ -43,7 +43,7 @@ public final class FurnaceDragon extends CardImpl { // When Furnace Dragon enters the battlefield, if you cast it from your hand, exile all artifacts. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new ExileAllEffect(filter), false), - CastFromHandSourceCondition.instance, + CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, exile all artifacts."), new CastFromHandWatcher()); } diff --git a/Mage.Sets/src/mage/cards/f/FurystokeGiant.java b/Mage.Sets/src/mage/cards/f/FurystokeGiant.java index 1698a5bd10..d221e36d62 100644 --- a/Mage.Sets/src/mage/cards/f/FurystokeGiant.java +++ b/Mage.Sets/src/mage/cards/f/FurystokeGiant.java @@ -36,7 +36,7 @@ public final class FurystokeGiant extends CardImpl { SimpleActivatedAbility FurystokeGiantAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2), new TapSourceCost()); FurystokeGiantAbility.addTarget(new TargetAnyTarget()); Effect effect = new GainAbilityAllEffect(FurystokeGiantAbility, Duration.EndOfTurn, new FilterControlledCreaturePermanent("other creatures"), true); - effect.setText("other creatures you control gain \"{T}: This creature deals 2 damage to any target.\" until end of turn."); + effect.setText("other creatures you control gain \"{T}: This creature deals 2 damage to any target\" until end of turn."); this.addAbility(new EntersBattlefieldTriggeredAbility(effect)); // Persist diff --git a/Mage.Sets/src/mage/cards/f/FyndhornDruid.java b/Mage.Sets/src/mage/cards/f/FyndhornDruid.java index a1b1fb15af..5c3f7b50cb 100644 --- a/Mage.Sets/src/mage/cards/f/FyndhornDruid.java +++ b/Mage.Sets/src/mage/cards/f/FyndhornDruid.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; @@ -31,7 +31,7 @@ public final class FyndhornDruid extends CardImpl { this.toughness = new MageInt(2); // When Fyndhorn Druid dies, if it was blocked this turn, you gain 4 life. - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesTriggeredAbility(new GainLifeEffect(4)), new SourceWasBlockedThisTurnCondition(), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(4)), new SourceWasBlockedThisTurnCondition(), "When {this} dies, if it was blocked this turn, you gain 4 life."), new WasBlockedThisTurnWatcher()); } diff --git a/Mage.Sets/src/mage/cards/g/GaddockTeeg.java b/Mage.Sets/src/mage/cards/g/GaddockTeeg.java index a18c4d4bd6..bc8b26b2fc 100644 --- a/Mage.Sets/src/mage/cards/g/GaddockTeeg.java +++ b/Mage.Sets/src/mage/cards/g/GaddockTeeg.java @@ -48,7 +48,7 @@ class GaddockTeegReplacementEffect4 extends ContinuousRuleModifyingEffectImpl { public GaddockTeegReplacementEffect4() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Noncreature spells with converted mana cost 4 or greater can't be cast. Noncreature spells with {X} in their mana costs can't be cast."; + staticText = "Noncreature spells with converted mana cost 4 or greater can't be cast. Noncreature spells with {X} in their mana costs can't be cast"; } public GaddockTeegReplacementEffect4(final GaddockTeegReplacementEffect4 effect) { @@ -85,7 +85,7 @@ class GaddockTeegReplacementEffectX extends ContinuousRuleModifyingEffectImpl { public GaddockTeegReplacementEffectX() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Noncreature spells with {X} in their mana costs can't be cast."; + staticText = "Noncreature spells with {X} in their mana costs can't be cast"; } public GaddockTeegReplacementEffectX(final GaddockTeegReplacementEffectX effect) { diff --git a/Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java b/Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java new file mode 100644 index 0000000000..2fd15a1465 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java @@ -0,0 +1,126 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.combat.CantAttackSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.PermanentToken; +import mage.game.permanent.token.TreasureToken; +import mage.watchers.Watcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GadrakTheCrownScourge extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_PERMANENT_ARTIFACT, ComparisonType.FEWER_THAN, 4 + ); + + public GadrakTheCrownScourge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts. + this.addAbility(new SimpleStaticAbility(new ConditionalRestrictionEffect( + new CantAttackSourceEffect(Duration.WhileOnBattlefield), condition, + "{this} can't attack unless you control four or more artifacts" + ))); + + // At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect( + new TreasureToken(), GadrakTheCrownScourgeValue.instance + ), TargetController.YOU, false), new GadrakTheCrownScourgeWatcher()); + } + + private GadrakTheCrownScourge(final GadrakTheCrownScourge card) { + super(card); + } + + @Override + public GadrakTheCrownScourge copy() { + return new GadrakTheCrownScourge(this); + } +} + +enum GadrakTheCrownScourgeValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + GadrakTheCrownScourgeWatcher watcher = game.getState().getWatcher(GadrakTheCrownScourgeWatcher.class); + return watcher != null ? watcher.getDiedThisTurn() : 0; + } + + @Override + public GadrakTheCrownScourgeValue copy() { + return instance; + } + + @Override + public String getMessage() { + return "nontoken creature that died this turn"; + } + + @Override + public String toString() { + return "1"; + } +} + +class GadrakTheCrownScourgeWatcher extends Watcher { + + private int diedThisTurn = 0; + + GadrakTheCrownScourgeWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ZONE_CHANGE) { + return; + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.isDiesEvent() + && zEvent.getTarget() != null + && zEvent.getTarget().isCreature() + && !(zEvent.getTarget() instanceof PermanentToken)) { + diedThisTurn++; + } + } + + @Override + public void reset() { + super.reset(); + diedThisTurn = 0; + } + + int getDiedThisTurn() { + return diedThisTurn; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GaeasBlessing.java b/Mage.Sets/src/mage/cards/g/GaeasBlessing.java index 62b9516c83..e3de06d4a5 100644 --- a/Mage.Sets/src/mage/cards/g/GaeasBlessing.java +++ b/Mage.Sets/src/mage/cards/g/GaeasBlessing.java @@ -1,7 +1,6 @@ package mage.cards.g; -import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ZoneChangeTriggeredAbility; @@ -10,6 +9,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -68,24 +68,9 @@ class GaeasBlessingEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - List targets = source.getTargets().get(1).getTargets(); - boolean shuffle = false; - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - if (player.getGraveyard().contains(card.getId())) { - player.getGraveyard().remove(card); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - shuffle = true; - } - } - } - if (shuffle) { - player.shuffleLibrary(source, game); - } - return true; + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (targetPlayer != null) { + return targetPlayer.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source); } return false; } @@ -155,14 +140,7 @@ class GaeasBlessingGraveToLibraryEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - game.informPlayers(controller.getLogName() + " shuffle their graveyard into their library"); - for (Card card : controller.getGraveyard().getCards(game)) { - controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.GRAVEYARD, true, true); - } - controller.getLibrary().addAll(controller.getGraveyard().getCards(game), game); - controller.getGraveyard().clear(); - controller.shuffleLibrary(source, game); - return true; + return controller.shuffleCardsToLibrary(controller.getGraveyard(), game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/g/GaeasCradle.java b/Mage.Sets/src/mage/cards/g/GaeasCradle.java index a79fa4c13d..e46ab5bd45 100644 --- a/Mage.Sets/src/mage/cards/g/GaeasCradle.java +++ b/Mage.Sets/src/mage/cards/g/GaeasCradle.java @@ -10,7 +10,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; /** * diff --git a/Mage.Sets/src/mage/cards/g/GaleSwooper.java b/Mage.Sets/src/mage/cards/g/GaleSwooper.java new file mode 100644 index 0000000000..14130beddf --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GaleSwooper.java @@ -0,0 +1,48 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GaleSwooper extends CardImpl { + + public GaleSwooper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.GRIFFIN); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Gale Swooper enters the battlefield, target creature gains flying until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility( + new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn) + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private GaleSwooper(final GaleSwooper card) { + super(card); + } + + @Override + public GaleSwooper copy() { + return new GaleSwooper(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GalepowderMage.java b/Mage.Sets/src/mage/cards/g/GalepowderMage.java index f59ef1f927..5353fc095f 100644 --- a/Mage.Sets/src/mage/cards/g/GalepowderMage.java +++ b/Mage.Sets/src/mage/cards/g/GalepowderMage.java @@ -91,7 +91,7 @@ class GalepowderMageEffect extends OneShotEffect { if (controller.moveCardToExileWithInfo(permanent, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId()))); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/g/GalvanicBlast.java b/Mage.Sets/src/mage/cards/g/GalvanicBlast.java index 5c49d14547..bf7146663b 100644 --- a/Mage.Sets/src/mage/cards/g/GalvanicBlast.java +++ b/Mage.Sets/src/mage/cards/g/GalvanicBlast.java @@ -3,8 +3,10 @@ package mage.cards.g; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.target.common.TargetAnyTarget; @@ -29,6 +31,8 @@ public final class GalvanicBlast extends CardImpl { MetalcraftCondition.instance, effectText )); this.getSpellAbility().addTarget(new TargetAnyTarget()); + this.getSpellAbility().setAbilityWord(AbilityWord.METALCRAFT); + this.getSpellAbility().addHint(MetalcraftHint.instance); } public GalvanicBlast(final GalvanicBlast card) { diff --git a/Mage.Sets/src/mage/cards/g/Gamekeeper.java b/Mage.Sets/src/mage/cards/g/Gamekeeper.java index d3e3ef3ab0..967860a4e2 100644 --- a/Mage.Sets/src/mage/cards/g/Gamekeeper.java +++ b/Mage.Sets/src/mage/cards/g/Gamekeeper.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.common.ExileSourceFromGraveCost; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.RevealCardsFromLibraryUntilEffect; @@ -27,7 +27,7 @@ public final class Gamekeeper extends CardImpl { this.toughness = new MageInt(2); // When Gamekeeper dies, you may exile it. If you do, reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield and put all other cards revealed this way into your graveyard. - Ability ability = new DiesTriggeredAbility(new DoIfCostPaid(new RevealCardsFromLibraryUntilEffect(StaticFilters.FILTER_CARD_CREATURE, Zone.BATTLEFIELD, Zone.GRAVEYARD), new ExileSourceFromGraveCost(), "Exile to reveal cards from the top of your library until you reveal a creature card?"), false); + Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new RevealCardsFromLibraryUntilEffect(StaticFilters.FILTER_CARD_CREATURE, Zone.BATTLEFIELD, Zone.GRAVEYARD), new ExileSourceFromGraveCost(), "Exile to reveal cards from the top of your library until you reveal a creature card?"), false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GangOfDevils.java b/Mage.Sets/src/mage/cards/g/GangOfDevils.java index 551c096e7a..db51d712d7 100644 --- a/Mage.Sets/src/mage/cards/g/GangOfDevils.java +++ b/Mage.Sets/src/mage/cards/g/GangOfDevils.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -27,7 +27,7 @@ public final class GangOfDevils extends CardImpl { this.toughness = new MageInt(3); // When Gang of Devils dies, it deals 3 damage divided as you choose among one, two, or three target creatures and/or players. - Ability ability = new DiesTriggeredAbility(new DamageMultiEffect(3, "it")); + Ability ability = new DiesSourceTriggeredAbility(new DamageMultiEffect(3, "it")); ability.addTarget(new TargetAnyTargetAmount(3)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GangOfElk.java b/Mage.Sets/src/mage/cards/g/GangOfElk.java index 3a9ce2c366..5372dfaedc 100644 --- a/Mage.Sets/src/mage/cards/g/GangOfElk.java +++ b/Mage.Sets/src/mage/cards/g/GangOfElk.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MultipliedValue; import mage.abilities.dynamicvalue.common.BlockedCreatureCount; @@ -33,7 +33,7 @@ public final class GangOfElk extends CardImpl { DynamicValue value = new MultipliedValue(new BlockedCreatureCount(), 2); Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true); effect.setText("it gets +2/+2 until end of turn for each creature blocking it"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public GangOfElk(final GangOfElk card) { diff --git a/Mage.Sets/src/mage/cards/g/GarbageElementalD.java b/Mage.Sets/src/mage/cards/g/GarbageElementalD.java index e8152cc11e..b0076f79d4 100644 --- a/Mage.Sets/src/mage/cards/g/GarbageElementalD.java +++ b/Mage.Sets/src/mage/cards/g/GarbageElementalD.java @@ -1,80 +1,80 @@ -package mage.cards.g; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.keyword.CascadeAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetOpponent; - -/** - * - * @author spjspj - */ -public final class GarbageElementalD extends CardImpl { - - public GarbageElementalD(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); - - this.subtype.add(SubType.ELEMENTAL); - this.power = new MageInt(3); - this.toughness = new MageInt(3); - - // Cascade - this.addAbility(new CascadeAbility()); - - // When Garbage Elemental enters the battlefield, roll a six-sided die. Garbage Elemental deals damage equal to the result to target opponent. - Ability ability = new EntersBattlefieldAbility(new GarbageElementalDEffect(), - null, - "When {this} enters the battlefield, roll a six-sided die. {this} deals damage equal to the result to target opponent", - null); - ability.addTarget(new TargetOpponent()); - this.addAbility(ability); - - } - - public GarbageElementalD(final GarbageElementalD card) { - super(card); - } - - @Override - public GarbageElementalD copy() { - return new GarbageElementalD(this); - } -} - -class GarbageElementalDEffect extends OneShotEffect { - - GarbageElementalDEffect() { - super(Outcome.Benefit); - this.staticText = "roll a six-sided die. {this} deals damage equal to the result to target opponent"; - } - - GarbageElementalDEffect(final GarbageElementalDEffect effect) { - super(effect); - } - - @Override - public GarbageElementalDEffect copy() { - return new GarbageElementalDEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Player opponent = game.getPlayer(source.getFirstTarget()); - if (controller != null && opponent != null) { - int damage = controller.rollDice(game, 6); - return game.damagePlayerOrPlaneswalker(opponent.getId(), damage, source.getId(), game, false, true) > 0; - } - return false; - } -} +package mage.cards.g; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CascadeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +/** + * + * @author spjspj + */ +public final class GarbageElementalD extends CardImpl { + + public GarbageElementalD(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Cascade + this.addAbility(new CascadeAbility()); + + // When Garbage Elemental enters the battlefield, roll a six-sided die. Garbage Elemental deals damage equal to the result to target opponent. + Ability ability = new EntersBattlefieldAbility(new GarbageElementalDEffect(), + null, + "When {this} enters the battlefield, roll a six-sided die. {this} deals damage equal to the result to target opponent", + null); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + + } + + public GarbageElementalD(final GarbageElementalD card) { + super(card); + } + + @Override + public GarbageElementalD copy() { + return new GarbageElementalD(this); + } +} + +class GarbageElementalDEffect extends OneShotEffect { + + GarbageElementalDEffect() { + super(Outcome.Benefit); + this.staticText = "roll a six-sided die. {this} deals damage equal to the result to target opponent"; + } + + GarbageElementalDEffect(final GarbageElementalDEffect effect) { + super(effect); + } + + @Override + public GarbageElementalDEffect copy() { + return new GarbageElementalDEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller != null && opponent != null) { + int damage = controller.rollDice(game, 6); + return game.damagePlayerOrPlaneswalker(opponent.getId(), damage, source.getId(), game, false, true) > 0; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java b/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java index af4fbc6779..3ed6116d62 100644 --- a/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java +++ b/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java @@ -9,15 +9,17 @@ import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.target.TargetPermanent; import mage.game.stack.Spell; +import mage.target.TargetPermanent; import java.util.UUID; @@ -63,15 +65,9 @@ public final class GargosViciousWatcher extends CardImpl { class GargosViciousWatcherTriggeredAbility extends TriggeredAbilityImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - GargosViciousWatcherTriggeredAbility() { super(Zone.BATTLEFIELD, new FightTargetSourceEffect()); - this.addTarget(new TargetPermanent(0, 1, filter, false)); + this.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); } private GargosViciousWatcherTriggeredAbility(final GargosViciousWatcherTriggeredAbility ability) { @@ -99,7 +95,6 @@ class GargosViciousWatcherTriggeredAbility extends TriggeredAbilityImpl { return false; } return object instanceof Spell; // must be a type of spell (instant, sorcery, or aura) - } @Override diff --git a/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java b/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java index a3240598e9..5e7f34fc2c 100644 --- a/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java +++ b/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java @@ -66,7 +66,7 @@ class GargoyleSentinelEffect extends ContinuousEffectImpl { switch (layer) { case AbilityAddingRemovingEffects_6: if (sublayer == SubLayer.NA) { - permanent.getAbilities().removeIf(entry -> entry.getId().equals(DefenderAbility.getInstance().getId())); + permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game); permanent.getAbilities().add(FlyingAbility.getInstance()); } break; diff --git a/Mage.Sets/src/mage/cards/g/GarrisonCat.java b/Mage.Sets/src/mage/cards/g/GarrisonCat.java new file mode 100644 index 0000000000..5050906846 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarrisonCat.java @@ -0,0 +1,38 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.HumanSoldierToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GarrisonCat extends CardImpl { + + public GarrisonCat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // When Garrison Cat dies, create a 1/1 white Human Soldier creature token. + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken()))); + } + + private GarrisonCat(final GarrisonCat card) { + super(card); + } + + @Override + public GarrisonCat copy() { + return new GarrisonCat(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java b/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java index 80a0d2e4c5..fa37f0f872 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java +++ b/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java @@ -97,7 +97,7 @@ class GarrukApexPredatorEffect3 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Permanent creature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (player != null && creature != null) { player.gainLife(creature.getToughness().getValue(), game, source); return true; diff --git a/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java b/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java index 680b42dc5c..872225567b 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java +++ b/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java @@ -80,7 +80,7 @@ class GarrukPrimalHunterEffect extends OneShotEffect { amount = p.getPower().getValue(); } } - player.drawCards(amount, game); + player.drawCards(amount, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java new file mode 100644 index 0000000000..5d2aaf48a8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java @@ -0,0 +1,113 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.DamageAsThoughNotBlockedAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class GarrukSavageHerald extends CardImpl { + + public GarrukSavageHerald(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.GARRUK); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // +1: Reveal the top card of your library. If it's a creature card, put it into your hand. Otherwise, put it on the bottom of your library. + this.addAbility(new LoyaltyAbility(new GarrukSavageHeraldEffect(), 1)); + + // −2: Target creature you control deals damage equal to its power to another target creature. + Effect effect = new DamageWithPowerFromOneToAnotherTargetEffect(); + effect.setText("Target creature you control deals damage equal to its power to another target creature"); + + Ability minusAbility = new LoyaltyAbility(effect, -2); + TargetControlledCreaturePermanent controlledCreature = new TargetControlledCreaturePermanent(); + controlledCreature.setTargetTag(1); + minusAbility.addTarget(controlledCreature); + + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + filter.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent anotherTargetCreature = new TargetCreaturePermanent(filter); + minusAbility.addTarget(anotherTargetCreature.withChooseHint("another creature to deal damage to")); + + this.addAbility(minusAbility); + + // −7: Until end of turn, creatures you control gain "You may have this creature assign its combat damage as though it weren't blocked." + ContinuousEffect ultimateEffect = new GainAbilityControlledEffect(DamageAsThoughNotBlockedAbility.getInstance(), Duration.EndOfTurn); + ultimateEffect.setText("Until end of turn, creatures you control gain \"You may have this creature assign its combat damage as though it weren't blocked.\""); + this.addAbility(new LoyaltyAbility(ultimateEffect, -7)); + } + + private GarrukSavageHerald(final GarrukSavageHerald card) { + super(card); + } + + @Override + public GarrukSavageHerald copy() { + return new GarrukSavageHerald(this); + } +} + +class GarrukSavageHeraldEffect extends OneShotEffect { + + GarrukSavageHeraldEffect() { + super(Outcome.Benefit); + staticText = "reveal the top card of your library. If it's a creature card, " + + "put it into your hand. Otherwise, put it on the bottom of your library"; + } + + private GarrukSavageHeraldEffect(final GarrukSavageHeraldEffect effect) { + super(effect); + } + + @Override + public GarrukSavageHeraldEffect copy() { + return new GarrukSavageHeraldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.revealCards(source, new CardsImpl(card), game); + if (card.isCreature()) { + return player.moveCards(card, Zone.HAND, source, game); + } else { + return player.putCardsOnBottomOfLibrary(card, game, source, false); + } + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GarrukUnleashed.java b/Mage.Sets/src/mage/cards/g/GarrukUnleashed.java new file mode 100644 index 0000000000..44e56a1345 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarrukUnleashed.java @@ -0,0 +1,71 @@ +package mage.cards.g; + +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.condition.common.OpponentControlsMoreCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.command.emblems.GarrukUnleashedEmblem; +import mage.game.permanent.token.BeastToken; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class GarrukUnleashed extends CardImpl { + + public GarrukUnleashed(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.GARRUK); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // +1: Up to one target creature gets +3/+3 and gains trample until end of turn. + Effect effect = new BoostTargetEffect(3, 3, Duration.EndOfTurn) + .setText("up to one target creature gets +3/+3"); + LoyaltyAbility ability = new LoyaltyAbility(effect, 1); + effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn) + .setText("and gains trample until end of turn"); + ability.addEffect(effect); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + + // −2: Create a 3/3 green Beast creature token. Then if an opponent controls more creatures than you, put a loyalty counter on Garruk, Unleashed. + ability = new LoyaltyAbility(new CreateTokenEffect(new BeastToken()), -2); + ability.addEffect(new ConditionalOneShotEffect( + new AddCountersSourceEffect(CounterType.LOYALTY.createInstance()), + new OpponentControlsMoreCondition(new FilterCreaturePermanent())) + .setText("Then if an opponent controls more creatures than you, put a loyalty counter on {this}")); + this.addAbility(ability); + + // −7: You get an emblem with "At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library." + this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new GarrukUnleashedEmblem()), -7)); + } + + private GarrukUnleashed(final GarrukUnleashed card) { + super(card); + } + + @Override + public GarrukUnleashed copy() { + return new GarrukUnleashed(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GarruksGorehorn.java b/Mage.Sets/src/mage/cards/g/GarruksGorehorn.java new file mode 100644 index 0000000000..237734487f --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarruksGorehorn.java @@ -0,0 +1,32 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GarruksGorehorn extends CardImpl { + + public GarruksGorehorn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(7); + this.toughness = new MageInt(3); + } + + private GarruksGorehorn(final GarruksGorehorn card) { + super(card); + } + + @Override + public GarruksGorehorn copy() { + return new GarruksGorehorn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GarruksHarbinger.java b/Mage.Sets/src/mage/cards/g/GarruksHarbinger.java new file mode 100644 index 0000000000..1d21b265a2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarruksHarbinger.java @@ -0,0 +1,86 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.keyword.HexproofFromBlackAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class GarruksHarbinger extends CardImpl { + + public GarruksHarbinger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Hexproof from Black + this.addAbility(HexproofFromBlackAbility.getInstance()); + + // Whenever Garruk's Harbinger deals combat damage to a player or planeswalker, look at that many cards from the top of your library. You may reveal a creature card or Garruk planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. + this.addAbility( + new DealsCombatDamageToAPlayerTriggeredAbility(new GarruksHarbingerEffect(), false, true) + .setOrPlaneswalker(true) + ); + } + + private GarruksHarbinger(final GarruksHarbinger card) { + super(card); + } + + @Override + public GarruksHarbinger copy() { + return new GarruksHarbinger(this); + } +} + +class GarruksHarbingerEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("a creature or Garruk planeswalker card"); + + static { + filter.add(Predicates.or(CardType.CREATURE.getPredicate(), Predicates.and(CardType.PLANESWALKER.getPredicate(), SubType.GARRUK.getPredicate()))); + } + + GarruksHarbingerEffect() { + super(Outcome.Benefit); + staticText = "look at that many cards from the top of your library. You may reveal a creature card or Garruk planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in a random order"; + } + + private GarruksHarbingerEffect(GarruksHarbingerEffect effect) { + super(effect); + } + + @Override + public GarruksHarbingerEffect copy() { + return new GarruksHarbingerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Integer damage = (Integer) getValue("damage"); + if (damage != null) { + LookLibraryAndPickControllerEffect effect = new LookLibraryAndPickControllerEffect(StaticValue.get(damage), false, StaticValue.get(1), filter, false); + effect.setBackInRandomOrder(true); + return effect.apply(game, source); + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GarruksHorde.java b/Mage.Sets/src/mage/cards/g/GarruksHorde.java index 7ac147eb18..a8513fe25f 100644 --- a/Mage.Sets/src/mage/cards/g/GarruksHorde.java +++ b/Mage.Sets/src/mage/cards/g/GarruksHorde.java @@ -1,9 +1,7 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; import mage.abilities.effects.common.continuous.PlayWithTheTopCardRevealedEffect; import mage.abilities.keyword.TrampleAbility; @@ -11,14 +9,18 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.StaticFilters; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; + +import java.util.UUID; /** * @author nantuko */ public final class GarruksHorde extends CardImpl { + private static final FilterCard filter = new FilterCreatureCard("cast creature spells"); + public GarruksHorde(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}"); this.subtype.add(SubType.BEAST); @@ -26,16 +28,17 @@ public final class GarruksHorde extends CardImpl { this.power = new MageInt(7); this.toughness = new MageInt(7); + // Trample this.addAbility(TrampleAbility.getInstance()); + // Play with the top card of your library revealed. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); + this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); + // You may cast the top card of your library if it's a creature card. - Effect effect = new PlayTheTopCardEffect(StaticFilters.FILTER_CARD_CREATURE); - effect.setText("You may cast the top card of your library if it's a creature card"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); } - public GarruksHorde(final GarruksHorde card) { + private GarruksHorde(final GarruksHorde card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/g/GarruksUprising.java b/Mage.Sets/src/mage/cards/g/GarruksUprising.java new file mode 100644 index 0000000000..4ed2994409 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarruksUprising.java @@ -0,0 +1,65 @@ +package mage.cards.g; + +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.FerociousCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.hint.common.FerociousHint; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class GarruksUprising extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("a creature with power 4 or greater"); + + static { + filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); + } + + public GarruksUprising(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + // When Garruk's Uprising enters the battlefield, if you control a creature with power 4 or greater, draw a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), FerociousCondition.instance)) + .addHint(FerociousHint.instance)); + + // Creatures you control have trample. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURES + ))); + + // Whenever a creature with power 4 or greater enters the battlefield under your control, draw a card. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), filter, false + )); + } + + private GarruksUprising(final GarruksUprising card) { + super(card); + } + + @Override + public GarruksUprising copy() { + return new GarruksUprising(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GarruksWarsteed.java b/Mage.Sets/src/mage/cards/g/GarruksWarsteed.java new file mode 100644 index 0000000000..8652a4ad3b --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarruksWarsteed.java @@ -0,0 +1,50 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class GarruksWarsteed extends CardImpl { + + private static final FilterCard filter = new FilterCard("Garruk, Savage Herald"); + + static { + filter.add(new NamePredicate("Garruk, Savage Herald")); + } + + public GarruksWarsteed(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + + this.subtype.add(SubType.RHINO); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // When Garruk's Warsteed enters the battlefield, you may search your library and/or graveyard for a card named Garruk, Savage Herald, reveal it, and put it into your hand. If you search your library this way, shuffle it. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter, false, true))); + } + + private GarruksWarsteed(final GarruksWarsteed card) { + super(card); + } + + @Override + public GarruksWarsteed copy() { + return new GarruksWarsteed(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GarzasAssassin.java b/Mage.Sets/src/mage/cards/g/GarzasAssassin.java index 475fc26f03..3136ddf356 100644 --- a/Mage.Sets/src/mage/cards/g/GarzasAssassin.java +++ b/Mage.Sets/src/mage/cards/g/GarzasAssassin.java @@ -74,7 +74,7 @@ class GarzasAssassinCost extends CostImpl { @Override public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { Player controller = game.getPlayer(controllerId); - return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost()); + return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost(ability)); } @Override diff --git a/Mage.Sets/src/mage/cards/g/GateColossus.java b/Mage.Sets/src/mage/cards/g/GateColossus.java index 42420c65f4..000c9e2692 100644 --- a/Mage.Sets/src/mage/cards/g/GateColossus.java +++ b/Mage.Sets/src/mage/cards/g/GateColossus.java @@ -5,9 +5,11 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.common.SimpleEvasionAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.GateYouControlCount; import mage.abilities.effects.common.PutOnLibrarySourceEffect; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; import mage.abilities.hint.common.GateYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -39,8 +41,10 @@ public final class GateColossus extends CardImpl { this.toughness = new MageInt(8); // This spell costs {1} less to cast for each Gate you control. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new GateColossusCostReductionEffect()) - .addHint(GateYouControlHint.instance)); + this.addAbility(new SimpleStaticAbility(Zone.ALL, + new SpellCostReductionForEachSourceEffect(1, GateYouControlCount.instance)) + .addHint(GateYouControlHint.instance) + ); // Gate Colossus can't be blocked by creatures with power 2 or less. this.addAbility(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield))); @@ -84,9 +88,7 @@ class GateColossusCostReductionEffect extends CostModificationEffectImpl { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { int count = game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game).size(); - if (count > 0) { - CardUtil.reduceCost(abilityToModify, count); - } + CardUtil.reduceCost(abilityToModify, count); return true; } diff --git a/Mage.Sets/src/mage/cards/g/GateHound.java b/Mage.Sets/src/mage/cards/g/GateHound.java index c25ebded1b..357df06b8f 100644 --- a/Mage.Sets/src/mage/cards/g/GateHound.java +++ b/Mage.Sets/src/mage/cards/g/GateHound.java @@ -24,7 +24,7 @@ public final class GateHound extends CardImpl { public GateHound(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java b/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java index 43a3218777..b112771820 100644 --- a/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java +++ b/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java @@ -29,7 +29,7 @@ public final class GatekeeperGargoyle extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Gargoyle Guardian enters the battlefield with a +1/+1 counter on it for each Gate you control. + // Gatekeeper Gargoyle enters the battlefield with a +1/+1 counter on it for each Gate you control. this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect( CounterType.P1P1.createInstance(), diff --git a/Mage.Sets/src/mage/cards/g/GauntletOfPower.java b/Mage.Sets/src/mage/cards/g/GauntletOfPower.java index e274f28652..ec7df62533 100644 --- a/Mage.Sets/src/mage/cards/g/GauntletOfPower.java +++ b/Mage.Sets/src/mage/cards/g/GauntletOfPower.java @@ -1,5 +1,7 @@ package mage.cards.g; +import java.util.ArrayList; +import java.util.List; import mage.Mana; import mage.ObjectColor; import mage.abilities.Ability; @@ -43,10 +45,10 @@ public final class GauntletOfPower extends CardImpl { // As Gauntlet of Power enters the battlefield, choose a color. this.addAbility(new EntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral))); // Creatures of the chosen color get +1/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GauntletOfPowerEffect1())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GauntletOfPowerBoostEffect())); // Whenever a basic land is tapped for mana of the chosen color, its controller adds one mana of that color. - this.addAbility(new TapForManaAllTriggeredAbility(new GauntletOfPowerEffectEffect2(), filter, SetTargetPointer.PERMANENT)); + this.addAbility(new GauntletOfPowerTapForManaAllTriggeredAbility(new GauntletOfPowerManaEffect2(), filter, SetTargetPointer.PERMANENT)); } public GauntletOfPower(final GauntletOfPower card) { @@ -59,22 +61,22 @@ public final class GauntletOfPower extends CardImpl { } } -class GauntletOfPowerEffect1 extends ContinuousEffectImpl { +class GauntletOfPowerBoostEffect extends ContinuousEffectImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - public GauntletOfPowerEffect1() { + public GauntletOfPowerBoostEffect() { super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); staticText = "Creatures of the chosen color get +1/+1"; } - public GauntletOfPowerEffect1(final GauntletOfPowerEffect1 effect) { + public GauntletOfPowerBoostEffect(final GauntletOfPowerBoostEffect effect) { super(effect); } @Override - public GauntletOfPowerEffect1 copy() { - return new GauntletOfPowerEffect1(this); + public GauntletOfPowerBoostEffect copy() { + return new GauntletOfPowerBoostEffect(this); } @Override @@ -93,18 +95,18 @@ class GauntletOfPowerEffect1 extends ContinuousEffectImpl { } -class TapForManaAllTriggeredAbility extends TriggeredManaAbility { +class GauntletOfPowerTapForManaAllTriggeredAbility extends TriggeredManaAbility { private final FilterPermanent filter; private final SetTargetPointer setTargetPointer; - public TapForManaAllTriggeredAbility(ManaEffect effect, FilterPermanent filter, SetTargetPointer setTargetPointer) { + public GauntletOfPowerTapForManaAllTriggeredAbility(ManaEffect effect, FilterPermanent filter, SetTargetPointer setTargetPointer) { super(Zone.BATTLEFIELD, effect, false); this.filter = filter; this.setTargetPointer = setTargetPointer; } - public TapForManaAllTriggeredAbility(TapForManaAllTriggeredAbility ability) { + public GauntletOfPowerTapForManaAllTriggeredAbility(GauntletOfPowerTapForManaAllTriggeredAbility ability) { super(ability); this.filter = ability.filter.copy(); this.setTargetPointer = ability.setTargetPointer; @@ -142,7 +144,7 @@ class TapForManaAllTriggeredAbility extends TriggeredManaAbility { } switch (setTargetPointer) { case PERMANENT: - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); break; case PLAYER: getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getControllerId())); @@ -156,8 +158,8 @@ class TapForManaAllTriggeredAbility extends TriggeredManaAbility { } @Override - public TapForManaAllTriggeredAbility copy() { - return new TapForManaAllTriggeredAbility(this); + public GauntletOfPowerTapForManaAllTriggeredAbility copy() { + return new GauntletOfPowerTapForManaAllTriggeredAbility(this); } @Override @@ -167,30 +169,42 @@ class TapForManaAllTriggeredAbility extends TriggeredManaAbility { } } -class GauntletOfPowerEffectEffect2 extends ManaEffect { +class GauntletOfPowerManaEffect2 extends ManaEffect { - public GauntletOfPowerEffectEffect2() { + public GauntletOfPowerManaEffect2() { super(); staticText = "its controller adds one additional mana of that color"; } - public GauntletOfPowerEffectEffect2(final GauntletOfPowerEffectEffect2 effect) { + public GauntletOfPowerManaEffect2(final GauntletOfPowerManaEffect2 effect) { super(effect); } @Override public Player getPlayer(Game game, Ability source) { - Permanent land = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent land = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (land != null) { return game.getPlayer(land.getControllerId()); } return null; } + @Override + public List getNetMana(Game game, Ability source) { + List netMana = new ArrayList<>(); + if (game != null) { + Mana mana = (Mana) getValue("mana"); + if (mana != null) { + netMana.add(mana.copy()); + } + } + return netMana; + } + @Override public Mana produceMana(Game game, Ability source) { if (game != null) { - Permanent land = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent land = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (land != null) { Mana mana = (Mana) getValue("mana"); if (mana != null) { @@ -202,7 +216,7 @@ class GauntletOfPowerEffectEffect2 extends ManaEffect { } @Override - public GauntletOfPowerEffectEffect2 copy() { - return new GauntletOfPowerEffectEffect2(this); + public GauntletOfPowerManaEffect2 copy() { + return new GauntletOfPowerManaEffect2(this); } } diff --git a/Mage.Sets/src/mage/cards/g/GaviNestWarden.java b/Mage.Sets/src/mage/cards/g/GaviNestWarden.java new file mode 100644 index 0000000000..fc839dbfcd --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GaviNestWarden.java @@ -0,0 +1,118 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DrawSecondCardTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.CyclingDiscardCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.DinosaurCatToken; +import mage.players.Player; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author htrajan + */ +public final class GaviNestWarden extends CardImpl { + + public GaviNestWarden(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + + // You may pay {0} rather than pay the cycling cost of the first card you cycle each turn. + this.addAbility(new SimpleStaticAbility(new CyclingZeroCostEffect()), new GaviNestWardenWatcher()); + + // Whenever you draw your second card each turn, create a 2/2 red and white Dinosaur Cat creature token. + this.addAbility(new DrawSecondCardTriggeredAbility(new CreateTokenEffect(new DinosaurCatToken()), false)); + } + + private GaviNestWarden(final GaviNestWarden card) { + super(card); + } + + @Override + public GaviNestWarden copy() { + return new GaviNestWarden(this); + } +} + +class GaviNestWardenWatcher extends Watcher { + + private final Map playerCyclingActivations = new HashMap<>(); + + GaviNestWardenWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.CYCLED_CARD) { + playerCyclingActivations.merge(event.getPlayerId(), 1, Integer::sum); + } + } + + int cyclingActivationsThisTurn(UUID playerId) { + return playerCyclingActivations.getOrDefault(playerId, 0); + } + + @Override + public void reset() { + super.reset(); + playerCyclingActivations.clear(); + } +} + +class CyclingZeroCostEffect extends CostModificationEffectImpl { + + CyclingZeroCostEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.SET_COST); + staticText = "You may pay {0} rather than pay the cycling cost of the first card you cycle each turn."; + } + + private CyclingZeroCostEffect(CyclingZeroCostEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || !player.chooseUse(outcome, "Pay {0} to cycle this card?", source, game)) { + return true; + } + abilityToModify.getManaCostsToPay().clear(); + abilityToModify.getCosts().removeIf(cost -> !CyclingDiscardCost.class.isInstance(cost)); + abilityToModify.getManaCostsToPay().add(new GenericManaCost(0)); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + GaviNestWardenWatcher watcher = game.getState().getWatcher(GaviNestWardenWatcher.class); + return abilityToModify instanceof CyclingAbility + && watcher != null + && watcher.cyclingActivationsThisTurn(abilityToModify.getControllerId()) == 0 + && abilityToModify.getControllerId().equals(source.getControllerId()); + } + + @Override + public CyclingZeroCostEffect copy() { + return new CyclingZeroCostEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GazeOfPain.java b/Mage.Sets/src/mage/cards/g/GazeOfPain.java index 139cb07c2c..d442ab3c87 100644 --- a/Mage.Sets/src/mage/cards/g/GazeOfPain.java +++ b/Mage.Sets/src/mage/cards/g/GazeOfPain.java @@ -1,188 +1,188 @@ -package mage.cards.g; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.combat.CombatGroup; -import mage.game.events.DamageEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author jeffwadsworth - */ -public final class GazeOfPain extends CardImpl { - - public GazeOfPain(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); - - // Until end of turn, whenever a creature you control attacks and isn't blocked, you may choose to have it deal damage equal to its power to a target creature. If you do, it assigns no combat damage this turn. - this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new GazeOfPainDelayedTriggeredAbility())); - } - - public GazeOfPain(final GazeOfPain card) { - super(card); - } - - @Override - public GazeOfPain copy() { - return new GazeOfPain(this); - } -} - -class GazeOfPainDelayedTriggeredAbility extends DelayedTriggeredAbility { - - Set creatures = new HashSet<>(); - - public GazeOfPainDelayedTriggeredAbility() { - super(new GazeOfPainEffect(), Duration.EndOfTurn, false, true); - } - - public GazeOfPainDelayedTriggeredAbility(GazeOfPainDelayedTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty()) { - for (UUID attackerId : combatGroup.getAttackers()) { - if (game.getPermanent(attackerId).getControllerId() == controllerId - && game.getPermanent(attackerId).isAttacking()) { - creatures.add(attackerId); - } - } - } - } - if (!creatures.isEmpty()) { - game.getState().setValue("gazeOfPain", creatures); - return true; - } - return false; - } - - @Override - public GazeOfPainDelayedTriggeredAbility copy() { - return new GazeOfPainDelayedTriggeredAbility(this); - } - - @Override - public String getRule() { - return "Until end of turn, whenever a creature you control attacks and isn't blocked, " + super.getRule(); - } -} - -class GazeOfPainEffect extends OneShotEffect { - - Set creatures = new HashSet<>(); - - GazeOfPainEffect() { - super(Outcome.Benefit); - this.staticText = "you may choose to have it deal damage equal to its power to a target creature. If you do, it assigns no combat damage this turn."; - } - - GazeOfPainEffect(final GazeOfPainEffect effect) { - super(effect); - } - - @Override - public GazeOfPainEffect copy() { - return new GazeOfPainEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - creatures = (Set) game.getState().getValue("gazeOfPain"); - if (!creatures.isEmpty()) { - for (UUID attackerId : creatures) { - Permanent attacker = game.getPermanent(attackerId); - if (controller != null - && attacker != null) { - if (controller.chooseUse(outcome, "Do you wish to deal damage equal to " + attacker.getName() + "'s power to a target creature?", source, game)) { - TargetCreaturePermanent target = new TargetCreaturePermanent(); - if (target.canChoose(controller.getId(), game) - && controller.choose(Outcome.Detriment, target, source.getSourceId(), game)) { - Effect effect = new DamageTargetEffect(attacker.getPower().getValue()); - effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); - effect.apply(game, source); - ContinuousEffect effect2 = new AssignNoCombatDamageTargetEffect(); - effect2.setTargetPointer(new FixedTarget(attackerId)); - game.addEffect(effect2, source); - } - } - } - } - return true; - } - return false; - } -} - -class AssignNoCombatDamageTargetEffect extends ReplacementEffectImpl { - - public AssignNoCombatDamageTargetEffect() { - super(Duration.EndOfTurn, Outcome.Neutral); - } - - public AssignNoCombatDamageTargetEffect(final AssignNoCombatDamageTargetEffect effect) { - super(effect); - } - - @Override - public AssignNoCombatDamageTargetEffect copy() { - return new AssignNoCombatDamageTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - switch (event.getType()) { - case DAMAGE_CREATURE: - case DAMAGE_PLAYER: - case DAMAGE_PLANESWALKER: - return true; - default: - return false; - } - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - DamageEvent damageEvent = (DamageEvent) event; - return event.getSourceId().equals(targetPointer.getFirst(game, source)) - && damageEvent.isCombatDamage(); - } -} +package mage.cards.g; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jeffwadsworth + */ +public final class GazeOfPain extends CardImpl { + + public GazeOfPain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); + + // Until end of turn, whenever a creature you control attacks and isn't blocked, you may choose to have it deal damage equal to its power to a target creature. If you do, it assigns no combat damage this turn. + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new GazeOfPainDelayedTriggeredAbility())); + } + + public GazeOfPain(final GazeOfPain card) { + super(card); + } + + @Override + public GazeOfPain copy() { + return new GazeOfPain(this); + } +} + +class GazeOfPainDelayedTriggeredAbility extends DelayedTriggeredAbility { + + Set creatures = new HashSet<>(); + + public GazeOfPainDelayedTriggeredAbility() { + super(new GazeOfPainEffect(), Duration.EndOfTurn, false, true); + } + + public GazeOfPainDelayedTriggeredAbility(GazeOfPainDelayedTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + for (CombatGroup combatGroup : game.getCombat().getGroups()) { + if (combatGroup.getBlockers().isEmpty()) { + for (UUID attackerId : combatGroup.getAttackers()) { + if (game.getPermanent(attackerId).getControllerId() == controllerId + && game.getPermanent(attackerId).isAttacking()) { + creatures.add(attackerId); + } + } + } + } + if (!creatures.isEmpty()) { + game.getState().setValue("gazeOfPain", creatures); + return true; + } + return false; + } + + @Override + public GazeOfPainDelayedTriggeredAbility copy() { + return new GazeOfPainDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Until end of turn, whenever a creature you control attacks and isn't blocked, " + super.getRule(); + } +} + +class GazeOfPainEffect extends OneShotEffect { + + Set creatures = new HashSet<>(); + + GazeOfPainEffect() { + super(Outcome.Benefit); + this.staticText = "you may choose to have it deal damage equal to its power to a target creature. If you do, it assigns no combat damage this turn."; + } + + GazeOfPainEffect(final GazeOfPainEffect effect) { + super(effect); + } + + @Override + public GazeOfPainEffect copy() { + return new GazeOfPainEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + creatures = (Set) game.getState().getValue("gazeOfPain"); + if (!creatures.isEmpty()) { + for (UUID attackerId : creatures) { + Permanent attacker = game.getPermanent(attackerId); + if (controller != null + && attacker != null) { + if (controller.chooseUse(outcome, "Do you wish to deal damage equal to " + attacker.getName() + "'s power to a target creature?", source, game)) { + TargetCreaturePermanent target = new TargetCreaturePermanent(); + if (target.canChoose(controller.getId(), game) + && controller.choose(Outcome.Detriment, target, source.getSourceId(), game)) { + Effect effect = new DamageTargetEffect(attacker.getPower().getValue()); + effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + effect.apply(game, source); + ContinuousEffect effect2 = new AssignNoCombatDamageTargetEffect(); + effect2.setTargetPointer(new FixedTarget(attackerId)); + game.addEffect(effect2, source); + } + } + } + } + return true; + } + return false; + } +} + +class AssignNoCombatDamageTargetEffect extends ReplacementEffectImpl { + + public AssignNoCombatDamageTargetEffect() { + super(Duration.EndOfTurn, Outcome.Neutral); + } + + public AssignNoCombatDamageTargetEffect(final AssignNoCombatDamageTargetEffect effect) { + super(effect); + } + + @Override + public AssignNoCombatDamageTargetEffect copy() { + return new AssignNoCombatDamageTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_CREATURE: + case DAMAGE_PLAYER: + case DAMAGE_PLANESWALKER: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + DamageEvent damageEvent = (DamageEvent) event; + return event.getSourceId().equals(targetPointer.getFirst(game, source)) + && damageEvent.isCombatDamage(); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java b/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java index f4fffd3401..8b955be607 100644 --- a/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java +++ b/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java @@ -1,35 +1,37 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class GearseekerSerpent extends CardImpl { public GearseekerSerpent(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); this.subtype.add(SubType.SERPENT); this.power = new MageInt(5); this.toughness = new MageInt(6); // Gearseeker Serpent costs {1} less to cast for each artifact you control - this.addAbility(new SimpleStaticAbility(Zone.STACK, new GearseekerSerpentCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, + new SpellCostReductionForEachSourceEffect(1, ArtifactYouControlCount.instance) + ).addHint(ArtifactYouControlHint.instance)); // 5U: Gearseeker Serpent can't be blocked this turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, @@ -46,41 +48,3 @@ public final class GearseekerSerpent extends CardImpl { return new GearseekerSerpent(this); } } - -class GearseekerSerpentCostReductionEffect extends CostModificationEffectImpl { - - private static final FilterControlledPermanent filter = new FilterControlledPermanent(); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } - - public GearseekerSerpentCostReductionEffect() { - super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "This spell costs {1} less to cast for each artifact you control"; - } - - protected GearseekerSerpentCostReductionEffect(final GearseekerSerpentCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - int count = game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game).size(); - if (count > 0) { - CardUtil.reduceCost(abilityToModify, count); - } - - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify.getSourceId().equals(source.getSourceId()); - } - - @Override - public GearseekerSerpentCostReductionEffect copy() { - return new GearseekerSerpentCostReductionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/g/GeistFueledScarecrow.java b/Mage.Sets/src/mage/cards/g/GeistFueledScarecrow.java index 2393e2b07b..b64c673afb 100644 --- a/Mage.Sets/src/mage/cards/g/GeistFueledScarecrow.java +++ b/Mage.Sets/src/mage/cards/g/GeistFueledScarecrow.java @@ -1,24 +1,23 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; +import java.util.UUID; + /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class GeistFueledScarecrow extends CardImpl { - + private static final FilterCard filter = new FilterCard("Creature spells"); static { @@ -26,14 +25,14 @@ public final class GeistFueledScarecrow extends CardImpl { } public GeistFueledScarecrow(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); this.subtype.add(SubType.SCARECROW); this.power = new MageInt(4); this.toughness = new MageInt(4); // Creature spells you cast cost {1} more to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{1}")))); + new SpellsCostIncreasingAllEffect(1, filter, TargetController.YOU))); } public GeistFueledScarecrow(final GeistFueledScarecrow card) { diff --git a/Mage.Sets/src/mage/cards/g/GeistHonoredMonk.java b/Mage.Sets/src/mage/cards/g/GeistHonoredMonk.java index 879fce467c..d6d1174281 100644 --- a/Mage.Sets/src/mage/cards/g/GeistHonoredMonk.java +++ b/Mage.Sets/src/mage/cards/g/GeistHonoredMonk.java @@ -38,7 +38,7 @@ public final class GeistHonoredMonk extends CardImpl { .addHint(CreaturesYouControlHint.instance)); // When Geist-Honored Monk enters the battlefield, create two 1/1 white Spirit creature tokens with flying. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken("ISD"), 2))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken(), 2))); } public GeistHonoredMonk(final GeistHonoredMonk card) { diff --git a/Mage.Sets/src/mage/cards/g/Gemrazer.java b/Mage.Sets/src/mage/cards/g/Gemrazer.java new file mode 100644 index 0000000000..d1490225e6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Gemrazer.java @@ -0,0 +1,63 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.MutateAbility; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Gemrazer extends CardImpl { + + private static final FilterPermanent filter + = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment an opponent controls"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public Gemrazer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Mutate {1}{G}{G} + this.addAbility(new MutateAbility(this, "{1}{G}{G}")); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever this creature mutates, destroy target artifact or enchantment an opponent controls. + Ability ability = new MutatesSourceTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private Gemrazer(final Gemrazer card) { + super(card); + } + + @Override + public Gemrazer copy() { + return new Gemrazer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GemstoneArray.java b/Mage.Sets/src/mage/cards/g/GemstoneArray.java index a629de5f07..075a2fcb74 100644 --- a/Mage.Sets/src/mage/cards/g/GemstoneArray.java +++ b/Mage.Sets/src/mage/cards/g/GemstoneArray.java @@ -5,6 +5,7 @@ import java.util.UUID; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; @@ -25,7 +26,8 @@ public final class GemstoneArray extends CardImpl { // {2}: Put a charge counter on Gemstone Array. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)), new ManaCostsImpl("{2}"))); // Remove a charge counter from Gemstone Array: Add one mana of any color. - this.addAbility(new AnyColorManaAbility(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(1)))); + this.addAbility(new AnyColorManaAbility(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(1)), + new CountersSourceCount(CounterType.CHARGE), false)); } public GemstoneArray(final GemstoneArray card) { diff --git a/Mage.Sets/src/mage/cards/g/GeneralKudroOfDrannith.java b/Mage.Sets/src/mage/cards/g/GeneralKudroOfDrannith.java new file mode 100644 index 0000000000..a9c81282e6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GeneralKudroOfDrannith.java @@ -0,0 +1,81 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInOpponentsGraveyard; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GeneralKudroOfDrannith extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent(SubType.HUMAN, "Humans"); + private static final FilterPermanent filter2 + = new FilterPermanent(SubType.HUMAN, "Human"); + private static final FilterCard filter3 + = new FilterCard("card from an opponent's graveyard"); + private static final FilterControlledPermanent filter4 + = new FilterControlledPermanent(SubType.HUMAN, "Humans"); + private static final FilterCreaturePermanent filter5 + = new FilterCreaturePermanent("creature with power 4 or greater"); + + static { + filter5.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); + } + + public GeneralKudroOfDrannith(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Other Humans you control get +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + ))); + + // Whenever General Kudro of Drannith or another Human enters the battlefield under your control, exile target card from an opponent's graveyard. + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(new ExileTargetEffect(), filter2, false, true); + ability.addTarget(new TargetCardInOpponentsGraveyard(filter3)); + this.addAbility(ability); + + // {2}, Sacrifice two Humans: Destroy target creature with power 4 or greater. + ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new GenericManaCost(2)); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, filter4))); + ability.addTarget(new TargetPermanent(filter5)); + this.addAbility(ability); + } + + private GeneralKudroOfDrannith(final GeneralKudroOfDrannith card) { + super(card); + } + + @Override + public GeneralKudroOfDrannith copy() { + return new GeneralKudroOfDrannith(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GeneralsEnforcer.java b/Mage.Sets/src/mage/cards/g/GeneralsEnforcer.java new file mode 100644 index 0000000000..130990fd90 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GeneralsEnforcer.java @@ -0,0 +1,96 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.token.HumanSoldierToken; +import mage.game.permanent.token.Token; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GeneralsEnforcer extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.HUMAN, "Legendary Humans"); + + static { + filter.add(SuperType.LEGENDARY.getPredicate()); + } + + public GeneralsEnforcer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Legendary Humans you control have indestructible. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, filter + ))); + + // {2}{W}{B}: Exile target card from a graveyard. If it was a creature card, create a 1/1 white Human Soldier creature token. + Ability ability = new SimpleActivatedAbility(new GeneralsEnforcerEffect(), new ManaCostsImpl("{2}{W}{B}")); + ability.addTarget(new TargetCardInGraveyard()); + this.addAbility(ability); + } + + private GeneralsEnforcer(final GeneralsEnforcer card) { + super(card); + } + + @Override + public GeneralsEnforcer copy() { + return new GeneralsEnforcer(this); + } +} + +class GeneralsEnforcerEffect extends OneShotEffect { + + private static final Token token = new HumanSoldierToken(); + + GeneralsEnforcerEffect() { + super(Outcome.Benefit); + staticText = "Exile target card from a graveyard. If it was a creature card, " + + "create a 1/1 white Human Soldier creature token."; + } + + private GeneralsEnforcerEffect(final GeneralsEnforcerEffect effect) { + super(effect); + } + + @Override + public GeneralsEnforcerEffect copy() { + return new GeneralsEnforcerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + boolean isCreature = card.isCreature(); + if (player.moveCards(card, Zone.EXILED, source, game) && isCreature) { + token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GenesisChamber.java b/Mage.Sets/src/mage/cards/g/GenesisChamber.java index 5cee00b105..4cdb08d115 100644 --- a/Mage.Sets/src/mage/cards/g/GenesisChamber.java +++ b/Mage.Sets/src/mage/cards/g/GenesisChamber.java @@ -92,7 +92,7 @@ class GenesisChamberEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = ((FixedTarget) targetPointer).getTargetedPermanentOrLKIBattlefield(game); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { MyrToken token = new MyrToken(); token.putOntoBattlefield(1, game, source.getSourceId(), permanent.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/g/GenesisUltimatum.java b/Mage.Sets/src/mage/cards/g/GenesisUltimatum.java new file mode 100644 index 0000000000..47dbbfcfcf --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GenesisUltimatum.java @@ -0,0 +1,86 @@ +package mage.cards.g; + +import mage.MageItem; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GenesisUltimatum extends CardImpl { + + public GenesisUltimatum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}{G}{U}{U}{U}{R}{R}"); + + // Look at the top five cards of your library. Put any number of permanent cards from among them onto the battlefield and the rest into your hand. Exile Genesis Ultimatum. + this.getSpellAbility().addEffect(new GenesisUltimatumEffect()); + this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + } + + private GenesisUltimatum(final GenesisUltimatum card) { + super(card); + } + + @Override + public GenesisUltimatum copy() { + return new GenesisUltimatum(this); + } +} + +class GenesisUltimatumEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterPermanentCard("any number of permanent cards"); + + GenesisUltimatumEffect() { + super(Outcome.Benefit); + staticText = "Look at the top five cards of your library. Put any number of permanent cards " + + "from among them onto the battlefield and the rest into your hand"; + } + + private GenesisUltimatumEffect(final GenesisUltimatumEffect effect) { + super(effect); + } + + @Override + public GenesisUltimatumEffect copy() { + return new GenesisUltimatumEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards toHand = new CardsImpl(player.getLibrary().getTopCards(game, 5)); +// player.lookAtCards("", toHand, game); + TargetCard targetCard = new TargetCardInLibrary(0, 5, filter); + player.choose(outcome, toHand, targetCard, game); + Cards toBattlefield = new CardsImpl(targetCard.getTargets()); + if (player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game)) { + toBattlefield + .stream() + .map(game::getPermanent) + .map(MageItem::getId) + .forEach(toHand::remove); + } + player.moveCards(toHand, Zone.HAND, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GenjuOfTheFields.java b/Mage.Sets/src/mage/cards/g/GenjuOfTheFields.java index b60ddcf288..037446ca09 100644 --- a/Mage.Sets/src/mage/cards/g/GenjuOfTheFields.java +++ b/Mage.Sets/src/mage/cards/g/GenjuOfTheFields.java @@ -47,7 +47,7 @@ public final class GenjuOfTheFields extends CardImpl { "Until end of turn, enchanted Plains becomes a 2/5 white Spirit creature", Duration.EndOfTurn); Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(2)); effect = new GainAbilityAttachedEffect(new DealsDamageGainLifeSourceTriggeredAbility(), AttachmentType.AURA, Duration.EndOfTurn); - effect.setText("with \"Whenever this creature deals damage, its controller gains that much life.\". It's still a land"); + effect.setText("with \"Whenever this creature deals damage, its controller gains that much life.\" It's still a land"); ability2.addEffect(effect); this.addAbility(ability2); diff --git a/Mage.Sets/src/mage/cards/g/GeomancersGambit.java b/Mage.Sets/src/mage/cards/g/GeomancersGambit.java index 3c5c47b580..e1bcac24d8 100644 --- a/Mage.Sets/src/mage/cards/g/GeomancersGambit.java +++ b/Mage.Sets/src/mage/cards/g/GeomancersGambit.java @@ -66,7 +66,7 @@ class GeomancersGambitEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/g/GeralfsMessenger.java b/Mage.Sets/src/mage/cards/g/GeralfsMessenger.java index f2445f84dd..347c139b77 100644 --- a/Mage.Sets/src/mage/cards/g/GeralfsMessenger.java +++ b/Mage.Sets/src/mage/cards/g/GeralfsMessenger.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTappedAbility; @@ -14,14 +12,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author Loki */ public final class GeralfsMessenger extends CardImpl { public GeralfsMessenger(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(3); @@ -29,10 +28,12 @@ public final class GeralfsMessenger extends CardImpl { // Geralf's Messenger enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); + // When Geralf's Messenger enters the battlefield, target opponent loses 2 life. Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(2)); ability.addTarget(new TargetOpponent()); this.addAbility(ability); + // Undying this.addAbility(new UndyingAbility()); } diff --git a/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java b/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java index 63374c1973..9e627ba1b4 100644 --- a/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java +++ b/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java @@ -3,7 +3,7 @@ package mage.cards.g; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.keyword.FirstStrikeAbility; @@ -40,7 +40,7 @@ public final class GerrardWeatherlightHero extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // When Gerrard, Weatherlight Hero dies, exile it and return to the battlefield all artifact and creature cards in your graveyard that were put there from the battlefield this turn. - Ability ability = new DiesTriggeredAbility(new ExileSourceEffect().setText("exile it")); + Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect().setText("exile it")); ability.addEffect(new GerrardWeatherlightHeroEffect()); this.addAbility(ability, new GerrardWeatherlightHeroWatcher()); } diff --git a/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java b/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java index 3db2f0e8b7..6d96b9f29f 100644 --- a/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java +++ b/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java @@ -1,6 +1,5 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -10,12 +9,7 @@ import mage.abilities.keyword.IntimidateAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.game.Game; @@ -23,6 +17,8 @@ import mage.players.Player; import mage.target.common.TargetCardInGraveyard; import mage.target.targetadjustment.XCMCGraveyardAdjuster; +import java.util.UUID; + /** * @author nantuko */ @@ -70,7 +66,7 @@ class GethLordOfTheVaultEffect extends OneShotEffect { public GethLordOfTheVaultEffect() { super(Outcome.Benefit); - staticText = "Put target artifact or creature card with converted mana cost X from an opponent's graveyard onto the battlefield under your control tapped. Then that player puts the top X cards of their library into their graveyard"; + staticText = "Put target artifact or creature card with converted mana cost X from an opponent's graveyard onto the battlefield under your control tapped. Then that player mills X cards"; } public GethLordOfTheVaultEffect(final GethLordOfTheVaultEffect effect) { @@ -80,18 +76,19 @@ class GethLordOfTheVaultEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (card != null) { - controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); - Player player = game.getPlayer(card.getOwnerId()); - if (player != null) { - player.moveCards(player.getLibrary().getTopCards(game, card.getConvertedManaCost()), Zone.GRAVEYARD, source, game); - } - } + if (controller == null) { + return false; + } + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (card == null) { return true; } - return false; + controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); + Player player = game.getPlayer(card.getOwnerId()); + if (player != null) { + player.millCards(card.getConvertedManaCost(), source, game); + } + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/g/GhalmasWarden.java b/Mage.Sets/src/mage/cards/g/GhalmasWarden.java index a371efbecb..188e926cdf 100644 --- a/Mage.Sets/src/mage/cards/g/GhalmasWarden.java +++ b/Mage.Sets/src/mage/cards/g/GhalmasWarden.java @@ -1,42 +1,42 @@ - - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; + +import java.util.UUID; /** - * * @author Loki */ public final class GhalmasWarden extends CardImpl { private static final String rule = "Metalcraft — Ghalma's Warden gets +2/+2 as long as you control three or more artifacts"; - public GhalmasWarden (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + public GhalmasWarden(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.ELEPHANT); this.subtype.add(SubType.SOLDIER); - this.power = new MageInt(2); this.toughness = new MageInt(4); + + // Metalcraft — Ghalma’s Warden gets +2/+2 as long as you control three or more artifacts. ContinuousEffect boostSource = new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield); ConditionalContinuousEffect effect = new ConditionalContinuousEffect(boostSource, MetalcraftCondition.instance, rule); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); } - public GhalmasWarden (final GhalmasWarden card) { + public GhalmasWarden(final GhalmasWarden card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java b/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java index e60680a5b6..c4dcf0929c 100644 --- a/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java +++ b/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java @@ -40,7 +40,7 @@ public final class GhaltaPrimalHunger extends CardImpl { this.toughness = new MageInt(12); // Ghalta, Primal Hunger costs {X} less to cast, where X is the total power of creatures you control. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new GhaltaPrimalHungerCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new GhaltaPrimalHungerCostReductionEffect())); // Trample this.addAbility(TrampleAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java b/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java index 058c4b9168..c831e3c0e3 100644 --- a/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java +++ b/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java @@ -58,7 +58,7 @@ class GhastlyDiscoveryEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(2, game); + controller.drawCards(2, source.getSourceId(), game); controller.discard(1, false, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/g/GhostCouncilOfOrzhova.java b/Mage.Sets/src/mage/cards/g/GhostCouncilOfOrzhova.java index e9181831f2..d4b7ec441a 100644 --- a/Mage.Sets/src/mage/cards/g/GhostCouncilOfOrzhova.java +++ b/Mage.Sets/src/mage/cards/g/GhostCouncilOfOrzhova.java @@ -1,29 +1,28 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileReturnBattlefieldOwnerNextEndStepSourceEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; -import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; -import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + +import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; + /** - * * @author Loki */ public final class GhostCouncilOfOrzhova extends CardImpl { @@ -37,7 +36,8 @@ public final class GhostCouncilOfOrzhova extends CardImpl { this.toughness = new MageInt(4); // When Ghost Council of Orzhova enters the battlefield, target opponent loses 1 life and you gain 1 life. - Ability ability = new EntersBattlefieldTriggeredAbility(new GhostCouncilOfOrzhovaEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(1)); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); @@ -62,34 +62,3 @@ public final class GhostCouncilOfOrzhova extends CardImpl { } } - -class GhostCouncilOfOrzhovaEffect extends OneShotEffect { - - GhostCouncilOfOrzhovaEffect() { - super(Outcome.GainLife); - staticText = "target opponent loses 1 life and you gain 1 life"; - } - - GhostCouncilOfOrzhovaEffect(final GhostCouncilOfOrzhovaEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - Player controller = game.getPlayer(source.getControllerId()); - if (targetPlayer != null - && controller != null) { - targetPlayer.loseLife(1, game, false); - controller.gainLife(1, game, source); - return true; - } - return false; - } - - @Override - public GhostCouncilOfOrzhovaEffect copy() { - return new GhostCouncilOfOrzhovaEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/g/GhostHounds.java b/Mage.Sets/src/mage/cards/g/GhostHounds.java index 1dcdcad465..6ec9ac4172 100644 --- a/Mage.Sets/src/mage/cards/g/GhostHounds.java +++ b/Mage.Sets/src/mage/cards/g/GhostHounds.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.VigilanceAbility; @@ -31,7 +31,7 @@ public final class GhostHounds extends CardImpl { public GhostHounds(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.SPIRIT); this.power = new MageInt(1); this.toughness = new MageInt(1); @@ -40,7 +40,7 @@ public final class GhostHounds extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Whenever Ghost Hounds blocks or becomes blocked by a white creature, Ghost Hounds gains first strike until end of turn. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), filter, false)); } diff --git a/Mage.Sets/src/mage/cards/g/GhostQuarter.java b/Mage.Sets/src/mage/cards/g/GhostQuarter.java index 881f2da242..0e394f2ad6 100644 --- a/Mage.Sets/src/mage/cards/g/GhostQuarter.java +++ b/Mage.Sets/src/mage/cards/g/GhostQuarter.java @@ -68,7 +68,7 @@ class GhostQuarterEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player controller = game.getPlayer(permanent.getControllerId()); if (controller != null && controller.chooseUse(Outcome.PutLandInPlay, "Do you wish to search for a basic land, put it onto the battlefield and then shuffle your library?", source, game)) { diff --git a/Mage.Sets/src/mage/cards/g/GhostfireBlade.java b/Mage.Sets/src/mage/cards/g/GhostfireBlade.java index 0a397ede81..cb4606ff3e 100644 --- a/Mage.Sets/src/mage/cards/g/GhostfireBlade.java +++ b/Mage.Sets/src/mage/cards/g/GhostfireBlade.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; @@ -11,38 +9,53 @@ import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.util.CardUtil; +import java.util.Objects; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class GhostfireBlade extends CardImpl { public GhostfireBlade(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +2/+2 this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 2))); - + // Equip {3} - this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(3))); + this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(3))); // todo // Ghostfire Blade's equip ability costs {2} less to activate if it targets a colorless creature. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("{this}'s equip ability costs {2} less to activate if it targets a colorless creature"))); } - @Override + + @Override public void adjustCosts(Ability ability, Game game) { if (ability instanceof EquipAbility) { - Permanent targetCreature = game.getPermanent(ability.getTargets().getFirstTarget()); - if (targetCreature != null && targetCreature.getColor(game).isColorless()) { - CardUtil.reduceCost(ability, 2); + if (game.inCheckPlayableState()) { + // checking state + boolean canSelectColorlessCreature = CardUtil.getAllPossibleTargets(ability, game).stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> permanent.getColor(game).isColorless()); + if (canSelectColorlessCreature) { + CardUtil.reduceCost(ability, 2); + } + } else { + // real cast state + Permanent targetCreature = game.getPermanent(ability.getTargets().getFirstTarget()); + if (targetCreature != null && targetCreature.getColor(game).isColorless()) { + CardUtil.reduceCost(ability, 2); + } } } } diff --git a/Mage.Sets/src/mage/cards/g/GhostlyFlicker.java b/Mage.Sets/src/mage/cards/g/GhostlyFlicker.java index e69fb17a01..cf35f53a13 100644 --- a/Mage.Sets/src/mage/cards/g/GhostlyFlicker.java +++ b/Mage.Sets/src/mage/cards/g/GhostlyFlicker.java @@ -81,7 +81,7 @@ class GhostlyFlickerEffect extends OneShotEffect { } } controller.moveCards(toExile, Zone.EXILED, source, game); - game.applyEffects(); + game.getState().processAction(game); Set toBattlefield = new HashSet<>(); for (Card card : toExile) { Zone currentZone = game.getState().getZone(card.getId()); diff --git a/Mage.Sets/src/mage/cards/g/GhostlyPilferer.java b/Mage.Sets/src/mage/cards/g/GhostlyPilferer.java new file mode 100644 index 0000000000..68ffecfd52 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GhostlyPilferer.java @@ -0,0 +1,94 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.abilities.keyword.InspiredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GhostlyPilferer extends CardImpl { + + public GhostlyPilferer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Whenever Ghostly Pilferer becomes untapped, you may pay {2}. If you do, draw a card. + this.addAbility(new InspiredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), new GenericManaCost(2) + ), false, false)); + + // Whenever an opponent casts a spell from anywhere other than their hand, draw a card. + this.addAbility(new GhostlyPilfererTriggeredAbility()); + + // Discard a card: Ghostly Pilferer can't be blocked this turn. + this.addAbility(new SimpleActivatedAbility( + new CantBeBlockedSourceEffect(Duration.EndOfTurn), new DiscardCardCost() + )); + } + + private GhostlyPilferer(final GhostlyPilferer card) { + super(card); + } + + @Override + public GhostlyPilferer copy() { + return new GhostlyPilferer(this); + } +} + +class GhostlyPilfererTriggeredAbility extends TriggeredAbilityImpl { + + GhostlyPilfererTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false); + } + + private GhostlyPilfererTriggeredAbility(final GhostlyPilfererTriggeredAbility ability) { + super(ability); + } + + @Override + public GhostlyPilfererTriggeredAbility copy() { + return new GhostlyPilfererTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getZone() == Zone.HAND) { + return false; + } + Spell spell = game.getStack().getSpell(event.getTargetId()); + return spell != null; + } + + @Override + public String getRule() { + return "Whenever an opponent casts a spell from anywhere other than their hand, draw a card."; + } +} diff --git a/Mage.Sets/src/mage/cards/g/Ghostway.java b/Mage.Sets/src/mage/cards/g/Ghostway.java index 000b248d18..109b489b1c 100644 --- a/Mage.Sets/src/mage/cards/g/Ghostway.java +++ b/Mage.Sets/src/mage/cards/g/Ghostway.java @@ -1,9 +1,5 @@ - package mage.cards.g; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -20,14 +16,17 @@ import mage.players.Player; import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class Ghostway extends CardImpl { public Ghostway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); // Exile each creature you control. Return those cards to the battlefield under their owner's control at the beginning of the next end step. this.getSpellAbility().addEffect(new GhostwayEffect()); @@ -77,7 +76,7 @@ class GhostwayEffect extends OneShotEffect { cardsToReturn.add(exiled); } } - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setTargetPointer(new FixedTargets(cardsToReturn, game)); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/g/GhoulcallersBell.java b/Mage.Sets/src/mage/cards/g/GhoulcallersBell.java index 6ad15678fb..e7638cd5de 100644 --- a/Mage.Sets/src/mage/cards/g/GhoulcallersBell.java +++ b/Mage.Sets/src/mage/cards/g/GhoulcallersBell.java @@ -1,22 +1,16 @@ - package mage.cards.g; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; +import mage.constants.TargetController; + +import java.util.UUID; /** - * * @author North */ public final class GhoulcallersBell extends CardImpl { @@ -25,10 +19,10 @@ public final class GhoulcallersBell extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); // {T}: Each player puts the top card of their library into their graveyard. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GhoulcallersBellEffect(), new TapSourceCost())); + this.addAbility(new SimpleActivatedAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(1, TargetController.ANY), new TapSourceCost())); } - public GhoulcallersBell(final GhoulcallersBell card) { + private GhoulcallersBell(final GhoulcallersBell card) { super(card); } @@ -37,34 +31,3 @@ public final class GhoulcallersBell extends CardImpl { return new GhoulcallersBell(this); } } - -class GhoulcallersBellEffect extends OneShotEffect { - - public GhoulcallersBellEffect() { - super(Outcome.Discard); - this.staticText = "Each player puts the top card of their library into their graveyard"; - } - - public GhoulcallersBellEffect(final GhoulcallersBellEffect effect) { - super(effect); - } - - @Override - public GhoulcallersBellEffect copy() { - return new GhoulcallersBellEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Card card = player.getLibrary().getFromTop(game); - if (card != null) { - player.moveCards(card, Zone.GRAVEYARD, source, game); - } - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/g/Ghoultree.java b/Mage.Sets/src/mage/cards/g/Ghoultree.java index 63a9982da0..ae65b24830 100644 --- a/Mage.Sets/src/mage/cards/g/Ghoultree.java +++ b/Mage.Sets/src/mage/cards/g/Ghoultree.java @@ -1,9 +1,12 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -11,8 +14,9 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author North */ public final class Ghoultree extends CardImpl { @@ -26,7 +30,11 @@ public final class Ghoultree extends CardImpl { this.toughness = new MageInt(10); // Ghoultree costs {1} less to cast for each creature card in your graveyard. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(StaticFilters.FILTER_CARD_CREATURE))); + DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)); + ability.setRuleAtTheTop(true); + ability.addHint(new ValueHint("Creature card in your graveyard", xValue)); + this.addAbility(ability); } public Ghoultree(final Ghoultree card) { diff --git a/Mage.Sets/src/mage/cards/g/GiantAlbatross.java b/Mage.Sets/src/mage/cards/g/GiantAlbatross.java index e81634de13..ceaa56f5b1 100644 --- a/Mage.Sets/src/mage/cards/g/GiantAlbatross.java +++ b/Mage.Sets/src/mage/cards/g/GiantAlbatross.java @@ -3,7 +3,7 @@ package mage.cards.g; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -39,7 +39,7 @@ public final class GiantAlbatross extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Giant Albatross dies, you may pay {1}{U}. If you do, for each creature that dealt damage to Giant Albatross this turn, destroy that creature unless its controller pays 2 life. A creature destroyed this way can't be regenerated. - Ability ability = new DiesTriggeredAbility(new DoIfCostPaid(new GiantAlbatrossEffect(), new ManaCostsImpl("{1}{U}"))); + Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new GiantAlbatrossEffect(), new ManaCostsImpl("{1}{U}"))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GiantShark.java b/Mage.Sets/src/mage/cards/g/GiantShark.java index 20feb429e6..9ebb586001 100644 --- a/Mage.Sets/src/mage/cards/g/GiantShark.java +++ b/Mage.Sets/src/mage/cards/g/GiantShark.java @@ -1,10 +1,8 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.ControlsPermanentsControllerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -14,19 +12,15 @@ import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import java.util.UUID; + /** - * * @author KholdFuzion & L_J - * */ public final class GiantShark extends CardImpl { @@ -39,8 +33,8 @@ public final class GiantShark extends CardImpl { } public GiantShark(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{U}"); - this.subtype.add(SubType.FISH); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}"); + this.subtype.add(SubType.SHARK); this.power = new MageInt(4); this.toughness = new MageInt(4); @@ -49,7 +43,7 @@ public final class GiantShark extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackUnlessDefenderControllsPermanent(filter))); // Whenever Giant Shark blocks or becomes blocked by a creature that has been dealt damage this turn, Giant Shark gets +2/+0 and gains trample until end of turn. - Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn).setText("{this} gets +2/+0"), filter2, false); + Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn).setText("{this} gets +2/+0"), filter2, false); ability.addEffect(new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn).setText("and gains trample until end of turn")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GideonsDefeat.java b/Mage.Sets/src/mage/cards/g/GideonsDefeat.java index b73f9f2dec..de6db2d921 100644 --- a/Mage.Sets/src/mage/cards/g/GideonsDefeat.java +++ b/Mage.Sets/src/mage/cards/g/GideonsDefeat.java @@ -71,7 +71,7 @@ class GideonsDefeatEffect extends OneShotEffect { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (controller != null && permanent != null) { controller.moveCards(permanent, Zone.EXILED, source, game); - game.applyEffects(); + game.getState().processAction(game); if (permanent.isPlaneswalker() && permanent.hasSubtype(SubType.GIDEON, game)) { controller.gainLife(5, game, source); } diff --git a/Mage.Sets/src/mage/cards/g/GideonsIntervention.java b/Mage.Sets/src/mage/cards/g/GideonsIntervention.java index 9ab17cfb9b..1a679be278 100644 --- a/Mage.Sets/src/mage/cards/g/GideonsIntervention.java +++ b/Mage.Sets/src/mage/cards/g/GideonsIntervention.java @@ -20,6 +20,7 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.events.PreventDamageEvent; import mage.game.permanent.Permanent; +import mage.util.CardUtil; import java.util.UUID; @@ -132,11 +133,12 @@ class GideonsInterventionPreventAllDamageEffect extends PreventionEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { MageObject object = game.getObject(event.getSourceId()); Permanent targetPerm = game.getPermanent(event.getTargetId()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); if (object != null && (event.getType() == GameEvent.EventType.DAMAGE_PLAYER || targetPerm != null && (event.getType() == GameEvent.EventType.DAMAGE_CREATURE || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER))) { - if (object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY)) + if (CardUtil.haveSameNames(object, cardName, game) && (event.getTargetId().equals(source.getControllerId()) || targetPerm != null && targetPerm.isControlledBy(source.getControllerId()))) { return super.applies(event, source, game); diff --git a/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java b/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java index 185f1a9eb7..1d88c170a7 100644 --- a/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java +++ b/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; @@ -37,7 +37,7 @@ public final class GiftOfTheWoods extends CardImpl { // Whenever enchanted creature blocks or becomes blocked, it gets +0/+3 until // end of turn and you gain 1 life. - Ability ability2 = new BlocksOrBecomesBlockedTriggeredAbility( + Ability ability2 = new BlocksOrBecomesBlockedSourceTriggeredAbility( new BoostEnchantedEffect(0, 3, Duration.EndOfTurn), false); ability2.addEffect(new GainLifeEffect(1).concatBy("and")); this.addAbility(ability2); diff --git a/Mage.Sets/src/mage/cards/g/GiveTake.java b/Mage.Sets/src/mage/cards/g/GiveTake.java index 0e3832a49a..d27da367c5 100644 --- a/Mage.Sets/src/mage/cards/g/GiveTake.java +++ b/Mage.Sets/src/mage/cards/g/GiveTake.java @@ -72,7 +72,7 @@ class TakeEffect extends OneShotEffect { creature.removeCounters(CounterType.P1P1.getName(), numberCounters, game); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(numberCounters, game); + controller.drawCards(numberCounters, source.getSourceId(), game); } else { throw new UnsupportedOperationException("Controller missing"); } diff --git a/Mage.Sets/src/mage/cards/g/Glademuse.java b/Mage.Sets/src/mage/cards/g/Glademuse.java new file mode 100644 index 0000000000..6c94ccb35d --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Glademuse.java @@ -0,0 +1,60 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.StackObject; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Glademuse extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a spell, if it's not their turn"); + + static { + filter.add(GlademusePredicate.instance); + } + + public Glademuse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Whenever a player casts a spell, if it's not their turn, that player draws a card. + this.addAbility(new SpellCastAllTriggeredAbility( + new DrawCardTargetEffect(1).setText("that player draws a card"), + filter, false, SetTargetPointer.PLAYER + )); + } + + private Glademuse(final Glademuse card) { + super(card); + } + + @Override + public Glademuse copy() { + return new Glademuse(this); + } +} + +enum GlademusePredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject spell, Game game) { + return spell != null && !spell.getControllerId().equals(game.getActivePlayerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java b/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java index fcfdbdfbf7..69eaad0167 100644 --- a/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java +++ b/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java @@ -15,7 +15,6 @@ import mage.cards.CardSetInfo; import mage.constants.AttachmentType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import java.util.UUID; @@ -30,13 +29,7 @@ public final class GlaiveOfTheGuildpact extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +1/+0 for each Gate you control and has vigilance and menace. - Ability ability = new SimpleStaticAbility( - Zone.BATTLEFIELD, - new BoostEquippedEffect( - GateYouControlCount.instance, - StaticValue.get(0) - ) - ); + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(GateYouControlCount.instance, StaticValue.get(0))); ability.addEffect(new GainAbilityAttachedEffect( VigilanceAbility.getInstance(), AttachmentType.EQUIPMENT ).setText("and has vigilance")); @@ -50,7 +43,7 @@ public final class GlaiveOfTheGuildpact extends CardImpl { this.addAbility(new EquipAbility(3)); } - public GlaiveOfTheGuildpact(final GlaiveOfTheGuildpact card) { + private GlaiveOfTheGuildpact(final GlaiveOfTheGuildpact card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/g/GleamingBarrier.java b/Mage.Sets/src/mage/cards/g/GleamingBarrier.java index 8a0c60476a..fc03735d67 100644 --- a/Mage.Sets/src/mage/cards/g/GleamingBarrier.java +++ b/Mage.Sets/src/mage/cards/g/GleamingBarrier.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; @@ -29,7 +29,7 @@ public final class GleamingBarrier extends CardImpl { this.addAbility(DefenderAbility.getInstance()); // When Gleaming Barrier dies, create a colorless Treasure artifact token with "{t}, Sacrifice this artifact: Add one mana of any color." - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new TreasureToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new TreasureToken()))); } public GleamingBarrier(final GleamingBarrier card) { diff --git a/Mage.Sets/src/mage/cards/g/Glimmerbell.java b/Mage.Sets/src/mage/cards/g/Glimmerbell.java new file mode 100644 index 0000000000..c9604e6fce --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Glimmerbell.java @@ -0,0 +1,43 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Glimmerbell extends CardImpl { + + public Glimmerbell(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.JELLYFISH); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {1}{U}: Untap Glimmerbell. + this.addAbility(new SimpleActivatedAbility(new UntapSourceEffect(), new ManaCostsImpl("{1}{U}"))); + } + + private Glimmerbell(final Glimmerbell card) { + super(card); + } + + @Override + public Glimmerbell copy() { + return new Glimmerbell(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GlimmerpointStag.java b/Mage.Sets/src/mage/cards/g/GlimmerpointStag.java index 939b63db59..90436fcda1 100644 --- a/Mage.Sets/src/mage/cards/g/GlimmerpointStag.java +++ b/Mage.Sets/src/mage/cards/g/GlimmerpointStag.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -13,8 +11,8 @@ import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.permanent.AnotherPredicate; @@ -24,8 +22,9 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author maurer.it_at_gmail.com */ public final class GlimmerpointStag extends CardImpl { @@ -37,7 +36,7 @@ public final class GlimmerpointStag extends CardImpl { } public GlimmerpointStag(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.subtype.add(SubType.ELK); this.power = new MageInt(3); @@ -84,7 +83,7 @@ class GlimmerpointStagEffect extends OneShotEffect { int zcc = permanent.getZoneChangeCounter(game); controller.moveCards(permanent, Zone.EXILED, source, game); //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setTargetPointer(new FixedTarget(permanent.getId(), zcc + 1)); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/g/GlintEyeNephilim.java b/Mage.Sets/src/mage/cards/g/GlintEyeNephilim.java index d9eb7c728a..f20c884b5d 100644 --- a/Mage.Sets/src/mage/cards/g/GlintEyeNephilim.java +++ b/Mage.Sets/src/mage/cards/g/GlintEyeNephilim.java @@ -74,7 +74,7 @@ class GlintEyeNephilimEffect extends OneShotEffect { if (amount > 0) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(amount, game); + controller.drawCards(amount, source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/g/GlitteringLion.java b/Mage.Sets/src/mage/cards/g/GlitteringLion.java index 737200acbc..5f1651ad36 100644 --- a/Mage.Sets/src/mage/cards/g/GlitteringLion.java +++ b/Mage.Sets/src/mage/cards/g/GlitteringLion.java @@ -35,9 +35,8 @@ public final class GlitteringLion extends CardImpl { // Prevent all damage that would be dealt to Glittering Lion. this.addAbility(GlitteringLionAbility.getInstance()); // {3}: Until end of turn, Glittering Lion loses "Prevent all damage that would be dealt to Glittering Lion." Any player may activate this ability. - SimpleActivatedAbility ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseAbilitySourceEffect(GlitteringLionAbility.getInstance(), Duration.EndOfTurn).setText("Until end of turn, {this} loses \"Prevent all damage that would be dealt to {this}.\""), new ManaCostsImpl("{3}")); + SimpleActivatedAbility ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseAbilitySourceEffect(GlitteringLionAbility.getInstance(), Duration.EndOfTurn).setText("Until end of turn, {this} loses \"Prevent all damage that would be dealt to {this}.\" Any player may activate this ability"), new ManaCostsImpl("{3}")); ability2.setMayActivate(TargetController.ANY); - ability2.addEffect(new InfoEffect("Any player may activate this ability")); this.addAbility(ability2); } diff --git a/Mage.Sets/src/mage/cards/g/Gloom.java b/Mage.Sets/src/mage/cards/g/Gloom.java index 43a47d98b6..8df36787ea 100644 --- a/Mage.Sets/src/mage/cards/g/Gloom.java +++ b/Mage.Sets/src/mage/cards/g/Gloom.java @@ -1,13 +1,11 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -16,23 +14,25 @@ import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class Gloom extends CardImpl { - + private static final FilterCard filter = new FilterCard("White spells"); + static { filter.add(new ColorPredicate(ObjectColor.WHITE)); } public Gloom(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); // White spells cost {3} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(filter, 3))); - + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(3, filter, TargetController.ANY))); + // Activated abilities of white enchantments cost {3} more to activate. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GloomCostIncreaseEffect())); } @@ -63,12 +63,12 @@ class GloomCostIncreaseEffect extends CostModificationEffectImpl { CardUtil.increaseCost(abilityToModify, 3); return true; } - + @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { + public boolean applies(Ability abilityToModify, Ability source, Game game) { boolean isWhiteEnchantment = false; boolean isActivated = abilityToModify.getAbilityType() == AbilityType.ACTIVATED; - if (isActivated) { + if (isActivated) { MageObject permanent = game.getPermanent(abilityToModify.getSourceId()); if (permanent != null) { isWhiteEnchantment = permanent.isEnchantment() && permanent.getColor(game).isWhite(); diff --git a/Mage.Sets/src/mage/cards/g/GloomPangolin.java b/Mage.Sets/src/mage/cards/g/GloomPangolin.java new file mode 100644 index 0000000000..095b18a68a --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GloomPangolin.java @@ -0,0 +1,33 @@ +package mage.cards.g; + +import java.util.UUID; +import mage.MageInt; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +/** + * + * @author TheElk801 + */ +public final class GloomPangolin extends CardImpl { + + public GloomPangolin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.PANGOLIN); + this.power = new MageInt(1); + this.toughness = new MageInt(5); + } + + private GloomPangolin(final GloomPangolin card) { + super(card); + } + + @Override + public GloomPangolin copy() { + return new GloomPangolin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GloomSower.java b/Mage.Sets/src/mage/cards/g/GloomSower.java new file mode 100644 index 0000000000..29db3f13c6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GloomSower.java @@ -0,0 +1,81 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author arcox + */ +public final class GloomSower extends CardImpl { + + public GloomSower(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}"); + + this.subtype.add(SubType.HORROR); + this.power = new MageInt(8); + this.toughness = new MageInt(6); + + // Whenever Gloom Sower becomes blocked by a creature, that creature’s controller loses 2 life and you gain 2 life. + Ability ability = new BecomesBlockedByCreatureTriggeredAbility(new GloomSowerEffect(), false); + Effect effect = new GainLifeEffect(2); + effect.setText("and you gain 2 life"); + ability.addEffect(effect); + this.addAbility(ability); + } + + private GloomSower(final GloomSower card) { + super(card); + } + + @Override + public GloomSower copy() { + return new GloomSower(this); + } +} + +class GloomSowerEffect extends OneShotEffect { + + GloomSowerEffect() { + super(Outcome.LoseLife); + staticText = "that creature's controller loses 2 life"; + } + + private GloomSowerEffect(final GloomSowerEffect effect) { + super(effect); + } + + @Override + public GloomSowerEffect copy() { + return new GloomSowerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + return false; + } + + Player player = game.getPlayer(permanent.getControllerId()); + if (player == null) { + return false; + } + + player.loseLife(2, game, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/Gloomlance.java b/Mage.Sets/src/mage/cards/g/Gloomlance.java index f7a42d9e08..dc16a34913 100644 --- a/Mage.Sets/src/mage/cards/g/Gloomlance.java +++ b/Mage.Sets/src/mage/cards/g/Gloomlance.java @@ -60,7 +60,7 @@ class GloomlanceEffect extends OneShotEffect { if (targetCreature != null) { Player targetController = game.getPlayer(targetCreature.getControllerId()); targetCreature.destroy(source.getSourceId(), game, false); - Permanent destroyedCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent destroyedCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (destroyedCreature.getColor(game).isGreen() || destroyedCreature.getColor(game).isWhite()) { if(targetController != null) { diff --git a/Mage.Sets/src/mage/cards/g/GloomwidowsFeast.java b/Mage.Sets/src/mage/cards/g/GloomwidowsFeast.java index 3739a74863..e6622535df 100644 --- a/Mage.Sets/src/mage/cards/g/GloomwidowsFeast.java +++ b/Mage.Sets/src/mage/cards/g/GloomwidowsFeast.java @@ -71,7 +71,7 @@ class GloomwidowsFeastEffect extends OneShotEffect { Permanent targetCreature = game.getPermanent(source.getFirstTarget()); if (targetCreature != null) { targetCreature.destroy(source.getSourceId(), game, false); - Permanent destroyedCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent destroyedCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (destroyedCreature.getColor(game).isBlue() || destroyedCreature.getColor(game).isBlack()) { SpiderToken token = new SpiderToken(); diff --git a/Mage.Sets/src/mage/cards/g/Glowrider.java b/Mage.Sets/src/mage/cards/g/Glowrider.java index 3ff7663897..7948e732c9 100644 --- a/Mage.Sets/src/mage/cards/g/Glowrider.java +++ b/Mage.Sets/src/mage/cards/g/Glowrider.java @@ -1,25 +1,30 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.cards.Card; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; + +import java.util.UUID; /** - * * @author fireshoes */ public final class Glowrider extends CardImpl { + private static final FilterCard filter = new FilterCard("Noncreature spells"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + public Glowrider(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); @@ -28,7 +33,7 @@ public final class Glowrider extends CardImpl { this.toughness = new MageInt(1); // Noncreature spells cost {1} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GlowriderCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY))); } public Glowrider(final Glowrider card) { @@ -40,38 +45,3 @@ public final class Glowrider extends CardImpl { return new Glowrider(this); } } - -class GlowriderCostReductionEffect extends CostModificationEffectImpl { - - GlowriderCostReductionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Noncreature spells cost {1} more to cast"; - } - - GlowriderCostReductionEffect(GlowriderCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - CardUtil.increaseCost(abilityToModify, 1); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - Card card = game.getCard(abilityToModify.getSourceId()); - if (card != null && !card.isCreature()) { - return true; - } - } - return false; - } - - @Override - public GlowriderCostReductionEffect copy() { - return new GlowriderCostReductionEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/g/GlowstoneRecluse.java b/Mage.Sets/src/mage/cards/g/GlowstoneRecluse.java new file mode 100644 index 0000000000..3adb9f7ff4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GlowstoneRecluse.java @@ -0,0 +1,50 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.MutateAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GlowstoneRecluse extends CardImpl { + + public GlowstoneRecluse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.SPIDER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Mutate {3}{G} + this.addAbility(new MutateAbility(this, "{3}{G}")); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Whenever this creature mutates, put two +1/+1 counters on it. + this.addAbility(new MutatesSourceTriggeredAbility( + new AddCountersSourceEffect( + CounterType.P1P1.createInstance(2) + ).setText("put two +1/+1 counters on it") + )); + } + + private GlowstoneRecluse(final GlowstoneRecluse card) { + super(card); + } + + @Override + public GlowstoneRecluse copy() { + return new GlowstoneRecluse(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GlyphOfReincarnation.java b/Mage.Sets/src/mage/cards/g/GlyphOfReincarnation.java index 349ab923be..410741c12b 100644 --- a/Mage.Sets/src/mage/cards/g/GlyphOfReincarnation.java +++ b/Mage.Sets/src/mage/cards/g/GlyphOfReincarnation.java @@ -80,7 +80,7 @@ class GlyphOfReincarnationEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent targetWall = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent targetWall = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && targetWall != null) { BlockedAttackerWatcher watcher = game.getState().getWatcher(BlockedAttackerWatcher.class); if (watcher != null) { diff --git a/Mage.Sets/src/mage/cards/g/GnarledSage.java b/Mage.Sets/src/mage/cards/g/GnarledSage.java new file mode 100644 index 0000000000..8b5ff451c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GnarledSage.java @@ -0,0 +1,72 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.game.Game; +import mage.watchers.common.CardsDrawnThisTurnWatcher; + +import java.util.UUID; + +/** + * @author arcox + */ +public final class GnarledSage extends CardImpl { + + public GnarledSage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + + this.subtype.add(SubType.TREEFOLK); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + this.addAbility(ReachAbility.getInstance()); + + // As long as you’ve drawn two or more cards this turn, Gnarled Sage gets +0/+2 and has vigilance. + Ability ability = new SimpleStaticAbility( + new ConditionalContinuousEffect( + new BoostSourceEffect(0, 2, Duration.WhileOnBattlefield), + GnarledSageCondition.instance, + "As long as you've drawn two or more cards this turn, {this} gets +0/+2" + )); + + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield), + GnarledSageCondition.instance, + "and has vigilance" + )); + + this.addAbility(ability, new CardsDrawnThisTurnWatcher()); + } + + private GnarledSage(final GnarledSage card) { + super(card); + } + + @Override + public GnarledSage copy() { + return new GnarledSage(this); + } +} + +enum GnarledSageCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + CardsDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsDrawnThisTurnWatcher.class); + return watcher != null && watcher.getCardsDrawnThisTurn(source.getControllerId()) > 1; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GoForBlood.java b/Mage.Sets/src/mage/cards/g/GoForBlood.java new file mode 100644 index 0000000000..8dc6a87c62 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoForBlood.java @@ -0,0 +1,40 @@ +package mage.cards.g; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.FightTargetsEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoForBlood extends CardImpl { + + public GoForBlood(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); + + // Target creature you control fights target creature you don't control. + this.getSpellAbility().addEffect(new FightTargetsEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + + // Cycling {1} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}"))); + } + + private GoForBlood(final GoForBlood card) { + super(card); + } + + @Override + public GoForBlood copy() { + return new GoForBlood(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/Goatnap.java b/Mage.Sets/src/mage/cards/g/Goatnap.java index 72ae6a27d4..4da5af8f5f 100644 --- a/Mage.Sets/src/mage/cards/g/Goatnap.java +++ b/Mage.Sets/src/mage/cards/g/Goatnap.java @@ -33,7 +33,7 @@ public final class Goatnap extends CardImpl { this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature")); this.getSpellAbility().addEffect(new GainAbilityTargetEffect( HasteAbility.getInstance(), Duration.EndOfTurn - ).setText("It gains haste until end of turn.")); + ).setText("It gains haste until end of turn")); this.getSpellAbility().addEffect(new GoatnapEffect()); } diff --git a/Mage.Sets/src/mage/cards/g/GoblinArsonist.java b/Mage.Sets/src/mage/cards/g/GoblinArsonist.java index df8e6b48d3..3e6e67a7ff 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinArsonist.java +++ b/Mage.Sets/src/mage/cards/g/GoblinArsonist.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -27,7 +27,7 @@ public final class GoblinArsonist extends CardImpl { this.toughness = new MageInt(1); // When Goblin Arsonist dies, you may have it deal 1 damage to any target. - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(1), true); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(1), true); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GoblinArtisans.java b/Mage.Sets/src/mage/cards/g/GoblinArtisans.java index 876e1ee6dc..1e95ff9b77 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinArtisans.java +++ b/Mage.Sets/src/mage/cards/g/GoblinArtisans.java @@ -79,7 +79,7 @@ class GoblinArtisansEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { if (controller.flipCoin(source, game, true)) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } else { List artifacts = game.getBattlefield().getActivePermanents(new FilterControlledArtifactPermanent(), source.getControllerId(), game); if (artifacts.isEmpty()) {//Don't even bother if there is no artifact to 'counter'/sacrifice diff --git a/Mage.Sets/src/mage/cards/g/GoblinAssassin.java b/Mage.Sets/src/mage/cards/g/GoblinAssassin.java index c94cb90e39..20e0ebebe1 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinAssassin.java +++ b/Mage.Sets/src/mage/cards/g/GoblinAssassin.java @@ -1,44 +1,44 @@ - package mage.cards.g; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** * @author BursegSardaukar */ public final class GoblinAssassin extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent(SubType.GOBLIN, "Goblin"); + public GoblinAssassin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); this.subtype.add(SubType.GOBLIN); this.subtype.add(SubType.ASSASSIN); - + this.power = new MageInt(2); this.toughness = new MageInt(2); // Whenever Goblin Assassin or another Goblin enters the battlefield, each player flips a coin. Each player whose coin comes up tails sacrifices a creature. - this.addAbility(new GoblinAssassinTriggeredAbiliy()); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(new GoblinAssassinTriggeredEffect(), filter)); } - public GoblinAssassin(final GoblinAssassin card) { + private GoblinAssassin(final GoblinAssassin card) { super(card); } @@ -48,55 +48,21 @@ public final class GoblinAssassin extends CardImpl { } } -class GoblinAssassinTriggeredAbiliy extends TriggeredAbilityImpl { - GoblinAssassinTriggeredAbiliy() { - super(Zone.BATTLEFIELD, new GoblinAssassinTriggeredEffect(), false); - } - - GoblinAssassinTriggeredAbiliy(final GoblinAssassinTriggeredAbiliy ability) { - super(ability); - } - - @Override - public GoblinAssassinTriggeredAbiliy copy() { - return new GoblinAssassinTriggeredAbiliy(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - UUID targetId = event.getTargetId(); - Permanent permanent = game.getPermanent(targetId); - if ((targetId.equals(this.getSourceId())) || (permanent.hasSubtype(SubType.GOBLIN, game) && !targetId.equals(this.getSourceId()))) { - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} or another Goblin enters battlefield, each player flips a coin. Each player whose coin comes up tails sacrifices a creature."; - } -} - class GoblinAssassinTriggeredEffect extends OneShotEffect { + GoblinAssassinTriggeredEffect() { super(Outcome.Sacrifice); } - GoblinAssassinTriggeredEffect(final GoblinAssassinTriggeredEffect effect) { + private GoblinAssassinTriggeredEffect(final GoblinAssassinTriggeredEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - List perms = new ArrayList<>(); + List perms = new ArrayList<>(); Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { + if (controller != null) { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null && !player.flipCoin(source, game, false)) { @@ -114,7 +80,7 @@ class GoblinAssassinTriggeredEffect extends OneShotEffect { permanent.sacrifice(source.getSourceId(), game); } } - return true; + return true; } return false; } @@ -123,4 +89,4 @@ class GoblinAssassinTriggeredEffect extends OneShotEffect { public GoblinAssassinTriggeredEffect copy() { return new GoblinAssassinTriggeredEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/g/GoblinAssaultTeam.java b/Mage.Sets/src/mage/cards/g/GoblinAssaultTeam.java index 6efdad649a..119ff344a2 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinAssaultTeam.java +++ b/Mage.Sets/src/mage/cards/g/GoblinAssaultTeam.java @@ -2,7 +2,7 @@ package mage.cards.g; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; @@ -31,7 +31,7 @@ public final class GoblinAssaultTeam extends CardImpl { this.addAbility(HasteAbility.getInstance()); // When Goblin Assault Team dies, put a +1/+1 counter on target creature you control. - Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GoblinBalloonBrigade.java b/Mage.Sets/src/mage/cards/g/GoblinBalloonBrigade.java index 679784ec56..e95ac8b313 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinBalloonBrigade.java +++ b/Mage.Sets/src/mage/cards/g/GoblinBalloonBrigade.java @@ -29,7 +29,10 @@ public final class GoblinBalloonBrigade extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{R}"))); + // {R}: Goblin Balloon Brigade gains flying until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, + new GainAbilitySourceEffect(FlyingAbility.getInstance(), + Duration.EndOfTurn), new ManaCostsImpl("{R}"))); } public GoblinBalloonBrigade(final GoblinBalloonBrigade card) { diff --git a/Mage.Sets/src/mage/cards/g/GoblinCadets.java b/Mage.Sets/src/mage/cards/g/GoblinCadets.java index dff65dab69..318f94c7f2 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinCadets.java +++ b/Mage.Sets/src/mage/cards/g/GoblinCadets.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -27,7 +27,7 @@ public final class GoblinCadets extends CardImpl { this.toughness = new MageInt(1); // Whenever Goblin Cadets blocks or becomes blocked, target opponent gains control of it. - Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(new GoblinCadetsChangeControlEffect(), false); + Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(new GoblinCadetsChangeControlEffect(), false); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GoblinClearcutter.java b/Mage.Sets/src/mage/cards/g/GoblinClearcutter.java index ed9702a2ce..74224e3c00 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinClearcutter.java +++ b/Mage.Sets/src/mage/cards/g/GoblinClearcutter.java @@ -56,10 +56,9 @@ public final class GoblinClearcutter extends CardImpl { } } -// TODO: replace by mana effect to use with mana reflection class GoblinClearCutterManaEffect extends ManaEffect { - private List netMana = new ArrayList<>(); + private final List netMana = new ArrayList<>(); public GoblinClearCutterManaEffect() { super(); diff --git a/Mage.Sets/src/mage/cards/g/GoblinEliteInfantry.java b/Mage.Sets/src/mage/cards/g/GoblinEliteInfantry.java index 4207a963e5..bc9233e335 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinEliteInfantry.java +++ b/Mage.Sets/src/mage/cards/g/GoblinEliteInfantry.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class GoblinEliteInfantry extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(-1, -1, Duration.EndOfTurn), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(-1, -1, Duration.EndOfTurn), false)); } public GoblinEliteInfantry(final GoblinEliteInfantry card) { diff --git a/Mage.Sets/src/mage/cards/g/GoblinFireFiend.java b/Mage.Sets/src/mage/cards/g/GoblinFireFiend.java index b96b3ed2a2..830bbfeeb7 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinFireFiend.java +++ b/Mage.Sets/src/mage/cards/g/GoblinFireFiend.java @@ -34,7 +34,7 @@ public final class GoblinFireFiend extends CardImpl { this.addAbility(HasteAbility.getInstance()); //Goblin Fire Fiend must be blocked if able. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect(Duration.WhileOnBattlefield))); //{R}: Goblin Fire Fiend gets +1/+0 until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}"))); diff --git a/Mage.Sets/src/mage/cards/g/GoblinFlotilla.java b/Mage.Sets/src/mage/cards/g/GoblinFlotilla.java index 2abda79241..dd281d5386 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinFlotilla.java +++ b/Mage.Sets/src/mage/cards/g/GoblinFlotilla.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfCombatTriggeredAbility; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DoUnlessControllerPaysEffect; @@ -37,7 +37,7 @@ public final class GoblinFlotilla extends CardImpl { // At the beginning of each combat, unless you pay {R}, whenever Goblin Flotilla blocks or becomes blocked by a creature this combat, that creature gains first strike until end of turn. Effect effect = new DoUnlessControllerPaysEffect( new GainAbilitySourceEffect( - new BlocksOrBecomesBlockedTriggeredAbility( + new BlocksOrBecomesBlockedSourceTriggeredAbility( new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, "Blocks or Blocked by Goblin Flotilla"), diff --git a/Mage.Sets/src/mage/cards/g/GoblinGardener.java b/Mage.Sets/src/mage/cards/g/GoblinGardener.java index 63f5b18591..ae7e2be66e 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinGardener.java +++ b/Mage.Sets/src/mage/cards/g/GoblinGardener.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class GoblinGardener extends CardImpl { this.toughness = new MageInt(1); // When Goblin Gardener dies, destroy target land. - Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), false); ability.addTarget(new TargetLandPermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GoblinMasons.java b/Mage.Sets/src/mage/cards/g/GoblinMasons.java index 7d6fff4100..4afef73b5c 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinMasons.java +++ b/Mage.Sets/src/mage/cards/g/GoblinMasons.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -33,7 +33,7 @@ public final class GoblinMasons extends CardImpl { this.toughness = new MageInt(1); //When Goblin Masons dies, destroy target Wall - DiesTriggeredAbility ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false); + DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), false); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GoblinSwineRider.java b/Mage.Sets/src/mage/cards/g/GoblinSwineRider.java index 44cd1c855b..97d586821b 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinSwineRider.java +++ b/Mage.Sets/src/mage/cards/g/GoblinSwineRider.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.DamageAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class GoblinSwineRider extends CardImpl { this.toughness = new MageInt(1); // Whenever Goblin Swine-Rider becomes blocked, it deals 2 damage to each attacking creature and each blocking creature. - this.addAbility(new BecomesBlockedTriggeredAbility(new DamageAllEffect(2, "it", new FilterAttackingOrBlockingCreature("attacking creature and each blocking creature")), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DamageAllEffect(2, "it", new FilterAttackingOrBlockingCreature("attacking creature and each blocking creature")), false)); } public GoblinSwineRider(final GoblinSwineRider card) { diff --git a/Mage.Sets/src/mage/cards/g/GoblinTinkerer.java b/Mage.Sets/src/mage/cards/g/GoblinTinkerer.java index d16991a09b..98604a8c2e 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinTinkerer.java +++ b/Mage.Sets/src/mage/cards/g/GoblinTinkerer.java @@ -71,7 +71,7 @@ class GoblinTinkererDamageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent targetArtifact = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent targetArtifact = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && targetArtifact != null) { Permanent sourceObject = game.getPermanent(source.getSourceId()); int damage = targetArtifact.getConvertedManaCost(); diff --git a/Mage.Sets/src/mage/cards/g/GoblinWarchief.java b/Mage.Sets/src/mage/cards/g/GoblinWarchief.java index 13106e9ba2..9ae9194961 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinWarchief.java +++ b/Mage.Sets/src/mage/cards/g/GoblinWarchief.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -16,8 +14,9 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterCreaturePermanent; +import java.util.UUID; + /** - * * @author jonubuu */ public final class GoblinWarchief extends CardImpl { @@ -38,6 +37,7 @@ public final class GoblinWarchief extends CardImpl { // Goblin spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filterSpells, 1))); + // Goblins you control have haste. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, new FilterCreaturePermanent(SubType.GOBLIN, "Goblins"), false))); diff --git a/Mage.Sets/src/mage/cards/g/GoblinWizardry.java b/Mage.Sets/src/mage/cards/g/GoblinWizardry.java new file mode 100644 index 0000000000..c24367aeac --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoblinWizardry.java @@ -0,0 +1,31 @@ +package mage.cards.g; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.GoblinWizardToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoblinWizardry extends CardImpl { + + public GoblinWizardry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}"); + + // Create two 1/1 red Goblin Wizard creature tokens with prowess. + this.getSpellAbility().addEffect(new CreateTokenEffect(new GoblinWizardToken(), 2)); + } + + private GoblinWizardry(final GoblinWizardry card) { + super(card); + } + + @Override + public GoblinWizardry copy() { + return new GoblinWizardry(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GodEternalBontu.java b/Mage.Sets/src/mage/cards/g/GodEternalBontu.java index ab11688724..b52d00e9b7 100644 --- a/Mage.Sets/src/mage/cards/g/GodEternalBontu.java +++ b/Mage.Sets/src/mage/cards/g/GodEternalBontu.java @@ -96,6 +96,6 @@ class GodEternalBontuEffect extends OneShotEffect { counter++; } } - return player.drawCards(counter, game) > 0; + return player.drawCards(counter, source.getSourceId(), game) > 0; } } diff --git a/Mage.Sets/src/mage/cards/g/GodEternalKefnet.java b/Mage.Sets/src/mage/cards/g/GodEternalKefnet.java index 3aecf9aeb7..626c5868a0 100644 --- a/Mage.Sets/src/mage/cards/g/GodEternalKefnet.java +++ b/Mage.Sets/src/mage/cards/g/GodEternalKefnet.java @@ -1,5 +1,7 @@ package mage.cards.g; +import java.awt.*; +import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; @@ -20,9 +22,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.watchers.common.CardsAmountDrawnThisTurnWatcher; -import java.awt.*; -import java.util.UUID; - /** * @author JayDi85 */ @@ -94,7 +93,7 @@ class GodEternalKefnetDrawCardReplacementEffect extends ReplacementEffectImpl { // cast copy if (topCard.isInstantOrSorcery() && you.chooseUse(outcome, "Would you like to copy " + topCard.getName() - + " and cast it {2} less?", source, game)) { + + " and cast it for {2} less?", source, game)) { Card blueprint = topCard.copy(); blueprint.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2))); Card copiedCard = game.copyCard(blueprint, source, source.getControllerId()); @@ -154,4 +153,3 @@ class GodEternalKefnetDrawCardReplacementEffect extends ReplacementEffectImpl { } } - diff --git a/Mage.Sets/src/mage/cards/g/GodPharaohsStatue.java b/Mage.Sets/src/mage/cards/g/GodPharaohsStatue.java index 2e2c67444d..1fbd05ac20 100644 --- a/Mage.Sets/src/mage/cards/g/GodPharaohsStatue.java +++ b/Mage.Sets/src/mage/cards/g/GodPharaohsStatue.java @@ -1,16 +1,15 @@ package mage.cards.g; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.LoseLifeOpponentsEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterCard; import java.util.UUID; @@ -25,7 +24,7 @@ public final class GodPharaohsStatue extends CardImpl { this.addSuperType(SuperType.LEGENDARY); // Spells your opponents cast cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(new GodPharaohsStatueEffect())); + this.addAbility(new SimpleStaticAbility(new SpellsCostIncreasingAllEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))); // At the beginning of your end step, each opponent loses 1 life. this.addAbility(new BeginningOfEndStepTriggeredAbility( @@ -42,40 +41,3 @@ public final class GodPharaohsStatue extends CardImpl { return new GodPharaohsStatue(this); } } - -class GodPharaohsStatueEffect extends CostModificationEffectImpl { - - private static final String effectText = "Spells your opponents cast cost {2} more to cast"; - - GodPharaohsStatueEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = effectText; - } - - private GodPharaohsStatueEffect(GodPharaohsStatueEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - return true; - } - } - return false; - } - - @Override - public GodPharaohsStatueEffect copy() { - return new GodPharaohsStatueEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/g/GolemSkinGauntlets.java b/Mage.Sets/src/mage/cards/g/GolemSkinGauntlets.java index 9c9e80861d..e64712a120 100644 --- a/Mage.Sets/src/mage/cards/g/GolemSkinGauntlets.java +++ b/Mage.Sets/src/mage/cards/g/GolemSkinGauntlets.java @@ -31,6 +31,8 @@ public final class GolemSkinGauntlets extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); this.subtype.add(SubType.EQUIPMENT); + // Equipped creature gets +1/+0 for each Equipment attached to it. + // Equip 2 (2: Attach to target creature you control. Equip only as a sorcery. This card enters the battlefield unattached and stays on the battlefield if the creature leaves.) this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(new GolemSkinGauntletsAttachedCount(), StaticValue.get(0), Duration.WhileOnBattlefield))); this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(2))); } diff --git a/Mage.Sets/src/mage/cards/g/GolgariFindbroker.java b/Mage.Sets/src/mage/cards/g/GolgariFindbroker.java index e95d580c61..06ee57f2aa 100644 --- a/Mage.Sets/src/mage/cards/g/GolgariFindbroker.java +++ b/Mage.Sets/src/mage/cards/g/GolgariFindbroker.java @@ -1,27 +1,24 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; -import mage.filter.common.FilterPermanentCard; +import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** * * @author TheElk801 */ public final class GolgariFindbroker extends CardImpl { - private static final FilterCard filter - = new FilterPermanentCard("permanent card from your graveyard"); - public GolgariFindbroker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}{G}{G}"); @@ -32,9 +29,10 @@ public final class GolgariFindbroker extends CardImpl { // When Golgari Findbroker enters the battlefield, return target permanent card from your graveyard to your hand. Ability ability = new EntersBattlefieldTriggeredAbility( - new ReturnFromGraveyardToHandTargetEffect(), false + new ReturnFromGraveyardToHandTargetEffect().setText("return target permanent card from your graveyard to your hand"), + false ); - ability.addTarget(new TargetCardInYourGraveyard(filter)); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_PERMANENT)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GolgariThug.java b/Mage.Sets/src/mage/cards/g/GolgariThug.java index 98b5759fed..2054216b9a 100644 --- a/Mage.Sets/src/mage/cards/g/GolgariThug.java +++ b/Mage.Sets/src/mage/cards/g/GolgariThug.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.abilities.keyword.DredgeAbility; import mage.cards.CardImpl; @@ -35,7 +35,7 @@ public final class GolgariThug extends CardImpl { this.toughness = new MageInt(1); // When Golgari Thug dies, put target creature card from your graveyard on top of your library. - Ability ability = new DiesTriggeredAbility(new PutOnLibraryTargetEffect(true)); + Ability ability = new DiesSourceTriggeredAbility(new PutOnLibraryTargetEffect(true)); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); // Dredge 4 diff --git a/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java b/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java index 1f4d3077e2..abd9d19893 100644 --- a/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java +++ b/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java @@ -7,9 +7,8 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -19,7 +18,6 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; -import mage.target.targetpointer.FixedTarget; /** * @author TheElk801 @@ -75,15 +73,8 @@ class GolosTirelessPilgrimEffect extends OneShotEffect { return false; } Set cards = player.getLibrary().getTopCards(game, 3); - player.moveCards(cards, Zone.EXILED, source, game); - cards.stream() - .filter(card -> game.getState().getZone(card.getId()) == Zone.EXILED) - .forEach(card -> { - ContinuousEffect effect = new GolosTirelessPilgrimCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card, game)); - game.addEffect(effect, source); - }); - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, cards, + TargetController.YOU, Duration.EndOfTurn, true); } @Override @@ -91,41 +82,3 @@ class GolosTirelessPilgrimEffect extends OneShotEffect { return new GolosTirelessPilgrimEffect(this); } } - -class GolosTirelessPilgrimCastFromExileEffect extends AsThoughEffectImpl { - - GolosTirelessPilgrimCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - } - - private GolosTirelessPilgrimCastFromExileEffect(final GolosTirelessPilgrimCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public GolosTirelessPilgrimCastFromExileEffect copy() { - return new GolosTirelessPilgrimCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!objectId.equals(getTargetPointer().getFirst(game, source)) - || !affectedControllerId.equals(source.getControllerId())) { - return false; - } - Card card = game.getCard(objectId); - if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) { - return true; - } - Player player = game.getPlayer(affectedControllerId); - if (player != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/g/GontisAetherHeart.java b/Mage.Sets/src/mage/cards/g/GontisAetherHeart.java index edac7052ff..4f35197bd9 100644 --- a/Mage.Sets/src/mage/cards/g/GontisAetherHeart.java +++ b/Mage.Sets/src/mage/cards/g/GontisAetherHeart.java @@ -1,9 +1,8 @@ package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.ExileSourceCost; import mage.abilities.costs.common.PayEnergyCost; @@ -13,37 +12,33 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterArtifactPermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author fireshoes */ public final class GontisAetherHeart extends CardImpl { - private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("{this} or another artifact"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - public GontisAetherHeart(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}"); addSuperType(SuperType.LEGENDARY); // Whenever Gonti's Aether Heart or another artifact enters the battlefield under your control, you get {E}{E} (two energy counters). - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new GetEnergyCountersControllerEffect(2), filter, false)); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new GetEnergyCountersControllerEffect(2), + StaticFilters.FILTER_PERMANENT_ARTIFACT, false, true + )); // Pay {E}{E}{E}{E}{E}{E}{E}{E}, Exile Gonti's Aether Heart: Take an extra turn after this one. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddExtraTurnControllerEffect(), new PayEnergyCost(8)); + Ability ability = new SimpleActivatedAbility(new AddExtraTurnControllerEffect(), new PayEnergyCost(8)); ability.addCost(new ExileSourceCost()); this.addAbility(ability); } - public GontisAetherHeart(final GontisAetherHeart card) { + private GontisAetherHeart(final GontisAetherHeart card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/g/GoreVassal.java b/Mage.Sets/src/mage/cards/g/GoreVassal.java index cdfbfbb094..4435be0dac 100644 --- a/Mage.Sets/src/mage/cards/g/GoreVassal.java +++ b/Mage.Sets/src/mage/cards/g/GoreVassal.java @@ -23,7 +23,7 @@ public final class GoreVassal extends CardImpl { public GoreVassal(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/g/Goremand.java b/Mage.Sets/src/mage/cards/g/Goremand.java new file mode 100644 index 0000000000..89a708bb09 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Goremand.java @@ -0,0 +1,55 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.SacrificeOpponentsEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Goremand extends CardImpl { + + public Goremand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); + + this.subtype.add(SubType.DEMON); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // As an additional cost to cast this spell, sacrifice a creature. + this.getSpellAbility().addCost(new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + )); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Goremand enters the battlefield, each opponent sacrifices a creature. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SacrificeOpponentsEffect(StaticFilters.FILTER_PERMANENT_A_CREATURE) + )); + } + + private Goremand(final Goremand card) { + super(card); + } + + @Override + public Goremand copy() { + return new Goremand(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GorgingVulture.java b/Mage.Sets/src/mage/cards/g/GorgingVulture.java index 5f042ced90..1c35c788b6 100644 --- a/Mage.Sets/src/mage/cards/g/GorgingVulture.java +++ b/Mage.Sets/src/mage/cards/g/GorgingVulture.java @@ -5,7 +5,9 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; @@ -48,8 +50,7 @@ class GorgingVultureEffect extends OneShotEffect { GorgingVultureEffect() { super(Outcome.Benefit); - staticText = "put the top four cards of your library into your graveyard. " + - "You gain 1 life for each creature card put into your graveyard this way."; + staticText = "mill four cards. You gain 1 life for each creature card put into your graveyard this way."; } private GorgingVultureEffect(final GorgingVultureEffect effect) { @@ -67,9 +68,8 @@ class GorgingVultureEffect extends OneShotEffect { if (player == null) { return false; } - Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 4)); - player.moveCards(cards, Zone.GRAVEYARD, source, game); - int lifeToGain = cards + int lifeToGain = player + .millCards(4, source, game) .getCards(game) .stream() .filter(Card::isCreature) diff --git a/Mage.Sets/src/mage/cards/g/GorgonRecluse.java b/Mage.Sets/src/mage/cards/g/GorgonRecluse.java index f9730324bd..1fa3627c58 100644 --- a/Mage.Sets/src/mage/cards/g/GorgonRecluse.java +++ b/Mage.Sets/src/mage/cards/g/GorgonRecluse.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; @@ -40,7 +40,7 @@ public final class GorgonRecluse extends CardImpl { // Whenever Gorgon Recluse blocks or becomes blocked by a nonblack creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false)); // Madness {B}{B} this.addAbility(new MadnessAbility(this, new ManaCostsImpl("{B}{B}"))); diff --git a/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java b/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java index 93c9692277..518db6f2ab 100644 --- a/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java +++ b/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java @@ -1,37 +1,33 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class GrandArbiterAugustinIV extends CardImpl { private static final FilterCard filterWhite = new FilterCard("White spells"); private static final FilterCard filterBlue = new FilterCard("Blue spells"); + static { filterWhite.add(new ColorPredicate(ObjectColor.WHITE)); filterBlue.add(new ColorPredicate(ObjectColor.BLUE)); } public GrandArbiterAugustinIV(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ADVISOR); @@ -41,10 +37,12 @@ public final class GrandArbiterAugustinIV extends CardImpl { // White spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filterWhite, 1))); + // Blue spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filterBlue, 1))); + // Spells your opponents cast cost {1} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GrandArbiterAugustinIVCostIncreaseEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, new FilterCard("Spells"), TargetController.OPPONENT))); } public GrandArbiterAugustinIV(final GrandArbiterAugustinIV card) { @@ -56,40 +54,3 @@ public final class GrandArbiterAugustinIV extends CardImpl { return new GrandArbiterAugustinIV(this); } } - -class GrandArbiterAugustinIVCostIncreaseEffect extends CostModificationEffectImpl { - - private static final String effectText = "Spells your opponents cast cost {1} more to cast"; - - GrandArbiterAugustinIVCostIncreaseEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = effectText; - } - - GrandArbiterAugustinIVCostIncreaseEffect(GrandArbiterAugustinIVCostIncreaseEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -1); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - return true; - } - } - return false; - } - - @Override - public GrandArbiterAugustinIVCostIncreaseEffect copy() { - return new GrandArbiterAugustinIVCostIncreaseEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/g/GrandArchitect.java b/Mage.Sets/src/mage/cards/g/GrandArchitect.java index 8907f8e8dc..c7b26b830d 100644 --- a/Mage.Sets/src/mage/cards/g/GrandArchitect.java +++ b/Mage.Sets/src/mage/cards/g/GrandArchitect.java @@ -25,6 +25,7 @@ import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; +import mage.filter.FilterPermanent; /** * @author BetaSteward_at_googlemail.com, nantuko @@ -56,7 +57,10 @@ public final class GrandArchitect extends CardImpl { this.addAbility(ability); // Tap an untapped blue creature you control: Add {C}{C}. Spend this mana only to cast artifact spells or activate abilities of artifacts. - this.addAbility(new GrandArchitectManaAbility()); + FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped blue creature"); + filter.add(new ColorPredicate(ObjectColor.BLUE)); + filter.add(Predicates.not(TappedPredicate.instance)); + this.addAbility(new GrandArchitectManaAbility(filter)); } public GrandArchitect(final GrandArchitect card) { @@ -104,20 +108,18 @@ class GrandArchitectEffect extends ContinuousEffectImpl { class GrandArchitectManaAbility extends ActivatedManaAbilityImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped blue creature"); + private final FilterPermanent filter; - static { - filter.add(new ColorPredicate(ObjectColor.BLUE)); - filter.add(Predicates.not(TappedPredicate.instance)); - } - - GrandArchitectManaAbility() { - super(Zone.BATTLEFIELD, new BasicManaEffect(new GrandArchitectConditionalMana()), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))); - this.netMana.add(Mana.ColorlessMana(2)); + GrandArchitectManaAbility(FilterControlledCreaturePermanent filter) { + super(Zone.BATTLEFIELD, new BasicManaEffect(new GrandArchitectConditionalMana()), + new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))); + this.netMana.add(new GrandArchitectConditionalMana()); + this.filter = filter; } GrandArchitectManaAbility(GrandArchitectManaAbility ability) { super(ability); + this.filter = ability.filter.copy(); } @Override diff --git a/Mage.Sets/src/mage/cards/g/GrandMoffTarkin.java b/Mage.Sets/src/mage/cards/g/GrandMoffTarkin.java index 3299f166c3..a14a1d58dd 100644 --- a/Mage.Sets/src/mage/cards/g/GrandMoffTarkin.java +++ b/Mage.Sets/src/mage/cards/g/GrandMoffTarkin.java @@ -137,7 +137,7 @@ class GrandMoffTarkinEffect extends OneShotEffect { game.informPlayers(player.getLogName() + " pays 2 life to prevent " + targetCreature.getName() + " being destroyed"); Player sourceController = game.getPlayer(source.getControllerId()); if (sourceController != null) { - sourceController.drawCards(1, game); + sourceController.drawCards(1, source.getSourceId(), game); } return true; diff --git a/Mage.Sets/src/mage/cards/g/Grapeshot.java b/Mage.Sets/src/mage/cards/g/Grapeshot.java index 82a6a8b5c6..5101aaa10f 100644 --- a/Mage.Sets/src/mage/cards/g/Grapeshot.java +++ b/Mage.Sets/src/mage/cards/g/Grapeshot.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.StormAbility; import mage.cards.CardImpl; @@ -9,19 +7,20 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author Plopman */ public final class Grapeshot extends CardImpl { public Grapeshot(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); // Grapeshot deals 1 damage to any target. this.getSpellAbility().addTarget(new TargetAnyTarget()); this.getSpellAbility().addEffect(new DamageTargetEffect(1)); + // Storm this.addAbility(new StormAbility()); } diff --git a/Mage.Sets/src/mage/cards/g/GraveBetrayal.java b/Mage.Sets/src/mage/cards/g/GraveBetrayal.java index fb1ccc708a..21ad036129 100644 --- a/Mage.Sets/src/mage/cards/g/GraveBetrayal.java +++ b/Mage.Sets/src/mage/cards/g/GraveBetrayal.java @@ -79,7 +79,7 @@ class GraveBetrayalTriggeredAbility extends TriggeredAbilityImpl { Card card = (Card) game.getObject(permanent.getId()); if (card != null) { Effect effect = new GraveBetrayalEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, this); return true; diff --git a/Mage.Sets/src/mage/cards/g/GraveStrength.java b/Mage.Sets/src/mage/cards/g/GraveStrength.java index 229eef94cb..84bdb9d410 100644 --- a/Mage.Sets/src/mage/cards/g/GraveStrength.java +++ b/Mage.Sets/src/mage/cards/g/GraveStrength.java @@ -24,7 +24,7 @@ public final class GraveStrength extends CardImpl { // Choose target creature. Put the top three cards of your library into your graveyard, then put a +1/+1 counter on that creature for each creature card in your graveyard. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); Effect effect = new PutTopCardOfLibraryIntoGraveControllerEffect(3); - effect.setText("Choose target creature. Put the top three cards of your library into your graveyard"); + effect.setText("Choose target creature. Mill three cards"); this.getSpellAbility().addEffect(effect); effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(0), new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE)); effect.setText(", then put a +1/+1 counter on that creature for each creature card in your graveyard"); diff --git a/Mage.Sets/src/mage/cards/g/GraveUpheaval.java b/Mage.Sets/src/mage/cards/g/GraveUpheaval.java index f75d9f3789..3afdacc9c7 100644 --- a/Mage.Sets/src/mage/cards/g/GraveUpheaval.java +++ b/Mage.Sets/src/mage/cards/g/GraveUpheaval.java @@ -1,26 +1,15 @@ package mage.cards.g; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect; import mage.abilities.keyword.BasicLandcyclingAbility; -import mage.abilities.keyword.HasteAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCardInGraveyard; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** * @@ -32,7 +21,7 @@ public final class GraveUpheaval extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{R}"); // Put target creature card from a graveyard onto the battlefield under your control. It gains haste. - this.getSpellAbility().addEffect(new GraveUpheavalEffect()); + this.getSpellAbility().addEffect(new ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect()); this.getSpellAbility().addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE)); // Basic landcycling {2} @@ -49,40 +38,3 @@ public final class GraveUpheaval extends CardImpl { } } -class GraveUpheavalEffect extends OneShotEffect { - - public GraveUpheavalEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "Put target creature card from a graveyard onto the battlefield under your control. It gains haste"; - } - - public GraveUpheavalEffect(final GraveUpheavalEffect effect) { - super(effect); - } - - @Override - public GraveUpheavalEffect copy() { - return new GraveUpheavalEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - Card card = game.getCard(source.getFirstTarget()); - if (card != null) { - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); - if (permanent != null) { - ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); - effect.setTargetPointer(new FixedTarget(permanent, game)); - game.addEffect(effect, source); - } - return true; - } - - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/g/GravebaneZombie.java b/Mage.Sets/src/mage/cards/g/GravebaneZombie.java index 43d6a30779..6717c31377 100644 --- a/Mage.Sets/src/mage/cards/g/GravebaneZombie.java +++ b/Mage.Sets/src/mage/cards/g/GravebaneZombie.java @@ -18,6 +18,7 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; +import mage.players.Player; /** * @@ -50,7 +51,7 @@ class GravebaneZombieEffect extends ReplacementEffectImpl { GravebaneZombieEffect() { super(Duration.WhileOnBattlefield, Outcome.Neutral); - staticText = "If {this} would die, put Gravebane Zombie on top of its owner's library instead"; + staticText = "If {this} would die, put {this} on top of its owner's library instead"; } GravebaneZombieEffect(final GravebaneZombieEffect effect) { @@ -60,11 +61,9 @@ class GravebaneZombieEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent permanent = ((ZoneChangeEvent) event).getTarget(); - if (permanent != null) { - if (permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true)) { - game.informPlayers(permanent.getName() + " was put on the top of its owner's library"); - return true; - } + Player controller = game.getPlayer(source.getControllerId()); + if (permanent != null && controller !=null) { + return controller.putCardsOnTopOfLibrary(permanent, game, source, true); } return false; } diff --git a/Mage.Sets/src/mage/cards/g/Gravegouger.java b/Mage.Sets/src/mage/cards/g/Gravegouger.java index 57f99373d0..ea67ffe519 100644 --- a/Mage.Sets/src/mage/cards/g/Gravegouger.java +++ b/Mage.Sets/src/mage/cards/g/Gravegouger.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.cards.CardImpl; @@ -12,7 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInASingleGraveyard; import java.util.UUID; @@ -30,9 +29,8 @@ public final class Gravegouger extends CardImpl { this.toughness = new MageInt(2); // When Gravegouger enters the battlefield, exile up to two target cards from a single graveyard. - Effect effect = new ExileTargetForSourceEffect(); - Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); - ability.addTarget(new TargetCardInASingleGraveyard(0, 2, new FilterCard("cards from a single graveyard"))); + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false); + ability.addTarget(new TargetCardInASingleGraveyard(0, 2, StaticFilters.FILTER_CARD_CARDS)); this.addAbility(ability); // When Gravegouger leaves the battlefield, return the exiled cards to their owner's graveyard. diff --git a/Mage.Sets/src/mage/cards/g/Gravestorm.java b/Mage.Sets/src/mage/cards/g/Gravestorm.java index 7e9074117f..3dead51265 100644 --- a/Mage.Sets/src/mage/cards/g/Gravestorm.java +++ b/Mage.Sets/src/mage/cards/g/Gravestorm.java @@ -79,7 +79,7 @@ class GravestormEffect extends OneShotEffect { if (!opponentExilesACard) { if (you.chooseUse(Outcome.DrawCard, "Draw a card?", source, game)) { - you.drawCards(1, game); + you.drawCards(1, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/g/GraveyardShovel.java b/Mage.Sets/src/mage/cards/g/GraveyardShovel.java index b19130c100..9e348846c1 100644 --- a/Mage.Sets/src/mage/cards/g/GraveyardShovel.java +++ b/Mage.Sets/src/mage/cards/g/GraveyardShovel.java @@ -69,7 +69,6 @@ class GraveyardShovelEffect extends OneShotEffect { if (targetPlayer.chooseTarget(Outcome.Exile, target, source, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { - targetPlayer.getGraveyard().remove(card); card.moveToExile(null, "", source.getSourceId(), game); if (card.isCreature()) { controller.gainLife(2, game, source); diff --git a/Mage.Sets/src/mage/cards/g/GraviticPunch.java b/Mage.Sets/src/mage/cards/g/GraviticPunch.java index 41344214d2..ee8559365b 100644 --- a/Mage.Sets/src/mage/cards/g/GraviticPunch.java +++ b/Mage.Sets/src/mage/cards/g/GraviticPunch.java @@ -62,7 +62,7 @@ class GraviticPunchEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent controlledCreature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent controlledCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Player player = game.getPlayer(source.getTargets().get(1).getFirstTarget()); if (player == null || controlledCreature == null) { return false; diff --git a/Mage.Sets/src/mage/cards/g/GravityWell.java b/Mage.Sets/src/mage/cards/g/GravityWell.java index 00f083c6d7..0efefec910 100644 --- a/Mage.Sets/src/mage/cards/g/GravityWell.java +++ b/Mage.Sets/src/mage/cards/g/GravityWell.java @@ -100,7 +100,7 @@ class GravityWellEffect extends ContinuousEffectImpl { switch (layer) { case AbilityAddingRemovingEffects_6: if (sublayer == SubLayer.NA) { - permanent.getAbilities().removeIf(entry -> entry.getId().equals(FlyingAbility.getInstance().getId())); + permanent.removeAbility(FlyingAbility.getInstance(), source.getSourceId(), game); } break; } diff --git a/Mage.Sets/src/mage/cards/g/GreaterMossdog.java b/Mage.Sets/src/mage/cards/g/GreaterMossdog.java index 605af0ca90..13c03b9187 100644 --- a/Mage.Sets/src/mage/cards/g/GreaterMossdog.java +++ b/Mage.Sets/src/mage/cards/g/GreaterMossdog.java @@ -18,7 +18,7 @@ public final class GreaterMossdog extends CardImpl { public GreaterMossdog(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); this.subtype.add(SubType.PLANT); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/g/GreaterWerewolf.java b/Mage.Sets/src/mage/cards/g/GreaterWerewolf.java index 5c05d3c4be..6022daf232 100644 --- a/Mage.Sets/src/mage/cards/g/GreaterWerewolf.java +++ b/Mage.Sets/src/mage/cards/g/GreaterWerewolf.java @@ -72,7 +72,7 @@ class GreaterWerewolfEffect extends OneShotEffect { filter.add(Predicates.or(new BlockedByIdPredicate(sourcePermanent.getId()), new BlockingAttackerIdPredicate(sourcePermanent.getId()))); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { Effect effect = new AddCountersTargetEffect(new BoostCounter(0, -2), Outcome.UnboostCreature); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); } return true; diff --git a/Mage.Sets/src/mage/cards/g/Greedo.java b/Mage.Sets/src/mage/cards/g/Greedo.java index 16bf9f64eb..199ed4564f 100644 --- a/Mage.Sets/src/mage/cards/g/Greedo.java +++ b/Mage.Sets/src/mage/cards/g/Greedo.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; @@ -45,7 +45,7 @@ public final class Greedo extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter, "Creatures blocking or blocked by {this} have first strike"))); // When Greedo dies, you may search your library for Hunter or Rogue card, reveal it, and put it into your hand. - this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filterCard), true), true)); + this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filterCard), true), true)); } public Greedo(final Greedo card) { diff --git a/Mage.Sets/src/mage/cards/g/GreelMindRaker.java b/Mage.Sets/src/mage/cards/g/GreelMindRaker.java index bc79a60e01..d615d2a683 100644 --- a/Mage.Sets/src/mage/cards/g/GreelMindRaker.java +++ b/Mage.Sets/src/mage/cards/g/GreelMindRaker.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -15,17 +13,19 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterCard; import mage.target.TargetPlayer; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * * @author Styxo */ public final class GreelMindRaker extends CardImpl { + private static final FilterCard filter = new FilterCard("two cards"); + public GreelMindRaker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); @@ -36,14 +36,16 @@ public final class GreelMindRaker extends CardImpl { this.toughness = new MageInt(3); // {X}{B}, {tap}, Discard two cards: Target player discards X cards at random. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DiscardTargetEffect(ManacostVariableValue.instance, true), new ManaCostsImpl("{X}{B}")); + Ability ability = new SimpleActivatedAbility(new DiscardTargetEffect( + ManacostVariableValue.instance, true + ), new ManaCostsImpl("{X}{B}")); ability.addCost(new TapSourceCost()); - ability.addCost(new DiscardTargetCost(new TargetCardInHand(2, new FilterCard()))); + ability.addCost(new DiscardTargetCost(new TargetCardInHand(2, filter))); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } - public GreelMindRaker(final GreelMindRaker card) { + private GreelMindRaker(final GreelMindRaker card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/g/GreenManaBattery.java b/Mage.Sets/src/mage/cards/g/GreenManaBattery.java index 9669605db2..46d3669bdf 100644 --- a/Mage.Sets/src/mage/cards/g/GreenManaBattery.java +++ b/Mage.Sets/src/mage/cards/g/GreenManaBattery.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.UUID; @@ -28,18 +27,20 @@ public final class GreenManaBattery extends CardImpl { public GreenManaBattery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {2}, {tap}: Put a charge counter on Green Mana Battery. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance(1)), new GenericManaCost(2)); + // {2}, {T}: Put a charge counter on Green Mana Battery. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); this.addAbility(ability); - // {tap}, Remove any number of charge counters from Green Mana Battery: Add {G}, then add an additional {G} for each charge counter removed this way. + // {T}, Remove any number of charge counters from Green Mana Battery: Add {G}, + // then add an additional {G} for each charge counter removed this way. ability = new DynamicManaAbility( Mana.GreenMana(1), new IntPlusDynamicValue(1, RemovedCountersForCostValue.instance), new TapSourceCost(), "Add {G}, then add {G} for each charge counter removed this way", - true, new CountersSourceCount(CounterType.CHARGE)); + true, new IntPlusDynamicValue(1, new CountersSourceCount(CounterType.CHARGE))); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE.createInstance(), "Remove any number of charge counters from {this}")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GreenwardenOfMurasa.java b/Mage.Sets/src/mage/cards/g/GreenwardenOfMurasa.java index 3a2bdfcaaa..ab52e22056 100644 --- a/Mage.Sets/src/mage/cards/g/GreenwardenOfMurasa.java +++ b/Mage.Sets/src/mage/cards/g/GreenwardenOfMurasa.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.costs.common.ExileSourceFromGraveCost; import mage.abilities.effects.common.DoIfCostPaid; @@ -33,7 +33,7 @@ public final class GreenwardenOfMurasa extends CardImpl { this.addAbility(ability); // When Greenwarden of Murasa dies, you may exile it. If you do, return target card from your graveyard to your hand. - ability = new DiesTriggeredAbility(new DoIfCostPaid(new ReturnFromGraveyardToHandTargetEffect(), new ExileSourceFromGraveCost(), + ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new ReturnFromGraveyardToHandTargetEffect(), new ExileSourceFromGraveCost(), "Exile {this} and return target card from your graveyard to your hand?", true), false); ability.addTarget(new TargetCardInYourGraveyard()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GrevenPredatorCaptain.java b/Mage.Sets/src/mage/cards/g/GrevenPredatorCaptain.java index cdf3130f8d..78b9047874 100644 --- a/Mage.Sets/src/mage/cards/g/GrevenPredatorCaptain.java +++ b/Mage.Sets/src/mage/cards/g/GrevenPredatorCaptain.java @@ -124,7 +124,7 @@ class GrevenPredatorCaptainEffect extends OneShotEffect { if (!permanent.sacrifice(source.getSourceId(), game)) { return false; } - player.drawCards(power, game); + player.drawCards(power, source.getSourceId(), game); player.loseLife(toughness, game, false); return true; } diff --git a/Mage.Sets/src/mage/cards/g/GriefTyrant.java b/Mage.Sets/src/mage/cards/g/GriefTyrant.java index 603df5b941..c30b52fd2e 100644 --- a/Mage.Sets/src/mage/cards/g/GriefTyrant.java +++ b/Mage.Sets/src/mage/cards/g/GriefTyrant.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -34,7 +34,7 @@ public final class GriefTyrant extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance(4)))); // When Grief Tyrant dies, put a -1/-1 counter on target creature for each -1/-1 counter on Grief Tyrant. - Ability ability = new DiesTriggeredAbility(new GriefTyrantEffect()); + Ability ability = new DiesSourceTriggeredAbility(new GriefTyrantEffect()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GriffinAerie.java b/Mage.Sets/src/mage/cards/g/GriffinAerie.java new file mode 100644 index 0000000000..a3e4a3297c --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GriffinAerie.java @@ -0,0 +1,48 @@ +package mage.cards.g; + +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.TargetController; +import mage.game.permanent.token.GriffinToken; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GriffinAerie extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2); + private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn"); + + public GriffinAerie(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + + // At the beginning of your end step, if you gained 3 or more life this turn, create a 2/2 white Griffin creature token with flying. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new CreateTokenEffect(new GriffinToken()), TargetController.YOU, false + ), condition, "At the beginning of your end step, " + + "if you gained 3 or more life this turn, create a 2/2 white Griffin creature token with flying." + ).addHint(hint), new PlayerGainedLifeWatcher()); + } + + private GriffinAerie(final GriffinAerie card) { + super(card); + } + + @Override + public GriffinAerie copy() { + return new GriffinAerie(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GrimFeast.java b/Mage.Sets/src/mage/cards/g/GrimFeast.java index 57abc08d14..5b4c07864d 100644 --- a/Mage.Sets/src/mage/cards/g/GrimFeast.java +++ b/Mage.Sets/src/mage/cards/g/GrimFeast.java @@ -91,7 +91,7 @@ class GrimFeastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent creature = ((FixedTarget) targetPointer).getTargetedPermanentOrLKIBattlefield(game); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (creature == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/g/GrimInitiate.java b/Mage.Sets/src/mage/cards/g/GrimInitiate.java index ac59582d80..09e20471ba 100644 --- a/Mage.Sets/src/mage/cards/g/GrimInitiate.java +++ b/Mage.Sets/src/mage/cards/g/GrimInitiate.java @@ -1,7 +1,7 @@ package mage.cards.g; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.keyword.AmassEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class GrimInitiate extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // When Grim Initiate dies, amass 1. - this.addAbility(new DiesTriggeredAbility(new AmassEffect(1))); + this.addAbility(new DiesSourceTriggeredAbility(new AmassEffect(1))); } private GrimInitiate(final GrimInitiate card) { diff --git a/Mage.Sets/src/mage/cards/g/GrimPhysician.java b/Mage.Sets/src/mage/cards/g/GrimPhysician.java index 598305c58e..12daab2dd8 100644 --- a/Mage.Sets/src/mage/cards/g/GrimPhysician.java +++ b/Mage.Sets/src/mage/cards/g/GrimPhysician.java @@ -2,7 +2,7 @@ package mage.cards.g; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class GrimPhysician extends CardImpl { this.toughness = new MageInt(1); // When Grim Physician dies, target creature an opponent controls gets -1/-1 until end of turn. - Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-1, -1)); + Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-1, -1)); ability.addTarget(new TargetOpponentsCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/Grimdancer.java b/Mage.Sets/src/mage/cards/g/Grimdancer.java new file mode 100644 index 0000000000..d4e7673ab7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Grimdancer.java @@ -0,0 +1,107 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.Counter; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Grimdancer extends CardImpl { + + public Grimdancer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); + + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Grimdancer enters the battlefield with your choice of two different counters on it from among menace, deathtouch, and lifelink. + this.addAbility(new EntersBattlefieldAbility(new GrimdancerEffect())); + } + + private Grimdancer(final Grimdancer card) { + super(card); + } + + @Override + public Grimdancer copy() { + return new Grimdancer(this); + } +} + +class GrimdancerEffect extends OneShotEffect { + + private static final Set choices = new HashSet(); + + static { + choices.add("Menace and deathtouch"); + choices.add("Menace and lifelink"); + choices.add("Deathtouch and lifelink"); + } + + GrimdancerEffect() { + super(Outcome.Benefit); + staticText = "with your choice of two different counters on it from among menace, deathtouch, and lifelink"; + } + + private GrimdancerEffect(final GrimdancerEffect effect) { + super(effect); + } + + @Override + public GrimdancerEffect copy() { + return new GrimdancerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + if (player == null || permanent == null) { + return false; + } + Choice choice = new ChoiceImpl(true); + choice.setMessage("Choose two abilities"); + choice.setChoices(choices); + if (!player.choose(outcome, choice, game)) { + return false; + } + Counter counter1 = null; + Counter counter2 = null; + switch (choice.getChoice()) { + case "Menace and deathtouch": + counter1 = CounterType.MENACE.createInstance(); + counter2 = CounterType.DEATHTOUCH.createInstance(); + break; + case "Menace and lifelink": + counter1 = CounterType.MENACE.createInstance(); + counter2 = CounterType.LIFELINK.createInstance(); + break; + case "Deathtouch and lifelink": + counter1 = CounterType.DEATHTOUCH.createInstance(); + counter2 = CounterType.LIFELINK.createInstance(); + break; + } + permanent.addCounters(counter1, source, game); + permanent.addCounters(counter2, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/Grindclock.java b/Mage.Sets/src/mage/cards/g/Grindclock.java index 0a873bff09..26b5840587 100644 --- a/Mage.Sets/src/mage/cards/g/Grindclock.java +++ b/Mage.Sets/src/mage/cards/g/Grindclock.java @@ -1,7 +1,6 @@ package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -18,8 +17,9 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author Loki */ public final class Grindclock extends CardImpl { @@ -62,7 +62,7 @@ class GrindclockEffect extends OneShotEffect { int amount = sourceObject.getCounters(game).getCount(CounterType.CHARGE); Player targetPlayer = game.getPlayer(source.getFirstTarget()); if (targetPlayer != null) { - targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game); + targetPlayer.millCards(amount, source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/g/Grindstone.java b/Mage.Sets/src/mage/cards/g/Grindstone.java index 1b7c1e738d..2e5d271aac 100644 --- a/Mage.Sets/src/mage/cards/g/Grindstone.java +++ b/Mage.Sets/src/mage/cards/g/Grindstone.java @@ -1,22 +1,25 @@ - package mage.cards.g; -import java.util.UUID; +import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * * @author LevelX2 */ public final class Grindstone extends CardImpl { @@ -25,11 +28,10 @@ public final class Grindstone extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); // {3}, {T}: Target player puts the top two cards of their library into their graveyard. If both cards share a color, repeat this process. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GrindstoneEffect(), new ManaCostsImpl("{3}")); + Ability ability = new SimpleActivatedAbility(new GrindstoneEffect(), new ManaCostsImpl("{3}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetPlayer()); this.addAbility(ability); - } public Grindstone(final Grindstone card) { @@ -44,12 +46,12 @@ public final class Grindstone extends CardImpl { class GrindstoneEffect extends OneShotEffect { - public GrindstoneEffect() { + GrindstoneEffect() { super(Outcome.Benefit); - this.staticText = "Target player puts the top two cards of their library into their graveyard. If both cards share a color, repeat this process"; + this.staticText = "target player mills two cards. If two cards that share a color were milled this way, repeat this process."; } - public GrindstoneEffect(final GrindstoneEffect effect) { + private GrindstoneEffect(final GrindstoneEffect effect) { super(effect); } @@ -77,18 +79,33 @@ class GrindstoneEffect extends OneShotEffect { return true; } colorShared = false; - Card card1 = null; - Cards toGraveyard = new CardsImpl(); - for (Card card : targetPlayer.getLibrary().getCards(game)) { - toGraveyard.add(card); - if (card1 == null) { - card1 = card; - } else { - colorShared = card1.getColor(game).shares(card.getColor(game)); + List cards = targetPlayer + .millCards(2, source, game) + .getCards(game) + .stream() + .collect(Collectors.toList()); + if (cards.size() < 2) { + break; + } + for (int i = 0; i < cards.size(); i++) { + if (colorShared) { break; } + ObjectColor color1 = cards.get(i).getColor(game); + if (color1.isColorless()) { + continue; + } + for (int j = 0; j < cards.size(); j++) { + if (i >= j) { + continue; + } + ObjectColor color2 = cards.get(j).getColor(game); + if (color1.shares(color2)) { + colorShared = true; + break; + } + } } - targetPlayer.moveCards(toGraveyard, Zone.GRAVEYARD, source, game); } while (colorShared); return true; } diff --git a/Mage.Sets/src/mage/cards/g/GripOfChaos.java b/Mage.Sets/src/mage/cards/g/GripOfChaos.java index 479a74548d..158782a7f8 100644 --- a/Mage.Sets/src/mage/cards/g/GripOfChaos.java +++ b/Mage.Sets/src/mage/cards/g/GripOfChaos.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.Iterator; @@ -126,7 +125,7 @@ class GripOfChaosEffect extends OneShotEffect { Mode mode = stackObject.getStackAbility().getModes().get(modeId); for (Target target : mode.getTargets()) { UUID oldTargetId = target.getFirstTarget(); - Set possibleTargets = target.possibleTargets(stackObject.getId(), stackObject.getControllerId(), game); + Set possibleTargets = target.possibleTargets(stackObject.getSourceId(), stackObject.getControllerId(), game); if (possibleTargets.contains(stackObject.getId())) { // The stackObject can't target itself possibleTargets.remove(stackObject.getId()); } diff --git a/Mage.Sets/src/mage/cards/g/GrislySpectacle.java b/Mage.Sets/src/mage/cards/g/GrislySpectacle.java index 074d37f0f3..13cd075001 100644 --- a/Mage.Sets/src/mage/cards/g/GrislySpectacle.java +++ b/Mage.Sets/src/mage/cards/g/GrislySpectacle.java @@ -54,7 +54,7 @@ class GrislySpectacleEffect extends OneShotEffect { public GrislySpectacleEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Its controller puts a number of cards equal to that creature's power from the top of their library into their graveyard"; + this.staticText = "Its controller mills cards equal to that creature's power"; } public GrislySpectacleEffect(final GrislySpectacleEffect effect) { @@ -68,7 +68,7 @@ class GrislySpectacleEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent creature = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (creature != null) { Player controller = game.getPlayer(creature.getControllerId()); if (controller != null) { diff --git a/Mage.Sets/src/mage/cards/g/GrixisSojourners.java b/Mage.Sets/src/mage/cards/g/GrixisSojourners.java index 9c8af92701..1f15137feb 100644 --- a/Mage.Sets/src/mage/cards/g/GrixisSojourners.java +++ b/Mage.Sets/src/mage/cards/g/GrixisSojourners.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CycleTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.keyword.CyclingAbility; @@ -35,7 +35,7 @@ public final class GrixisSojourners extends CardImpl { // When you cycle Grixis Sojourners or it dies, you may exile target card from a graveyard. Ability ability1 = new CycleTriggeredAbility(new ExileTargetEffect(), true); - Ability ability2 = new DiesTriggeredAbility(new ExileTargetEffect(), true); + Ability ability2 = new DiesSourceTriggeredAbility(new ExileTargetEffect(), true); ability1.addTarget(new TargetCardInASingleGraveyard(1, 1, new FilterCard())); ability2.addTarget(new TargetCardInASingleGraveyard(1, 1, new FilterCard())); this.addAbility(ability1); diff --git a/Mage.Sets/src/mage/cards/g/GrizzledAngler.java b/Mage.Sets/src/mage/cards/g/GrizzledAngler.java index b1715bac1f..4726349725 100644 --- a/Mage.Sets/src/mage/cards/g/GrizzledAngler.java +++ b/Mage.Sets/src/mage/cards/g/GrizzledAngler.java @@ -1,7 +1,6 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,22 +11,23 @@ import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ColorlessPredicate; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author fireshoes */ public final class GrizzledAngler extends CardImpl { public GrizzledAngler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.HUMAN); this.power = new MageInt(2); this.toughness = new MageInt(3); @@ -60,7 +60,7 @@ class GrizzledAnglerEffect extends OneShotEffect { public GrizzledAnglerEffect() { super(Outcome.Benefit); - staticText = "Put the top two cards of your library into your graveyard. Then if there is a colorless creature card in your graveyard, transform {this}"; + staticText = "Mill two cards. Then if there is a colorless creature card in your graveyard, transform {this}"; } public GrizzledAnglerEffect(final GrizzledAnglerEffect effect) { @@ -76,7 +76,7 @@ class GrizzledAnglerEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.moveCards(controller.getLibrary().getTopCards(game, 2), Zone.GRAVEYARD, source, game); + controller.millCards(2, source, game); if (controller.getGraveyard().count(filter, source.getSourceId(), source.getControllerId(), game) >= 1) { return new TransformSourceEffect(true).apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/g/Groffskithur.java b/Mage.Sets/src/mage/cards/g/Groffskithur.java index 3a5bd1e998..e11f64f130 100644 --- a/Mage.Sets/src/mage/cards/g/Groffskithur.java +++ b/Mage.Sets/src/mage/cards/g/Groffskithur.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -33,7 +33,7 @@ public final class Groffskithur extends CardImpl { this.toughness = new MageInt(3); // Whenever Groffskithur becomes blocked, you may return target card named Groffskithur from your graveyard to your hand. - Ability ability = new BecomesBlockedTriggeredAbility(new ReturnToHandTargetEffect(), true); + Ability ability = new BecomesBlockedSourceTriggeredAbility(new ReturnToHandTargetEffect(), true); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/Grollub.java b/Mage.Sets/src/mage/cards/g/Grollub.java index 2bab76463a..d5414ff62e 100644 --- a/Mage.Sets/src/mage/cards/g/Grollub.java +++ b/Mage.Sets/src/mage/cards/g/Grollub.java @@ -1,70 +1,70 @@ -package mage.cards.g; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.DealtDamageToSourceTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.game.Game; - -/** - * - * @author jeffwadsworth - */ -public final class Grollub extends CardImpl { - - public Grollub(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); - - this.subtype.add(SubType.BEAST); - this.power = new MageInt(3); - this.toughness = new MageInt(3); - - // Whenever Grollub is dealt damage, each opponent gains that much life. - this.addAbility(new DealtDamageToSourceTriggeredAbility( - new EachOpponentGainsLifeEffect(), false, false, true)); - - } - - public Grollub(final Grollub card) { - super(card); - } - - @Override - public Grollub copy() { - return new Grollub(this); - } -} - -class EachOpponentGainsLifeEffect extends OneShotEffect { - - public EachOpponentGainsLifeEffect() { - super(Outcome.Neutral); - this.staticText = "each opponent gains that much life"; - } - - public EachOpponentGainsLifeEffect(final EachOpponentGainsLifeEffect effect) { - super(effect); - } - - @Override - public EachOpponentGainsLifeEffect copy() { - return new EachOpponentGainsLifeEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.getOpponents(source.getControllerId()).stream().map((opponentId) -> game.getPlayer(opponentId)).filter((opponent) -> (opponent != null)).forEachOrdered((opponent) -> { - int amount = (Integer) getValue("damage"); - if (amount > 0) { - opponent.gainLife(amount, game, source); - } - }); - return true; - } -} +package mage.cards.g; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealtDamageToSourceTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; + +/** + * + * @author jeffwadsworth + */ +public final class Grollub extends CardImpl { + + public Grollub(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever Grollub is dealt damage, each opponent gains that much life. + this.addAbility(new DealtDamageToSourceTriggeredAbility( + new EachOpponentGainsLifeEffect(), false, false, true)); + + } + + public Grollub(final Grollub card) { + super(card); + } + + @Override + public Grollub copy() { + return new Grollub(this); + } +} + +class EachOpponentGainsLifeEffect extends OneShotEffect { + + public EachOpponentGainsLifeEffect() { + super(Outcome.Neutral); + this.staticText = "each opponent gains that much life"; + } + + public EachOpponentGainsLifeEffect(final EachOpponentGainsLifeEffect effect) { + super(effect); + } + + @Override + public EachOpponentGainsLifeEffect copy() { + return new EachOpponentGainsLifeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.getOpponents(source.getControllerId()).stream().map((opponentId) -> game.getPlayer(opponentId)).filter((opponent) -> (opponent != null)).forEachOrdered((opponent) -> { + int amount = (Integer) getValue("damage"); + if (amount > 0) { + opponent.gainLife(amount, game, source); + } + }); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java b/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java index c576139f72..0d56901a0a 100644 --- a/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java +++ b/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java @@ -158,7 +158,7 @@ class GrothamaAllDevouringDrawCardsEffect extends OneShotEffect { if (player != null) { int toDraw = damageMap.getOrDefault(player.getId(), 0); if (toDraw > 0) { - player.drawCards(toDraw, game); + player.drawCards(toDraw, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/g/GroveOfTheGuardian.java b/Mage.Sets/src/mage/cards/g/GroveOfTheGuardian.java index d7c445de70..2237f3b4c8 100644 --- a/Mage.Sets/src/mage/cards/g/GroveOfTheGuardian.java +++ b/Mage.Sets/src/mage/cards/g/GroveOfTheGuardian.java @@ -1,8 +1,5 @@ - package mage.cards.g; -import java.util.UUID; -import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -11,20 +8,19 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.keyword.VigilanceAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; -import mage.game.permanent.token.TokenImpl; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.GreenAndWhiteElementalToken; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** * @author LevelX2 */ @@ -43,7 +39,7 @@ public final class GroveOfTheGuardian extends CardImpl { this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1), new TapSourceCost())); // {3}{G}{W}, {T}, Tap two untapped creatures you control, Sacrifice Grove of the Guardian: Create an 8/8 green and white Elemental creature token with vigilance. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new ElementalToken(), 1), new ManaCostsImpl("{3}{G}{W}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GreenAndWhiteElementalToken(), 1), new ManaCostsImpl("{3}{G}{W}")); ability.addCost(new TapSourceCost()); ability.addCost(new TapTargetCost(new TargetControlledCreaturePermanent(2, 2, filter, false))); ability.addCost(new SacrificeSourceCost()); @@ -58,26 +54,4 @@ public final class GroveOfTheGuardian extends CardImpl { public GroveOfTheGuardian copy() { return new GroveOfTheGuardian(this); } - - private static class ElementalToken extends TokenImpl { - - ElementalToken() { - super("Elemental", "8/8 green and white Elemental creature token with vigilance"); - - cardType.add(CardType.CREATURE); - color.setGreen(true); - color.setWhite(true); - this.subtype.add(SubType.ELEMENTAL); - power = new MageInt(8); - toughness = new MageInt(8); - this.addAbility(VigilanceAbility.getInstance()); - } - public ElementalToken(final ElementalToken token) { - super(token); - } - - public ElementalToken copy() { - return new ElementalToken(this); - } - } } diff --git a/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java b/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java index 7e996c5a25..9d33d69e3c 100644 --- a/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java +++ b/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java @@ -1,4 +1,3 @@ - package mage.cards.g; import mage.abilities.Ability; @@ -6,19 +5,18 @@ import mage.abilities.condition.common.MorbidCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPlayer; +import mage.target.common.TargetCardInHand; -import java.util.List; import java.util.UUID; /** @@ -29,18 +27,18 @@ public final class GruesomeDiscovery extends CardImpl { public GruesomeDiscovery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); - // Target player discards two cards. // Morbid — If a creature died this turn, instead that player reveals their hand, you choose two cards from it, then that player discards those cards. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new GruesomeDiscoveryEffect(), - new DiscardTargetEffect(2), - MorbidCondition.instance, - "Target player discards two cards. Morbid — If a creature died this turn, instead that player reveals their hand, you choose two cards from it, then that player discards those cards")); + new GruesomeDiscoveryEffect(), new DiscardTargetEffect(2), + MorbidCondition.instance, "Target player discards two cards. " + + "
Morbid — If a creature died this turn, instead that player reveals their hand, " + + "you choose two cards from it, then that player discards those cards" + )); this.getSpellAbility().addTarget(new TargetPlayer()); } - public GruesomeDiscovery(final GruesomeDiscovery card) { + private GruesomeDiscovery(final GruesomeDiscovery card) { super(card); } @@ -52,12 +50,11 @@ public final class GruesomeDiscovery extends CardImpl { class GruesomeDiscoveryEffect extends OneShotEffect { - public GruesomeDiscoveryEffect() { + GruesomeDiscoveryEffect() { super(Outcome.Discard); - this.staticText = "target player reveals their hand, you choose two cards from it, then that player discards those cards"; } - public GruesomeDiscoveryEffect(final GruesomeDiscoveryEffect effect) { + private GruesomeDiscoveryEffect(final GruesomeDiscoveryEffect effect) { super(effect); } @@ -70,25 +67,16 @@ class GruesomeDiscoveryEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(source.getFirstTarget()); - - if (player != null && targetPlayer != null) { - targetPlayer.revealCards("Gruesome Discovery", targetPlayer.getHand(), game); - - if (targetPlayer.getHand().size() <= 2) { - targetPlayer.discard(2, false, source, game); - } - - TargetCard target = new TargetCard(2, Zone.HAND, new FilterCard()); - if (player.choose(Outcome.Benefit, targetPlayer.getHand(), target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = targetPlayer.getHand().get(targetId, game); - targetPlayer.discard(card, source, game); - - } - } - return true; + if (player == null || targetPlayer == null) { + return false; } - return false; + targetPlayer.revealCards(source, targetPlayer.getHand(), game); + if (targetPlayer.getHand().size() <= 2) { + targetPlayer.discard(2, false, source, game); + } + TargetCard target = new TargetCardInHand(2, StaticFilters.FILTER_CARD_CARDS); + player.choose(Outcome.Discard, targetPlayer.getHand(), target, game); + targetPlayer.discard(new CardsImpl(target.getTargets()), source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/g/GruulRagebeast.java b/Mage.Sets/src/mage/cards/g/GruulRagebeast.java index 7a7ec1517a..2ac9f509ae 100644 --- a/Mage.Sets/src/mage/cards/g/GruulRagebeast.java +++ b/Mage.Sets/src/mage/cards/g/GruulRagebeast.java @@ -20,6 +20,7 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; import mage.target.targetpointer.FixedTarget; /** @@ -28,12 +29,6 @@ import mage.target.targetpointer.FixedTarget; */ public final class GruulRagebeast extends CardImpl { - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter2.add(TargetController.OPPONENT.getControllerPredicate()); - } - public GruulRagebeast(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{G}"); this.subtype.add(SubType.BEAST); @@ -42,10 +37,7 @@ public final class GruulRagebeast extends CardImpl { this.toughness = new MageInt(6); // Whenever Gruul Ragebeast or another creature enters the battlefield under your control, that creature fights target creature an opponent controls. - Ability ability = new GruulRagebeastTriggeredAbility(); - - ability.addTarget(new TargetCreaturePermanent(filter2)); - this.addAbility(ability); + this.addAbility(new GruulRagebeastTriggeredAbility()); } public GruulRagebeast(final GruulRagebeast card) { @@ -61,10 +53,10 @@ public final class GruulRagebeast extends CardImpl { class GruulRagebeastTriggeredAbility extends TriggeredAbilityImpl { GruulRagebeastTriggeredAbility() { - super(Zone.BATTLEFIELD, new GruulRagebeastEffect(), false); + super(Zone.BATTLEFIELD, new GruulRagebeastEffect(), false);this.addTarget(new TargetOpponentsCreaturePermanent()); } - GruulRagebeastTriggeredAbility(final GruulRagebeastTriggeredAbility ability) { + private GruulRagebeastTriggeredAbility(final GruulRagebeastTriggeredAbility ability) { super(ability); } @@ -106,7 +98,7 @@ class GruulRagebeastEffect extends OneShotEffect { super(Outcome.Damage); } - GruulRagebeastEffect(final GruulRagebeastEffect effect) { + private GruulRagebeastEffect(final GruulRagebeastEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/g/GuanYuSaintedWarrior.java b/Mage.Sets/src/mage/cards/g/GuanYuSaintedWarrior.java index e47a6ea0b8..76b4dd93d7 100644 --- a/Mage.Sets/src/mage/cards/g/GuanYuSaintedWarrior.java +++ b/Mage.Sets/src/mage/cards/g/GuanYuSaintedWarrior.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; import mage.abilities.keyword.HorsemanshipAbility; import mage.cards.CardImpl; @@ -30,7 +30,7 @@ public final class GuanYuSaintedWarrior extends CardImpl { // Horsemanship this.addAbility(HorsemanshipAbility.getInstance()); // When Guan Yu, Sainted Warrior is put into your graveyard from the battlefield, you may shuffle Guan Yu into your library. - this.addAbility(new DiesTriggeredAbility(new ShuffleIntoLibrarySourceEffect(), true)); + this.addAbility(new DiesSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect(), true)); } public GuanYuSaintedWarrior(final GuanYuSaintedWarrior card) { diff --git a/Mage.Sets/src/mage/cards/g/GuardDogs.java b/Mage.Sets/src/mage/cards/g/GuardDogs.java index 6101684b23..f642ea67d7 100644 --- a/Mage.Sets/src/mage/cards/g/GuardDogs.java +++ b/Mage.Sets/src/mage/cards/g/GuardDogs.java @@ -30,7 +30,7 @@ public final class GuardDogs extends CardImpl { public GuardDogs(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -86,7 +86,7 @@ class GuardDogsEffect extends PreventionEffectImpl { if (mageObject != null && controlledTarget.getFirstTarget() != null) { Permanent permanent = game.getPermanentOrLKIBattlefield(controlledTarget.getFirstTarget()); - Permanent targetPermanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent targetPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null && targetPermanent != null && this.getTargetPointer().getTargets(game, source).contains(event.getSourceId()) diff --git a/Mage.Sets/src/mage/cards/g/GuardianAutomaton.java b/Mage.Sets/src/mage/cards/g/GuardianAutomaton.java index 29da03f155..62bb99b5ba 100644 --- a/Mage.Sets/src/mage/cards/g/GuardianAutomaton.java +++ b/Mage.Sets/src/mage/cards/g/GuardianAutomaton.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class GuardianAutomaton extends CardImpl { this.toughness = new MageInt(3); // When Guardian Automaton dies, you gain 3 life. - this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(3))); + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(3))); } public GuardianAutomaton(final GuardianAutomaton card) { diff --git a/Mage.Sets/src/mage/cards/g/GuardianOfTazeem.java b/Mage.Sets/src/mage/cards/g/GuardianOfTazeem.java index d020498187..b93d560868 100644 --- a/Mage.Sets/src/mage/cards/g/GuardianOfTazeem.java +++ b/Mage.Sets/src/mage/cards/g/GuardianOfTazeem.java @@ -119,7 +119,7 @@ class GuardianOfTazeemEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent land = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent land = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Permanent targetCreature = game.getPermanent(source.getFirstTarget()); if (land != null && targetCreature != null && land.hasSubtype(SubType.ISLAND, game)) { ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("that creature"); diff --git a/Mage.Sets/src/mage/cards/g/GuardianProject.java b/Mage.Sets/src/mage/cards/g/GuardianProject.java index dd4dcd85a1..d4b791f4fd 100644 --- a/Mage.Sets/src/mage/cards/g/GuardianProject.java +++ b/Mage.Sets/src/mage/cards/g/GuardianProject.java @@ -21,7 +21,6 @@ import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; @@ -145,7 +144,7 @@ class GuardianProjectEffect extends OneShotEffect { if (GuardianProjectTriggeredAbility.checkCondition( mor.getPermanentOrLKIBattlefield(game), source.getControllerId(), game) ) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/g/GuildSummit.java b/Mage.Sets/src/mage/cards/g/GuildSummit.java index 748fa134a1..e6ef9058de 100644 --- a/Mage.Sets/src/mage/cards/g/GuildSummit.java +++ b/Mage.Sets/src/mage/cards/g/GuildSummit.java @@ -1,6 +1,5 @@ package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -16,11 +15,13 @@ import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class GuildSummit extends CardImpl { @@ -82,15 +83,16 @@ class GuildSummitEffect extends OneShotEffect { Player you = game.getPlayer(source.getControllerId()); TargetPermanent target = new TargetPermanent(0, Integer.MAX_VALUE, filter, true); if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.Tap, source.getControllerId(), source.getSourceId(), game)) { - for (UUID creature : target.getTargets()) { + for (UUID creatureId : target.getTargets()) { + Permanent creature = game.getPermanent(creatureId); if (creature != null) { - game.getPermanent(creature).tap(game); + creature.tap(game); tappedAmount++; } } } if (tappedAmount > 0) { - you.drawCards(tappedAmount, game); + you.drawCards(tappedAmount, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/g/GustOfWind.java b/Mage.Sets/src/mage/cards/g/GustOfWind.java new file mode 100644 index 0000000000..19c5a506f2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GustOfWind.java @@ -0,0 +1,65 @@ +package mage.cards.g; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GustOfWind extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent("you control a creature with flying"); + private static final FilterPermanent filter2 + = new FilterNonlandPermanent("nonland permanent you don't control"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + filter2.add(TargetController.OPPONENT.getControllerPredicate()); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + + public GustOfWind(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); + + // This spell costs {2} less to cast if you control a creature with flying. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionSourceEffect(2, condition) + ).setRuleAtTheTop(true).addHint(new ConditionHint(condition, "You control a creature with flying"))); + + // Return target nonland permanent you don't control to its owner's hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter2)); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + } + + private GustOfWind(final GustOfWind card) { + super(card); + } + + @Override + public GustOfWind copy() { + return new GustOfWind(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GustcloakCavalier.java b/Mage.Sets/src/mage/cards/g/GustcloakCavalier.java index b8049a75f3..335994260c 100644 --- a/Mage.Sets/src/mage/cards/g/GustcloakCavalier.java +++ b/Mage.Sets/src/mage/cards/g/GustcloakCavalier.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RemoveFromCombatSourceEffect; import mage.abilities.effects.common.TapTargetEffect; @@ -39,7 +39,7 @@ public final class GustcloakCavalier extends CardImpl { this.addAbility(ability); // Whenever Gustcloak Cavalier becomes blocked, you may untap Gustcloak Cavalier and remove it from combat. - Ability ability2 = new BecomesBlockedTriggeredAbility(new UntapSourceEffect(), true); + Ability ability2 = new BecomesBlockedSourceTriggeredAbility(new UntapSourceEffect(), true); Effect effect = new RemoveFromCombatSourceEffect(); effect.setText("and remove it from combat"); ability2.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/g/GustcloakHarrier.java b/Mage.Sets/src/mage/cards/g/GustcloakHarrier.java index fbc6a66b81..51c59a0704 100644 --- a/Mage.Sets/src/mage/cards/g/GustcloakHarrier.java +++ b/Mage.Sets/src/mage/cards/g/GustcloakHarrier.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RemoveFromCombatSourceEffect; import mage.abilities.effects.common.UntapSourceEffect; @@ -31,7 +31,7 @@ public final class GustcloakHarrier extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Gustcloak Harrier becomes blocked, you may untap it and remove it from combat. - Ability ability = new BecomesBlockedTriggeredAbility(new UntapSourceEffect(), true); + Ability ability = new BecomesBlockedSourceTriggeredAbility(new UntapSourceEffect(), true); Effect effect = new RemoveFromCombatSourceEffect(); effect.setText("and remove it from combat"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/g/GustcloakRunner.java b/Mage.Sets/src/mage/cards/g/GustcloakRunner.java index d7364a2336..924a806fbf 100644 --- a/Mage.Sets/src/mage/cards/g/GustcloakRunner.java +++ b/Mage.Sets/src/mage/cards/g/GustcloakRunner.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RemoveFromCombatSourceEffect; import mage.abilities.effects.common.UntapSourceEffect; @@ -27,7 +27,7 @@ public final class GustcloakRunner extends CardImpl { this.toughness = new MageInt(1); // Whenever Gustcloak Runner becomes blocked, you may untap it and remove it from combat. - Ability ability = new BecomesBlockedTriggeredAbility(new UntapSourceEffect(), true); + Ability ability = new BecomesBlockedSourceTriggeredAbility(new UntapSourceEffect(), true); Effect effect = new RemoveFromCombatSourceEffect(); effect.setText("and remove it from combat"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/g/GustcloakSentinel.java b/Mage.Sets/src/mage/cards/g/GustcloakSentinel.java index 1b7a699fac..276ce6da8c 100644 --- a/Mage.Sets/src/mage/cards/g/GustcloakSentinel.java +++ b/Mage.Sets/src/mage/cards/g/GustcloakSentinel.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RemoveFromCombatSourceEffect; import mage.abilities.effects.common.UntapSourceEffect; @@ -28,7 +28,7 @@ public final class GustcloakSentinel extends CardImpl { this.toughness = new MageInt(3); // Whenever Gustcloak Sentinel becomes blocked, you may untap it and remove it from combat. - Ability ability = new BecomesBlockedTriggeredAbility(new UntapSourceEffect(), true); + Ability ability = new BecomesBlockedSourceTriggeredAbility(new UntapSourceEffect(), true); Effect effect = new RemoveFromCombatSourceEffect(); effect.setText("and remove it from combat"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/g/GustcloakSkirmisher.java b/Mage.Sets/src/mage/cards/g/GustcloakSkirmisher.java index 5520f5979a..732e2cd3e0 100644 --- a/Mage.Sets/src/mage/cards/g/GustcloakSkirmisher.java +++ b/Mage.Sets/src/mage/cards/g/GustcloakSkirmisher.java @@ -4,7 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RemoveFromCombatSourceEffect; import mage.abilities.effects.common.UntapSourceEffect; @@ -31,7 +31,7 @@ public final class GustcloakSkirmisher extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Gustcloak Skirmisher becomes blocked, you may untap it and remove it from combat. - Ability ability = new BecomesBlockedTriggeredAbility(new UntapSourceEffect(), true); + Ability ability = new BecomesBlockedSourceTriggeredAbility(new UntapSourceEffect(), true); Effect effect = new RemoveFromCombatSourceEffect(); effect.setText("and remove it from combat"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/g/GuulDrazOverseer.java b/Mage.Sets/src/mage/cards/g/GuulDrazOverseer.java index fafd74cf71..3c1af084cf 100644 --- a/Mage.Sets/src/mage/cards/g/GuulDrazOverseer.java +++ b/Mage.Sets/src/mage/cards/g/GuulDrazOverseer.java @@ -63,7 +63,7 @@ class GuulDrazOverseerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent land = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent land = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && land != null) { int boost = 1; if (land.hasSubtype(SubType.SWAMP, game)) { diff --git a/Mage.Sets/src/mage/cards/g/GwafaHazidProfiteer.java b/Mage.Sets/src/mage/cards/g/GwafaHazidProfiteer.java index 65c3712376..cf5bce56f8 100644 --- a/Mage.Sets/src/mage/cards/g/GwafaHazidProfiteer.java +++ b/Mage.Sets/src/mage/cards/g/GwafaHazidProfiteer.java @@ -12,7 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -25,12 +25,6 @@ import java.util.UUID; */ public final class GwafaHazidProfiteer extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public GwafaHazidProfiteer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); addSuperType(SuperType.LEGENDARY); @@ -41,17 +35,16 @@ public final class GwafaHazidProfiteer extends CardImpl { this.toughness = new MageInt(2); // {W}{U}, {tap}: Put a bribery counter on target creature you don't control. Its controller draws a card. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GwafaHazidProfiteerEffect1(), new ManaCostsImpl("{W}{U}")); + Ability ability = new SimpleActivatedAbility(new GwafaHazidProfiteerEffect1(), new ManaCostsImpl("{W}{U}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); // Creatures with bribery counters on them can't attack or block. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GwafaHazidProfiteerEffect2())); - + this.addAbility(new SimpleStaticAbility(new GwafaHazidProfiteerEffect2())); } - public GwafaHazidProfiteer(final GwafaHazidProfiteer card) { + private GwafaHazidProfiteer(final GwafaHazidProfiteer card) { super(card); } @@ -68,7 +61,7 @@ class GwafaHazidProfiteerEffect1 extends OneShotEffect { staticText = "Put a bribery counter on target creature you don't control. Its controller draws a card"; } - public GwafaHazidProfiteerEffect1(final GwafaHazidProfiteerEffect1 effect) { + private GwafaHazidProfiteerEffect1(final GwafaHazidProfiteerEffect1 effect) { super(effect); } @@ -79,7 +72,7 @@ class GwafaHazidProfiteerEffect1 extends OneShotEffect { Player controller = game.getPlayer(targetCreature.getControllerId()); targetCreature.addCounters(CounterType.BRIBERY.createInstance(), source, game); if (controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } @@ -96,12 +89,12 @@ class GwafaHazidProfiteerEffect1 extends OneShotEffect { class GwafaHazidProfiteerEffect2 extends RestrictionEffect { - public GwafaHazidProfiteerEffect2() { + GwafaHazidProfiteerEffect2() { super(Duration.WhileOnBattlefield); staticText = "Creatures with bribery counters on them can't attack or block"; } - public GwafaHazidProfiteerEffect2(final GwafaHazidProfiteerEffect2 effect) { + private GwafaHazidProfiteerEffect2(final GwafaHazidProfiteerEffect2 effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java b/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java new file mode 100644 index 0000000000..effc47bf9a --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java @@ -0,0 +1,121 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CompanionAbility; +import mage.abilities.keyword.CompanionCondition; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ConvertedManaCostParityPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInGraveyard; + +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GyrudaDoomOfDepths extends CardImpl { + + public GyrudaDoomOfDepths(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U/B}{U/B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DEMON); + this.subtype.add(SubType.KRAKEN); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Companion — Your starting deck contains only cards with even converted mana costs. + this.addAbility(new CompanionAbility(GyrudaDoomOfDepthsCompanionCondition.instance)); + + // When Gyruda, Doom of Depths enters the battlefield, each player puts the top four cards of the library into their graveyard. Put a creature card with an even converted mana cost from among those cards onto the battlefield under your control. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GyrudaDoomOfDepthsEffect())); + } + + private GyrudaDoomOfDepths(final GyrudaDoomOfDepths card) { + super(card); + } + + @Override + public GyrudaDoomOfDepths copy() { + return new GyrudaDoomOfDepths(this); + } +} + +enum GyrudaDoomOfDepthsCompanionCondition implements CompanionCondition { + instance; + + @Override + public String getRule() { + return "Your starting deck contains only cards with even converted mana costs."; + } + + @Override + public boolean isLegal(Set deck, int startingSize) { + return deck + .stream() + .mapToInt(MageObject::getConvertedManaCost) + .map(i -> i % 2) + .allMatch(i -> i == 0); + } +} + +class GyrudaDoomOfDepthsEffect extends OneShotEffect { + + private static final FilterCard filter + = new FilterCreatureCard("creature card with an even converted mana cost"); + + static { + filter.add(ConvertedManaCostParityPredicate.EVEN); + } + + GyrudaDoomOfDepthsEffect() { + super(Outcome.Benefit); + staticText = "each player mills four cards. Put a creature card with an even converted mana cost " + + "from among the milled cards onto the battlefield under your control"; + } + + private GyrudaDoomOfDepthsEffect(final GyrudaDoomOfDepthsEffect effect) { + super(effect); + } + + @Override + public GyrudaDoomOfDepthsEffect copy() { + return new GyrudaDoomOfDepthsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + cards.addAll(player.millCards(4, source, game)); + } + cards.removeIf(cardId -> game.getState().getZone(cardId) != Zone.GRAVEYARD + && game.getState().getZone(cardId) != Zone.EXILED); + if (cards.isEmpty()) { + return true; + } + TargetCard targetCard = new TargetCardInGraveyard(0, 1, filter); + targetCard.setNotTarget(true); + controller.choose(outcome, cards, targetCard, game); + Card card = game.getCard(targetCard.getFirstTarget()); + return card != null && controller.moveCards(card, Zone.BATTLEFIELD, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java b/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java index 00f97af202..eb5cf51177 100644 --- a/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java +++ b/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java @@ -99,7 +99,7 @@ class GyrusWakerOfCorpsesEffect extends OneShotEffect { } controller.moveCards(card, Zone.EXILED, source, game); CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true, 1, true, true); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.apply(game, source); for (Permanent addedToken : effect.getAddedPermanent()) { Effect exileEffect = new ExileTargetEffect(); diff --git a/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java b/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java index edba324a79..d900893451 100644 --- a/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java +++ b/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java @@ -5,7 +5,7 @@ package mage.cards.h; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; @@ -41,7 +41,7 @@ public final class HaakonStromgaldScourge extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HaakonPlayKnightsFromGraveyardEffect())); // When Haakon dies, you lose 2 life. - this.addAbility(new DiesTriggeredAbility(new LoseLifeSourceControllerEffect(2))); + this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeSourceControllerEffect(2))); } diff --git a/Mage.Sets/src/mage/cards/h/HalcyonGlaze.java b/Mage.Sets/src/mage/cards/h/HalcyonGlaze.java index a833d9ac98..e42ab1ed77 100644 --- a/Mage.Sets/src/mage/cards/h/HalcyonGlaze.java +++ b/Mage.Sets/src/mage/cards/h/HalcyonGlaze.java @@ -27,7 +27,7 @@ public final class HalcyonGlaze extends CardImpl { // Whenever you cast a creature spell, Halcyon Glaze becomes a 4/4 Illusion creature with flying in addition to its other types until end of turn. Effect effect = new BecomesCreatureSourceEffect(new HalcyonGlazeToken(), "enchantment", Duration.EndOfTurn); - effect.setText("Whenever you cast a creature spell, {this} becomes a 4/4 Illusion creature with flying in addition to its other types until end of turn"); + effect.setText("{this} becomes a 4/4 Illusion creature with flying in addition to its other types until end of turn"); this.addAbility(new SpellCastControllerTriggeredAbility(effect, StaticFilters.FILTER_SPELL_A_CREATURE, false)); } diff --git a/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java b/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java new file mode 100644 index 0000000000..4131e5765b --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java @@ -0,0 +1,123 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.AsThoughManaEffect; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.p.PakoArcaneRetriever; +import mage.constants.*; +import mage.counters.CounterType; +import mage.game.Game; +import mage.players.ManaPoolItem; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HaldanAvidArcanist extends CardImpl { + + public HaldanAvidArcanist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Partner with Pako, Arcane Retriever + this.addAbility(new PartnerWithAbility("Pako, Arcane Retriever")); + + // You may play noncreature cards from exile with fetch counters on them if you + // exiled them, and you may spend mana as though it were mana of any color to cast those spells. + Ability ability = new SimpleStaticAbility(new HaldanAvidArcanistCastFromExileEffect()); + ability.addEffect(new HaldanAvidArcanistSpendAnyManaEffect()); + this.addAbility(ability); + } + + private HaldanAvidArcanist(final HaldanAvidArcanist card) { + super(card); + } + + @Override + public HaldanAvidArcanist copy() { + return new HaldanAvidArcanist(this); + } + + static boolean checkCard(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + if (!PakoArcaneRetriever.checkWatcher(affectedControllerId, game.getCard(sourceId), game) + || !source.isControlledBy(affectedControllerId) + || game.getState().getZone(sourceId) != Zone.EXILED) { + return false; + } + Card card = game.getCard(sourceId); + return card != null + && !card.isCreature() + && card.getCounters(game).containsKey(CounterType.FETCH); + } +} + +class HaldanAvidArcanistCastFromExileEffect extends AsThoughEffectImpl { + + HaldanAvidArcanistCastFromExileEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); + staticText = "You may play noncreature cards from exile with fetch counters on them if you exiled them"; + } + + private HaldanAvidArcanistCastFromExileEffect(final HaldanAvidArcanistCastFromExileEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public HaldanAvidArcanistCastFromExileEffect copy() { + return new HaldanAvidArcanistCastFromExileEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return HaldanAvidArcanist.checkCard(sourceId, source, affectedControllerId, game); + } +} + +class HaldanAvidArcanistSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect { + + HaldanAvidArcanistSpendAnyManaEffect() { + super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit); + staticText = ", and you may spend mana as though it were mana of any color to cast those spells"; + } + + private HaldanAvidArcanistSpendAnyManaEffect(final HaldanAvidArcanistSpendAnyManaEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public HaldanAvidArcanistSpendAnyManaEffect copy() { + return new HaldanAvidArcanistSpendAnyManaEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + return true; + } + + @Override + public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) { + return mana.getFirstAvailable(); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HallowedSpiritkeeper.java b/Mage.Sets/src/mage/cards/h/HallowedSpiritkeeper.java index 99be8546da..857fe640b9 100644 --- a/Mage.Sets/src/mage/cards/h/HallowedSpiritkeeper.java +++ b/Mage.Sets/src/mage/cards/h/HallowedSpiritkeeper.java @@ -3,7 +3,7 @@ package mage.cards.h; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; @@ -34,7 +34,7 @@ public final class HallowedSpiritkeeper extends CardImpl { // When Hallowed Spiritkeeper dies, create X 1/1 white Spirit creature tokens with flying, where X is the number of creature cards in your graveyard. Effect effect = new CreateTokenEffect(new SpiritWhiteToken(), new CardsInControllerGraveyardCount(new FilterCreatureCard("creature cards"))); effect.setText("create X 1/1 white Spirit creature tokens with flying, where X is the number of creature cards in your graveyard"); - this.addAbility(new DiesTriggeredAbility(effect, false)); + this.addAbility(new DiesSourceTriggeredAbility(effect, false)); } diff --git a/Mage.Sets/src/mage/cards/h/HamletbackGoliath.java b/Mage.Sets/src/mage/cards/h/HamletbackGoliath.java index 0c8df31678..4de43f8c52 100644 --- a/Mage.Sets/src/mage/cards/h/HamletbackGoliath.java +++ b/Mage.Sets/src/mage/cards/h/HamletbackGoliath.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -10,8 +9,8 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; @@ -27,7 +26,7 @@ import mage.target.targetpointer.FixedTarget; public final class HamletbackGoliath extends CardImpl { public HamletbackGoliath(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{6}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{R}"); this.subtype.add(SubType.GIANT); this.subtype.add(SubType.WARRIOR); @@ -72,10 +71,10 @@ class HamletbackGoliathTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { UUID targetId = event.getTargetId(); Permanent permanent = game.getPermanent(targetId); - if (permanent.isCreature() + if (permanent != null && permanent.isCreature() && !(targetId.equals(this.getSourceId()))) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); } return true; } @@ -100,11 +99,8 @@ class HamletbackGoliathEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent creature = game.getPermanent(targetPointer.getFirst(game, source)); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Permanent sourceObject = game.getPermanent(source.getSourceId()); - if (creature == null) { - creature = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); - } if (creature != null && sourceObject != null) { sourceObject.addCounters(CounterType.P1P1.createInstance(creature.getPower().getValue()), source, game); } diff --git a/Mage.Sets/src/mage/cards/h/HammerOfNazahn.java b/Mage.Sets/src/mage/cards/h/HammerOfNazahn.java index a973a49601..44e9f58776 100644 --- a/Mage.Sets/src/mage/cards/h/HammerOfNazahn.java +++ b/Mage.Sets/src/mage/cards/h/HammerOfNazahn.java @@ -1,9 +1,8 @@ package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.Effect; @@ -14,34 +13,29 @@ import mage.abilities.keyword.EquipAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SetTargetPointer; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.common.FilterEquipmentPermanent; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author Saga */ public final class HammerOfNazahn extends CardImpl { - - private static final FilterEquipmentPermanent filter = new FilterEquipmentPermanent("{this} or another Equipment"); public HammerOfNazahn(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.EQUIPMENT); - + // Whenever Hammer of Nazahn or another Equipment enters the battlefiend under your control, you may attach that Equipment to target creature you control. - Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new HammerOfNazahnEffect(), filter, true, SetTargetPointer.PERMANENT, ""); + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new HammerOfNazahnEffect(), StaticFilters.FILTER_PERMANENT_EQUIPMENT, true, SetTargetPointer.PERMANENT, true + ); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/h/HammerheadShark.java b/Mage.Sets/src/mage/cards/h/HammerheadShark.java index c415b95bdc..217c645ed8 100644 --- a/Mage.Sets/src/mage/cards/h/HammerheadShark.java +++ b/Mage.Sets/src/mage/cards/h/HammerheadShark.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.combat.CantAttackUnlessDefenderControllsPermanent; @@ -12,23 +10,25 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterLandPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class HammerheadShark extends CardImpl { public HammerheadShark(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); - this.subtype.add(SubType.FISH); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + this.subtype.add(SubType.SHARK); + this.power = new MageInt(2); this.toughness = new MageInt(3); // Hammerhead Shark can't attack unless defending player controls an Island. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackUnlessDefenderControllsPermanent(new FilterLandPermanent(SubType.ISLAND,"an Island")))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackUnlessDefenderControllsPermanent(new FilterLandPermanent(SubType.ISLAND, "an Island")))); } - public HammerheadShark(final HammerheadShark card) { + private HammerheadShark(final HammerheadShark card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/h/HamperingSnare.java b/Mage.Sets/src/mage/cards/h/HamperingSnare.java new file mode 100644 index 0000000000..2dc408cfad --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HamperingSnare.java @@ -0,0 +1,36 @@ +package mage.cards.h; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostOpponentsEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HamperingSnare extends CardImpl { + + public HamperingSnare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Creatures your opponents control get -2/-0 until end of turn. + this.getSpellAbility().addEffect(new BoostOpponentsEffect(-2, 0, Duration.EndOfTurn)); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private HamperingSnare(final HamperingSnare card) { + super(card); + } + + @Override + public HamperingSnare copy() { + return new HamperingSnare(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HangarbackWalker.java b/Mage.Sets/src/mage/cards/h/HangarbackWalker.java index 4570d90376..ce79708ffd 100644 --- a/Mage.Sets/src/mage/cards/h/HangarbackWalker.java +++ b/Mage.Sets/src/mage/cards/h/HangarbackWalker.java @@ -4,7 +4,7 @@ package mage.cards.h; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -37,7 +37,7 @@ public final class HangarbackWalker extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); // When Hangarback Walker dies, create a 1/1 colorless Thopter artifact creature token with flying for each +1/+1 counter on Hangarback Walker. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken(), new CountersSourceCount(CounterType.P1P1)), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken(), new CountersSourceCount(CounterType.P1P1)), false)); // {1}, {t}: Put a +1/+1 counter on Hangarback Walker. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(1)); diff --git a/Mage.Sets/src/mage/cards/h/HappilyEverAfter.java b/Mage.Sets/src/mage/cards/h/HappilyEverAfter.java index c1f1b476a1..d478d436bc 100644 --- a/Mage.Sets/src/mage/cards/h/HappilyEverAfter.java +++ b/Mage.Sets/src/mage/cards/h/HappilyEverAfter.java @@ -80,7 +80,7 @@ class HappilyEverAfterEffect extends OneShotEffect { .filter(Objects::nonNull) .forEachOrdered(player -> { player.gainLife(5, game, source); - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); }); return true; } diff --git a/Mage.Sets/src/mage/cards/h/HarborGuardian.java b/Mage.Sets/src/mage/cards/h/HarborGuardian.java index 9f6834dffc..6e0553058a 100644 --- a/Mage.Sets/src/mage/cards/h/HarborGuardian.java +++ b/Mage.Sets/src/mage/cards/h/HarborGuardian.java @@ -67,7 +67,7 @@ class HarborGuardianEffect extends OneShotEffect { Player defender = game.getPlayer(defenderId); if (defender != null) { if (defender.chooseUse(outcome, "Draw a card?", source, game)) { - defender.drawCards(1, game); + defender.drawCards(1, source.getSourceId(), game); } } return false; diff --git a/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java b/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java index 95dc56ddf9..5c33eb781a 100644 --- a/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java +++ b/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java @@ -17,6 +17,9 @@ import mage.players.Player; import mage.target.TargetCard; import java.util.*; +import mage.cards.Cards; +import mage.filter.common.FilterEnchantmentPermanent; +import mage.filter.predicate.other.OwnerIdPredicate; /** * @@ -27,7 +30,6 @@ public final class HarmonicConvergence extends CardImpl { public HarmonicConvergence(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}"); - // Put all enchantments on top of their owners' libraries. this.getSpellAbility().addEffect(new HarmonicConvergenceEffect()); } @@ -60,44 +62,18 @@ class HarmonicConvergenceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List enchantments = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, - source.getControllerId(), - source.getSourceId(), - game); - - Map> moveList = new HashMap<>(); - for (Permanent permanent : enchantments) { - List list = moveList.computeIfAbsent(permanent.getControllerId(), k -> new ArrayList<>()); - list.add(permanent); - } - - TargetCard target = new TargetCard(Zone.BATTLEFIELD, new FilterCard("card to put on top of your library")); - for (UUID playerId : moveList.keySet()) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); - List list = moveList.get(playerId); - if (player == null) { - continue; - } - - CardsImpl cards = new CardsImpl(); - for (Permanent permanent : list) { - cards.add(permanent); - } - while (player.canRespond() && cards.size() > 1) { - player.choose(Outcome.Neutral, cards, target, game); - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - cards.remove(permanent); - permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + if (player != null) { + FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent(); + filter.add(new OwnerIdPredicate(player.getId())); + Cards toLib = new CardsImpl(); + for(Permanent enchantment: game.getBattlefield().getActivePermanents(filter, playerId, source.getSourceId(), game)) { + toLib.add(enchantment); } - target.clearChosen(); - } - if (cards.size() == 1) { - Permanent permanent = game.getPermanent(cards.iterator().next()); - permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - } - + player.putCardsOnTopOfLibrary(toLib, game, source, true); + } + } return true; } } diff --git a/Mage.Sets/src/mage/cards/h/HarnessByForce.java b/Mage.Sets/src/mage/cards/h/HarnessByForce.java index b06c51f48d..4cc279d51e 100644 --- a/Mage.Sets/src/mage/cards/h/HarnessByForce.java +++ b/Mage.Sets/src/mage/cards/h/HarnessByForce.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.UntapTargetEffect; @@ -14,18 +12,19 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HarnessByForce extends CardImpl { public HarnessByForce(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{R}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}{R}"); // Strive - Harness by Force costs {2}{R} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{R}")); + // Gain control of any number of target creatures until end of turn. Untap those creatures. They gain haste until end of turn. Effect effect = new GainControlTargetEffect(Duration.EndOfTurn); effect.setText("Gain control of any number of target creatures until end of turn"); diff --git a/Mage.Sets/src/mage/cards/h/Harrow.java b/Mage.Sets/src/mage/cards/h/Harrow.java index c02b637224..27d536df96 100644 --- a/Mage.Sets/src/mage/cards/h/Harrow.java +++ b/Mage.Sets/src/mage/cards/h/Harrow.java @@ -27,7 +27,7 @@ public final class Harrow extends CardImpl { this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_LAND_SHORT_TEXT))); // Search your library for up to two basic land cards and put them onto the battlefield. Then shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND); + TargetCardInLibrary target = new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LANDS); this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect(target, false, Outcome.PutLandInPlay)); } diff --git a/Mage.Sets/src/mage/cards/h/HarvestHand.java b/Mage.Sets/src/mage/cards/h/HarvestHand.java index 7661170773..5a888543a5 100644 --- a/Mage.Sets/src/mage/cards/h/HarvestHand.java +++ b/Mage.Sets/src/mage/cards/h/HarvestHand.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.Card; @@ -34,7 +34,7 @@ public final class HarvestHand extends CardImpl { // When Harvest Hand dies, return it to the battlefield transformed under your control. this.addAbility(new TransformAbility()); - this.addAbility(new DiesTriggeredAbility(new HarvestHandReturnTransformedEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new HarvestHandReturnTransformedEffect())); } public HarvestHand(final HarvestHand card) { diff --git a/Mage.Sets/src/mage/cards/h/HarvestMage.java b/Mage.Sets/src/mage/cards/h/HarvestMage.java index 421737b6a1..c5adf94384 100644 --- a/Mage.Sets/src/mage/cards/h/HarvestMage.java +++ b/Mage.Sets/src/mage/cards/h/HarvestMage.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -84,9 +83,14 @@ class HarvestMageReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { ManaEvent manaEvent = (ManaEvent) event; Mana mana = manaEvent.getMana(); - new AddManaOfAnyColorEffect().apply(game,source); - mana.setToMana(new Mana(0,0,0,0,0,0,0,0)); - return true; + if (game != null && game.inCheckPlayableState()) { + mana.setToMana(new Mana(0, 0, 0, 0, 0, 0, 1, 0)); + return false; + } else { + new AddManaOfAnyColorEffect().apply(game, source); + mana.setToMana(new Mana(0, 0, 0, 0, 0, 0, 0, 0)); + return true; + } } @Override diff --git a/Mage.Sets/src/mage/cards/h/HateMirage.java b/Mage.Sets/src/mage/cards/h/HateMirage.java index 15ac2fb993..385805d46c 100644 --- a/Mage.Sets/src/mage/cards/h/HateMirage.java +++ b/Mage.Sets/src/mage/cards/h/HateMirage.java @@ -7,9 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.target.Target; import mage.target.TargetPermanent; @@ -23,18 +21,12 @@ import java.util.UUID; */ public final class HateMirage extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creatures you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public HateMirage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); // Choose up to two target creatures you don't control. For each of those creatures, create a token that's a copy of that creature. Those tokens gain haste. Exile them at the beginning of the next end step. this.getSpellAbility().addEffect(new HateMirageEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(0, 2, filter, false)); + this.getSpellAbility().addTarget(new TargetPermanent(0, 2, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); } private HateMirage(final HateMirage card) { @@ -81,4 +73,4 @@ class HateMirageEffect extends OneShotEffect { }); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/h/HauntedAngel.java b/Mage.Sets/src/mage/cards/h/HauntedAngel.java index 7bdf24ead2..ff9fe07823 100644 --- a/Mage.Sets/src/mage/cards/h/HauntedAngel.java +++ b/Mage.Sets/src/mage/cards/h/HauntedAngel.java @@ -4,7 +4,7 @@ package mage.cards.h; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -32,7 +32,7 @@ public final class HauntedAngel extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Haunted Angel dies, exile Haunted Angel and each other player creates a 3/3 black Angel creature token with flying. - Ability ability = new DiesTriggeredAbility(new ExileSourceEffect()); + Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect()); ability.addEffect(new HauntedAngelEffect()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HauntedCloak.java b/Mage.Sets/src/mage/cards/h/HauntedCloak.java index 1a6e8bc058..c618841903 100644 --- a/Mage.Sets/src/mage/cards/h/HauntedCloak.java +++ b/Mage.Sets/src/mage/cards/h/HauntedCloak.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -31,7 +30,7 @@ public final class HauntedCloak extends CardImpl { // Equipped creature has vigilance, trample, and haste. Effect effect = new GainAbilityAttachedEffect(VigilanceAbility.getInstance(), AttachmentType.EQUIPMENT); - effect.setText("Equipped creature has vigilance,"); + effect.setText("Equipped creature has vigilance"); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); effect = new GainAbilityAttachedEffect(TrampleAbility.getInstance(), AttachmentType.EQUIPMENT); effect.setText(", trample"); @@ -45,7 +44,7 @@ public final class HauntedCloak extends CardImpl { this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(1))); } - public HauntedCloak(final HauntedCloak card) { + private HauntedCloak(final HauntedCloak card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/h/HavocDemon.java b/Mage.Sets/src/mage/cards/h/HavocDemon.java index aa285492e9..2be010eca2 100644 --- a/Mage.Sets/src/mage/cards/h/HavocDemon.java +++ b/Mage.Sets/src/mage/cards/h/HavocDemon.java @@ -3,7 +3,7 @@ package mage.cards.h; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class HavocDemon extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Havoc Demon dies, all creatures get -5/-5 until end of turn. - this.addAbility(new DiesTriggeredAbility(new BoostAllEffect(-5, -5, Duration.EndOfTurn), false)); + this.addAbility(new DiesSourceTriggeredAbility(new BoostAllEffect(-5, -5, Duration.EndOfTurn), false)); } public HavocDemon(final HavocDemon card) { diff --git a/Mage.Sets/src/mage/cards/h/HavocJester.java b/Mage.Sets/src/mage/cards/h/HavocJester.java new file mode 100644 index 0000000000..ffa1ab353e --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HavocJester.java @@ -0,0 +1,73 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HavocJester extends CardImpl { + + public HavocJester(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.DEVIL); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Whenever you sacrifice a permanent, Havoc Jester deals 1 damage to any target. + this.addAbility(new HavocJesterTriggeredAbility()); + } + + private HavocJester(final HavocJester card) { + super(card); + } + + @Override + public HavocJester copy() { + return new HavocJester(this); + } +} + +class HavocJesterTriggeredAbility extends TriggeredAbilityImpl { + + HavocJesterTriggeredAbility() { + super(Zone.BATTLEFIELD, new DamageTargetEffect(1)); + this.addTarget(new TargetAnyTarget()); + } + + private HavocJesterTriggeredAbility(final HavocJesterTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SACRIFICED_PERMANENT; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals(getControllerId()); + } + + @Override + public HavocJesterTriggeredAbility copy() { + return new HavocJesterTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever you sacrifice a permanent, {this} deals 1 damage to any target."; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HealTheScars.java b/Mage.Sets/src/mage/cards/h/HealTheScars.java index 4c7a3423f0..6cb9b7a5d6 100644 --- a/Mage.Sets/src/mage/cards/h/HealTheScars.java +++ b/Mage.Sets/src/mage/cards/h/HealTheScars.java @@ -58,7 +58,7 @@ class HealTheScarsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java b/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java index ecf355dea2..371e4e83e3 100644 --- a/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java +++ b/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java @@ -1,32 +1,29 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.abilities.keyword.EmbalmAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class HeartPiercerManticore extends CardImpl { @@ -39,16 +36,13 @@ public final class HeartPiercerManticore extends CardImpl { this.toughness = new MageInt(3); // When Heart-Piercer Manticore enters the battlefield, you may sacrifice another creature. When you do, Heart-Piercer Manticore deals damage equal to that creature's power to any target. - this.addAbility(new EntersBattlefieldTriggeredAbility( - new HeartPiercerManticoreSacrificeEffect(), true - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new HeartPiercerManticoreSacrificeEffect(), true)); // Embalm {5}{R} this.addAbility(new EmbalmAbility(new ManaCostsImpl("{5}{R}"), this)); - } - public HeartPiercerManticore(final HeartPiercerManticore card) { + private HeartPiercerManticore(final HeartPiercerManticore card) { super(card); } @@ -60,13 +54,13 @@ public final class HeartPiercerManticore extends CardImpl { class HeartPiercerManticoreSacrificeEffect extends OneShotEffect { - public HeartPiercerManticoreSacrificeEffect() { + HeartPiercerManticoreSacrificeEffect() { super(Outcome.Damage); this.staticText = "sacrifice another creature. When you do, " + "{this} deals damage equal to that creature's power to any target"; } - public HeartPiercerManticoreSacrificeEffect(final HeartPiercerManticoreSacrificeEffect effect) { + private HeartPiercerManticoreSacrificeEffect(final HeartPiercerManticoreSacrificeEffect effect) { super(effect); } @@ -78,54 +72,29 @@ class HeartPiercerManticoreSacrificeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Target target = new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, true); - if (controller.choose(outcome, target, source.getSourceId(), game)) { - Permanent toSacrifice = game.getPermanent(target.getFirstTarget()); - if (toSacrifice != null) { - DelayedTriggeredAbility trigger = new HeartPiercerManticoreReflexiveTriggeredAbility(toSacrifice.getPower().getValue()); - if (toSacrifice.sacrifice(source.getSourceId(), game)) { - game.addDelayedTriggeredAbility(trigger, source); - return new SendOptionUsedEventEffect().apply(game, source); - } - } - } - return true; + if (controller == null) { + return false; } - return false; - } -} - -class HeartPiercerManticoreReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - public HeartPiercerManticoreReflexiveTriggeredAbility(int damage) { - super(new DamageTargetEffect(damage), Duration.OneUse, true); - this.addTarget(new TargetAnyTarget()); - } - - public HeartPiercerManticoreReflexiveTriggeredAbility(final HeartPiercerManticoreReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public HeartPiercerManticoreReflexiveTriggeredAbility copy() { - return new HeartPiercerManticoreReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you sacrifice a creature to {this}'s ability, " - + "{this} deals damage equal to that creature's power to any target"; + Target target = new TargetControlledCreaturePermanent( + 1, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, true + ); + if (!controller.choose(outcome, target, source.getSourceId(), game)) { + return false; + } + Permanent toSacrifice = game.getPermanent(target.getFirstTarget()); + if (toSacrifice == null) { + return false; + } + int power = toSacrifice.getPower().getValue(); + if (!toSacrifice.sacrifice(source.getSourceId(), game)) { + return false; + } + ReflexiveTriggeredAbility trigger = new ReflexiveTriggeredAbility( + new DamageTargetEffect(power), false, + "{this} deals damage equal to that creature's power to any target." + ); + trigger.addTarget(new TargetAnyTarget()); + game.fireReflexiveTriggeredAbility(trigger, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/h/HeartfireImmolator.java b/Mage.Sets/src/mage/cards/h/HeartfireImmolator.java new file mode 100644 index 0000000000..89ca31facb --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeartfireImmolator.java @@ -0,0 +1,55 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HeartfireImmolator extends CardImpl { + + private static final DynamicValue xValue = new SourcePermanentPowerCount(false); + + public HeartfireImmolator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Prowess + this.addAbility(new ProwessAbility()); + + // {R}, Sacrifice Heartfire Immolator: It deals damage equal to its power to target creature or planeswalker. + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(xValue, "it"), new ManaCostsImpl("{R}") + ); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetCreatureOrPlaneswalker()); + this.addAbility(ability); + } + + private HeartfireImmolator(final HeartfireImmolator card) { + super(card); + } + + @Override + public HeartfireImmolator copy() { + return new HeartfireImmolator(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeartlessAct.java b/Mage.Sets/src/mage/cards/h/HeartlessAct.java new file mode 100644 index 0000000000..3e6504137f --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeartlessAct.java @@ -0,0 +1,109 @@ +package mage.cards.h; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HeartlessAct extends CardImpl { + + private static final FilterCreaturePermanent filterWithoutCounters + = new FilterCreaturePermanent("creature with no counters on it"); + + static { + filterWithoutCounters.add(Predicates.not(CounterAnyPredicate.instance)); + } + + public HeartlessAct(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Choose one — + // • Destroy target creature with no counters on it. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filterWithoutCounters)); + + // • Remove up to three counters from target creature. + Mode mode = new Mode(new HeartlessActEffect()); + mode.addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + } + + private HeartlessAct(final HeartlessAct card) { + super(card); + } + + @Override + public HeartlessAct copy() { + return new HeartlessAct(this); + } +} + +class HeartlessActEffect extends OneShotEffect { + + HeartlessActEffect() { + super(Outcome.AIDontUseIt); + staticText = "Remove up to three counters from target creature"; + } + + private HeartlessActEffect(final HeartlessActEffect effect) { + super(effect); + } + + @Override + public HeartlessActEffect copy() { + return new HeartlessActEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + int toRemove = 3; + int removed = 0; + String[] counterNames = permanent.getCounters(game).keySet().toArray(new String[0]); + for (String counterName : counterNames) { + if (controller.chooseUse(Outcome.Neutral, "Do you want to remove " + counterName + " counters?", source, game)) { + if (permanent.getCounters(game).get(counterName).getCount() == 1 || (toRemove - removed == 1)) { + permanent.removeCounters(counterName, 1, game); + removed++; + } else { + int amount = controller.getAmount(1, Math.min(permanent.getCounters(game).get(counterName).getCount(), toRemove - removed), "How many?", game); + if (amount > 0) { + removed += amount; + permanent.removeCounters(counterName, amount, game); + } + } + } + if (removed >= toRemove) { + break; + } + } + game.addEffect(new BoostSourceEffect(removed, 0, Duration.EndOfTurn), source); + return true; + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/h/HeartlessPillage.java b/Mage.Sets/src/mage/cards/h/HeartlessPillage.java index 07cab44e50..a960175713 100644 --- a/Mage.Sets/src/mage/cards/h/HeartlessPillage.java +++ b/Mage.Sets/src/mage/cards/h/HeartlessPillage.java @@ -1,20 +1,21 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.game.permanent.token.TreasureToken; import mage.target.common.TargetOpponent; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HeartlessPillage extends CardImpl { @@ -32,6 +33,8 @@ public final class HeartlessPillage extends CardImpl { RaidCondition.instance, "

Raid — If you attacked with a creature this turn, create a colorless Treasure artifact token with \"{T}, Sacrifice this artifact: Add one mana of any color.\"")); this.getSpellAbility().addWatcher(new PlayerAttackedWatcher()); + this.getSpellAbility().setAbilityWord(AbilityWord.RAID); + this.getSpellAbility().addHint(RaidHint.instance); } public HeartlessPillage(final HeartlessPillage card) { diff --git a/Mage.Sets/src/mage/cards/h/Heartstone.java b/Mage.Sets/src/mage/cards/h/Heartstone.java index da3828a014..65c244fea5 100644 --- a/Mage.Sets/src/mage/cards/h/Heartstone.java +++ b/Mage.Sets/src/mage/cards/h/Heartstone.java @@ -1,36 +1,31 @@ - package mage.cards.h; -import java.util.UUID; -import mage.Mana; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; -import mage.constants.CardType; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author pcasaretto_at_gmail.com */ public final class Heartstone extends CardImpl { public Heartstone(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - // Activated abilities of creatures cost {1} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana. + // Activated abilities of creatures cost {1} less to activate. + // This effect can't reduce the amount of mana an ability + // costs to activate to less than one mana. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HeartstoneEffect())); } @@ -46,7 +41,8 @@ public final class Heartstone extends CardImpl { class HeartstoneEffect extends CostModificationEffectImpl { - private static final String effectText = "Activated abilities of creatures cost {1} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana."; + private static final String effectText = "Activated abilities of creatures cost " + + "{1} less to activate. This effect can't reduce the mana in that cost to less than one mana."; private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); public HeartstoneEffect() { @@ -62,10 +58,7 @@ class HeartstoneEffect extends CostModificationEffectImpl { public boolean apply(Game game, Ability source, Ability abilityToModify) { Player controller = game.getPlayer(abilityToModify.getControllerId()); if (controller != null) { - Mana mana = abilityToModify.getManaCostsToPay().getMana(); - if (mana.count() > 1 && mana.getGeneric() > 0) { - CardUtil.reduceCost(abilityToModify, 1); - } + CardUtil.reduceCost(abilityToModify, 1); return true; } return false; @@ -74,10 +67,12 @@ class HeartstoneEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify.getAbilityType() == AbilityType.ACTIVATED - || (abilityToModify.getAbilityType() == AbilityType.MANA && (abilityToModify instanceof ActivatedAbility))) { + || (abilityToModify.getAbilityType() == AbilityType.MANA + && (abilityToModify instanceof ActivatedAbility))) { // Activated abilities of creatures - Permanent permanent = game.getPermanent(abilityToModify.getSourceId()); - if (permanent != null && filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) { + Permanent permanent = game.getPermanentOrLKIBattlefield(abilityToModify.getSourceId()); + if (permanent != null + && filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) { return true; } } diff --git a/Mage.Sets/src/mage/cards/h/HeartwarmingRedemption.java b/Mage.Sets/src/mage/cards/h/HeartwarmingRedemption.java index 80dd636e95..11447206c6 100644 --- a/Mage.Sets/src/mage/cards/h/HeartwarmingRedemption.java +++ b/Mage.Sets/src/mage/cards/h/HeartwarmingRedemption.java @@ -57,7 +57,7 @@ class HeartwarmingRedemptionEffect extends OneShotEffect { return false; } int discarded = player.discard(player.getHand().size(), false, source, game).size(); - player.drawCards(discarded + 1, game); + player.drawCards(discarded + 1, source.getSourceId(), game); player.gainLife(player.getHand().size(), game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/h/HeartwoodStoryteller.java b/Mage.Sets/src/mage/cards/h/HeartwoodStoryteller.java index a4238dec7c..090d335f58 100644 --- a/Mage.Sets/src/mage/cards/h/HeartwoodStoryteller.java +++ b/Mage.Sets/src/mage/cards/h/HeartwoodStoryteller.java @@ -106,7 +106,7 @@ class HeartwoodStorytellerEffect extends OneShotEffect { Player player = game.getPlayer(playerId); if (player != null) { if (player.chooseUse(outcome, "Draw a card?", source, game)) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/h/HeatShimmer.java b/Mage.Sets/src/mage/cards/h/HeatShimmer.java index e0e8cb79ca..3c431549e1 100644 --- a/Mage.Sets/src/mage/cards/h/HeatShimmer.java +++ b/Mage.Sets/src/mage/cards/h/HeatShimmer.java @@ -61,7 +61,7 @@ class HeatShimmerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); diff --git a/Mage.Sets/src/mage/cards/h/HedronBlade.java b/Mage.Sets/src/mage/cards/h/HedronBlade.java index 463af05b0c..41df7e6add 100644 --- a/Mage.Sets/src/mage/cards/h/HedronBlade.java +++ b/Mage.Sets/src/mage/cards/h/HedronBlade.java @@ -100,6 +100,7 @@ class HedronBladeTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever equipped creature becomes blocked by one or more colorless creatures, " + super.getRule(); + return "Whenever equipped creature becomes blocked by one or more colorless creatures, " + + "" + "it gains deathtouch until end of turn."; } } diff --git a/Mage.Sets/src/mage/cards/h/HeedTheMists.java b/Mage.Sets/src/mage/cards/h/HeedTheMists.java index a2be2063be..bc92038248 100644 --- a/Mage.Sets/src/mage/cards/h/HeedTheMists.java +++ b/Mage.Sets/src/mage/cards/h/HeedTheMists.java @@ -1,21 +1,20 @@ package mage.cards.h; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HeedTheMists extends CardImpl { @@ -41,7 +40,7 @@ public final class HeedTheMists extends CardImpl { public HeedTheMistsEffect() { super(Outcome.DrawCard); - staticText = "Put the top card of your library into your graveyard, then draw cards equal to that card's converted mana cost"; + staticText = "Mill a card, then draw cards equal to that card's converted mana cost"; } public HeedTheMistsEffect(HeedTheMistsEffect effect) { @@ -52,15 +51,14 @@ public final class HeedTheMists extends CardImpl { public boolean apply(Game game, Ability source) { boolean result = false; Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - int cmc = card.getConvertedManaCost(); - controller.moveCards(card, Zone.GRAVEYARD, source, game); - controller.drawCards(cmc, game); - } - } - return result; + int totalCMC = controller + .millCards(1, source, game) + .getCards(game) + .stream() + .mapToInt(MageObject::getConvertedManaCost) + .sum(); + controller.millCards(totalCMC, source, game); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/h/HeightenedReflexes.java b/Mage.Sets/src/mage/cards/h/HeightenedReflexes.java new file mode 100644 index 0000000000..7088fe9295 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeightenedReflexes.java @@ -0,0 +1,36 @@ +package mage.cards.h; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HeightenedReflexes extends CardImpl { + + public HeightenedReflexes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); + + // Target creature gets +1/+0 until end of turn. Put a first strike counter on it. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0)); + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.FIRST_STRIKE.createInstance()) + .setText("Put a first strike counter on it")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private HeightenedReflexes(final HeightenedReflexes card) { + super(card); + } + + @Override + public HeightenedReflexes copy() { + return new HeightenedReflexes(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HelicaGlider.java b/Mage.Sets/src/mage/cards/h/HelicaGlider.java new file mode 100644 index 0000000000..5c85a86160 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HelicaGlider.java @@ -0,0 +1,41 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.common.counter.AddCounterChoiceSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HelicaGlider extends CardImpl { + + public HelicaGlider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.SQUIRREL); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Helica Glider enters the battlfield with your choice of a flying counter or a first strike counter on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCounterChoiceSourceEffect(CounterType.FLYING, CounterType.FIRST_STRIKE) + )); + } + + private HelicaGlider(final HelicaGlider card) { + super(card); + } + + @Override + public HelicaGlider copy() { + return new HelicaGlider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeliodsPunishment.java b/Mage.Sets/src/mage/cards/h/HeliodsPunishment.java index 3d2e3c216f..eaf577e9b4 100644 --- a/Mage.Sets/src/mage/cards/h/HeliodsPunishment.java +++ b/Mage.Sets/src/mage/cards/h/HeliodsPunishment.java @@ -62,7 +62,7 @@ class HeliodsPunishmentLoseAllAbilitiesEnchantedEffect extends ContinuousEffectI public HeliodsPunishmentLoseAllAbilitiesEnchantedEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.LoseAbility); - staticText = "It loses all abilities and has \"{T}: Remove a task counter from {this}. Then if it has no task counters on it, destroy {this}.\" "; + staticText = "It loses all abilities and has \"{T}: Remove a task counter from {this}. Then if it has no task counters on it, destroy {this}.\""; } public HeliodsPunishmentLoseAllAbilitiesEnchantedEffect(final HeliodsPunishmentLoseAllAbilitiesEnchantedEffect effect) { diff --git a/Mage.Sets/src/mage/cards/h/HellfireMongrel.java b/Mage.Sets/src/mage/cards/h/HellfireMongrel.java index 125442d401..da1da307dd 100644 --- a/Mage.Sets/src/mage/cards/h/HellfireMongrel.java +++ b/Mage.Sets/src/mage/cards/h/HellfireMongrel.java @@ -25,7 +25,7 @@ public final class HellfireMongrel extends CardImpl { public HellfireMongrel(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); this.subtype.add(SubType.ELEMENTAL); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/h/HellkitePunisher.java b/Mage.Sets/src/mage/cards/h/HellkitePunisher.java new file mode 100644 index 0000000000..f6509448db --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HellkitePunisher.java @@ -0,0 +1,45 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HellkitePunisher extends CardImpl { + + public HellkitePunisher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {R}: Hellkite Punisher gets +1/+0 until end of turn. + this.addAbility(new SimpleActivatedAbility( + new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}") + )); + } + + private HellkitePunisher(final HellkitePunisher card) { + super(card); + } + + @Override + public HellkitePunisher copy() { + return new HellkitePunisher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HellkiteTyrant.java b/Mage.Sets/src/mage/cards/h/HellkiteTyrant.java index d39feaeecf..4bdf2470ab 100644 --- a/Mage.Sets/src/mage/cards/h/HellkiteTyrant.java +++ b/Mage.Sets/src/mage/cards/h/HellkiteTyrant.java @@ -101,7 +101,7 @@ class HellkiteTyrantEffect extends OneShotEffect { List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game); for (Permanent permanent : permanents) { ContinuousEffect effect = new HellkiteTyrantControlEffect(source.getControllerId()); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/h/HelmOfAwakening.java b/Mage.Sets/src/mage/cards/h/HelmOfAwakening.java index a9192b1bbb..1f0f9f4659 100644 --- a/Mage.Sets/src/mage/cards/h/HelmOfAwakening.java +++ b/Mage.Sets/src/mage/cards/h/HelmOfAwakening.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect; import mage.cards.CardImpl; @@ -9,15 +7,15 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HelmOfAwakening extends CardImpl { - public HelmOfAwakening(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Spells cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionAllEffect(1))); diff --git a/Mage.Sets/src/mage/cards/h/HelmOfObedience.java b/Mage.Sets/src/mage/cards/h/HelmOfObedience.java index bad8be2592..6eb4fc5cc6 100644 --- a/Mage.Sets/src/mage/cards/h/HelmOfObedience.java +++ b/Mage.Sets/src/mage/cards/h/HelmOfObedience.java @@ -1,7 +1,10 @@ - package mage.cards.h; +import java.util.Objects; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -11,6 +14,7 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -20,7 +24,6 @@ import mage.players.Player; import mage.target.common.TargetOpponent; /** - * * @author Plopman */ public final class HelmOfObedience extends CardImpl { @@ -31,13 +34,13 @@ public final class HelmOfObedience extends CardImpl { // {X}, {T}: Target opponent puts cards from the top of their library into their graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice Helm of Obedience and put that card onto the battlefield under your control. X can't be 0. VariableManaCost xCosts = new VariableManaCost(); xCosts.setMinX(1); - SimpleActivatedAbility abilitiy = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HelmOfObedienceEffect(), xCosts); + SimpleActivatedAbility abilitiy = new SimpleActivatedAbility(new HelmOfObedienceEffect(), xCosts); abilitiy.addCost(new TapSourceCost()); abilitiy.addTarget(new TargetOpponent()); this.addAbility(abilitiy); } - public HelmOfObedience(final HelmOfObedience card) { + private HelmOfObedience(final HelmOfObedience card) { super(card); } @@ -51,12 +54,15 @@ class HelmOfObedienceEffect extends OneShotEffect { private static final ManacostVariableValue amount = ManacostVariableValue.instance; - public HelmOfObedienceEffect() { + HelmOfObedienceEffect() { super(Outcome.Detriment); - staticText = "Target opponent puts cards from the top of their library into their graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice {this} and put that card onto the battlefield under your control. X can't be 0"; + staticText = "Target opponent mills a card, then repeats this process until a creature card " + + "or X cards have been put into their graveyard this way, whichever comes first. " + + "If one or more creature cards were put into that graveyard this way, " + + "sacrifice {this} and put one of them onto the battlefield under your control. X can't be 0"; } - public HelmOfObedienceEffect(final HelmOfObedienceEffect effect) { + private HelmOfObedienceEffect(final HelmOfObedienceEffect effect) { super(effect); } @@ -70,32 +76,35 @@ class HelmOfObedienceEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source)); int max = amount.calculate(game, source, this); - if (targetOpponent != null && controller != null && max > 0) { - int numberOfCard = 0; - for (Card card : targetOpponent.getLibrary().getCards(game)) { - if (card != null) { - if (targetOpponent.moveCards(card, Zone.GRAVEYARD, source, game)) { - if (card.isCreature()) { - // If a creature card is put into that graveyard this way, sacrifice Helm of Obedience - // and put that card onto the battlefield under your control. - Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); - if (sourcePermanent != null) { - sourcePermanent.sacrifice(source.getSourceId(), game); - } - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - break; - } else { - numberOfCard++; - if (numberOfCard >= max) { - break; - } - } - } - } - } - return true; + if (targetOpponent == null || controller == null || max <= 0) { + return false; } - return false; + int numberOfCard = 0; + while (targetOpponent.getLibrary().hasCards()) { + Cards cards = targetOpponent.millCards(1, source, game); + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.GRAVEYARD); + numberOfCard += cards.size(); + Set creatures = cards + .getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(MageObject::isCreature) + .collect(Collectors.toSet()); + if (!creatures.isEmpty()) { + controller.moveCards(creatures, Zone.BATTLEFIELD, source, game); + } + if (!creatures.isEmpty()) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.sacrifice(source.getSourceId(), game); + } + break; + } + if (numberOfCard >= max) { + break; + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/h/Helvault.java b/Mage.Sets/src/mage/cards/h/Helvault.java index 894ac8b4cf..325e1aadad 100644 --- a/Mage.Sets/src/mage/cards/h/Helvault.java +++ b/Mage.Sets/src/mage/cards/h/Helvault.java @@ -1,6 +1,6 @@ package mage.cards.h; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; @@ -10,9 +10,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -23,12 +22,6 @@ import java.util.UUID; */ public final class Helvault extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public Helvault(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); addSuperType(SuperType.LEGENDARY); @@ -42,14 +35,14 @@ public final class Helvault extends CardImpl { // {7}, {T}: Exile target creature you don't control. ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetForSourceEffect(), new GenericManaCost(7)); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); // When Helvault is put into a graveyard from the battlefield, return all cards exiled with it to the battlefield under their owners' control. - this.addAbility(new DiesTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD))); + this.addAbility(new DiesSourceTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD))); } - public Helvault(final Helvault card) { + private Helvault(final Helvault card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java b/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java index 1ae97254ff..bd411af8e5 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java +++ b/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java @@ -97,7 +97,7 @@ class HeraldOfLeshracCumulativeCost extends CostImpl { ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame); effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); game.addEffect(effect, ability); - game.applyEffects(); + game.getState().processAction(game); paid = true; } return paid; @@ -141,7 +141,7 @@ class HeraldOfLeshracLeavesEffect extends OneShotEffect { filter.add(new ControllerIdPredicate(source.getControllerId())); for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, playerId); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfTheDreadhorde.java b/Mage.Sets/src/mage/cards/h/HeraldOfTheDreadhorde.java index ef383f86e4..0538dfcd8b 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldOfTheDreadhorde.java +++ b/Mage.Sets/src/mage/cards/h/HeraldOfTheDreadhorde.java @@ -1,7 +1,7 @@ package mage.cards.h; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.keyword.AmassEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class HeraldOfTheDreadhorde extends CardImpl { this.toughness = new MageInt(2); // When Herald of the Dreadhorde dies, amass 2. - this.addAbility(new DiesTriggeredAbility(new AmassEffect(2))); + this.addAbility(new DiesSourceTriggeredAbility(new AmassEffect(2))); } private HeraldOfTheDreadhorde(final HeraldOfTheDreadhorde card) { diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfTheForgotten.java b/Mage.Sets/src/mage/cards/h/HeraldOfTheForgotten.java new file mode 100644 index 0000000000..253c9df3fa --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeraldOfTheForgotten.java @@ -0,0 +1,63 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.CastFromEverywhereSourceCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HeraldOfTheForgotten extends CardImpl { + + private static final FilterCard filter + = new FilterPermanentCard("permanent cards with cycling abilities from your graveyard"); + + static { + filter.add(new AbilityPredicate(CyclingAbility.class)); + } + + public HeraldOfTheForgotten(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{W}{W}"); + + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Herald of the Forgotten enters the battlefield, if you cast it, return any number of target permanent cards with cycling abilities from your graveyard to the battlefield. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()), + CastFromEverywhereSourceCondition.instance, "When {this} enters the battlefield, if you cast it, " + + "return any number of target permanent cards with cycling abilities from your graveyard to the battlefield." + ); + ability.addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, filter)); + this.addAbility(ability); + } + + private HeraldOfTheForgotten(final HeraldOfTheForgotten card) { + super(card); + } + + @Override + public HeraldOfTheForgotten copy() { + return new HeraldOfTheForgotten(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfWar.java b/Mage.Sets/src/mage/cards/h/HeraldOfWar.java index 4ab890792c..27bbed4c1e 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldOfWar.java +++ b/Mage.Sets/src/mage/cards/h/HeraldOfWar.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -25,7 +24,7 @@ import mage.util.CardUtil; public final class HeraldOfWar extends CardImpl { public HeraldOfWar(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); this.subtype.add(SubType.ANGEL); this.power = new MageInt(3); @@ -54,7 +53,7 @@ class HeraldOfWarCostReductionEffect extends CostModificationEffectImpl { HeraldOfWarCostReductionEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "Angel spells and Human spells you cast cost {1} less to cast for each +1/+1 counter on Herald of War"; + staticText = "Angel spells and Human spells you cast cost {1} less to cast for each +1/+1 counter on {this}"; } HeraldOfWarCostReductionEffect(HeraldOfWarCostReductionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/h/HereticsPunishment.java b/Mage.Sets/src/mage/cards/h/HereticsPunishment.java index 71c4bbe179..87439fa4df 100644 --- a/Mage.Sets/src/mage/cards/h/HereticsPunishment.java +++ b/Mage.Sets/src/mage/cards/h/HereticsPunishment.java @@ -1,21 +1,20 @@ package mage.cards.h; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetAnyTarget; -import java.util.Set; +import java.util.Objects; import java.util.UUID; /** @@ -27,7 +26,7 @@ public final class HereticsPunishment extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}"); // {3}{R}: Choose any target, then put the top three cards of your library into your graveyard. Heretic's Punishment deals damage to that creature or player equal to the highest converted mana cost among those cards. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HereticsPunishmentEffect(), new ManaCostsImpl("{3}{R}")); + Ability ability = new SimpleActivatedAbility(new HereticsPunishmentEffect(), new ManaCostsImpl("{3}{R}")); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } @@ -46,7 +45,7 @@ class HereticsPunishmentEffect extends OneShotEffect { public HereticsPunishmentEffect() { super(Outcome.Damage); - staticText = "Choose any target, then put the top three cards of your library into your graveyard. {this} deals damage to that permanent or player equal to the highest converted mana cost among those cards"; + staticText = "Choose any target, then mill three cards. {this} deals damage to that permanent or player equal to the highest converted mana cost among the milled cards"; } public HereticsPunishmentEffect(final HereticsPunishmentEffect effect) { @@ -56,26 +55,26 @@ class HereticsPunishmentEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int maxCost = 0; - Set cardList = controller.getLibrary().getTopCards(game, 3); - for (Card card : cardList) { - int test = card.getConvertedManaCost(); - if (test > maxCost) { - maxCost = test; - } - } - controller.moveCards(cardList, Zone.GRAVEYARD, source, game); - Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); - if (permanent != null) { - permanent.damage(maxCost, source.getSourceId(), game, false, true); - return true; - } - Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (targetPlayer != null) { - targetPlayer.damage(maxCost, source.getSourceId(), game); - return true; - } + if (controller == null) { + return false; + } + int maxCost = controller + .millCards(3, source, game) + .getCards(game) + .stream() + .filter(Objects::nonNull) + .mapToInt(MageObject::getConvertedManaCost) + .max() + .orElse(0); + Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); + if (permanent != null) { + permanent.damage(maxCost, source.getSourceId(), game, false, true); + return true; + } + Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); + if (targetPlayer != null) { + targetPlayer.damage(maxCost, source.getSourceId(), game); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/h/HeritageDruid.java b/Mage.Sets/src/mage/cards/h/HeritageDruid.java index 804c139373..cbfc1b23d6 100644 --- a/Mage.Sets/src/mage/cards/h/HeritageDruid.java +++ b/Mage.Sets/src/mage/cards/h/HeritageDruid.java @@ -1,19 +1,24 @@ - package mage.cards.h; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import mage.MageInt; import mage.Mana; +import mage.abilities.Ability; import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; import mage.target.common.TargetControlledPermanent; /** @@ -21,15 +26,8 @@ import mage.target.common.TargetControlledPermanent; */ public final class HeritageDruid extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("untapped Elves you control"); - - static { - filter.add(Predicates.not(TappedPredicate.instance)); - filter.add(SubType.ELF.getPredicate()); - } - public HeritageDruid(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.DRUID); @@ -37,7 +35,13 @@ public final class HeritageDruid extends CardImpl { this.toughness = new MageInt(1); // Tap three untapped Elves you control: Add {G}{G}{G}. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana(3), new TapTargetCost(new TargetControlledPermanent(3, 3, filter, true)))); + FilterControlledPermanent filter = new FilterControlledPermanent("untapped Elves you control"); + filter.add(Predicates.not(TappedPredicate.instance)); + filter.add(SubType.ELF.getPredicate()); + this.addAbility(new SimpleManaAbility( + Zone.BATTLEFIELD, + new HeritageDruidManaEffect(filter), + new TapTargetCost(new TargetControlledPermanent(3, 3, filter, true)))); } public HeritageDruid(final HeritageDruid card) { @@ -49,3 +53,37 @@ public final class HeritageDruid extends CardImpl { return new HeritageDruid(this); } } + +class HeritageDruidManaEffect extends BasicManaEffect { + + private final FilterPermanent filter; + + public HeritageDruidManaEffect(FilterPermanent filter) { + super(Mana.GreenMana(3)); + this.filter = filter; + } + + public HeritageDruidManaEffect(final HeritageDruidManaEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + } + + @Override + public HeritageDruidManaEffect copy() { + return new HeritageDruidManaEffect(this); + } + + @Override + public List getNetMana(Game game, Ability source) { + if (game != null && game.inCheckPlayableState()) { + int count = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) / 3; + List netMana = new ArrayList<>(); + if (count > 0) { + netMana.add(Mana.GreenMana(count * 3)); + } + return netMana; + } + return super.getNetMana(game, source); + } + +} diff --git a/Mage.Sets/src/mage/cards/h/HeroOfIroas.java b/Mage.Sets/src/mage/cards/h/HeroOfIroas.java index 21855980f9..f00ae978c6 100644 --- a/Mage.Sets/src/mage/cards/h/HeroOfIroas.java +++ b/Mage.Sets/src/mage/cards/h/HeroOfIroas.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; @@ -15,19 +13,21 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterCard; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HeroOfIroas extends CardImpl { private static final FilterCard filter = new FilterCard("Aura spells"); + static { filter.add(SubType.AURA.getPredicate()); } public HeroOfIroas(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); @@ -36,6 +36,7 @@ public final class HeroOfIroas extends CardImpl { // Aura spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); + // Heroic — Whenever you cast a spell that targets Hero of Iroas, put a +1/+1 counter on Hero of Iroas. this.addAbility(new HeroicAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()))); } diff --git a/Mage.Sets/src/mage/cards/h/HeroesBane.java b/Mage.Sets/src/mage/cards/h/HeroesBane.java index 07be3f80bf..9cc3086757 100644 --- a/Mage.Sets/src/mage/cards/h/HeroesBane.java +++ b/Mage.Sets/src/mage/cards/h/HeroesBane.java @@ -34,7 +34,7 @@ public final class HeroesBane extends CardImpl { "with four +1/+1 counters on it")); // {2}{G}{G}: Put X +1/+1 counters on Heroe's Bane, where X is its power. Effect effect = new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), new SourcePermanentPowerCount(), true); - effect.setText("Put X +1/+1 counters on Heroe's Bane, where X is its power"); + effect.setText("Put X +1/+1 counters on {this}, where X is its power"); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{2}{G}{G}"))); } diff --git a/Mage.Sets/src/mage/cards/h/Heroism.java b/Mage.Sets/src/mage/cards/h/Heroism.java index 85b7e04162..fd867fc861 100644 --- a/Mage.Sets/src/mage/cards/h/Heroism.java +++ b/Mage.Sets/src/mage/cards/h/Heroism.java @@ -107,7 +107,7 @@ class HeroismEffect extends OneShotEffect { for (Permanent permanent : permanentsToPrevent) { ContinuousEffect effect = new PreventDamageByTargetEffect(Duration.EndOfTurn, Integer.MAX_VALUE, true); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/h/HexParasite.java b/Mage.Sets/src/mage/cards/h/HexParasite.java index 5944124cac..915c2cbb62 100644 --- a/Mage.Sets/src/mage/cards/h/HexParasite.java +++ b/Mage.Sets/src/mage/cards/h/HexParasite.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -10,24 +8,21 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class HexParasite extends CardImpl { public HexParasite(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}"); this.subtype.add(SubType.INSECT); this.power = new MageInt(1); @@ -76,7 +71,7 @@ class HexParasiteEffect extends OneShotEffect { String[] counterNames = permanent.getCounters(game).keySet().toArray(new String[0]); for (String counterName : counterNames) { if (player.chooseUse(Outcome.Neutral, "Do you want to remove " + counterName + " counters?", source, game)) { - if (permanent.getCounters(game).get(counterName).getCount() == 1 || toRemove == 1) { + if (permanent.getCounters(game).get(counterName).getCount() == 1 || (toRemove - removed == 1)) { permanent.removeCounters(counterName, 1, game); removed++; } else { @@ -91,7 +86,9 @@ class HexParasiteEffect extends OneShotEffect { break; } } - game.addEffect(new BoostSourceEffect(removed, 0, Duration.EndOfTurn), source); + if (removed > 0) { + game.addEffect(new BoostSourceEffect(removed, 0, Duration.EndOfTurn), source); + } return true; } return false; diff --git a/Mage.Sets/src/mage/cards/h/HiddenPredators.java b/Mage.Sets/src/mage/cards/h/HiddenPredators.java index c483339629..7f21ddab91 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenPredators.java +++ b/Mage.Sets/src/mage/cards/h/HiddenPredators.java @@ -1,124 +1,124 @@ -package mage.cards.h; - -import mage.MageInt; -import mage.abilities.StateTriggeredAbility; -import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.PowerPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.token.TokenImpl; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class HiddenPredators extends CardImpl { - - public HiddenPredators(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); - - // When an opponent controls a creature with power 4 or greater, if Hidden Predators is an enchantment, Hidden Predators becomes a 4/4 Beast creature. - this.addAbility(new HiddenPredatorsStateTriggeredAbility()); - } - - public HiddenPredators(final HiddenPredators card) { - super(card); - } - - @Override - public HiddenPredators copy() { - return new HiddenPredators(this); - } -} - -class HiddenPredatorsStateTriggeredAbility extends StateTriggeredAbility { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); - } - - public HiddenPredatorsStateTriggeredAbility() { - super(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new HiddenPredatorsToken(), "", Duration.Custom, true, false)); - } - - public HiddenPredatorsStateTriggeredAbility(final HiddenPredatorsStateTriggeredAbility ability) { - super(ability); - } - - @Override - public HiddenPredatorsStateTriggeredAbility copy() { - return new HiddenPredatorsStateTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return !game.getBattlefield().getAllActivePermanents(filter, game).isEmpty(); - } - - @Override - public boolean checkInterveningIfClause(Game game) { - if (getSourcePermanentIfItStillExists(game) != null) { - return getSourcePermanentIfItStillExists(game).isEnchantment(); - } - return false; - } - - @Override - public boolean canTrigger(Game game) { - //20100716 - 603.8 - return !Boolean.TRUE.equals(game.getState().getValue(getSourceId().toString() + "triggered")); - } - - @Override - public void trigger(Game game, UUID controllerId) { - //20100716 - 603.8 - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); - } - - @Override - public boolean resolve(Game game) { - //20100716 - 603.8 - boolean result = super.resolve(game); - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); - return result; - } - - @Override - public void counter(Game game) { - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); - } - - @Override - public String getRule() { - return "When an opponent controls a creature with 4 or greater power, if {this} is an enchantment, " + super.getRule(); - } - -} - -class HiddenPredatorsToken extends TokenImpl { - - public HiddenPredatorsToken() { - super("Beast", "4/4 Beast creature"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.BEAST); - power = new MageInt(4); - toughness = new MageInt(4); - } - - public HiddenPredatorsToken(final HiddenPredatorsToken token) { - super(token); - } - - public HiddenPredatorsToken copy() { - return new HiddenPredatorsToken(this); - } -} +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.StateTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.TokenImpl; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class HiddenPredators extends CardImpl { + + public HiddenPredators(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); + + // When an opponent controls a creature with power 4 or greater, if Hidden Predators is an enchantment, Hidden Predators becomes a 4/4 Beast creature. + this.addAbility(new HiddenPredatorsStateTriggeredAbility()); + } + + public HiddenPredators(final HiddenPredators card) { + super(card); + } + + @Override + public HiddenPredators copy() { + return new HiddenPredators(this); + } +} + +class HiddenPredatorsStateTriggeredAbility extends StateTriggeredAbility { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); + } + + public HiddenPredatorsStateTriggeredAbility() { + super(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new HiddenPredatorsToken(), "", Duration.Custom, true, false)); + } + + public HiddenPredatorsStateTriggeredAbility(final HiddenPredatorsStateTriggeredAbility ability) { + super(ability); + } + + @Override + public HiddenPredatorsStateTriggeredAbility copy() { + return new HiddenPredatorsStateTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return !game.getBattlefield().getAllActivePermanents(filter, game).isEmpty(); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + if (getSourcePermanentIfItStillExists(game) != null) { + return getSourcePermanentIfItStillExists(game).isEnchantment(); + } + return false; + } + + @Override + public boolean canTrigger(Game game) { + //20100716 - 603.8 + return !Boolean.TRUE.equals(game.getState().getValue(getSourceId().toString() + "triggered")); + } + + @Override + public void trigger(Game game, UUID controllerId) { + //20100716 - 603.8 + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); + super.trigger(game, controllerId); + } + + @Override + public boolean resolve(Game game) { + //20100716 - 603.8 + boolean result = super.resolve(game); + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); + return result; + } + + @Override + public void counter(Game game) { + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); + } + + @Override + public String getRule() { + return "When an opponent controls a creature with 4 or greater power, if {this} is an enchantment, " + super.getRule(); + } + +} + +class HiddenPredatorsToken extends TokenImpl { + + public HiddenPredatorsToken() { + super("Beast", "4/4 Beast creature"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.BEAST); + power = new MageInt(4); + toughness = new MageInt(4); + } + + public HiddenPredatorsToken(final HiddenPredatorsToken token) { + super(token); + } + + public HiddenPredatorsToken copy() { + return new HiddenPredatorsToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HiddenRetreat.java b/Mage.Sets/src/mage/cards/h/HiddenRetreat.java index 9569a11ab6..7cf7ab8744 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenRetreat.java +++ b/Mage.Sets/src/mage/cards/h/HiddenRetreat.java @@ -1,76 +1,76 @@ -package mage.cards.h; - -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.PutCardFromHandOnTopOfLibraryCost; -import mage.abilities.effects.PreventionEffectImpl; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.DamageEvent; -import mage.game.events.GameEvent; -import mage.target.TargetSpell; - -import java.util.UUID; - -/** - * @author bunchOfDevs - */ -public class HiddenRetreat extends CardImpl { - - public HiddenRetreat(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); - - //Put a card from your hand on top of your library: Prevent all damage that would be dealt by target instant or sorcery spell this turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HiddenRetreatEffect(), new PutCardFromHandOnTopOfLibraryCost()); - ability.addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY)); - this.addAbility(ability); - - } - - public HiddenRetreat(final HiddenRetreat hiddenRetreat) { - super(hiddenRetreat); - } - - @Override - public HiddenRetreat copy() { - return new HiddenRetreat(this); - } -} - -class HiddenRetreatEffect extends PreventionEffectImpl { - - public HiddenRetreatEffect() { - super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); - this.staticText = "Prevent all damage that would be dealt by target instant or sorcery spell this turn."; - } - - public HiddenRetreatEffect(final HiddenRetreatEffect effect) { - super(effect); - } - - @Override - public HiddenRetreatEffect copy() { - return new HiddenRetreatEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return (super.applies(event, source, game) - && event instanceof DamageEvent - && event.getAmount() > 0 - && game.getObject(source.getFirstTarget()) != null - && game.getObject(event.getSourceId()) != null - && game.getObject(source.getFirstTarget()).equals(game.getObject(event.getSourceId())) - && game.getObject(source.getFirstTarget()).isInstantOrSorcery()); - } -} +package mage.cards.h; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PutCardFromHandOnTopOfLibraryCost; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author bunchOfDevs + */ +public class HiddenRetreat extends CardImpl { + + public HiddenRetreat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + //Put a card from your hand on top of your library: Prevent all damage that would be dealt by target instant or sorcery spell this turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HiddenRetreatEffect(), new PutCardFromHandOnTopOfLibraryCost()); + ability.addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY)); + this.addAbility(ability); + + } + + public HiddenRetreat(final HiddenRetreat hiddenRetreat) { + super(hiddenRetreat); + } + + @Override + public HiddenRetreat copy() { + return new HiddenRetreat(this); + } +} + +class HiddenRetreatEffect extends PreventionEffectImpl { + + public HiddenRetreatEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); + this.staticText = "Prevent all damage that would be dealt by target instant or sorcery spell this turn."; + } + + public HiddenRetreatEffect(final HiddenRetreatEffect effect) { + super(effect); + } + + @Override + public HiddenRetreatEffect copy() { + return new HiddenRetreatEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return (super.applies(event, source, game) + && event instanceof DamageEvent + && event.getAmount() > 0 + && game.getObject(source.getFirstTarget()) != null + && game.getObject(event.getSourceId()) != null + && game.getObject(source.getFirstTarget()).equals(game.getObject(event.getSourceId())) + && game.getObject(source.getFirstTarget()).isInstantOrSorcery()); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HiddenStag.java b/Mage.Sets/src/mage/cards/h/HiddenStag.java index 6083cbaf30..7af07d85a7 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenStag.java +++ b/Mage.Sets/src/mage/cards/h/HiddenStag.java @@ -1,73 +1,73 @@ -package mage.cards.h; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.ControllerPlaysLandTriggeredAbility; -import mage.abilities.common.OpponentPlaysLandTriggeredAbility; -import mage.abilities.condition.common.SourceMatchesFilterCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.abilities.effects.common.continuous.BecomesEnchantmentSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.StaticFilters; -import mage.game.permanent.token.TokenImpl; - -/** - * - * @author jeffwadsworth - */ -public final class HiddenStag extends CardImpl { - - public HiddenStag(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); - - // Whenever an opponent plays a land, if Hidden Stag is an enchantment, Hidden Stag becomes a 3/2 Elk Beast creature. - Effect effect = new BecomesCreatureSourceEffect(new ElkBeastToken(), "", Duration.WhileOnBattlefield, true, false); - TriggeredAbility ability = new OpponentPlaysLandTriggeredAbility(Zone.BATTLEFIELD, effect, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), - "Whenever an opponent plays a land, if Hidden Stag is an enchantment, Hidden Stag becomes a 3/2 Elk Beast creature.")); - - // Whenever you play a land, if Hidden Stag is a creature, Hidden Stag becomes an enchantment. - Effect effect2 = new BecomesEnchantmentSourceEffect(); - TriggeredAbility ability2 = new ControllerPlaysLandTriggeredAbility(Zone.BATTLEFIELD, effect2, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability2, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE), - "Whenever you play a land, if Hidden Stag is a creature, Hidden Stag becomes an enchantment.")); - - } - - public HiddenStag(final HiddenStag card) { - super(card); - } - - @Override - public HiddenStag copy() { - return new HiddenStag(this); - } -} - -class ElkBeastToken extends TokenImpl { - - public ElkBeastToken() { - super("Elk Beast", "3/2 Elk Beast creature"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.ELK); - subtype.add(SubType.BEAST); - power = new MageInt(3); - toughness = new MageInt(2); - } - - public ElkBeastToken(final ElkBeastToken token) { - super(token); - } - - public ElkBeastToken copy() { - return new ElkBeastToken(this); - } -} +package mage.cards.h; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.ControllerPlaysLandTriggeredAbility; +import mage.abilities.common.OpponentPlaysLandTriggeredAbility; +import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.effects.common.continuous.BecomesEnchantmentSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.permanent.token.TokenImpl; + +/** + * + * @author jeffwadsworth + */ +public final class HiddenStag extends CardImpl { + + public HiddenStag(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); + + // Whenever an opponent plays a land, if Hidden Stag is an enchantment, Hidden Stag becomes a 3/2 Elk Beast creature. + Effect effect = new BecomesCreatureSourceEffect(new ElkBeastToken(), "", Duration.WhileOnBattlefield, true, false); + TriggeredAbility ability = new OpponentPlaysLandTriggeredAbility(Zone.BATTLEFIELD, effect, false); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + "Whenever an opponent plays a land, if Hidden Stag is an enchantment, Hidden Stag becomes a 3/2 Elk Beast creature.")); + + // Whenever you play a land, if Hidden Stag is a creature, Hidden Stag becomes an enchantment. + Effect effect2 = new BecomesEnchantmentSourceEffect(); + TriggeredAbility ability2 = new ControllerPlaysLandTriggeredAbility(Zone.BATTLEFIELD, effect2, false); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability2, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE), + "Whenever you play a land, if Hidden Stag is a creature, Hidden Stag becomes an enchantment.")); + + } + + public HiddenStag(final HiddenStag card) { + super(card); + } + + @Override + public HiddenStag copy() { + return new HiddenStag(this); + } +} + +class ElkBeastToken extends TokenImpl { + + public ElkBeastToken() { + super("Elk Beast", "3/2 Elk Beast creature"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.ELK); + subtype.add(SubType.BEAST); + power = new MageInt(3); + toughness = new MageInt(2); + } + + public ElkBeastToken(final ElkBeastToken token) { + super(token); + } + + public ElkBeastToken copy() { + return new ElkBeastToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HierophantsChalice.java b/Mage.Sets/src/mage/cards/h/HierophantsChalice.java index d803d89a9b..8fa3f4a8b8 100644 --- a/Mage.Sets/src/mage/cards/h/HierophantsChalice.java +++ b/Mage.Sets/src/mage/cards/h/HierophantsChalice.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; @@ -13,8 +11,9 @@ import mage.constants.CardType; import mage.target.Target; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class HierophantsChalice extends CardImpl { @@ -24,7 +23,7 @@ public final class HierophantsChalice extends CardImpl { // When Hierophant's Chalice enters the battlefield, target opponent loses 1 life and you gain 1 life. Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(1), false); - ability.addEffect(new GainLifeEffect(1).setText("and you gain one life.")); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); Target target = new TargetOpponent(); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/h/HighSeas.java b/Mage.Sets/src/mage/cards/h/HighSeas.java index 412c56ce64..652a5c6f17 100644 --- a/Mage.Sets/src/mage/cards/h/HighSeas.java +++ b/Mage.Sets/src/mage/cards/h/HighSeas.java @@ -1,35 +1,36 @@ - package mage.cards.h; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author fireshoes */ public final class HighSeas extends CardImpl { - + private static final FilterCreatureCard filter = new FilterCreatureCard("Red creature spells and green creature spells"); + static { filter.add(Predicates.or(new ColorPredicate(ObjectColor.RED), (new ColorPredicate(ObjectColor.GREEN)))); } public HighSeas(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); // Red creature spells and green creature spells cost {1} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(filter, 1))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY))); } public HighSeas(final HighSeas card) { diff --git a/Mage.Sets/src/mage/cards/h/HighcliffFelidar.java b/Mage.Sets/src/mage/cards/h/HighcliffFelidar.java index 38a85db364..f637e82547 100644 --- a/Mage.Sets/src/mage/cards/h/HighcliffFelidar.java +++ b/Mage.Sets/src/mage/cards/h/HighcliffFelidar.java @@ -60,8 +60,8 @@ class HighcliffFelidarEffect extends OneShotEffect { HighcliffFelidarEffect() { super(Outcome.Benefit); - staticText = "for each opponent, choose a creature with the greatest power " + - "among creatures that player controls. Destroy those creatures."; + staticText = "for each opponent, choose a creature with the greatest power " + + "among creatures that player controls. Destroy those creatures."; } private HighcliffFelidarEffect(final HighcliffFelidarEffect effect) { @@ -75,8 +75,8 @@ class HighcliffFelidarEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } Set toDestroy = new HashSet(); @@ -93,18 +93,17 @@ class HighcliffFelidarEffect extends OneShotEffect { .mapToInt(MageInt::getValue) .max() .orElse(Integer.MIN_VALUE); - if (maxPower == Integer.MIN_VALUE) { - return; - } - FilterPermanent filter = new FilterCreaturePermanent( - "creature with the greatest power controlled by " + opponent.getName() - ); - filter.add(new ControllerIdPredicate(opponent.getId())); - filter.add(new PowerPredicate(ComparisonType.EQUAL_TO, maxPower)); - TargetPermanent target = new TargetPermanent(filter); - target.setNotTarget(true); - if (player.choose(outcome, target, source.getSourceId(), game)) { - toDestroy.add(target.getFirstTarget()); + if (maxPower > Integer.MIN_VALUE) { + FilterPermanent filter = new FilterCreaturePermanent( + "creature with the greatest power controlled by " + opponent.getName() + ); + filter.add(new ControllerIdPredicate(opponent.getId())); + filter.add(new PowerPredicate(ComparisonType.EQUAL_TO, maxPower)); + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + if (controller.choose(outcome, target, source.getSourceId(), game)) { + toDestroy.add(target.getFirstTarget()); + } } }); toDestroy.stream() @@ -116,7 +115,6 @@ class HighcliffFelidarEffect extends OneShotEffect { } // I realized after writing all this that the ability doesn't target but I like this code too much to erase it - //enum HighcliffFelidarAdjuster implements TargetAdjuster { // instance; // diff --git a/Mage.Sets/src/mage/cards/h/HighlandGame.java b/Mage.Sets/src/mage/cards/h/HighlandGame.java index 27d939079e..65f04583c0 100644 --- a/Mage.Sets/src/mage/cards/h/HighlandGame.java +++ b/Mage.Sets/src/mage/cards/h/HighlandGame.java @@ -3,7 +3,7 @@ package mage.cards.h; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class HighlandGame extends CardImpl { this.toughness = new MageInt(1); // When Highland Game dies, you gain 2 life. - this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(2))); + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(2))); } public HighlandGame(final HighlandGame card) { diff --git a/Mage.Sets/src/mage/cards/h/HintOfInsanity.java b/Mage.Sets/src/mage/cards/h/HintOfInsanity.java index 1d29958721..d41189a633 100644 --- a/Mage.Sets/src/mage/cards/h/HintOfInsanity.java +++ b/Mage.Sets/src/mage/cards/h/HintOfInsanity.java @@ -1,21 +1,26 @@ package mage.cards.h; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.common.TargetCardInHand; -import mage.util.CardUtil; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; import java.util.UUID; +import java.util.stream.Collectors; /** - * @author jeffwadsworth + * @author TheElk801 */ public final class HintOfInsanity extends CardImpl { @@ -25,10 +30,9 @@ public final class HintOfInsanity extends CardImpl { // Target player reveals their hand. That player discards all nonland cards with the same name as another card in their hand. this.getSpellAbility().addEffect(new HintOfInsanityEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - } - public HintOfInsanity(final HintOfInsanity card) { + private HintOfInsanity(final HintOfInsanity card) { super(card); } @@ -40,12 +44,13 @@ public final class HintOfInsanity extends CardImpl { class HintOfInsanityEffect extends OneShotEffect { - public HintOfInsanityEffect() { + HintOfInsanityEffect() { super(Outcome.Discard); - this.staticText = "Target player reveals their hand. That player discards all nonland cards with the same name as another card in their hand"; + this.staticText = "Target player reveals their hand. " + + "That player discards all nonland cards with the same name as another card in their hand"; } - public HintOfInsanityEffect(final HintOfInsanityEffect effect) { + private HintOfInsanityEffect(final HintOfInsanityEffect effect) { super(effect); } @@ -56,26 +61,26 @@ class HintOfInsanityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - FilterCard filter = new FilterCard("card from your hand"); - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - Card chosenCard; - if (targetPlayer != null) { - TargetCardInHand targetCard = new TargetCardInHand(filter); - targetCard.setNotTarget(true); - Cards cardsInHand = new CardsImpl(); - cardsInHand.addAll(targetPlayer.getHand()); - targetPlayer.revealCards("Hint of Insanity Reveal", cardsInHand, game); - if (!cardsInHand.isEmpty() - && targetPlayer.choose(Outcome.Discard, targetCard, source.getSourceId(), game)) { - chosenCard = game.getCard(targetCard.getFirstTarget()); - for (Card card : cardsInHand.getCards(game)) { - if (CardUtil.haveSameNames(card, chosenCard) && !card.isLand()) { - targetPlayer.discard(card, source, game); - } - } - return true; - } + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; } - return false; + Map nameCounts = new HashMap<>(); + player.getHand() + .getCards(game) + .stream() + .map(MageObject::getName) + .forEach(s -> nameCounts.compute(s, (u, i) -> i == null ? 1 : Integer.sum(i, 1))); + Cards cards = new CardsImpl( + player.getHand() + .getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> !card.isLand()) + .filter(card -> nameCounts.getOrDefault(card.getName(), 0) > 1) + .collect(Collectors.toSet()) + ); + player.discard(cards, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/h/HinterlandScourge.java b/Mage.Sets/src/mage/cards/h/HinterlandScourge.java index 8e51404854..433124334d 100644 --- a/Mage.Sets/src/mage/cards/h/HinterlandScourge.java +++ b/Mage.Sets/src/mage/cards/h/HinterlandScourge.java @@ -14,6 +14,7 @@ import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.SubType; import mage.constants.TargetController; import mage.constants.Zone; @@ -37,7 +38,7 @@ public final class HinterlandScourge extends CardImpl { this.toughness = new MageInt(2); // Hinterland Scourge must be blocked if able. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect(Duration.WhileOnBattlefield))); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Hinterland Scourge. TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); diff --git a/Mage.Sets/src/mage/cards/h/HisokasGuard.java b/Mage.Sets/src/mage/cards/h/HisokasGuard.java index 0d11887e2a..886b4aea6d 100644 --- a/Mage.Sets/src/mage/cards/h/HisokasGuard.java +++ b/Mage.Sets/src/mage/cards/h/HisokasGuard.java @@ -100,7 +100,7 @@ class HisokasGuardGainAbilityTargetEffect extends ContinuousEffectImpl { if (hisokasGuard != null && !hisokasGuard.getConnectedCards("HisokasGuard").isEmpty()) { Permanent guardedCreature = game.getPermanent(hisokasGuard.getConnectedCards("HisokasGuard").get(0)); if (guardedCreature != null && hisokasGuard.isTapped()) { - guardedCreature.addAbility(ability, game); + guardedCreature.addAbility(ability, source.getSourceId(), game); return true; } else { // if guard isn't tapped, the effect is no more valid diff --git a/Mage.Sets/src/mage/cards/h/HistorianOfZhalfir.java b/Mage.Sets/src/mage/cards/h/HistorianOfZhalfir.java new file mode 100644 index 0000000000..4bbe30d4f3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HistorianOfZhalfir.java @@ -0,0 +1,49 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPlaneswalkerPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HistorianOfZhalfir extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPlaneswalkerPermanent(SubType.TEFERI); + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + + public HistorianOfZhalfir(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever Historian of Zhalfir attacks, if you control a Teferi planeswalker, draw a card. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1), false), + condition, "Whenever {this} attacks, if you control a Teferi planeswalker, draw a card") + ); + } + + private HistorianOfZhalfir(final HistorianOfZhalfir card) { + super(card); + } + + @Override + public HistorianOfZhalfir copy() { + return new HistorianOfZhalfir(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HoardersGreed.java b/Mage.Sets/src/mage/cards/h/HoardersGreed.java index 580f83182a..0c14b533c2 100644 --- a/Mage.Sets/src/mage/cards/h/HoardersGreed.java +++ b/Mage.Sets/src/mage/cards/h/HoardersGreed.java @@ -58,7 +58,7 @@ class HoardersGreedEffect extends OneShotEffect { if (controller != null) { do { controller.loseLife(2, game, false); - controller.drawCards(2, game); + controller.drawCards(2, source.getSourceId(), game); } while (controller.canRespond() && ClashEffect.getInstance().apply(game, source)); return true; } diff --git a/Mage.Sets/src/mage/cards/h/HoardingDragon.java b/Mage.Sets/src/mage/cards/h/HoardingDragon.java index a5a5b394b6..1a42bc8d5f 100644 --- a/Mage.Sets/src/mage/cards/h/HoardingDragon.java +++ b/Mage.Sets/src/mage/cards/h/HoardingDragon.java @@ -6,7 +6,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromExileEffect; @@ -44,7 +44,7 @@ public final class HoardingDragon extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new HoardingDragonEffect(this.getId()), true)); // When Hoarding Dragon dies, you may put the exiled card into its owner's hand. - this.addAbility(new DiesTriggeredAbility(new ReturnFromExileEffect(this.getId(), Zone.HAND), false)); + this.addAbility(new DiesSourceTriggeredAbility(new ReturnFromExileEffect(this.getId(), Zone.HAND), false)); } public HoardingDragon(final HoardingDragon card) { diff --git a/Mage.Sets/src/mage/cards/h/Hobblefiend.java b/Mage.Sets/src/mage/cards/h/Hobblefiend.java new file mode 100644 index 0000000000..7f81332b25 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/Hobblefiend.java @@ -0,0 +1,53 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Hobblefiend extends CardImpl { + + public Hobblefiend(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.DEVIL); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // {1}, Sacrifice another creature: Put a +1/+1 counter on Hobblefiend. + Ability ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(1) + ); + ability.addCost(new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE) + )); + this.addAbility(ability); + } + + private Hobblefiend(final Hobblefiend card) { + super(card); + } + + @Override + public Hobblefiend copy() { + return new Hobblefiend(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HoldTheGates.java b/Mage.Sets/src/mage/cards/h/HoldTheGates.java index e8b5b30675..1cdda2a3b1 100644 --- a/Mage.Sets/src/mage/cards/h/HoldTheGates.java +++ b/Mage.Sets/src/mage/cards/h/HoldTheGates.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import java.util.UUID; @@ -25,12 +25,14 @@ public final class HoldTheGates extends CardImpl { public HoldTheGates(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); - // Creatures you control get +0/+1 for each Gate you control and have vigilance. Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, - new BoostControlledEffect(StaticValue.get(0), GateYouControlCount.instance, Duration.WhileOnBattlefield)); - ability.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, new FilterControlledCreaturePermanent("Creatures")) - .setText("Creatures you control get +0/+1 for each Gate you control and have vigilance")); + new BoostControlledEffect(StaticValue.get(0), GateYouControlCount.instance, Duration.WhileOnBattlefield) + ); + ability.addEffect( + new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED) + .setText("and have vigilance") + ); ability.addHint(GateYouControlHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HollowDogs.java b/Mage.Sets/src/mage/cards/h/HollowDogs.java index 16f4eaa5e7..b2daa9ed90 100644 --- a/Mage.Sets/src/mage/cards/h/HollowDogs.java +++ b/Mage.Sets/src/mage/cards/h/HollowDogs.java @@ -20,7 +20,7 @@ public final class HollowDogs extends CardImpl { public HollowDogs(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}"); this.subtype.add(SubType.ZOMBIE); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/h/HollowbornBarghest.java b/Mage.Sets/src/mage/cards/h/HollowbornBarghest.java index 21a5fe789f..3e8991c95e 100644 --- a/Mage.Sets/src/mage/cards/h/HollowbornBarghest.java +++ b/Mage.Sets/src/mage/cards/h/HollowbornBarghest.java @@ -36,7 +36,7 @@ public final class HollowbornBarghest extends CardImpl { public HollowbornBarghest(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}"); this.subtype.add(SubType.DEMON); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(7); this.toughness = new MageInt(6); diff --git a/Mage.Sets/src/mage/cards/h/Hollowsage.java b/Mage.Sets/src/mage/cards/h/Hollowsage.java index d8d2aa755e..5ccc5a6d99 100644 --- a/Mage.Sets/src/mage/cards/h/Hollowsage.java +++ b/Mage.Sets/src/mage/cards/h/Hollowsage.java @@ -1,84 +1,42 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.Ability; import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.abilities.keyword.InspiredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class Hollowsage extends CardImpl { - + public Hollowsage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.MERFOLK); this.subtype.add(SubType.WIZARD); - + this.power = new MageInt(2); this.toughness = new MageInt(2); // Whenever Hollowsage becomes untapped, you may have target player discard a card. - TriggeredAbility ability = new BecomesUntappedTriggeredAbility(new DiscardTargetEffect(1), true); + Ability ability = new InspiredAbility(new DiscardTargetEffect(1), true, false); ability.addTarget(new TargetPlayer()); this.addAbility(ability); - } - + public Hollowsage(final Hollowsage card) { super(card); } - + @Override public Hollowsage copy() { return new Hollowsage(this); } } - -class BecomesUntappedTriggeredAbility extends TriggeredAbilityImpl { - - public BecomesUntappedTriggeredAbility(Effect effect, boolean isOptional) { - super(Zone.BATTLEFIELD, effect, isOptional); - } - - public BecomesUntappedTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - public BecomesUntappedTriggeredAbility(final BecomesUntappedTriggeredAbility ability) { - super(ability); - } - - @Override - public BecomesUntappedTriggeredAbility copy() { - return new BecomesUntappedTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.UNTAPPED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getTargetId().equals(sourceId); - } - - @Override - public String getRule() { - return "When {this} becomes untapped, " + super.getRule(); - } -} diff --git a/Mage.Sets/src/mage/cards/h/HomuraHumanAscendant.java b/Mage.Sets/src/mage/cards/h/HomuraHumanAscendant.java index 6a06beb23d..6b9715e047 100644 --- a/Mage.Sets/src/mage/cards/h/HomuraHumanAscendant.java +++ b/Mage.Sets/src/mage/cards/h/HomuraHumanAscendant.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CantBlockAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.FlippedCondition; @@ -50,7 +50,7 @@ public final class HomuraHumanAscendant extends CardImpl { // Homura, Human Ascendant can't block. this.addAbility(new CantBlockAbility()); // When Homura dies, return it to the battlefield flipped. - this.addAbility(new DiesTriggeredAbility(new HomuraReturnFlippedSourceEffect(new HomurasEssence2()))); + this.addAbility(new DiesSourceTriggeredAbility(new HomuraReturnFlippedSourceEffect(new HomurasEssence2()))); } public HomuraHumanAscendant(final HomuraHumanAscendant card) { diff --git a/Mage.Sets/src/mage/cards/h/HoneyMammoth.java b/Mage.Sets/src/mage/cards/h/HoneyMammoth.java new file mode 100644 index 0000000000..53421d92d0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HoneyMammoth.java @@ -0,0 +1,37 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HoneyMammoth extends CardImpl { + + public HoneyMammoth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.ELEPHANT); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // When Honey Mammoth enters the battlefield, you gain 4 life. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(4))); + } + + private HoneyMammoth(final HoneyMammoth card) { + super(card); + } + + @Override + public HoneyMammoth copy() { + return new HoneyMammoth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HoodedBlightfang.java b/Mage.Sets/src/mage/cards/h/HoodedBlightfang.java new file mode 100644 index 0000000000..f3eb71472d --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HoodedBlightfang.java @@ -0,0 +1,58 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; +import mage.abilities.common.DestroyPlaneswalkerWhenDamagedTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class HoodedBlightfang extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a creature you control with deathtouch"); + + static { + filter.add(new AbilityPredicate(DeathtouchAbility.class)); + } + + public HoodedBlightfang(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.SNAKE); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever a creature you control with deathtouch attacks, each opponent loses 1 life and you gain 1 life. + Ability ability = new AttacksCreatureYouControlTriggeredAbility(new LoseLifeOpponentsEffect(1), false, filter); + ability.addEffect(new GainLifeEffect(1).setText("and you gain 1 life")); + this.addAbility(ability); + + // Whenever a creature you control with deathtouch deals damage to a planeswalker, destroy that planeswalker. + this.addAbility(new DestroyPlaneswalkerWhenDamagedTriggeredAbility(filter)); + } + + private HoodedBlightfang(final HoodedBlightfang card) { + super(card); + } + + @Override + public HoodedBlightfang copy() { + return new HoodedBlightfang(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HoodedHydra.java b/Mage.Sets/src/mage/cards/h/HoodedHydra.java index 379d8270aa..38c59f42d3 100644 --- a/Mage.Sets/src/mage/cards/h/HoodedHydra.java +++ b/Mage.Sets/src/mage/cards/h/HoodedHydra.java @@ -4,7 +4,7 @@ package mage.cards.h; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -41,7 +41,7 @@ public final class HoodedHydra extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); // When Hooded Hydra dies, create a 1/1 green Snake creature token for each +1/+1 counter on it. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SnakeToken("KTK"), new CountersSourceCount(CounterType.P1P1)), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SnakeToken("KTK"), new CountersSourceCount(CounterType.P1P1)), false)); // Morph {3}{G}{G} this.addAbility(new MorphAbility(this, new ManaCostsImpl("{3}{G}{G}"))); diff --git a/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java b/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java index 5a2246cb10..d2b560305d 100644 --- a/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java +++ b/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java @@ -1,7 +1,7 @@ package mage.cards.h; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -35,7 +35,7 @@ public final class HorizonSpellbomb extends CardImpl { ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); // When Horizon Spellbomb is put into a graveyard from the battlefield, you may pay {G}. If you do, draw a card. - this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{G}")))); + this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{G}")))); } public HorizonSpellbomb(final HorizonSpellbomb card) { diff --git a/Mage.Sets/src/mage/cards/h/HornbashMentor.java b/Mage.Sets/src/mage/cards/h/HornbashMentor.java new file mode 100644 index 0000000000..a59c438f72 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HornbashMentor.java @@ -0,0 +1,71 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HornbashMentor extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("non-Human creature you control"); + private static final FilterPermanent filter2 + = new FilterControlledCreaturePermanent("creature you control with trample"); + + static { + filter.add(Predicates.not(SubType.HUMAN.getPredicate())); + filter2.add(new AbilityPredicate(TrampleAbility.class)); + } + + public HornbashMentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Hornbash Mentor enters the battlefield, put a trample counter on target non-Human creature you control. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.TRAMPLE.createInstance()) + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {2}{G}, {T}: Put a +1/+1 counter on each creature you control with trample. + ability = new SimpleActivatedAbility( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter2), new ManaCostsImpl("{2}{G}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private HornbashMentor(final HornbashMentor card) { + super(card); + } + + @Override + public HornbashMentor copy() { + return new HornbashMentor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HornetHarasser.java b/Mage.Sets/src/mage/cards/h/HornetHarasser.java index b6aa781c47..7cd503d8af 100644 --- a/Mage.Sets/src/mage/cards/h/HornetHarasser.java +++ b/Mage.Sets/src/mage/cards/h/HornetHarasser.java @@ -4,7 +4,7 @@ package mage.cards.h; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class HornetHarasser extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), false); + Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HoundOfGriselbrand.java b/Mage.Sets/src/mage/cards/h/HoundOfGriselbrand.java index 938d482164..50a3b66a2a 100644 --- a/Mage.Sets/src/mage/cards/h/HoundOfGriselbrand.java +++ b/Mage.Sets/src/mage/cards/h/HoundOfGriselbrand.java @@ -19,7 +19,7 @@ public final class HoundOfGriselbrand extends CardImpl { public HoundOfGriselbrand(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); this.subtype.add(SubType.ELEMENTAL); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java b/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java index 9206adf00f..3af2670f03 100644 --- a/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java +++ b/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java @@ -28,7 +28,7 @@ public final class HoundOfTheFarbogs extends CardImpl { public HoundOfTheFarbogs(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); this.subtype.add(SubType.ZOMBIE); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(5); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/h/HourOfGlory.java b/Mage.Sets/src/mage/cards/h/HourOfGlory.java index 5be4f80dcb..4f4dfb2737 100644 --- a/Mage.Sets/src/mage/cards/h/HourOfGlory.java +++ b/Mage.Sets/src/mage/cards/h/HourOfGlory.java @@ -69,7 +69,7 @@ class HourOfGloryEffect extends OneShotEffect { if (targetCreature != null) { controller.moveCards(targetCreature, Zone.EXILED, source, game); if (targetCreature.hasSubtype(SubType.GOD, game)) { - game.applyEffects(); + game.getState().processAction(game); Player targetController = game.getPlayer(targetCreature.getControllerId()); if (targetController != null) { targetController.revealCards(sourceObject.getIdName(), targetController.getHand(), game); diff --git a/Mage.Sets/src/mage/cards/h/HourOfNeed.java b/Mage.Sets/src/mage/cards/h/HourOfNeed.java index f5b9f2142f..89baaebb51 100644 --- a/Mage.Sets/src/mage/cards/h/HourOfNeed.java +++ b/Mage.Sets/src/mage/cards/h/HourOfNeed.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.OneShotEffect; @@ -13,13 +11,13 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.HourOfNeedSphinxToken; -import mage.game.permanent.token.TokenImpl; import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HourOfNeed extends CardImpl { @@ -29,6 +27,7 @@ public final class HourOfNeed extends CardImpl { // Strive — Hour of Need costs {1}{U} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{1}{U}")); + // Exile any number of target creatures. For each creature exiled this way, its controller creates a 4/4 blue Sphinx creature token with flying. this.getSpellAbility().addEffect(new HourOfNeedExileEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE)); diff --git a/Mage.Sets/src/mage/cards/h/HourOfRevelation.java b/Mage.Sets/src/mage/cards/h/HourOfRevelation.java index 15eb45648a..8ba5adb763 100644 --- a/Mage.Sets/src/mage/cards/h/HourOfRevelation.java +++ b/Mage.Sets/src/mage/cards/h/HourOfRevelation.java @@ -1,11 +1,11 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -13,8 +13,9 @@ import mage.constants.ComparisonType; import mage.constants.Zone; import mage.filter.common.FilterNonlandPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class HourOfRevelation extends CardImpl { @@ -23,10 +24,12 @@ public final class HourOfRevelation extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}{W}"); // Hour of Revelation costs {3} less to cast if there are ten or more nonland permanents on the battlefield. - SimpleStaticAbility ability = new SimpleStaticAbility(Zone.STACK, + SimpleStaticAbility ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(3, new PermanentsOnTheBattlefieldCondition( - new FilterNonlandPermanent("there are ten or more nonland permanents on the battlefield"), ComparisonType.MORE_THAN, 9, false))); + new FilterNonlandPermanent("there are ten or more nonland permanents on the battlefield"), + ComparisonType.MORE_THAN, 9, false))); ability.setRuleAtTheTop(true); + ability.addHint(new ValueHint("Nonland permanents on the battlefield", new PermanentsOnBattlefieldCount(new FilterNonlandPermanent()))); this.addAbility(ability); // Destroy all nonland permanents. diff --git a/Mage.Sets/src/mage/cards/h/HowlOfTheHorde.java b/Mage.Sets/src/mage/cards/h/HowlOfTheHorde.java index 4349c1f79c..a7f4d7ed63 100644 --- a/Mage.Sets/src/mage/cards/h/HowlOfTheHorde.java +++ b/Mage.Sets/src/mage/cards/h/HowlOfTheHorde.java @@ -1,15 +1,15 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; import mage.game.Game; @@ -18,24 +18,31 @@ import mage.game.stack.Spell; import mage.target.targetpointer.FixedTarget; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author emerald000 */ public final class HowlOfTheHorde extends CardImpl { public HowlOfTheHorde(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); // When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy. Effect effect = new CreateDelayedTriggeredAbilityEffect(new HowlOfTheHordeDelayedTriggeredAbility()); effect.setText("When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy."); this.getSpellAbility().addEffect(effect); - + // Raid — If you attacked with a creature this turn, when you cast your next instant or sorcery spell this turn, copy that spell an additional time. You may choose new targets for the copy. - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new CreateDelayedTriggeredAbilityEffect(new HowlOfTheHordeDelayedTriggeredAbility()), RaidCondition.instance, "

Raid — If you attacked with a creature this turn, when you cast your next instant or sorcery spell this turn, copy that spell an additional time. You may choose new targets for the copy.")); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new CreateDelayedTriggeredAbilityEffect(new HowlOfTheHordeDelayedTriggeredAbility()), + RaidCondition.instance, + "

Raid — If you attacked with a creature this turn, when you cast your next instant or sorcery spell this turn, copy that spell an additional time. You may choose new targets for the copy.") + ); this.getSpellAbility().addWatcher(new PlayerAttackedWatcher()); + this.getSpellAbility().setAbilityWord(AbilityWord.RAID); + this.getSpellAbility().addHint(RaidHint.instance); } public HowlOfTheHorde(final HowlOfTheHorde card) { @@ -49,7 +56,7 @@ public final class HowlOfTheHorde extends CardImpl { } class HowlOfTheHordeDelayedTriggeredAbility extends DelayedTriggeredAbility { - + HowlOfTheHordeDelayedTriggeredAbility() { super(new CopyTargetSpellEffect(true), Duration.EndOfTurn); } @@ -70,11 +77,11 @@ class HowlOfTheHordeDelayedTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(this.getControllerId())) { + if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null && (spell.isInstant() || spell.isSorcery())) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId())); } return true; } diff --git a/Mage.Sets/src/mage/cards/h/HuatliDinosaurKnight.java b/Mage.Sets/src/mage/cards/h/HuatliDinosaurKnight.java index 5ecde9b0b2..6129e2ed0a 100644 --- a/Mage.Sets/src/mage/cards/h/HuatliDinosaurKnight.java +++ b/Mage.Sets/src/mage/cards/h/HuatliDinosaurKnight.java @@ -10,6 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -21,14 +22,12 @@ import java.util.UUID; public final class HuatliDinosaurKnight extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Dinosaur you control"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature you don't control"); - private static final FilterCreaturePermanent filter3 = new FilterCreaturePermanent("Dinosaurs"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("Dinosaurs"); static { filter.add(SubType.DINOSAUR.getPredicate()); filter.add(TargetController.YOU.getControllerPredicate()); - filter2.add(TargetController.NOT_YOU.getControllerPredicate()); - filter3.add(SubType.DINOSAUR.getPredicate()); + filter2.add(SubType.DINOSAUR.getPredicate()); } public HuatliDinosaurKnight(UUID ownerId, CardSetInfo setInfo) { @@ -49,14 +48,14 @@ public final class HuatliDinosaurKnight extends CardImpl { // -3: Target Dinosaur you control deals damage equal to its power to target creature you don't control. ability = new LoyaltyAbility(new DamageWithPowerFromOneToAnotherTargetEffect(), -3); ability.addTarget(new TargetCreaturePermanent(filter)); - ability.addTarget(new TargetCreaturePermanent(filter2)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); // -7: Dinosaurs you control get +4/+4 until end of turn. - this.addAbility(new LoyaltyAbility(new BoostControlledEffect(4, 4, Duration.EndOfTurn, filter3), -7)); + this.addAbility(new LoyaltyAbility(new BoostControlledEffect(4, 4, Duration.EndOfTurn, filter2), -7)); } - public HuatliDinosaurKnight(final HuatliDinosaurKnight card) { + private HuatliDinosaurKnight(final HuatliDinosaurKnight card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java b/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java index 3eafa3ed17..b462ecff10 100644 --- a/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java +++ b/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java @@ -1,25 +1,20 @@ - package mage.cards.h; -import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterArtifactPermanent; import mage.game.Game; import mage.util.CardUtil; +import java.util.UUID; +import mage.cards.Card; + /** - * * @author Pete Rossi */ public final class HumOfTheRadix extends CardImpl { @@ -56,18 +51,16 @@ class HumOfTheRadixCostIncreaseEffect extends CostModificationEffectImpl { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { int additionalCost = game.getBattlefield().getAllActivePermanents(new FilterArtifactPermanent(), abilityToModify.getControllerId(), game).size(); - if (additionalCost > 0) { - CardUtil.increaseCost(abilityToModify, additionalCost); - } + CardUtil.increaseCost(abilityToModify, additionalCost); return true; } @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify instanceof SpellAbility) { - MageObject sourceObject = abilityToModify.getSourceObject(game); - if (sourceObject != null && sourceObject.isArtifact()) { - return true; + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + if (spellCard != null) { + return !spellCard.isArtifact(); } } return false; diff --git a/Mage.Sets/src/mage/cards/h/HumbleDefector.java b/Mage.Sets/src/mage/cards/h/HumbleDefector.java index 7ea21d0641..89488a7966 100644 --- a/Mage.Sets/src/mage/cards/h/HumbleDefector.java +++ b/Mage.Sets/src/mage/cards/h/HumbleDefector.java @@ -69,7 +69,7 @@ class HumbleDefectorEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(2, game); + controller.drawCards(2, source.getSourceId(), game); } Permanent humbleDefector = (Permanent) source.getSourceObjectIfItStillExists(game); Player targetOpponent = game.getPlayer(getTargetPointer().getFirst(game, source)); diff --git a/Mage.Sets/src/mage/cards/h/HumbleNaturalist.java b/Mage.Sets/src/mage/cards/h/HumbleNaturalist.java new file mode 100644 index 0000000000..5f424d65e5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HumbleNaturalist.java @@ -0,0 +1,52 @@ +package mage.cards.h; + +import mage.ConditionalMana; +import mage.MageInt; +import mage.abilities.mana.ConditionalAnyColorManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.abilities.mana.conditional.CreatureCastConditionalMana; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HumbleNaturalist extends CardImpl { + + public HumbleNaturalist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {T}: Add one mana of any color. Spend this mana only to cast a creature spell. + this.addAbility(new ConditionalAnyColorManaAbility(1, new HumbleNaturalistManaBuilder())); + } + + private HumbleNaturalist(final HumbleNaturalist card) { + super(card); + } + + @Override + public HumbleNaturalist copy() { + return new HumbleNaturalist(this); + } +} + +class HumbleNaturalistManaBuilder extends ConditionalManaBuilder { + @Override + public ConditionalMana build(Object... options) { + return new CreatureCastConditionalMana(this.mana); + } + + @Override + public String getRule() { + return "Spend this mana only to cast a creature spell"; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HungerOfTheNim.java b/Mage.Sets/src/mage/cards/h/HungerOfTheNim.java index 657891ee47..28dfd0b529 100644 --- a/Mage.Sets/src/mage/cards/h/HungerOfTheNim.java +++ b/Mage.Sets/src/mage/cards/h/HungerOfTheNim.java @@ -1,38 +1,31 @@ - package mage.cards.h; -import java.util.UUID; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class HungerOfTheNim extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifact you control"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } - public HungerOfTheNim(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Target creature gets +1/+0 until end of turn for each artifact you control. - Effect effect = new BoostTargetEffect(new PermanentsOnBattlefieldCount(filter), StaticValue.get(0), Duration.EndOfTurn, true); - effect.setText("Target creature gets +1/+0 until end of turn for each artifact you control"); + Effect effect = new BoostTargetEffect(ArtifactYouControlCount.instance, StaticValue.get(0), Duration.EndOfTurn, true); getSpellAbility().addEffect(effect); getSpellAbility().addTarget(new TargetCreaturePermanent()); + getSpellAbility().addHint(ArtifactYouControlHint.instance); } public HungerOfTheNim(final HungerOfTheNim card) { diff --git a/Mage.Sets/src/mage/cards/h/HungeringYeti.java b/Mage.Sets/src/mage/cards/h/HungeringYeti.java index 05fdd7fe58..3e8c5bef04 100644 --- a/Mage.Sets/src/mage/cards/h/HungeringYeti.java +++ b/Mage.Sets/src/mage/cards/h/HungeringYeti.java @@ -1,48 +1,50 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalAsThoughEffect; -import mage.abilities.effects.AsThoughEffect; import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HungeringYeti extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("as long as you control a green or blue permanent"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent(); static { - filter.add(Predicates.or(new ColorPredicate(ObjectColor.GREEN), new ColorPredicate(ObjectColor.BLUE))); + filter.add(Predicates.or( + new ColorPredicate(ObjectColor.GREEN), + new ColorPredicate(ObjectColor.BLUE) + )); } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + public HungeringYeti(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); this.subtype.add(SubType.YETI); this.power = new MageInt(4); this.toughness = new MageInt(4); // As long as you control a green or blue permanent, you may cast Hungering Yeti as though it had flash. - AsThoughEffect effect = new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame); - effect.setText("As long as you control a green or blue permanent, you may cast {this} as though it had flash"); - this.addAbility(new SimpleStaticAbility(Zone.ALL, new ConditionalAsThoughEffect(effect, - new PermanentsOnTheBattlefieldCondition(filter)))); - + this.addAbility(new SimpleStaticAbility(Zone.ALL, new ConditionalAsThoughEffect( + new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame), condition + ).setText("as long as you control a green or blue permanent, you may cast {this} as though it had flash"))); } public HungeringYeti(final HungeringYeti card) { diff --git a/Mage.Sets/src/mage/cards/h/HuntTheWeak.java b/Mage.Sets/src/mage/cards/h/HuntTheWeak.java index 6337355f78..626e3c49f4 100644 --- a/Mage.Sets/src/mage/cards/h/HuntTheWeak.java +++ b/Mage.Sets/src/mage/cards/h/HuntTheWeak.java @@ -1,32 +1,24 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.FightTargetsEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HuntTheWeak extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public HuntTheWeak(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); @@ -37,12 +29,11 @@ public final class HuntTheWeak extends CardImpl { effect.setText("Then that creature fights target creature you don't control"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - Target target = new TargetCreaturePermanent(filter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL); this.getSpellAbility().addTarget(target); - } - public HuntTheWeak(final HuntTheWeak card) { + private HuntTheWeak(final HuntTheWeak card) { super(card); } @@ -50,5 +41,4 @@ public final class HuntTheWeak extends CardImpl { public HuntTheWeak copy() { return new HuntTheWeak(this); } - } diff --git a/Mage.Sets/src/mage/cards/h/HuntedNightmare.java b/Mage.Sets/src/mage/cards/h/HuntedNightmare.java new file mode 100644 index 0000000000..22c2ffb871 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HuntedNightmare.java @@ -0,0 +1,85 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HuntedNightmare extends CardImpl { + + public HuntedNightmare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); + + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Menace + this.addAbility(new MenaceAbility()); + + // When Hunted Nightmare enters the battlefield, target opponent puts a deathtouch counter on a creature they control. + Ability ability = new EntersBattlefieldTriggeredAbility(new HuntedNightmareEffect()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private HuntedNightmare(final HuntedNightmare card) { + super(card); + } + + @Override + public HuntedNightmare copy() { + return new HuntedNightmare(this); + } +} + +class HuntedNightmareEffect extends OneShotEffect { + + HuntedNightmareEffect() { + super(Outcome.Benefit); + staticText = "target opponent puts a deathtouch counter on a creature they control"; + } + + private HuntedNightmareEffect(final HuntedNightmareEffect effect) { + super(effect); + } + + @Override + public HuntedNightmareEffect copy() { + return new HuntedNightmareEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null || game.getBattlefield().countAll( + StaticFilters.FILTER_PERMANENT_CREATURE, player.getId(), game + ) == 0) { + return false; + } + Target target = new TargetControlledCreaturePermanent(); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + return permanent != null && permanent.addCounters(CounterType.DEATHTOUCH.createInstance(), source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HuntedWitness.java b/Mage.Sets/src/mage/cards/h/HuntedWitness.java index 33c60a3177..436b98c9b3 100644 --- a/Mage.Sets/src/mage/cards/h/HuntedWitness.java +++ b/Mage.Sets/src/mage/cards/h/HuntedWitness.java @@ -2,7 +2,7 @@ package mage.cards.h; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.constants.SubType; import mage.cards.CardImpl; @@ -24,7 +24,7 @@ public final class HuntedWitness extends CardImpl { this.toughness = new MageInt(1); // When Hunted Witness dies, create a 1/1 white Soldier creature token with lifelink. - this.addAbility(new DiesTriggeredAbility( + this.addAbility(new DiesSourceTriggeredAbility( new CreateTokenEffect(new SoldierLifelinkToken()) )); } diff --git a/Mage.Sets/src/mage/cards/h/HunterOfEyeblights.java b/Mage.Sets/src/mage/cards/h/HunterOfEyeblights.java index 994d1ea533..7e7c3bba92 100644 --- a/Mage.Sets/src/mage/cards/h/HunterOfEyeblights.java +++ b/Mage.Sets/src/mage/cards/h/HunterOfEyeblights.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -14,30 +12,27 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.CounterAnyPredicate; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Styxo */ public final class HunterOfEyeblights extends CardImpl { - private static final FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature you don't control"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature with a counter on it"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with a counter on it"); static { - filter1.add(TargetController.NOT_YOU.getControllerPredicate()); - filter2.add(CounterAnyPredicate.instance); - + filter.add(CounterAnyPredicate.instance); } public HunterOfEyeblights(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.ASSASSIN); this.power = new MageInt(3); @@ -45,17 +40,17 @@ public final class HunterOfEyeblights extends CardImpl { // When Hunter of Eyeblights enters the battlefield, put a +1/+1 counter on target creature you don't control Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); - ability.addTarget(new TargetCreaturePermanent(filter1)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); //{B}{2},{T}: Destroy target creature with a counter on it. - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{2}{B}")); + Ability ability2 = new SimpleActivatedAbility(new DestroyTargetEffect(), new ManaCostsImpl("{2}{B}")); ability2.addCost(new TapSourceCost()); - ability2.addTarget(new TargetCreaturePermanent(filter2)); + ability2.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability2); } - public HunterOfEyeblights(final HunterOfEyeblights card) { + private HunterOfEyeblights(final HunterOfEyeblights card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/h/HuntersEdge.java b/Mage.Sets/src/mage/cards/h/HuntersEdge.java new file mode 100644 index 0000000000..919602bf5c --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HuntersEdge.java @@ -0,0 +1,38 @@ +package mage.cards.h; + +import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HuntersEdge extends CardImpl { + + public HuntersEdge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); + + // Put a +1/+1 counter on target creature you control. Then that creature deals damage equal to its power to target creature you don't control. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("that creature").concatBy("Then")); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + } + + private HuntersEdge(final HuntersEdge card) { + super(card); + } + + @Override + public HuntersEdge copy() { + return new HuntersEdge(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HuntersFeast.java b/Mage.Sets/src/mage/cards/h/HuntersFeast.java index 8da0721896..24b87153c9 100644 --- a/Mage.Sets/src/mage/cards/h/HuntersFeast.java +++ b/Mage.Sets/src/mage/cards/h/HuntersFeast.java @@ -1,5 +1,3 @@ - - package mage.cards.h; import java.util.UUID; @@ -16,8 +14,9 @@ import mage.target.TargetPlayer; public final class HuntersFeast extends CardImpl { public HuntersFeast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); + // Any number of target players each gain 6 life. this.getSpellAbility().addTarget(new TargetPlayer(0, Integer.MAX_VALUE, false)); this.getSpellAbility().addEffect(new GainLifeTargetEffect(6)); } diff --git a/Mage.Sets/src/mage/cards/h/HuntersInsight.java b/Mage.Sets/src/mage/cards/h/HuntersInsight.java index 04e7044898..d2dbeda1de 100644 --- a/Mage.Sets/src/mage/cards/h/HuntersInsight.java +++ b/Mage.Sets/src/mage/cards/h/HuntersInsight.java @@ -1,37 +1,38 @@ - package mage.cards.h; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.Outcome; import mage.game.Game; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class HuntersInsight extends CardImpl { public HuntersInsight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); // Choose target creature you control. Whenever that creature deals combat damage to a player or planeswalker this turn, draw that many cards. - this.getSpellAbility().addEffect(new GainAbilityTargetEffect(new HuntersInsightTriggeredAbility(), Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new HuntersInsightEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); } - public HuntersInsight(final HuntersInsight card) { + private HuntersInsight(final HuntersInsight card) { super(card); } @@ -41,14 +42,46 @@ public final class HuntersInsight extends CardImpl { } } -class HuntersInsightTriggeredAbility extends TriggeredAbilityImpl { +class HuntersInsightEffect extends OneShotEffect { - public HuntersInsightTriggeredAbility() { - super(Zone.BATTLEFIELD, null, false); + HuntersInsightEffect() { + super(Outcome.Benefit); + staticText = "Choose target creature you control. Whenever that creature deals combat damage " + + "to a player or planeswalker this turn, draw that many cards."; } - public HuntersInsightTriggeredAbility(final HuntersInsightTriggeredAbility ability) { + private HuntersInsightEffect(final HuntersInsightEffect effect) { + super(effect); + } + + @Override + public HuntersInsightEffect copy() { + return new HuntersInsightEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + game.addDelayedTriggeredAbility(new HuntersInsightTriggeredAbility(new MageObjectReference(permanent, game)), source); + return true; + } +} + +class HuntersInsightTriggeredAbility extends DelayedTriggeredAbility { + + private final MageObjectReference mor; + + HuntersInsightTriggeredAbility(MageObjectReference mor) { + super(null, Duration.EndOfTurn, false, false); + this.mor = mor; + } + + private HuntersInsightTriggeredAbility(final HuntersInsightTriggeredAbility ability) { super(ability); + this.mor = ability.mor; } @Override @@ -58,17 +91,19 @@ class HuntersInsightTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DAMAGED_PLAYER || event.getType() == EventType.DAMAGED_PLANESWALKER; + return event.getType() == EventType.DAMAGED_PLAYER + || event.getType() == EventType.DAMAGED_PLANESWALKER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.sourceId) && ((DamagedEvent) event).isCombatDamage()) { - this.getEffects().clear(); - this.addEffect(new DrawCardSourceControllerEffect(event.getAmount())); - return true; + if (!mor.refersTo(event.getSourceId(), game) + || !((DamagedEvent) event).isCombatDamage()) { + return false; } - return false; + this.getEffects().clear(); + this.addEffect(new DrawCardSourceControllerEffect(event.getAmount())); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/h/HuntersProwess.java b/Mage.Sets/src/mage/cards/h/HuntersProwess.java index 68f7e96a0e..8fcbe40d41 100644 --- a/Mage.Sets/src/mage/cards/h/HuntersProwess.java +++ b/Mage.Sets/src/mage/cards/h/HuntersProwess.java @@ -73,7 +73,7 @@ class HuntersProwessDrawEffect extends OneShotEffect { if (controller != null) { int damage = (Integer) this.getValue("damage"); if (damage > 0) { - controller.drawCards(damage, game); + controller.drawCards(damage, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/h/HuntmasterLiger.java b/Mage.Sets/src/mage/cards/h/HuntmasterLiger.java new file mode 100644 index 0000000000..3524529dd9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HuntmasterLiger.java @@ -0,0 +1,48 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.dynamicvalue.common.SourceMutatedCount; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HuntmasterLiger extends CardImpl { + + public HuntmasterLiger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Mutate {2}{W} + this.addAbility(new MutateAbility(this, "{2}{W}")); + + // Whenever this creature mutates, other creatures you control get +X/+X until end of turn, where X is the number of times this creature has mutated. + this.addAbility(new MutatesSourceTriggeredAbility(new BoostControlledEffect( + SourceMutatedCount.instance, SourceMutatedCount.instance, Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE, true, true + ))); + } + + private HuntmasterLiger(final HuntmasterLiger card) { + super(card); + } + + @Override + public HuntmasterLiger copy() { + return new HuntmasterLiger(this); + } +} +// it's like a lion and a tiger mixed... bred for its skills in magic diff --git a/Mage.Sets/src/mage/cards/h/HurloonShaman.java b/Mage.Sets/src/mage/cards/h/HurloonShaman.java index c14aee9699..0347659da2 100644 --- a/Mage.Sets/src/mage/cards/h/HurloonShaman.java +++ b/Mage.Sets/src/mage/cards/h/HurloonShaman.java @@ -3,7 +3,7 @@ package mage.cards.h; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class HurloonShaman extends CardImpl { this.toughness = new MageInt(3); // When Hurloon Shaman dies, each player sacrifices a land. - this.addAbility(new DiesTriggeredAbility(new SacrificeAllEffect(new FilterControlledLandPermanent("land")))); + this.addAbility(new DiesSourceTriggeredAbility(new SacrificeAllEffect(new FilterControlledLandPermanent("land")))); } public HurloonShaman(final HurloonShaman card) { diff --git a/Mage.Sets/src/mage/cards/h/Hydradoodle.java b/Mage.Sets/src/mage/cards/h/Hydradoodle.java index 75fc85bff8..9ab48ab269 100644 --- a/Mage.Sets/src/mage/cards/h/Hydradoodle.java +++ b/Mage.Sets/src/mage/cards/h/Hydradoodle.java @@ -32,7 +32,7 @@ public final class Hydradoodle extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{X}{G}{G}"); this.subtype.add(SubType.HYDRA); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(0); this.toughness = new MageInt(0); diff --git a/Mage.Sets/src/mage/cards/h/HydroidKrasis.java b/Mage.Sets/src/mage/cards/h/HydroidKrasis.java index 85012dac51..41e5cc9507 100644 --- a/Mage.Sets/src/mage/cards/h/HydroidKrasis.java +++ b/Mage.Sets/src/mage/cards/h/HydroidKrasis.java @@ -86,7 +86,7 @@ class HydroidKrasisEffect extends OneShotEffect { return false; } int halfCost = Math.floorDiv(((SpellAbility) obj).getManaCostsToPay().getX(), 2); - player.drawCards(halfCost, game); + player.drawCards(halfCost, source.getSourceId(), game); player.gainLife(halfCost, game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/h/Hypnox.java b/Mage.Sets/src/mage/cards/h/Hypnox.java index 41e0be205e..a9fd985565 100644 --- a/Mage.Sets/src/mage/cards/h/Hypnox.java +++ b/Mage.Sets/src/mage/cards/h/Hypnox.java @@ -7,7 +7,7 @@ import mage.abilities.Ability; import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; @@ -43,7 +43,7 @@ public final class Hypnox extends CardImpl { // When Hypnox enters the battlefield, if you cast it from your hand, exile all cards from target opponent's hand. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new HypnoxExileEffect()); ability.addTarget(new TargetOpponent()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourceCondition.instance, + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, exile all cards from target opponent's hand."), new CastFromHandWatcher()); // When Hypnox leaves the battlefield, return the exiled cards to their owner's hand. diff --git a/Mage.Sets/src/mage/cards/h/Hypothesizzle.java b/Mage.Sets/src/mage/cards/h/Hypothesizzle.java index f866ac8f02..82681c07c5 100644 --- a/Mage.Sets/src/mage/cards/h/Hypothesizzle.java +++ b/Mage.Sets/src/mage/cards/h/Hypothesizzle.java @@ -1,26 +1,19 @@ package mage.cards.h; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class Hypothesizzle extends CardImpl { @@ -30,15 +23,19 @@ public final class Hypothesizzle extends CardImpl { // Draw two cards. Then you may discard a nonland card. When you do, Hypothesizzle deals 4 damage to target creature. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); - this.getSpellAbility().addEffect(new DoIfCostPaid( - new HypothesizzleCreateReflexiveTriggerEffect(), - new DiscardCardCost(StaticFilters.FILTER_CARD_A_NON_LAND), - "Discard a nonland card to deal 4 damage?" - ).setText("Then you may discard a nonland card. " - + "When you do, {this} deals 4 damage to target creature.")); + + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(4), false, + "{this} deals 4 damage to target creature" + ); + ability.addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new DoWhenCostPaid( + ability, new DiscardCardCost(StaticFilters.FILTER_CARD_A_NON_LAND), + "Discard a nonland card?" + ).concatBy("Then")); } - public Hypothesizzle(final Hypothesizzle card) { + private Hypothesizzle(final Hypothesizzle card) { super(card); } @@ -47,60 +44,3 @@ public final class Hypothesizzle extends CardImpl { return new Hypothesizzle(this); } } - -class HypothesizzleCreateReflexiveTriggerEffect extends OneShotEffect { - - public HypothesizzleCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - this.staticText = "When you do, it deals 4 damage to target creature"; - } - - public HypothesizzleCreateReflexiveTriggerEffect(final HypothesizzleCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public HypothesizzleCreateReflexiveTriggerEffect copy() { - return new HypothesizzleCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new HypothesizzleReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class HypothesizzleReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - public HypothesizzleReflexiveTriggeredAbility() { - super(new DamageTargetEffect(4), Duration.OneUse, true); - this.addTarget(new TargetCreaturePermanent()); - } - - public HypothesizzleReflexiveTriggeredAbility(final HypothesizzleReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public HypothesizzleReflexiveTriggeredAbility copy() { - return new HypothesizzleReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you discard a nonland card, " - + "{this} deals 4 damage to target creature"; - } -} diff --git a/Mage.Sets/src/mage/cards/i/IceCauldron.java b/Mage.Sets/src/mage/cards/i/IceCauldron.java index c1d8b404e2..a7f69f4657 100644 --- a/Mage.Sets/src/mage/cards/i/IceCauldron.java +++ b/Mage.Sets/src/mage/cards/i/IceCauldron.java @@ -193,7 +193,7 @@ class IceCauldronAddManaEffect extends ManaEffect { IceCauldronAddManaEffect(IceCauldronAddManaEffect effect) { super(effect); - storedMana = effect.storedMana.copy(); + storedMana = effect.storedMana == null ? null : effect.storedMana.copy(); exiledCardMor = effect.exiledCardMor; } diff --git a/Mage.Sets/src/mage/cards/i/Iceberg.java b/Mage.Sets/src/mage/cards/i/Iceberg.java index 98fd771829..87e904940f 100644 --- a/Mage.Sets/src/mage/cards/i/Iceberg.java +++ b/Mage.Sets/src/mage/cards/i/Iceberg.java @@ -7,6 +7,7 @@ import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.SimpleManaAbility; @@ -32,7 +33,9 @@ public final class Iceberg extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.ICE.createInstance(1)), new ManaCostsImpl("{3}"))); // Remove an ice counter from Iceberg: Add {C}. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1), new RemoveCountersSourceCost(CounterType.ICE.createInstance(1)))); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1), + new RemoveCountersSourceCost(CounterType.ICE.createInstance(1)), + new CountersSourceCount(CounterType.ICE))); } public Iceberg(final Iceberg card) { diff --git a/Mage.Sets/src/mage/cards/i/IcebergCancrix.java b/Mage.Sets/src/mage/cards/i/IcebergCancrix.java index c0a59ad7b7..0f2cb53c50 100644 --- a/Mage.Sets/src/mage/cards/i/IcebergCancrix.java +++ b/Mage.Sets/src/mage/cards/i/IcebergCancrix.java @@ -40,7 +40,7 @@ public final class IcebergCancrix extends CardImpl { Ability ability = new EntersBattlefieldControlledTriggeredAbility( Zone.BATTLEFIELD, new PutLibraryIntoGraveTargetEffect(2), filter, true, "Whenever another snow permanent enters the battlefield under your control, " + - "you may have target player put the top two cards of their library into their graveyard." + "you may have target player mill two cards." ); ability.addTarget(new TargetPlayer()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/i/IcefallRegent.java b/Mage.Sets/src/mage/cards/i/IcefallRegent.java index 71e5d8e267..582882559a 100644 --- a/Mage.Sets/src/mage/cards/i/IcefallRegent.java +++ b/Mage.Sets/src/mage/cards/i/IcefallRegent.java @@ -2,17 +2,16 @@ package mage.cards.i; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.SpellAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.TapTargetEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterCard; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; @@ -20,7 +19,6 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.UUID; @@ -53,8 +51,9 @@ public final class IcefallRegent extends CardImpl { this.addAbility(ability, new IcefallRegentWatcher()); // Spells your opponents cast that target Icefall Regent cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IcefallRegentCostIncreaseEffect())); - + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT)) + ); } public IcefallRegent(final IcefallRegent card) { @@ -157,48 +156,3 @@ class IcefallRegentWatcher extends Watcher { //don't reset condition each turn - only when this leaves the battlefield } } - -class IcefallRegentCostIncreaseEffect extends CostModificationEffectImpl { - - - IcefallRegentCostIncreaseEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Spells your opponents cast that target {this} cost {2} more to cast"; - } - - private IcefallRegentCostIncreaseEffect(IcefallRegentCostIncreaseEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - if (targetUUID.equals(source.getSourceId())) { - return true; - } - } - } - } - } - } - return false; - } - - @Override - public IcefallRegentCostIncreaseEffect copy() { - return new IcefallRegentCostIncreaseEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/i/IchorclawMyr.java b/Mage.Sets/src/mage/cards/i/IchorclawMyr.java index 51e0308bf3..388a5e989e 100644 --- a/Mage.Sets/src/mage/cards/i/IchorclawMyr.java +++ b/Mage.Sets/src/mage/cards/i/IchorclawMyr.java @@ -3,7 +3,7 @@ package mage.cards.i; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class IchorclawMyr extends CardImpl { this.addAbility(InfectAbility.getInstance()); // Whenever Ichorclaw Myr becomes blocked, it gets +2/+2 until end of turn. - this.addAbility(new BecomesBlockedTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false)); } public IchorclawMyr(final IchorclawMyr card) { diff --git a/Mage.Sets/src/mage/cards/i/IconOfAncestry.java b/Mage.Sets/src/mage/cards/i/IconOfAncestry.java index a04801cda9..a65420d527 100644 --- a/Mage.Sets/src/mage/cards/i/IconOfAncestry.java +++ b/Mage.Sets/src/mage/cards/i/IconOfAncestry.java @@ -1,12 +1,15 @@ package mage.cards.i; -import java.util.Set; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.ChooseCreatureTypeEffect; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -14,16 +17,7 @@ import mage.constants.*; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ChosenSubtypePredicate; - -import java.util.UUID; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; /** * @author TheElk801 @@ -49,9 +43,10 @@ public final class IconOfAncestry extends CardImpl { new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filter, false) )); - // {3}, {T}: Look at the top three cards of your library. You may reveal a creature card of the + // {3}, {T}: Look at the top three cards of your library. You may reveal a creature card of the // chosen type from among them and put it into your hand. Put the rest on the bottom of your library in a random order. - Ability ability = new SimpleActivatedAbility(new IconOfAncestryEffect(), new ManaCostsImpl("{3}")); + FilterCreatureCard filter2 = new FilterCreatureCard("creature card that matches the chosen subtype"); + Ability ability = new SimpleActivatedAbility(new IconOfAncestryEffect(filter2), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } @@ -66,14 +61,25 @@ public final class IconOfAncestry extends CardImpl { } } -class IconOfAncestryEffect extends OneShotEffect { +class IconOfAncestryEffect extends LookLibraryAndPickControllerEffect { - public IconOfAncestryEffect() { - super(Outcome.AIDontUseIt); - this.staticText = "Look at the top three cards of your library. " - + "You may reveal a creature card of the " - + "chosen type from among them and put it into your hand. " - + "Put the rest on the bottom of your library in a random order"; + public IconOfAncestryEffect(FilterCreatureCard filter) { + super(StaticValue.get(3), false, StaticValue.get(1), filter, Zone.LIBRARY, false, + true, false, Zone.HAND, true, false, false); + this.setOutcome(Outcome.AIDontUseIt); + this.setBackInRandomOrder(true); + } + + @Override + public boolean apply(Game game, Ability source) { + SubType subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type"); + if (subtype != null) { + filter.add(subtype.getPredicate()); + filter.setMessage("creature card of subtype " + subtype.toString()); + } else { + return false; + } + return super.apply(game, source); } public IconOfAncestryEffect(final IconOfAncestryEffect effect) { @@ -84,39 +90,4 @@ class IconOfAncestryEffect extends OneShotEffect { public IconOfAncestryEffect copy() { return new IconOfAncestryEffect(this); } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null - || !controller.getLibrary().hasCards()) { - return false; - } - Set cardsFromTopOfLibrary = controller.getLibrary().getTopCards(game, 3); - Cards cardsFromLibrary = new CardsImpl(); - Cards revealedCard = new CardsImpl(); - cardsFromLibrary.addAll(cardsFromTopOfLibrary); - if (cardsFromTopOfLibrary.isEmpty()) { - return false; - } - FilterCreatureCard filter = new FilterCreatureCard("creature card that matches the chosen subtype"); - SubType subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type"); - if (subtype != null) { - filter.add(subtype.getPredicate()); - } - TargetCard target = new TargetCard(Zone.LIBRARY, filter); - if (target.canChoose(controller.getId(), game) - && controller.chooseUse(outcome, "Do you wish to use Icon of Ancestry's effect?", source, game) - && controller.choose(Outcome.Benefit, cardsFromLibrary, target, game)) { - Card chosenCard = game.getCard(target.getFirstTarget()); - if (chosenCard != null) { - revealedCard.add(chosenCard); - controller.revealCards(source, revealedCard, game); - controller.putInHand(chosenCard, game); - cardsFromLibrary.remove(chosenCard); - } - } - controller.putCardsOnBottomOfLibrary(cardsFromLibrary, game, source, true); - return true; - } } diff --git a/Mage.Sets/src/mage/cards/i/IdentityThief.java b/Mage.Sets/src/mage/cards/i/IdentityThief.java index 79cb1bd4f8..f50ea256cd 100644 --- a/Mage.Sets/src/mage/cards/i/IdentityThief.java +++ b/Mage.Sets/src/mage/cards/i/IdentityThief.java @@ -108,7 +108,7 @@ class IdentityThiefEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetPermanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent targetPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (controller != null diff --git a/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java b/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java new file mode 100644 index 0000000000..7c6b09e686 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java @@ -0,0 +1,280 @@ +package mage.cards.i; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.watchers.Watcher; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class IdolOfEndurance extends CardImpl { + + public IdolOfEndurance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}"); + + // When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new IdolOfEnduranceExileEffect())); + + // {1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without paying its mana cost. + Ability ability = new SimpleActivatedAbility( + new IdolOfEnduranceCastFromExileEffect(), new ManaCostsImpl<>("{1}{W}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability, new IdolOfEnduranceWatcher()); + } + + private IdolOfEndurance(final IdolOfEndurance card) { + super(card); + } + + @Override + public IdolOfEndurance copy() { + return new IdolOfEndurance(this); + } +} + +class IdolOfEnduranceExileEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCreatureCard(); + + static { + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + } + + IdolOfEnduranceExileEffect() { + super(Outcome.Benefit); + staticText = "exile all creature cards with converted mana cost 3 or less from your graveyard until {this} leaves the battlefield"; + } + + private IdolOfEnduranceExileEffect(final IdolOfEnduranceExileEffect effect) { + super(effect); + } + + @Override + public IdolOfEnduranceExileEffect copy() { + return new IdolOfEnduranceExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || permanent == null) { + return false; + } + Cards cards = new CardsImpl(player.getGraveyard().getCards(filter, game)); + MageObjectReference mor = new MageObjectReference(permanent, game); + player.moveCards(cards, Zone.EXILED, source, game); + Set morSet = cards + .getCards(game) + .stream() + .filter(Objects::nonNull) + .map(card -> new MageObjectReference(card, game)) + .collect(Collectors.toSet()); + String exileId = "idolOfEndurance_" + mor.getSourceId() + mor.getZoneChangeCounter(); + if (game.getState().getValue(exileId) == null) { + game.getState().setValue(exileId, new HashSet()); + } + ((Set) game.getState().getValue(exileId)).addAll(morSet); + game.addDelayedTriggeredAbility(new IdolOfEnduranceDelayedTrigger(exileId), source); + return true; + } +} + +class IdolOfEnduranceDelayedTrigger extends DelayedTriggeredAbility { + + IdolOfEnduranceDelayedTrigger(String exileId) { + super(new IdolOfEnduranceLeaveEffect(exileId), Duration.Custom, true, false); + this.usesStack = false; + this.setRuleVisible(false); + } + + private IdolOfEnduranceDelayedTrigger(final IdolOfEnduranceDelayedTrigger ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getTargetId().equals(this.getSourceId())) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getFromZone() == Zone.BATTLEFIELD) { + return true; + } + } + return false; + } + + @Override + public IdolOfEnduranceDelayedTrigger copy() { + return new IdolOfEnduranceDelayedTrigger(this); + } +} + +class IdolOfEnduranceLeaveEffect extends OneShotEffect { + + private final String exileId; + + IdolOfEnduranceLeaveEffect(String exileId) { + super(Outcome.Benefit); + this.exileId = exileId; + } + + private IdolOfEnduranceLeaveEffect(final IdolOfEnduranceLeaveEffect effect) { + super(effect); + this.exileId = effect.exileId; + } + + @Override + public IdolOfEnduranceLeaveEffect copy() { + return new IdolOfEnduranceLeaveEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Object object = game.getState().getValue(exileId); + if (!(object instanceof Set)) { + return false; + } + Set morSet = (Set) object; + return player != null && player.moveCards( + morSet.stream() + .map(mor -> mor.getCard(game)) + .filter(Objects::nonNull) + .collect(Collectors.toSet()), + Zone.GRAVEYARD, source, game + ); + } +} + +class IdolOfEnduranceCastFromExileEffect extends AsThoughEffectImpl { + + IdolOfEnduranceCastFromExileEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); + staticText = "until end of turn, you may cast a creature spell from among the cards exiled with {this} without paying its mana cost"; + } + + private IdolOfEnduranceCastFromExileEffect(final IdolOfEnduranceCastFromExileEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public IdolOfEnduranceCastFromExileEffect copy() { + return new IdolOfEnduranceCastFromExileEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + IdolOfEnduranceWatcher watcher = game.getState().getWatcher(IdolOfEnduranceWatcher.class); + if (watcher != null) { + watcher.addPlayable(source, game); + } + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + IdolOfEnduranceWatcher watcher = game.getState().getWatcher(IdolOfEnduranceWatcher.class); + if (watcher == null || !watcher.checkPermission(affectedControllerId, source, game)) { + return false; + } + Object value = game.getState().getValue( + "idolOfEndurance_" + source.getSourceId() + source.getSourceObjectZoneChangeCounter() + ); + if (!(value instanceof Set)) { + discard(); + return false; + } + Set morSet = (Set) value; + if (game.getState().getZone(sourceId) != Zone.EXILED + || morSet.stream().noneMatch(mor -> mor.refersTo(sourceId, game))) { + return false; + } + Card card = game.getCard(sourceId); + if (card == null || !card.isCreature() || card.isLand()) { + return false; + } + return allowCardToPlayWithoutMana(sourceId, source, affectedControllerId, game); + } +} + +class IdolOfEnduranceWatcher extends Watcher { + + private final Map> morMap = new HashMap<>(); + + IdolOfEnduranceWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.SPELL_CAST) { + if (event.getAdditionalReference() == null) { + return; + } + morMap.computeIfAbsent(event.getAdditionalReference(), m -> new HashMap<>()) + .compute(event.getPlayerId(), (u, i) -> i == null ? 0 : Integer.sum(i, -1)); + return; + } + } + + @Override + public void reset() { + morMap.clear(); + super.reset(); + } + + boolean checkPermission(UUID playerId, Ability source, Game game) { + if (!playerId.equals(source.getControllerId())) { + return false; + } + MageObjectReference mor = new MageObjectReference( + source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + ); + if (!morMap.containsKey(mor)) { + return false; + } + return morMap.get(mor).getOrDefault(playerId, 0) > 0; + } + + void addPlayable(Ability source, Game game) { + MageObjectReference mor = new MageObjectReference( + source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + ); + morMap.computeIfAbsent(mor, m -> new HashMap<>()) + .compute(source.getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IgneousCur.java b/Mage.Sets/src/mage/cards/i/IgneousCur.java new file mode 100644 index 0000000000..941413c9c4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IgneousCur.java @@ -0,0 +1,42 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IgneousCur extends CardImpl { + + public IgneousCur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.DOG); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {1}{R}: Igneous Cur gets +2/+0 until end of turn. + this.addAbility(new SimpleActivatedAbility( + new BoostSourceEffect(2, 0, Duration.EndOfTurn), new ManaCostsImpl("{1}{R}") + )); + } + + private IgneousCur(final IgneousCur card) { + super(card); + } + + @Override + public IgneousCur copy() { + return new IgneousCur(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IgneousElemental.java b/Mage.Sets/src/mage/cards/i/IgneousElemental.java index aba782659f..5d750cea5b 100644 --- a/Mage.Sets/src/mage/cards/i/IgneousElemental.java +++ b/Mage.Sets/src/mage/cards/i/IgneousElemental.java @@ -4,9 +4,11 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.CardsInControllerGraveCondition; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -30,10 +32,11 @@ public final class IgneousElemental extends CardImpl { this.toughness = new MageInt(3); // This spell costs {2} less to cast if there is a land card in your graveyard. - Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect( - 2, new CardsInControllerGraveCondition(1, StaticFilters.FILTER_CARD_LAND) - ).setText("This spell costs {2} less to cast if there is a land card in your graveyard.")); + Condition condition = new CardsInControllerGraveCondition(1, StaticFilters.FILTER_CARD_LAND); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, condition) + .setText("This spell costs {2} less to cast if there is a land card in your graveyard.")); ability.setRuleAtTheTop(true); + ability.addHint(new ConditionHint(condition, "There is a land card in your graveyard")); this.addAbility(ability); // When Igneous Elemental enters the battlefield, you may have it deal 2 damage to target creature. diff --git a/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java b/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java index 14cd2b90b2..926b3ca706 100644 --- a/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java +++ b/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java @@ -2,7 +2,6 @@ package mage.cards.i; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlashbackAbility; @@ -13,10 +12,11 @@ import mage.constants.*; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; import java.util.Set; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.target.targetpointer.FixedTargets; /** * @author TheElk801 @@ -69,81 +69,8 @@ class IgniteTheFutureEffect extends OneShotEffect { if (controller == null || spell == null) { return false; } - boolean forFree = spell.getFromZone() == Zone.GRAVEYARD; Set cards = controller.getLibrary().getTopCards(game, 3); - controller.moveCards(cards, Zone.EXILED, source, game); - - cards.stream().forEach(card -> { - ContinuousEffect effect = new IgniteTheFutureMayPlayEffect(forFree); - effect.setTargetPointer(new FixedTarget(card.getId(), game)); - game.addEffect(effect, source); - }); - - return true; - } -} - -class IgniteTheFutureMayPlayEffect extends AsThoughEffectImpl { - - private int castOnTurn = 0; - private final boolean forFree; - - IgniteTheFutureMayPlayEffect(boolean forFree) { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - this.forFree = forFree; - if (forFree) { - this.staticText = "Until the end of your next turn, you may play that card without playing its mana cost."; - } else { - this.staticText = "Until the end of your next turn, you may play that card."; - } - } - - private IgniteTheFutureMayPlayEffect(final IgniteTheFutureMayPlayEffect effect) { - super(effect); - castOnTurn = effect.castOnTurn; - this.forFree = effect.forFree; - } - - @Override - public IgniteTheFutureMayPlayEffect copy() { - return new IgniteTheFutureMayPlayEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - castOnTurn = game.getTurnNum(); - } - - @Override - public boolean isInactive(Ability source, Game game) { - return castOnTurn != game.getTurnNum() - && game.getPhase().getStep().getType() == PhaseStep.END_TURN - && game.isActivePlayer(source.getControllerId()); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!source.isControlledBy(affectedControllerId) - || !getTargetPointer().getTargets(game, source).contains(objectId)) { - return false; - } - if (!forFree) { - return true; - } - Card card = game.getCard(objectId); - if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) { - return true; - } - Player player = game.getPlayer(affectedControllerId); - if (player != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - } - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, cards, + TargetController.YOU, Duration.UntilEndOfYourNextTurn, Zone.GRAVEYARD.equals(spell.getFromZone())); } } diff --git a/Mage.Sets/src/mage/cards/i/IgnobleSoldier.java b/Mage.Sets/src/mage/cards/i/IgnobleSoldier.java index 26e90533fb..859bc1abaf 100644 --- a/Mage.Sets/src/mage/cards/i/IgnobleSoldier.java +++ b/Mage.Sets/src/mage/cards/i/IgnobleSoldier.java @@ -3,7 +3,7 @@ package mage.cards.i; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.PreventCombatDamageBySourceEffect; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class IgnobleSoldier extends CardImpl { // Whenever Ignoble Soldier becomes blocked, prevent all combat damage that would be dealt by it this turn. Effect effect = new PreventCombatDamageBySourceEffect(Duration.EndOfTurn); effect.setText("prevent all combat damage that would be dealt by it this turn"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public IgnobleSoldier(final IgnobleSoldier card) { diff --git a/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java b/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java index b17883fd8a..25a75accab 100644 --- a/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java +++ b/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java @@ -95,7 +95,7 @@ class IlhargTheRazeBoarEffect extends OneShotEffect { } game.getCombat().addAttackingCreature(permanent.getId(), game); Effect effect = new ReturnToHandTargetEffect(); - effect.setText("return {this} to its owner's hand"); + effect.setText("return " + permanent.getName() + " to its owner's hand"); effect.setTargetPointer(new FixedTarget(permanent, game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); return true; diff --git a/Mage.Sets/src/mage/cards/i/IllunaApexOfWishes.java b/Mage.Sets/src/mage/cards/i/IllunaApexOfWishes.java new file mode 100644 index 0000000000..8251d17684 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IllunaApexOfWishes.java @@ -0,0 +1,98 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MutateAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.*; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IllunaApexOfWishes extends CardImpl { + + public IllunaApexOfWishes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.BEAST); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Mutate {3}{R/G}{U}{U} + this.addAbility(new MutateAbility(this, "{3}{R/G}{U}{U}")); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever this creature mutates, exile cards from the top of your library until you exile a nonland permanent card. Put that card onto the battlefield or into your hand. + this.addAbility(new MutatesSourceTriggeredAbility(new IllunaApexOfWishesEffect())); + } + + private IllunaApexOfWishes(final IllunaApexOfWishes card) { + super(card); + } + + @Override + public IllunaApexOfWishes copy() { + return new IllunaApexOfWishes(this); + } +} + +class IllunaApexOfWishesEffect extends OneShotEffect { + + IllunaApexOfWishesEffect() { + super(Outcome.Benefit); + staticText = "exile cards from the top of your library until you exile a nonland permanent card. " + + "Put that card onto the battlefield or into your hand"; + } + + private IllunaApexOfWishesEffect(final IllunaApexOfWishesEffect effect) { + super(effect); + } + + @Override + public IllunaApexOfWishesEffect copy() { + return new IllunaApexOfWishesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card permCard = null; + Cards cards = new CardsImpl(); + for (Card card : player.getLibrary().getCards(game)) { + cards.add(card); + if (card == null || card.isLand() || !card.isPermanent()) { + continue; + } + permCard = card; + break; + } + player.moveCards(cards, Zone.EXILED, source, game); + if (permCard == null) { + return true; + } + Zone zone = player.chooseUse( + outcome, "Put " + permCard.getName() + " into your hand or onto the battlefield?", + "", "Hand", "Battlefield", source, game + ) ? Zone.HAND : Zone.BATTLEFIELD; + return player.moveCards(permCard, zone, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IllusionaryPresence.java b/Mage.Sets/src/mage/cards/i/IllusionaryPresence.java index b8adf4654f..cd3b943c7f 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionaryPresence.java +++ b/Mage.Sets/src/mage/cards/i/IllusionaryPresence.java @@ -1,111 +1,111 @@ -package mage.cards.i; - -import java.util.UUID; -import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.constants.SubType; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ChooseBasicLandTypeEffect; -import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.abilities.keyword.CumulativeUpkeepAbility; -import mage.abilities.keyword.ForestwalkAbility; -import mage.abilities.keyword.IslandwalkAbility; -import mage.abilities.keyword.MountainwalkAbility; -import mage.abilities.keyword.PlainswalkAbility; -import mage.abilities.keyword.SwampwalkAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.game.Game; - -/** - * - * @author jeffwadsworth - */ -public final class IllusionaryPresence extends CardImpl { - - public IllusionaryPresence(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); - - this.subtype.add(SubType.ILLUSION); - this.power = new MageInt(2); - this.toughness = new MageInt(2); - - // Cumulative upkeep {U} - this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl("{U}"))); - - // At the beginning of your upkeep, choose a land type. Illusionary Presence gains landwalk of the chosen type until end of turn. - Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new ChooseBasicLandTypeEffect(Outcome.Neutral), TargetController.YOU, false); - ability.addEffect(new IllusionaryPresenceEffect()); - this.addAbility(ability); - - } - - private IllusionaryPresence(final IllusionaryPresence card) { - super(card); - } - - @Override - public IllusionaryPresence copy() { - return new IllusionaryPresence(this); - } -} - -class IllusionaryPresenceEffect extends OneShotEffect { - - Ability gainedAbility; - - public IllusionaryPresenceEffect() { - super(Outcome.Benefit); - this.staticText = "{this} gains landwalk of the chosen type until end of turn"; - } - - public IllusionaryPresenceEffect(final IllusionaryPresenceEffect effect) { - super(effect); - } - - @Override - public IllusionaryPresenceEffect copy() { - return new IllusionaryPresenceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - MageObject mageObject = game.getObject(source.getSourceId()); - if (mageObject != null) { - SubType landTypeChoice = SubType.byDescription((String) game.getState().getValue(mageObject.getId().toString() + "BasicLandType")); - if (landTypeChoice != null) { - switch (landTypeChoice) { - case PLAINS: - gainedAbility = new PlainswalkAbility(); - break; - case FOREST: - gainedAbility = new ForestwalkAbility(); - break; - case SWAMP: - gainedAbility = new SwampwalkAbility(); - break; - case ISLAND: - gainedAbility = new IslandwalkAbility(); - break; - case MOUNTAIN: - gainedAbility = new MountainwalkAbility(); - break; - } - if (gainedAbility != null) { - GainAbilitySourceEffect effect = new GainAbilitySourceEffect(gainedAbility, Duration.EndOfTurn); - game.addEffect(effect, source); - return true; - } - } - } - return false; - } -} +package mage.cards.i; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.constants.SubType; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseBasicLandTypeEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.CumulativeUpkeepAbility; +import mage.abilities.keyword.ForestwalkAbility; +import mage.abilities.keyword.IslandwalkAbility; +import mage.abilities.keyword.MountainwalkAbility; +import mage.abilities.keyword.PlainswalkAbility; +import mage.abilities.keyword.SwampwalkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; + +/** + * + * @author jeffwadsworth + */ +public final class IllusionaryPresence extends CardImpl { + + public IllusionaryPresence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + + this.subtype.add(SubType.ILLUSION); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Cumulative upkeep {U} + this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl("{U}"))); + + // At the beginning of your upkeep, choose a land type. Illusionary Presence gains landwalk of the chosen type until end of turn. + Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new ChooseBasicLandTypeEffect(Outcome.Neutral), TargetController.YOU, false); + ability.addEffect(new IllusionaryPresenceEffect()); + this.addAbility(ability); + + } + + private IllusionaryPresence(final IllusionaryPresence card) { + super(card); + } + + @Override + public IllusionaryPresence copy() { + return new IllusionaryPresence(this); + } +} + +class IllusionaryPresenceEffect extends OneShotEffect { + + Ability gainedAbility; + + public IllusionaryPresenceEffect() { + super(Outcome.Benefit); + this.staticText = "{this} gains landwalk of the chosen type until end of turn"; + } + + public IllusionaryPresenceEffect(final IllusionaryPresenceEffect effect) { + super(effect); + } + + @Override + public IllusionaryPresenceEffect copy() { + return new IllusionaryPresenceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject mageObject = game.getObject(source.getSourceId()); + if (mageObject != null) { + SubType landTypeChoice = SubType.byDescription((String) game.getState().getValue(mageObject.getId().toString() + "BasicLandType")); + if (landTypeChoice != null) { + switch (landTypeChoice) { + case PLAINS: + gainedAbility = new PlainswalkAbility(); + break; + case FOREST: + gainedAbility = new ForestwalkAbility(); + break; + case SWAMP: + gainedAbility = new SwampwalkAbility(); + break; + case ISLAND: + gainedAbility = new IslandwalkAbility(); + break; + case MOUNTAIN: + gainedAbility = new MountainwalkAbility(); + break; + } + if (gainedAbility != null) { + GainAbilitySourceEffect effect = new GainAbilitySourceEffect(gainedAbility, Duration.EndOfTurn); + game.addEffect(effect, source); + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java b/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java index 4fe4f00784..5fd36bb4ea 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java +++ b/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java @@ -1,197 +1,197 @@ -package mage.cards.i; - -import java.util.List; -import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.common.AsEntersBattlefieldAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.keyword.CumulativeUpkeepAbility; -import mage.abilities.mana.BlackManaAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.abilities.mana.RedManaAbility; -import mage.abilities.mana.WhiteManaAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.choices.ChoiceBasicLandType; -import mage.choices.ChoiceImpl; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import static mage.constants.Layer.TypeChangingEffects_4; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.util.CardUtil; - -/** - * - * @author jeffwadsworth - */ -public final class IllusionaryTerrain extends CardImpl { - - public IllusionaryTerrain(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}{U}"); - - // Cumulative upkeep {2} - this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl("{2}"))); - - // As Illusionary Terrain enters the battlefield, choose two basic land types. - this.addAbility(new AsEntersBattlefieldAbility(new ChooseTwoBasicLandTypesEffect(Outcome.Neutral))); - - // Basic lands of the first chosen type are the second chosen type. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IllusionaryTerrainEffect())); - - } - - private IllusionaryTerrain(final IllusionaryTerrain card) { - super(card); - } - - @Override - public IllusionaryTerrain copy() { - return new IllusionaryTerrain(this); - } -} - -class IllusionaryTerrainEffect extends ContinuousEffectImpl { - - public IllusionaryTerrainEffect() { - super(Duration.WhileOnBattlefield, Outcome.Neutral); - staticText = "Basic lands of the first chosen type are the second chosen type"; - } - - public IllusionaryTerrainEffect(final IllusionaryTerrainEffect effect) { - super(effect); - } - - @Override - public IllusionaryTerrainEffect copy() { - return new IllusionaryTerrainEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Player controller = game.getPlayer(source.getControllerId()); - SubType firstChoice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "firstChoice")); - SubType secondChoice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "secondChoice")); - List lands = game.getBattlefield().getAllActivePermanents(CardType.LAND); - if (controller != null - && firstChoice != null - && secondChoice != null) { - for (Permanent land : lands) { - if (land.isBasic()) { - switch (layer) { - case TypeChangingEffects_4: - // the land mana ability is intrinsic, so add it here, not layer 6 - if (land.getSubtype(game).contains(firstChoice)) { - land.getSubtype(game).removeAll(SubType.getLandTypes()); - land.getSubtype(game).add(secondChoice); - land.removeAllAbilities(source.getSourceId(), game); - if (land.getSubtype(game).contains(SubType.FOREST)) { - this.dependencyTypes.add(DependencyType.BecomeForest); - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.PLAINS)) { - this.dependencyTypes.add(DependencyType.BecomePlains); - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { - this.dependencyTypes.add(DependencyType.BecomeMountain); - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.ISLAND)) { - this.dependencyTypes.add(DependencyType.BecomeIsland); - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.SWAMP)) { - this.dependencyTypes.add(DependencyType.BecomeSwamp); - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - } - break; - } - } - } - return true; - } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; - } -} - -class ChooseTwoBasicLandTypesEffect extends OneShotEffect { - - String choiceOne; - String choiceTwo; - - public ChooseTwoBasicLandTypesEffect(Outcome outcome) { - super(outcome); - this.staticText = "choose two basic land types"; - } - - public ChooseTwoBasicLandTypesEffect(final ChooseTwoBasicLandTypesEffect effect) { - super(effect); - } - - @Override - public ChooseTwoBasicLandTypesEffect copy() { - return new ChooseTwoBasicLandTypesEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject mageObject = game.getPermanentEntering(source.getSourceId()); - if (mageObject == null) { - mageObject = game.getObject(source.getSourceId()); - } - if (controller != null - && mageObject != null) { - ChoiceImpl choices = new ChoiceBasicLandType(); - if (controller.choose(Outcome.Neutral, choices, game)) { - game.informPlayers(mageObject.getName() - + ": First chosen basic land type is " + choices.getChoice()); - game.getState().setValue(mageObject.getId().toString() - + "firstChoice", choices.getChoice()); - choiceOne = SubType.byDescription((String) game.getState().getValue( - source.getSourceId().toString() + "firstChoice")).getDescription(); - } - if (controller.choose(Outcome.Neutral, choices, game)) { - game.informPlayers(mageObject.getName() - + ": Second chosen basic land type is " + choices.getChoice()); - game.getState().setValue(mageObject.getId().toString() - + "secondChoice", choices.getChoice()); - choiceTwo = SubType.byDescription((String) game.getState().getValue( - source.getSourceId().toString() + "secondChoice")).getDescription(); - if (mageObject instanceof Permanent - && choiceOne != null - && choiceTwo != null) { - ((Permanent) mageObject).addInfo("Chosen Types", CardUtil - .addToolTipMarkTags("First chosen basic land type: " + choiceOne - + "\n Second chosen basic land type: " + choiceTwo), game); - } - return true; - } - } - return false; - } -} +package mage.cards.i; + +import java.util.List; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CumulativeUpkeepAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.ChoiceBasicLandType; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.DependencyType; +import mage.constants.Duration; +import mage.constants.Layer; +import static mage.constants.Layer.TypeChangingEffects_4; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author jeffwadsworth + */ +public final class IllusionaryTerrain extends CardImpl { + + public IllusionaryTerrain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}{U}"); + + // Cumulative upkeep {2} + this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl("{2}"))); + + // As Illusionary Terrain enters the battlefield, choose two basic land types. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseTwoBasicLandTypesEffect(Outcome.Neutral))); + + // Basic lands of the first chosen type are the second chosen type. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IllusionaryTerrainEffect())); + + } + + private IllusionaryTerrain(final IllusionaryTerrain card) { + super(card); + } + + @Override + public IllusionaryTerrain copy() { + return new IllusionaryTerrain(this); + } +} + +class IllusionaryTerrainEffect extends ContinuousEffectImpl { + + public IllusionaryTerrainEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + staticText = "Basic lands of the first chosen type are the second chosen type"; + } + + public IllusionaryTerrainEffect(final IllusionaryTerrainEffect effect) { + super(effect); + } + + @Override + public IllusionaryTerrainEffect copy() { + return new IllusionaryTerrainEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + SubType firstChoice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "firstChoice")); + SubType secondChoice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "secondChoice")); + List lands = game.getBattlefield().getAllActivePermanents(CardType.LAND); + if (controller != null + && firstChoice != null + && secondChoice != null) { + for (Permanent land : lands) { + if (land.isBasic()) { + switch (layer) { + case TypeChangingEffects_4: + // the land mana ability is intrinsic, so add it here, not layer 6 + if (land.getSubtype(game).contains(firstChoice)) { + land.getSubtype(game).removeAll(SubType.getLandTypes()); + land.getSubtype(game).add(secondChoice); + land.removeAllAbilities(source.getSourceId(), game); + if (land.getSubtype(game).contains(SubType.FOREST)) { + this.dependencyTypes.add(DependencyType.BecomeForest); + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.PLAINS)) { + this.dependencyTypes.add(DependencyType.BecomePlains); + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { + this.dependencyTypes.add(DependencyType.BecomeMountain); + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.ISLAND)) { + this.dependencyTypes.add(DependencyType.BecomeIsland); + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.SWAMP)) { + this.dependencyTypes.add(DependencyType.BecomeSwamp); + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + } + } + break; + } + } + } + return true; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4; + } +} + +class ChooseTwoBasicLandTypesEffect extends OneShotEffect { + + String choiceOne; + String choiceTwo; + + public ChooseTwoBasicLandTypesEffect(Outcome outcome) { + super(outcome); + this.staticText = "choose two basic land types"; + } + + public ChooseTwoBasicLandTypesEffect(final ChooseTwoBasicLandTypesEffect effect) { + super(effect); + } + + @Override + public ChooseTwoBasicLandTypesEffect copy() { + return new ChooseTwoBasicLandTypesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (controller != null + && mageObject != null) { + ChoiceImpl choices = new ChoiceBasicLandType(); + if (controller.choose(Outcome.Neutral, choices, game)) { + game.informPlayers(mageObject.getName() + + ": First chosen basic land type is " + choices.getChoice()); + game.getState().setValue(mageObject.getId().toString() + + "firstChoice", choices.getChoice()); + choiceOne = SubType.byDescription((String) game.getState().getValue( + source.getSourceId().toString() + "firstChoice")).getDescription(); + } + if (controller.choose(Outcome.Neutral, choices, game)) { + game.informPlayers(mageObject.getName() + + ": Second chosen basic land type is " + choices.getChoice()); + game.getState().setValue(mageObject.getId().toString() + + "secondChoice", choices.getChoice()); + choiceTwo = SubType.byDescription((String) game.getState().getValue( + source.getSourceId().toString() + "secondChoice")).getDescription(); + if (mageObject instanceof Permanent + && choiceOne != null + && choiceTwo != null) { + ((Permanent) mageObject).addInfo("Chosen Types", CardUtil + .addToolTipMarkTags("First chosen basic land type: " + choiceOne + + "\n Second chosen basic land type: " + choiceTwo), game); + } + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java b/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java index 257ca55ff2..6b98993ed8 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java +++ b/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java @@ -1,7 +1,5 @@ - package mage.cards.i; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.mana.GenericManaCost; @@ -12,8 +10,8 @@ import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -22,18 +20,19 @@ import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class IllusionistsBracers extends CardImpl { public IllusionistsBracers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); this.subtype.add(SubType.EQUIPMENT); // Whenever an ability of equipped creature is activated, if it isn't a mana ability, copy that ability. You may choose new targets for the copy. - this.addAbility(new AbilityActivatedTriggeredAbility()); + this.addAbility(new IllusionistsBracersTriggeredAbility()); // Equip 3 this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(3))); @@ -49,19 +48,19 @@ public final class IllusionistsBracers extends CardImpl { } } -class AbilityActivatedTriggeredAbility extends TriggeredAbilityImpl { +class IllusionistsBracersTriggeredAbility extends TriggeredAbilityImpl { - AbilityActivatedTriggeredAbility() { + IllusionistsBracersTriggeredAbility() { super(Zone.BATTLEFIELD, new CopyActivatedAbilityEffect()); } - AbilityActivatedTriggeredAbility(final AbilityActivatedTriggeredAbility ability) { + IllusionistsBracersTriggeredAbility(final IllusionistsBracersTriggeredAbility ability) { super(ability); } @Override - public AbilityActivatedTriggeredAbility copy() { - return new AbilityActivatedTriggeredAbility(this); + public IllusionistsBracersTriggeredAbility copy() { + return new IllusionistsBracersTriggeredAbility(this); } @Override @@ -72,7 +71,7 @@ class AbilityActivatedTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent equipment = game.getPermanent(this.getSourceId()); - if (equipment != null && equipment.isAttachedTo(event.getSourceId())) { + if (equipment != null && equipment.isAttachedTo(event.getSourceId())) { StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); if (!(stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl)) { Effect effect = this.getEffects().get(0); diff --git a/Mage.Sets/src/mage/cards/i/IllusionistsStratagem.java b/Mage.Sets/src/mage/cards/i/IllusionistsStratagem.java index cac0578a60..0e5247f736 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionistsStratagem.java +++ b/Mage.Sets/src/mage/cards/i/IllusionistsStratagem.java @@ -21,7 +21,7 @@ public final class IllusionistsStratagem extends CardImpl { // Exile up to two target creatures you control, then return those cards to the battlefield under their owner's control. this.getSpellAbility().addEffect(new ExileTargetForSourceEffect()); - this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(true) + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(false) .withReturnNames("those cards", "their owner's").concatBy(", then")); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, 2, new FilterControlledCreaturePermanent("creatures you control"), false)); diff --git a/Mage.Sets/src/mage/cards/i/IllusoryAmbusher.java b/Mage.Sets/src/mage/cards/i/IllusoryAmbusher.java index f53dc19472..1b6e2b315a 100644 --- a/Mage.Sets/src/mage/cards/i/IllusoryAmbusher.java +++ b/Mage.Sets/src/mage/cards/i/IllusoryAmbusher.java @@ -67,7 +67,7 @@ class IllusoryAmbusherDealtDamageEffect extends OneShotEffect { if (player != null) { int amount = (Integer) getValue("damage"); if (amount > 0) { - player.drawCards(amount, game); + player.drawCards(amount, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/i/ImmolatingGyre.java b/Mage.Sets/src/mage/cards/i/ImmolatingGyre.java new file mode 100644 index 0000000000..eaccc74c36 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImmolatingGyre.java @@ -0,0 +1,46 @@ +package mage.cards.i; + +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.DamageAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ImmolatingGyre extends CardImpl { + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY); + private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent(); + + static { + filter.add(TargetController.NOT_YOU.getControllerPredicate()); + } + + public ImmolatingGyre(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}"); + + // Immolating Gyre deals X damage to each creature and planeswalker you don't control, where X is the number of instant and sorcery cards in your graveyard. + this.getSpellAbility().addEffect(new DamageAllEffect(xValue, filter).setText( + "{this} deals X damage to each creature and planeswalker you don't control, " + + "where X is the number of instant and sorcery cards in your graveyard" + )); + } + + private ImmolatingGyre(final ImmolatingGyre card) { + super(card); + } + + @Override + public ImmolatingGyre copy() { + return new ImmolatingGyre(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java b/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java index bbeb302609..02445eb06b 100644 --- a/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java +++ b/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java @@ -22,7 +22,7 @@ public final class ImmolatingSouleater extends CardImpl { public ImmolatingSouleater(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/i/ImmortalPhoenix.java b/Mage.Sets/src/mage/cards/i/ImmortalPhoenix.java index e43b33376c..076ea534f2 100644 --- a/Mage.Sets/src/mage/cards/i/ImmortalPhoenix.java +++ b/Mage.Sets/src/mage/cards/i/ImmortalPhoenix.java @@ -1,7 +1,7 @@ package mage.cards.i; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class ImmortalPhoenix extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Immortal Phoenix dies, return it to its owner’s hand. - this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect())); } public ImmortalPhoenix(final ImmortalPhoenix card) { diff --git a/Mage.Sets/src/mage/cards/i/ImpelledGiant.java b/Mage.Sets/src/mage/cards/i/ImpelledGiant.java index 0baf4fff2c..d32858d470 100644 --- a/Mage.Sets/src/mage/cards/i/ImpelledGiant.java +++ b/Mage.Sets/src/mage/cards/i/ImpelledGiant.java @@ -1,7 +1,5 @@ - package mage.cards.i; -import java.util.List; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; @@ -16,9 +14,9 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; @@ -45,7 +43,7 @@ public final class ImpelledGiant extends CardImpl { } public ImpelledGiant(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); this.subtype.add(SubType.GIANT); this.subtype.add(SubType.WARRIOR); @@ -87,13 +85,14 @@ class ImpelledGiantCost extends CostImpl { @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { if (target.choose(Outcome.Tap, controllerId, sourceId, game)) { - for (UUID targetId: target.getTargets()) { + for (UUID targetId : target.getTargets()) { Permanent permanent = game.getPermanent(targetId); - if (permanent == null) + if (permanent == null) { return false; + } paid |= permanent.tap(game); for (Effect effect : ability.getEffects()) { - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); } } } @@ -110,10 +109,8 @@ class ImpelledGiantCost extends CostImpl { return new ImpelledGiantCost(this); } - } - class ImpelledGiantBoostEffect extends OneShotEffect { public ImpelledGiantBoostEffect() { @@ -128,7 +125,7 @@ class ImpelledGiantBoostEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent impelledGiant = game.getPermanent(source.getSourceId()); - Permanent tappedCreature = game.getPermanentOrLKIBattlefield(this.targetPointer.getFirst(game, source)); + Permanent tappedCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (tappedCreature != null && impelledGiant != null) { int amount = tappedCreature.getPower().getValue(); game.addEffect(new BoostSourceEffect(amount, 0, Duration.EndOfTurn), source); diff --git a/Mage.Sets/src/mage/cards/i/ImposingVantasaur.java b/Mage.Sets/src/mage/cards/i/ImposingVantasaur.java new file mode 100644 index 0000000000..159d1d3120 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImposingVantasaur.java @@ -0,0 +1,41 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ImposingVantasaur extends CardImpl { + + public ImposingVantasaur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}"); + + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(3); + this.toughness = new MageInt(6); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Cycling {1} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}"))); + } + + private ImposingVantasaur(final ImposingVantasaur card) { + super(card); + } + + @Override + public ImposingVantasaur copy() { + return new ImposingVantasaur(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/Imprison.java b/Mage.Sets/src/mage/cards/i/Imprison.java index 7f1b8c062b..b507ab20c4 100644 --- a/Mage.Sets/src/mage/cards/i/Imprison.java +++ b/Mage.Sets/src/mage/cards/i/Imprison.java @@ -141,7 +141,7 @@ class ImprisonUnblockEffect extends OneShotEffect { // Remove it from combat Effect effect = new RemoveFromCombatTargetEffect(); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); // Make blocked creatures unblocked diff --git a/Mage.Sets/src/mage/cards/i/ImpulsiveWager.java b/Mage.Sets/src/mage/cards/i/ImpulsiveWager.java index fbb277555a..7c2cf13d44 100644 --- a/Mage.Sets/src/mage/cards/i/ImpulsiveWager.java +++ b/Mage.Sets/src/mage/cards/i/ImpulsiveWager.java @@ -68,7 +68,7 @@ class ImpulsiveWagerEffect extends OneShotEffect { effect.setTargetPointer(getTargetPointer()); effect.apply(game, source); } else { - player.drawCards(2, game); + player.drawCards(2, source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java b/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java index 05917740bf..7e84b40142 100644 --- a/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java +++ b/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java @@ -110,7 +110,7 @@ class InallaArchmageRitualistEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = ((FixedTarget) getTargetPointer()).getTargetedPermanentOrLKIBattlefield(game); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true); effect.setTargetPointer(getTargetPointer()); diff --git a/Mage.Sets/src/mage/cards/i/InameAsOne.java b/Mage.Sets/src/mage/cards/i/InameAsOne.java index ba36e99e3c..9d81ec065f 100644 --- a/Mage.Sets/src/mage/cards/i/InameAsOne.java +++ b/Mage.Sets/src/mage/cards/i/InameAsOne.java @@ -5,9 +5,9 @@ import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -51,12 +51,12 @@ public final class InameAsOne extends CardImpl { // When Iname as One enters the battlefield, if you cast it from your hand, you may search your library for a Spirit permanent card, put it onto the battlefield, then shuffle your library. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, filter)), true), - CastFromHandSourceCondition.instance, + CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, you may search your library for a Spirit permanent card, put it onto the battlefield, then shuffle your library."), new CastFromHandWatcher()); // When Iname as One dies, you may exile it. If you do, return target Spirit permanent card from your graveyard to the battlefield. - Ability ability = new DiesTriggeredAbility(new InameAsOneEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new InameAsOneEffect(), false); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/InameLifeAspect.java b/Mage.Sets/src/mage/cards/i/InameLifeAspect.java index 91489ac7b0..4ba6cc586d 100644 --- a/Mage.Sets/src/mage/cards/i/InameLifeAspect.java +++ b/Mage.Sets/src/mage/cards/i/InameLifeAspect.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSourceEffect; @@ -42,7 +42,7 @@ public final class InameLifeAspect extends CardImpl { this.toughness = new MageInt(4); // When Iname, Life Aspect dies, you may exile it. If you do, return any number of target Spirit cards from your graveyard to your hand. - Ability ability = new DiesTriggeredAbility(new InameLifeAspectEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new InameLifeAspectEffect(), false); ability.addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/Incendiary.java b/Mage.Sets/src/mage/cards/i/Incendiary.java index f5987a4793..7c6e3149fe 100644 --- a/Mage.Sets/src/mage/cards/i/Incendiary.java +++ b/Mage.Sets/src/mage/cards/i/Incendiary.java @@ -1,65 +1,65 @@ -package mage.cards.i; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.DiesAttachedTriggeredAbility; -import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.target.common.TargetCreatureOrPlayer; - -/** - * - * @author jeffwadsworth - */ -public final class Incendiary extends CardImpl { - - private static final String rule = "{this} deals X damage to any target, where X is the number of fuse counters on {this}."; - - public Incendiary(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // At the beginning of your upkeep, you may put a fuse counter on Incendiary. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.FUSE.createInstance(), true), TargetController.YOU, true)); - - // When enchanted creature dies, Incendiary deals X damage to any target, where X is the number of fuse counters on Incendiary. - Effect effect = new DamageTargetEffect(new CountersSourceCount(CounterType.FUSE)).setText(rule); - Ability ability2 = new DiesAttachedTriggeredAbility(effect, "enchanted creature"); - ability.addTarget(new TargetCreatureOrPlayer()); - this.addAbility(ability2); - - } - - public Incendiary(final Incendiary card) { - super(card); - } - - @Override - public Incendiary copy() { - return new Incendiary(this); - } -} +package mage.cards.i; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DiesAttachedTriggeredAbility; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author jeffwadsworth + */ +public final class Incendiary extends CardImpl { + + private static final String rule = "{this} deals X damage to any target, where X is the number of fuse counters on {this}."; + + public Incendiary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of your upkeep, you may put a fuse counter on Incendiary. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.FUSE.createInstance(), true), TargetController.YOU, true)); + + // When enchanted creature dies, Incendiary deals X damage to any target, where X is the number of fuse counters on Incendiary. + Effect effect = new DamageTargetEffect(new CountersSourceCount(CounterType.FUSE)).setText(rule); + Ability ability2 = new DiesAttachedTriggeredAbility(effect, "enchanted creature"); + ability.addTarget(new TargetCreatureOrPlayer()); + this.addAbility(ability2); + + } + + public Incendiary(final Incendiary card) { + super(card); + } + + @Override + public Incendiary copy() { + return new Incendiary(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IncendiaryCommand.java b/Mage.Sets/src/mage/cards/i/IncendiaryCommand.java index 6fc1a4aaa8..d5f4a165a0 100644 --- a/Mage.Sets/src/mage/cards/i/IncendiaryCommand.java +++ b/Mage.Sets/src/mage/cards/i/IncendiaryCommand.java @@ -95,7 +95,7 @@ class IncendiaryCommandDrawEffect extends OneShotEffect { for (Map.Entry toDrawByPlayer : cardsToDraw.entrySet()) { Player player = game.getPlayer(toDrawByPlayer.getKey()); if (player != null) { - player.drawCards(toDrawByPlayer.getValue(), game); + player.drawCards(toDrawByPlayer.getValue(), source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/i/IncreasingConfusion.java b/Mage.Sets/src/mage/cards/i/IncreasingConfusion.java index 909f4ec7c0..3e9ca55f5f 100644 --- a/Mage.Sets/src/mage/cards/i/IncreasingConfusion.java +++ b/Mage.Sets/src/mage/cards/i/IncreasingConfusion.java @@ -46,7 +46,7 @@ class IncreasingConfusionEffect extends OneShotEffect { public IncreasingConfusionEffect() { super(Outcome.Detriment); - staticText = "Target player puts the top X cards of their library into their graveyard. If this spell was cast from a graveyard, that player puts twice that many cards into their graveyard instead"; + staticText = "Target player mills X cards. If this spell was cast from a graveyard, that player mills twice that many cards"; } public IncreasingConfusionEffect(final IncreasingConfusionEffect effect) { @@ -63,7 +63,7 @@ class IncreasingConfusionEffect extends OneShotEffect { if (spell.getFromZone() == Zone.GRAVEYARD) { amount *= 2; } - player.moveCards(player.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game); + player.millCards(amount, source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/i/IncreasingVengeance.java b/Mage.Sets/src/mage/cards/i/IncreasingVengeance.java index 9c740c9f27..3ad6985b68 100644 --- a/Mage.Sets/src/mage/cards/i/IncreasingVengeance.java +++ b/Mage.Sets/src/mage/cards/i/IncreasingVengeance.java @@ -1,7 +1,6 @@ package mage.cards.i; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; @@ -18,8 +17,9 @@ import mage.players.Player; import mage.target.Target; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class IncreasingVengeance extends CardImpl { @@ -34,7 +34,7 @@ public final class IncreasingVengeance extends CardImpl { } public IncreasingVengeance(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{R}"); // Copy target instant or sorcery spell you control. If this spell was cast from a graveyard, copy that spell twice instead. You may choose new targets for the copies. this.getSpellAbility().addEffect(new IncreasingVengeanceEffect()); @@ -69,26 +69,23 @@ class IncreasingVengeanceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); - if (spell != null) { - StackObject stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true); - if (stackObjectCopy instanceof Spell) { - game.informPlayers(controller.getLogName() + ((Spell) stackObjectCopy).getActivatedMessage(game)); - } - Spell sourceSpell = (Spell) game.getStack().getStackObject(source.getSourceId()); - if (sourceSpell != null) { - if (sourceSpell.getFromZone() == Zone.GRAVEYARD) { - stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true); - if (stackObjectCopy instanceof Spell) { - game.informPlayers(new StringBuilder(controller.getLogName()).append(((Spell) stackObjectCopy).getActivatedMessage(game)).toString()); - } - } - } - return true; - } + if (controller == null) { + return false; } - return false; + Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); + if (spell == null) { + return false; + } + Spell sourceSpell = (Spell) game.getSpell(source.getSourceId()); + int copies = 1; + if (sourceSpell != null && sourceSpell.getFromZone() == Zone.GRAVEYARD) { + copies++; + } + StackObject stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true, copies); + if (stackObjectCopy instanceof Spell) { + game.informPlayers(controller.getLogName() + ((Spell) stackObjectCopy).getActivatedMessage(game)); + } + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/i/IncrementalGrowth.java b/Mage.Sets/src/mage/cards/i/IncrementalGrowth.java index 8f0342ec46..c0feb2db66 100644 --- a/Mage.Sets/src/mage/cards/i/IncrementalGrowth.java +++ b/Mage.Sets/src/mage/cards/i/IncrementalGrowth.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -23,23 +22,23 @@ import mage.target.common.TargetCreaturePermanent; public final class IncrementalGrowth extends CardImpl { public IncrementalGrowth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}{G}"); - - // Put a +1/+1 counter on target creature, two +1/+1 counters on another target creature, and three +1/+1 counters on a third target creature. + // Put a +1/+1 counter on target creature, two +1/+1 counters on another target + // creature, and three +1/+1 counters on a third target creature. this.getSpellAbility().addEffect(new IncrementalGrowthEffect()); - + FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature (gets a +1/+1 counter)"); TargetCreaturePermanent target1 = new TargetCreaturePermanent(filter1); target1.setTargetTag(1); this.getSpellAbility().addTarget(target1); - + FilterCreaturePermanent filter2 = new FilterCreaturePermanent("another creature (gets two +1/+1 counter)"); filter2.add(new AnotherTargetPredicate(2)); TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter2); target2.setTargetTag(2); this.getSpellAbility().addTarget(target2); - + FilterCreaturePermanent filter3 = new FilterCreaturePermanent("another creature (gets three +1/+1 counters)"); filter3.add(new AnotherTargetPredicate(3)); TargetCreaturePermanent target3 = new TargetCreaturePermanent(filter3); @@ -60,8 +59,10 @@ public final class IncrementalGrowth extends CardImpl { class IncrementalGrowthEffect extends OneShotEffect { public IncrementalGrowthEffect() { - super(Outcome.UnboostCreature); - this.staticText = "Put a +1/+1 counter on target creature, two +1/+1 counters on another target creature, and three +1/+1 counters on a third target creature"; + super(Outcome.Benefit); + this.staticText = "Put a +1/+1 counter on target creature, " + + "two +1/+1 counters on another target creature, " + + "and three +1/+1 counters on a third target creature"; } public IncrementalGrowthEffect(final IncrementalGrowthEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/IncubationIncongruity.java b/Mage.Sets/src/mage/cards/i/IncubationIncongruity.java index 17fa9b0850..18b1348157 100644 --- a/Mage.Sets/src/mage/cards/i/IncubationIncongruity.java +++ b/Mage.Sets/src/mage/cards/i/IncubationIncongruity.java @@ -74,7 +74,7 @@ class IncongruityEffect extends OneShotEffect { // If the target creature is an illegal target by the time Incongruity tries to resolve, the spell doesn’t resolve. // No player creates a Frog Lizard token. // (2019-01-25) - Permanent permanent = game.getPermanentOrLKIBattlefield(targetPointer.getFirst(game, source)); // must use LKI + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); // must use LKI if (permanent != null) { FrogLizardToken token = new FrogLizardToken(); token.putOntoBattlefield(1, game, source.getSourceId(), permanent.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/i/IndathaCrystal.java b/Mage.Sets/src/mage/cards/i/IndathaCrystal.java new file mode 100644 index 0000000000..7a5857ff15 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IndathaCrystal.java @@ -0,0 +1,39 @@ +package mage.cards.i; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IndathaCrystal extends CardImpl { + + public IndathaCrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add {W}, {B}, or {G}. + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlackManaAbility()); + this.addAbility(new GreenManaAbility()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private IndathaCrystal(final IndathaCrystal card) { + super(card); + } + + @Override + public IndathaCrystal copy() { + return new IndathaCrystal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IndathaTriome.java b/Mage.Sets/src/mage/cards/i/IndathaTriome.java new file mode 100644 index 0000000000..22d2d1572f --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IndathaTriome.java @@ -0,0 +1,48 @@ +package mage.cards.i; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IndathaTriome extends CardImpl { + + public IndathaTriome(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.PLAINS); + this.subtype.add(SubType.SWAMP); + this.subtype.add(SubType.FOREST); + + // ({T}: Add {W}, {B}, or {G}.) + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlackManaAbility()); + this.addAbility(new GreenManaAbility()); + + // Indatha Triome enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Cycling {3} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"))); + } + + private IndathaTriome(final IndathaTriome card) { + super(card); + } + + @Override + public IndathaTriome copy() { + return new IndathaTriome(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IndomitableArchangel.java b/Mage.Sets/src/mage/cards/i/IndomitableArchangel.java index f0282360f9..fb63a57c27 100644 --- a/Mage.Sets/src/mage/cards/i/IndomitableArchangel.java +++ b/Mage.Sets/src/mage/cards/i/IndomitableArchangel.java @@ -1,24 +1,21 @@ - - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.ShroudAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterPermanent; +import java.util.UUID; + /** * @author nantuko */ @@ -33,15 +30,21 @@ public final class IndomitableArchangel extends CardImpl { } public IndomitableArchangel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.subtype.add(SubType.ANGEL); - this.power = new MageInt(4); this.toughness = new MageInt(4); + + // Flying this.addAbility(FlyingAbility.getInstance()); + + // Metalcraft — Artifacts you control have shroud as long as you control three or more artifacts. (An artifact with shroud can’t be the target of spells or abilities.) ContinuousEffect gainAbilityEffect = new GainAbilityControlledEffect(ShroudAbility.getInstance(), Duration.WhileOnBattlefield, filter); ConditionalContinuousEffect effect = new ConditionalContinuousEffect(gainAbilityEffect, MetalcraftCondition.instance, rule); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); } public IndomitableArchangel(final IndomitableArchangel card) { diff --git a/Mage.Sets/src/mage/cards/i/InduceParanoia.java b/Mage.Sets/src/mage/cards/i/InduceParanoia.java index d8862fa168..eb699cc84e 100644 --- a/Mage.Sets/src/mage/cards/i/InduceParanoia.java +++ b/Mage.Sets/src/mage/cards/i/InduceParanoia.java @@ -51,7 +51,7 @@ class InduceParanoiaEffect extends OneShotEffect { InduceParanoiaEffect() { super(Outcome.Detriment); - this.staticText = "Counter target spell. If {B} was spent to cast this spell, that spell's controller puts the top X cards of their library into their graveyard, where X is the spell's converted mana cost."; + this.staticText = "Counter target spell. If {B} was spent to cast this spell, that spell's controller mills X cards, where X is the spell's converted mana cost."; } InduceParanoiaEffect(final InduceParanoiaEffect effect) { @@ -71,7 +71,7 @@ class InduceParanoiaEffect extends OneShotEffect { int spellCMC = spell.getConvertedManaCost(); Player player = game.getPlayer(spell.getControllerId()); if (player != null) { - player.moveCards(player.getLibrary().getTopCards(game, spellCMC), Zone.GRAVEYARD, source, game); + player.millCards(spellCMC, source, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/i/InducedAmnesia.java b/Mage.Sets/src/mage/cards/i/InducedAmnesia.java index 9c4588818d..9f2e5d158b 100644 --- a/Mage.Sets/src/mage/cards/i/InducedAmnesia.java +++ b/Mage.Sets/src/mage/cards/i/InducedAmnesia.java @@ -76,8 +76,8 @@ class InducedAmnesiaExileEffect extends OneShotEffect { card.setFaceDown(true, game); } game.informPlayers(sourcePermanent.getLogName() + ": " + targetPlayer.getLogName() + " exiles their hand face down (" + numberOfCards + "card" + (numberOfCards > 1 ? "s" : "") + ')'); - game.applyEffects(); - targetPlayer.drawCards(numberOfCards, game); + game.getState().processAction(game); + targetPlayer.drawCards(numberOfCards, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/i/IndulgentTormentor.java b/Mage.Sets/src/mage/cards/i/IndulgentTormentor.java index c2dbee8616..824bb44abe 100644 --- a/Mage.Sets/src/mage/cards/i/IndulgentTormentor.java +++ b/Mage.Sets/src/mage/cards/i/IndulgentTormentor.java @@ -88,7 +88,7 @@ class IndulgentTormentorEffect extends OneShotEffect { return true; } } - game.getPlayer(source.getControllerId()).drawCards(1, game); + game.getPlayer(source.getControllerId()).drawCards(1, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java b/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java new file mode 100644 index 0000000000..4ddb3b7ad8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java @@ -0,0 +1,62 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IndulgingPatrician extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2); + private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn"); + + public IndulgingPatrician(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new LoseLifeOpponentsEffect(3), TargetController.YOU, false + ), condition, "At the beginning of your end step, " + + "if you gained 3 or more life this turn, each opponent loses 3 life." + ).addHint(hint), new PlayerGainedLifeWatcher()); + } + + private IndulgingPatrician(final IndulgingPatrician card) { + super(card); + } + + @Override + public IndulgingPatrician copy() { + return new IndulgingPatrician(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InescapableBlaze.java b/Mage.Sets/src/mage/cards/i/InescapableBlaze.java index 2aa756c1ba..ba670d533c 100644 --- a/Mage.Sets/src/mage/cards/i/InescapableBlaze.java +++ b/Mage.Sets/src/mage/cards/i/InescapableBlaze.java @@ -1,7 +1,7 @@ package mage.cards.i; import java.util.UUID; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -18,7 +18,7 @@ public final class InescapableBlaze extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{R}{R}"); // This spell can't be countered. - this.addAbility(new CantBeCounteredAbility().setRuleAtTheTop(true)); + this.addAbility(new CantBeCounteredSourceAbility().setRuleAtTheTop(true)); // Inescapable Flame deals 6 damage to any target. this.getSpellAbility().addEffect(new DamageTargetEffect(6)); diff --git a/Mage.Sets/src/mage/cards/i/InescapableBrute.java b/Mage.Sets/src/mage/cards/i/InescapableBrute.java index 64045c3b56..a36278429e 100644 --- a/Mage.Sets/src/mage/cards/i/InescapableBrute.java +++ b/Mage.Sets/src/mage/cards/i/InescapableBrute.java @@ -9,6 +9,7 @@ import mage.abilities.keyword.WitherAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; @@ -29,7 +30,7 @@ public final class InescapableBrute extends CardImpl { this.addAbility(WitherAbility.getInstance()); // Inescapable Brute must be blocked if able. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect(Duration.WhileOnBattlefield))); } diff --git a/Mage.Sets/src/mage/cards/i/InevitableEnd.java b/Mage.Sets/src/mage/cards/i/InevitableEnd.java index f05ef549b7..8d17f6ab00 100644 --- a/Mage.Sets/src/mage/cards/i/InevitableEnd.java +++ b/Mage.Sets/src/mage/cards/i/InevitableEnd.java @@ -1,5 +1,6 @@ package mage.cards.i; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -14,31 +15,35 @@ import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import java.util.UUID; - /** * @author TheElk801 */ public final class InevitableEnd extends CardImpl { public InevitableEnd(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, + "{2}{B}"); this.subtype.add(SubType.AURA); // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.getSpellAbility().addEffect( + new AttachEffect(Outcome.Detriment)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); - // Enchanted creature has "At the beginning of your upkeep, sacrifice a creature." + // Enchanted creature has "At the beginning of your upkeep, sacrifice a + // creature." + BeginningOfUpkeepTriggeredAbility triggeredAbility + = new BeginningOfUpkeepTriggeredAbility( + new SacrificeControllerEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, 1, null), + TargetController.YOU, false); this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( - new BeginningOfUpkeepTriggeredAbility(new SacrificeControllerEffect( - StaticFilters.FILTER_PERMANENT_CREATURE, 1, null - ), TargetController.YOU, false), AttachmentType.AURA - ))); + triggeredAbility, AttachmentType.AURA, Duration.WhileOnBattlefield, + "Enchanted creature has \"" + triggeredAbility.getRule() + "\""))); } private InevitableEnd(final InevitableEnd card) { diff --git a/Mage.Sets/src/mage/cards/i/InfectiousCurse.java b/Mage.Sets/src/mage/cards/i/InfectiousCurse.java index 555dc866bd..f3e631dcb5 100644 --- a/Mage.Sets/src/mage/cards/i/InfectiousCurse.java +++ b/Mage.Sets/src/mage/cards/i/InfectiousCurse.java @@ -1,9 +1,6 @@ - package mage.cards.i; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleStaticAbility; @@ -18,20 +15,23 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.game.stack.Spell; import mage.players.Player; -import mage.target.Target; import mage.target.TargetPlayer; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + /** - * * @author halljared */ public final class InfectiousCurse extends CardImpl { public InfectiousCurse(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},""); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); this.subtype.add(SubType.AURA, SubType.CURSE); this.color.setBlack(true); @@ -46,6 +46,7 @@ public final class InfectiousCurse extends CardImpl { // Spells you cast that target enchanted player cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfectiousCurseCostReductionEffect())); + // At the beginning of enchanted player's upkeep, that player loses 1 life and you gain 1 life. InfectiousCurseAbility curseAbility = new InfectiousCurseAbility(); curseAbility.addEffect(new GainLifeEffect(1)); @@ -106,7 +107,7 @@ class InfectiousCurseCostReductionEffect extends CostModificationEffectImpl { public InfectiousCurseCostReductionEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); - this.staticText = "Spells you cast that target enchanted player cost {1} less to cast."; + this.staticText = "Spells you cast that target enchanted player cost {1} less to cast"; } protected InfectiousCurseCostReductionEffect(InfectiousCurseCostReductionEffect effect) { @@ -121,23 +122,31 @@ class InfectiousCurseCostReductionEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - if (source.isControlledBy(abilityToModify.getControllerId())) { - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - UUID attachedTo = enchantment.getAttachedTo(); - if (targetUUID.equals(attachedTo)) { - return true; - } - } - } - } - } + if (!(abilityToModify instanceof SpellAbility)) { + return false; } - return false; + + if (!source.isControlledBy(abilityToModify.getControllerId())) { + return false; + } + + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment == null || enchantment.getAttachedTo() == null) { + return false; + } + + Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); + Set allTargets; + if (spell != null) { + // real cast + allTargets = CardUtil.getAllSelectedTargets(abilityToModify, game); + } else { + // playable + allTargets = CardUtil.getAllPossibleTargets(abilityToModify, game); + } + + // try to reduce all the time (if it possible to target) + return allTargets.stream().anyMatch(target -> Objects.equals(target, enchantment.getAttachedTo())); } @Override diff --git a/Mage.Sets/src/mage/cards/i/InfectiousHost.java b/Mage.Sets/src/mage/cards/i/InfectiousHost.java index 8e352d1287..b8740b2a61 100644 --- a/Mage.Sets/src/mage/cards/i/InfectiousHost.java +++ b/Mage.Sets/src/mage/cards/i/InfectiousHost.java @@ -4,7 +4,7 @@ package mage.cards.i; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class InfectiousHost extends CardImpl { this.toughness = new MageInt(1); // When Infectious Host dies, target player loses 2 life. - Ability ability = new DiesTriggeredAbility(new LoseLifeTargetEffect(2), false); + Ability ability = new DiesSourceTriggeredAbility(new LoseLifeTargetEffect(2), false); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/InfernalDarkness.java b/Mage.Sets/src/mage/cards/i/InfernalDarkness.java index cef74faccc..eae4f1c6de 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalDarkness.java +++ b/Mage.Sets/src/mage/cards/i/InfernalDarkness.java @@ -78,7 +78,7 @@ class InfernalDarknessCost extends CostImpl { manaCost.clearPaid(); if (manaCost.pay(ability, game, player.getId(), player.getId(), false) - && player.canPayLifeCost() + && player.canPayLifeCost(ability) && player.getLife() >= 1 && lifeCost.pay(ability, game, player.getId(), player.getId(), false)) { paid = true; @@ -91,7 +91,7 @@ class InfernalDarknessCost extends CostImpl { public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { Player player = game.getPlayer(controllerId); if (player != null - && player.canPayLifeCost() + && player.canPayLifeCost(ability) && player.getLife() >= 1) { return true; } diff --git a/Mage.Sets/src/mage/cards/i/InfernalDenizen.java b/Mage.Sets/src/mage/cards/i/InfernalDenizen.java index 7b162869c3..d5005c10b5 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalDenizen.java +++ b/Mage.Sets/src/mage/cards/i/InfernalDenizen.java @@ -7,6 +7,7 @@ import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.common.SourceOnBattlefieldCondition; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.dynamicvalue.DynamicValue; @@ -52,7 +53,7 @@ public final class InfernalDenizen extends CardImpl { // {tap}: Gain control of target creature for as long as Infernal Denizen remains on the battlefield. ConditionalContinuousEffect effect = new ConditionalContinuousEffect( new GainControlTargetEffect(Duration.Custom, true), - SourceOnBattlefieldCondition.instance, + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), "gain control of target creature for as long as {this} remains on the battlefield"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/i/InfernalGenesis.java b/Mage.Sets/src/mage/cards/i/InfernalGenesis.java index daa9e51bfd..2fac791347 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalGenesis.java +++ b/Mage.Sets/src/mage/cards/i/InfernalGenesis.java @@ -1,20 +1,23 @@ - package mage.cards.i; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; import mage.game.Game; import mage.game.permanent.token.MinionToken2; +import mage.game.permanent.token.Token; import mage.players.Player; +import java.util.Objects; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class InfernalGenesis extends CardImpl { @@ -39,28 +42,30 @@ public final class InfernalGenesis extends CardImpl { class InfernalGenesisEffect extends OneShotEffect { + private static final Token token = new MinionToken2(); + InfernalGenesisEffect() { super(Outcome.PutCreatureInPlay); - staticText = "that player puts the top card of their library into their graveyard. " + - "Then they create X 1/1 black Minion creature tokens, where X is that card's converted mana cost"; + staticText = "that player mills a card. Then they create X 1/1 black Minion creature tokens, " + + "where X is the milled card's converted mana cost."; } - InfernalGenesisEffect(final InfernalGenesisEffect effect) { + private InfernalGenesisEffect(final InfernalGenesisEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (player != null) { - Card card = player.getLibrary().getFromTop(game); - if (card != null) { - if (player.moveCards(card, Zone.GRAVEYARD, source, game)) { - int cmc = card.getConvertedManaCost(); - MinionToken2 token = new MinionToken2(); - token.putOntoBattlefield(cmc, game, source.getSourceId(), player.getId()); - } - } + int totalCMC = player + .millCards(1, source, game) + .getCards(game) + .stream() + .filter(Objects::nonNull) + .mapToInt(MageObject::getConvertedManaCost) + .sum(); + if (totalCMC > 0) { + token.putOntoBattlefield(totalCMC, game, source.getSourceId(), player.getId()); } return true; } diff --git a/Mage.Sets/src/mage/cards/i/InfernalKirin.java b/Mage.Sets/src/mage/cards/i/InfernalKirin.java index 38cdc8b4a7..eedc3483ba 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalKirin.java +++ b/Mage.Sets/src/mage/cards/i/InfernalKirin.java @@ -1,20 +1,14 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.cards.Cards; +import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.stack.Spell; @@ -22,8 +16,9 @@ import mage.players.Player; import mage.target.Target; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class InfernalKirin extends CardImpl { @@ -39,14 +34,14 @@ public final class InfernalKirin extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Whenever you cast a Spirit or Arcane spell, target player reveals their hand and discards all cards with that spell's converted mana cost. Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new InfernalKirinEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, false, true); ability.addTarget(new TargetPlayer()); this.addAbility(ability); - } - public InfernalKirin(final InfernalKirin card) { + private InfernalKirin(final InfernalKirin card) { super(card); } @@ -58,12 +53,12 @@ public final class InfernalKirin extends CardImpl { class InfernalKirinEffect extends OneShotEffect { - public InfernalKirinEffect() { + InfernalKirinEffect() { super(Outcome.Detriment); this.staticText = "target player reveals their hand and discards all cards with that spell's converted mana cost"; } - public InfernalKirinEffect(final InfernalKirinEffect effect) { + private InfernalKirinEffect(final InfernalKirinEffect effect) { super(effect); } @@ -75,27 +70,26 @@ class InfernalKirinEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source)); - if (spell != null) { - int cmc = spell.getConvertedManaCost(); - Player targetPlayer = null; - for (Target target : source.getTargets()) { - if (target instanceof TargetPlayer) { - targetPlayer = game.getPlayer(target.getFirstTarget()); - } - } - if (targetPlayer != null) { - if (!targetPlayer.getHand().isEmpty()) { - targetPlayer.revealCards("Infernal Kirin", targetPlayer.getHand(), game); - for (UUID uuid : targetPlayer.getHand().copy()) { - Card card = game.getCard(uuid); - if (card != null && card.getConvertedManaCost() == cmc) { - targetPlayer.discard(card, source, game); - } - } - } - return true; + if (spell == null) { + return false; + } + int cmc = spell.getConvertedManaCost(); + Player targetPlayer = null; + for (Target target : source.getTargets()) { + if (target instanceof TargetPlayer) { + targetPlayer = game.getPlayer(target.getFirstTarget()); } } - return false; + if (targetPlayer == null) { + return false; + } + if (targetPlayer.getHand().isEmpty()) { + return true; + } + targetPlayer.revealCards(source, targetPlayer.getHand(), game); + Cards cards = targetPlayer.getHand().copy(); + cards.removeIf(uuid -> game.getCard(uuid).getConvertedManaCost() != cmc); + targetPlayer.discard(cards, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/i/InfernalOffering.java b/Mage.Sets/src/mage/cards/i/InfernalOffering.java index 11be4c2397..3a829371a8 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalOffering.java +++ b/Mage.Sets/src/mage/cards/i/InfernalOffering.java @@ -99,7 +99,7 @@ class InfernalOfferingSacrificeEffect extends OneShotEffect { for (UUID playerId : toDraw) { Player playerToDraw = game.getPlayer(playerId); if (playerToDraw != null) { - playerToDraw.drawCards(2, game); + playerToDraw.drawCards(2, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/i/InfernalReckoning.java b/Mage.Sets/src/mage/cards/i/InfernalReckoning.java index 1d1cc6ded2..b7c90635ba 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalReckoning.java +++ b/Mage.Sets/src/mage/cards/i/InfernalReckoning.java @@ -62,14 +62,14 @@ class InfernalJudgmentEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Player player = game.getPlayer(source.getControllerId()); if (permanent == null || player == null) { return false; } int creaturePower = permanent.getPower().getValue(); permanent.moveToExile(null, null, source.getSourceId(), game); - game.applyEffects(); + game.getState().processAction(game); player.gainLife(creaturePower, game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/i/InfernalScarring.java b/Mage.Sets/src/mage/cards/i/InfernalScarring.java index 10cccb69b0..95d47bd72e 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalScarring.java +++ b/Mage.Sets/src/mage/cards/i/InfernalScarring.java @@ -3,7 +3,7 @@ package mage.cards.i; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; @@ -43,7 +43,7 @@ public final class InfernalScarring extends CardImpl { Effect effect = new BoostEnchantedEffect(2, 0, Duration.WhileOnBattlefield); effect.setText("Enchanted creature gets +2/+0"); ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); - effect = new GainAbilityAttachedEffect(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1)), AttachmentType.AURA, Duration.WhileOnBattlefield); + effect = new GainAbilityAttachedEffect(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1)), AttachmentType.AURA, Duration.WhileOnBattlefield); effect.setText("and has \"When this creature dies, draw a card.\""); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/i/InfernoElemental.java b/Mage.Sets/src/mage/cards/i/InfernoElemental.java index 6815e8a26a..1e3b74c9a4 100644 --- a/Mage.Sets/src/mage/cards/i/InfernoElemental.java +++ b/Mage.Sets/src/mage/cards/i/InfernoElemental.java @@ -3,7 +3,7 @@ package mage.cards.i; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class InfernoElemental extends CardImpl { this.toughness = new MageInt(4); // Whenever Inferno Elemental blocks or becomes blocked by a creature, Inferno Elemental deals 3 damage to that creature. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new DamageTargetEffect(3, true, "that creature"), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(3, true, "that creature"), false)); } public InfernoElemental(final InfernoElemental card) { diff --git a/Mage.Sets/src/mage/cards/i/InfiltrationLens.java b/Mage.Sets/src/mage/cards/i/InfiltrationLens.java index 459fc28c9c..5b7456216e 100644 --- a/Mage.Sets/src/mage/cards/i/InfiltrationLens.java +++ b/Mage.Sets/src/mage/cards/i/InfiltrationLens.java @@ -57,7 +57,7 @@ class EquippedBecomesBlockedTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.CREATURE_BLOCKED; + return event.getType() == EventType.BLOCKER_DECLARED; } @Override diff --git a/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java b/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java index 60f71b540e..19b6a685f4 100644 --- a/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java +++ b/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java @@ -1,46 +1,49 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.hint.common.AbilityResolutionCountHint; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.watchers.common.AbilityResolvedWatcher; + +import java.util.UUID; /** - * - * @author LevelX2 + * @author TheElk801 */ public final class InnerFlameIgniter extends CardImpl { public InnerFlameIgniter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); this.subtype.add(SubType.ELEMENTAL); this.subtype.add(SubType.WARRIOR); this.power = new MageInt(2); this.toughness = new MageInt(2); // {2}{R}: Creatures you control get +1/+0 until end of turn. If this is the third time this ability has resolved this turn, creatures you control gain first strike until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1,0,Duration.EndOfTurn), new ManaCostsImpl("{2}{R}")); - ability.addEffect(new InnerFlameIgniterEffect()); - this.addAbility(ability); + Ability ability = new SimpleActivatedAbility( + new BoostControlledEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{2}{R}") + ); + ContinuousEffect effectIf3rdResolution = new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES); + ability.addEffect(new IfAbilityHasResolvedXTimesEffect(Outcome.AddAbility, 3, effectIf3rdResolution)); + ability.addHint(AbilityResolutionCountHint.instance); + this.addAbility(ability, new AbilityResolvedWatcher()); } - public InnerFlameIgniter(final InnerFlameIgniter card) { + private InnerFlameIgniter(final InnerFlameIgniter card) { super(card); } @@ -48,56 +51,4 @@ public final class InnerFlameIgniter extends CardImpl { public InnerFlameIgniter copy() { return new InnerFlameIgniter(this); } -} - -class InnerFlameIgniterEffect extends OneShotEffect { - - static class ActivationInfo { - public int zoneChangeCounter; - public int turn; - public int activations; - } - - public InnerFlameIgniterEffect() { - super(Outcome.Damage); - this.staticText = "If this is the third time this ability has resolved this turn, creatures you control gain first strike until end of turn"; - } - - public InnerFlameIgniterEffect(final InnerFlameIgniterEffect effect) { - super(effect); - } - - @Override - public InnerFlameIgniterEffect copy() { - return new InnerFlameIgniterEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - ActivationInfo info; - Object object = game.getState().getValue(source.getSourceId() + "ActivationInfo"); - if (object instanceof ActivationInfo) { - info = (ActivationInfo) object; - if (info.turn != game.getTurnNum() || sourcePermanent.getZoneChangeCounter(game) != info.zoneChangeCounter) { - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - info.activations = 0; - } - } else { - info = new ActivationInfo(); - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - game.getState().setValue(source.getSourceId() + "ActivationInfo", info); - } - info.activations++; - if (info.activations == 3) { - game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), source); - } - return true; - } - return false; - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java b/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java new file mode 100644 index 0000000000..3562ec3dc7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java @@ -0,0 +1,165 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.players.PlayerList; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InniazTheGaleForce extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("Attacking creatures with flying"); + private static final FilterCreaturePermanent filter2 + = new FilterCreaturePermanent("creatures you control with flying"); + + static { + filter.add(AttackingPredicate.instance); + filter.add(new AbilityPredicate(FlyingAbility.class)); + filter2.add(new AbilityPredicate(FlyingAbility.class)); + } + + public InniazTheGaleForce(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DJINN); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {2}{W/U}: Attacking creatures with flying get +1/+1 until end of turn. + this.addAbility(new SimpleActivatedAbility(new BoostAllEffect( + 1, 1, Duration.EndOfTurn, filter, false + ), new ManaCostsImpl("{2}{W/U}"))); + + // Whenever three or more creatures you control with flying attack, each player gains control + // of a nonland permanent of your choice controlled by the player to their right. + this.addAbility(new AttacksWithCreaturesTriggeredAbility( + new InniazTheGaleForceEffect(), 3, filter2 + )); + } + + private InniazTheGaleForce(final InniazTheGaleForce card) { + super(card); + } + + @Override + public InniazTheGaleForce copy() { + return new InniazTheGaleForce(this); + } +} + +class InniazTheGaleForceEffect extends OneShotEffect { + + private static final class PlayerPair { + + private final Player leftPlayer; + private final Player rightPlayer; + private final FilterPermanent filter; + private final TargetPermanent target; + + private PlayerPair(Player rightPlayer, Player leftPlayer) { + this.leftPlayer = leftPlayer; + this.rightPlayer = rightPlayer; + this.filter = this.makeFilter(); + this.target = this.makeTarget(); + } + + private FilterPermanent makeFilter() { + FilterPermanent filter = new FilterNonlandPermanent( + "nonland permanent controlled by " + rightPlayer.getName() + + " to give to " + leftPlayer.getName() + ); + filter.add(new ControllerIdPredicate(rightPlayer.getId())); + return filter; + } + + private TargetPermanent makeTarget() { + return new TargetPermanent(1, 1, this.filter, true); + } + + private void chooseTargets(Player controller, Game game, Ability source) { + if (game.getBattlefield().count(this.filter, source.getSourceId(), source.getControllerId(), game) > 0) { + controller.choose(Outcome.Neutral, this.target, source.getSourceId(), game); + } + } + + private void createEffect(Game game, Ability source) { + if (this.target.getFirstTarget() == null) { + return; + } + game.addEffect(new GainControlTargetEffect( + Duration.Custom, true, leftPlayer.getId() + ).setTargetPointer(new FixedTarget(target.getFirstTarget(), game)), source); + } + } + + InniazTheGaleForceEffect() { + super(Outcome.Benefit); + staticText = "each player gains control of a nonland permanent of your choice controlled by the player to their right."; + } + + private InniazTheGaleForceEffect(final InniazTheGaleForceEffect effect) { + super(effect); + } + + @Override + public InniazTheGaleForceEffect copy() { + return new InniazTheGaleForceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + PlayerList playerList = game.getState().getPlayersInRange(source.getControllerId(), game); + List playerPairList = new ArrayList<>(); + for (int i = 0; i < playerList.size() - 1; i++) { + playerPairList.add(new PlayerPair( + game.getPlayer(playerList.get(i)), + game.getPlayer(playerList.get(i + 1)) + )); + } + playerPairList.add(new PlayerPair( + game.getPlayer(playerList.get(playerList.size() - 1)), + game.getPlayer(playerList.get(0)) + )); + for (PlayerPair playerPair : playerPairList) { + playerPair.chooseTargets(controller, game, source); + } + for (PlayerPair playerPair : playerPairList) { + playerPair.createEffect(game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InsatiableHemophage.java b/Mage.Sets/src/mage/cards/i/InsatiableHemophage.java new file mode 100644 index 0000000000..118438619b --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InsatiableHemophage.java @@ -0,0 +1,53 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.dynamicvalue.common.SourceMutatedCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InsatiableHemophage extends CardImpl { + + public InsatiableHemophage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Mutate {2}{B} + this.addAbility(new MutateAbility(this, "{2}{B}")); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever this creature mutates, each opponent loses X life and you gain X life, where X is the number of times this creature has mutated. + Ability ability = new MutatesSourceTriggeredAbility( + new LoseLifeOpponentsEffect(SourceMutatedCount.instance).setText("each opponent loses X life") + ); + ability.addEffect(new GainLifeEffect(SourceMutatedCount.instance) + .setText("and you gain X life, where X is the number of times this creature has mutated")); + this.addAbility(ability); + } + + private InsatiableHemophage(final InsatiableHemophage card) { + super(card); + } + + @Override + public InsatiableHemophage copy() { + return new InsatiableHemophage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InsidiousBookworms.java b/Mage.Sets/src/mage/cards/i/InsidiousBookworms.java index 4f6e4b83be..09e327fa68 100644 --- a/Mage.Sets/src/mage/cards/i/InsidiousBookworms.java +++ b/Mage.Sets/src/mage/cards/i/InsidiousBookworms.java @@ -4,7 +4,7 @@ package mage.cards.i; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.discard.DiscardTargetEffect; @@ -27,7 +27,7 @@ public final class InsidiousBookworms extends CardImpl { this.toughness = new MageInt(1); // When Insidious Bookworms dies, you may pay {1}{B}. If you do, target player discards a card at random. - Ability ability = new DiesTriggeredAbility(new DoIfCostPaid(new DiscardTargetEffect(1, true), new ManaCostsImpl("{1}{B}"))); + Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new DiscardTargetEffect(1, true), new ManaCostsImpl("{1}{B}"))); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/InsidiousDreams.java b/Mage.Sets/src/mage/cards/i/InsidiousDreams.java index fecfde77a6..bdd0e66400 100644 --- a/Mage.Sets/src/mage/cards/i/InsidiousDreams.java +++ b/Mage.Sets/src/mage/cards/i/InsidiousDreams.java @@ -113,7 +113,7 @@ class InsidiousDreamsAdditionalCost extends VariableCostImpl { InsidiousDreamsAdditionalCost() { super("cards to discard"); - this.text = "as an additional cost to cast this spell, discard X cards"; + this.text = "discard X cards"; } InsidiousDreamsAdditionalCost(final InsidiousDreamsAdditionalCost cost) { diff --git a/Mage.Sets/src/mage/cards/i/InspiredUltimatum.java b/Mage.Sets/src/mage/cards/i/InspiredUltimatum.java new file mode 100644 index 0000000000..d49fbd5cb2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InspiredUltimatum.java @@ -0,0 +1,72 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InspiredUltimatum extends CardImpl { + + public InspiredUltimatum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}{U}{R}{R}{R}{W}{W}"); + + // Target player gains 5 life. Inspired Ultimatum deals 5 damage to any target. You draw five cards. + this.getSpellAbility().addEffect(new InspiredUltimatumEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addTarget(new TargetAnyTarget()); + } + + private InspiredUltimatum(final InspiredUltimatum card) { + super(card); + } + + @Override + public InspiredUltimatum copy() { + return new InspiredUltimatum(this); + } +} + +class InspiredUltimatumEffect extends OneShotEffect { + + InspiredUltimatumEffect() { + super(Outcome.Benefit); + staticText = "Target player gains 5 life, {this} deals 5 damage to any target, then you draw five cards."; + } + + private InspiredUltimatumEffect(final InspiredUltimatumEffect effect) { + super(effect); + } + + @Override + public InspiredUltimatumEffect copy() { + return new InspiredUltimatumEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getTargets().get(0).getFirstTarget()); + if (player != null) { + player.gainLife(5, game, source); + } + game.damagePlayerOrPlaneswalker( + source.getTargets().get(1).getFirstTarget(), 5, + source.getSourceId(), game, false, true + ); + player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.drawCards(5, source.getSourceId(), game); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/i/InspiringStatuary.java b/Mage.Sets/src/mage/cards/i/InspiringStatuary.java index 104c79b3b6..04471a83be 100644 --- a/Mage.Sets/src/mage/cards/i/InspiringStatuary.java +++ b/Mage.Sets/src/mage/cards/i/InspiringStatuary.java @@ -1,7 +1,5 @@ - package mage.cards.i; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect; import mage.abilities.keyword.ImproviseAbility; @@ -9,16 +7,17 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.FilterSpell; +import mage.filter.FilterCard; import mage.filter.predicate.Predicates; +import java.util.UUID; + /** - * * @author Styxo */ public final class InspiringStatuary extends CardImpl { - private static final FilterSpell filter = new FilterSpell("non-artifact spells you cast"); + private static final FilterCard filter = new FilterCard("non-artifact spells you cast"); static { filter.add(Predicates.not(CardType.ARTIFACT.getPredicate())); diff --git a/Mage.Sets/src/mage/cards/i/InsultInjury.java b/Mage.Sets/src/mage/cards/i/InsultInjury.java index 14be28e21f..3ae41e1abf 100644 --- a/Mage.Sets/src/mage/cards/i/InsultInjury.java +++ b/Mage.Sets/src/mage/cards/i/InsultInjury.java @@ -32,7 +32,7 @@ public final class InsultInjury extends SplitCard { // Insult // Damage can't be prevented this turn. If a source you control would deal damage this turn it deals // double that damage instead. - getLeftHalfCard().getSpellAbility().addEffect(new DamageCantBePreventedEffect(Duration.EndOfTurn, "Damage can't be prevented this turn.", true, false)); + getLeftHalfCard().getSpellAbility().addEffect(new DamageCantBePreventedEffect(Duration.EndOfTurn, "Damage can't be prevented this turn", true, false)); getLeftHalfCard().getSpellAbility().addEffect(new InsultDoubleDamageEffect()); // to diff --git a/Mage.Sets/src/mage/cards/i/IntellectualOffering.java b/Mage.Sets/src/mage/cards/i/IntellectualOffering.java index 1218fe286b..a97e937645 100644 --- a/Mage.Sets/src/mage/cards/i/IntellectualOffering.java +++ b/Mage.Sets/src/mage/cards/i/IntellectualOffering.java @@ -64,10 +64,10 @@ class IntellectualOfferingDrawEffect extends OneShotEffect { if (player != null) { Target target = new TargetOpponent(true); target.choose(Outcome.DrawCard, source.getControllerId(), source.getSourceId(), game); - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); Player opponent = game.getPlayer(target.getFirstTarget()); if (opponent != null) { - opponent.drawCards(3, game); + opponent.drawCards(3, source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/i/InterpretTheSigns.java b/Mage.Sets/src/mage/cards/i/InterpretTheSigns.java index d1d486f31d..877c0bd31f 100644 --- a/Mage.Sets/src/mage/cards/i/InterpretTheSigns.java +++ b/Mage.Sets/src/mage/cards/i/InterpretTheSigns.java @@ -64,7 +64,7 @@ class InterpretTheSignsEffect extends OneShotEffect { Card card = controller.getLibrary().getFromTop(game); if (card != null) { controller.revealCards(sourceCard.getName(), new CardsImpl(card), game); - controller.drawCards(card.getConvertedManaCost(), game); + controller.drawCards(card.getConvertedManaCost(), source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java b/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java index 5bf1a68777..c93e56b139 100644 --- a/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java +++ b/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java @@ -14,12 +14,16 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.target.targetpointer.FixedTarget; /** * @author fireshoes @@ -39,15 +43,13 @@ public final class IntetTheDreamer extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Intet, the Dreamer deals combat damage to a player, you may pay {2}{U}. If you do, exile the top card of your library face down. + // You may play that card without paying its mana cost for as long as Intet remains on the battlefield. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( new DoIfCostPaid(new IntetTheDreamerExileEffect(), new ManaCostsImpl("{2}{U}")), false, true)); // You may look at that card for as long as it remains exiled. this.addAbility(new SimpleStaticAbility(Zone.ALL, new IntetTheDreamerLookEffect())); - // You may play that card without paying its mana cost for as long as Intet remains on the battlefield. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IntetTheDreamerCastEffect())); - } public IntetTheDreamer(final IntetTheDreamer card) { @@ -64,7 +66,7 @@ class IntetTheDreamerExileEffect extends OneShotEffect { public IntetTheDreamerExileEffect() { super(Outcome.Discard); - staticText = "exile the top card of your library face down"; + staticText = "exile the top card of your library face down. You may play that card without paying its mana cost for as long as Intet remains on the battlefield"; } public IntetTheDreamerExileEffect(final IntetTheDreamerExileEffect effect) { @@ -77,20 +79,18 @@ class IntetTheDreamerExileEffect extends OneShotEffect { if (controller != null) { Card card = controller.getLibrary().getFromTop(game); MageObject sourceObject = source.getSourceObject(game); - if (card != null - && sourceObject != null) { + if (card != null && sourceObject != null) { card.setFaceDown(true, game); - controller.moveCardsToExile( - card, - source, - game, - false, - CardUtil.getExileZoneId(game, - source.getSourceId(), - sourceObject.getZoneChangeCounter(game)), // sourceObject must be used due to source not working correctly - sourceObject.getIdName()); + controller.moveCardsToExile(card, source, game, false, + CardUtil.getExileZoneId(game, source.getSourceId(), sourceObject.getZoneChangeCounter(game)), // sourceObject must be used due to source not working correctly + sourceObject.getIdName() + " (" + sourceObject.getZoneChangeCounter(game) + ")"); card.setFaceDown(true, game); - game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), Boolean.TRUE); + ContinuousEffect effect = new ConditionalAsThoughEffect( + new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.Custom, true), + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD)); + effect.setTargetPointer(new FixedTarget(card, game)); + game.getState().addEffect(effect, source); + game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), Boolean.TRUE); // TODO This value will never be removed return true; } } @@ -103,63 +103,6 @@ class IntetTheDreamerExileEffect extends OneShotEffect { } } -class IntetTheDreamerCastEffect extends AsThoughEffectImpl { - - public IntetTheDreamerCastEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "You may play the card from exile without paying its mana cost for as long as {this} remains on the battlefield"; - } - - public IntetTheDreamerCastEffect(final IntetTheDreamerCastEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public IntetTheDreamerCastEffect copy() { - return new IntetTheDreamerCastEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null - && sourceObject != null) { - Card card = game.getCard(objectId); - if (card != null - && card.isFaceDown(game)) { - ExileZone zone = game.getExile().getExileZone( - CardUtil.getExileZoneId(game, - source.getSourceId(), - sourceObject.getZoneChangeCounter(game))); // sourceObject must be used due to source not working correctly - if (zone != null - && zone.contains(card.getId())) { - if (card.isLand()) { - if (game.canPlaySorcery(controller.getId()) - && game.getPlayer(controller.getId()).canPlayLand()) { - return controller.chooseUse(outcome, "Play " + card.getIdName() + '?', source, game); - } - } else { - controller.setCastSourceIdWithAlternateMana(objectId, - null, - card.getSpellAbility().getCosts()); - return true; - } - } - } - } - } - return false; - - } -} - class IntetTheDreamerLookEffect extends AsThoughEffectImpl { public IntetTheDreamerLookEffect() { @@ -187,10 +130,16 @@ class IntetTheDreamerLookEffect extends AsThoughEffectImpl { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Card card = game.getCard(objectId); - return (card != null - && card.isFaceDown(game) - && game.getExile().containsId(card.getId(), game) - && Boolean.TRUE.equals(game.getState().getValue("Exiled_IntetTheDreamer" + card.getId()))); + if (card != null) { + if (card.isFaceDown(game) + && game.getExile().containsId(card.getId(), game) + && Boolean.TRUE.equals(game.getState().getValue("Exiled_IntetTheDreamer" + card.getId()))) { + return true; + } else { + this.discard(); + game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), null); + } + } } } return false; diff --git a/Mage.Sets/src/mage/cards/i/IntoTheStory.java b/Mage.Sets/src/mage/cards/i/IntoTheStory.java index e48ef7bf07..02d5dfded4 100644 --- a/Mage.Sets/src/mage/cards/i/IntoTheStory.java +++ b/Mage.Sets/src/mage/cards/i/IntoTheStory.java @@ -5,6 +5,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -25,8 +26,8 @@ public final class IntoTheStory extends CardImpl { // This spell costs {3} less to cast if an opponent has seven or more cards in their graveyard. this.addAbility(new SimpleStaticAbility( - Zone.STACK, new SpellCostReductionSourceEffect(3, IntoTheStoryCondition.instance) - ).setRuleAtTheTop(true)); + Zone.ALL, new SpellCostReductionSourceEffect(3, IntoTheStoryCondition.instance) + ).setRuleAtTheTop(true).addHint(new ConditionHint(IntoTheStoryCondition.instance, "Opponent has seven or more cards in their graveyard"))); // Draw four cards. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(4)); diff --git a/Mage.Sets/src/mage/cards/i/InventorsFair.java b/Mage.Sets/src/mage/cards/i/InventorsFair.java index f604e54046..502f184803 100644 --- a/Mage.Sets/src/mage/cards/i/InventorsFair.java +++ b/Mage.Sets/src/mage/cards/i/InventorsFair.java @@ -1,7 +1,5 @@ - package mage.cards.i; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.ActivateIfConditionActivatedAbility; @@ -11,9 +9,11 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SuperType; import mage.constants.Zone; @@ -24,8 +24,9 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author fireshoes */ public final class InventorsFair extends CardImpl { @@ -37,7 +38,7 @@ public final class InventorsFair extends CardImpl { } public InventorsFair(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); addSuperType(SuperType.LEGENDARY); // At the beginning of your upkeep, if you control three or more artifacts, you gain 1 life. @@ -52,6 +53,8 @@ public final class InventorsFair extends CardImpl { new GenericManaCost(4), MetalcraftCondition.instance); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); + ability.setAbilityWord(AbilityWord.METALCRAFT); + ability.addHint(MetalcraftHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/Invigorate.java b/Mage.Sets/src/mage/cards/i/Invigorate.java index ad4e2d3ab9..dddb541810 100644 --- a/Mage.Sets/src/mage/cards/i/Invigorate.java +++ b/Mage.Sets/src/mage/cards/i/Invigorate.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -9,7 +8,7 @@ import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; +import mage.constants.Duration; import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.target.common.TargetCreaturePermanent; @@ -29,9 +28,9 @@ public final class Invigorate extends CardImpl { public Invigorate(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}"); - // If you control a Forest, rather than pay Invigorate's mana cost, you may have an opponent gain 3 life. this.addAbility(new AlternativeCostSourceAbility(new GainLifeOpponentCost(3), new PermanentsOnTheBattlefieldCondition(filter))); + // Target creature gets +4/+4 until end of turn. this.getSpellAbility().addEffect(new BoostTargetEffect(4,4,Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/i/InvigoratingSurge.java b/Mage.Sets/src/mage/cards/i/InvigoratingSurge.java new file mode 100644 index 0000000000..dfaae8a0e3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InvigoratingSurge.java @@ -0,0 +1,66 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InvigoratingSurge extends CardImpl { + + public InvigoratingSurge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // Put a +1/+1 counter on target creature you control, then double the number of +1/+1 counters on that creature. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + this.getSpellAbility().addEffect(new InvigoratingSurgeEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + } + + private InvigoratingSurge(final InvigoratingSurge card) { + super(card); + } + + @Override + public InvigoratingSurge copy() { + return new InvigoratingSurge(this); + } +} + +class InvigoratingSurgeEffect extends OneShotEffect { + + InvigoratingSurgeEffect() { + super(Outcome.Benefit); + staticText = ", then double the number of +1/+1 counters on that creature"; + } + + private InvigoratingSurgeEffect(final InvigoratingSurgeEffect effect) { + super(effect); + } + + @Override + public InvigoratingSurgeEffect copy() { + return new InvigoratingSurgeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + int counterCount = permanent.getCounters(game).getCount(CounterType.P1P1); + return counterCount > 0 && permanent.addCounters(CounterType.P1P1.createInstance(counterCount), source, game); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/i/IriniSengir.java b/Mage.Sets/src/mage/cards/i/IriniSengir.java index 9ba91444fd..8d485f939f 100644 --- a/Mage.Sets/src/mage/cards/i/IriniSengir.java +++ b/Mage.Sets/src/mage/cards/i/IriniSengir.java @@ -1,35 +1,32 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterEnchantmentCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author fireshoes */ public final class IriniSengir extends CardImpl { - + private static final FilterEnchantmentCard filter = new FilterEnchantmentCard("Green enchantment spells and white enchantment spells"); + static { filter.add(Predicates.or(new ColorPredicate(ObjectColor.GREEN), (new ColorPredicate(ObjectColor.WHITE)))); } public IriniSengir(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VAMPIRE); this.subtype.add(SubType.DWARF); @@ -37,7 +34,9 @@ public final class IriniSengir extends CardImpl { this.toughness = new MageInt(2); // Green enchantment spells and white enchantment spells cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(filter, 2))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostIncreasingAllEffect(2, filter, TargetController.ANY)) + ); } public IriniSengir(final IriniSengir card) { diff --git a/Mage.Sets/src/mage/cards/i/Irradiate.java b/Mage.Sets/src/mage/cards/i/Irradiate.java index 2e19012e7c..7208209f31 100644 --- a/Mage.Sets/src/mage/cards/i/Irradiate.java +++ b/Mage.Sets/src/mage/cards/i/Irradiate.java @@ -1,9 +1,8 @@ - package mage.cards.i; -import java.util.UUID; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -11,19 +10,21 @@ import mage.constants.Duration; import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LoneFox */ public final class Irradiate extends CardImpl { public Irradiate(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}"); // Target creature gets -1/-1 until end of turn for each artifact you control. PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent(), -1); this.getSpellAbility().addEffect(new BoostTargetEffect(count, count, Duration.EndOfTurn, true)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addHint(ArtifactYouControlHint.instance); } public Irradiate(final Irradiate card) { diff --git a/Mage.Sets/src/mage/cards/i/IsamaruHoundOfKonda.java b/Mage.Sets/src/mage/cards/i/IsamaruHoundOfKonda.java index 35296f3e40..4c7eb1b336 100644 --- a/Mage.Sets/src/mage/cards/i/IsamaruHoundOfKonda.java +++ b/Mage.Sets/src/mage/cards/i/IsamaruHoundOfKonda.java @@ -19,7 +19,7 @@ public final class IsamaruHoundOfKonda extends CardImpl { public IsamaruHoundOfKonda(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); this.addSuperType(SuperType.LEGENDARY); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/i/IsaoEnlightenedBushi.java b/Mage.Sets/src/mage/cards/i/IsaoEnlightenedBushi.java index 7f69d6bb9f..d0f044d1ed 100644 --- a/Mage.Sets/src/mage/cards/i/IsaoEnlightenedBushi.java +++ b/Mage.Sets/src/mage/cards/i/IsaoEnlightenedBushi.java @@ -4,7 +4,7 @@ package mage.cards.i; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.RegenerateTargetEffect; @@ -40,7 +40,7 @@ public final class IsaoEnlightenedBushi extends CardImpl { this.toughness = new MageInt(1); // Isao, Enlightened Bushi can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); this.addAbility(new BushidoAbility(2)); // {2}: Regenerate target Samurai. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateTargetEffect(), new GenericManaCost(2)); diff --git a/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java b/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java index 62cf6c7eff..ced07404f1 100644 --- a/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java +++ b/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java @@ -1,27 +1,19 @@ package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.SendOptionUsedEventEffect; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.abilities.keyword.DeathtouchAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.counters.Counters; import mage.filter.FilterCard; @@ -33,8 +25,9 @@ import mage.game.events.ZoneChangeEvent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class IsarethTheAwakener extends CardImpl { @@ -55,7 +48,7 @@ public final class IsarethTheAwakener extends CardImpl { this.addAbility(new AttacksTriggeredAbility(new IsarethTheAwakenerCreateReflexiveTriggerEffect(), false)); } - public IsarethTheAwakener(final IsarethTheAwakener card) { + private IsarethTheAwakener(final IsarethTheAwakener card) { super(card); } @@ -67,15 +60,17 @@ public final class IsarethTheAwakener extends CardImpl { class IsarethTheAwakenerCreateReflexiveTriggerEffect extends OneShotEffect { - public IsarethTheAwakenerCreateReflexiveTriggerEffect() { + private static final String rule = "return target creature card " + + "with converted mana cost X from your graveyard to the battlefield " + + "with a corpse counter on it. If that creature would leave the battlefield, " + + "exile it instead of putting it anywhere else."; + + IsarethTheAwakenerCreateReflexiveTriggerEffect() { super(Outcome.Benefit); - this.staticText = "you may pay {X}. When you do, return target creature card " - + "with converted mana cost X from your graveyard to the battlefield " - + "with a corpse counter on it. If that creature would leave the battlefield, " - + "exile it instead of putting it anywhere else."; + this.staticText = "you may pay {X}. When you do, " + rule; } - public IsarethTheAwakenerCreateReflexiveTriggerEffect(final IsarethTheAwakenerCreateReflexiveTriggerEffect effect) { + private IsarethTheAwakenerCreateReflexiveTriggerEffect(final IsarethTheAwakenerCreateReflexiveTriggerEffect effect) { super(effect); } @@ -97,67 +92,34 @@ class IsarethTheAwakenerCreateReflexiveTriggerEffect extends OneShotEffect { if (!cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { return false; } - game.addDelayedTriggeredAbility(new IsarethTheAwakenerReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect(costX).apply(game, source); - } -} - -class IsarethTheAwakenerReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - public IsarethTheAwakenerReflexiveTriggeredAbility() { - super(new IsarethTheAwakenerEffect(), Duration.OneUse, true); - this.addEffect(new IsarethTheAwakenerReplacementEffect()); - } - - public IsarethTheAwakenerReflexiveTriggeredAbility(final IsarethTheAwakenerReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public IsarethTheAwakenerReflexiveTriggeredAbility copy() { - return new IsarethTheAwakenerReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getPlayerId().equals(this.getControllerId()) - || !event.getSourceId().equals(this.getSourceId())) { - return false; - } - FilterCard filter = new FilterCreatureCard( - "creature card with converted mana cost " - + event.getAmount() - + " or less from your graveyard" + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new IsarethTheAwakenerEffect(), false, rule ); - filter.add(new ConvertedManaCostPredicate( - ComparisonType.FEWER_THAN, event.getAmount() + 1 - )); - this.getTargets().clear(); - this.addTarget(new TargetCardInYourGraveyard(filter)); + ability.addEffect(new IsarethTheAwakenerReplacementEffect()); + ability.addTarget(new TargetCardInYourGraveyard(makeFilter(costX))); + game.fireReflexiveTriggeredAbility(ability, source); return true; } - @Override - public String getRule() { - return "When you pay {X}, return target creature card " - + "with converted mana cost X from your graveyard to the battlefield " - + "with a corpse counter on it. If that creature would leave the battlefield, " - + "exile it instead of putting it anywhere else."; + private static FilterCard makeFilter(int xValue) { + FilterCard filter = new FilterCreatureCard( + "creature card with converted mana cost " + + xValue + " or less from your graveyard" + ); + filter.add(new ConvertedManaCostPredicate( + ComparisonType.EQUAL_TO, xValue + )); + return filter; } } class IsarethTheAwakenerEffect extends OneShotEffect { - public IsarethTheAwakenerEffect() { + IsarethTheAwakenerEffect() { super(Outcome.PutCreatureInPlay); } - public IsarethTheAwakenerEffect(final IsarethTheAwakenerEffect effect) { + private IsarethTheAwakenerEffect(final IsarethTheAwakenerEffect effect) { super(effect); } @@ -182,11 +144,11 @@ class IsarethTheAwakenerEffect extends OneShotEffect { class IsarethTheAwakenerReplacementEffect extends ReplacementEffectImpl { - public IsarethTheAwakenerReplacementEffect() { + IsarethTheAwakenerReplacementEffect() { super(Duration.Custom, Outcome.Exile); } - public IsarethTheAwakenerReplacementEffect(final IsarethTheAwakenerReplacementEffect effect) { + private IsarethTheAwakenerReplacementEffect(final IsarethTheAwakenerReplacementEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java b/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java index 7a6a663c2d..ade3ddfe38 100644 --- a/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java +++ b/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java @@ -1,7 +1,5 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; @@ -14,23 +12,25 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author lunaskyrise */ public final class IsperiaTheInscrutable extends CardImpl { public IsperiaTheInscrutable(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}{W}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}{U}{U}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SPHINX); this.power = new MageInt(3); @@ -38,7 +38,7 @@ public final class IsperiaTheInscrutable extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // Whenever Isperia the Inscrutable deals combat damage to a player, name a card. That player reveals their hand. If they reveal the named card, search your library for a creature card with flying, reveal it, put it into your hand, then shuffle your library. Effect effect1 = new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.ALL); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect1, false, true); @@ -78,12 +78,11 @@ class IsperiaTheInscrutableEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); - Object object = game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - if (player != null && object instanceof String) { + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + if (player != null && cardName != null) { player.revealCards(player.getLogName() + " hand", player.getHand(), game, true); - String namedCard = (String) object; for (Card card : player.getHand().getCards(game)) { - if (card != null && card.getName().equals(namedCard)) { + if (CardUtil.haveSameNames(card, cardName, game)) { return new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/i/IvoryGargoyle.java b/Mage.Sets/src/mage/cards/i/IvoryGargoyle.java index 0cd6c5e37d..98dcfe2652 100644 --- a/Mage.Sets/src/mage/cards/i/IvoryGargoyle.java +++ b/Mage.Sets/src/mage/cards/i/IvoryGargoyle.java @@ -3,7 +3,7 @@ package mage.cards.i; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -34,7 +34,7 @@ public final class IvoryGargoyle extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Ivory Gargoyle dies, return it to the battlefield under its owner's control at the beginning of the next end step and you skip your next draw step. - Ability ability = new DiesTriggeredAbility(new CreateDelayedTriggeredAbilityEffect( + Ability ability = new DiesSourceTriggeredAbility(new CreateDelayedTriggeredAbilityEffect( new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect()))); ability.addEffect(new SkipNextDrawStepControllerEffect()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/i/IzzetKeyrune.java b/Mage.Sets/src/mage/cards/i/IzzetKeyrune.java index 992bca57bc..028df9bfd5 100644 --- a/Mage.Sets/src/mage/cards/i/IzzetKeyrune.java +++ b/Mage.Sets/src/mage/cards/i/IzzetKeyrune.java @@ -20,7 +20,6 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.token.TokenImpl; -import mage.game.permanent.token.Token; import mage.players.Player; /** @@ -71,7 +70,7 @@ public final class IzzetKeyrune extends CardImpl { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null && player.chooseUse(Outcome.DrawCard, "Do you wish to draw a card? If you do, discard a card.", source, game)) { - if (player.drawCards(1, game) > 0) { + if (player.drawCards(1, source.getSourceId(), game) > 0) { player.discard(1, false, source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/j/JaceMemoryAdept.java b/Mage.Sets/src/mage/cards/j/JaceMemoryAdept.java index 4694847ea2..ffc521f1a4 100644 --- a/Mage.Sets/src/mage/cards/j/JaceMemoryAdept.java +++ b/Mage.Sets/src/mage/cards/j/JaceMemoryAdept.java @@ -73,7 +73,7 @@ class JaceMemoryAdeptEffect extends DrawCardTargetEffect { for (UUID target : targetPointer.getTargets(game, source)) { Player player = game.getPlayer(target); if (player != null) { - player.drawCards(amount.calculate(game, source, this), game); + player.drawCards(amount.calculate(game, source, this), source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java b/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java index e9504663f3..13fd8dd325 100644 --- a/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java +++ b/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java @@ -1,36 +1,22 @@ - package mage.cards.j; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.*; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.game.Game; import mage.game.command.emblems.JaceTelepathUnboundEmblem; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -57,11 +43,13 @@ public final class JaceTelepathUnbound extends CardImpl { this.addAbility(ability); // -3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead. - ability = new LoyaltyAbility(new JaceTelepathUnboundEffect(), -3); + CastCardFromGraveyardThenExileItEffect minusEffect = new CastCardFromGraveyardThenExileItEffect(); + minusEffect.setText("You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead"); + ability = new LoyaltyAbility(minusEffect, -3); ability.addTarget(new TargetCardInYourGraveyard(new FilterInstantOrSorceryCard())); this.addAbility(ability); - // -9: You get an emblem with "Whenever you cast a spell, target opponent puts the top five cards of their library into their graveyard". + // −9: You get an emblem with "Whenever you cast a spell, target opponent mills five cards." this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new JaceTelepathUnboundEmblem()), -9)); } @@ -74,104 +62,3 @@ public final class JaceTelepathUnbound extends CardImpl { return new JaceTelepathUnbound(this); } } - -class JaceTelepathUnboundEffect extends OneShotEffect { - - JaceTelepathUnboundEffect() { - super(Outcome.Benefit); - this.staticText = "You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead"; - } - - JaceTelepathUnboundEffect(final JaceTelepathUnboundEffect effect) { - super(effect); - } - - @Override - public JaceTelepathUnboundEffect copy() { - return new JaceTelepathUnboundEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); - if (card != null) { - ContinuousEffect effect = new JaceTelepathUnboundCastFromGraveyardEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); - game.addEffect(effect, source); - effect = new JaceTelepathUnboundReplacementEffect(card.getId()); - game.addEffect(effect, source); - return true; - } - return false; - } -} - -class JaceTelepathUnboundCastFromGraveyardEffect extends AsThoughEffectImpl { - - JaceTelepathUnboundCastFromGraveyardEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - } - - JaceTelepathUnboundCastFromGraveyardEffect(final JaceTelepathUnboundCastFromGraveyardEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public JaceTelepathUnboundCastFromGraveyardEffect copy() { - return new JaceTelepathUnboundCastFromGraveyardEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId()); - } -} - -class JaceTelepathUnboundReplacementEffect extends ReplacementEffectImpl { - - private final UUID cardId; - - JaceTelepathUnboundReplacementEffect(UUID cardId) { - super(Duration.EndOfTurn, Outcome.Exile); - this.cardId = cardId; - staticText = "If that card would be put into your graveyard this turn, exile it instead"; - } - - JaceTelepathUnboundReplacementEffect(final JaceTelepathUnboundReplacementEffect effect) { - super(effect); - this.cardId = effect.cardId; - } - - @Override - public JaceTelepathUnboundReplacementEffect copy() { - return new JaceTelepathUnboundReplacementEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player controller = game.getPlayer(source.getControllerId()); - Card card = game.getCard(this.cardId); - if (controller != null && card != null) { - controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.STACK, true); - return true; - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - return zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getTargetId().equals(this.cardId); - } -} diff --git a/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java b/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java index e6feb931d1..ddfcc1a16e 100644 --- a/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java +++ b/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java @@ -90,14 +90,14 @@ class JaceWielderOfMysteriesContinuousEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.EMPTY_DRAW; + return event.getType() == GameEvent.EventType.DRAW_CARD; } @Override public boolean applies(GameEvent event, Ability source, Game game) { if (event.getPlayerId().equals(source.getControllerId())) { Player player = game.getPlayer(event.getPlayerId()); - if (player != null && !player.hasLost() && player.isEmptyDraw()) { + if (player != null && !player.hasLost() && !player.getLibrary().hasCards()) { return true; } } @@ -127,7 +127,7 @@ class JaceWielderOfMysteriesEffect extends OneShotEffect { if (player == null) { return false; } - player.drawCards(7, game); + player.drawCards(7, source.getSourceId(), game); if (!player.getLibrary().hasCards()) { player.won(game); } diff --git a/Mage.Sets/src/mage/cards/j/JacesArchivist.java b/Mage.Sets/src/mage/cards/j/JacesArchivist.java index a6934dd862..3cb16b5b21 100644 --- a/Mage.Sets/src/mage/cards/j/JacesArchivist.java +++ b/Mage.Sets/src/mage/cards/j/JacesArchivist.java @@ -77,7 +77,7 @@ class JacesArchivistEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(maxDiscarded, game); + player.drawCards(maxDiscarded, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/j/JacesMindseeker.java b/Mage.Sets/src/mage/cards/j/JacesMindseeker.java index dd0f2354fe..cc4e59d68f 100644 --- a/Mage.Sets/src/mage/cards/j/JacesMindseeker.java +++ b/Mage.Sets/src/mage/cards/j/JacesMindseeker.java @@ -1,19 +1,12 @@ package mage.cards.j; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; @@ -25,8 +18,10 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponent; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class JacesMindseeker extends CardImpl { @@ -65,9 +60,8 @@ class JaceMindseekerEffect extends OneShotEffect { public JaceMindseekerEffect() { super(Outcome.PlayForFree); - this.staticText = "target opponent puts the top five cards of their " - + "library into their graveyard. You may cast an instant or " - + "sorcery card from among them without paying its mana cost"; + this.staticText = "target opponent mills five cards. You may cast an instant or sorcery spell " + + "from among them without paying its mana cost."; } public JaceMindseekerEffect(final JaceMindseekerEffect effect) { @@ -84,10 +78,7 @@ class JaceMindseekerEffect extends OneShotEffect { Cards cardsToCast = new CardsImpl(); Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source)); if (targetOpponent != null) { - Set allCards = targetOpponent.getLibrary().getTopCards(game, 5); - Set toMove = new HashSet<>(); - toMove.addAll(allCards); - targetOpponent.moveCards(toMove, Zone.GRAVEYARD, source, game); + Set allCards = targetOpponent.millCards(5, source, game).getCards(game); for (Card card : allCards) { if (filter.match(card, game)) { Zone zone = game.getState().getZone(card.getId()); diff --git a/Mage.Sets/src/mage/cards/j/JaddiLifestrider.java b/Mage.Sets/src/mage/cards/j/JaddiLifestrider.java index 802a648f8b..6c38cd8e8c 100644 --- a/Mage.Sets/src/mage/cards/j/JaddiLifestrider.java +++ b/Mage.Sets/src/mage/cards/j/JaddiLifestrider.java @@ -1,7 +1,5 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -9,24 +7,26 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class JaddiLifestrider extends CardImpl { public JaddiLifestrider(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); this.subtype.add(SubType.ELEMENTAL); this.power = new MageInt(2); @@ -47,9 +47,9 @@ public final class JaddiLifestrider extends CardImpl { } class JaddiLifestriderEffect extends OneShotEffect { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("untapped creatures you control"); - + static { filter.add(TargetController.YOU.getControllerPredicate()); filter.add(Predicates.not(TappedPredicate.instance)); @@ -70,9 +70,10 @@ class JaddiLifestriderEffect extends OneShotEffect { Player you = game.getPlayer(source.getControllerId()); TargetCreaturePermanent target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true); if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.Tap, source.getControllerId(), source.getSourceId(), game)) { - for (UUID creature : target.getTargets()) { + for (UUID creatureId : target.getTargets()) { + Permanent creature = game.getPermanent(creatureId); if (creature != null) { - game.getPermanent(creature).tap(game); + creature.tap(game); tappedAmount++; } } diff --git a/Mage.Sets/src/mage/cards/j/JadeLeech.java b/Mage.Sets/src/mage/cards/j/JadeLeech.java index 80bfff6d4c..d5a8a6dab3 100644 --- a/Mage.Sets/src/mage/cards/j/JadeLeech.java +++ b/Mage.Sets/src/mage/cards/j/JadeLeech.java @@ -1,22 +1,22 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author LoneFox */ public final class JadeLeech extends CardImpl { @@ -28,14 +28,14 @@ public final class JadeLeech extends CardImpl { } public JadeLeech(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); this.subtype.add(SubType.LEECH); this.power = new MageInt(5); this.toughness = new MageInt(5); // Green spells you cast cost {G} more to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{G}")))); + new SpellsCostIncreasingAllEffect(new ManaCostsImpl("{G}"), filter, TargetController.YOU))); } public JadeLeech(final JadeLeech card) { diff --git a/Mage.Sets/src/mage/cards/j/JadedResponse.java b/Mage.Sets/src/mage/cards/j/JadedResponse.java index 64e124858c..9f35fb200a 100644 --- a/Mage.Sets/src/mage/cards/j/JadedResponse.java +++ b/Mage.Sets/src/mage/cards/j/JadedResponse.java @@ -1,22 +1,19 @@ - package mage.cards.j; -import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CounterTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.stack.StackObject; +import mage.target.TargetSpell; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class JadedResponse extends CardImpl { @@ -26,9 +23,10 @@ public final class JadedResponse extends CardImpl { // Counter target spell if it shares a color with a creature you control. this.getSpellAbility().addEffect(new JadedResponseEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); } - public JadedResponse(final JadedResponse card) { + private JadedResponse(final JadedResponse card) { super(card); } @@ -45,7 +43,7 @@ class JadedResponseEffect extends OneShotEffect { this.staticText = "Counter target spell if it shares a color with a creature you control"; } - JadedResponseEffect(final JadedResponseEffect effect) { + private JadedResponseEffect(final JadedResponseEffect effect) { super(effect); } @@ -60,13 +58,12 @@ class JadedResponseEffect extends OneShotEffect { if (stackObject == null) { return false; } - ObjectColor creatureColors = new ObjectColor(); - for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURES, source.getControllerId(), game)) { - creatureColors = creatureColors.union(creature.getColor(game)); - if (!creatureColors.intersection(stackObject.getColor(game)).isColorless()) { - return new CounterTargetEffect().apply(game, source); - } - } - return false; + boolean matches = game.getBattlefield() + .getAllActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game + ).stream() + .map(permanent -> permanent.getColor(game)) + .anyMatch(stackObject.getColor(game)::shares); + return matches && game.getStack().counter(stackObject.getId(), source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/cards/j/JandorsRing.java b/Mage.Sets/src/mage/cards/j/JandorsRing.java index c9022834ae..13bfcd8a20 100644 --- a/Mage.Sets/src/mage/cards/j/JandorsRing.java +++ b/Mage.Sets/src/mage/cards/j/JandorsRing.java @@ -37,8 +37,7 @@ public final class JandorsRing extends CardImpl { Watcher watcher = new JandorsRingWatcher(); // {2}, {tap}, Discard the last card you drew this turn: Draw a card. // TODO: discard has to be a cost not a payment during resolution - Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, - new JandorsRingEffect(), new ManaCostsImpl("{2}"), WatchedCardInHandCondition.instance, "Last drawn card still in hand?"); + Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new JandorsRingEffect(), new ManaCostsImpl("{2}"), WatchedCardInHandCondition.instance, "{2}, {T}, Discard the last card you drew this turn: Draw a card."); ability.addCost(new TapSourceCost()); this.addAbility(ability, watcher); } @@ -82,7 +81,7 @@ class JandorsRingEffect extends OneShotEffect { if (effect.apply(game, source)) {//Conditional was already checked, card should be in hand, but if for some weird reason it fails, the card won't be drawn, although the cost will already be paid Player controller = game.getPlayer(source.getControllerId()); if(controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/j/JanglingAutomaton.java b/Mage.Sets/src/mage/cards/j/JanglingAutomaton.java new file mode 100644 index 0000000000..ce649f6691 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JanglingAutomaton.java @@ -0,0 +1,72 @@ +package mage.cards.j; + +import java.util.UUID; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * @author arcox + */ +public final class JanglingAutomaton extends CardImpl { + public JanglingAutomaton(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + this.subtype.add(SubType.CONSTRUCT); + + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever Jangling Automaton attacks, untap all creatures defending player controls. + this.addAbility(new AttacksTriggeredAbility(new JanglingAutomatonEffect(), false)); + } + + public JanglingAutomaton(final JanglingAutomaton card) { + super(card); + } + + @Override + public JanglingAutomaton copy() { + return new JanglingAutomaton(this); + } +} + +class JanglingAutomatonEffect extends OneShotEffect { + public JanglingAutomatonEffect() { + super(Outcome.Untap); + this.staticText = "untap all creatures defending player controls"; + } + + public JanglingAutomatonEffect(final JanglingAutomatonEffect effect) { + super(effect); + } + + @Override + public JanglingAutomatonEffect copy() { + return new JanglingAutomatonEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID defenderId = game.getCombat().getDefendingPlayerId(source.getSourceId(), game); + if (defenderId != null) { + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + filter.add(new ControllerIdPredicate(defenderId)); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + permanent.untap(game); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/j/JayaBallard.java b/Mage.Sets/src/mage/cards/j/JayaBallard.java index 7d7e86c13e..6b0e37e075 100644 --- a/Mage.Sets/src/mage/cards/j/JayaBallard.java +++ b/Mage.Sets/src/mage/cards/j/JayaBallard.java @@ -1,18 +1,16 @@ - package mage.cards.j; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.mana.AddConditionalManaEffect; import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.mana.AddConditionalManaEffect; import mage.abilities.mana.builder.common.InstantOrSorcerySpellManaBuilder; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; @@ -24,8 +22,9 @@ import mage.players.Player; import mage.target.common.TargetDiscard; import mage.watchers.common.CastFromGraveyardWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class JayaBallard extends CardImpl { @@ -76,20 +75,13 @@ class JayaBallardDiscardDrawEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - TargetDiscard target = new TargetDiscard(0, 3, new FilterCard(), controller.getId()); - target.choose(outcome, controller.getId(), source.getSourceId(), game); - int count = 0; - for (UUID cardId : target.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - controller.discard(card, source, game); - count++; - } - } - controller.drawCards(count, game); - return true; + if (controller == null) { + return false; } - return false; + TargetDiscard target = new TargetDiscard(0, 3, new FilterCard(), controller.getId()); + target.choose(outcome, controller.getId(), source.getSourceId(), game); + int count = controller.discard(new CardsImpl(target.getTargets()), source, game).size(); + controller.drawCards(count, source.getSourceId(), game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/j/JediSentinel.java b/Mage.Sets/src/mage/cards/j/JediSentinel.java index d0f1218489..b4802f9787 100644 --- a/Mage.Sets/src/mage/cards/j/JediSentinel.java +++ b/Mage.Sets/src/mage/cards/j/JediSentinel.java @@ -1,7 +1,5 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -12,25 +10,23 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Styxo */ public final class JediSentinel extends CardImpl { private static final FilterControlledCreaturePermanent filter1 = new FilterControlledCreaturePermanent("another target creature you control"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature you don't control"); static { filter1.add(AnotherPredicate.instance); - filter2.add(TargetController.NOT_YOU.getControllerPredicate()); } public JediSentinel(UUID ownerId, CardSetInfo setInfo) { @@ -48,12 +44,11 @@ public final class JediSentinel extends CardImpl { effect.setText("return another target creature you control and target creature you don't control to their owners' hands"); Ability ability = new EntersBattlefieldTriggeredAbility(effect); ability.addTarget(new TargetControlledCreaturePermanent(filter1)); - ability.addTarget(new TargetCreaturePermanent(filter2)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); - } - public JediSentinel(final JediSentinel card) { + private JediSentinel(final JediSentinel card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java b/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java new file mode 100644 index 0000000000..2a8a274919 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java @@ -0,0 +1,137 @@ +package mage.cards.j; + +import mage.ConditionalMana; +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.effects.common.ManaEffect; +import mage.abilities.keyword.CompanionAbility; +import mage.abilities.keyword.CompanionCondition; +import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.conditional.ManaCondition; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class JeganthaTheWellspring extends CardImpl { + + public JeganthaTheWellspring(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R/G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.ELK); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Companion — No card in your starting deck has more than one of the same mana symbol in its mana cost. + this.addAbility(new CompanionAbility(JeganthaTheWellspringCompanionCondition.instance)); + + // {T}: Add {W}{U}{B}{R}{G}. This mana can't be spent to pay generic mana costs. + this.addAbility(new SimpleManaAbility( + Zone.BATTLEFIELD, new JeganthaTheWellspringManaEffect(), new TapSourceCost() + )); + } + + private JeganthaTheWellspring(final JeganthaTheWellspring card) { + super(card); + } + + @Override + public JeganthaTheWellspring copy() { + return new JeganthaTheWellspring(this); + } +} + +enum JeganthaTheWellspringCompanionCondition implements CompanionCondition { + instance; + + @Override + public String getRule() { + return "No card in your starting deck has more than one of the same mana symbol in its mana cost."; + } + + @Override + public boolean isLegal(Set deck, int startingSize) { + return deck.stream().noneMatch(JeganthaTheWellspringCompanionCondition::checkCard); + } + + private static boolean checkCard(Card card) { + Map symbolMap = new HashMap(); + return card.getManaCost() + .getSymbols() + .stream() + .anyMatch(s -> symbolMap.compute( + s, (str, i) -> (i == null) ? 1 : i + 1 + ) > 1); + } +} + +class JeganthaTheWellspringManaEffect extends ManaEffect { + + JeganthaTheWellspringManaEffect() { + super(); + staticText = "Add {W}{U}{B}{R}{G}. This mana can't be spent to pay generic mana costs."; + } + + private JeganthaTheWellspringManaEffect(final JeganthaTheWellspringManaEffect effect) { + super(effect); + } + + @Override + public Mana produceMana(Game game, Ability source) { + Mana mana = new Mana(); + mana.add(new JeganthaTheWellspringConditionalMana("W")); + mana.add(new JeganthaTheWellspringConditionalMana("U")); + mana.add(new JeganthaTheWellspringConditionalMana("B")); + mana.add(new JeganthaTheWellspringConditionalMana("R")); + mana.add(new JeganthaTheWellspringConditionalMana("G")); + return mana; + } + + @Override + public JeganthaTheWellspringManaEffect copy() { + return new JeganthaTheWellspringManaEffect(this); + } +} + +class JeganthaTheWellspringConditionalMana extends ConditionalMana { + + JeganthaTheWellspringConditionalMana(String manaSymbol) { + super(new Mana(ColoredManaSymbol.valueOf(manaSymbol))); + addCondition(new JeganthaTheWellspringManaCondition(manaSymbol)); + } +} + +class JeganthaTheWellspringManaCondition extends ManaCondition { + + private final String manaSymbol; + + JeganthaTheWellspringManaCondition(String manaSymbol) { + this.manaSymbol = manaSymbol.toLowerCase(Locale.ENGLISH); + } + + @Override + public boolean apply(Game game, Ability source, UUID originalId, Cost costToPay) { + if (!(costToPay instanceof ManaCosts)) { + return false; + } + return Arrays.stream( + ((ManaCosts) costToPay) + .getUnpaid() + .getText() + .split("[\\}\\{]") + ).map(s -> s.toLowerCase(Locale.ENGLISH)).anyMatch(s -> s.contains(manaSymbol)); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JeskaiSage.java b/Mage.Sets/src/mage/cards/j/JeskaiSage.java index f5b2c24b15..645dc0bf29 100644 --- a/Mage.Sets/src/mage/cards/j/JeskaiSage.java +++ b/Mage.Sets/src/mage/cards/j/JeskaiSage.java @@ -3,7 +3,7 @@ package mage.cards.j; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.ProwessAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class JeskaiSage extends CardImpl { // Prowess this.addAbility(new ProwessAbility()); // When Jeskai Sage dies, draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } diff --git a/Mage.Sets/src/mage/cards/j/JeweledAmulet.java b/Mage.Sets/src/mage/cards/j/JeweledAmulet.java index 39d7ad556e..9d124d75bd 100644 --- a/Mage.Sets/src/mage/cards/j/JeweledAmulet.java +++ b/Mage.Sets/src/mage/cards/j/JeweledAmulet.java @@ -102,7 +102,7 @@ class JeweledAmuletAddManaEffect extends ManaEffect { JeweledAmuletAddManaEffect(JeweledAmuletAddManaEffect effect) { super(effect); - storedMana = effect.storedMana; + storedMana = effect.storedMana == null ? null : effect.storedMana.copy(); } @Override diff --git a/Mage.Sets/src/mage/cards/j/JirinaKudro.java b/Mage.Sets/src/mage/cards/j/JirinaKudro.java new file mode 100644 index 0000000000..2869c9b253 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JirinaKudro.java @@ -0,0 +1,55 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.CommanderCastCountValue; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.token.HumanSoldierToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JirinaKudro extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.HUMAN, "Humans"); + + public JirinaKudro(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Jirina Kudro enters the battlefield, create a 1/1 white Human Soldier creature token for each time you've cast a commander from the command zone this game. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect( + new HumanSoldierToken(), CommanderCastCountValue.instance + ).setText("create a 1/1 white Human Soldier creature token for each time you've cast a commander from the command zone this game"))); + + // Other Humans you control get +2/+0. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 2, 0, Duration.WhileOnBattlefield, filter, true + ))); + } + + private JirinaKudro(final JirinaKudro card) { + super(card); + } + + @Override + public JirinaKudro copy() { + return new JirinaKudro(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JohtullWurm.java b/Mage.Sets/src/mage/cards/j/JohtullWurm.java index 5b0bb8b2f0..42c171fcce 100644 --- a/Mage.Sets/src/mage/cards/j/JohtullWurm.java +++ b/Mage.Sets/src/mage/cards/j/JohtullWurm.java @@ -3,7 +3,7 @@ package mage.cards.j; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MultipliedValue; import mage.abilities.dynamicvalue.common.BlockedCreatureCount; @@ -31,7 +31,7 @@ public final class JohtullWurm extends CardImpl { DynamicValue blockedCreatureCount = new BlockedCreatureCount("each creature blocking it beyond the first", true); Effect effect = new BoostSourceEffect(new MultipliedValue(blockedCreatureCount, -2), new MultipliedValue(blockedCreatureCount, -1), Duration.EndOfTurn, true); effect.setText("it gets -2/-1 until end of turn for each creature blocking it beyond the first"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public JohtullWurm(final JohtullWurm card) { diff --git a/Mage.Sets/src/mage/cards/j/JolraelMwonvuliRecluse.java b/Mage.Sets/src/mage/cards/j/JolraelMwonvuliRecluse.java new file mode 100644 index 0000000000..deca477cce --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JolraelMwonvuliRecluse.java @@ -0,0 +1,54 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.common.DrawSecondCardTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.GreenCat2Token; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JolraelMwonvuliRecluse extends CardImpl { + + public JolraelMwonvuliRecluse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Whenever you draw your second card each turn, create a 2/2 green Cat creature token. + this.addAbility(new DrawSecondCardTriggeredAbility(new CreateTokenEffect(new GreenCat2Token()), false)); + + // {4}{G}{G}: Until end of turn, creatures you control have base power and toughness X/X, where X is the number of cards in your hand. + this.addAbility(new SimpleActivatedAbility(new SetPowerToughnessAllEffect( + CardsInControllerHandCount.instance, CardsInControllerHandCount.instance, + Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, true + ).setText("until end of turn, creatures you control have base power and toughness X/X, " + + "where X is the number of cards in your hand"), new ManaCostsImpl("{4}{G}{G}"))); + } + + private JolraelMwonvuliRecluse(final JolraelMwonvuliRecluse card) { + super(card); + } + + @Override + public JolraelMwonvuliRecluse copy() { + return new JolraelMwonvuliRecluse(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java b/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java index 0a9910044d..2588b2b5ab 100644 --- a/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java +++ b/Mage.Sets/src/mage/cards/j/JorKadeenThePrevailer.java @@ -1,24 +1,20 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author North */ public final class JorKadeenThePrevailer extends CardImpl { @@ -40,7 +36,9 @@ public final class JorKadeenThePrevailer extends CardImpl { // Metalcraft — Creatures you control get +3/+0 as long as you control three or more artifacts. ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new BoostControlledEffect(3, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE, false), MetalcraftCondition.instance, effectText); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance)); } public JorKadeenThePrevailer(final JorKadeenThePrevailer card) { diff --git a/Mage.Sets/src/mage/cards/j/JoragaInvocation.java b/Mage.Sets/src/mage/cards/j/JoragaInvocation.java index ae428a239c..d250f4f51b 100644 --- a/Mage.Sets/src/mage/cards/j/JoragaInvocation.java +++ b/Mage.Sets/src/mage/cards/j/JoragaInvocation.java @@ -65,7 +65,7 @@ class JoragaInvocationEffect extends OneShotEffect { if (controller != null) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, game)) { ContinuousEffect effect = new MustBeBlockedByAtLeastOneTargetEffect(); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/j/JotunOwlKeeper.java b/Mage.Sets/src/mage/cards/j/JotunOwlKeeper.java index 3fa5faf6fd..0107825f92 100644 --- a/Mage.Sets/src/mage/cards/j/JotunOwlKeeper.java +++ b/Mage.Sets/src/mage/cards/j/JotunOwlKeeper.java @@ -3,7 +3,7 @@ package mage.cards.j; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.OrCost; import mage.constants.SubType; import mage.abilities.costs.mana.ManaCostsImpl; @@ -37,7 +37,7 @@ public final class JotunOwlKeeper extends CardImpl { ))); // When Jötun Owl Keeper dies, put a 1/1 white Bird creature token with flying onto the battlefield for each age counter on it. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new BirdToken(), new CountersSourceCount(CounterType.AGE)))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new BirdToken(), new CountersSourceCount(CounterType.AGE)))); } public JotunOwlKeeper(final JotunOwlKeeper card) { diff --git a/Mage.Sets/src/mage/cards/j/Joust.java b/Mage.Sets/src/mage/cards/j/Joust.java index e5229de750..77b8d33df2 100644 --- a/Mage.Sets/src/mage/cards/j/Joust.java +++ b/Mage.Sets/src/mage/cards/j/Joust.java @@ -6,9 +6,11 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -22,20 +24,13 @@ import java.util.UUID; */ public final class Joust extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public Joust(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); // Choose target creature you control and target creature you don't control. The creature you control gets +2/+1 until end of turn if it's a Knight. Then those creatures fight each other. this.getSpellAbility().addEffect(new JoustEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } private Joust(final Joust card) { @@ -81,7 +76,7 @@ class JoustEffect extends OneShotEffect { if (creature2 == null) { return true; } - game.applyEffects(); + game.getState().processAction(game); return creature1.fight(creature2, source, game); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/j/JubilantSkybonder.java b/Mage.Sets/src/mage/cards/j/JubilantSkybonder.java new file mode 100644 index 0000000000..ee0d7622b2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JubilantSkybonder.java @@ -0,0 +1,62 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JubilantSkybonder extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("creatures you control with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public JubilantSkybonder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W/U}{W/U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Creatures you control with flying have "Spells your opponents cast that target this creature cost {2} more to cast." + Ability gainAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT) + .withTargetName("this creature") + ); + + ContinuousEffect effect = new GainAbilityAllEffect(gainAbility, Duration.WhileOnBattlefield, filter).withForceQuotes(); + effect.setDependedToType(DependencyType.AddingAbility); + this.addAbility(new SimpleStaticAbility(effect)); + } + + private JubilantSkybonder(final JubilantSkybonder card) { + super(card); + } + + @Override + public JubilantSkybonder copy() { + return new JubilantSkybonder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java b/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java index e51b970247..5e5531682b 100644 --- a/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java +++ b/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java @@ -5,7 +5,7 @@ package mage.cards.j; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -33,7 +33,7 @@ public final class JuganTheRisingStar extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Jugan, the Rising Star dies, you may distribute five +1/+1 counters among any number of target creatures. - Ability ability = new DiesTriggeredAbility(new DistributeCountersEffect(CounterType.P1P1, 5, false, "any number of target creatures"), true); + Ability ability = new DiesSourceTriggeredAbility(new DistributeCountersEffect(CounterType.P1P1, 5, false, "any number of target creatures"), true); ability.addTarget(new TargetCreaturePermanentAmount(5)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/j/JundSojourners.java b/Mage.Sets/src/mage/cards/j/JundSojourners.java index 8a57e5939f..6c7964133e 100644 --- a/Mage.Sets/src/mage/cards/j/JundSojourners.java +++ b/Mage.Sets/src/mage/cards/j/JundSojourners.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CycleTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.CyclingAbility; @@ -34,7 +34,7 @@ public final class JundSojourners extends CardImpl { // When you cycle Jund Sojourners or it dies, you may have it deal 1 damage to any target. Ability ability1 = new CycleTriggeredAbility(new DamageTargetEffect(1)); - Ability ability2 = new DiesTriggeredAbility(new DamageTargetEffect(1)); + Ability ability2 = new DiesSourceTriggeredAbility(new DamageTargetEffect(1)); ability1.addTarget(new TargetAnyTarget()); ability2.addTarget(new TargetAnyTarget()); this.addAbility(ability1); diff --git a/Mage.Sets/src/mage/cards/j/JunglePatrol.java b/Mage.Sets/src/mage/cards/j/JunglePatrol.java index 31e9ecade3..b421756526 100644 --- a/Mage.Sets/src/mage/cards/j/JunglePatrol.java +++ b/Mage.Sets/src/mage/cards/j/JunglePatrol.java @@ -9,8 +9,10 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -42,14 +44,14 @@ public final class JunglePatrol extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - // {1}{G}, {tap}: Create a 0/1 green Wall creature token with defender named Wood. + // {1}{G}, {T}: Create a 0/1 green Wall creature token with defender named Wood. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new WoodToken()), new ManaCostsImpl("{1}{G}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); // Sacrifice a token named Wood: Add {R}. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new BasicManaEffect(Mana.RedMana(1)), + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, + new BasicManaEffect(Mana.RedMana(1), new PermanentsOnBattlefieldCount(filter)), new SacrificeTargetCost(new TargetControlledPermanent(1, 1, filter, true)))); } diff --git a/Mage.Sets/src/mage/cards/j/JungleWurm.java b/Mage.Sets/src/mage/cards/j/JungleWurm.java index abb6b248d6..5d885826e7 100644 --- a/Mage.Sets/src/mage/cards/j/JungleWurm.java +++ b/Mage.Sets/src/mage/cards/j/JungleWurm.java @@ -3,7 +3,7 @@ package mage.cards.j; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MultipliedValue; import mage.abilities.dynamicvalue.common.BlockedCreatureCount; @@ -32,7 +32,7 @@ public final class JungleWurm extends CardImpl { DynamicValue value = new MultipliedValue(blockedCreatureCount, -1); Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true); effect.setText("it gets -1/-1 until end of turn for each creature blocking it beyond the first"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public JungleWurm(final JungleWurm card) { diff --git a/Mage.Sets/src/mage/cards/j/JunkDiver.java b/Mage.Sets/src/mage/cards/j/JunkDiver.java index f7b14942e3..9d5d731f6c 100644 --- a/Mage.Sets/src/mage/cards/j/JunkDiver.java +++ b/Mage.Sets/src/mage/cards/j/JunkDiver.java @@ -4,7 +4,7 @@ package mage.cards.j; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.keyword.FlyingAbility; @@ -40,7 +40,7 @@ public final class JunkDiver extends CardImpl { // When Junk Diver dies, return another target artifact card from your graveyard to your hand. Effect effect = new ReturnFromGraveyardToHandTargetEffect(); effect.setText("return another target artifact card from your graveyard to your hand"); - Ability ability = new DiesTriggeredAbility(effect); + Ability ability = new DiesSourceTriggeredAbility(effect); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/j/JusticiarsPortal.java b/Mage.Sets/src/mage/cards/j/JusticiarsPortal.java index 7dfc2956ba..3062e29384 100644 --- a/Mage.Sets/src/mage/cards/j/JusticiarsPortal.java +++ b/Mage.Sets/src/mage/cards/j/JusticiarsPortal.java @@ -62,7 +62,7 @@ class JusticiarsPortalEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { UUID targetId = source.getFirstTarget(); new ExileTargetForSourceEffect().apply(game, source); - new ReturnToBattlefieldUnderYourControlTargetEffect(true).apply(game, source); + new ReturnToBattlefieldUnderYourControlTargetEffect().apply(game, source); ContinuousEffect effect = new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); effect.setTargetPointer(new FixedTarget(targetId, game)); game.addEffect(effect, source); diff --git a/Mage.Sets/src/mage/cards/j/Juxtapose.java b/Mage.Sets/src/mage/cards/j/Juxtapose.java index a5464c16ed..912883598a 100644 --- a/Mage.Sets/src/mage/cards/j/Juxtapose.java +++ b/Mage.Sets/src/mage/cards/j/Juxtapose.java @@ -29,8 +29,8 @@ public final class Juxtapose extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); // You and target player exchange control of the creature you each control with the highest converted mana cost. Then exchange control of artifacts the same way. If two or more permanents a player controls are tied for highest cost, their controller chooses one of them. - this.getSpellAbility().addEffect(new JuxtaposeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, "You and target player exchange control of the creature you each control with the highest converted mana cost.")); - this.getSpellAbility().addEffect(new JuxtaposeEffect(new FilterArtifactPermanent(), "Then exchange control of artifacts the same way. If two or more permanents a player controls are tied for highest cost, their controller chooses one of them.")); + this.getSpellAbility().addEffect(new JuxtaposeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, "You and target player exchange control of the creature you each control with the highest converted mana cost")); + this.getSpellAbility().addEffect(new JuxtaposeEffect(new FilterArtifactPermanent(), "Then exchange control of artifacts the same way. If two or more permanents a player controls are tied for highest cost, their controller chooses one of them")); this.getSpellAbility().addTarget(new TargetPlayer()); } diff --git a/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java b/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java index 1b8349cf81..181c718676 100644 --- a/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java +++ b/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java @@ -5,6 +5,7 @@ import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -14,10 +15,9 @@ import mage.constants.WatcherScope; import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.ObjectPlayer; -import mage.filter.predicate.ObjectPlayerPredicate; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.other.FaceDownCastablePredicate; import mage.filter.predicate.other.FaceDownPredicate; -import mage.game.Controllable; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; @@ -32,12 +32,12 @@ import java.util.UUID; */ public final class KadenaSlinkingSorcerer extends CardImpl { - private static final FilterCard filter = new FilterCard(); - private static final FilterPermanent filter2 = new FilterControlledCreaturePermanent("a face-down creature"); + private static final FilterCard filterFirstFaceDownSpell = new FilterCard("first face-down creature spell"); + private static final FilterPermanent filterFaceDownPermanent = new FilterControlledCreaturePermanent("a face-down creature"); static { - filter.add(KadenaSlinkingSorcererPredicate.instance); - filter2.add(FaceDownPredicate.instance); + filterFirstFaceDownSpell.add(KadenaSlinkingSorcererPredicate.instance); + filterFaceDownPermanent.add(FaceDownPredicate.instance); } public KadenaSlinkingSorcerer(UUID ownerId, CardSetInfo setInfo) { @@ -51,13 +51,13 @@ public final class KadenaSlinkingSorcerer extends CardImpl { // The first face-down creature spell you cast each turn costs {3} less to cast. this.addAbility(new SimpleStaticAbility( - new SpellsCostReductionControllerEffect(filter, 3) + new SpellsCostReductionControllerEffect(filterFirstFaceDownSpell, 3) .setText("The first face-down creature spell you cast each turn costs {3} less to cast.") ), new KadenaSlinkingSorcererWatcher()); // Whenever a face-down creature enters the battlefield under your control, draw a card. this.addAbility(new EntersBattlefieldControlledTriggeredAbility( - new DrawCardSourceControllerEffect(1), filter2 + new DrawCardSourceControllerEffect(1), filterFaceDownPermanent )); } @@ -71,16 +71,15 @@ public final class KadenaSlinkingSorcerer extends CardImpl { } } -enum KadenaSlinkingSorcererPredicate implements ObjectPlayerPredicate> { +enum KadenaSlinkingSorcererPredicate implements Predicate { instance; @Override - public boolean apply(ObjectPlayer input, Game game) { - if (input.getObject() instanceof Spell - && ((Spell) input.getObject()).isCreature() - && ((Spell) input.getObject()).isFaceDown(game)) { - KadenaSlinkingSorcererWatcher watcher = game.getState().getWatcher(KadenaSlinkingSorcererWatcher.class); - return watcher != null && !watcher.castFaceDownThisTurn(input.getPlayerId()); + public boolean apply(Card input, Game game) { + KadenaSlinkingSorcererWatcher watcher = game.getState().getWatcher(KadenaSlinkingSorcererWatcher.class); + if (watcher != null) { + return FaceDownCastablePredicate.instance.apply(input, game) + && !watcher.castFaceDownThisTurn(input.getOwnerId()); } return false; } diff --git a/Mage.Sets/src/mage/cards/k/KaervekTheSpiteful.java b/Mage.Sets/src/mage/cards/k/KaervekTheSpiteful.java new file mode 100644 index 0000000000..493dd68b22 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KaervekTheSpiteful.java @@ -0,0 +1,43 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KaervekTheSpiteful extends CardImpl { + + public KaervekTheSpiteful(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Other creatures get -1/-1. + this.addAbility(new SimpleStaticAbility(new BoostAllEffect( + -1, -1, Duration.WhileOnBattlefield, true + ))); + } + + private KaervekTheSpiteful(final KaervekTheSpiteful card) { + super(card); + } + + @Override + public KaervekTheSpiteful copy() { + return new KaervekTheSpiteful(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KaerveksPurge.java b/Mage.Sets/src/mage/cards/k/KaerveksPurge.java index 19b5c69c3e..4eabe29bd9 100644 --- a/Mage.Sets/src/mage/cards/k/KaerveksPurge.java +++ b/Mage.Sets/src/mage/cards/k/KaerveksPurge.java @@ -78,7 +78,7 @@ class KaerveksPurgeEffect extends OneShotEffect { // Destroy target creature with converted mana cost X. Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); if (targetCreature != null && targetCreature.destroy(source.getSourceId(), game, false)) { - game.applyEffects(); + game.getState().processAction(game); if (targetCreature.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(targetCreature.getId()) && game.getState().getZone(targetCreature.getId()) != Zone.GRAVEYARD) { // A replacement effect has moved the card to another zone as graveyard diff --git a/Mage.Sets/src/mage/cards/k/KaerveksSpite.java b/Mage.Sets/src/mage/cards/k/KaerveksSpite.java index f38414c79f..fe2406d23e 100644 --- a/Mage.Sets/src/mage/cards/k/KaerveksSpite.java +++ b/Mage.Sets/src/mage/cards/k/KaerveksSpite.java @@ -1,30 +1,34 @@ package mage.cards.k; -import java.util.UUID; import mage.abilities.costs.common.DiscardHandCost; import mage.abilities.costs.common.SacrificeAllCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; +import mage.target.TargetPlayer; + +import java.util.UUID; public final class KaerveksSpite extends CardImpl { + private static final FilterPermanent filter = new FilterControlledPermanent("permanents you control"); + public KaerveksSpite(UUID ownerId, CardSetInfo cardSetInfo) { super(ownerId, cardSetInfo, new CardType[]{CardType.INSTANT}, "{B}{B}{B}"); // As an additional cost to cast Kaervek's Spite, sacrifice all permanents you control and discard your hand. - this.getSpellAbility().addCost(new SacrificeAllCost(new FilterControlledPermanent("permanents you control"))); + this.getSpellAbility().addCost(new SacrificeAllCost(filter)); this.getSpellAbility().addCost(new DiscardHandCost()); - // Target player loses 5 life. - Effect effect = new LoseLifeTargetEffect(5); - this.getSpellAbility().addEffect(effect); + // Target player loses 5 life.= + this.getSpellAbility().addEffect(new LoseLifeTargetEffect(5)); + this.getSpellAbility().addTarget(new TargetPlayer()); } - public KaerveksSpite(final KaerveksSpite other) { + private KaerveksSpite(final KaerveksSpite other) { super(other); } diff --git a/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java b/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java new file mode 100644 index 0000000000..6f9386979f --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java @@ -0,0 +1,104 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.CompanionAbility; +import mage.abilities.keyword.CompanionCondition; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.util.SubTypeList; + +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KaheeraTheOrphanguard extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(AnotherPredicate.instance); + filter.add(Predicates.or( + SubType.CAT.getPredicate(), + SubType.ELEMENTAL.getPredicate(), + SubType.NIGHTMARE.getPredicate(), + SubType.DINOSAUR.getPredicate(), + SubType.BEAST.getPredicate() + )); + } + + public KaheeraTheOrphanguard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G/W}{G/W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Companion — Each creature card in your starting deck is a Cat, Elemental, Nightmare, Dinosaur or Beast card. + this.addAbility(new CompanionAbility(KaheeraTheOrphanguardCompanionCondition.instance)); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Each other creature you control that's a Cat, Elemental, Nightmare, Dinosaur, or Beast gets +1/+1 and has vigilance. + Ability ability = new SimpleStaticAbility(new BoostAllEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + ).setText("Each other creature you control that's a Cat, Elemental, Nightmare, Dinosaur or Beast gets +1/+1")); + ability.addEffect(new GainAbilityAllEffect( + VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, filter, "and has vigilance" + )); + this.addAbility(ability); + } + + private KaheeraTheOrphanguard(final KaheeraTheOrphanguard card) { + super(card); + } + + @Override + public KaheeraTheOrphanguard copy() { + return new KaheeraTheOrphanguard(this); + } +} + +enum KaheeraTheOrphanguardCompanionCondition implements CompanionCondition { + instance; + + @Override + public String getRule() { + return "Each creature card in your starting deck is a Cat, Elemental, Nightmare, Dinosaur or Beast card."; + } + + private static final SubTypeList subtypes = new SubTypeList( + SubType.CAT, + SubType.ELEMENTAL, + SubType.NIGHTMARE, + SubType.DINOSAUR, + SubType.BEAST + ); + + private static boolean checkTypes(Card card) { + return subtypes.stream().anyMatch(subtype -> card.hasSubtype(subtype, null)); + } + + @Override + public boolean isLegal(Set deck, int startingSize) { + return deck.stream() + .filter(MageObject::isCreature) + .allMatch(KaheeraTheOrphanguardCompanionCondition::checkTypes); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KalamaxTheStormsire.java b/Mage.Sets/src/mage/cards/k/KalamaxTheStormsire.java new file mode 100644 index 0000000000..99ca98fd8f --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KalamaxTheStormsire.java @@ -0,0 +1,137 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CopyTargetSpellEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterInstantSpell; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.List; +import java.util.UUID; + +/** + * @author AsterAether + */ +public final class KalamaxTheStormsire extends CardImpl { + + public KalamaxTheStormsire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever you cast your first instant spell each turn, if Kalamax, the Stormsire is tapped, copy that spell. You may choose new targets for the copy. + this.addAbility(new KalamaxTheStormsireSpellCastAbility(), new SpellsCastWatcher()); + // Whenever you copy an instant spell, put a +1/+1 counter on Kalamax. + this.addAbility(new KalamaxTheStormsireCopyTriggeredAbility()); + } + + private KalamaxTheStormsire(final KalamaxTheStormsire card) { + super(card); + } + + @Override + public KalamaxTheStormsire copy() { + return new KalamaxTheStormsire(this); + } +} + +class KalamaxTheStormsireSpellCastAbility extends SpellCastControllerTriggeredAbility { + KalamaxTheStormsireSpellCastAbility() { + super(new CopyTargetSpellEffect(true), new FilterInstantSpell(), false); + } + + KalamaxTheStormsireSpellCastAbility(KalamaxTheStormsireSpellCastAbility ability) { + super(ability); + } + + @Override + public KalamaxTheStormsireSpellCastAbility copy() { + return new KalamaxTheStormsireSpellCastAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (super.checkTrigger(event, game)) { + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + if (watcher != null) { + List spells = watcher.getSpellsCastThisTurn(event.getPlayerId()); + if (spells != null && spells.stream().filter(MageObject::isInstant).count() == 1) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && spell.isInstant()) { + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getTargetId())); + } + return true; + } + } + } + } + return false; + } + + @Override + public boolean checkInterveningIfClause(Game game) { + Permanent permanent = game.getPermanent(getSourceId()); + return permanent != null && permanent.isTapped(); + } + + @Override + public String getRule() { + return "Whenever you cast your first instant spell each turn, " + + "if Kalamax, the Stormsire is tapped, " + + "copy that spell. You may choose new targets for the copy."; + } +} + +class KalamaxTheStormsireCopyTriggeredAbility extends TriggeredAbilityImpl { + + KalamaxTheStormsireCopyTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); + } + + private KalamaxTheStormsireCopyTriggeredAbility(final KalamaxTheStormsireCopyTriggeredAbility effect) { + super(effect); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COPIED_STACKOBJECT; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Spell spell = game.getSpell(event.getTargetId()); + return spell != null && spell.isControlledBy(getControllerId()) && spell.isInstant(); + } + + @Override + public KalamaxTheStormsireCopyTriggeredAbility copy() { + return new KalamaxTheStormsireCopyTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever you copy an instant spell, put a +1/+1 counter on {this}."; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/k/KalastriaHighborn.java b/Mage.Sets/src/mage/cards/k/KalastriaHighborn.java index 01beb96f7e..f04e289d06 100644 --- a/Mage.Sets/src/mage/cards/k/KalastriaHighborn.java +++ b/Mage.Sets/src/mage/cards/k/KalastriaHighborn.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -40,6 +39,7 @@ public final class KalastriaHighborn extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); + // Whenever Kalastria Highborn or another Vampire you control dies, you may pay {B}. If you do, target player loses 2 life and you gain 2 life. Ability ability = new DiesThisOrAnotherCreatureTriggeredAbility(new DoIfCostPaid(new LoseGainEffect(), new ManaCostsImpl("{B}")), false, filter); ability.addTarget(new TargetPlayer()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/k/KapshoKitefins.java b/Mage.Sets/src/mage/cards/k/KapshoKitefins.java index db6aa7c954..da025e429c 100644 --- a/Mage.Sets/src/mage/cards/k/KapshoKitefins.java +++ b/Mage.Sets/src/mage/cards/k/KapshoKitefins.java @@ -1,37 +1,26 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.StaticFilters; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class KapshoKitefins extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("{this} or another creature"); - private static final FilterCreaturePermanent filterTarget = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - filterTarget.add(TargetController.OPPONENT.getControllerPredicate()); - } - public KapshoKitefins(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); this.subtype.add(SubType.FISH); this.color.setBlue(true); @@ -40,14 +29,16 @@ public final class KapshoKitefins extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Whenever Kapsho Kitefins or another creature enters the battlefield under your control, tap target creature an opponent controls. - Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new TapTargetEffect(), filter, false); - ability.addTarget(new TargetCreaturePermanent(filterTarget)); - this.addAbility(ability); + // Whenever Kapsho Kitefins or another creature enters the battlefield under your control, tap target creature an opponent controls. + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new TapTargetEffect(), StaticFilters.FILTER_PERMANENT_CREATURE, false, true + ); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); } - public KapshoKitefins(final KapshoKitefins card) { + private KapshoKitefins(final KapshoKitefins card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java b/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java index b5ac0e5a96..f5345fe00d 100644 --- a/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java +++ b/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java @@ -38,7 +38,7 @@ public final class KaradorGhostChieftain extends CardImpl { this.toughness = new MageInt(4); // Karador, Ghost Chieftain costs {1} less to cast for each creature card in your graveyard. - this.addAbility(new SimpleStaticAbility(Zone.STACK, + this.addAbility(new SimpleStaticAbility(Zone.ALL, new KaradorGhostChieftainCostReductionEffect())); // During each of your turns, you may cast one creature card from your graveyard. diff --git a/Mage.Sets/src/mage/cards/k/KariZevsExpertise.java b/Mage.Sets/src/mage/cards/k/KariZevsExpertise.java index e827b9021c..cf095b415f 100644 --- a/Mage.Sets/src/mage/cards/k/KariZevsExpertise.java +++ b/Mage.Sets/src/mage/cards/k/KariZevsExpertise.java @@ -36,7 +36,7 @@ public final class KariZevsExpertise extends CardImpl { this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap it")); - this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn).setText("It gans haste until end of turn.")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn).setText("It gains haste until end of turn")); // You may cast a card with converted mana cost 2 or less from your hand without paying its mana cost. this.getSpellAbility().addEffect(new CastWithoutPayingManaCostEffect(2)); diff --git a/Mage.Sets/src/mage/cards/k/KarnLiberated.java b/Mage.Sets/src/mage/cards/k/KarnLiberated.java index 662cc84fa1..45a6c7b1aa 100644 --- a/Mage.Sets/src/mage/cards/k/KarnLiberated.java +++ b/Mage.Sets/src/mage/cards/k/KarnLiberated.java @@ -1,8 +1,5 @@ package mage.cards.k; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; @@ -26,6 +23,10 @@ import mage.target.TargetPlayer; import mage.target.common.TargetCardInHand; import mage.util.CardUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** * @author bunchOfDevs */ @@ -100,7 +101,7 @@ class KarnLiberatedEffect extends OneShotEffect { game.getState().addCard(card); } for (Player player : game.getPlayers().values()) { - if (player.isInGame()) { // only players alive are in the restarted game + if (player.canRespond()) { // only players alive are in the restarted game player.getGraveyard().clear(); player.getHand().clear(); player.getLibrary().clear(); diff --git a/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java b/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java index b42cf9d604..db1fcbb798 100644 --- a/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java +++ b/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java @@ -6,6 +6,7 @@ import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.*; import mage.constants.*; import mage.counters.CounterType; @@ -19,7 +20,6 @@ import mage.target.Target; import mage.target.TargetCard; import mage.target.common.TargetOpponent; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; @@ -46,6 +46,7 @@ public final class KarnScionOfUrza extends CardImpl { // -2: Create a 0/0 colorless Construct artifact creature token with "This creature gets +1/+1 for each artifact you control." LoyaltyAbility ability3 = new LoyaltyAbility(new KarnConstructEffect(), -2); + ability3.addHint(ArtifactYouControlHint.instance); this.addAbility(ability3); } diff --git a/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java b/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java index 57e69f971b..141991d4e4 100644 --- a/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java +++ b/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java @@ -4,7 +4,7 @@ package mage.cards.k; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffectImpl; @@ -38,7 +38,7 @@ public final class KarnSilverGolem extends CardImpl { this.toughness = new MageInt(4); // Whenever Karn, Silver Golem blocks or becomes blocked, it gets -4/+4 until end of turn. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(-4, +4, Duration.EndOfTurn), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(-4, +4, Duration.EndOfTurn), false)); // {1}: Target noncreature artifact becomes an artifact creature with power and toughness each equal to its converted mana cost until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new KarnSilverGolemEffect(), new ManaCostsImpl("{1")); diff --git a/Mage.Sets/src/mage/cards/k/KarplusanHound.java b/Mage.Sets/src/mage/cards/k/KarplusanHound.java index 1d0077baa7..62149c4f00 100644 --- a/Mage.Sets/src/mage/cards/k/KarplusanHound.java +++ b/Mage.Sets/src/mage/cards/k/KarplusanHound.java @@ -24,7 +24,7 @@ public final class KarplusanHound extends CardImpl { public KarplusanHound(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); FilterPlaneswalkerPermanent filter = new FilterPlaneswalkerPermanent("a Chandra planeswalker"); diff --git a/Mage.Sets/src/mage/cards/k/KarplusanWolverine.java b/Mage.Sets/src/mage/cards/k/KarplusanWolverine.java index daa070e71a..d6e8cec8d3 100644 --- a/Mage.Sets/src/mage/cards/k/KarplusanWolverine.java +++ b/Mage.Sets/src/mage/cards/k/KarplusanWolverine.java @@ -4,7 +4,7 @@ package mage.cards.k; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -28,7 +28,7 @@ public final class KarplusanWolverine extends CardImpl { this.toughness = new MageInt(1); // Whenever Karplusan Wolverine becomes blocked, you may have it deal 1 damage to any target. - Ability ability = new BecomesBlockedTriggeredAbility(new DamageTargetEffect(1), true); + Ability ability = new BecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(1), true); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KasminaEnigmaticMentor.java b/Mage.Sets/src/mage/cards/k/KasminaEnigmaticMentor.java index 21db376b54..fb2e41862b 100644 --- a/Mage.Sets/src/mage/cards/k/KasminaEnigmaticMentor.java +++ b/Mage.Sets/src/mage/cards/k/KasminaEnigmaticMentor.java @@ -2,7 +2,6 @@ package mage.cards.k; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; @@ -15,9 +14,11 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.WizardToken; -import mage.target.Target; +import mage.game.stack.Spell; import mage.util.CardUtil; +import java.util.Objects; +import java.util.Set; import java.util.UUID; /** @@ -33,7 +34,7 @@ public final class KasminaEnigmaticMentor extends CardImpl { this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); // Spells your opponents cast that target a creature or planeswalker you control cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(new KasminaEnigmaticMentorCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(new KasminaEnigmaticMentorCostModificationEffect())); // -2: Create a 2/2 blue Wizard creature token. Draw a card, then discard a card. Ability ability = new LoyaltyAbility(new CreateTokenEffect(new WizardToken()), -2); @@ -53,49 +54,66 @@ public final class KasminaEnigmaticMentor extends CardImpl { } } -class KasminaEnigmaticMentorCostReductionEffect extends CostModificationEffectImpl { +class KasminaEnigmaticMentorCostModificationEffect extends CostModificationEffectImpl { - KasminaEnigmaticMentorCostReductionEffect() { + KasminaEnigmaticMentorCostModificationEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); staticText = "Spells your opponents cast that target a creature or planeswalker you control cost {2} more to cast"; } - private KasminaEnigmaticMentorCostReductionEffect(KasminaEnigmaticMentorCostReductionEffect effect) { + private KasminaEnigmaticMentorCostModificationEffect(KasminaEnigmaticMentorCostModificationEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -2); + CardUtil.increaseCost(abilityToModify, 2); return true; } @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify.getAbilityType() != AbilityType.SPELL - || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { + if (!(abilityToModify instanceof SpellAbility)) { return false; } - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - Permanent permanent = game.getPermanent(targetUUID); - if (permanent != null - && (permanent.isCreature() || permanent.isPlaneswalker()) - && permanent.isControlledBy(source.getControllerId())) { - return true; - } - } - } + + if (!game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { + return false; } - return false; + + Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); + Set allTargets; + if (spell != null) { + // real cast + allTargets = CardUtil.getAllSelectedTargets(abilityToModify, game); + } else { + // playable + allTargets = CardUtil.getAllPossibleTargets(abilityToModify, game); + + // can target without cost increase + if (allTargets.stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> !isTargetCompatible(permanent, source))) { + return false; + } + ; + } + + return allTargets.stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> isTargetCompatible(permanent, source)); + } + + private boolean isTargetCompatible(Permanent permanent, Ability source) { + // target a creature or planeswalker you control + return permanent.isControlledBy(source.getControllerId()) + && (permanent.isCreature() || permanent.isPlaneswalker()); } @Override - public KasminaEnigmaticMentorCostReductionEffect copy() { - return new KasminaEnigmaticMentorCostReductionEffect(this); + public KasminaEnigmaticMentorCostModificationEffect copy() { + return new KasminaEnigmaticMentorCostModificationEffect(this); } - } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/k/KatabaticWinds.java b/Mage.Sets/src/mage/cards/k/KatabaticWinds.java index 847459cd0f..3f166e61b9 100644 --- a/Mage.Sets/src/mage/cards/k/KatabaticWinds.java +++ b/Mage.Sets/src/mage/cards/k/KatabaticWinds.java @@ -3,7 +3,6 @@ package mage.cards.k; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.RestrictionEffect; import mage.abilities.keyword.FlyingAbility; @@ -119,14 +118,12 @@ class KatabaticWindsRuleModifyingEffect extends ContinuousRuleModifyingEffectImp public boolean applies(GameEvent event, Ability source, Game game) { MageObject object = game.getObject(event.getSourceId()); Optional ability = game.getAbility(event.getTargetId(), event.getSourceId()); - if (ability.isPresent() + return ability.isPresent() && object != null && object.isCreature() && object.getAbilities().contains(FlyingAbility.getInstance()) - && game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId())) { - return ability.get().getCosts().stream().anyMatch((cost) -> (cost instanceof TapSourceCost)); - } - return false; + && game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) + && ability.get().hasTapCost(); } @Override diff --git a/Mage.Sets/src/mage/cards/k/KathrilAspectWarper.java b/Mage.Sets/src/mage/cards/k/KathrilAspectWarper.java new file mode 100644 index 0000000000..651bf742e3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KathrilAspectWarper.java @@ -0,0 +1,163 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledPermanent; + +import java.util.Collection; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class KathrilAspectWarper extends CardImpl { + + public KathrilAspectWarper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.INSECT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Kathril, Aspect Warper enters the battlefield, put a flying counter on any creature you control if a creature card in your graveyard has flying. Repeat this process for first strike, double strike, deathtouch, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance. Then put a +1/+1 counter on Kathril for each counter put on a creature this way. + this.addAbility(new EntersBattlefieldTriggeredAbility(new KathrilAspectWarperEffect())); + } + + private KathrilAspectWarper(final KathrilAspectWarper card) { + super(card); + } + + @Override + public KathrilAspectWarper copy() { + return new KathrilAspectWarper(this); + } +} + +class KathrilAspectWarperEffect extends OneShotEffect { + + KathrilAspectWarperEffect() { + super(Outcome.Benefit); + staticText = "put a flying counter on any creature you control if a creature card in your graveyard has flying. " + + "Repeat this process for first strike, double strike, deathtouch, hexproof, " + + "indestructible, lifelink, menace, reach, trample, and vigilance. " + + "Then put a +1/+1 counter on {this} for each counter put on a creature this way."; + } + + private KathrilAspectWarperEffect(final KathrilAspectWarperEffect effect) { + super(effect); + } + + @Override + public KathrilAspectWarperEffect copy() { + return new KathrilAspectWarperEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || game.getBattlefield().countAll( + StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game + ) == 0) { + return false; + } + Set counterSet = player + .getGraveyard() + .getCards(StaticFilters.FILTER_CARD_CREATURE, game) + .stream() + .map(Card::getAbilities) + .flatMap(Collection::stream) + .map(this::checkAbility) + .collect(Collectors.toSet()); + if (counterSet == null || counterSet.size() == 0) { + return false; + } + int countersAdded = 0; + for (CounterType counterType : counterSet) { + if (counterType == null) { + continue; + } + FilterControlledPermanent filter + = new FilterControlledCreaturePermanent("creature to give a " + counterType + " counter"); + Target target = new TargetControlledPermanent(filter); + target.setNotTarget(true); + if (!player.choose(outcome, target, source.getSourceId(), game)) { + continue; + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + continue; + } + if (permanent.addCounters(counterType.createInstance(), source, game)) { + countersAdded++; + } + } + if (countersAdded == 0) { + return false; + } + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return true; + } + permanent.addCounters(CounterType.P1P1.createInstance(countersAdded), source, game); + return true; + } + + private CounterType checkAbility(Ability ability) { + if (ability instanceof FlyingAbility) { + return CounterType.FLYING; + } + if (ability instanceof FirstStrikeAbility) { + return CounterType.FIRST_STRIKE; + } + if (ability instanceof DoubleStrikeAbility) { + return CounterType.DOUBLE_STRIKE; + } + if (ability instanceof DeathtouchAbility) { + return CounterType.DEATHTOUCH; + } + if (ability instanceof HexproofBaseAbility) { + return CounterType.HEXPROOF; + } + if (ability instanceof IndestructibleAbility) { + return CounterType.INDESTRUCTIBLE; + } + if (ability instanceof LifelinkAbility) { + return CounterType.LIFELINK; + } + if (ability instanceof MenaceAbility) { + return CounterType.MENACE; + } + if (ability instanceof ReachAbility) { + return CounterType.REACH; + } + if (ability instanceof TrampleAbility) { + return CounterType.TRAMPLE; + } + if (ability instanceof VigilanceAbility) { + return CounterType.VIGILANCE; + } + return null; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/k/KayaGhostAssassin.java b/Mage.Sets/src/mage/cards/k/KayaGhostAssassin.java index 62b4c1298b..ce729a92a6 100644 --- a/Mage.Sets/src/mage/cards/k/KayaGhostAssassin.java +++ b/Mage.Sets/src/mage/cards/k/KayaGhostAssassin.java @@ -1,7 +1,5 @@ - package mage.cards.k; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; @@ -15,12 +13,7 @@ import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetE import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -28,8 +21,9 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author fireshoes */ public final class KayaGhostAssassin extends CardImpl { @@ -37,7 +31,7 @@ public final class KayaGhostAssassin extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("target creature to exile. Choose no targets to exile Kaya."); public KayaGhostAssassin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{2}{W}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{W}{B}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KAYA); @@ -101,7 +95,7 @@ class KayaGhostAssassinEffect extends OneShotEffect { if (targetCreature != null) { int zcc = targetCreature.getZoneChangeCounter(game); if (controller.moveCards(targetCreature, Zone.EXILED, source, game)) { - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setTargetPointer(new FixedTarget(targetCreature.getId(), zcc + 1)); AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility(effect); @@ -111,12 +105,12 @@ class KayaGhostAssassinEffect extends OneShotEffect { } else { int zcc = sourcePermanent.getZoneChangeCounter(game); if (controller.moveCards(sourcePermanent, Zone.EXILED, source, game)) { - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); - effect.setTargetPointer(new FixedTarget(sourcePermanent.getId(), zcc + 1)); - AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility delayedAbility - = new AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility(effect); - game.addDelayedTriggeredAbility(delayedAbility, source); - } + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); + effect.setTargetPointer(new FixedTarget(sourcePermanent.getId(), zcc + 1)); + AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility delayedAbility + = new AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility(effect); + game.addDelayedTriggeredAbility(delayedAbility, source); + } } controller.loseLife(2, game, false); return true; diff --git a/Mage.Sets/src/mage/cards/k/KeenGlidemaster.java b/Mage.Sets/src/mage/cards/k/KeenGlidemaster.java new file mode 100644 index 0000000000..9205f83928 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KeenGlidemaster.java @@ -0,0 +1,47 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KeenGlidemaster extends CardImpl { + + public KeenGlidemaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // {2}{U}: Target creature gains flying until end of turn. + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl("{2}{U}")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private KeenGlidemaster(final KeenGlidemaster card) { + super(card); + } + + @Override + public KeenGlidemaster copy() { + return new KeenGlidemaster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KeeningStone.java b/Mage.Sets/src/mage/cards/k/KeeningStone.java index 9ed4d7fbec..11a4de0547 100644 --- a/Mage.Sets/src/mage/cards/k/KeeningStone.java +++ b/Mage.Sets/src/mage/cards/k/KeeningStone.java @@ -1,7 +1,6 @@ package mage.cards.k; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -11,22 +10,22 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author North */ public final class KeeningStone extends CardImpl { public KeeningStone(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{6}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}"); // {5}, {tap}: Target player puts the top X cards of their library into their graveyard, where X is the number of cards in that player's graveyard. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new KeeningStoneEffect(), new GenericManaCost(5)); + Ability ability = new SimpleActivatedAbility(new KeeningStoneEffect(), new GenericManaCost(5)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetPlayer()); this.addAbility(ability); @@ -46,7 +45,7 @@ class KeeningStoneEffect extends OneShotEffect { public KeeningStoneEffect() { super(Outcome.Neutral); - this.staticText = "Target player puts the top X cards of their library into their graveyard, where X is the number of cards in that player's graveyard"; + this.staticText = "Target player mills X cards, where X is the number of cards in that player's graveyard"; } public KeeningStoneEffect(final KeeningStoneEffect effect) { @@ -62,7 +61,7 @@ class KeeningStoneEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { - player.moveCards(player.getLibrary().getTopCards(game, player.getGraveyard().size()), Zone.GRAVEYARD, source, game); + player.millCards(player.getGraveyard().size(), source, game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/k/KeensightMentor.java b/Mage.Sets/src/mage/cards/k/KeensightMentor.java new file mode 100644 index 0000000000..d43a187a7c --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KeensightMentor.java @@ -0,0 +1,71 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KeensightMentor extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("non-Human creature you control"); + private static final FilterPermanent filter2 + = new FilterControlledCreaturePermanent("creature you control with vigilance"); + + static { + filter.add(Predicates.not(SubType.HUMAN.getPredicate())); + filter2.add(new AbilityPredicate(VigilanceAbility.class)); + } + + public KeensightMentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // When Keensight Mentor enters the battlefield, put a vigilance counter on target non-Human creature you control. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.VIGILANCE.createInstance()) + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {1}{W}, {T}: Put a +1/+1 counter on each creature you control with vigilance. + ability = new SimpleActivatedAbility( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter2), new ManaCostsImpl("{1}{W}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private KeensightMentor(final KeensightMentor card) { + super(card); + } + + @Override + public KeensightMentor copy() { + return new KeensightMentor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KeepSafe.java b/Mage.Sets/src/mage/cards/k/KeepSafe.java new file mode 100644 index 0000000000..997a355c03 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KeepSafe.java @@ -0,0 +1,45 @@ +package mage.cards.k; + +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.filter.predicate.other.TargetsPermanentPredicate; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KeepSafe extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("spell that targets a permanent you control"); + + static { + filter.add(new TargetsPermanentPredicate(StaticFilters.FILTER_CONTROLLED_PERMANENT)); + } + + public KeepSafe(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Counter target spell that targets a permanent you control. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell(filter)); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + } + + private KeepSafe(final KeepSafe card) { + super(card); + } + + @Override + public KeepSafe copy() { + return new KeepSafe(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KeeperOfTheFlame.java b/Mage.Sets/src/mage/cards/k/KeeperOfTheFlame.java index b70d3feaea..7f77349e93 100644 --- a/Mage.Sets/src/mage/cards/k/KeeperOfTheFlame.java +++ b/Mage.Sets/src/mage/cards/k/KeeperOfTheFlame.java @@ -1,79 +1,79 @@ -package mage.cards.k; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.filter.FilterOpponent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetPlayer; - -/** - * - * @author jeffwadsworth - */ -public final class KeeperOfTheFlame extends CardImpl { - - private static final FilterOpponent filter = new FilterOpponent(); - - static { - filter.add(new KeeperOfTheFlamePredicate()); - } - - public KeeperOfTheFlame(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{R}"); - - this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.WIZARD); - this.power = new MageInt(1); - this.toughness = new MageInt(2); - - // {R}, {tap}: Choose target opponent who had more life than you did as you activated this ability. Keeper of the Flame deals 2 damage to that player. - Ability ability = new SimpleActivatedAbility( - new DamageTargetEffect(2).setText("Choose target opponent who had more life than you did as you activated this ability. {this} deals 2 damage to that player"), - new ManaCostsImpl("{R}")); - ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetPlayer(1, 1, false, filter)); - this.addAbility(ability); - - } - - public KeeperOfTheFlame(final KeeperOfTheFlame card) { - super(card); - } - - @Override - public KeeperOfTheFlame copy() { - return new KeeperOfTheFlame(this); - } -} - -class KeeperOfTheFlamePredicate implements ObjectSourcePlayerPredicate> { - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - Player targetOpponent = input.getObject(); - Player controller = game.getPlayer(input.getPlayerId()); - if (targetOpponent == null - || controller == null - || !controller.hasOpponent(targetOpponent.getId(), game)) { - return false; - } - return targetOpponent.getLife() > controller.getLife(); - } - - @Override - public String toString() { - return "opponent who had more life than you did as you activated this ability"; - } -} +package mage.cards.k; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterOpponent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; + +/** + * + * @author jeffwadsworth + */ +public final class KeeperOfTheFlame extends CardImpl { + + private static final FilterOpponent filter = new FilterOpponent(); + + static { + filter.add(new KeeperOfTheFlamePredicate()); + } + + public KeeperOfTheFlame(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {R}, {tap}: Choose target opponent who had more life than you did as you activated this ability. Keeper of the Flame deals 2 damage to that player. + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(2).setText("Choose target opponent who had more life than you did as you activated this ability. {this} deals 2 damage to that player"), + new ManaCostsImpl("{R}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetPlayer(1, 1, false, filter)); + this.addAbility(ability); + + } + + public KeeperOfTheFlame(final KeeperOfTheFlame card) { + super(card); + } + + @Override + public KeeperOfTheFlame copy() { + return new KeeperOfTheFlame(this); + } +} + +class KeeperOfTheFlamePredicate implements ObjectSourcePlayerPredicate> { + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Player targetOpponent = input.getObject(); + Player controller = game.getPlayer(input.getPlayerId()); + if (targetOpponent == null + || controller == null + || !controller.hasOpponent(targetOpponent.getId(), game)) { + return false; + } + return targetOpponent.getLife() > controller.getLife(); + } + + @Override + public String toString() { + return "opponent who had more life than you did as you activated this ability"; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KeeperOfTheMind.java b/Mage.Sets/src/mage/cards/k/KeeperOfTheMind.java index eda3e048b1..a815bda398 100644 --- a/Mage.Sets/src/mage/cards/k/KeeperOfTheMind.java +++ b/Mage.Sets/src/mage/cards/k/KeeperOfTheMind.java @@ -1,126 +1,126 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ -package mage.cards.k; - -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.filter.FilterOpponent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetPlayer; -import mage.target.targetadjustment.TargetAdjuster; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public class KeeperOfTheMind extends CardImpl { - - public KeeperOfTheMind(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{U}"); - - this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.WIZARD); - this.power = new MageInt(1); - this.toughness = new MageInt(2); - - // {U}, {tap}: Choose target opponent who had at least two more cards in hand than you did as you activated this ability. Draw a card. - Effect effect = new DrawCardSourceControllerEffect(1); - effect.setText("Choose target opponent who had at least two more cards in hand than you did as you activated this ability. Draw a card."); - Ability ability = new SimpleActivatedAbility(effect, new ManaCostsImpl("{U}")); - ability.addCost(new TapSourceCost()); - ability.setTargetAdjuster(KeeperOfTheMindAdjuster.instance); - this.addAbility(ability); - } - - public KeeperOfTheMind(final KeeperOfTheMind card) { - super(card); - } - - @Override - public KeeperOfTheMind copy() { - return new KeeperOfTheMind(this); - } -} - -enum KeeperOfTheMindAdjuster implements TargetAdjuster { - instance; - - private static final FilterOpponent filter = new FilterOpponent(); - - static { - filter.add(new KeeperOfTheMindPredicate()); - } - - @Override - public void adjustTargets(Ability ability, Game game) { - Player activePlayer = game.getPlayer(game.getActivePlayerId()); - if (activePlayer == null) { - return; - } - ability.getTargets().clear(); - TargetPlayer target = new TargetPlayer(1, 1, false, filter); - target.setTargetController(activePlayer.getId()); - ability.addTarget(target); - } -} - -class KeeperOfTheMindPredicate implements ObjectSourcePlayerPredicate> { - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - Player targetPlayer = input.getObject(); - Player firstPlayer = game.getPlayer(game.getActivePlayerId()); - if (targetPlayer == null - || firstPlayer == null - || !firstPlayer.hasOpponent(targetPlayer.getId(), game)) { - return false; - } - int countHandTargetPlayer = targetPlayer.getHand().size(); - int countHandFirstPlayer = firstPlayer.getHand().size(); - - return countHandTargetPlayer - 2 >= countHandFirstPlayer; - } - - @Override - public String toString() { - return "opponent who had at least two more cards in hand than you did as you activated this ability"; - } -} +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterOpponent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public class KeeperOfTheMind extends CardImpl { + + public KeeperOfTheMind(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {U}, {tap}: Choose target opponent who had at least two more cards in hand than you did as you activated this ability. Draw a card. + Effect effect = new DrawCardSourceControllerEffect(1); + effect.setText("Choose target opponent who had at least two more cards in hand than you did as you activated this ability. Draw a card."); + Ability ability = new SimpleActivatedAbility(effect, new ManaCostsImpl("{U}")); + ability.addCost(new TapSourceCost()); + ability.setTargetAdjuster(KeeperOfTheMindAdjuster.instance); + this.addAbility(ability); + } + + public KeeperOfTheMind(final KeeperOfTheMind card) { + super(card); + } + + @Override + public KeeperOfTheMind copy() { + return new KeeperOfTheMind(this); + } +} + +enum KeeperOfTheMindAdjuster implements TargetAdjuster { + instance; + + private static final FilterOpponent filter = new FilterOpponent(); + + static { + filter.add(new KeeperOfTheMindPredicate()); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + Player activePlayer = game.getPlayer(game.getActivePlayerId()); + if (activePlayer == null) { + return; + } + ability.getTargets().clear(); + TargetPlayer target = new TargetPlayer(1, 1, false, filter); + target.setTargetController(activePlayer.getId()); + ability.addTarget(target); + } +} + +class KeeperOfTheMindPredicate implements ObjectSourcePlayerPredicate> { + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Player targetPlayer = input.getObject(); + Player firstPlayer = game.getPlayer(game.getActivePlayerId()); + if (targetPlayer == null + || firstPlayer == null + || !firstPlayer.hasOpponent(targetPlayer.getId(), game)) { + return false; + } + int countHandTargetPlayer = targetPlayer.getHand().size(); + int countHandFirstPlayer = firstPlayer.getHand().size(); + + return countHandTargetPlayer - 2 >= countHandFirstPlayer; + } + + @Override + public String toString() { + return "opponent who had at least two more cards in hand than you did as you activated this ability"; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KefnetTheMindful.java b/Mage.Sets/src/mage/cards/k/KefnetTheMindful.java index 447a86bc25..bcc6c9a437 100644 --- a/Mage.Sets/src/mage/cards/k/KefnetTheMindful.java +++ b/Mage.Sets/src/mage/cards/k/KefnetTheMindful.java @@ -115,7 +115,7 @@ class KefnetTheMindfulEffect extends OneShotEffect { filterControlledLand.add(CardType.LAND.getPredicate()); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); if (controller.chooseUse(Outcome.AIDontUseIt, "Do you want to return a land you control to its owner's hand?", null, "Yes", "No", source, game)) { Effect effect = new ReturnToHandChosenControlledPermanentEffect(filterControlledLand); effect.apply(game, source); diff --git a/Mage.Sets/src/mage/cards/k/KeigaTheTideStar.java b/Mage.Sets/src/mage/cards/k/KeigaTheTideStar.java index 3ba20234cc..6559e0b608 100644 --- a/Mage.Sets/src/mage/cards/k/KeigaTheTideStar.java +++ b/Mage.Sets/src/mage/cards/k/KeigaTheTideStar.java @@ -5,7 +5,7 @@ package mage.cards.k; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -34,7 +34,7 @@ public final class KeigaTheTideStar extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Keiga, the Tide Star dies, gain control of target creature. - Ability ability = new DiesTriggeredAbility(new GainControlTargetEffect(Duration.Custom)); + Ability ability = new DiesSourceTriggeredAbility(new GainControlTargetEffect(Duration.Custom)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KeldonBattlewagon.java b/Mage.Sets/src/mage/cards/k/KeldonBattlewagon.java index 091a1b3292..ac7b16a92e 100644 --- a/Mage.Sets/src/mage/cards/k/KeldonBattlewagon.java +++ b/Mage.Sets/src/mage/cards/k/KeldonBattlewagon.java @@ -1,7 +1,5 @@ - package mage.cards.k; -import java.util.List; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -20,9 +18,9 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; @@ -39,12 +37,13 @@ import mage.target.targetpointer.FixedTarget; public final class KeldonBattlewagon extends CardImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("an untapped creature you control"); + static { filter.add(Predicates.not(TappedPredicate.instance)); } public KeldonBattlewagon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); this.subtype.add(SubType.JUGGERNAUT); this.power = new MageInt(0); this.toughness = new MageInt(3); @@ -90,13 +89,14 @@ class KeldonBattlewagonCost extends CostImpl { @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { if (target.choose(Outcome.Tap, controllerId, sourceId, game)) { - for (UUID targetId: target.getTargets()) { + for (UUID targetId : target.getTargets()) { Permanent permanent = game.getPermanent(targetId); - if (permanent == null) + if (permanent == null) { return false; + } paid |= permanent.tap(game); for (Effect effect : ability.getEffects()) { - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); } } } @@ -128,7 +128,7 @@ class KeldonBattlewagonBoostEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent KeldonBattlewagon = game.getPermanent(source.getSourceId()); - Permanent tappedCreature = game.getPermanentOrLKIBattlefield(this.targetPointer.getFirst(game, source)); + Permanent tappedCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (tappedCreature != null && KeldonBattlewagon != null) { int amount = tappedCreature.getPower().getValue(); game.addEffect(new BoostSourceEffect(amount, 0, Duration.EndOfTurn), source); diff --git a/Mage.Sets/src/mage/cards/k/KelsFightFixer.java b/Mage.Sets/src/mage/cards/k/KelsFightFixer.java new file mode 100644 index 0000000000..3762736bc8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KelsFightFixer.java @@ -0,0 +1,94 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KelsFightFixer extends CardImpl { + + public KelsFightFixer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.AZRA); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever you sacrifice a creature, you may pay {U/B}. If you do, draw a card. + this.addAbility(new KelsFightFixerTriggeredAbility()); + + // {1}, Sacrifice a creature: Kels, Fight Fixer gains indestructible until end of turn. + Ability ability = new SimpleActivatedAbility(new GainAbilitySourceEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ), new GenericManaCost(1)); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); + this.addAbility(ability); + } + + private KelsFightFixer(final KelsFightFixer card) { + super(card); + } + + @Override + public KelsFightFixer copy() { + return new KelsFightFixer(this); + } +} + +class KelsFightFixerTriggeredAbility extends TriggeredAbilityImpl { + + KelsFightFixerTriggeredAbility() { + super(Zone.BATTLEFIELD, new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{U/B}")), false); + setLeavesTheBattlefieldTrigger(true); + } + + private KelsFightFixerTriggeredAbility(final KelsFightFixerTriggeredAbility ability) { + super(ability); + } + + @Override + public KelsFightFixerTriggeredAbility copy() { + return new KelsFightFixerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SACRIFICED_PERMANENT; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals(this.getControllerId()) + && game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD).isCreature(); + } + + @Override + public String getRule() { + return "Whenever you sacrifice a creature, " + super.getRule(); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KelsienThePlague.java b/Mage.Sets/src/mage/cards/k/KelsienThePlague.java new file mode 100644 index 0000000000..a46f223c43 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KelsienThePlague.java @@ -0,0 +1,172 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.counter.AddCountersControllerEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KelsienThePlague extends CardImpl { + + public KelsienThePlague(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ASSASSIN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Kelsien, the Plague gets +1/+1 for each experience counter you have. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + KelsienThePlagueCount.instance, KelsienThePlagueCount.instance, + Duration.WhileOnBattlefield, false + ))); + + // {T}: Kelsien deals 1 damage to target creature you don't control. When that creature dies this turn, you get an experience counter. + Ability ability = new SimpleActivatedAbility(new KelsienThePlagueEffect(), new TapSourceCost()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.addAbility(ability); + } + + private KelsienThePlague(final KelsienThePlague card) { + super(card); + } + + @Override + public KelsienThePlague copy() { + return new KelsienThePlague(this); + } +} + +enum KelsienThePlagueCount implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int amount = 0; + Player player = game.getPlayer(sourceAbility.getControllerId()); + if (player != null) { + amount = player.getCounters().getCount(CounterType.EXPERIENCE); + } + return amount; + } + + @Override + public KelsienThePlagueCount copy() { + return instance; + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return "experience counter you have"; + } +} + +class KelsienThePlagueEffect extends OneShotEffect { + + KelsienThePlagueEffect() { + super(Outcome.Benefit); + staticText = "{this} deals 1 damage to target creature you don't control. " + + "When that creature dies this turn, you get an experience counter."; + } + + private KelsienThePlagueEffect(final KelsienThePlagueEffect effect) { + super(effect); + } + + @Override + public KelsienThePlagueEffect copy() { + return new KelsienThePlagueEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + permanent.damage(1, source.getSourceId(), game); + game.addDelayedTriggeredAbility(new KelsienThePlagueDelayedTriggeredAbility(permanent.getId()), source); + return true; + } +} + +class KelsienThePlagueDelayedTriggeredAbility extends DelayedTriggeredAbility { + + private final UUID target; + + KelsienThePlagueDelayedTriggeredAbility(UUID target) { + super(new AddCountersControllerEffect( + CounterType.EXPERIENCE.createInstance(), false + ), Duration.EndOfTurn, true); + this.target = target; + } + + private KelsienThePlagueDelayedTriggeredAbility(KelsienThePlagueDelayedTriggeredAbility ability) { + super(ability); + this.target = ability.target; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getTargetId().equals(target)) { + return false; + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.isDiesEvent()) { + return true; + } + return false; + } + + @Override + public KelsienThePlagueDelayedTriggeredAbility copy() { + return new KelsienThePlagueDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "When that creature dies this turn, you get an experience counter."; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KeralKeepDisciples.java b/Mage.Sets/src/mage/cards/k/KeralKeepDisciples.java new file mode 100644 index 0000000000..83fbcf75b1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KeralKeepDisciples.java @@ -0,0 +1,40 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class KeralKeepDisciples extends CardImpl { + + public KeralKeepDisciples(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Whenever you activate a loyalty ability of a Chandra planeswalker, Keral Keep Disciples deals 1 damage to each opponent. + this.addAbility(new ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility(new DamagePlayersEffect(1, TargetController.OPPONENT), SubType.CHANDRA)); + } + + private KeralKeepDisciples(final KeralKeepDisciples card) { + super(card); + } + + @Override + public KeralKeepDisciples copy() { + return new KeralKeepDisciples(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KerugaTheMacrosage.java b/Mage.Sets/src/mage/cards/k/KerugaTheMacrosage.java new file mode 100644 index 0000000000..a9373bc5b7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KerugaTheMacrosage.java @@ -0,0 +1,72 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.CompanionAbility; +import mage.abilities.keyword.CompanionCondition; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; + +import java.util.Set; +import java.util.UUID; + +/** + * @author emerald000 + */ +public final class KerugaTheMacrosage extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("other permanent you control with converted mana cost 3 or greater"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(new ConvertedManaCostPredicate(ComparisonType.MORE_THAN, 2)); + } + + public KerugaTheMacrosage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G/U}{G/U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DINOSAUR); + this.subtype.add(SubType.HIPPO); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Companion — Your starting deck contains only cards with converted mana cost 3 or greater and land cards. + this.addAbility(new CompanionAbility(KerugaCondition.instance)); + // When Keruga, the Macrosage enters the battlefield, draw a card for each other permanent you control with converted mana cost 3 or greater. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(filter)))); + } + + private KerugaTheMacrosage(final KerugaTheMacrosage card) { + super(card); + } + + @Override + public KerugaTheMacrosage copy() { + return new KerugaTheMacrosage(this); + } +} + +enum KerugaCondition implements CompanionCondition { + instance; + + @Override + public String getRule() { + return "Your starting deck contains only cards with converted mana cost 3 or greater and land cards."; + } + + @Override + public boolean isLegal(Set deck, int startingSize) { + return deck.stream().allMatch(card -> card.isLand() || card.getConvertedManaCost() >= 3); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/k/KessigForgemaster.java b/Mage.Sets/src/mage/cards/k/KessigForgemaster.java index c8ffa6637f..13f65fe901 100644 --- a/Mage.Sets/src/mage/cards/k/KessigForgemaster.java +++ b/Mage.Sets/src/mage/cards/k/KessigForgemaster.java @@ -6,7 +6,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; @@ -36,7 +36,7 @@ public final class KessigForgemaster extends CardImpl { this.secondSideCardClazz = mage.cards.f.FlameheartWerewolf.class; // Whenever Kessig Forgemaster blocks or becomes blocked by a creature, Kessig Forgemaster deals 1 damage to that creature. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new DamageTargetEffect(1, true, "that creature"), + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(1, true, "that creature"), StaticFilters.FILTER_PERMANENT_CREATURE, false, null, true)); // At the beginning of each upkeep, if no spells were cast last turn, transform Kessig Forgemaster. diff --git a/Mage.Sets/src/mage/cards/k/KessigWolfRun.java b/Mage.Sets/src/mage/cards/k/KessigWolfRun.java index 787bae0044..138ec4808e 100644 --- a/Mage.Sets/src/mage/cards/k/KessigWolfRun.java +++ b/Mage.Sets/src/mage/cards/k/KessigWolfRun.java @@ -1,7 +1,5 @@ - package mage.cards.k; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -16,29 +14,34 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author nantuko */ public final class KessigWolfRun extends CardImpl { public KessigWolfRun(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); // {X}{R}{G}, {T}: Target creature gets +X/+0 and gains trample until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{X}{R}{G}")); + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ).setText("Target creature gets +X/+0"), new ManaCostsImpl("{X}{R}{G}")); + ability.addEffect(new BoostTargetEffect( + ManacostVariableValue.instance, StaticValue.get(0), Duration.EndOfTurn + ).setText("and gains trample until end of turn")); ability.addCost(new TapSourceCost()); - ability.addEffect(new BoostTargetEffect(ManacostVariableValue.instance, StaticValue.get(0), Duration.EndOfTurn)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } - public KessigWolfRun(final KessigWolfRun card) { + private KessigWolfRun(final KessigWolfRun card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/k/KetriaCrystal.java b/Mage.Sets/src/mage/cards/k/KetriaCrystal.java new file mode 100644 index 0000000000..6231879ede --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KetriaCrystal.java @@ -0,0 +1,39 @@ +package mage.cards.k; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KetriaCrystal extends CardImpl { + + public KetriaCrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add {G}, {U}, or {R}. + this.addAbility(new GreenManaAbility()); + this.addAbility(new BlueManaAbility()); + this.addAbility(new RedManaAbility()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private KetriaCrystal(final KetriaCrystal card) { + super(card); + } + + @Override + public KetriaCrystal copy() { + return new KetriaCrystal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KetriaTriome.java b/Mage.Sets/src/mage/cards/k/KetriaTriome.java new file mode 100644 index 0000000000..a85a5d44b5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KetriaTriome.java @@ -0,0 +1,48 @@ +package mage.cards.k; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KetriaTriome extends CardImpl { + + public KetriaTriome(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.FOREST); + this.subtype.add(SubType.ISLAND); + this.subtype.add(SubType.MOUNTAIN); + + // ({T}: Add {G}, {U}, or {R}.) + this.addAbility(new GreenManaAbility()); + this.addAbility(new BlueManaAbility()); + this.addAbility(new RedManaAbility()); + + // Ketria Triome enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Cycling {3} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"))); + } + + private KetriaTriome(final KetriaTriome card) { + super(card); + } + + @Override + public KetriaTriome copy() { + return new KetriaTriome(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KeyToTheCity.java b/Mage.Sets/src/mage/cards/k/KeyToTheCity.java index 1fce751780..909321cf6c 100644 --- a/Mage.Sets/src/mage/cards/k/KeyToTheCity.java +++ b/Mage.Sets/src/mage/cards/k/KeyToTheCity.java @@ -1,9 +1,6 @@ - package mage.cards.k; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.costs.common.TapSourceCost; @@ -11,35 +8,35 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.abilities.keyword.InspiredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author emerald000 */ public final class KeyToTheCity extends CardImpl { public KeyToTheCity(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // {T}, Discard a card: Up to one target creature can't be blocked this turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedTargetEffect(), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new CantBeBlockedTargetEffect(), new TapSourceCost()); ability.addCost(new DiscardCardCost()); ability.addTarget(new TargetCreaturePermanent(0, 1)); this.addAbility(ability); // Whenever Key to the City becomes untapped, you may pay {2}. If you do, draw a card. - this.addAbility(new KeyToTheCityTriggeredAbility()); + this.addAbility(new InspiredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), new GenericManaCost(2) + ), false, false)); } - public KeyToTheCity(final KeyToTheCity card) { + private KeyToTheCity(final KeyToTheCity card) { super(card); } @@ -48,35 +45,3 @@ public final class KeyToTheCity extends CardImpl { return new KeyToTheCity(this); } } - -class KeyToTheCityTriggeredAbility extends TriggeredAbilityImpl{ - - KeyToTheCityTriggeredAbility() { - super(Zone.BATTLEFIELD, new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new GenericManaCost(2))); - } - - KeyToTheCityTriggeredAbility(final KeyToTheCityTriggeredAbility ability) { - super(ability); - } - - @Override - public KeyToTheCityTriggeredAbility copy() { - return new KeyToTheCityTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.UNTAPPED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getTargetId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "Whenever Key to the City becomes untapped, you may pay {2}. If you do, draw a card."; - } - -} diff --git a/Mage.Sets/src/mage/cards/k/KhalniHydra.java b/Mage.Sets/src/mage/cards/k/KhalniHydra.java index 0421a15082..f37225ddcf 100644 --- a/Mage.Sets/src/mage/cards/k/KhalniHydra.java +++ b/Mage.Sets/src/mage/cards/k/KhalniHydra.java @@ -1,45 +1,53 @@ - package mage.cards.k; -import java.util.Iterator; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; + +import java.util.UUID; /** - * * @author maurer.it_at_gmail.com */ public final class KhalniHydra extends CardImpl { - private static final FilterControlledCreaturePermanent filter; + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("green creature you control"); static { - filter = new FilterControlledCreaturePermanent(); filter.add(new ColorPredicate(ObjectColor.GREEN)); } public KhalniHydra(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}{G}{G}{G}{G}{G}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{G}{G}{G}{G}{G}{G}{G}"); this.subtype.add(SubType.HYDRA); this.power = new MageInt(8); this.toughness = new MageInt(8); - this.addAbility(new SimpleStaticAbility(Zone.STACK, new KhalniHydraCostReductionEffect())); + + // This spell costs {G} less to cast for each green creature you control. + ManaCosts manaReduce = new ManaCostsImpl<>("{G}"); + DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + this.addAbility(new SimpleStaticAbility(Zone.ALL, + new SpellCostReductionForEachSourceEffect(manaReduce, xValue)) + .addHint(new ValueHint("Green creature you control", xValue)) + ); + + // Trample this.addAbility(TrampleAbility.getInstance()); } @@ -47,47 +55,8 @@ public final class KhalniHydra extends CardImpl { super(card); } - @Override - public void adjustCosts(Ability ability, Game game) { - super.adjustCosts(ability, game); - int reductionAmount = game.getBattlefield().count(filter, ability.getSourceId(), ability.getControllerId(), game); - Iterator iter = ability.getManaCostsToPay().iterator(); - - while ( reductionAmount > 0 && iter.hasNext() ) { - ManaCost manaCost = iter.next(); - if (manaCost.getMana().getGreen() > 0) { // in case another effect adds additional mana cost - iter.remove(); - reductionAmount--; - } - } - } - @Override public KhalniHydra copy() { return new KhalniHydra(this); } } - -class KhalniHydraCostReductionEffect extends OneShotEffect { - private static final String effectText = "{this} costs {G} less to cast for each green creature you control"; - - KhalniHydraCostReductionEffect ( ) { - super(Outcome.Benefit); - this.staticText = effectText; - } - - KhalniHydraCostReductionEffect ( KhalniHydraCostReductionEffect effect ) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public KhalniHydraCostReductionEffect copy() { - return new KhalniHydraCostReductionEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java index f6779f259a..7a53d2f08e 100644 --- a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java +++ b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java @@ -5,8 +5,6 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.MorphAbility; import mage.cards.Card; @@ -14,14 +12,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.stack.Spell; import mage.game.stack.StackObject; -import mage.players.Player; import mage.target.TargetSpell; -import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; /** * @author emerald000 @@ -59,7 +54,9 @@ class KheruSpellsnatcherEffect extends OneShotEffect { KheruSpellsnatcherEffect() { super(Outcome.Benefit); - this.staticText = "counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may cast that card without paying its mana cost as long as it remains exiled"; + this.staticText = "counter target spell. If that spell is countered this way, " + + "exile it instead of putting it into its owner's graveyard. " + + "You may cast that card without paying its mana cost as long as it remains exiled"; } KheruSpellsnatcherEffect(final KheruSpellsnatcherEffect effect) { @@ -73,68 +70,14 @@ class KheruSpellsnatcherEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID objectId = targetPointer.getFirst(game, source); - UUID sourceId = source.getSourceId(); - - StackObject stackObject = game.getStack().getStackObject(objectId); - if (stackObject != null + MageObject sourceObject = source.getSourceObject(game); + StackObject stackObject = game.getStack().getStackObject(targetPointer.getFirst(game, source)); + if (stackObject != null && sourceObject != null && game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, Zone.EXILED, false, ZoneDetail.NONE)) { if (!stackObject.isCopy()) { MageObject card = game.getObject(stackObject.getSourceId()); - if (card instanceof Card) { - UUID exileId = CardUtil.getCardExileZoneId(game, sourceId); - ((Card) card).moveToExile(exileId, "Kheru Spellsnatcher - cast without mana cost", sourceId, game); - ContinuousEffect effect = new KheruSpellsnatcherCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - game.addEffect(effect, source); - } - } - return true; - } - return false; - } -} - -class KheruSpellsnatcherCastFromExileEffect extends AsThoughEffectImpl { - - KheruSpellsnatcherCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - staticText = "You may cast that card without paying its mana cost as long as it remains exiled"; - } - - KheruSpellsnatcherCastFromExileEffect(final KheruSpellsnatcherCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public KheruSpellsnatcherCastFromExileEffect copy() { - return new KheruSpellsnatcherCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - if (getTargetPointer().getFirst(game, source) == null) { - this.discard(); - return false; - } - if (sourceId.equals(getTargetPointer().getFirst(game, source))) { - Card card = game.getCard(sourceId); - if (card != null) { - if (game.getState().getZone(sourceId) == Zone.EXILED) { - Player player = game.getPlayer(affectedControllerId); - if(player != null) { - player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts()); - return true; - } - } else { - this.discard(); - } + if (card instanceof Card) { + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, (Card)card, TargetController.YOU, Duration.Custom, true); } } } diff --git a/Mage.Sets/src/mage/cards/k/KhorvathsFury.java b/Mage.Sets/src/mage/cards/k/KhorvathsFury.java index 208080a28d..6c9471afde 100644 --- a/Mage.Sets/src/mage/cards/k/KhorvathsFury.java +++ b/Mage.Sets/src/mage/cards/k/KhorvathsFury.java @@ -75,7 +75,7 @@ class KhorvathsFuryEffect extends OneShotEffect { } for (Player player : choice.getFriends()) { if (player != null) { - player.drawCards(cardsToDraw.get(player.getId()) + 1, game); + player.drawCards(cardsToDraw.get(player.getId()) + 1, source.getSourceId(), game); } } for (Player player : choice.getFoes()) { diff --git a/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java b/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java index 2122879c3e..12935d0415 100644 --- a/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java +++ b/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java @@ -83,7 +83,7 @@ class KikiJikiMirrorBreakerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/k/KindleTheCarnage.java b/Mage.Sets/src/mage/cards/k/KindleTheCarnage.java index 6e7b84b254..d1c3af51e2 100644 --- a/Mage.Sets/src/mage/cards/k/KindleTheCarnage.java +++ b/Mage.Sets/src/mage/cards/k/KindleTheCarnage.java @@ -1,7 +1,5 @@ - package mage.cards.k; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageAllEffect; @@ -15,8 +13,9 @@ import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class KindleTheCarnage extends CardImpl { @@ -62,7 +61,6 @@ class KindleTheCarnageEffect extends OneShotEffect { Cards hand = controller.getHand(); while (hand != null && hand.size() > 0 - && controller.isInGame() && controller.chooseUse(Outcome.AIDontUseIt, "Discard a card randomly from your hand?", source, game)) { Card discardedCard = controller.discardOne(true, source, game); if (discardedCard != null) { diff --git a/Mage.Sets/src/mage/cards/k/KineticAugur.java b/Mage.Sets/src/mage/cards/k/KineticAugur.java new file mode 100644 index 0000000000..458110b550 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KineticAugur.java @@ -0,0 +1,91 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.SetPowerSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetDiscard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KineticAugur extends CardImpl { + + private static final FilterCard filter = new FilterInstantOrSorceryCard("instant and sorcery cards"); + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(filter); + + public KineticAugur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Kinetic Augur's power is equal to the number of instant and sorcery cards in your graveyard. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerSourceEffect(xValue, Duration.EndOfGame))); + + // When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards. + this.addAbility(new EntersBattlefieldTriggeredAbility(new KineticAugurEffect())); + } + + private KineticAugur(final KineticAugur card) { + super(card); + } + + @Override + public KineticAugur copy() { + return new KineticAugur(this); + } +} + +class KineticAugurEffect extends OneShotEffect { + + KineticAugurEffect() { + super(Outcome.Benefit); + staticText = "discard up to two cards, then draw that many cards"; + } + + private KineticAugurEffect(final KineticAugurEffect effect) { + super(effect); + } + + @Override + public KineticAugurEffect copy() { + return new KineticAugurEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || player.getHand().isEmpty()) { + return false; + } + TargetDiscard target = new TargetDiscard(0, 2, StaticFilters.FILTER_CARD, player.getId()); + player.choose(Outcome.AIDontUseIt, player.getHand(), target, game); + int discarded = player.discard(new CardsImpl(target.getTargets()), source, game).size(); + if (discarded > 0) { + player.drawCards(discarded, source.getSourceId(), game); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/k/Kingfisher.java b/Mage.Sets/src/mage/cards/k/Kingfisher.java index 1144f22229..92ec2a993f 100644 --- a/Mage.Sets/src/mage/cards/k/Kingfisher.java +++ b/Mage.Sets/src/mage/cards/k/Kingfisher.java @@ -3,7 +3,7 @@ package mage.cards.k; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class Kingfisher extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Kingfisher dies, draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } public Kingfisher(final Kingfisher card) { diff --git a/Mage.Sets/src/mage/cards/k/KinnanBonderProdigy.java b/Mage.Sets/src/mage/cards/k/KinnanBonderProdigy.java new file mode 100644 index 0000000000..857260f64d --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KinnanBonderProdigy.java @@ -0,0 +1,68 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.TapForManaAllTriggeredManaAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.effects.mana.AddManaOfAnyTypeProducedEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KinnanBonderProdigy extends CardImpl { + + private static final FilterPermanent filter = new FilterNonlandPermanent("you tap a nonland permanent"); + private static final FilterCard filter2 = new FilterCreatureCard("non-Human creature card"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter2.add(Predicates.not(SubType.HUMAN.getPredicate())); + } + + public KinnanBonderProdigy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever you tap a nonland permanent for mana, add one mana of any type that permanent produced. + AddManaOfAnyTypeProducedEffect effect = new AddManaOfAnyTypeProducedEffect(); + effect.setText("add one mana of any type that permanent produced"); + this.addAbility(new TapForManaAllTriggeredManaAbility( + effect, filter, SetTargetPointer.PERMANENT + )); + + // {5}{G}{U}: Look at the top five cards of your library. You may put a non-Human creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order. + this.addAbility(new SimpleActivatedAbility(new LookLibraryAndPickControllerEffect( + StaticValue.get(5), false, StaticValue.get(1), filter2, Zone.LIBRARY, false, + true, false, Zone.BATTLEFIELD, true, false, false + ).setBackInRandomOrder(true).setText("Look at the top five cards of your library. " + + "You may put a non-Human creature card from among them onto the battlefield. " + + "Put the rest on the bottom of your library in a random order." + ), new ManaCostsImpl("{5}{G}{U}"))); + } + + private KinnanBonderProdigy(final KinnanBonderProdigy card) { + super(card); + } + + @Override + public KinnanBonderProdigy copy() { + return new KinnanBonderProdigy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KinsbaileBorderguard.java b/Mage.Sets/src/mage/cards/k/KinsbaileBorderguard.java index d8134ac438..97feb48727 100644 --- a/Mage.Sets/src/mage/cards/k/KinsbaileBorderguard.java +++ b/Mage.Sets/src/mage/cards/k/KinsbaileBorderguard.java @@ -4,7 +4,7 @@ package mage.cards.k; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; @@ -45,7 +45,7 @@ public final class KinsbaileBorderguard extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), new PermanentsOnBattlefieldCount(filter), true), "with a +1/+1 counter on it for each other Kithkin you control")); // When Kinsbaile Borderguard dies, create a 1/1 white Kithkin Soldier creature token for each counter on it. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new KithkinToken(), new AllCountersCount()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new KithkinToken(), new AllCountersCount()))); } public KinsbaileBorderguard(final KinsbaileBorderguard card) { diff --git a/Mage.Sets/src/mage/cards/k/KiorasDismissal.java b/Mage.Sets/src/mage/cards/k/KiorasDismissal.java index 7f4e3e8e8f..086f7ce500 100644 --- a/Mage.Sets/src/mage/cards/k/KiorasDismissal.java +++ b/Mage.Sets/src/mage/cards/k/KiorasDismissal.java @@ -1,4 +1,3 @@ - package mage.cards.k; import mage.abilities.abilityword.StriveAbility; @@ -13,17 +12,17 @@ import mage.target.TargetPermanent; import java.util.UUID; /** - * * @author LevelX2 */ public final class KiorasDismissal extends CardImpl { public KiorasDismissal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); // Strive - Kiora's Dismissal costs U more to cast for each target beyond the first. this.addAbility(new StriveAbility("{U}")); + // Return any number of target enchantments to their owners' hands. this.getSpellAbility().addTarget(new TargetPermanent(0, Integer.MAX_VALUE, StaticFilters.FILTER_ENCHANTMENT_PERMANENT, false)); Effect effect = new ReturnToHandTargetEffect(); diff --git a/Mage.Sets/src/mage/cards/k/KithkinArmor.java b/Mage.Sets/src/mage/cards/k/KithkinArmor.java index 1a43c262fc..d17a312c21 100644 --- a/Mage.Sets/src/mage/cards/k/KithkinArmor.java +++ b/Mage.Sets/src/mage/cards/k/KithkinArmor.java @@ -1,174 +1,174 @@ -package mage.cards.k; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; -import mage.abilities.effects.PreventionEffectImpl; -import mage.abilities.effects.RestrictionEffect; -import mage.abilities.effects.common.AttachEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.DamageCreatureEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.TargetSource; - -/** - * - * @author jeffwadsworth - */ -public final class KithkinArmor extends CardImpl { - - public KithkinArmor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // Enchanted creature can't be blocked by creatures with power 3 or greater. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KithkinArmorRestrictionEffect())); - - // Sacrifice Kithkin Armor: The next time a source of your choice would deal damage to enchanted creature this turn, prevent that damage. - Ability protectionAbility = new SimpleActivatedAbility( - Zone.BATTLEFIELD, - new KithkinArmorPreventionEffect(), - new KithkinArmorCost()); - protectionAbility.addTarget(new TargetSource()); - this.addAbility(protectionAbility); - - } - - private KithkinArmor(final KithkinArmor card) { - super(card); - } - - @Override - public KithkinArmor copy() { - return new KithkinArmor(this); - } -} - -class KithkinArmorCost extends CostImpl { - - public KithkinArmorCost() { - this.text = "sacrifice {this}"; - } - - public KithkinArmorCost(KithkinArmorCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { - Permanent permanent = game.getPermanent(sourceId); - if (permanent != null) { - // store attached to information due to getLastInfo being completely fubared - game.getState().setValue(ability.getSourceId().toString() + "attachedToPermanent", permanent.getAttachedTo()); - paid = permanent.sacrifice(sourceId, game); - if (!paid) { - game.getState().setValue(ability.getSourceId().toString() + "attachedToPermanent", null); - } - } - return paid; - } - - @Override - public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - Permanent permanent = game.getPermanent(sourceId); - return permanent != null - && game.getPlayer(controllerId).canPaySacrificeCost(permanent, sourceId, controllerId, game); - } - - @Override - public KithkinArmorCost copy() { - return new KithkinArmorCost(this); - } -} - -class KithkinArmorRestrictionEffect extends RestrictionEffect { - - public KithkinArmorRestrictionEffect() { - super(Duration.WhileOnBattlefield); - staticText = "Enchanted creature can't be blocked by creatures with power 3 or greater"; - } - - public KithkinArmorRestrictionEffect(final KithkinArmorRestrictionEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null - && enchantment.getAttachedTo() != null) { - Permanent enchantedPermanent = game.getPermanent(enchantment.getAttachedTo()); - return enchantedPermanent != null - && permanent.getId().equals(enchantedPermanent.getId()); - } - return false; - } - - @Override - public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { - return blocker.getPower().getValue() < 3; - } - - @Override - public KithkinArmorRestrictionEffect copy() { - return new KithkinArmorRestrictionEffect(this); - } -} - -class KithkinArmorPreventionEffect extends PreventionEffectImpl { - - KithkinArmorPreventionEffect() { - super(Duration.EndOfTurn, Integer.MAX_VALUE, false); - staticText = "The next time a source of your choice would deal damage to enchanted creature this turn, prevent that damage"; - } - - KithkinArmorPreventionEffect(final KithkinArmorPreventionEffect effect) { - super(effect); - } - - @Override - public KithkinArmorPreventionEffect copy() { - return new KithkinArmorPreventionEffect(this); - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game) - && event instanceof DamageCreatureEvent - && event.getAmount() > 0 - && !this.used) { - UUID enchantedCreatureId = (UUID) game.getState().getValue(source.getSourceId().toString() + "attachedToPermanent"); - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; - if (enchantedCreatureId != null - && event.getTargetId().equals(enchantedCreatureId) - && damageEvent.getSourceId().equals(source.getFirstTarget())) { - this.used = true; - this.discard(); - return true; - } - } - return false; - } -} +package mage.cards.k; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.DamageCreatureEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetSource; + +/** + * + * @author jeffwadsworth + */ +public final class KithkinArmor extends CardImpl { + + public KithkinArmor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature can't be blocked by creatures with power 3 or greater. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KithkinArmorRestrictionEffect())); + + // Sacrifice Kithkin Armor: The next time a source of your choice would deal damage to enchanted creature this turn, prevent that damage. + Ability protectionAbility = new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new KithkinArmorPreventionEffect(), + new KithkinArmorCost()); + protectionAbility.addTarget(new TargetSource()); + this.addAbility(protectionAbility); + + } + + private KithkinArmor(final KithkinArmor card) { + super(card); + } + + @Override + public KithkinArmor copy() { + return new KithkinArmor(this); + } +} + +class KithkinArmorCost extends CostImpl { + + public KithkinArmorCost() { + this.text = "sacrifice {this}"; + } + + public KithkinArmorCost(KithkinArmorCost cost) { + super(cost); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Permanent permanent = game.getPermanent(sourceId); + if (permanent != null) { + // store attached to information due to getLastInfo being completely fubared + game.getState().setValue(ability.getSourceId().toString() + "attachedToPermanent", permanent.getAttachedTo()); + paid = permanent.sacrifice(sourceId, game); + if (!paid) { + game.getState().setValue(ability.getSourceId().toString() + "attachedToPermanent", null); + } + } + return paid; + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + Permanent permanent = game.getPermanent(sourceId); + return permanent != null + && game.getPlayer(controllerId).canPaySacrificeCost(permanent, sourceId, controllerId, game); + } + + @Override + public KithkinArmorCost copy() { + return new KithkinArmorCost(this); + } +} + +class KithkinArmorRestrictionEffect extends RestrictionEffect { + + public KithkinArmorRestrictionEffect() { + super(Duration.WhileOnBattlefield); + staticText = "Enchanted creature can't be blocked by creatures with power 3 or greater"; + } + + public KithkinArmorRestrictionEffect(final KithkinArmorRestrictionEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment != null + && enchantment.getAttachedTo() != null) { + Permanent enchantedPermanent = game.getPermanent(enchantment.getAttachedTo()); + return enchantedPermanent != null + && permanent.getId().equals(enchantedPermanent.getId()); + } + return false; + } + + @Override + public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { + return blocker.getPower().getValue() < 3; + } + + @Override + public KithkinArmorRestrictionEffect copy() { + return new KithkinArmorRestrictionEffect(this); + } +} + +class KithkinArmorPreventionEffect extends PreventionEffectImpl { + + KithkinArmorPreventionEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false); + staticText = "The next time a source of your choice would deal damage to enchanted creature this turn, prevent that damage"; + } + + KithkinArmorPreventionEffect(final KithkinArmorPreventionEffect effect) { + super(effect); + } + + @Override + public KithkinArmorPreventionEffect copy() { + return new KithkinArmorPreventionEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game) + && event instanceof DamageCreatureEvent + && event.getAmount() > 0 + && !this.used) { + UUID enchantedCreatureId = (UUID) game.getState().getValue(source.getSourceId().toString() + "attachedToPermanent"); + DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + if (enchantedCreatureId != null + && event.getTargetId().equals(enchantedCreatureId) + && damageEvent.getSourceId().equals(source.getFirstTarget())) { + this.used = true; + this.discard(); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KitsunePalliator.java b/Mage.Sets/src/mage/cards/k/KitsunePalliator.java index a905c0e62d..55f0d7112b 100644 --- a/Mage.Sets/src/mage/cards/k/KitsunePalliator.java +++ b/Mage.Sets/src/mage/cards/k/KitsunePalliator.java @@ -68,7 +68,7 @@ class KitsunePalliatorEffect extends OneShotEffect { List permanents = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game); for (Permanent permanent : permanents) { - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { diff --git a/Mage.Sets/src/mage/cards/k/KnightOfTheHolyNimbus.java b/Mage.Sets/src/mage/cards/k/KnightOfTheHolyNimbus.java index bf5963f8a1..13d5eb1068 100644 --- a/Mage.Sets/src/mage/cards/k/KnightOfTheHolyNimbus.java +++ b/Mage.Sets/src/mage/cards/k/KnightOfTheHolyNimbus.java @@ -1,7 +1,5 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.ActivateOnlyByOpponentActivatedAbility; @@ -12,23 +10,20 @@ import mage.abilities.effects.common.CantBeRegeneratedSourceEffect; import mage.abilities.keyword.FlankingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class KnightOfTheHolyNimbus extends CardImpl { public KnightOfTheHolyNimbus(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.REBEL); this.subtype.add(SubType.KNIGHT); @@ -37,13 +32,13 @@ public final class KnightOfTheHolyNimbus extends CardImpl { // Flanking this.addAbility(new FlankingAbility()); - + // If Knight of the Holy Nimbus would be destroyed, regenerate it. this.addAbility(new SimpleStaticAbility(Zone.ALL, new KnightOfTheHolyNimbusReplacementEffect())); - + // {2}: Knight of the Holy Nimbus can't be regenerated this turn. Only any opponent may activate this ability. this.addAbility(new ActivateOnlyByOpponentActivatedAbility(Zone.BATTLEFIELD, new CantBeRegeneratedSourceEffect(Duration.EndOfTurn), new ManaCostsImpl("{2}"))); - + } public KnightOfTheHolyNimbus(final KnightOfTheHolyNimbus card) { @@ -70,9 +65,9 @@ class KnightOfTheHolyNimbusReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent knightOfTheHolyNimbus = game.getPermanent(event.getTargetId()); - if (knightOfTheHolyNimbus != null + if (knightOfTheHolyNimbus != null && event.getAmount() == 0) { // 1=noRegen - if (knightOfTheHolyNimbus.regenerate(source.getSourceId(), game)) { + if (knightOfTheHolyNimbus.regenerate(source, game)) { game.informPlayers(source.getSourceObject(game).getName() + " has been regenerated."); return true; } diff --git a/Mage.Sets/src/mage/cards/k/KnollspineDragon.java b/Mage.Sets/src/mage/cards/k/KnollspineDragon.java index 10124eefd8..63395447f9 100644 --- a/Mage.Sets/src/mage/cards/k/KnollspineDragon.java +++ b/Mage.Sets/src/mage/cards/k/KnollspineDragon.java @@ -71,12 +71,11 @@ class KnollspineDragonEffect extends OneShotEffect { AmountOfDamageAPlayerReceivedThisTurnWatcher watcher = game.getState().getWatcher(AmountOfDamageAPlayerReceivedThisTurnWatcher.class); if (watcher != null) { int drawAmount = watcher.getAmountOfDamageReceivedThisTurn(targetOpponent.getId()); - controller.drawCards(drawAmount, game); - game.informPlayers(controller.getLogName() + "draws " + drawAmount + " cards"); + controller.drawCards(drawAmount, source.getSourceId(), game); return true; } } - game.informPlayers(controller.getLogName() + "drew no cards"); + game.informPlayers(controller.getLogName() + " drew no cards"); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/k/KoglaTheTitanApe.java b/Mage.Sets/src/mage/cards/k/KoglaTheTitanApe.java new file mode 100644 index 0000000000..172626e3ff --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KoglaTheTitanApe.java @@ -0,0 +1,78 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.FightTargetSourceEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KoglaTheTitanApe extends CardImpl { + + private static final FilterPermanent filter + = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment defending player controls"); + private static final FilterPermanent filter2 + = new FilterControlledPermanent(SubType.HUMAN); + + static { + filter.add(DefendingPlayerControlsPredicate.instance); + } + + public KoglaTheTitanApe(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.APE); + this.power = new MageInt(7); + this.toughness = new MageInt(6); + + // When Kogla, the Titan Ape enters the battlefield, it fights up to one target creature you don't control. + Ability ability = new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect() + .setText("it fights up to one target creature you don't control")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.addAbility(ability); + + // Whenever Kogla attacks, destroy target artifact or enchantment defending player controls. + ability = new AttacksTriggeredAbility(new DestroyTargetEffect(), false); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {1}{G}: Return target Human you control to its owner's hand. Kogla gains indestructible until end of turn. + ability = new SimpleActivatedAbility(new ReturnToHandTargetEffect(), new ManaCostsImpl("{1}{G}")); + ability.addEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn)); + ability.addTarget(new TargetPermanent(filter2)); + this.addAbility(ability); + } + + private KoglaTheTitanApe(final KoglaTheTitanApe card) { + super(card); + } + + @Override + public KoglaTheTitanApe copy() { + return new KoglaTheTitanApe(this); + } +} +// it was beauty killed the beast diff --git a/Mage.Sets/src/mage/cards/k/KokushoTheEveningStar.java b/Mage.Sets/src/mage/cards/k/KokushoTheEveningStar.java index 0336c4a729..c1881be2aa 100644 --- a/Mage.Sets/src/mage/cards/k/KokushoTheEveningStar.java +++ b/Mage.Sets/src/mage/cards/k/KokushoTheEveningStar.java @@ -5,7 +5,7 @@ package mage.cards.k; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -30,7 +30,7 @@ public final class KokushoTheEveningStar extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(5); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new DiesTriggeredAbility(new KokushoTheEveningStarEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new KokushoTheEveningStarEffect(), false)); } public KokushoTheEveningStar(final KokushoTheEveningStar card) { diff --git a/Mage.Sets/src/mage/cards/k/KopalaWardenOfWaves.java b/Mage.Sets/src/mage/cards/k/KopalaWardenOfWaves.java index 1683c59aee..6ca5365a8e 100644 --- a/Mage.Sets/src/mage/cards/k/KopalaWardenOfWaves.java +++ b/Mage.Sets/src/mage/cards/k/KopalaWardenOfWaves.java @@ -1,31 +1,23 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Mode; +import mage.abilities.ActivatedAbility; import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; -import mage.constants.CardType; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.Target; import mage.util.CardUtil; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class KopalaWardenOfWaves extends CardImpl { @@ -40,10 +32,10 @@ public final class KopalaWardenOfWaves extends CardImpl { this.toughness = new MageInt(2); // Spells your opponents cast that target a Merfolk you control cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KopalaWardenOfWavesCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KopalaWardenOfWavesCostModificationEffect1())); // Abilities your opponents activate that target a Merfolk you control cost {2} more to activate. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KopalaWardenOfWavesCostReductionEffect2())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KopalaWardenOfWavesCostModificationEffect2())); } public KopalaWardenOfWaves(final KopalaWardenOfWaves card) { @@ -54,77 +46,51 @@ public final class KopalaWardenOfWaves extends CardImpl { public KopalaWardenOfWaves copy() { return new KopalaWardenOfWaves(this); } -} -class KopalaWardenOfWavesCostReductionEffect extends CostModificationEffectImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a Merfolk you control"); - private static final String effectText = "Spells your opponents cast that target a Merfolk you control cost {2} more to cast"; - - static { - filter.add(SubType.MERFOLK.getPredicate()); + static boolean isTargetCompatible(Permanent permanent, Ability source, Game game) { + // target a Merfolk you control + return permanent.isControlledBy(source.getControllerId()) + && permanent.hasSubtype(SubType.MERFOLK, game); } - KopalaWardenOfWavesCostReductionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = effectText; - } - - KopalaWardenOfWavesCostReductionEffect(KopalaWardenOfWavesCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify.getAbilityType() == AbilityType.SPELL) { - if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - Permanent creature = game.getPermanent(targetUUID); - if (creature != null - && filter.match(creature, game) - && creature.isControlledBy(source.getControllerId())) { - return true; - } - } - } - } - } + static boolean isAbilityCompatible(Ability abilityToModify, Ability source, Game game) { + if (!game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { + return false; } - return false; - } - @Override - public KopalaWardenOfWavesCostReductionEffect copy() { - return new KopalaWardenOfWavesCostReductionEffect(this); - } + Set allTargets; + if (game.getStack().getStackObject(abilityToModify.getId()) != null) { + // real cast + allTargets = CardUtil.getAllSelectedTargets(abilityToModify, game); + } else { + // playable + allTargets = CardUtil.getAllPossibleTargets(abilityToModify, game); + // can target without cost increase + if (allTargets.stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> !isTargetCompatible(permanent, source, game))) { + return false; + } + ; + } + + return allTargets.stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> isTargetCompatible(permanent, source, game)); + } } -class KopalaWardenOfWavesCostReductionEffect2 extends CostModificationEffectImpl { +class KopalaWardenOfWavesCostModificationEffect1 extends CostModificationEffectImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a Merfolk you control"); - private static final String effectText = "Abilities your opponents activate that target a Merfolk you control cost {2} more to activate"; - - static { - filter.add(SubType.MERFOLK.getPredicate()); - } - - KopalaWardenOfWavesCostReductionEffect2() { + KopalaWardenOfWavesCostModificationEffect1() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = effectText; + this.staticText = "Spells your opponents cast that target a Merfolk you control cost {2} more to cast"; } - KopalaWardenOfWavesCostReductionEffect2(KopalaWardenOfWavesCostReductionEffect2 effect) { + KopalaWardenOfWavesCostModificationEffect1(KopalaWardenOfWavesCostModificationEffect1 effect) { super(effect); } @@ -136,26 +102,49 @@ class KopalaWardenOfWavesCostReductionEffect2 extends CostModificationEffectImpl @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify.getAbilityType() == AbilityType.ACTIVATED) { - if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - for (Target target : abilityToModify.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - Permanent creature = game.getPermanent(targetUUID); - if (creature != null - && filter.match(creature, game) - && creature.isControlledBy(source.getControllerId())) { - return true; - } - } - } - } + if (!(abilityToModify instanceof SpellAbility)) { + return false; } - return false; + + return KopalaWardenOfWaves.isAbilityCompatible(abilityToModify, source, game); } @Override - public KopalaWardenOfWavesCostReductionEffect2 copy() { - return new KopalaWardenOfWavesCostReductionEffect2(this); + public KopalaWardenOfWavesCostModificationEffect1 copy() { + return new KopalaWardenOfWavesCostModificationEffect1(this); + } + +} + +class KopalaWardenOfWavesCostModificationEffect2 extends CostModificationEffectImpl { + + KopalaWardenOfWavesCostModificationEffect2() { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); + this.staticText = "Abilities your opponents activate that target a Merfolk you control cost {2} more to activate"; + } + + KopalaWardenOfWavesCostModificationEffect2(KopalaWardenOfWavesCostModificationEffect2 effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.increaseCost(abilityToModify, 2); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + if (!(abilityToModify instanceof ActivatedAbility)) { + return false; + } + + return KopalaWardenOfWaves.isAbilityCompatible(abilityToModify, source, game); + } + + @Override + public KopalaWardenOfWavesCostModificationEffect2 copy() { + return new KopalaWardenOfWavesCostModificationEffect2(this); } } diff --git a/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java b/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java index 9f97442b73..fa1649cf91 100644 --- a/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java +++ b/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java @@ -91,7 +91,7 @@ class KozilekDrawEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(7 - controller.getHand().size(), game); + controller.drawCards(7 - controller.getHand().size(), source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/k/KrarkClanIronworks.java b/Mage.Sets/src/mage/cards/k/KrarkClanIronworks.java index adb05023d6..2ac48444d8 100644 --- a/Mage.Sets/src/mage/cards/k/KrarkClanIronworks.java +++ b/Mage.Sets/src/mage/cards/k/KrarkClanIronworks.java @@ -5,11 +5,13 @@ import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetControlledPermanent; @@ -23,7 +25,9 @@ public final class KrarkClanIronworks extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); // Sacrifice an artifact: Add {C}{C}. - Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(2), new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledArtifactPermanent("an artifact")))); + Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(2), + new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledArtifactPermanent("an artifact"))), + new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KraulHarpooner.java b/Mage.Sets/src/mage/cards/k/KraulHarpooner.java index 13c16e28cb..dbb43938a5 100644 --- a/Mage.Sets/src/mage/cards/k/KraulHarpooner.java +++ b/Mage.Sets/src/mage/cards/k/KraulHarpooner.java @@ -95,7 +95,7 @@ class KraulHarpoonerEffect extends OneShotEffect { } int xValue = player.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game); game.addEffect(new BoostSourceEffect(xValue, 0, Duration.EndOfTurn), source); - game.applyEffects(); + game.getState().processAction(game); Permanent creature = game.getPermanent(source.getFirstTarget()); if (creature == null || !player.chooseUse(outcome, "Have " + sourcePerm.getLogName() + " fight " + creature.getLogName() + "?", source, game)) { return true; diff --git a/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java b/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java index 7ba8079a32..10b4d902e5 100644 --- a/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java +++ b/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java @@ -70,7 +70,7 @@ class KrenkoTinStreetKingpinEffect extends OneShotEffect { return false; } new AddCountersSourceEffect(CounterType.P1P1.createInstance()).apply(game, source); - game.applyEffects(); + game.getState().processAction(game); int xValue = permanent.getPower().getValue(); return new CreateTokenEffect(new GoblinToken("WAR"), xValue).apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/k/KreshTheBloodbraided.java b/Mage.Sets/src/mage/cards/k/KreshTheBloodbraided.java index 41dff62957..1eb4113608 100644 --- a/Mage.Sets/src/mage/cards/k/KreshTheBloodbraided.java +++ b/Mage.Sets/src/mage/cards/k/KreshTheBloodbraided.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -12,7 +11,6 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.SuperType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; @@ -24,7 +22,7 @@ import mage.game.permanent.Permanent; public final class KreshTheBloodbraided extends CardImpl { public KreshTheBloodbraided(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{R}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}{G}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WARRIOR); @@ -59,7 +57,7 @@ class KreshTheBloodbraidedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Permanent kreshTheBloodbraided = game.getPermanent(source.getSourceId()); if (permanent != null && kreshTheBloodbraided != null) { int amount = permanent.getPower().getValue(); diff --git a/Mage.Sets/src/mage/cards/k/KrosanReclamation.java b/Mage.Sets/src/mage/cards/k/KrosanReclamation.java index 73e848177c..671b114408 100644 --- a/Mage.Sets/src/mage/cards/k/KrosanReclamation.java +++ b/Mage.Sets/src/mage/cards/k/KrosanReclamation.java @@ -1,7 +1,6 @@ package mage.cards.k; -import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; @@ -10,6 +9,7 @@ import mage.abilities.keyword.FlashbackAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TimingRule; @@ -66,24 +66,9 @@ class KrosanReclamationEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - List targets = source.getTargets().get(1).getTargets(); - boolean shuffle = false; - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - if (player.getGraveyard().contains(card.getId())) { - player.getGraveyard().remove(card); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - shuffle = true; - } - } - } - if (shuffle) { - player.shuffleLibrary(source, game); - } - return true; + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (targetPlayer != null) { + return targetPlayer.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/k/KrovikanVampire.java b/Mage.Sets/src/mage/cards/k/KrovikanVampire.java index b8247dd765..6457d95cca 100644 --- a/Mage.Sets/src/mage/cards/k/KrovikanVampire.java +++ b/Mage.Sets/src/mage/cards/k/KrovikanVampire.java @@ -1,240 +1,240 @@ -package mage.cards.k; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.BeginningOfEndStepTriggeredAbility; -import mage.abilities.condition.Condition; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.SacrificeTargetEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.WatcherScope; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; -import mage.watchers.Watcher; - -/** - * - * @author jeffwadsworth - */ -public final class KrovikanVampire extends CardImpl { - - public KrovikanVampire(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); - - this.subtype.add(SubType.VAMPIRE); - this.power = new MageInt(3); - this.toughness = new MageInt(3); - - // At the beginning of each end step, if a creature dealt damage by Krovikan Vampire this turn died, put that card onto the battlefield under your control. Sacrifice it when you lose control of Krovikan Vampire. - Ability ability = new BeginningOfEndStepTriggeredAbility( - Zone.BATTLEFIELD, - new KrovikanVampireEffect(), - TargetController.ANY, - new KrovikanVampireInterveningIfCondition(), - false); - ability.addWatcher(new KrovikanVampireCreaturesDamagedWatcher()); - ability.addWatcher(new KrovikanVampireCreaturesDiedWatcher()); - this.addAbility(ability); - } - - public KrovikanVampire(final KrovikanVampire card) { - super(card); - } - - @Override - public KrovikanVampire copy() { - return new KrovikanVampire(this); - } -} - -class KrovikanVampireEffect extends OneShotEffect { - - Set creaturesAffected = new HashSet<>(); - - KrovikanVampireEffect() { - super(Outcome.Neutral); - staticText = "put that card onto the battlefield under your control. Sacrifice it when you lose control of {this}"; - } - - KrovikanVampireEffect(KrovikanVampireEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent krovikanVampire = game.getPermanent(source.getSourceId()); - creaturesAffected = (Set) game.getState().getValue(source.getSourceId() + "creatureToGainControl"); - if (creaturesAffected != null - && controller != null - && krovikanVampire != null) { - creaturesAffected.stream().map((creatureId) -> { - controller.moveCards(game.getCard(creatureId), Zone.BATTLEFIELD, source, game, false, false, false, null); - return creatureId; - }).map((creatureId) -> { - OneShotEffect effect = new SacrificeTargetEffect(); - effect.setText("Sacrifice this if Krovikan Vampire leaves the battlefield or its current controller loses control of it."); - effect.setTargetPointer(new FixedTarget(creatureId)); - return effect; - }).map((effect) -> new KrovikanVampireDelayedTriggeredAbility(effect, krovikanVampire.getId())).forEachOrdered((dTA) -> { - game.addDelayedTriggeredAbility(dTA, source); - }); - creaturesAffected.clear(); - return true; - } - return false; - } - - @Override - public KrovikanVampireEffect copy() { - return new KrovikanVampireEffect(this); - } -} - -class KrovikanVampireInterveningIfCondition implements Condition { - - Set creaturesAffected = new HashSet<>(); - - @Override - public boolean apply(Game game, Ability source) { - KrovikanVampireCreaturesDiedWatcher watcherDied = game.getState().getWatcher(KrovikanVampireCreaturesDiedWatcher.class); - KrovikanVampireCreaturesDamagedWatcher watcherDamaged = game.getState().getWatcher(KrovikanVampireCreaturesDamagedWatcher.class); - if (watcherDied != null) { - Set creaturesThatDiedThisTurn = watcherDied.getDiedThisTurn(); - creaturesThatDiedThisTurn.stream().filter((mor) -> (watcherDamaged != null)).forEachOrdered((mor) -> { - watcherDamaged.getDamagedBySource().stream().filter((mor2) -> (mor2 != null - && mor == mor2)).forEachOrdered((_item) -> { - creaturesAffected.add(mor); - }); - }); - if (creaturesAffected != null - && creaturesAffected.size() > 0) { - game.getState().setValue(source.getSourceId() + "creatureToGainControl", creaturesAffected); - return true; - } - } - return false; - } - - @Override - public String toString() { - return "if a creature dealt damage by Krovikan Vampire this turn died"; - } -} - -class KrovikanVampireCreaturesDamagedWatcher extends Watcher { - - private final Set damagedBySource = new HashSet<>(); - - public KrovikanVampireCreaturesDamagedWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == EventType.DAMAGED_CREATURE - && sourceId.equals(event.getSourceId())) { - damagedBySource.add(event.getTargetId()); - } - } - - public Set getDamagedBySource() { - return this.damagedBySource; - } - - @Override - public void reset() { - damagedBySource.clear(); - } -} - -class KrovikanVampireCreaturesDiedWatcher extends Watcher { - - private final Set diedThisTurn = new HashSet<>(); - - public KrovikanVampireCreaturesDiedWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.isDiesEvent() - && zEvent.getTarget() != null - && zEvent.getTarget().isCreature()) { - diedThisTurn.add(zEvent.getTargetId()); - } - } - } - - @Override - public void reset() { - diedThisTurn.clear(); - } - - public Set getDiedThisTurn() { - return this.diedThisTurn; - } -} - -class KrovikanVampireDelayedTriggeredAbility extends DelayedTriggeredAbility { - - UUID krovikanVampire; - - KrovikanVampireDelayedTriggeredAbility(Effect effect, UUID krovikanVampire) { - super(effect, Duration.EndOfGame, true); - this.krovikanVampire = krovikanVampire; - } - - KrovikanVampireDelayedTriggeredAbility(KrovikanVampireDelayedTriggeredAbility ability) { - super(ability); - this.krovikanVampire = ability.krovikanVampire; - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.LOST_CONTROL - || event.getType() == EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == EventType.LOST_CONTROL - && event.getSourceId().equals(krovikanVampire)) { - return true; - } - if (event.getType() == EventType.ZONE_CHANGE - && event.getTargetId().equals(krovikanVampire)) { - return true; - } - return false; - } - - @Override - public KrovikanVampireDelayedTriggeredAbility copy() { - return new KrovikanVampireDelayedTriggeredAbility(this); - } - - @Override - public String getRule() { - return "Control of Krovikan Vampire lost"; - } -} +package mage.cards.k; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.Watcher; + +/** + * + * @author jeffwadsworth + */ +public final class KrovikanVampire extends CardImpl { + + public KrovikanVampire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // At the beginning of each end step, if a creature dealt damage by Krovikan Vampire this turn died, put that card onto the battlefield under your control. Sacrifice it when you lose control of Krovikan Vampire. + Ability ability = new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, + new KrovikanVampireEffect(), + TargetController.ANY, + new KrovikanVampireInterveningIfCondition(), + false); + ability.addWatcher(new KrovikanVampireCreaturesDamagedWatcher()); + ability.addWatcher(new KrovikanVampireCreaturesDiedWatcher()); + this.addAbility(ability); + } + + public KrovikanVampire(final KrovikanVampire card) { + super(card); + } + + @Override + public KrovikanVampire copy() { + return new KrovikanVampire(this); + } +} + +class KrovikanVampireEffect extends OneShotEffect { + + Set creaturesAffected = new HashSet<>(); + + KrovikanVampireEffect() { + super(Outcome.Neutral); + staticText = "put that card onto the battlefield under your control. Sacrifice it when you lose control of {this}"; + } + + KrovikanVampireEffect(KrovikanVampireEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent krovikanVampire = game.getPermanent(source.getSourceId()); + creaturesAffected = (Set) game.getState().getValue(source.getSourceId() + "creatureToGainControl"); + if (creaturesAffected != null + && controller != null + && krovikanVampire != null) { + creaturesAffected.stream().map((creatureId) -> { + controller.moveCards(game.getCard(creatureId), Zone.BATTLEFIELD, source, game, false, false, false, null); + return creatureId; + }).map((creatureId) -> { + OneShotEffect effect = new SacrificeTargetEffect(); + effect.setText("Sacrifice this if Krovikan Vampire leaves the battlefield or its current controller loses control of it."); + effect.setTargetPointer(new FixedTarget(creatureId)); + return effect; + }).map((effect) -> new KrovikanVampireDelayedTriggeredAbility(effect, krovikanVampire.getId())).forEachOrdered((dTA) -> { + game.addDelayedTriggeredAbility(dTA, source); + }); + creaturesAffected.clear(); + return true; + } + return false; + } + + @Override + public KrovikanVampireEffect copy() { + return new KrovikanVampireEffect(this); + } +} + +class KrovikanVampireInterveningIfCondition implements Condition { + + Set creaturesAffected = new HashSet<>(); + + @Override + public boolean apply(Game game, Ability source) { + KrovikanVampireCreaturesDiedWatcher watcherDied = game.getState().getWatcher(KrovikanVampireCreaturesDiedWatcher.class); + KrovikanVampireCreaturesDamagedWatcher watcherDamaged = game.getState().getWatcher(KrovikanVampireCreaturesDamagedWatcher.class); + if (watcherDied != null) { + Set creaturesThatDiedThisTurn = watcherDied.getDiedThisTurn(); + creaturesThatDiedThisTurn.stream().filter((mor) -> (watcherDamaged != null)).forEachOrdered((mor) -> { + watcherDamaged.getDamagedBySource().stream().filter((mor2) -> (mor2 != null + && mor == mor2)).forEachOrdered((_item) -> { + creaturesAffected.add(mor); + }); + }); + if (creaturesAffected != null + && creaturesAffected.size() > 0) { + game.getState().setValue(source.getSourceId() + "creatureToGainControl", creaturesAffected); + return true; + } + } + return false; + } + + @Override + public String toString() { + return "if a creature dealt damage by Krovikan Vampire this turn died"; + } +} + +class KrovikanVampireCreaturesDamagedWatcher extends Watcher { + + private final Set damagedBySource = new HashSet<>(); + + public KrovikanVampireCreaturesDamagedWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == EventType.DAMAGED_CREATURE + && sourceId.equals(event.getSourceId())) { + damagedBySource.add(event.getTargetId()); + } + } + + public Set getDamagedBySource() { + return this.damagedBySource; + } + + @Override + public void reset() { + damagedBySource.clear(); + } +} + +class KrovikanVampireCreaturesDiedWatcher extends Watcher { + + private final Set diedThisTurn = new HashSet<>(); + + public KrovikanVampireCreaturesDiedWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ZONE_CHANGE) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.isDiesEvent() + && zEvent.getTarget() != null + && zEvent.getTarget().isCreature()) { + diedThisTurn.add(zEvent.getTargetId()); + } + } + } + + @Override + public void reset() { + diedThisTurn.clear(); + } + + public Set getDiedThisTurn() { + return this.diedThisTurn; + } +} + +class KrovikanVampireDelayedTriggeredAbility extends DelayedTriggeredAbility { + + UUID krovikanVampire; + + KrovikanVampireDelayedTriggeredAbility(Effect effect, UUID krovikanVampire) { + super(effect, Duration.EndOfGame, true); + this.krovikanVampire = krovikanVampire; + } + + KrovikanVampireDelayedTriggeredAbility(KrovikanVampireDelayedTriggeredAbility ability) { + super(ability); + this.krovikanVampire = ability.krovikanVampire; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.LOST_CONTROL + || event.getType() == EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == EventType.LOST_CONTROL + && event.getSourceId().equals(krovikanVampire)) { + return true; + } + if (event.getType() == EventType.ZONE_CHANGE + && event.getTargetId().equals(krovikanVampire)) { + return true; + } + return false; + } + + @Override + public KrovikanVampireDelayedTriggeredAbility copy() { + return new KrovikanVampireDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Control of Krovikan Vampire lost"; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KrrikSonOfYawgmoth.java b/Mage.Sets/src/mage/cards/k/KrrikSonOfYawgmoth.java index 20e98faf09..f4aafa6519 100644 --- a/Mage.Sets/src/mage/cards/k/KrrikSonOfYawgmoth.java +++ b/Mage.Sets/src/mage/cards/k/KrrikSonOfYawgmoth.java @@ -1,102 +1,102 @@ -package mage.cards.k; - -import java.util.UUID; -import mage.MageInt; -import mage.ObjectColor; -import mage.abilities.Ability; -import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.keyword.LifelinkAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SubLayer; -import mage.constants.SuperType; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.FilterMana; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * - * @author ssouders412 - */ -public final class KrrikSonOfYawgmoth extends CardImpl { - - private static final FilterSpell filterSpell = new FilterSpell("a black spell"); - - static { - filterSpell.add(new ColorPredicate(ObjectColor.BLACK)); - } - - public KrrikSonOfYawgmoth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B/P}{B/P}{B/P}"); - - this.addSuperType(SuperType.LEGENDARY); - this.subtype.add(SubType.HORROR); - this.subtype.add(SubType.MINION); - this.power = new MageInt(2); - this.toughness = new MageInt(2); - - // ({B/P} can be paid with either {B} or 2 life.) - - // Lifelink - this.addAbility(LifelinkAbility.getInstance()); - - // For each {B} in a cost, you may pay 2 life rather than pay that mana. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KrrikSonOfYawgmothPhyrexianEffect())); - - // Whenever you cast a black spell, put a +1/+1 counter on K'rrik, Son of Yawgmoth. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filterSpell, false)); - } - - private KrrikSonOfYawgmoth(final KrrikSonOfYawgmoth card) { - super(card); - } - - @Override - public KrrikSonOfYawgmoth copy() { - return new KrrikSonOfYawgmoth(this); - } -} - -class KrrikSonOfYawgmothPhyrexianEffect extends ContinuousEffectImpl { - - public KrrikSonOfYawgmothPhyrexianEffect() { - super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit); - this.staticText = "for each {B} in a cost, you may pay 2 life rather than pay that mana"; - } - - public KrrikSonOfYawgmothPhyrexianEffect(final KrrikSonOfYawgmothPhyrexianEffect effect) { - super(effect); - } - - @Override - public KrrikSonOfYawgmothPhyrexianEffect copy() { - return new KrrikSonOfYawgmothPhyrexianEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - Player controller = game.getPlayer(source.getControllerId()); - FilterMana phyrexianBlack = new FilterMana(); - - phyrexianBlack.setBlack(true); - if (controller != null && sourcePermanent != null) { - controller.addPhyrexianToColors(phyrexianBlack); - return true; - } - return false; - } -} +package mage.cards.k; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SubLayer; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterMana; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author ssouders412 + */ +public final class KrrikSonOfYawgmoth extends CardImpl { + + private static final FilterSpell filterSpell = new FilterSpell("a black spell"); + + static { + filterSpell.add(new ColorPredicate(ObjectColor.BLACK)); + } + + public KrrikSonOfYawgmoth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B/P}{B/P}{B/P}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HORROR); + this.subtype.add(SubType.MINION); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // ({B/P} can be paid with either {B} or 2 life.) + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // For each {B} in a cost, you may pay 2 life rather than pay that mana. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KrrikSonOfYawgmothPhyrexianEffect())); + + // Whenever you cast a black spell, put a +1/+1 counter on K'rrik, Son of Yawgmoth. + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filterSpell, false)); + } + + private KrrikSonOfYawgmoth(final KrrikSonOfYawgmoth card) { + super(card); + } + + @Override + public KrrikSonOfYawgmoth copy() { + return new KrrikSonOfYawgmoth(this); + } +} + +class KrrikSonOfYawgmothPhyrexianEffect extends ContinuousEffectImpl { + + public KrrikSonOfYawgmothPhyrexianEffect() { + super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit); + this.staticText = "for each {B} in a cost, you may pay 2 life rather than pay that mana"; + } + + public KrrikSonOfYawgmothPhyrexianEffect(final KrrikSonOfYawgmothPhyrexianEffect effect) { + super(effect); + } + + @Override + public KrrikSonOfYawgmothPhyrexianEffect copy() { + return new KrrikSonOfYawgmothPhyrexianEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + FilterMana phyrexianBlack = new FilterMana(); + + phyrexianBlack.setBlack(true); + if (controller != null && sourcePermanent != null) { + controller.addPhyrexianToColors(phyrexianBlack); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java b/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java index 37a4218383..6cf352d6dd 100644 --- a/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java +++ b/Mage.Sets/src/mage/cards/k/KuldothaPhoenix.java @@ -1,7 +1,5 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.condition.CompoundCondition; @@ -10,24 +8,22 @@ import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityWord; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.PhaseStep; -import mage.constants.Zone; +import mage.constants.*; + +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class KuldothaPhoenix extends CardImpl { public KuldothaPhoenix(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}{R}"); this.subtype.add(SubType.PHOENIX); this.power = new MageInt(4); @@ -36,16 +32,17 @@ public final class KuldothaPhoenix extends CardImpl { // Flying, haste this.addAbility(FlyingAbility.getInstance()); this.addAbility(HasteAbility.getInstance()); - + // Metalcraft — {4}: Return Kuldotha Phoenix from your graveyard to the battlefield. // Activate this ability only during your upkeep and only if you control three or more artifacts. - Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, - new ReturnSourceFromGraveyardToBattlefieldEffect(), - new ManaCostsImpl("{4}"), + Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, + new ReturnSourceFromGraveyardToBattlefieldEffect(), + new ManaCostsImpl("{4}"), new CompoundCondition("during your upkeep and only if you control three or more artifacts", new IsStepCondition(PhaseStep.UPKEEP), MetalcraftCondition.instance) ); ability.setAbilityWord(AbilityWord.METALCRAFT); + ability.addHint(MetalcraftHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KumanoMasterYamabushi.java b/Mage.Sets/src/mage/cards/k/KumanoMasterYamabushi.java index 371a716cbf..a833e83fdb 100644 --- a/Mage.Sets/src/mage/cards/k/KumanoMasterYamabushi.java +++ b/Mage.Sets/src/mage/cards/k/KumanoMasterYamabushi.java @@ -1,5 +1,5 @@ /* - * + * * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -25,12 +25,11 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. - * + * */ package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -40,21 +39,19 @@ import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.replacement.DealtDamageToCreatureBySourceDies; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.target.common.TargetAnyTarget; import mage.watchers.common.DamagedByWatcher; +import java.util.UUID; + /** * @author LevelX */ public final class KumanoMasterYamabushi extends CardImpl { public KumanoMasterYamabushi(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SHAMAN); @@ -62,13 +59,13 @@ public final class KumanoMasterYamabushi extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // {{1}{R}: Kumano, Master Yamabushi deals 1 damage to any target. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("{1}{R}") ); + // {1}{R}: Kumano, Master Yamabushi deals 1 damage to any target. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("{1}{R}")); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); // If a creature dealt damage by Kumano this turn would die, exile it instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DealtDamageToCreatureBySourceDies(this, Duration.WhileOnBattlefield)), new DamagedByWatcher(false)); - + } public KumanoMasterYamabushi(final KumanoMasterYamabushi card) { diff --git a/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java b/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java index fe8e6d8b15..f6eddc8ae6 100644 --- a/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java +++ b/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java @@ -26,7 +26,7 @@ public final class KunorosHoundOfAthreos extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); this.addSuperType(SuperType.LEGENDARY); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/k/KydeleChosenOfKruphix.java b/Mage.Sets/src/mage/cards/k/KydeleChosenOfKruphix.java index 52a80f2758..43411eb8cb 100644 --- a/Mage.Sets/src/mage/cards/k/KydeleChosenOfKruphix.java +++ b/Mage.Sets/src/mage/cards/k/KydeleChosenOfKruphix.java @@ -31,8 +31,8 @@ public final class KydeleChosenOfKruphix extends CardImpl { // {T}: Add {C} for each card you've drawn this turn. DynamicManaAbility ability = new DynamicManaAbility( - Mana.ColorlessMana(1), CardsDrawnThisTurnDynamicValue.instance, new TapSourceCost() - ); + Mana.ColorlessMana(1), CardsDrawnThisTurnDynamicValue.instance, + new TapSourceCost(), null, false, CardsDrawnThisTurnDynamicValue.instance); this.addAbility(ability, new CardsDrawnThisTurnWatcher()); // Partner diff --git a/Mage.Sets/src/mage/cards/k/KykarWindsFury.java b/Mage.Sets/src/mage/cards/k/KykarWindsFury.java index dd0f84d93e..1a6b2265e8 100644 --- a/Mage.Sets/src/mage/cards/k/KykarWindsFury.java +++ b/Mage.Sets/src/mage/cards/k/KykarWindsFury.java @@ -20,6 +20,7 @@ import mage.game.permanent.token.SpiritWhiteToken; import mage.target.common.TargetControlledPermanent; import java.util.UUID; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; /** * @author TheElk801 @@ -49,7 +50,7 @@ public final class KykarWindsFury extends CardImpl { // Sacrifice a Spirit: Add {R}. this.addAbility(new SimpleManaAbility( - Zone.BATTLEFIELD, new BasicManaEffect(Mana.RedMana(1)), + Zone.BATTLEFIELD, new BasicManaEffect(Mana.RedMana(1), new PermanentsOnBattlefieldCount(filter)), new SacrificeTargetCost(new TargetControlledPermanent(filter)) )); } diff --git a/Mage.Sets/src/mage/cards/k/KynaiosAndTiroOfMeletis.java b/Mage.Sets/src/mage/cards/k/KynaiosAndTiroOfMeletis.java index 7fe353b16e..df0d477d0f 100644 --- a/Mage.Sets/src/mage/cards/k/KynaiosAndTiroOfMeletis.java +++ b/Mage.Sets/src/mage/cards/k/KynaiosAndTiroOfMeletis.java @@ -70,7 +70,7 @@ class KynaiosAndTirosEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); PlayerList playerList = game.getState().getPlayerList().copy(); while (!playerList.get().equals(source.getControllerId()) && controller.canRespond()) { @@ -113,7 +113,7 @@ class KynaiosAndTirosEffect extends OneShotEffect { for (UUID playerId : noLandPlayers) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/k/KyrenToy.java b/Mage.Sets/src/mage/cards/k/KyrenToy.java index af6dd7d7dd..25211ebb84 100644 --- a/Mage.Sets/src/mage/cards/k/KyrenToy.java +++ b/Mage.Sets/src/mage/cards/k/KyrenToy.java @@ -9,7 +9,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.ManaEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.mana.BasicManaAbility; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -32,12 +32,12 @@ public final class KyrenToy extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {1}, {T}: Put a charge counter on Kyren Toy. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)), new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); // {T}, Remove X charge counters from Kyren Toy: Add X mana of {C}, and then add {C}. - ability = new KyrenToyManaAbility(); + ability = new SimpleManaAbility(Zone.BATTLEFIELD, new KyrenToyManaEffect(), new TapSourceCost()); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE.createInstance(1))); this.addAbility(ability); } @@ -51,30 +51,14 @@ public final class KyrenToy extends CardImpl { return new KyrenToy(this); } - private class KyrenToyManaAbility extends BasicManaAbility { - - KyrenToyManaAbility() { - super(new KyrenToyManaEffect()); - } - - KyrenToyManaAbility(final KyrenToyManaAbility ability) { - super(ability); - } - - @Override - public KyrenToyManaAbility copy() { - return new KyrenToyManaAbility(this); - } - } - private static class KyrenToyManaEffect extends ManaEffect { - KyrenToyManaEffect() { + private KyrenToyManaEffect() { super(); staticText = "Add an amount of {C} equal to X plus one"; } - KyrenToyManaEffect(final KyrenToyManaEffect effect) { + private KyrenToyManaEffect(final KyrenToyManaEffect effect) { super(effect); } @@ -98,16 +82,16 @@ public final class KyrenToy extends CardImpl { return mana; } Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - int numberOfMana = 0; - for (Cost cost : source.getCosts()) { - if (cost instanceof RemoveVariableCountersSourceCost) { - numberOfMana = ((RemoveVariableCountersSourceCost) cost).getAmount(); - } - } - return new Mana(Mana.ColorlessMana(numberOfMana + 1)); + if (player == null) { + return mana; } - return mana; + int numberOfMana = 0; + for (Cost cost : source.getCosts()) { + if (cost instanceof RemoveVariableCountersSourceCost) { + numberOfMana = ((RemoveVariableCountersSourceCost) cost).getAmount(); + } + } + return new Mana(Mana.ColorlessMana(numberOfMana + 1)); } @Override diff --git a/Mage.Sets/src/mage/cards/l/LaboratoryManiac.java b/Mage.Sets/src/mage/cards/l/LaboratoryManiac.java index c01a0e7681..7c372edebe 100644 --- a/Mage.Sets/src/mage/cards/l/LaboratoryManiac.java +++ b/Mage.Sets/src/mage/cards/l/LaboratoryManiac.java @@ -79,14 +79,14 @@ class LaboratoryManiacEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.EMPTY_DRAW; + return event.getType() == EventType.DRAW_CARD; } @Override public boolean applies(GameEvent event, Ability source, Game game) { if (event.getPlayerId().equals(source.getControllerId())) { Player player = game.getPlayer(event.getPlayerId()); - if (player != null && !player.hasLost() && player.isEmptyDraw()) { + if (player != null && !player.hasLost() && !player.getLibrary().hasCards()) { return true; } } diff --git a/Mage.Sets/src/mage/cards/l/LabyrinthRaptor.java b/Mage.Sets/src/mage/cards/l/LabyrinthRaptor.java new file mode 100644 index 0000000000..0ad3bdccf8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LabyrinthRaptor.java @@ -0,0 +1,109 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BecomesBlockedAllTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.permanent.BlockingAttackerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LabyrinthRaptor extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature you control with menace"); + + static { + filter.add(new AbilityPredicate(MenaceAbility.class)); + filter.add(TargetController.YOU.getControllerPredicate()); + } + + private static final FilterCreaturePermanent filter2 = filter.copy(); + + static { + filter2.setMessage("creatures you control with menace"); + } + + public LabyrinthRaptor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{R}"); + + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever a creature you control with menace becomes blocked, defending player sacrifices a creature blocking it. + this.addAbility(new BecomesBlockedAllTriggeredAbility( + new LabyrinthRaptorEffect(), false, filter, true + )); + + // {B}{R}: Creatures you control with menace get +1/+0 until end of turn. + this.addAbility(new SimpleActivatedAbility(new BoostAllEffect( + 1, 0, Duration.EndOfTurn, filter2, false + ), new ManaCostsImpl("{B}{R}"))); + } + + private LabyrinthRaptor(final LabyrinthRaptor card) { + super(card); + } + + @Override + public LabyrinthRaptor copy() { + return new LabyrinthRaptor(this); + } +} + +class LabyrinthRaptorEffect extends OneShotEffect { + + LabyrinthRaptorEffect() { + super(Outcome.Benefit); + staticText = "defending player sacrifices a creature blocking it"; + } + + private LabyrinthRaptorEffect(final LabyrinthRaptorEffect effect) { + super(effect); + } + + @Override + public LabyrinthRaptorEffect copy() { + return new LabyrinthRaptorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (permanent == null) { + return false; + } + Player player = game.getPlayer(game.getCombat().getDefendingPlayerId(permanent.getId(), game)); + if (player == null) { + return false; + } + FilterPermanent filterPermanent = new FilterPermanent("creature blocking " + permanent.getIdName()); + filterPermanent.add(new BlockingAttackerIdPredicate(permanent.getId())); + Effect effect = new SacrificeEffect(filterPermanent, 1, ""); + effect.setTargetPointer(new FixedTarget(player.getId(), game)); + return effect.apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LaccolithGrunt.java b/Mage.Sets/src/mage/cards/l/LaccolithGrunt.java index 6c0570f65a..98c271a2e8 100644 --- a/Mage.Sets/src/mage/cards/l/LaccolithGrunt.java +++ b/Mage.Sets/src/mage/cards/l/LaccolithGrunt.java @@ -4,7 +4,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; import mage.cards.CardImpl; @@ -32,7 +32,7 @@ public final class LaccolithGrunt extends CardImpl { this.toughness = new MageInt(2); // Whenever Laccolith Grunt becomes blocked, you may have it deal damage equal to its power to target creature. If you do, Laccolith Grunt assigns no combat damage this turn. - Ability ability = new BecomesBlockedTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true); + Ability ability = new BecomesBlockedSourceTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true); ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/l/LaccolithTitan.java b/Mage.Sets/src/mage/cards/l/LaccolithTitan.java index 2af5b5a8e3..886255e887 100644 --- a/Mage.Sets/src/mage/cards/l/LaccolithTitan.java +++ b/Mage.Sets/src/mage/cards/l/LaccolithTitan.java @@ -4,7 +4,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; import mage.cards.CardImpl; @@ -32,7 +32,7 @@ public final class LaccolithTitan extends CardImpl { this.toughness = new MageInt(6); // Whenever Laccolith Grunt becomes blocked, you may have it deal damage equal to its power to target creature. If you do, Laccolith Grunt assigns no combat damage this turn. - Ability ability = new BecomesBlockedTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true); + Ability ability = new BecomesBlockedSourceTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true); ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/l/LaccolithWarrior.java b/Mage.Sets/src/mage/cards/l/LaccolithWarrior.java index 435b53cb71..cb65c938df 100644 --- a/Mage.Sets/src/mage/cards/l/LaccolithWarrior.java +++ b/Mage.Sets/src/mage/cards/l/LaccolithWarrior.java @@ -4,7 +4,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; import mage.cards.CardImpl; @@ -33,7 +33,7 @@ public final class LaccolithWarrior extends CardImpl { this.toughness = new MageInt(3); // Whenever Laccolith Grunt becomes blocked, you may have it deal damage equal to its power to target creature. If you do, Laccolith Grunt assigns no combat damage this turn. - Ability ability = new BecomesBlockedTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true); + Ability ability = new BecomesBlockedSourceTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true); ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/l/LaccolithWhelp.java b/Mage.Sets/src/mage/cards/l/LaccolithWhelp.java index 19472a6399..fe6d96c22a 100644 --- a/Mage.Sets/src/mage/cards/l/LaccolithWhelp.java +++ b/Mage.Sets/src/mage/cards/l/LaccolithWhelp.java @@ -4,7 +4,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; import mage.cards.CardImpl; @@ -32,7 +32,7 @@ public final class LaccolithWhelp extends CardImpl { this.toughness = new MageInt(1); // Whenever Laccolith Grunt becomes blocked, you may have it deal damage equal to its power to target creature. If you do, Laccolith Grunt assigns no combat damage this turn. - Ability ability = new BecomesBlockedTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true); + Ability ability = new BecomesBlockedSourceTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true); ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/l/LammastideWeave.java b/Mage.Sets/src/mage/cards/l/LammastideWeave.java index 9cd195c43b..fc9365efdb 100644 --- a/Mage.Sets/src/mage/cards/l/LammastideWeave.java +++ b/Mage.Sets/src/mage/cards/l/LammastideWeave.java @@ -9,7 +9,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; @@ -35,7 +34,7 @@ public final class LammastideWeave extends CardImpl { } - public LammastideWeave(final LammastideWeave card) { + private LammastideWeave(final LammastideWeave card) { super(card); } @@ -47,13 +46,13 @@ public final class LammastideWeave extends CardImpl { class LammastideWeaveEffect extends OneShotEffect { - public LammastideWeaveEffect() { + LammastideWeaveEffect() { super(Outcome.DrawCard); - this.staticText = ", then target player puts the top card of their library into their graveyard. " - + "If that card has the chosen name, you gain life equal to its converted mana cost."; + this.staticText = ", then target player mills a card. If a card with the chosen name was milled this way, " + + "you gain life equal to its converted mana cost."; } - public LammastideWeaveEffect(final LammastideWeaveEffect effect) { + private LammastideWeaveEffect(final LammastideWeaveEffect effect) { super(effect); } @@ -67,17 +66,14 @@ class LammastideWeaveEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(source.getFirstTarget()); String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - if (controller != null && targetPlayer != null && cardName != null && !cardName.isEmpty()) { - Card card = targetPlayer.getLibrary().getFromTop(game); - if (card != null) { - controller.moveCards(card, Zone.GRAVEYARD, source, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { - controller.gainLife(card.getConvertedManaCost(), game, source); - } - } - return true; + if (controller == null || targetPlayer == null || cardName == null || cardName.isEmpty()) { + return false; } - return false; + for (Card card : targetPlayer.millCards(1, source, game).getCards(game)) { + if (CardUtil.haveSameNames(card, cardName, game)) { + controller.gainLife(card.getConvertedManaCost(), game, source); + } + } + return true; } - } diff --git a/Mage.Sets/src/mage/cards/l/LandsEdge.java b/Mage.Sets/src/mage/cards/l/LandsEdge.java index d0e026ad47..e6320be8fd 100644 --- a/Mage.Sets/src/mage/cards/l/LandsEdge.java +++ b/Mage.Sets/src/mage/cards/l/LandsEdge.java @@ -54,7 +54,7 @@ class LandsEdgeEffect extends OneShotEffect { public LandsEdgeEffect() { super(Outcome.Neutral); - staticText = "If the discarded card was a land card, Land's Edge deals 2 damage to target player."; + staticText = "If the discarded card was a land card, {this} deals 2 damage to target player"; } public LandsEdgeEffect(final LandsEdgeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LaquatussCreativity.java b/Mage.Sets/src/mage/cards/l/LaquatussCreativity.java index b2f75203a9..ec94e5aef9 100644 --- a/Mage.Sets/src/mage/cards/l/LaquatussCreativity.java +++ b/Mage.Sets/src/mage/cards/l/LaquatussCreativity.java @@ -59,7 +59,7 @@ class LaquatussCreativityEffect extends OneShotEffect { Player player = game.getPlayer(source.getFirstTarget()); if(player != null) { int handCount = player.getHand().count(new FilterCard(), game); - player.drawCards(handCount, game); + player.drawCards(handCount, source.getSourceId(), game); player.discard(handCount, false, source, game); } return false; diff --git a/Mage.Sets/src/mage/cards/l/LastBreath.java b/Mage.Sets/src/mage/cards/l/LastBreath.java index cc1fdf2727..02fc0d9ebe 100644 --- a/Mage.Sets/src/mage/cards/l/LastBreath.java +++ b/Mage.Sets/src/mage/cards/l/LastBreath.java @@ -1,4 +1,3 @@ - package mage.cards.l; import java.util.UUID; @@ -10,7 +9,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; import mage.game.Game; @@ -25,13 +23,13 @@ import mage.target.TargetPermanent; public final class LastBreath extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with power 2 or less"); + static { filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 3)); } public LastBreath(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); // Exile target creature with power 2 or less. Its controller gains 4 life. this.getSpellAbility().addEffect(new ExileTargetEffect()); @@ -51,6 +49,7 @@ public final class LastBreath extends CardImpl { } class LastBreathEffect extends OneShotEffect { + LastBreathEffect() { super(Outcome.GainLife); staticText = "Its controller gains 4 life"; @@ -62,7 +61,7 @@ class LastBreathEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent target = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); + Permanent target = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (target != null) { Player player = game.getPlayer(target.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/l/LastRites.java b/Mage.Sets/src/mage/cards/l/LastRites.java index 7cb78edc19..81d3dcb06b 100644 --- a/Mage.Sets/src/mage/cards/l/LastRites.java +++ b/Mage.Sets/src/mage/cards/l/LastRites.java @@ -1,29 +1,30 @@ package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPlayer; +import mage.target.common.TargetDiscard; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author L_J */ public final class LastRites extends CardImpl { @@ -32,14 +33,13 @@ public final class LastRites extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Discard any number of cards. Target player reveals their hand, then - //you choose a nonland card from it for each card discarded + // you choose a nonland card from it for each card discarded // this way. That player discards those cards. this.getSpellAbility().addEffect(new LastRitesEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - } - public LastRites(final LastRites card) { + private LastRites(final LastRites card) { super(card); } @@ -58,7 +58,7 @@ class LastRitesEffect extends OneShotEffect { + "each card discarded this way. That player discards those cards"; } - LastRitesEffect(final LastRitesEffect effect) { + private LastRitesEffect(final LastRitesEffect effect) { super(effect); } @@ -71,28 +71,21 @@ class LastRitesEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (controller != null && targetPlayer != null) { - Cards cardsInHand = controller.getHand().copy(); - TargetCard target = new TargetCard(0, Integer.MAX_VALUE, Zone.HAND, new FilterCard("cards to discard")); - controller.chooseTarget(Outcome.AIDontUseIt, cardsInHand, target, source, game); - int discardCount = target.getTargets().size(); - if (discardCount > 0) { - for (UUID cardId : target.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - controller.discard(card, source, game); - } - } - FilterCard filter = new FilterCard((discardCount > 1 ? "" : "a") - + " nonland card" + (discardCount > 1 ? "s" : "")); - filter.add(Predicates.not(CardType.LAND.getPredicate())); - StaticValue discardValue = StaticValue.get(discardCount); - Effect effect = new DiscardCardYouChooseTargetEffect(discardValue, filter, TargetController.ANY); - effect.setTargetPointer(new FixedTarget(targetPlayer.getId())); - effect.apply(game, source); - } - return true; + if (controller == null || targetPlayer == null) { + return false; } - return false; + Cards cardsInHand = controller.getHand().copy(); + TargetCard target = new TargetDiscard( + 0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CARDS, controller.getId() + ); + controller.chooseTarget(Outcome.AIDontUseIt, cardsInHand, target, source, game); + int discardCount = controller.discard(new CardsImpl(target.getTargets()), source, game).size(); + FilterCard filter = new FilterCard((discardCount > 1 ? "" : "a") + + " nonland card" + (discardCount > 1 ? "s" : "")); + filter.add(Predicates.not(CardType.LAND.getPredicate())); + Effect effect = new DiscardCardYouChooseTargetEffect(StaticValue.get(discardCount), filter, TargetController.ANY); + effect.setTargetPointer(new FixedTarget(targetPlayer.getId())); + effect.apply(game, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/l/LastStand.java b/Mage.Sets/src/mage/cards/l/LastStand.java index aca592d659..1c4ca32025 100644 --- a/Mage.Sets/src/mage/cards/l/LastStand.java +++ b/Mage.Sets/src/mage/cards/l/LastStand.java @@ -101,7 +101,7 @@ class LastStandEffect extends OneShotEffect { // Draw a card for each Island you control, then discard that many cards int islands = game.getBattlefield().count(filterIsland, source.getSourceId(), source.getControllerId(), game); if (islands > 0) { - controller.drawCards(islands, game); + controller.drawCards(islands, source.getSourceId(), game); controller.discard(islands, false, source, game); } diff --git a/Mage.Sets/src/mage/cards/l/LastWord.java b/Mage.Sets/src/mage/cards/l/LastWord.java index aa94e9991a..bf8cb38408 100644 --- a/Mage.Sets/src/mage/cards/l/LastWord.java +++ b/Mage.Sets/src/mage/cards/l/LastWord.java @@ -3,7 +3,7 @@ package mage.cards.l; import java.util.UUID; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.common.CounterTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -19,7 +19,7 @@ public final class LastWord extends CardImpl { public LastWord (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}{U}"); - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addTarget(new TargetSpell()); } diff --git a/Mage.Sets/src/mage/cards/l/LatchkeyFaerie.java b/Mage.Sets/src/mage/cards/l/LatchkeyFaerie.java index ca664a65f4..aab8921435 100644 --- a/Mage.Sets/src/mage/cards/l/LatchkeyFaerie.java +++ b/Mage.Sets/src/mage/cards/l/LatchkeyFaerie.java @@ -1,12 +1,11 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.ProwlCondition; +import mage.abilities.condition.common.ProwlCostWasPaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.common.ProwlCostWasPaidHint; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.ProwlAbility; import mage.cards.CardImpl; @@ -14,14 +13,15 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class LatchkeyFaerie extends CardImpl { public LatchkeyFaerie(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); this.subtype.add(SubType.FAERIE); this.subtype.add(SubType.ROGUE); @@ -36,8 +36,9 @@ public final class LatchkeyFaerie extends CardImpl { // When Latchkey Faerie enters the battlefield, if its prowl cost was paid, draw a card. EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, ProwlCondition.instance, - "When {this} enters the battlefield, if its prowl cost was paid, draw a card.")); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, ProwlCostWasPaidCondition.instance, + "When {this} enters the battlefield, if its prowl cost was paid, draw a card.") + .addHint(ProwlCostWasPaidHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/l/LaunchTheFleet.java b/Mage.Sets/src/mage/cards/l/LaunchTheFleet.java index 569ec7c9ac..cd47e2ba10 100644 --- a/Mage.Sets/src/mage/cards/l/LaunchTheFleet.java +++ b/Mage.Sets/src/mage/cards/l/LaunchTheFleet.java @@ -1,7 +1,5 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.Effect; @@ -14,8 +12,9 @@ import mage.constants.Duration; import mage.game.permanent.token.SoldierToken; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class LaunchTheFleet extends CardImpl { diff --git a/Mage.Sets/src/mage/cards/l/LavaHounds.java b/Mage.Sets/src/mage/cards/l/LavaHounds.java index 6e62d95e67..b7b481c90f 100644 --- a/Mage.Sets/src/mage/cards/l/LavaHounds.java +++ b/Mage.Sets/src/mage/cards/l/LavaHounds.java @@ -19,7 +19,7 @@ public final class LavaHounds extends CardImpl { public LavaHounds(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(4); this.toughness = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/l/LavaSerpent.java b/Mage.Sets/src/mage/cards/l/LavaSerpent.java new file mode 100644 index 0000000000..7451bc2e68 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LavaSerpent.java @@ -0,0 +1,42 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LavaSerpent extends CardImpl { + + public LavaSerpent(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.SERPENT); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private LavaSerpent(final LavaSerpent card) { + super(card); + } + + @Override + public LavaSerpent copy() { + return new LavaSerpent(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LavabellySliver.java b/Mage.Sets/src/mage/cards/l/LavabellySliver.java index 34d10475d3..86d764c891 100644 --- a/Mage.Sets/src/mage/cards/l/LavabellySliver.java +++ b/Mage.Sets/src/mage/cards/l/LavabellySliver.java @@ -30,12 +30,13 @@ public final class LavabellySliver extends CardImpl { this.toughness = new MageInt(2); // Sliver creatures you control have "When this creature enters the battlefield, it deals 1 damage to target player or planeswalker and you gain 1 life." - Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(1, "it")); + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(1, "When this creature enters the battlefield, it"),false,true); ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addTarget(new TargetPlayerOrPlaneswalker()); this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS - ))); + ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS) + .withForceQuotes() + )); } private LavabellySliver(final LavabellySliver card) { diff --git a/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java b/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java new file mode 100644 index 0000000000..6dc1f35714 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java @@ -0,0 +1,110 @@ +package mage.cards.l; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageAllEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LavabrinkFloodgates extends CardImpl { + + public LavabrinkFloodgates(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}{R}"); + + // {T}: Add {R}{R}. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana(2), new TapSourceCost())); + + // At the beginning of each player's upkeep, that player may put a doom counter on Lavabrink Floodgates or remove a doom counter from it. Then if it has three or more doom counters on it, sacrifice it. When you do, it deals 6 damage to each creature. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new LavabrinkFloodgatesEffect(), TargetController.ACTIVE, false + )); + } + + private LavabrinkFloodgates(final LavabrinkFloodgates card) { + super(card); + } + + @Override + public LavabrinkFloodgates copy() { + return new LavabrinkFloodgates(this); + } +} + +class LavabrinkFloodgatesEffect extends OneShotEffect { + + LavabrinkFloodgatesEffect() { + super(Outcome.Benefit); + staticText = "that player may put a doom counter on {this} or remove a doom counter from it. " + + "Then if it has three or more doom counters on it, sacrifice {this}. " + + "When you do, it deals 6 damage to each creature."; + } + + private LavabrinkFloodgatesEffect(final LavabrinkFloodgatesEffect effect) { + super(effect); + } + + @Override + public LavabrinkFloodgatesEffect copy() { + return new LavabrinkFloodgatesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(game.getActivePlayerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (player == null || permanent == null) { + return false; + } + Choice choice = new ChoiceImpl(); + choice.setChoices(new HashSet(Arrays.asList( + "Add a doom counter", + "Remove a doom counter", + "Do nothing" + ))); + player.choose(outcome, choice, game); + switch (choice.getChoice()) { + case "Add a doom counter": + permanent.addCounters(CounterType.DOOM.createInstance(), source, game); + break; + case "Remove a doom counter": + permanent.removeCounters(CounterType.DOOM.createInstance(), game); + break; + case "Do nothing": + default: + break; + } + if (permanent.getCounters(game).getCount(CounterType.DOOM) < 3 + || !permanent.sacrifice(source.getSourceId(), game)) { + return true; + } + game.fireReflexiveTriggeredAbility(new ReflexiveTriggeredAbility( + new DamageAllEffect( + 6, StaticFilters.FILTER_PERMANENT_CREATURE + ), false, "it deals 6 damage to each creature." + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java b/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java new file mode 100644 index 0000000000..ef18a42db3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java @@ -0,0 +1,99 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.ChooseModeEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterObject; +import mage.filter.predicate.mageobject.ConvertedManaCostParityPredicate; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LavabrinkVenturer extends CardImpl { + + public LavabrinkVenturer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // As Lavabrink Venturer enters the battlefield, choose odd or even. + this.addAbility(new AsEntersBattlefieldAbility( + new ChooseModeEffect("Odd or even?", "Odd", "Even"), + "choose odd or even. (Zero is even.)" + )); + + // Lavabrink Venturer has protection from each converted mana cost of the chosen value. + this.addAbility(new SimpleStaticAbility(new LavabrinkVenturerEffect())); + } + + private LavabrinkVenturer(final LavabrinkVenturer card) { + super(card); + } + + @Override + public LavabrinkVenturer copy() { + return new LavabrinkVenturer(this); + } +} + +class LavabrinkVenturerEffect extends GainAbilitySourceEffect { + + private static final FilterObject nullFilter = new FilterObject("nothing"); + private static final FilterObject oddFilter = new FilterObject("odd converted mana costs"); + private static final FilterObject evenFilter = new FilterObject("even converted mana costs"); + + static { + nullFilter.add(ConvertedManaCostParityPredicate.ODD); + nullFilter.add(ConvertedManaCostParityPredicate.EVEN); + oddFilter.add(ConvertedManaCostParityPredicate.ODD); + evenFilter.add(ConvertedManaCostParityPredicate.EVEN); + } + + LavabrinkVenturerEffect() { + super(new ProtectionAbility(nullFilter)); + this.ability.setRuleVisible(false); + staticText = "{this} has protection from each converted mana cost of the chosen value. (Zero is even.)"; + } + + private LavabrinkVenturerEffect(final LavabrinkVenturerEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + String choosenMode = (String) game.getState().getValue(source.getSourceId() + "_modeChoice"); + if (choosenMode == null) { + return false; + } + switch (choosenMode) { + case "Odd": + this.ability = new ProtectionAbility(oddFilter); + break; + case "Even": + this.ability = new ProtectionAbility(evenFilter); + break; + default: + return false; + } + return super.apply(game, source); + } + + @Override + public LavabrinkVenturerEffect copy() { + return new LavabrinkVenturerEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LawlessBroker.java b/Mage.Sets/src/mage/cards/l/LawlessBroker.java index 12ebf3c256..d1ddc1b929 100644 --- a/Mage.Sets/src/mage/cards/l/LawlessBroker.java +++ b/Mage.Sets/src/mage/cards/l/LawlessBroker.java @@ -4,7 +4,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -27,7 +27,7 @@ public final class LawlessBroker extends CardImpl { this.toughness = new MageInt(2); // When Lawless Broker dies, put a +1/+1 counter on target creature you control. - Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false); + Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LeadTheStampede.java b/Mage.Sets/src/mage/cards/l/LeadTheStampede.java index 6511e7f85f..7b5a8bd81a 100644 --- a/Mage.Sets/src/mage/cards/l/LeadTheStampede.java +++ b/Mage.Sets/src/mage/cards/l/LeadTheStampede.java @@ -1,32 +1,37 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterCard; +import java.util.UUID; + /** - * * @author North */ public final class LeadTheStampede extends CardImpl { private static final FilterCard filter = new FilterCard("any number of creature cards"); + static { filter.add(CardType.CREATURE.getPredicate()); } public LeadTheStampede(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); // Look at the top five cards of your library. You may reveal any number of creature cards from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in any order. - this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect(5, 5, filter, true)); + this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( + 5, 5, filter, true + ).setText("Look at the top five cards of your library. " + + "You may reveal any number of creature cards from among them " + + "and put the revealed cards into your hand. " + + "Put the rest on the bottom of your library in any order.")); } - public LeadTheStampede(final LeadTheStampede card) { + private LeadTheStampede(final LeadTheStampede card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/l/LeadershipVacuum.java b/Mage.Sets/src/mage/cards/l/LeadershipVacuum.java index 49366c7f21..de1d303adf 100644 --- a/Mage.Sets/src/mage/cards/l/LeadershipVacuum.java +++ b/Mage.Sets/src/mage/cards/l/LeadershipVacuum.java @@ -52,7 +52,7 @@ class LeadershipVacuumEffect extends OneShotEffect { LeadershipVacuumEffect() { super(Outcome.Detriment); - staticText = "Target player returns each commander they control from the battlefield to the command zone."; + staticText = "Target player returns each commander they control from the battlefield to the command zone"; } private LeadershipVacuumEffect(final LeadershipVacuumEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LeafkinAvenger.java b/Mage.Sets/src/mage/cards/l/LeafkinAvenger.java new file mode 100644 index 0000000000..587e7d4249 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LeafkinAvenger.java @@ -0,0 +1,68 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.mana.DynamicManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.target.common.TargetPlayerOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LeafkinAvenger extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature you control with power 4 or greater"); + + static { + filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final DynamicValue xValue2 = new SourcePermanentPowerCount(false); + + public LeafkinAvenger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // {T}: Add {G} for each creature you control with power 4 or greater. + this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), xValue)); + + // {7}{R}: Leafkin Avenger deals damage equal to its power to target player or planeswalker. + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(xValue2) + .setText("{this} deals damage equal to its power to target player or planeswalker"), + new ManaCostsImpl("{7}{R}") + ); + ability.addTarget(new TargetPlayerOrPlaneswalker()); + this.addAbility(ability); + } + + private LeafkinAvenger(final LeafkinAvenger card) { + super(card); + } + + @Override + public LeafkinAvenger copy() { + return new LeafkinAvenger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/Leashling.java b/Mage.Sets/src/mage/cards/l/Leashling.java index 41f2099042..c4dfc5cd1e 100644 --- a/Mage.Sets/src/mage/cards/l/Leashling.java +++ b/Mage.Sets/src/mage/cards/l/Leashling.java @@ -28,7 +28,7 @@ public final class Leashling extends CardImpl { public Leashling(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{6}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/l/LeaveChance.java b/Mage.Sets/src/mage/cards/l/LeaveChance.java index 262a76dc98..00c6923f17 100644 --- a/Mage.Sets/src/mage/cards/l/LeaveChance.java +++ b/Mage.Sets/src/mage/cards/l/LeaveChance.java @@ -1,39 +1,41 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.AftermathAbility; -import mage.cards.Card; import mage.cards.CardSetInfo; -import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SpellAbilityType; import mage.constants.TargetController; -import mage.filter.FilterCard; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPermanent; -import mage.target.common.TargetCardInHand; +import mage.target.common.TargetDiscard; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class LeaveChance extends SplitCard { + private static final FilterPermanent filter = new FilterPermanent("permanents you own"); + + static { + filter.add(TargetController.YOU.getOwnerPredicate()); + } + public LeaveChance(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, new CardType[]{CardType.SORCERY}, "{1}{W}", "{3}{R}", SpellAbilityType.SPLIT_AFTERMATH); // Return any number of target permanents you own to your hand. - FilterPermanent filter = new FilterPermanent("permanents you own"); - filter.add(TargetController.YOU.getOwnerPredicate()); getLeftHalfCard().getSpellAbility().addEffect(new ReturnToHandTargetEffect()); getLeftHalfCard().getSpellAbility().addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter, false)); @@ -41,12 +43,12 @@ public final class LeaveChance extends SplitCard { // Sorcery // Aftermath getRightHalfCard().addAbility(new AftermathAbility().setRuleAtTheTop(true)); + // Discard any number of cards, then draw that many cards. getRightHalfCard().getSpellAbility().addEffect(new ChanceEffect()); - } - public LeaveChance(final LeaveChance card) { + private LeaveChance(final LeaveChance card) { super(card); } @@ -63,7 +65,7 @@ class ChanceEffect extends OneShotEffect { this.staticText = "Discard any number of cards, then draw that many cards"; } - ChanceEffect(final ChanceEffect effect) { + private ChanceEffect(final ChanceEffect effect) { super(effect); } @@ -75,22 +77,13 @@ class ChanceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Cards cardsInHand = controller.getHand().copy(); - TargetCard target = new TargetCardInHand(0, cardsInHand.size(), new FilterCard()); - controller.chooseTarget(outcome, cardsInHand, target, source, game); - if (!target.getTargets().isEmpty()) { - for (UUID cardId : target.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - controller.discard(card, source, game); - } - } - game.applyEffects(); - controller.drawCards(target.getTargets().size(), game); - } - return true; + if (controller == null) { + return false; } - return false; + TargetCard target = new TargetDiscard(0, controller.getHand().size(), StaticFilters.FILTER_CARD_CARDS, controller.getId()); + controller.chooseTarget(outcome, controller.getHand(), target, source, game); + int amount = controller.discard(new CardsImpl(target.getTargets()), source, game).size(); + controller.drawCards(amount, source.getSourceId(), game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/l/LedevChampion.java b/Mage.Sets/src/mage/cards/l/LedevChampion.java index 108ac4328d..10303e8e7d 100644 --- a/Mage.Sets/src/mage/cards/l/LedevChampion.java +++ b/Mage.Sets/src/mage/cards/l/LedevChampion.java @@ -1,6 +1,5 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -9,23 +8,20 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.game.permanent.token.SoldierLifelinkToken; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class LedevChampion extends CardImpl { @@ -86,9 +82,10 @@ class LedevChampionEffect extends OneShotEffect { TargetCreaturePermanent target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true); if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.Tap, source.getControllerId(), source.getSourceId(), game)) { - for (UUID creature : target.getTargets()) { + for (UUID creatureId : target.getTargets()) { + Permanent creature = game.getPermanent(creatureId); if (creature != null) { - game.getPermanent(creature).tap(game); + creature.tap(game); tappedAmount++; } } diff --git a/Mage.Sets/src/mage/cards/l/LeechBonder.java b/Mage.Sets/src/mage/cards/l/LeechBonder.java index fda61b46e5..7b7b48d3b7 100644 --- a/Mage.Sets/src/mage/cards/l/LeechBonder.java +++ b/Mage.Sets/src/mage/cards/l/LeechBonder.java @@ -1,9 +1,5 @@ - package mage.cards.l; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; @@ -17,26 +13,29 @@ import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.choices.ChoiceImpl; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.Counter; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author jeffwadsworth - * */ public final class LeechBonder extends CardImpl { public LeechBonder(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.MERFOLK); this.subtype.add(SubType.SOLDIER); @@ -49,8 +48,17 @@ public final class LeechBonder extends CardImpl { // {U}, {untap}: Move a counter from target creature onto another target creature. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LeechBonderEffect(), new ManaCostsImpl("{U}")); ability.addCost(new UntapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature to remove counter from"))); - ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature to put counter on"))); + // target 1 + TargetCreaturePermanent target1 = new TargetCreaturePermanent(new FilterCreaturePermanent("creature to remove counter from")); + target1.setTargetTag(1); + ability.addTarget(target1); + // target 2 + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature to put counter on"); + filter.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter); + target2.setTargetTag(2); + ability.addTarget(target2); + this.addAbility(ability); } @@ -91,11 +99,13 @@ class LeechBonderEffect extends OneShotEffect { || controller == null) { return false; } - Choice choice = new ChoiceImpl(); - Set possibleChoices = new HashSet<>(); - for (String counterName : fromPermanent.getCounters(game).keySet()) { - possibleChoices.add(counterName); + + Set possibleChoices = new HashSet<>(fromPermanent.getCounters(game).keySet()); + if (possibleChoices.size() == 0) { + return false; } + + Choice choice = new ChoiceImpl(); choice.setChoices(possibleChoices); if (controller.choose(outcome, choice, game)) { String chosen = choice.getChoice(); @@ -103,7 +113,7 @@ class LeechBonderEffect extends OneShotEffect { CounterType counterType = CounterType.findByName(chosen); if (counterType != null) { Counter counter = counterType.createInstance(); - fromPermanent.removeCounters(counter, game); + fromPermanent.removeCounters(counterType.getName(), 1, game); toPermanent.addCounters(counter, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/l/LeeryFogbeast.java b/Mage.Sets/src/mage/cards/l/LeeryFogbeast.java index 6e9ac41b3a..9cef5b0184 100644 --- a/Mage.Sets/src/mage/cards/l/LeeryFogbeast.java +++ b/Mage.Sets/src/mage/cards/l/LeeryFogbeast.java @@ -3,7 +3,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.PreventAllDamageByAllPermanentsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class LeeryFogbeast extends CardImpl { this.toughness = new MageInt(2); // Whenever Leery Fogbeast becomes blocked, prevent all combat damage that would be dealt this turn. - this.addAbility(new BecomesBlockedTriggeredAbility(new PreventAllDamageByAllPermanentsEffect(Duration.EndOfTurn, true), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new PreventAllDamageByAllPermanentsEffect(Duration.EndOfTurn, true), false)); } public LeeryFogbeast(final LeeryFogbeast card) { diff --git a/Mage.Sets/src/mage/cards/l/LeoninOfTheLostPride.java b/Mage.Sets/src/mage/cards/l/LeoninOfTheLostPride.java index 3d5719900f..1fb3efdf0e 100644 --- a/Mage.Sets/src/mage/cards/l/LeoninOfTheLostPride.java +++ b/Mage.Sets/src/mage/cards/l/LeoninOfTheLostPride.java @@ -1,7 +1,7 @@ package mage.cards.l; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -28,7 +28,7 @@ public final class LeoninOfTheLostPride extends CardImpl { this.toughness = new MageInt(1); // When Leonin of the Lost Pride dies, exile target card from an opponent’s graveyard. - DiesTriggeredAbility diesTriggeredAbility = new DiesTriggeredAbility(new ExileTargetEffect()); + DiesSourceTriggeredAbility diesTriggeredAbility = new DiesSourceTriggeredAbility(new ExileTargetEffect()); diesTriggeredAbility.addTarget(new TargetCardInOpponentsGraveyard(new FilterCard("card from an opponent's graveyard"))); this.addAbility(diesTriggeredAbility); } diff --git a/Mage.Sets/src/mage/cards/l/LiarsPendulum.java b/Mage.Sets/src/mage/cards/l/LiarsPendulum.java index bf86998ce4..e30258077d 100644 --- a/Mage.Sets/src/mage/cards/l/LiarsPendulum.java +++ b/Mage.Sets/src/mage/cards/l/LiarsPendulum.java @@ -93,7 +93,7 @@ class LiarsPendulumEffect extends OneShotEffect { rightGuess = opponentGuess; } } - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { rightGuess = opponentGuess; } } @@ -102,7 +102,7 @@ class LiarsPendulumEffect extends OneShotEffect { if (controller.chooseUse(outcome, "Reveal your hand?", source, game)) { controller.revealCards("hand of " + controller.getName(), controller.getHand(), game); if (!rightGuess) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/l/Liberate.java b/Mage.Sets/src/mage/cards/l/Liberate.java index e78e990603..bea0f07499 100644 --- a/Mage.Sets/src/mage/cards/l/Liberate.java +++ b/Mage.Sets/src/mage/cards/l/Liberate.java @@ -1,7 +1,5 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -17,10 +15,10 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LoneFox - * */ public final class Liberate extends CardImpl { @@ -59,7 +57,7 @@ class LiberateEffect extends OneShotEffect { MageObject sourceObject = game.getObject(source.getSourceId()); if (permanent != null && sourceObject != null) { if (permanent.moveToExile(source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game)) { - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/l/LibraryLarcenist.java b/Mage.Sets/src/mage/cards/l/LibraryLarcenist.java new file mode 100644 index 0000000000..ad1c72df67 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LibraryLarcenist.java @@ -0,0 +1,38 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LibraryLarcenist extends CardImpl { + + public LibraryLarcenist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.MERFOLK); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Whenever Library Larcenist attacks, draw a card. + this.addAbility(new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + } + + private LibraryLarcenist(final LibraryLarcenist card) { + super(card); + } + + @Override + public LibraryLarcenist copy() { + return new LibraryLarcenist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/Lich.java b/Mage.Sets/src/mage/cards/l/Lich.java index e77d1031f5..6eee63d51a 100644 --- a/Mage.Sets/src/mage/cards/l/Lich.java +++ b/Mage.Sets/src/mage/cards/l/Lich.java @@ -86,7 +86,7 @@ class LichLifeGainReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(event.getPlayerId()); if (controller != null) { - controller.drawCards(event.getAmount(), game); + controller.drawCards(event.getAmount(), source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/l/LichsMirror.java b/Mage.Sets/src/mage/cards/l/LichsMirror.java index 0ca9064c0f..d12065242e 100644 --- a/Mage.Sets/src/mage/cards/l/LichsMirror.java +++ b/Mage.Sets/src/mage/cards/l/LichsMirror.java @@ -8,7 +8,10 @@ import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.other.OwnerIdPredicate; import mage.game.Game; @@ -64,32 +67,20 @@ class LichsMirrorEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getPlayerId()); if (player != null) { + Cards toLib = new CardsImpl(); FilterControlledPermanent filter = new FilterControlledPermanent(); filter.add(new OwnerIdPredicate(player.getId())); - for (UUID uuid : player.getHand().copy()) { - Card card = game.getCard(uuid); - if (card != null) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - } - - for (UUID uuid : player.getGraveyard().copy()) { - Card card = game.getCard(uuid); - if (card != null) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - } - + toLib.addAll(player.getHand()); + toLib.addAll(player.getGraveyard()); for(Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)){ - permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - player.shuffleLibrary(source, game); - - player.drawCards(7, game); - - player.setLife(20, game, source); + toLib.add(permanent); + } + player.shuffleCardsToLibrary(toLib, game, source); + game.getState().processAction(game); + player.drawCards(7, source.getSourceId(), game); + player.setLife(20, game, source); } - return true; + return true; // replace the loses event } @Override @@ -99,10 +90,7 @@ class LichsMirrorEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getPlayerId().equals(source.getControllerId())) { - return true; - } - return false; + return event.getPlayerId().equals(source.getControllerId()); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java b/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java index aa1c372205..67e9c624b5 100644 --- a/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java +++ b/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java @@ -40,7 +40,7 @@ public final class LiciaSanguineTribune extends CardImpl { this.toughness = new MageInt(4); // Licia, Sanguine Tribune costs 1 less to cast for each 1 life you gained this turn. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new LiciaSanguineTribuneCostReductionEffect()), new PlayerGainedLifeWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new LiciaSanguineTribuneCostReductionEffect()), new PlayerGainedLifeWatcher()); // First strike this.addAbility(FirstStrikeAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java b/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java new file mode 100644 index 0000000000..dc3267a00d --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java @@ -0,0 +1,95 @@ +package mage.cards.l; + +import java.util.HashMap; +import java.util.Map; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.token.SquirrelToken; +import mage.players.Player; +import mage.util.ManaUtil; + +import java.util.UUID; + +/** + * @author arcox + */ +public final class LiegeOfTheHollows extends CardImpl { + + public LiegeOfTheHollows(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + this.subtype.add(SubType.SPIRIT); + + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When Liege of the Hollows dies, each player may pay any amount of mana. + // Then each player creates a number of 1/1 green Squirrel creature tokens + // equal to the amount of mana they paid this way. + this.addAbility(new DiesSourceTriggeredAbility(new LiegeOfTheHollowsEffect())); + } + + public LiegeOfTheHollows(final LiegeOfTheHollows card) { + super(card); + } + + @Override + public LiegeOfTheHollows copy() { + return new LiegeOfTheHollows(this); + } +} + +class LiegeOfTheHollowsEffect extends OneShotEffect { + + public LiegeOfTheHollowsEffect() { + super(Outcome.Detriment); + this.staticText = "each player may pay any amount of mana. Then each player creates a number " + + "of 1/1 green Squirrel creature tokens equal to the amount of mana they paid this way"; + } + + public LiegeOfTheHollowsEffect(final LiegeOfTheHollowsEffect effect) { + super(effect); + } + + @Override + public LiegeOfTheHollowsEffect copy() { + return new LiegeOfTheHollowsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Map paidMana = new HashMap<>(); + if (controller != null) { + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + paidMana.put(player.getId(), ManaUtil.playerPaysXGenericMana(false, + "Liege of the Hollows", player, source, game)); + } + } + // create tokens + SquirrelToken token = new SquirrelToken(); + for (Map.Entry entry + : paidMana.entrySet()) { + Player player = game.getPlayer(entry.getKey()); + if (player != null) { + token.putOntoBattlefield(entry.getValue(), game, source.getSourceId(), player.getId()); + } + } + game.getState().processAction(game); + + // prevent undo + controller.resetStoredBookmark(game); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java b/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java index b9c327a785..db5983ad7a 100644 --- a/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java +++ b/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java @@ -1,4 +1,3 @@ - package mage.cards.l; import java.util.UUID; @@ -11,8 +10,8 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; @@ -27,13 +26,15 @@ public final class LieutenantsOfTheGuard extends CardImpl { public LieutenantsOfTheGuard(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); - + this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); this.power = new MageInt(2); this.toughness = new MageInt(2); - // Council's dilemma — When Lieutenants of the Guard enters the battlefield, starting with you, each player votes for strength or numbers. Put a +1/+1 counter on Lieutenants of the Guard for each strength vote and put a 1/1 white Soldier creature token onto the battlefield for each numbers vote. + // Council's dilemma — When Lieutenants of the Guard enters the battlefield, starting with you, + // each player votes for strength or numbers. Put a +1/+1 counter on Lieutenants of the Guard for each + // strength vote and put a 1/1 white Soldier creature token onto the battlefield for each numbers vote. this.addAbility(new EntersBattlefieldTriggeredAbility(new LieutenantsOfTheGuardDilemmaEffect(), false, "Council's dilemma — ")); } @@ -48,6 +49,7 @@ public final class LieutenantsOfTheGuard extends CardImpl { } class LieutenantsOfTheGuardDilemmaEffect extends CouncilsDilemmaVoteEffect { + public LieutenantsOfTheGuardDilemmaEffect() { super(Outcome.Benefit); this.staticText = "starting with you, each player votes for strength or numbers. Put a +1/+1 counter on {this} for each strength vote and put a 1/1 white Soldier creature token onto the battlefield for each numbers vote."; @@ -62,7 +64,9 @@ class LieutenantsOfTheGuardDilemmaEffect extends CouncilsDilemmaVoteEffect { Player controller = game.getPlayer(source.getControllerId()); //If no controller, exit out here and do not vote. - if (controller == null) return false; + if (controller == null) { + return false; + } this.vote("strength", "numbers", controller, game, source); @@ -70,8 +74,9 @@ class LieutenantsOfTheGuardDilemmaEffect extends CouncilsDilemmaVoteEffect { //Strength Votes //If strength received zero votes or the permanent is no longer on the battlefield, do not attempt to put P1P1 counters on it. - if (voteOneCount > 0 && permanent != null) + if (voteOneCount > 0 && permanent != null) { permanent.addCounters(CounterType.P1P1.createInstance(voteOneCount), source, game); + } //Numbers Votes if (voteTwoCount > 0) { diff --git a/Mage.Sets/src/mage/cards/l/LifeMatrix.java b/Mage.Sets/src/mage/cards/l/LifeMatrix.java index 09bb4c904e..137c62abe9 100644 --- a/Mage.Sets/src/mage/cards/l/LifeMatrix.java +++ b/Mage.Sets/src/mage/cards/l/LifeMatrix.java @@ -1,4 +1,3 @@ - package mage.cards.l; import java.util.UUID; @@ -30,10 +29,19 @@ public final class LifeMatrix extends CardImpl { public LifeMatrix(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {4}, {T}: Put a matrix counter on target creature and that creature gains “Remove a matrix counter from this creature: Regenerate this creature.” Activate this ability only during your upkeep. - Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.MATRIX.createInstance()), new GenericManaCost(4), - new IsStepCondition(PhaseStep.UPKEEP), null); - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new RemoveCountersSourceCost(CounterType.MATRIX.createInstance())); + // {4}, {T}: Put a matrix counter on target creature and that creature gains + // “Remove a matrix counter from this creature: Regenerate this creature.” + // Activate this ability only during your upkeep. + Ability ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, + new AddCountersTargetEffect(CounterType.MATRIX.createInstance()), + new GenericManaCost(4), + new IsStepCondition(PhaseStep.UPKEEP), "Put a matrix counter on target creature and " + + "that creature gains “Remove a matrix counter from this creature: " + + "Regenerate this creature.” Activate this ability only during your upkeep."); + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new RegenerateSourceEffect(), + new RemoveCountersSourceCost(CounterType.MATRIX.createInstance())); ability.addEffect(new GainAbilityTargetEffect(ability2, Duration.Custom)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/l/LifebloodHydra.java b/Mage.Sets/src/mage/cards/l/LifebloodHydra.java index 57fb1d7a07..7a77212541 100644 --- a/Mage.Sets/src/mage/cards/l/LifebloodHydra.java +++ b/Mage.Sets/src/mage/cards/l/LifebloodHydra.java @@ -4,7 +4,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; @@ -39,7 +39,7 @@ public final class LifebloodHydra extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); // When Lifeblood Hydra dies, you gain life and draw cards equal to its power. - this.addAbility(new DiesTriggeredAbility(new LifebloodHydraEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new LifebloodHydraEffect(), false)); } public LifebloodHydra(final LifebloodHydra card) { @@ -76,7 +76,7 @@ class LifebloodHydraEffect extends OneShotEffect { Permanent diedPermanent = (Permanent) getValue("permanentLeftBattlefield"); if (diedPermanent != null) { controller.gainLife(diedPermanent.getPower().getValue(), game, source); - controller.drawCards(diedPermanent.getPower().getValue(), game); + controller.drawCards(diedPermanent.getPower().getValue(), source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/l/Lifeline.java b/Mage.Sets/src/mage/cards/l/Lifeline.java index e78a26f708..46d0d56e3c 100644 --- a/Mage.Sets/src/mage/cards/l/Lifeline.java +++ b/Mage.Sets/src/mage/cards/l/Lifeline.java @@ -1,7 +1,5 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -18,8 +16,9 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author HCrescent */ public final class Lifeline extends CardImpl { @@ -27,13 +26,13 @@ public final class Lifeline extends CardImpl { public Lifeline(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); - + // Whenever a creature dies, if another creature is on the battlefield, return the first card to the battlefield under its owner's control at the beginning of the next end step. Ability ability = new ConditionalInterveningIfTriggeredAbility( - new DiesCreatureTriggeredAbility( Zone.BATTLEFIELD, new LifelineEffect(), false, StaticFilters.FILTER_PERMANENT_CREATURE, true), - new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_CREATURE, ComparisonType.MORE_THAN, 0, false), - "Whenever a creature dies, if another creature is on the battlefield, return the first card to the battlefield under its owner's control at the beginning of the next end step."); + new DiesCreatureTriggeredAbility(Zone.BATTLEFIELD, new LifelineEffect(), false, StaticFilters.FILTER_PERMANENT_CREATURE, true), + new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_CREATURE, ComparisonType.MORE_THAN, 0, false), + "Whenever a creature dies, if another creature is on the battlefield, return the first card to the battlefield under its owner's control at the beginning of the next end step."); this.addAbility(ability); } @@ -46,28 +45,29 @@ public final class Lifeline extends CardImpl { return new Lifeline(this); } } + class LifelineEffect extends OneShotEffect { - + public LifelineEffect() { super(Outcome.PutCardInPlay); this.staticText = ""; } - + public LifelineEffect(final LifelineEffect effect) { super(effect); } - + @Override public LifelineEffect copy() { return new LifelineEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return that card to the battlefield under it's owner's control at the beginning of the next end step"); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.ANY), source); return true; diff --git a/Mage.Sets/src/mage/cards/l/LifesLegacy.java b/Mage.Sets/src/mage/cards/l/LifesLegacy.java index cf2f6faaa3..bb093b9b0e 100644 --- a/Mage.Sets/src/mage/cards/l/LifesLegacy.java +++ b/Mage.Sets/src/mage/cards/l/LifesLegacy.java @@ -66,7 +66,7 @@ class LifesLegacyEffect extends OneShotEffect { } } if (power > 0) { - controller.drawCards(power, game); + controller.drawCards(power, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/l/LightOfHope.java b/Mage.Sets/src/mage/cards/l/LightOfHope.java new file mode 100644 index 0000000000..704a6281bb --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightOfHope.java @@ -0,0 +1,47 @@ +package mage.cards.l; + +import mage.abilities.Mode; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetEnchantmentPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LightOfHope extends CardImpl { + + public LightOfHope(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); + + // Choose one — + // • You gain 4 life. + this.getSpellAbility().addEffect(new GainLifeEffect(4)); + + // • Destroy target enchantment. + Mode mode = new Mode(new DestroyTargetEffect()); + mode.addTarget(new TargetEnchantmentPermanent()); + this.getSpellAbility().addMode(mode); + + // • Put a +1/+1 counter on target creature. + mode = new Mode(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + mode.addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + } + + private LightOfHope(final LightOfHope card) { + super(card); + } + + @Override + public LightOfHope copy() { + return new LightOfHope(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LightOfPromise.java b/Mage.Sets/src/mage/cards/l/LightOfPromise.java new file mode 100644 index 0000000000..622f7ee7ea --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightOfPromise.java @@ -0,0 +1,84 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.common.GainLifeControllerTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LightOfPromise extends CardImpl { + + public LightOfPromise(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature." + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + new GainLifeControllerTriggeredAbility( + new LightOfPromiseEffect(), false, true + ), AttachmentType.AURA + ))); + } + + private LightOfPromise(final LightOfPromise card) { + super(card); + } + + @Override + public LightOfPromise copy() { + return new LightOfPromise(this); + } +} + +class LightOfPromiseEffect extends OneShotEffect { + + LightOfPromiseEffect() { + super(Outcome.Benefit); + staticText = "put that many +1/+1 counters on this creature"; + } + + private LightOfPromiseEffect(final LightOfPromiseEffect effect) { + super(effect); + } + + @Override + public LightOfPromiseEffect copy() { + return new LightOfPromiseEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return false; + } + int gainedLife = (int) this.getValue("gainedLife"); + return permanent.addCounters(CounterType.P1P1.createInstance(gainedLife), source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LightOfTheLegion.java b/Mage.Sets/src/mage/cards/l/LightOfTheLegion.java index c9581e3949..ebe0aac8bf 100644 --- a/Mage.Sets/src/mage/cards/l/LightOfTheLegion.java +++ b/Mage.Sets/src/mage/cards/l/LightOfTheLegion.java @@ -3,7 +3,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; @@ -42,7 +42,7 @@ public final class LightOfTheLegion extends CardImpl { this.addAbility(new MentorAbility()); // When Light of the Legion dies, put a +1/+1 counter on each white creature you control. - this.addAbility(new DiesTriggeredAbility(new AddCountersAllEffect( + this.addAbility(new DiesSourceTriggeredAbility(new AddCountersAllEffect( CounterType.P1P1.createInstance(), filter ))); } diff --git a/Mage.Sets/src/mage/cards/l/LightningBolt.java b/Mage.Sets/src/mage/cards/l/LightningBolt.java index 3fff879e9e..59235d5862 100644 --- a/Mage.Sets/src/mage/cards/l/LightningBolt.java +++ b/Mage.Sets/src/mage/cards/l/LightningBolt.java @@ -1,4 +1,3 @@ - package mage.cards.l; import java.util.UUID; @@ -17,6 +16,7 @@ public final class LightningBolt extends CardImpl { public LightningBolt(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); + // Lightning Bolt deals 3 damage to any target. this.getSpellAbility().addTarget(new TargetAnyTarget()); this.getSpellAbility().addEffect(new DamageTargetEffect(3)); } diff --git a/Mage.Sets/src/mage/cards/l/LightningCoils.java b/Mage.Sets/src/mage/cards/l/LightningCoils.java index b3b8a32f3a..fbc516c5b5 100644 --- a/Mage.Sets/src/mage/cards/l/LightningCoils.java +++ b/Mage.Sets/src/mage/cards/l/LightningCoils.java @@ -1,7 +1,5 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; @@ -19,16 +17,18 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.ElementalToken; +import mage.game.permanent.token.ElementalTokenWithHaste; import mage.players.Player; +import java.util.UUID; + /** - * * @author escplan9 - Derek Monturo */ public final class LightningCoils extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a nontoken creature you control"); + static { filter.add(TargetController.YOU.getControllerPredicate()); filter.add(Predicates.not(TokenPredicate.instance)); @@ -36,13 +36,13 @@ public final class LightningCoils extends CardImpl { public LightningCoils(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - + // Whenever a nontoken creature you control dies, put a charge counter on Lightning Coils. this.addAbility( new DiesCreatureTriggeredAbility( - new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true), + new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true), false, filter)); - + // At the beginning of your upkeep, if Lightning Coils has five or more charge counters on it, remove all of them from it // and put that many 3/1 red Elemental creature tokens with haste onto the battlefield. // Exile them at the beginning of the next end step. @@ -79,9 +79,9 @@ class LightningCoilsEffect extends OneShotEffect { if (counters >= 5) { // remove all the counters and create that many tokens p.removeCounters(CounterType.CHARGE.getName(), p.getCounters(game).getCount(CounterType.CHARGE), game); - CreateTokenEffect effect = new CreateTokenEffect(new ElementalToken("CON", 1, true), counters); + CreateTokenEffect effect = new CreateTokenEffect(new ElementalTokenWithHaste(), counters); effect.apply(game, source); - + // exile those tokens at next end step effect.exileTokensCreatedAtNextEndStep(game, source); return true; diff --git a/Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java b/Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java new file mode 100644 index 0000000000..b0ee776149 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java @@ -0,0 +1,48 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import java.util.UUID; +import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; +import mage.target.common.TargetAnyTarget; + +/** + * @author TheElk801 + */ +public final class LightningCoreExcavator extends CardImpl { + + public LightningCoreExcavator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}"); + + this.subtype.add(SubType.GOLEM); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // {5}, {T}, Sacrifice Lightning-Core Excavator: It deals 3 damage to any target. + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(3), new GenericManaCost(5) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetAnyTarget(new FilterCreaturePlayerOrPlaneswalker("any target"))); + this.addAbility(ability); + } + + private LightningCoreExcavator(final LightningCoreExcavator card) { + super(card); + } + + @Override + public LightningCoreExcavator copy() { + return new LightningCoreExcavator(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LightningHounds.java b/Mage.Sets/src/mage/cards/l/LightningHounds.java index 4aba0b130e..36ed3340f5 100644 --- a/Mage.Sets/src/mage/cards/l/LightningHounds.java +++ b/Mage.Sets/src/mage/cards/l/LightningHounds.java @@ -17,7 +17,7 @@ public final class LightningHounds extends CardImpl { public LightningHounds(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/l/LightningMare.java b/Mage.Sets/src/mage/cards/l/LightningMare.java index 557cd1daa8..7d33dde8d3 100644 --- a/Mage.Sets/src/mage/cards/l/LightningMare.java +++ b/Mage.Sets/src/mage/cards/l/LightningMare.java @@ -3,7 +3,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -39,7 +39,7 @@ public final class LightningMare extends CardImpl { this.toughness = new MageInt(1); // This spell can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Lightning Mare can't be blocked by blue creatures. this.addAbility(new SimpleStaticAbility( diff --git a/Mage.Sets/src/mage/cards/l/LightningPhoenix.java b/Mage.Sets/src/mage/cards/l/LightningPhoenix.java new file mode 100644 index 0000000000..82649cc8c4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightningPhoenix.java @@ -0,0 +1,106 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.CantBlockAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LightningPhoenix extends CardImpl { + + public LightningPhoenix(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.PHOENIX); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Lightning Phoenix can't block. + this.addAbility(new CantBlockAbility()); + + // At the beginning of your end step, if an opponent was dealt 3 or more damage this turn, you may pay {R}. If you do, return Lightning Phoenix from your graveyard to the battlefield. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + Zone.GRAVEYARD, + new DoIfCostPaid( + new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl<>("{R}") + ), TargetController.YOU, null, false + ), LightningPhoenixCondition.instance, "At the beginning of your end step, " + + "if an opponent was dealt 3 or more damage this turn, you may pay {R}. " + + "If you do, return {this} from your graveyard to the battlefield." + ), new LightningPhoenixWatcher()); + } + + private LightningPhoenix(final LightningPhoenix card) { + super(card); + } + + @Override + public LightningPhoenix copy() { + return new LightningPhoenix(this); + } +} + +enum LightningPhoenixCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + LightningPhoenixWatcher watcher = game.getState().getWatcher(LightningPhoenixWatcher.class); + return watcher != null && watcher.checkDamage(source.getControllerId()); + } +} + +class LightningPhoenixWatcher extends Watcher { + + private final Map damageMap = new HashMap<>(); + + LightningPhoenixWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER) { + return; + } + for (UUID playerId : game.getOpponents(event.getTargetId())) { + damageMap.compute(playerId, ((u, i) -> i == null ? event.getAmount() : Integer.sum(i, event.getAmount()))); + } + } + + @Override + public void reset() { + super.reset(); + damageMap.clear(); + } + + boolean checkDamage(UUID playerId) { + return damageMap.getOrDefault(playerId, 0) >= 3; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LightningVisionary.java b/Mage.Sets/src/mage/cards/l/LightningVisionary.java new file mode 100644 index 0000000000..282585748b --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightningVisionary.java @@ -0,0 +1,37 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LightningVisionary extends CardImpl { + + public LightningVisionary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.MINOTAUR); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Prowess + this.addAbility(new ProwessAbility()); + } + + private LightningVisionary(final LightningVisionary card) { + super(card); + } + + @Override + public LightningVisionary copy() { + return new LightningVisionary(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java new file mode 100644 index 0000000000..0e1d8341ce --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java @@ -0,0 +1,121 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.LoseLifeTargetControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class LilianaDeathMage extends CardImpl { + + public LilianaDeathMage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.LILIANA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // +1: Return up to one target creature card from your graveyard to your hand. + Ability plusAbility = new LoyaltyAbility(new LilianaDeathMagePlusEffect(), 1); + plusAbility.addTarget(new TargetCardInYourGraveyard(0, 1, StaticFilters.FILTER_CARD_CREATURE)); + this.addAbility(plusAbility); + + // −3: Destroy target creature. Its controller loses 2 life. + Ability minusAbility = new LoyaltyAbility(new DestroyTargetEffect(), -3); + minusAbility.addTarget(new TargetCreaturePermanent()); + minusAbility.addEffect(new LoseLifeTargetControllerEffect(2)); + this.addAbility(minusAbility); + + // −7: Target opponent loses 2 life for each creature card in their graveyard. + Ability ultimateAbility = new LoyaltyAbility(new LilianaDeathMageUltimateEffect(), -7); + ultimateAbility.addTarget(new TargetOpponent()); + this.addAbility(ultimateAbility); + } + + private LilianaDeathMage(final LilianaDeathMage card) { + super(card); + } + + @Override + public LilianaDeathMage copy() { + return new LilianaDeathMage(this); + } +} + +class LilianaDeathMagePlusEffect extends OneShotEffect { + + LilianaDeathMagePlusEffect() { + super(Outcome.Benefit); + staticText = "Return up to one target creature card from your graveyard to your hand"; + } + + private LilianaDeathMagePlusEffect(LilianaDeathMagePlusEffect effect) { + super(effect); + } + + @Override + public LilianaDeathMagePlusEffect copy() { + return new LilianaDeathMagePlusEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = game.getCard(source.getTargets().get(0).getFirstTarget()); + if (card == null) { + return false; + } + return player.moveCards(card, Zone.HAND, source, game); + } +} + +class LilianaDeathMageUltimateEffect extends OneShotEffect { + + LilianaDeathMageUltimateEffect() { + super(Outcome.Damage); + staticText = "Target opponent loses 2 life for each creature card in their graveyard"; + } + + private LilianaDeathMageUltimateEffect(LilianaDeathMageUltimateEffect effect) { + super(effect); + } + + @Override + public LilianaDeathMageUltimateEffect copy() { + return new LilianaDeathMageUltimateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player opponent = game.getPlayer(source.getFirstTarget()); + if (opponent != null) { + int amount = opponent.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game); + opponent.loseLife(amount * 2, game, false); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java b/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java index a538d5ada6..5b77db31f2 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java +++ b/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java @@ -31,7 +31,7 @@ import java.util.UUID; */ public final class LilianaDreadhordeGeneral extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("creatures"); + private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent(" creatures"); public LilianaDreadhordeGeneral(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{B}{B}"); diff --git a/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java b/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java index f131e898ac..dde813dd58 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java +++ b/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java @@ -1,7 +1,5 @@ package mage.cards.l; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; @@ -12,22 +10,17 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.Card; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class LilianaUntouchedByDeath extends CardImpl { @@ -73,8 +66,7 @@ class LilianaUntouchedByDeathEffect extends OneShotEffect { public LilianaUntouchedByDeathEffect() { super(Outcome.Benefit); - this.staticText = "put the top three cards of your library into your graveyard. " - + "If at least one of them is a Zombie card, each opponent loses 2 life and you gain 2 life"; + this.staticText = "mill three cards. If at least one of them is a Zombie card, each opponent loses 2 life and you gain 2 life"; } public LilianaUntouchedByDeathEffect(final LilianaUntouchedByDeathEffect effect) { @@ -92,15 +84,11 @@ class LilianaUntouchedByDeathEffect extends OneShotEffect { if (player == null) { return false; } - boolean doEffect = false; - Set top3 = player.moveCardsToGraveyardWithInfo(player.getLibrary().getTopCards(game, 3), source, game, Zone.LIBRARY); - for (Card card : top3) { - if (card != null && card.hasSubtype(SubType.ZOMBIE, game)) { - doEffect = true; - break; - } - } - if (doEffect) { + if (player + .millCards(3, source, game) + .getCards(game) + .stream() + .anyMatch(card -> card.hasSubtype(SubType.ZOMBIE, game))) { new LoseLifeOpponentsEffect(2).apply(game, source); player.gainLife(2, game, source); } diff --git a/Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java b/Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java new file mode 100644 index 0000000000..56a8048702 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java @@ -0,0 +1,119 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.command.emblems.LilianaWakerOfTheDeadEmblem; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetDiscard; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class LilianaWakerOfTheDead extends CardImpl { + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD, -1); + private static final DynamicValue xValue_hint = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD); + + public LilianaWakerOfTheDead(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.LILIANA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // +1: Each player discards a card. Each opponent who can't loses 3 life. + this.addAbility(new LoyaltyAbility(new LilianaWakerOfTheDeadDiscardEffect(), 1)); + + // −3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard. + Ability ability = new LoyaltyAbility(new BoostTargetEffect( + xValue, xValue, Duration.EndOfTurn, true + ).setText("target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard"), -3) + .addHint(new ValueHint("Cards in your graveyard", xValue_hint)); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // −7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste." + this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new LilianaWakerOfTheDeadEmblem()), -7)); + } + + private LilianaWakerOfTheDead(final LilianaWakerOfTheDead card) { + super(card); + } + + @Override + public LilianaWakerOfTheDead copy() { + return new LilianaWakerOfTheDead(this); + } +} + +class LilianaWakerOfTheDeadDiscardEffect extends OneShotEffect { + + LilianaWakerOfTheDeadDiscardEffect() { + super(Outcome.Discard); + staticText = "Each player discards a card. Each opponent who can't loses 3 life"; + } + + private LilianaWakerOfTheDeadDiscardEffect(LilianaWakerOfTheDeadDiscardEffect effect) { + super(effect); + } + + @Override + public LilianaWakerOfTheDeadDiscardEffect copy() { + return new LilianaWakerOfTheDeadDiscardEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Map cardsToDiscard = new HashMap<>(); + if (controller == null) { + return true; + } + // choose cards to discard + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + int numberOfCardsToDiscard = Math.min(1, player.getHand().size()); + Cards cards = new CardsImpl(); + Target target = new TargetDiscard(numberOfCardsToDiscard, numberOfCardsToDiscard, StaticFilters.FILTER_CARD, playerId); + player.chooseTarget(outcome, target, source, game); + cards.addAll(target.getTargets()); + cardsToDiscard.put(playerId, cards); + } + // discard all choosen cards + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + int amountDiscarded = player.discard(cardsToDiscard.get(playerId), source, game).size(); + if (controller.hasOpponent(playerId, game) && amountDiscarded == 0) { + player.loseLife(3, game, false); + } + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LilianasDefeat.java b/Mage.Sets/src/mage/cards/l/LilianasDefeat.java index c39d3aaf27..02bedc3b11 100644 --- a/Mage.Sets/src/mage/cards/l/LilianasDefeat.java +++ b/Mage.Sets/src/mage/cards/l/LilianasDefeat.java @@ -67,7 +67,7 @@ class LilianasDefeatEffect extends OneShotEffect { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (player != null && permanent != null) { permanent.destroy(source.getSourceId(), game, true); - game.applyEffects(); + game.getState().processAction(game); if (permanent.isPlaneswalker() && permanent.hasSubtype(SubType.LILIANA, game)) { Player permanentController = game.getPlayer(permanent.getControllerId()); if (permanentController != null) { diff --git a/Mage.Sets/src/mage/cards/l/LilianasDevotee.java b/Mage.Sets/src/mage/cards/l/LilianasDevotee.java new file mode 100644 index 0000000000..32a0236aeb --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LilianasDevotee.java @@ -0,0 +1,62 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MorbidCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.token.ZombieToken; +import mage.watchers.common.MorbidWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LilianasDevotee extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.ZOMBIE, "Zombies"); + + public LilianasDevotee(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Zombies you control get +1/+0. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 0, Duration.WhileOnBattlefield, filter + ))); + + // At the beginning of your end step, if a creature died this turn, you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility(new DoIfCostPaid( + new CreateTokenEffect(new ZombieToken()), new ManaCostsImpl("{1}{B}") + ), TargetController.YOU, false), MorbidCondition.instance, + "At the beginning of your end step, if a creature died this turn, " + + "you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token." + ), new MorbidWatcher()); + } + + private LilianasDevotee(final LilianasDevotee card) { + super(card); + } + + @Override + public LilianasDevotee copy() { + return new LilianasDevotee(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LilianasIndignation.java b/Mage.Sets/src/mage/cards/l/LilianasIndignation.java index 67d0d59c1c..3456d56f29 100644 --- a/Mage.Sets/src/mage/cards/l/LilianasIndignation.java +++ b/Mage.Sets/src/mage/cards/l/LilianasIndignation.java @@ -1,24 +1,21 @@ package mage.cards.l; -import java.util.Set; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.Objects; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class LilianasIndignation extends CardImpl { @@ -45,7 +42,7 @@ class LilianasIndignationEffect extends OneShotEffect { public LilianasIndignationEffect() { super(Outcome.LoseLife); - this.staticText = "Put the top X cards of your library into your graveyard. Target player loses 2 life for each creature card put into your graveyard this way"; + this.staticText = "Mill X cards. Target player loses 2 life for each creature card put into your graveyard this way"; } public LilianasIndignationEffect(final LilianasIndignationEffect effect) { @@ -60,27 +57,28 @@ class LilianasIndignationEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int x = source.getManaCostsToPay().getX(); - if (x > 0) { - Cards cardsToGraveyard = new CardsImpl(); - cardsToGraveyard.addAll(controller.getLibrary().getTopCards(game, x)); - if (!cardsToGraveyard.isEmpty()) { - Set movedCards = controller.moveCardsToGraveyardWithInfo(cardsToGraveyard.getCards(game), source, game, Zone.LIBRARY); - Cards cardsMoved = new CardsImpl(); - cardsMoved.addAll(movedCards); - int creatures = cardsMoved.count(StaticFilters.FILTER_CARD_CREATURE, game); - if (creatures > 0) { - Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (targetPlayer != null) { - targetPlayer.loseLife(creatures * 2, game, false); - } - - } - } - } + if (controller == null) { + return false; + } + int xValue = source.getManaCostsToPay().getX(); + if (xValue < 1) { return true; } - return false; + int creatures = controller + .millCards(xValue, source, game) + .getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> game.getState().getZone(card.getId()) == Zone.GRAVEYARD) + .filter(MageObject::isCreature) + .mapToInt(x -> 2) + .sum(); + if (creatures > 0) { + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (targetPlayer != null) { + targetPlayer.loseLife(creatures, game, false); + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/l/LilianasInfluence.java b/Mage.Sets/src/mage/cards/l/LilianasInfluence.java index 74aa74a26d..5eb3cb48ec 100644 --- a/Mage.Sets/src/mage/cards/l/LilianasInfluence.java +++ b/Mage.Sets/src/mage/cards/l/LilianasInfluence.java @@ -1,30 +1,26 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.FilterCard; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.NamePredicate; +import java.util.UUID; + /** - * * @author fireshoes */ public final class LilianasInfluence extends CardImpl { private static final FilterCard filter = new FilterCard("Liliana, Death Wielder"); - private static final FilterCreaturePermanent filterCreatures = new FilterCreaturePermanent("creature you don't control"); static { filter.add(new NamePredicate("Liliana, Death Wielder")); - filterCreatures.add(TargetController.NOT_YOU.getControllerPredicate()); } public LilianasInfluence(UUID ownerId, CardSetInfo setInfo) { @@ -32,11 +28,11 @@ public final class LilianasInfluence extends CardImpl { // Put a -1/-1 counter on each creature you don't control. You may search your library and/or graveyard for a card named Liliana, Death Wielder, // reveal it, and put it into your hand. If you search your library this way, shuffle it. - getSpellAbility().addEffect(new AddCountersAllEffect(CounterType.M1M1.createInstance(1), filterCreatures)); + getSpellAbility().addEffect(new AddCountersAllEffect(CounterType.M1M1.createInstance(1), StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter)); } - public LilianasInfluence(final LilianasInfluence card) { + private LilianasInfluence(final LilianasInfluence card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/l/LilianasScorn.java b/Mage.Sets/src/mage/cards/l/LilianasScorn.java new file mode 100644 index 0000000000..0b577a559c --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LilianasScorn.java @@ -0,0 +1,42 @@ +package mage.cards.l; + +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LilianasScorn extends CardImpl { + + private static final FilterCard filter = new FilterCard("Liliana, Death Mage"); + + static { + filter.add(new NamePredicate("Liliana, Death Mage")); + } + + public LilianasScorn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}"); + + // Destroy target creature. You may search your library and/or graveyard for a card named Liliana, Death Mage, reveal it, and put it into your hand. If you search your library this way, shuffle it. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter, false, true)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private LilianasScorn(final LilianasScorn card) { + super(card); + } + + @Override + public LilianasScorn copy() { + return new LilianasScorn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LilianasScrounger.java b/Mage.Sets/src/mage/cards/l/LilianasScrounger.java new file mode 100644 index 0000000000..0553782b08 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LilianasScrounger.java @@ -0,0 +1,90 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.common.MorbidCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPlaneswalkerPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.watchers.common.MorbidWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LilianasScrounger extends CardImpl { + + public LilianasScrounger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // At the beginning of each end step, if a creature died this turn, you may put a loyalty counter on a Liliana planeswalker you control. + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new BeginningOfEndStepTriggeredAbility( + new LilianasScroungerEffect(), TargetController.ANY, false + ), MorbidCondition.instance, "At the beginning of each end step, " + + "if a creature died this turn, you may put a loyalty counter on a Liliana planeswalker you control." + ), new MorbidWatcher()); + } + + private LilianasScrounger(final LilianasScrounger card) { + super(card); + } + + @Override + public LilianasScrounger copy() { + return new LilianasScrounger(this); + } +} + +class LilianasScroungerEffect extends OneShotEffect { + + private static final FilterPermanent filter + = new FilterControlledPlaneswalkerPermanent(SubType.LILIANA, "Liliana planeswalker you control"); + + LilianasScroungerEffect() { + super(Outcome.Benefit); + } + + private LilianasScroungerEffect(final LilianasScroungerEffect effect) { + super(effect); + } + + @Override + public LilianasScroungerEffect copy() { + return new LilianasScroungerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || game.getBattlefield().count( + filter, source.getSourceId(), source.getControllerId(), game + ) < 1 || !player.chooseUse( + outcome, "Put a loyalty counter on a Liliana planeswalker you control?", source, game + )) { + return false; + } + TargetPermanent target = new TargetPermanent(0, 1, filter, true); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + return permanent != null && permanent.addCounters(CounterType.LOYALTY.createInstance(), source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java b/Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java new file mode 100644 index 0000000000..791b089bba --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java @@ -0,0 +1,105 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LilianasStandardBearer extends CardImpl { + + public LilianasStandardBearer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Liliana's Standard Bearer enters the battlefield, draw X cards, where X is the number of creatures that died under your control this turn. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new DrawCardSourceControllerEffect(LilianasStandardBearerCount.instance) + .setText("draw X cards, where X is the number of creatures that died under your control this turn") + ), new LilianasStandardBearerWatcher()); + } + + private LilianasStandardBearer(final LilianasStandardBearer card) { + super(card); + } + + @Override + public LilianasStandardBearer copy() { + return new LilianasStandardBearer(this); + } +} + +enum LilianasStandardBearerCount implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + LilianasStandardBearerWatcher watcher = game.getState().getWatcher(LilianasStandardBearerWatcher.class); + return watcher == null ? 0 : watcher.getCount(sourceAbility.getControllerId()); + } + + @Override + public LilianasStandardBearerCount copy() { + return instance; + } + + @Override + public String getMessage() { + return ""; + } +} + +class LilianasStandardBearerWatcher extends Watcher { + + private final Map playerMap = new HashMap<>(); + + LilianasStandardBearerWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ZONE_CHANGE) { + return; + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.isDiesEvent() && zEvent.getTarget().isCreature()) { + playerMap.compute(zEvent.getTarget().getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + } + } + + @Override + public void reset() { + playerMap.clear(); + super.reset(); + } + + int getCount(UUID playerId) { + return playerMap.getOrDefault(playerId, 0); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LilianasSteward.java b/Mage.Sets/src/mage/cards/l/LilianasSteward.java new file mode 100644 index 0000000000..9e7c7a5e4d --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LilianasSteward.java @@ -0,0 +1,47 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LilianasSteward extends CardImpl { + + public LilianasSteward(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {T}, Sacrifice Liliana's Steward: Target opponent discards a card. Activate this ability only any time you could cast a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + Zone.BATTLEFIELD, new DiscardTargetEffect(1), new TapSourceCost() + ); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private LilianasSteward(final LilianasSteward card) { + super(card); + } + + @Override + public LilianasSteward copy() { + return new LilianasSteward(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LimDulsCohort.java b/Mage.Sets/src/mage/cards/l/LimDulsCohort.java index fee4085bbd..0472534abd 100644 --- a/Mage.Sets/src/mage/cards/l/LimDulsCohort.java +++ b/Mage.Sets/src/mage/cards/l/LimDulsCohort.java @@ -1,45 +1,45 @@ -package mage.cards.l; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; -import mage.abilities.effects.common.CantBeRegeneratedTargetEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.filter.common.FilterCreaturePermanent; - -/** - * - * @author jeffwadsworth - */ -public final class LimDulsCohort extends CardImpl { - - public LimDulsCohort(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); - - this.subtype.add(SubType.ZOMBIE); - this.power = new MageInt(2); - this.toughness = new MageInt(3); - - // Whenever Lim-Dûl's Cohort blocks or becomes blocked by a creature, that creature can't be regenerated this turn. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility( - new CantBeRegeneratedTargetEffect(Duration.EndOfTurn), - new FilterCreaturePermanent(), - false, - "Whenever {this} blocks or becomes blocked by a creature, that creature can't be regenerated this turn.", - true)); - - } - - private LimDulsCohort(final LimDulsCohort card) { - super(card); - } - - @Override - public LimDulsCohort copy() { - return new LimDulsCohort(this); - } -} +package mage.cards.l; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; +import mage.abilities.effects.common.CantBeRegeneratedTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.common.FilterCreaturePermanent; + +/** + * + * @author jeffwadsworth + */ +public final class LimDulsCohort extends CardImpl { + + public LimDulsCohort(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever Lim-Dûl's Cohort blocks or becomes blocked by a creature, that creature can't be regenerated this turn. + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility( + new CantBeRegeneratedTargetEffect(Duration.EndOfTurn), + new FilterCreaturePermanent(), + false, + "Whenever {this} blocks or becomes blocked by a creature, that creature can't be regenerated this turn.", + true)); + + } + + private LimDulsCohort(final LimDulsCohort card) { + super(card); + } + + @Override + public LimDulsCohort copy() { + return new LimDulsCohort(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java b/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java index 27a88b67f1..d2187c28db 100644 --- a/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java +++ b/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.effects.Effect; @@ -44,7 +44,7 @@ public final class LimDulsPaladin extends CardImpl { // At the beginning of your upkeep, you may discard a card. If you don't, sacrifice Lim-Dul's Paladin and draw a card. this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new LimDulsPaladinEffect(), TargetController.YOU, false)); // Whenever Lim-Dul's Paladin becomes blocked, it gets +6/+3 until end of turn. - this.addAbility(new BecomesBlockedTriggeredAbility(new BoostSourceEffect(6, 3, Duration.EndOfTurn), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(6, 3, Duration.EndOfTurn), false)); // Whenever Lim-Dul's Paladin attacks and isn't blocked, it assigns no combat damage to defending player this turn and that player loses 4 life. Effect effect = new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn); effect.setText("it assigns no combat damage this turn"); diff --git a/Mage.Sets/src/mage/cards/l/LimitedResources.java b/Mage.Sets/src/mage/cards/l/LimitedResources.java index 71ba1053d7..5d27acdf1c 100644 --- a/Mage.Sets/src/mage/cards/l/LimitedResources.java +++ b/Mage.Sets/src/mage/cards/l/LimitedResources.java @@ -1,124 +1,124 @@ -package mage.cards.l; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.common.FilterLandPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; -import mage.target.common.TargetLandPermanent; - -/** - * - * @author jeffwadsworth - */ -public final class LimitedResources extends CardImpl { - - public LimitedResources(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); - - // When Limited Resources enters the battlefield, each player chooses five lands they control and sacrifices the rest. - this.addAbility(new EntersBattlefieldTriggeredAbility(new LimitedResourcesEffect(), false)); - - // Players can't play lands as long as ten or more lands are on the battlefield. - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - new ConditionalContinuousRuleModifyingEffect( - new CantPlayLandEffect(), - new PermanentsOnTheBattlefieldCondition( - new FilterLandPermanent(), - ComparisonType.MORE_THAN, 9)))); - - } - - public LimitedResources(final LimitedResources card) { - super(card); - } - - @Override - public LimitedResources copy() { - return new LimitedResources(this); - } -} - -class LimitedResourcesEffect extends OneShotEffect { - - public LimitedResourcesEffect() { - super(Outcome.Benefit); - this.staticText = "each player chooses five lands they control and sacrifices the rest"; - } - - public LimitedResourcesEffect(final LimitedResourcesEffect effect) { - super(effect); - } - - @Override - public LimitedResourcesEffect copy() { - return new LimitedResourcesEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.getState().getPlayersInRange(source.getControllerId(), game).forEach((playerId) -> { - Player player = game.getPlayer(playerId); - if (player != null) { - int lands = game.getBattlefield().countAll(new FilterControlledLandPermanent(), playerId, game); - TargetLandPermanent target = new TargetLandPermanent(Integer.min(5, lands)); - target.setNotTarget(true); - target.setRequired(true); - player.chooseTarget(outcome.Benefit, target, source, game); - game.getBattlefield().getAllActivePermanents(new FilterControlledLandPermanent(), playerId, game).stream().filter((land) -> (!target.getTargets().contains(land.getId()))).forEachOrdered((land) -> { - land.sacrifice(source.getSourceId(), game); - }); - } - }); - return true; - } -} - -class CantPlayLandEffect extends ContinuousRuleModifyingEffectImpl { - - public CantPlayLandEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); - this.staticText = "Players can't play lands as long as ten or more lands are on the battlefield"; - } - - public CantPlayLandEffect(final CantPlayLandEffect effect) { - super(effect); - } - - @Override - public CantPlayLandEffect copy() { - return new CantPlayLandEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.PLAY_LAND; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return true; - } - -} +package mage.cards.l; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterLandPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.common.TargetLandPermanent; + +/** + * + * @author jeffwadsworth + */ +public final class LimitedResources extends CardImpl { + + public LimitedResources(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + // When Limited Resources enters the battlefield, each player chooses five lands they control and sacrifices the rest. + this.addAbility(new EntersBattlefieldTriggeredAbility(new LimitedResourcesEffect(), false)); + + // Players can't play lands as long as ten or more lands are on the battlefield. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new ConditionalContinuousRuleModifyingEffect( + new CantPlayLandEffect(), + new PermanentsOnTheBattlefieldCondition( + new FilterLandPermanent(), + ComparisonType.MORE_THAN, 9, false)))); + + } + + public LimitedResources(final LimitedResources card) { + super(card); + } + + @Override + public LimitedResources copy() { + return new LimitedResources(this); + } +} + +class LimitedResourcesEffect extends OneShotEffect { + + public LimitedResourcesEffect() { + super(Outcome.Benefit); + this.staticText = "each player chooses five lands they control and sacrifices the rest"; + } + + public LimitedResourcesEffect(final LimitedResourcesEffect effect) { + super(effect); + } + + @Override + public LimitedResourcesEffect copy() { + return new LimitedResourcesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.getState().getPlayersInRange(source.getControllerId(), game).forEach((playerId) -> { + Player player = game.getPlayer(playerId); + if (player != null) { + int lands = game.getBattlefield().countAll(new FilterControlledLandPermanent(), playerId, game); + TargetLandPermanent target = new TargetLandPermanent(Integer.min(5, lands)); + target.setNotTarget(true); + target.setRequired(true); + player.chooseTarget(outcome.Benefit, target, source, game); + game.getBattlefield().getAllActivePermanents(new FilterControlledLandPermanent(), playerId, game).stream().filter((land) -> (!target.getTargets().contains(land.getId()))).forEachOrdered((land) -> { + land.sacrifice(source.getSourceId(), game); + }); + } + }); + return true; + } +} + +class CantPlayLandEffect extends ContinuousRuleModifyingEffectImpl { + + public CantPlayLandEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); + this.staticText = "Players can't play lands as long as ten or more lands are on the battlefield"; + } + + public CantPlayLandEffect(final CantPlayLandEffect effect) { + super(effect); + } + + @Override + public CantPlayLandEffect copy() { + return new CantPlayLandEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PLAY_LAND; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return true; + } + +} diff --git a/Mage.Sets/src/mage/cards/l/LingeringSouls.java b/Mage.Sets/src/mage/cards/l/LingeringSouls.java index e6d972cb4e..0ebfd5ca77 100644 --- a/Mage.Sets/src/mage/cards/l/LingeringSouls.java +++ b/Mage.Sets/src/mage/cards/l/LingeringSouls.java @@ -1,7 +1,5 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.FlashbackAbility; @@ -11,18 +9,19 @@ import mage.constants.CardType; import mage.constants.TimingRule; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** - * * @author Loki */ public final class LingeringSouls extends CardImpl { public LingeringSouls(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}"); // Create two 1/1 white Spirit creature tokens with flying. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken("ISD"), 2)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken(), 2)); // Flashback {1}{B} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{1}{B}"), TimingRule.SORCERY)); } diff --git a/Mage.Sets/src/mage/cards/l/LivingDeath.java b/Mage.Sets/src/mage/cards/l/LivingDeath.java index 904c560c38..37485fcd50 100644 --- a/Mage.Sets/src/mage/cards/l/LivingDeath.java +++ b/Mage.Sets/src/mage/cards/l/LivingDeath.java @@ -1,23 +1,11 @@ package mage.cards.l; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; + +import mage.abilities.effects.common.LivingDeathEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -41,64 +29,3 @@ public final class LivingDeath extends CardImpl { return new LivingDeath(this); } } - -class LivingDeathEffect extends OneShotEffect { - - public LivingDeathEffect() { - super(Outcome.Benefit); - this.staticText = "Each player exiles all creature cards from their graveyard, then sacrifices all creatures they control, then puts all cards they exiled this way onto the battlefield"; - } - - public LivingDeathEffect(final LivingDeathEffect effect) { - super(effect); - } - - @Override - public LivingDeathEffect copy() { - return new LivingDeathEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && sourceObject != null) { - Map> exiledCards = new HashMap<>(); - - // Move creature cards from graveyard to exile - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Set cardsPlayer = player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game); - if (!cardsPlayer.isEmpty()) { - exiledCards.put(player.getId(), cardsPlayer); - player.moveCards(cardsPlayer, Zone.EXILED, source, game); - } - } - } - game.applyEffects(); - - // Sacrifice all creatures - for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { - permanent.sacrifice(source.getSourceId(), game); - } - game.applyEffects(); - - // Exiled cards are put onto the battlefield at the same time under their owner's control - Set cardsToReturnFromExile = new HashSet<>(); - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Set cardsPlayer = exiledCards.get(playerId); - if (cardsPlayer != null - && !cardsPlayer.isEmpty()) { - cardsToReturnFromExile.addAll(cardsPlayer); - } - } - } - controller.moveCards(cardsToReturnFromExile, Zone.BATTLEFIELD, source, game, false, false, true, null); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/l/LivingEnd.java b/Mage.Sets/src/mage/cards/l/LivingEnd.java index 8868bb5357..9ee799e48e 100644 --- a/Mage.Sets/src/mage/cards/l/LivingEnd.java +++ b/Mage.Sets/src/mage/cards/l/LivingEnd.java @@ -1,24 +1,12 @@ package mage.cards.l; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.LivingDeathEffect; import mage.abilities.keyword.SuspendAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -35,8 +23,7 @@ public final class LivingEnd extends CardImpl { this.addAbility(new SuspendAbility(3, new ManaCostsImpl("{2}{B}{B}"), this)); // Each player exiles all creature cards from their graveyard, then sacrifices all creatures // they control, then puts all cards they exiled this way onto the battlefield. - this.getSpellAbility().addEffect(new LivingEndEffect()); - + this.getSpellAbility().addEffect(new LivingDeathEffect()); } public LivingEnd(final LivingEnd card) { @@ -48,56 +35,3 @@ public final class LivingEnd extends CardImpl { return new LivingEnd(this); } } - -class LivingEndEffect extends OneShotEffect { - - public LivingEndEffect() { - super(Outcome.Benefit); - this.staticText = "Each player exiles all creature cards from their graveyard, then sacrifices all creatures they control, then puts all cards they exiled this way onto the battlefield"; - } - - public LivingEndEffect(final LivingEndEffect effect) { - super(effect); - } - - @Override - public LivingEndEffect copy() { - return new LivingEndEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && sourceObject != null) { - Map> exiledCards = new HashMap<>(); - // move creature cards from graveyard to exile - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Set cardsPlayer = player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game); - if (!cardsPlayer.isEmpty()) { - exiledCards.put(player.getId(), cardsPlayer); - player.moveCards(cardsPlayer, Zone.EXILED, source, game); - } - } - } - // sacrifice all creatures - for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { - permanent.sacrifice(source.getSourceId(), game); - } - // put exiled cards to battlefield - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Set cardsPlayer = exiledCards.get(playerId); - if (cardsPlayer != null && !cardsPlayer.isEmpty()) { - player.moveCards(cardsPlayer, Zone.BATTLEFIELD, source, game, false, false, false, null); - } - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/l/LivingLightning.java b/Mage.Sets/src/mage/cards/l/LivingLightning.java new file mode 100644 index 0000000000..d024eb6c2a --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LivingLightning.java @@ -0,0 +1,47 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LivingLightning extends CardImpl { + + private static final FilterCard filter + = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"); + + public LivingLightning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Living Lightning dies, return target instant or sorcery card from your graveyard to your hand. + Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private LivingLightning(final LivingLightning card) { + super(card); + } + + @Override + public LivingLightning copy() { + return new LivingLightning(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LlanowarVisionary.java b/Mage.Sets/src/mage/cards/l/LlanowarVisionary.java new file mode 100644 index 0000000000..d8bb237913 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LlanowarVisionary.java @@ -0,0 +1,42 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LlanowarVisionary extends CardImpl { + + public LlanowarVisionary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Llanowar Visionary enters the battlefield, draw a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); + + // {T}: Add {G}. + this.addAbility(new GreenManaAbility()); + } + + private LlanowarVisionary(final LlanowarVisionary card) { + super(card); + } + + @Override + public LlanowarVisionary copy() { + return new LlanowarVisionary(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoafingGiant.java b/Mage.Sets/src/mage/cards/l/LoafingGiant.java index 0bffafbc68..2cd86e58f7 100644 --- a/Mage.Sets/src/mage/cards/l/LoafingGiant.java +++ b/Mage.Sets/src/mage/cards/l/LoafingGiant.java @@ -1,29 +1,31 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AttacksOrBlocksTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.PreventCombatDamageBySourceEffect; -import mage.abilities.effects.common.combat.CantBeBlockedByAllSourceEffect; -import mage.cards.Card; -import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.filter.StaticFilters; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; import mage.players.Player; +import java.util.Objects; +import java.util.UUID; + /** - * * @author noahg */ public final class LoafingGiant extends CardImpl { public LoafingGiant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); - + this.subtype.add(SubType.GIANT); this.power = new MageInt(4); this.toughness = new MageInt(6); @@ -46,7 +48,7 @@ class LoafingGiantEffect extends OneShotEffect { public LoafingGiantEffect() { super(Outcome.UnboostCreature); - this.staticText = "Put the top card of your library into your graveyard. If that card is a land card, prevent all combat damage {this} would deal this turn."; + this.staticText = "Mill a card. If a land card was milled this way, prevent all combat damage {this} would deal this turn."; } public LoafingGiantEffect(final LoafingGiantEffect effect) { @@ -61,14 +63,14 @@ class LoafingGiantEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Card card = player.getLibrary().getFromTop(game); - if (card != null) { - player.moveCards(card, Zone.GRAVEYARD, source, game); - if (card.isLand()) { - game.addEffect(new PreventCombatDamageBySourceEffect(Duration.EndOfTurn), source); - } - } + if (player != null + && player + .millCards(1, source, game) + .getCards(game) + .stream() + .filter(Objects::nonNull) + .anyMatch(MageObject::isLand)) { + game.addEffect(new PreventCombatDamageBySourceEffect(Duration.EndOfTurn), source); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java b/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java index ad78c80979..306d696d3a 100644 --- a/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java +++ b/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java @@ -4,7 +4,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect; @@ -41,7 +41,7 @@ public final class LoathsomeCatoblepas extends CardImpl { // {2}{G}: Loathsome Catoblepas must be blocked this turn if able. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect(), new ManaCostsImpl("{2}{G}"))); // When Loathsome Catoblepas dies, target creature an opponent controls gets -3/-3 until end of turn. - Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-3,-3, Duration.EndOfTurn), false); + Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-3,-3, Duration.EndOfTurn), false); Target target = new TargetCreaturePermanent(filter); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/l/LockjawSnapper.java b/Mage.Sets/src/mage/cards/l/LockjawSnapper.java index 7fd25e75c7..9ec5b304c2 100644 --- a/Mage.Sets/src/mage/cards/l/LockjawSnapper.java +++ b/Mage.Sets/src/mage/cards/l/LockjawSnapper.java @@ -4,7 +4,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.WitherAbility; import mage.cards.CardImpl; @@ -34,7 +34,7 @@ public final class LockjawSnapper extends CardImpl { this.addAbility(WitherAbility.getInstance()); // When Lockjaw Snapper dies, put a -1/-1 counter on each creature with a -1/-1 counter on it. - this.addAbility(new DiesTriggeredAbility(new LockjawSnapperEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new LockjawSnapperEffect())); } diff --git a/Mage.Sets/src/mage/cards/l/LodestoneGolem.java b/Mage.Sets/src/mage/cards/l/LodestoneGolem.java index 3121068d07..0a75868ada 100644 --- a/Mage.Sets/src/mage/cards/l/LodestoneGolem.java +++ b/Mage.Sets/src/mage/cards/l/LodestoneGolem.java @@ -1,30 +1,30 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.cards.Card; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; -import mage.game.Game; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class LodestoneGolem extends CardImpl { + private static final FilterCard filter = new FilterCard("Nonartifact spells"); + + static { + filter.add(Predicates.not(CardType.ARTIFACT.getPredicate())); + } + public LodestoneGolem(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); this.subtype.add(SubType.GOLEM); @@ -33,7 +33,7 @@ public final class LodestoneGolem extends CardImpl { this.toughness = new MageInt(3); // Nonartifact spells cost {1} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LodestoneGolemCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY))); } public LodestoneGolem(final LodestoneGolem card) { @@ -45,39 +45,3 @@ public final class LodestoneGolem extends CardImpl { return new LodestoneGolem(this); } } - -class LodestoneGolemCostReductionEffect extends CostModificationEffectImpl { - - LodestoneGolemCostReductionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Nonartifact spells cost {1} more to cast"; - } - - LodestoneGolemCostReductionEffect(LodestoneGolemCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - spellAbility.getManaCostsToPay().add(new GenericManaCost(1)); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - Card card = game.getCard(abilityToModify.getSourceId()); - if (card != null && !card.isArtifact()) { - return true; - } - } - return false; - } - - @Override - public LodestoneGolemCostReductionEffect copy() { - return new LodestoneGolemCostReductionEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/l/LoftyDenial.java b/Mage.Sets/src/mage/cards/l/LoftyDenial.java new file mode 100644 index 0000000000..30d834e78c --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoftyDenial.java @@ -0,0 +1,53 @@ +package mage.cards.l; + +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LoftyDenial extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + + public LoftyDenial(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Counter target spell unless its controller pays {1}. If you control a creature with flying, counter that spell unless its controller pays {4} instead. + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new CounterUnlessPaysEffect(new GenericManaCost(4)), + new CounterUnlessPaysEffect(new GenericManaCost(1)), + condition, "counter target spell unless its controller pays {1}. " + + "If you control a creature with flying, counter that spell unless its controller pays {4} instead" + )); + this.getSpellAbility().addTarget(new TargetSpell()); + } + + private LoftyDenial(final LoftyDenial card) { + super(card); + } + + @Override + public LoftyDenial copy() { + return new LoftyDenial(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoneRider.java b/Mage.Sets/src/mage/cards/l/LoneRider.java index bfc9584854..db9fa6f0b0 100644 --- a/Mage.Sets/src/mage/cards/l/LoneRider.java +++ b/Mage.Sets/src/mage/cards/l/LoneRider.java @@ -1,31 +1,34 @@ - package mage.cards.l; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.YouGainedLifeCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; -import mage.game.events.GameEvent; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.watchers.common.PlayerGainedLifeWatcher; +import java.util.UUID; + /** * @author fireshoes */ public final class LoneRider extends CardImpl { private static final String ruleText = "At the beginning of the end step, if you gained 3 or more life this turn, transform {this}"; + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2); + private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn"); public LoneRider(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); @@ -45,11 +48,14 @@ public final class LoneRider extends CardImpl { // At the beginning of the end step, if you gained 3 or more life this turn, transform Lone Rider. this.addAbility(new TransformAbility()); - TriggeredAbility triggered = new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new TransformSourceEffect(true)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(triggered, new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2), ruleText), new PlayerGainedLifeWatcher()); + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new TransformSourceEffect(true), TargetController.NEXT, false + ), condition, ruleText + ), new PlayerGainedLifeWatcher()); } - public LoneRider(final LoneRider card) { + private LoneRider(final LoneRider card) { super(card); } @@ -57,4 +63,4 @@ public final class LoneRider extends CardImpl { public LoneRider copy() { return new LoneRider(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/l/LongshotSquad.java b/Mage.Sets/src/mage/cards/l/LongshotSquad.java index 42676ccb5b..21f05d8648 100644 --- a/Mage.Sets/src/mage/cards/l/LongshotSquad.java +++ b/Mage.Sets/src/mage/cards/l/LongshotSquad.java @@ -35,7 +35,7 @@ public final class LongshotSquad extends CardImpl { public LongshotSquad(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.ARCHER); this.power = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java b/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java index ca432b80c0..c3b224a46c 100644 --- a/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java +++ b/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java @@ -1,13 +1,13 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -16,8 +16,9 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class LookoutsDispersal extends CardImpl { @@ -32,8 +33,10 @@ public final class LookoutsDispersal extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); // Lookout's Dispersal costs {1} less to cast if you control a Pirate. - Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter))); + Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, condition)); ability.setRuleAtTheTop(true); + ability.addHint(new ConditionHint(condition, "You control a Pirate")); this.addAbility(ability); // Counter target spell unless its controller pays {4}. diff --git a/Mage.Sets/src/mage/cards/l/LordWindgrace.java b/Mage.Sets/src/mage/cards/l/LordWindgrace.java index b751835dbf..9e4fcc1a9b 100644 --- a/Mage.Sets/src/mage/cards/l/LordWindgrace.java +++ b/Mage.Sets/src/mage/cards/l/LordWindgrace.java @@ -103,9 +103,9 @@ class LordWindgraceEffect extends OneShotEffect { } Card card = player.discardOne(false, source, game); if (card == null || !card.isLand()) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } else { - player.drawCards(2, game); + player.drawCards(2, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/l/LoreDrakkis.java b/Mage.Sets/src/mage/cards/l/LoreDrakkis.java new file mode 100644 index 0000000000..46a923ab7b --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoreDrakkis.java @@ -0,0 +1,51 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LoreDrakkis extends CardImpl { + + private static final FilterCard filter + = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"); + + public LoreDrakkis(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); + + this.subtype.add(SubType.LIZARD); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Mutate {U/R}{U/R} + this.addAbility(new MutateAbility(this, "{U/R}{U/R}")); + + // Whenever this creature mutates, return target instant or sorcery card from your graveyard to your hand. + Ability ability = new MutatesSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private LoreDrakkis(final LoreDrakkis card) { + super(card); + } + + @Override + public LoreDrakkis copy() { + return new LoreDrakkis(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LorescaleCoatl.java b/Mage.Sets/src/mage/cards/l/LorescaleCoatl.java index 45a24b501a..80077d3d8f 100644 --- a/Mage.Sets/src/mage/cards/l/LorescaleCoatl.java +++ b/Mage.Sets/src/mage/cards/l/LorescaleCoatl.java @@ -1,8 +1,5 @@ - - package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.DrawCardControllerTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -12,25 +9,26 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Loki */ public final class LorescaleCoatl extends CardImpl { - public LorescaleCoatl (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}{U}"); + public LorescaleCoatl(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}"); this.subtype.add(SubType.SNAKE); - - this.power = new MageInt(2); this.toughness = new MageInt(2); - this.addAbility(new DrawCardControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), true)); + this.addAbility(new DrawCardControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false + )); } - public LorescaleCoatl (final LorescaleCoatl card) { + private LorescaleCoatl(final LorescaleCoatl card) { super(card); } @@ -38,4 +36,4 @@ public final class LorescaleCoatl extends CardImpl { public LorescaleCoatl copy() { return new LorescaleCoatl(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/l/LostLegacy.java b/Mage.Sets/src/mage/cards/l/LostLegacy.java index a2eec689c1..a8bd230b49 100644 --- a/Mage.Sets/src/mage/cards/l/LostLegacy.java +++ b/Mage.Sets/src/mage/cards/l/LostLegacy.java @@ -61,7 +61,7 @@ class LostLegacyEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExi boolean result = super.applySearchAndExile(game, source, cardName, targetPointer.getFirst(game, source)); int cardsExiled = cardsInHandBefore - targetPlayer.getHand().count(filter, game); if (cardsExiled > 0) { - targetPlayer.drawCards(cardsExiled, game); + targetPlayer.drawCards(cardsExiled, source.getSourceId(), game); } return result; } diff --git a/Mage.Sets/src/mage/cards/l/LoxodonSmiter.java b/Mage.Sets/src/mage/cards/l/LoxodonSmiter.java index f43e90beba..834e7544cd 100644 --- a/Mage.Sets/src/mage/cards/l/LoxodonSmiter.java +++ b/Mage.Sets/src/mage/cards/l/LoxodonSmiter.java @@ -3,7 +3,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DiscardOntoBattlefieldEffect; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class LoxodonSmiter extends CardImpl { this.toughness = new MageInt(4); // Loxodon Smiter can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // If a spell or ability an opponent controls causes you to discard Loxodon Smiter, put it onto the battlefield instead of putting it into your graveyard. this.addAbility(new SimpleStaticAbility(Zone.HAND, new DiscardOntoBattlefieldEffect())); diff --git a/Mage.Sets/src/mage/cards/l/LoyalCathar.java b/Mage.Sets/src/mage/cards/l/LoyalCathar.java index 8282b63392..8764ce01df 100644 --- a/Mage.Sets/src/mage/cards/l/LoyalCathar.java +++ b/Mage.Sets/src/mage/cards/l/LoyalCathar.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -42,7 +42,7 @@ public final class LoyalCathar extends CardImpl { // When Loyal Cathar dies, return it to the battlefield transformed under your control at the beginning of the next end step. this.addAbility(new TransformAbility()); - this.addAbility(new DiesTriggeredAbility(new LoyalCatharEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new LoyalCatharEffect())); } public LoyalCathar(final LoyalCathar card) { diff --git a/Mage.Sets/src/mage/cards/l/LudevicNecroAlchemist.java b/Mage.Sets/src/mage/cards/l/LudevicNecroAlchemist.java index 45991117b8..4fc4cd12e2 100644 --- a/Mage.Sets/src/mage/cards/l/LudevicNecroAlchemist.java +++ b/Mage.Sets/src/mage/cards/l/LudevicNecroAlchemist.java @@ -103,7 +103,7 @@ class LudevicNecroAlchemistEffect extends OneShotEffect { Player player = game.getPlayer(game.getActivePlayerId()); if (player != null && player.chooseUse(Outcome.DrawCard, "Draw a card?", source, game)) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java b/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java new file mode 100644 index 0000000000..df191e4827 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java @@ -0,0 +1,205 @@ +package mage.cards.l; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterPlaneswalkerPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class LukkaCoppercoatOutcast extends CardImpl { + + public LukkaCoppercoatOutcast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.LUKKA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // +1: Exile the top three cards of your library. Creature cards exiled this way gain "You may cast this card from exile as long as you control a Lukka planeswalker." + this.addAbility(new LoyaltyAbility(new LukkaCoppercoatOutcastExileEffect(), 1)); + + // −2: Exile target creature you control, then reveal cards from the top of your library until you reveal a creature card with higher converted mana cost. Put that card onto the battlefield and the rest on the bottom of your library in a random order. + Ability ability = new LoyaltyAbility(new LukkaCoppercoatOutcastPolymorphEffect(), -2); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + + // −7: Each creature you control deals damage equal to its power to each opponent. + this.addAbility(new LoyaltyAbility(new LukkaCoppercoatOutcastDamageEffect(), -7)); + } + + private LukkaCoppercoatOutcast(final LukkaCoppercoatOutcast card) { + super(card); + } + + @Override + public LukkaCoppercoatOutcast copy() { + return new LukkaCoppercoatOutcast(this); + } +} + +class LukkaCoppercoatOutcastExileEffect extends OneShotEffect { + + LukkaCoppercoatOutcastExileEffect() { + super(Outcome.PlayForFree); + this.staticText = "Exile the top three cards of your library. Creature cards exiled this way gain " + + "\"You may cast this card from exile as long as you control a Lukka planeswalker.\""; + } + + private LukkaCoppercoatOutcastExileEffect(final LukkaCoppercoatOutcastExileEffect effect) { + super(effect); + } + + @Override + public LukkaCoppercoatOutcastExileEffect copy() { + return new LukkaCoppercoatOutcastExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Set cards = controller.getLibrary().getTopCards(game, 3); + controller.moveCards(cards, Zone.EXILED, source, game); + + cards.stream().filter(MageObject::isCreature).forEach(card -> { + ContinuousEffect effect = new LukkaCoppercoatOutcastCastEffect(); + effect.setTargetPointer(new FixedTarget(card, game)); + game.addEffect(effect, source); + }); + return true; + } +} + +class LukkaCoppercoatOutcastCastEffect extends AsThoughEffectImpl { + + private static final FilterPermanent filter = new FilterPlaneswalkerPermanent(SubType.LUKKA); + + LukkaCoppercoatOutcastCastEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); + this.staticText = "You may cast this card from exile as long as you control a Lukka planeswalker."; + } + + private LukkaCoppercoatOutcastCastEffect(final LukkaCoppercoatOutcastCastEffect effect) { + super(effect); + } + + @Override + public LukkaCoppercoatOutcastCastEffect copy() { + return new LukkaCoppercoatOutcastCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return game.getBattlefield().countAll(filter, affectedControllerId, game) > 0 + && source.isControlledBy(affectedControllerId) + && getTargetPointer().getTargets(game, source).contains(sourceId); + } +} + +class LukkaCoppercoatOutcastPolymorphEffect extends OneShotEffect { + + LukkaCoppercoatOutcastPolymorphEffect() { + super(Outcome.Benefit); + staticText = "Exile target creature you control, then reveal cards from the top of your library " + + "until you reveal a creature card with higher converted mana cost. " + + "Put that card onto the battlefield and the rest on the bottom of your library in a random order."; + } + + private LukkaCoppercoatOutcastPolymorphEffect(final LukkaCoppercoatOutcastPolymorphEffect effect) { + super(effect); + } + + @Override + public LukkaCoppercoatOutcastPolymorphEffect copy() { + return new LukkaCoppercoatOutcastPolymorphEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + Player player = game.getPlayer(source.getControllerId()); + if (permanent == null || player == null) { + return false; + } + int cmc = permanent.getConvertedManaCost(); + player.moveCards(permanent, Zone.EXILED, source, game); + Card toBattlefield = null; + Cards toReveal = new CardsImpl(); + for (Card card : player.getLibrary().getCards(game)) { + toReveal.add(card); + if (card.isCreature() && card.getConvertedManaCost() > cmc) { + toBattlefield = card; + break; + } + } + player.revealCards("", toReveal, game); + toReveal.remove(toBattlefield); + player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game); + player.putCardsOnBottomOfLibrary(toReveal, game, source, false); + return true; + } +} + +class LukkaCoppercoatOutcastDamageEffect extends OneShotEffect { + + LukkaCoppercoatOutcastDamageEffect() { + super(Outcome.Benefit); + staticText = "Each creature you control deals damage equal to its power to each opponent."; + } + + private LukkaCoppercoatOutcastDamageEffect(final LukkaCoppercoatOutcastDamageEffect effect) { + super(effect); + } + + @Override + public LukkaCoppercoatOutcastDamageEffect copy() { + return new LukkaCoppercoatOutcastDamageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List controlledCreatures = game.getBattlefield().getAllActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game + ); + List opponentList = game + .getOpponents(source.getControllerId()) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + for (Permanent permanent : controlledCreatures) { + for (Player opponent : opponentList) { + opponent.damage(permanent.getPower().getValue(), permanent.getId(), game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LumengridAugur.java b/Mage.Sets/src/mage/cards/l/LumengridAugur.java index 8f747c7a32..3139e17f5d 100644 --- a/Mage.Sets/src/mage/cards/l/LumengridAugur.java +++ b/Mage.Sets/src/mage/cards/l/LumengridAugur.java @@ -71,7 +71,7 @@ class LumengridAugurEffect extends OneShotEffect { Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (player != null) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); Card discardedCard = player.discardOne(false, source, game); if (discardedCard != null && discardedCard.isArtifact()) { if (sourcePermanent != null) { diff --git a/Mage.Sets/src/mage/cards/l/LumengridDrake.java b/Mage.Sets/src/mage/cards/l/LumengridDrake.java index 884ac2cced..6b2dd038a0 100644 --- a/Mage.Sets/src/mage/cards/l/LumengridDrake.java +++ b/Mage.Sets/src/mage/cards/l/LumengridDrake.java @@ -1,20 +1,22 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author ayrat */ @@ -23,7 +25,7 @@ public final class LumengridDrake extends CardImpl { private static final String ruleText = "Metalcraft — When {this} enters the battlefield, if you control three or more artifacts, return target creature to its owner's hand."; public LumengridDrake(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); this.subtype.add(SubType.DRAKE); this.color.setBlue(true); @@ -37,6 +39,8 @@ public final class LumengridDrake extends CardImpl { TriggeredAbility conditional = new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect()), MetalcraftCondition.instance, ruleText); conditional.addTarget(new TargetCreaturePermanent()); + conditional.setAbilityWord(AbilityWord.METALCRAFT); + conditional.addHint(MetalcraftHint.instance); this.addAbility(conditional); } diff --git a/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java b/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java new file mode 100644 index 0000000000..cad7881b47 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java @@ -0,0 +1,134 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LuminousBroodmoth extends CardImpl { + + public LuminousBroodmoth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); + + this.subtype.add(SubType.INSECT); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it. + this.addAbility(new LuminousBroodmothTriggeredAbility()); + } + + private LuminousBroodmoth(final LuminousBroodmoth card) { + super(card); + } + + @Override + public LuminousBroodmoth copy() { + return new LuminousBroodmoth(this); + } +} + +class LuminousBroodmothTriggeredAbility extends TriggeredAbilityImpl { + + LuminousBroodmothTriggeredAbility() { + super(Zone.BATTLEFIELD, new LuminousBroodmothEffect(), false); + } + + private LuminousBroodmothTriggeredAbility(final LuminousBroodmothTriggeredAbility ability) { + super(ability); + } + + @Override + public LuminousBroodmothTriggeredAbility copy() { + return new LuminousBroodmothTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getTarget() == null || zEvent.getTarget().getId().equals(this.getSourceId())) { + return false; + } + Permanent permanent = game.getPermanentOrLKIBattlefield(zEvent.getTarget().getId()); + + if (permanent != null + && zEvent.getToZone() == Zone.GRAVEYARD + && zEvent.getFromZone() == Zone.BATTLEFIELD + && permanent.isCreature() + && !permanent.getAbilities().containsKey(FlyingAbility.getInstance().getId()) + && permanent.isControlledBy(this.controllerId)) { + this.getEffects().setTargetPointer(new FixedTarget(zEvent.getTargetId())); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever a creature you control without flying dies, " + + "return it to the battlefield under its owner's control with a flying counter on it."; + } +} + +class LuminousBroodmothEffect extends OneShotEffect { + + LuminousBroodmothEffect() { + super(Outcome.PutCardInPlay); + } + + private LuminousBroodmothEffect(final LuminousBroodmothEffect effect) { + super(effect); + } + + @Override + public LuminousBroodmothEffect copy() { + return new LuminousBroodmothEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (card == null || game.getState().getZone(card.getId()) != Zone.GRAVEYARD) { + return false; + } + Player player = game.getPlayer(card.getOwnerId()); + if (player == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { + return false; + } + permanent.addCounters(CounterType.FLYING.createInstance(), source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LurkingDeadeye.java b/Mage.Sets/src/mage/cards/l/LurkingDeadeye.java new file mode 100644 index 0000000000..a4b1aae857 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LurkingDeadeye.java @@ -0,0 +1,56 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LurkingDeadeye extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature that was dealt damage this turn"); + + static { + filter.add(new WasDealtDamageThisTurnPredicate()); + } + + public LurkingDeadeye(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ASSASSIN); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Lurking Deadeye enters the battlefield, destroy target creature that was dealt damage this turn. + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private LurkingDeadeye(final LurkingDeadeye card) { + super(card); + } + + @Override + public LurkingDeadeye copy() { + return new LurkingDeadeye(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LurkingEvil.java b/Mage.Sets/src/mage/cards/l/LurkingEvil.java index c7ad56127a..ef773aee93 100644 --- a/Mage.Sets/src/mage/cards/l/LurkingEvil.java +++ b/Mage.Sets/src/mage/cards/l/LurkingEvil.java @@ -59,7 +59,7 @@ class LurkingEvilCost extends CostImpl { @Override public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { Player controller = game.getPlayer(controllerId); - return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost()); + return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost(ability)); } @Override diff --git a/Mage.Sets/src/mage/cards/l/LurkingJackals.java b/Mage.Sets/src/mage/cards/l/LurkingJackals.java index 4e292aad81..41b5458813 100644 --- a/Mage.Sets/src/mage/cards/l/LurkingJackals.java +++ b/Mage.Sets/src/mage/cards/l/LurkingJackals.java @@ -1,126 +1,126 @@ -package mage.cards.l; - -import mage.MageInt; -import mage.abilities.StateTriggeredAbility; -import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.token.TokenImpl; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class LurkingJackals extends CardImpl { - - public LurkingJackals(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); - - // When an opponent has 10 or less life, if Lurking Jackals is an enchantment, it becomes a 3/2 Hound creature. - this.addAbility(new LurkingJackalsStateTriggeredAbility()); - } - - public LurkingJackals(final LurkingJackals card) { - super(card); - } - - @Override - public LurkingJackals copy() { - return new LurkingJackals(this); - } -} - -class LurkingJackalsStateTriggeredAbility extends StateTriggeredAbility { - - public LurkingJackalsStateTriggeredAbility() { - super(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new LurkingJackalsToken(), "", Duration.Custom, true, false)); - } - - public LurkingJackalsStateTriggeredAbility(final LurkingJackalsStateTriggeredAbility ability) { - super(ability); - } - - @Override - public LurkingJackalsStateTriggeredAbility copy() { - return new LurkingJackalsStateTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(getControllerId()) != null) { - for (UUID opponentId : game.getOpponents(getControllerId())) { - if (game.getPlayer(opponentId).getLife() <= 10) { - return true; - } - } - } - return false; - } - - @Override - public boolean checkInterveningIfClause(Game game) { - if (getSourcePermanentIfItStillExists(game) != null) { - return getSourcePermanentIfItStillExists(game).isEnchantment(); - } - return false; - } - - @Override - public boolean canTrigger(Game game) { - //20100716 - 603.8 - return !Boolean.TRUE.equals(game.getState().getValue(getSourceId().toString() + "triggered")); - } - - @Override - public void trigger(Game game, UUID controllerId) { - //20100716 - 603.8 - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); - } - - @Override - public boolean resolve(Game game) { - //20100716 - 603.8 - boolean result = super.resolve(game); - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); - return result; - } - - @Override - public void counter(Game game) { - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); - } - - @Override - public String getRule() { - return "When an opponent has 10 or less life, if {this} is an enchantment, " + super.getRule(); - } - -} - -class LurkingJackalsToken extends TokenImpl { - - public LurkingJackalsToken() { - super("Hound", "3/2 Hound creature"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.HOUND); - power = new MageInt(3); - toughness = new MageInt(2); - } - - public LurkingJackalsToken(final LurkingJackalsToken token) { - super(token); - } - - @Override - public LurkingJackalsToken copy() { - return new LurkingJackalsToken(this); - } -} +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.StateTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.TokenImpl; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class LurkingJackals extends CardImpl { + + public LurkingJackals(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); + + // When an opponent has 10 or less life, if Lurking Jackals is an enchantment, it becomes a 3/2 Hound creature. + this.addAbility(new LurkingJackalsStateTriggeredAbility()); + } + + public LurkingJackals(final LurkingJackals card) { + super(card); + } + + @Override + public LurkingJackals copy() { + return new LurkingJackals(this); + } +} + +class LurkingJackalsStateTriggeredAbility extends StateTriggeredAbility { + + public LurkingJackalsStateTriggeredAbility() { + super(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new LurkingJackalsToken(), "", Duration.Custom, true, false)); + } + + public LurkingJackalsStateTriggeredAbility(final LurkingJackalsStateTriggeredAbility ability) { + super(ability); + } + + @Override + public LurkingJackalsStateTriggeredAbility copy() { + return new LurkingJackalsStateTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (game.getOpponents(getControllerId()) != null) { + for (UUID opponentId : game.getOpponents(getControllerId())) { + if (game.getPlayer(opponentId).getLife() <= 10) { + return true; + } + } + } + return false; + } + + @Override + public boolean checkInterveningIfClause(Game game) { + if (getSourcePermanentIfItStillExists(game) != null) { + return getSourcePermanentIfItStillExists(game).isEnchantment(); + } + return false; + } + + @Override + public boolean canTrigger(Game game) { + //20100716 - 603.8 + return !Boolean.TRUE.equals(game.getState().getValue(getSourceId().toString() + "triggered")); + } + + @Override + public void trigger(Game game, UUID controllerId) { + //20100716 - 603.8 + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); + super.trigger(game, controllerId); + } + + @Override + public boolean resolve(Game game) { + //20100716 - 603.8 + boolean result = super.resolve(game); + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); + return result; + } + + @Override + public void counter(Game game) { + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); + } + + @Override + public String getRule() { + return "When an opponent has 10 or less life, if {this} is an enchantment, " + super.getRule(); + } + +} + +class LurkingJackalsToken extends TokenImpl { + + public LurkingJackalsToken() { + super("Hound", "3/2 Hound creature"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.DOG); + power = new MageInt(3); + toughness = new MageInt(2); + } + + public LurkingJackalsToken(final LurkingJackalsToken token) { + super(token); + } + + @Override + public LurkingJackalsToken copy() { + return new LurkingJackalsToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LurkingSkirge.java b/Mage.Sets/src/mage/cards/l/LurkingSkirge.java index 8185db4b94..9188bb1cd4 100644 --- a/Mage.Sets/src/mage/cards/l/LurkingSkirge.java +++ b/Mage.Sets/src/mage/cards/l/LurkingSkirge.java @@ -1,70 +1,70 @@ -package mage.cards.l; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; -import mage.abilities.condition.common.SourceMatchesFilterCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.abilities.keyword.FlyingAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.permanent.token.TokenImpl; - -/** - * - * @author jeffwadsworth - */ -public final class LurkingSkirge extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(TargetController.OPPONENT.getOwnerPredicate()); - } - - public LurkingSkirge(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); - - // When a creature is put into an opponent's graveyard from the battlefield, if Lurking Skirge is an enchantment, Lurking Skirge becomes a 3/2 Imp creature with flying. - TriggeredAbility ability = new PutIntoGraveFromBattlefieldAllTriggeredAbility(new BecomesCreatureSourceEffect(new LurkingSkirgeToken(), "", Duration.WhileOnBattlefield, true, false), false, filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), - "When a creature is put into an opponent's graveyard from the battlefield, if {this} is an enchantment, {this} becomes a 3/2 Imp creature with flying.")); - } - - public LurkingSkirge(final LurkingSkirge card) { - super(card); - } - - @Override - public LurkingSkirge copy() { - return new LurkingSkirge(this); - } -} - -class LurkingSkirgeToken extends TokenImpl { - - public LurkingSkirgeToken() { - super("Imp", "3/2 Imp with flying."); - cardType.add(CardType.CREATURE); - subtype.add(SubType.IMP); - power = new MageInt(3); - toughness = new MageInt(2); - this.addAbility(FlyingAbility.getInstance()); - } - - public LurkingSkirgeToken(final LurkingSkirgeToken token) { - super(token); - } - - public LurkingSkirgeToken copy() { - return new LurkingSkirgeToken(this); - } -} +package mage.cards.l; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; +import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.token.TokenImpl; + +/** + * + * @author jeffwadsworth + */ +public final class LurkingSkirge extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(TargetController.OPPONENT.getOwnerPredicate()); + } + + public LurkingSkirge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + + // When a creature is put into an opponent's graveyard from the battlefield, if Lurking Skirge is an enchantment, Lurking Skirge becomes a 3/2 Imp creature with flying. + TriggeredAbility ability = new PutIntoGraveFromBattlefieldAllTriggeredAbility(new BecomesCreatureSourceEffect(new LurkingSkirgeToken(), "", Duration.WhileOnBattlefield, true, false), false, filter, false); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + "When a creature is put into an opponent's graveyard from the battlefield, if {this} is an enchantment, {this} becomes a 3/2 Imp creature with flying.")); + } + + public LurkingSkirge(final LurkingSkirge card) { + super(card); + } + + @Override + public LurkingSkirge copy() { + return new LurkingSkirge(this); + } +} + +class LurkingSkirgeToken extends TokenImpl { + + public LurkingSkirgeToken() { + super("Imp", "3/2 Imp with flying."); + cardType.add(CardType.CREATURE); + subtype.add(SubType.IMP); + power = new MageInt(3); + toughness = new MageInt(2); + this.addAbility(FlyingAbility.getInstance()); + } + + public LurkingSkirgeToken(final LurkingSkirgeToken token) { + super(token); + } + + public LurkingSkirgeToken copy() { + return new LurkingSkirgeToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java b/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java new file mode 100644 index 0000000000..5500a93a93 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java @@ -0,0 +1,197 @@ +package mage.cards.l; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.CompanionAbility; +import mage.abilities.keyword.CompanionCondition; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.Watcher; + +/** + * @author TheElk801 + */ +public final class LurrusOfTheDreamDen extends CardImpl { + + public LurrusOfTheDreamDen(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W/B}{W/B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Companion — Each permanent card in your starting deck has converted mana cost 2 or less. + this.addAbility(new CompanionAbility(LurrusOfTheDreamDenCompanionCondition.instance)); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // During each of your turns, you may cast one permanent spell with converted mana cost 2 or less from your graveyard. + this.addAbility(new SimpleStaticAbility( + new LurrusOfTheDreamDenContinuousEffect() + ), new LurrusOfTheDreamDenWatcher()); + } + + private LurrusOfTheDreamDen(final LurrusOfTheDreamDen card) { + super(card); + } + + @Override + public LurrusOfTheDreamDen copy() { + return new LurrusOfTheDreamDen(this); + } +} + +enum LurrusOfTheDreamDenCompanionCondition implements CompanionCondition { + instance; + + @Override + public String getRule() { + return "Each permanent card in your starting deck has converted mana cost 2 or less."; + } + + @Override + public boolean isLegal(Set deck, int startingSize) { + return deck.stream() + .filter(MageObject::isPermanent) + .mapToInt(MageObject::getConvertedManaCost) + .max() + .orElse(0) <= 2; + } +} + +class LurrusOfTheDreamDenContinuousEffect extends ContinuousEffectImpl { + + private static final FilterCard filter + = new FilterPermanentCard("permanent spell with converted mana cost 2 or less from your graveyard"); + + static { + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(Predicates.not(CardType.LAND.getPredicate())); + } + + LurrusOfTheDreamDenContinuousEffect() { + super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit); + staticText = "During each of your turns, you may cast one permanent spell " + + "with converted mana cost 2 or less from your graveyard"; + } + + private LurrusOfTheDreamDenContinuousEffect(final LurrusOfTheDreamDenContinuousEffect effect) { + super(effect); + } + + @Override + public LurrusOfTheDreamDenContinuousEffect copy() { + return new LurrusOfTheDreamDenContinuousEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + if (game.isActivePlayer(player.getId())) { + LurrusOfTheDreamDenWatcher watcher = game.getState().getWatcher(LurrusOfTheDreamDenWatcher.class); + if (watcher != null && !watcher.isAbilityUsed(new MageObjectReference(source.getSourceId(), game))) { + // if not used yet, add effect per card in graveyard to cast it + for (Card card : player.getGraveyard().getCards(filter, game)) { + ContinuousEffect effect = new LurrusOfTheDreamDenCastFromGraveyardEffect(); + effect.setTargetPointer(new FixedTarget(card, game)); + game.addEffect(effect, source); + } + } + } + return true; + } +} + +class LurrusOfTheDreamDenCastFromGraveyardEffect extends AsThoughEffectImpl { + + LurrusOfTheDreamDenCastFromGraveyardEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); + staticText = "You may cast one permanent spell with converted mana cost 2 or less from your graveyard"; + } + + private LurrusOfTheDreamDenCastFromGraveyardEffect(final LurrusOfTheDreamDenCastFromGraveyardEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public LurrusOfTheDreamDenCastFromGraveyardEffect copy() { + return new LurrusOfTheDreamDenCastFromGraveyardEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (!objectId.equals(getTargetPointer().getFirst(game, source))) { + return false; + } + if (!affectedControllerId.equals(source.getControllerId())) { + return false; + } + LurrusOfTheDreamDenWatcher watcher = game.getState().getWatcher(LurrusOfTheDreamDenWatcher.class); + return watcher != null && !watcher.isAbilityUsed(new MageObjectReference(source.getSourceId(), game)); + } +} + +class LurrusOfTheDreamDenWatcher extends Watcher { + + private final Set abilityUsed = new HashSet<>(); + + LurrusOfTheDreamDenWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST + || event.getZone() != Zone.GRAVEYARD + || event.getAdditionalReference() == null) { // permitting source to cast from graveyard + return; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null || !spell.isPermanent() || spell.getConvertedManaCost() > 2) { + return; + } + abilityUsed.add(event.getAdditionalReference()); + } + + @Override + public void reset() { + super.reset(); + abilityUsed.clear(); + } + + boolean isAbilityUsed(MageObjectReference mor) { + return abilityUsed.contains(mor); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LutriTheSpellchaser.java b/Mage.Sets/src/mage/cards/l/LutriTheSpellchaser.java new file mode 100644 index 0000000000..77ee48fc26 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LutriTheSpellchaser.java @@ -0,0 +1,97 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.CastFromEverywhereSourceCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.CopyTargetSpellEffect; +import mage.abilities.keyword.CompanionAbility; +import mage.abilities.keyword.CompanionCondition; +import mage.abilities.keyword.FlashAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterSpell; +import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.target.TargetSpell; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LutriTheSpellchaser extends CardImpl { + + private static final FilterSpell filter + = new FilterInstantOrSorcerySpell("instant or sorcery spell you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public LutriTheSpellchaser(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U/R}{U/R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.OTTER); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Companion — Each nonland card in your starting deck has a different name. + this.addAbility(new CompanionAbility(LutriTheSpellchaserCompanionCondition.instance)); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Lutri, the Spellchaser enters the battlefield, if you cast it, copy target instant or sorcery spell you control. You may choose new targets for the copy. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new CopyTargetSpellEffect(), false), + CastFromEverywhereSourceCondition.instance, "When {this} enters the battlefield, " + + "if you cast it, copy target instant or sorcery spell you control. " + + "You may choose new targets for the copy." + ); + ability.addTarget(new TargetSpell(filter)); + this.addAbility(ability); + } + + private LutriTheSpellchaser(final LutriTheSpellchaser card) { + super(card); + } + + @Override + public LutriTheSpellchaser copy() { + return new LutriTheSpellchaser(this); + } +} + +enum LutriTheSpellchaserCompanionCondition implements CompanionCondition { + instance; + + @Override + public String getRule() { + return "Each nonland card in your starting deck has a different name."; + } + + @Override + public boolean isLegal(Set deck, int startingSize) { + Map cardMap = new HashMap(); + deck.stream() + .filter(card -> !card.isLand()) + .map(MageObject::getName) + .forEach(s -> { + cardMap.putIfAbsent(s, 0); + cardMap.compute(s, (str, i) -> i + 1); + }); + return cardMap.values().stream().noneMatch(i -> i > 1); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MaalfeldTwins.java b/Mage.Sets/src/mage/cards/m/MaalfeldTwins.java index 1d080c5ed4..b13f6cb107 100644 --- a/Mage.Sets/src/mage/cards/m/MaalfeldTwins.java +++ b/Mage.Sets/src/mage/cards/m/MaalfeldTwins.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class MaalfeldTwins extends CardImpl { this.toughness = new MageInt(4); // When Maalfeld Twins dies, create two 2/2 black Zombie creature tokens. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ZombieToken(), 2))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ZombieToken(), 2))); } public MaalfeldTwins(final MaalfeldTwins card) { diff --git a/Mage.Sets/src/mage/cards/m/MadDog.java b/Mage.Sets/src/mage/cards/m/MadDog.java index 17254366cd..797f4cf6c3 100644 --- a/Mage.Sets/src/mage/cards/m/MadDog.java +++ b/Mage.Sets/src/mage/cards/m/MadDog.java @@ -30,7 +30,7 @@ public final class MadDog extends CardImpl { public MadDog(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java b/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java index fe3106b78c..a02f430b5d 100644 --- a/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java +++ b/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java @@ -53,11 +53,11 @@ public final class MaelstromArchangel extends CardImpl { class MaelstromArchangelCastEffect extends OneShotEffect { - private static final FilterCard filter = new FilterNonlandCard("nonland card from your hand"); + private static final FilterCard filter = new FilterNonlandCard("spell from your hand"); public MaelstromArchangelCastEffect() { super(Outcome.PlayForFree); - this.staticText = "you may cast a nonland card from your hand without paying its mana cost"; + this.staticText = "you may cast a spell from your hand without paying its mana cost"; } public MaelstromArchangelCastEffect(final MaelstromArchangelCastEffect effect) { @@ -75,7 +75,7 @@ class MaelstromArchangelCastEffect extends OneShotEffect { if (controller != null) { Target target = new TargetCardInHand(filter); if (target.canChoose(source.getSourceId(), controller.getId(), game) - && controller.chooseUse(outcome, "Cast a nonland card from your hand without paying its mana cost?", source, game)) { + && controller.chooseUse(outcome, "Cast a spell from your hand without paying its mana cost?", source, game)) { Card cardToCast = null; boolean cancel = false; while (controller.canRespond() && !cancel) { diff --git a/Mage.Sets/src/mage/cards/m/MagewrightsStone.java b/Mage.Sets/src/mage/cards/m/MagewrightsStone.java index 83bc556a45..dfe2fc0235 100644 --- a/Mage.Sets/src/mage/cards/m/MagewrightsStone.java +++ b/Mage.Sets/src/mage/cards/m/MagewrightsStone.java @@ -66,10 +66,8 @@ class HasAbilityWithTapSymbolPredicate implements Predicate { for (Ability ability : abilities) { if ((ability.getAbilityType() == AbilityType.ACTIVATED || ability.getAbilityType() == AbilityType.MANA) && !ability.getCosts().isEmpty()) { - for (Cost cost : ability.getCosts()) { - if (cost instanceof TapSourceCost) { + if (ability.hasTapCost()) { return true; - } } } } diff --git a/Mage.Sets/src/mage/cards/m/MagmaPhoenix.java b/Mage.Sets/src/mage/cards/m/MagmaPhoenix.java index 06b95eb719..848673d978 100644 --- a/Mage.Sets/src/mage/cards/m/MagmaPhoenix.java +++ b/Mage.Sets/src/mage/cards/m/MagmaPhoenix.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DamageEverythingEffect; @@ -30,7 +30,7 @@ public final class MagmaPhoenix extends CardImpl { this.toughness = new MageInt(3); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new DiesTriggeredAbility(new DamageEverythingEffect(3), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DamageEverythingEffect(3), false)); this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), new ManaCostsImpl("{3}{R}{R}"))); } diff --git a/Mage.Sets/src/mage/cards/m/MagmaSliver.java b/Mage.Sets/src/mage/cards/m/MagmaSliver.java index eadc0ecd65..967629a898 100644 --- a/Mage.Sets/src/mage/cards/m/MagmaSliver.java +++ b/Mage.Sets/src/mage/cards/m/MagmaSliver.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -9,6 +8,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.cards.CardImpl; @@ -35,11 +35,22 @@ public final class MagmaSliver extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - // All Slivers have "{tap}: Target Sliver creature gets +X/+0 until end of turn, where X is the number of Slivers on the battlefield." - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(new PermanentsOnBattlefieldCount(StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS), StaticValue.get(0), Duration.EndOfTurn, true), new TapSourceCost()); - Target target = new TargetCreaturePermanent(new FilterCreaturePermanent(SubType.SLIVER, "Sliver creature")); + // All Slivers have "{tap}: Target Sliver creature gets +X/+0 until end of turn, + // where X is the number of Slivers on the battlefield." + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new BoostTargetEffect(new PermanentsOnBattlefieldCount( + StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS), + StaticValue.get(0), Duration.EndOfTurn, true), + new TapSourceCost()); + Target target = new TargetCreaturePermanent( + new FilterCreaturePermanent(SubType.SLIVER, "Sliver creature")); ability.addTarget(target); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + Effect effect = new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS); + effect.setText("All Slivers have \"{T}: Target Sliver creature gets +X/+0 until end of turn," + + "where X is the number of Slivers on the battlefield.\""); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + effect)); } public MagmaSliver(final MagmaSliver card) { diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java b/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java index b5b386a559..bea1648029 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java @@ -6,22 +6,12 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.abilities.effects.common.BalanceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterControlledLandPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCardInHand; -import mage.target.common.TargetControlledPermanent; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; /** @@ -39,7 +29,7 @@ public final class MagusOfTheBalance extends CardImpl { // {4}{W}, {T}, Sacrifice Magus of the Balance: Each player chooses a number of lands they control equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players discard cards and sacrifice creatures the same way. Ability ability = new SimpleActivatedAbility( - new MagusOfTheBalanceEffect(), + new BalanceEffect(), new ManaCostsImpl("{4}{W}") ); ability.addCost(new TapSourceCost()); @@ -47,7 +37,7 @@ public final class MagusOfTheBalance extends CardImpl { this.addAbility(ability); } - public MagusOfTheBalance(final MagusOfTheBalance card) { + private MagusOfTheBalance(final MagusOfTheBalance card) { super(card); } @@ -56,139 +46,3 @@ public final class MagusOfTheBalance extends CardImpl { return new MagusOfTheBalance(this); } } - -class MagusOfTheBalanceEffect extends OneShotEffect { - - MagusOfTheBalanceEffect() { - super(Outcome.Sacrifice); - staticText = "each player chooses a number of lands they control " - + "equal to the number of lands controlled by the player " - + "who controls the fewest, then sacrifices the rest. " - + "Players discard cards and sacrifice creatures the same way"; - } - - MagusOfTheBalanceEffect(final MagusOfTheBalanceEffect effect) { - super(effect); - } - - @Override - public MagusOfTheBalanceEffect copy() { - return new MagusOfTheBalanceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - //Lands - int minLand = Integer.MAX_VALUE; - Cards landsToSacrifice = new CardsImpl(); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - int count = game.getBattlefield().countAll(new FilterControlledLandPermanent(), player.getId(), game); - if (count < minLand) { - minLand = count; - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - TargetControlledPermanent target = new TargetControlledPermanent(minLand, minLand, new FilterControlledLandPermanent("lands to keep"), true); - if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), player.getId(), source.getSourceId(), game)) { - if (permanent != null && !target.getTargets().contains(permanent.getId())) { - landsToSacrifice.add(permanent); - } - } - } - } - } - - for (UUID cardId : landsToSacrifice) { - Permanent permanent = game.getPermanent(cardId); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - } - } - - //Creatures - int minCreature = Integer.MAX_VALUE; - Cards creaturesToSacrifice = new CardsImpl(); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - int count = game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), player.getId(), game); - if (count < minCreature) { - minCreature = count; - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - TargetControlledPermanent target = new TargetControlledPermanent(minCreature, minCreature, new FilterControlledCreaturePermanent("creatures to keep"), true); - if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), player.getId(), source.getSourceId(), game)) { - if (permanent != null && !target.getTargets().contains(permanent.getId())) { - creaturesToSacrifice.add(permanent); - } - } - } - } - } - - for (UUID cardId : creaturesToSacrifice) { - Permanent permanent = game.getPermanent(cardId); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - } - } - - //Cards in hand - int minCard = Integer.MAX_VALUE; - Map cardsToDiscard = new HashMap<>(2); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - int count = player.getHand().size(); - if (count < minCard) { - minCard = count; - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Cards cards = new CardsImpl(); - TargetCardInHand target = new TargetCardInHand(minCard, new FilterCard("cards to keep")); - if (target.choose(Outcome.Discard, player.getId(), source.getSourceId(), game)) { - for (Card card : player.getHand().getCards(game)) { - if (card != null && !target.getTargets().contains(card.getId())) { - cards.add(card); - } - } - cardsToDiscard.put(playerId, cards); - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null && cardsToDiscard.get(playerId) != null) { - for (UUID cardId : cardsToDiscard.get(playerId)) { - Card card = game.getCard(cardId); - player.discard(card, source, game); - - } - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheJar.java b/Mage.Sets/src/mage/cards/m/MagusOfTheJar.java index f1c9c8778a..df05203174 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheJar.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheJar.java @@ -83,7 +83,7 @@ class MagusoftheJarEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(7, game); + player.drawCards(7, source.getSourceId(), game); } } //Delayed ability diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java b/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java index 26a6b6e3e2..c3aa993fd2 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.Set; @@ -10,21 +9,19 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; import mage.watchers.common.CastSpellLastTurnWatcher; /** @@ -79,7 +76,7 @@ class MagusOfTheMindEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); - if(watcher == null){ + if (watcher == null) { return false; } int stormCount = watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn() + 1; @@ -87,60 +84,10 @@ class MagusOfTheMindEffect extends OneShotEffect { controller.shuffleLibrary(source, game); if (controller.getLibrary().hasCards()) { Set cards = controller.getLibrary().getTopCards(game, stormCount); - if (cards != null) { - for (Card card : cards) { - if (card != null) { - controller.moveCardToExileWithInfo(card, source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); - ContinuousEffect effect = new MagusOfTheMindCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - game.addEffect(effect, source); - } - } - } + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, cards, TargetController.YOU, Duration.EndOfTurn, true); } return true; } return false; } } - -class MagusOfTheMindCastFromExileEffect extends AsThoughEffectImpl { - - MagusOfTheMindCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "you may play that card without paying its mana cost"; - } - - MagusOfTheMindCastFromExileEffect(final MagusOfTheMindCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public MagusOfTheMindCastFromExileEffect copy() { - return new MagusOfTheMindCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (objectId != null && objectId.equals(getTargetPointer().getFirst(game, source))) { - if (affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(objectId); - if (card != null && game.getState().getZone(objectId) == Zone.EXILED) { - if (!card.isLand() && card.getSpellAbility().getCosts() != null) { - Player player = game.getPlayer(affectedControllerId); - if (player != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - } - } - return true; - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java b/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java index 71f059afe6..3fd5ef2809 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java @@ -76,7 +76,7 @@ class MagusOfTheScrollEffect extends OneShotEffect { } revealed.add(card); you.revealCards(sourceObject.getName(), revealed, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { Permanent creature = game.getPermanent(targetPointer.getFirst(game, source)); if (creature != null) { creature.damage(2, source.getSourceId(), game, false, true); diff --git a/Mage.Sets/src/mage/cards/m/MairsilThePretender.java b/Mage.Sets/src/mage/cards/m/MairsilThePretender.java index 3a85125af0..3188b78a03 100644 --- a/Mage.Sets/src/mage/cards/m/MairsilThePretender.java +++ b/Mage.Sets/src/mage/cards/m/MairsilThePretender.java @@ -46,10 +46,12 @@ public final class MairsilThePretender extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // When Mairsil, the Pretender enters the battlefield, you may exile an artifact or creature card from your hand or graveyard and put a cage counter on it. + // When Mairsil, the Pretender enters the battlefield, you may exile an artifact or creature card from your hand + // or graveyard and put a cage counter on it. this.addAbility(new EntersBattlefieldTriggeredAbility(new MairsilThePretenderExileEffect(), true)); - // Mairsil, the Pretender has all activated abilities of all cards you own in exile with cage counters on them. You may activate each of those abilities only once each turn. + // Mairsil, the Pretender has all activated abilities of all cards you own in exile with cage counters on them. + // You may activate each of those abilities only once each turn. Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new MairsilThePretenderGainAbilitiesEffect()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MajesticAuricorn.java b/Mage.Sets/src/mage/cards/m/MajesticAuricorn.java new file mode 100644 index 0000000000..6ece7798a9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MajesticAuricorn.java @@ -0,0 +1,45 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.MutateAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MajesticAuricorn extends CardImpl { + + public MajesticAuricorn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); + + this.subtype.add(SubType.UNICORN); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Mutate {3}{W} + this.addAbility(new MutateAbility(this, "{3}{W}")); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever this creature mutates, you gain 4 life. + this.addAbility(new MutatesSourceTriggeredAbility(new GainLifeEffect(4))); + } + + private MajesticAuricorn(final MajesticAuricorn card) { + super(card); + } + + @Override + public MajesticAuricorn copy() { + return new MajesticAuricorn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MajesticMyriarch.java b/Mage.Sets/src/mage/cards/m/MajesticMyriarch.java index 6bf725d945..847eb1ed20 100644 --- a/Mage.Sets/src/mage/cards/m/MajesticMyriarch.java +++ b/Mage.Sets/src/mage/cards/m/MajesticMyriarch.java @@ -75,7 +75,7 @@ class MajesticMyriarchEffect extends OneShotEffect { filterDeathtouch.add(new AbilityPredicate(DeathtouchAbility.class)); filterDoubleStrike.add(new AbilityPredicate(DoubleStrikeAbility.class)); filterHaste.add(new AbilityPredicate(HasteAbility.class)); - filterHexproof.add(new AbilityPredicate(HexproofAbility.class)); + filterHexproof.add(new AbilityPredicate(HexproofBaseAbility.class)); filterIndestructible.add(new AbilityPredicate(IndestructibleAbility.class)); filterLifelink.add(new AbilityPredicate(LifelinkAbility.class)); filterMenace.add(new AbilityPredicate(MenaceAbility.class)); diff --git a/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java b/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java index a5f7c230ee..14f7a7d7f7 100644 --- a/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java +++ b/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java @@ -1,9 +1,9 @@ - package mage.cards.m; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BecomesTargetTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; @@ -24,7 +24,6 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.targetpointer.FixedTarget; /** * @@ -35,9 +34,13 @@ public final class MakeshiftMannequin extends CardImpl { public MakeshiftMannequin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}"); - // Return target creature card from your graveyard to the battlefield with a mannequin counter on it. For as long as that creature has a mannequin counter on it, it has "When this creature becomes the target of a spell or ability, sacrifice it." + // Return target creature card from your graveyard to the battlefield + // with a mannequin counter on it. For as long as that creature has a + // mannequin counter on it, it has "When this creature becomes the target + // of a spell or ability, sacrifice it." this.getSpellAbility().addEffect(new MakeshiftMannequinEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); } public MakeshiftMannequin(final MakeshiftMannequin card) { @@ -54,7 +57,11 @@ class MakeshiftMannequinEffect extends OneShotEffect { MakeshiftMannequinEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Return target creature card from your graveyard to the battlefield with a mannequin counter on it. For as long as that creature has a mannequin counter on it, it has \"When this creature becomes the target of a spell or ability, sacrifice it.\""; + this.staticText = "Return target creature card from your graveyard " + + "to the battlefield with a mannequin counter on it. " + + "For as long as that creature has a mannequin counter on it, " + + "it has \"When this creature becomes the target of a spell " + + "or ability, sacrifice it.\""; } MakeshiftMannequinEffect(final MakeshiftMannequinEffect effect) { @@ -80,11 +87,14 @@ class MakeshiftMannequinEffect extends OneShotEffect { Permanent permanent = game.getPermanent(cardId); if (permanent != null) { ContinuousEffect gainedEffect = new MakeshiftMannequinGainAbilityEffect(); - gainedEffect.setTargetPointer(new FixedTarget(permanent, game)); - game.addEffect(gainedEffect, source); + // Bug #6885 Fixed when owner/controller leaves the game the effect still applies + SimpleStaticAbility gainAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, gainedEffect); + gainAbility.setSourceId(cardId); + gainAbility.getTargets().add(source.getTargets().get(0)); + game.addEffect(gainedEffect, gainAbility); + return true; } } - return true; } } return false; @@ -105,7 +115,10 @@ class MakeshiftMannequinGainAbilityEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (permanent != null) { - permanent.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), source.getSourceId(), game); + permanent.addAbility( + new BecomesTargetTriggeredAbility( + new SacrificeSourceEffect()), + source.getSourceId(), game); return true; } return false; @@ -114,7 +127,8 @@ class MakeshiftMannequinGainAbilityEffect extends ContinuousEffectImpl { @Override public boolean isInactive(Ability source, Game game) { Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - return permanent == null || permanent.getCounters(game).getCount(CounterType.MANNEQUIN) < 1; + return permanent == null + || permanent.getCounters(game).getCount(CounterType.MANNEQUIN) < 1; } @Override diff --git a/Mage.Sets/src/mage/cards/m/MaleficScythe.java b/Mage.Sets/src/mage/cards/m/MaleficScythe.java new file mode 100644 index 0000000000..d89b4cc62e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MaleficScythe.java @@ -0,0 +1,58 @@ +package mage.cards.m; + +import mage.abilities.common.DiesAttachedTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MaleficScythe extends CardImpl { + + private static final DynamicValue xValue = new CountersSourceCount(CounterType.SOUL); + + public MaleficScythe(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{B}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Malefic Scythe enters the battlefield with a soul counter on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.SOUL.createInstance(1)), + "with a soul counter on it" + )); + + // Equipped creature gets +1/+1 for each soul counter on Malefic Scythe. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(xValue, xValue, Duration.WhileOnBattlefield))); + + // Whenever equipped creature dies, put a soul counter on Malefic Scythe. + this.addAbility(new DiesAttachedTriggeredAbility( + new AddCountersSourceEffect(CounterType.SOUL.createInstance()), "equipped" + )); + + // Equip {1} + this.addAbility(new EquipAbility(1)); + } + + private MaleficScythe(final MaleficScythe card) { + super(card); + } + + @Override + public MaleficScythe copy() { + return new MaleficScythe(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MaliciousAdvice.java b/Mage.Sets/src/mage/cards/m/MaliciousAdvice.java index 6949f32da7..9f6295f7ae 100644 --- a/Mage.Sets/src/mage/cards/m/MaliciousAdvice.java +++ b/Mage.Sets/src/mage/cards/m/MaliciousAdvice.java @@ -27,7 +27,7 @@ public final class MaliciousAdvice extends CardImpl { // Tap X target artifacts, creatures, and/or lands. You lose X life. Effect effect = new TapTargetEffect(); - effect.setText("X target artifacts, creatures, and/or lands."); + effect.setText("X target artifacts, creatures, and/or lands"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(ManacostVariableValue.instance)); this.getSpellAbility().setTargetAdjuster(MaliciousAdviceAdjuster.instance); diff --git a/Mage.Sets/src/mage/cards/m/MalignantGrowth.java b/Mage.Sets/src/mage/cards/m/MalignantGrowth.java index 3256638b3b..3172d1c94e 100644 --- a/Mage.Sets/src/mage/cards/m/MalignantGrowth.java +++ b/Mage.Sets/src/mage/cards/m/MalignantGrowth.java @@ -80,6 +80,6 @@ class MalignantGrowthEffect extends OneShotEffect { if (counters == 0) { return true; } - return player.damage(player.drawCards(counters, game), source.getSourceId(), game) > 0; + return player.damage(player.drawCards(counters, source.getSourceId(), game), source.getSourceId(), game) > 0; } } diff --git a/Mage.Sets/src/mage/cards/m/MammothHarness.java b/Mage.Sets/src/mage/cards/m/MammothHarness.java index aa126f999d..0df36ee9cf 100644 --- a/Mage.Sets/src/mage/cards/m/MammothHarness.java +++ b/Mage.Sets/src/mage/cards/m/MammothHarness.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -61,7 +61,7 @@ public final class MammothHarness extends CardImpl { } } -class MammothHarnessTriggeredAbility extends BlocksOrBecomesBlockedTriggeredAbility { +class MammothHarnessTriggeredAbility extends BlocksOrBecomesBlockedSourceTriggeredAbility { public MammothHarnessTriggeredAbility() { super(new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_PERMANENT_CREATURE, false, null, false); diff --git a/Mage.Sets/src/mage/cards/m/ManaCache.java b/Mage.Sets/src/mage/cards/m/ManaCache.java index 54cb19a0ba..9a163a4ed5 100644 --- a/Mage.Sets/src/mage/cards/m/ManaCache.java +++ b/Mage.Sets/src/mage/cards/m/ManaCache.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.TriggeredAbility; @@ -27,8 +25,10 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; +import mage.abilities.dynamicvalue.common.CountersSourceCount; + /** - * * @author L_J */ public final class ManaCache extends CardImpl { @@ -88,7 +88,8 @@ class ManaCacheEffect extends OneShotEffect { class ManaCacheManaAbility extends ActivatedManaAbilityImpl { public ManaCacheManaAbility() { - super(Zone.BATTLEFIELD, new BasicManaEffect(Mana.ColorlessMana(1)), new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(1))); + super(Zone.BATTLEFIELD, new BasicManaEffect(Mana.ColorlessMana(1), new CountersSourceCount(CounterType.CHARGE)), + new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(1))); this.netMana.add(new Mana(0, 0, 0, 0, 0, 0, 0, 1)); } @@ -105,7 +106,7 @@ class ManaCacheManaAbility extends ActivatedManaAbilityImpl { if (player != null && playerId.equals(game.getActivePlayerId()) && game.getStep().getType().isBefore(PhaseStep.END_TURN)) { if (costs.canPay(this, sourceId, playerId, game)) { this.setControllerId(playerId); - return ActivationStatus.getTrue(); + return ActivationStatus.getTrue(this, game); } } return ActivationStatus.getFalse(); diff --git a/Mage.Sets/src/mage/cards/m/ManaClash.java b/Mage.Sets/src/mage/cards/m/ManaClash.java index ef946eb397..1232a402f8 100644 --- a/Mage.Sets/src/mage/cards/m/ManaClash.java +++ b/Mage.Sets/src/mage/cards/m/ManaClash.java @@ -1,5 +1,6 @@ package mage.cards.m; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -10,8 +11,6 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; -import java.util.UUID; - /** * @author LevelX2 */ @@ -20,7 +19,8 @@ public final class ManaClash extends CardImpl { public ManaClash(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); - // You and target opponent each flip a coin. Mana Clash deals 1 damage to each player whose coin comes up tails. Repeat this process until both players' coins come up heads on the same flip. + // You and target opponent each flip a coin. Mana Clash deals 1 damage to each player whose coin comes up tails. + // Repeat this process until both players' coins come up heads on the same flip. this.getSpellAbility().addEffect(new ManaClashEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); } diff --git a/Mage.Sets/src/mage/cards/m/ManaCrypt.java b/Mage.Sets/src/mage/cards/m/ManaCrypt.java index 4adb41bbbd..70afe947fe 100644 --- a/Mage.Sets/src/mage/cards/m/ManaCrypt.java +++ b/Mage.Sets/src/mage/cards/m/ManaCrypt.java @@ -28,7 +28,7 @@ public final class ManaCrypt extends CardImpl { // At the beginning of your upkeep, flip a coin. If you lose the flip, Mana Crypt deals 3 damage to you. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ManaCryptEffect(), TargetController.YOU, false)); - // {tap}: Add {C}{C}. + // {T}: Add {C}{C}. this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(2), new TapSourceCost())); } diff --git a/Mage.Sets/src/mage/cards/m/ManaEchoes.java b/Mage.Sets/src/mage/cards/m/ManaEchoes.java index 0df24532a2..0e266ad925 100644 --- a/Mage.Sets/src/mage/cards/m/ManaEchoes.java +++ b/Mage.Sets/src/mage/cards/m/ManaEchoes.java @@ -61,7 +61,7 @@ class ManaEchoesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && permanent != null) { int foundCreatures = 0; for (Permanent perm : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { diff --git a/Mage.Sets/src/mage/cards/m/ManaWeb.java b/Mage.Sets/src/mage/cards/m/ManaWeb.java index 5fce3c5d7b..afc7ce6856 100644 --- a/Mage.Sets/src/mage/cards/m/ManaWeb.java +++ b/Mage.Sets/src/mage/cards/m/ManaWeb.java @@ -2,15 +2,18 @@ package mage.cards.m; import java.util.Objects; +import java.util.Set; import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.abilities.mana.AnyColorLandsProduceManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterLandPermanent; @@ -61,6 +64,9 @@ class ManaWebTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { + if (game.inCheckPlayableState()) { + return false; + } if (game.getOpponents(controllerId).contains(event.getPlayerId())) { Permanent permanent = game.getPermanent(event.getSourceId()); @@ -103,35 +109,19 @@ class ManaWebeffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = null; - - if (game != null && source != null) { - permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - } - - if (permanent != null && game != null) { - Mana mana = new Mana(); - - for (ActivatedManaAbilityImpl ability : permanent.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD)) { - for (Mana netMana : ability.getNetMana(game)) { - mana.add(netMana); - } - } - + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent != null) { + Set manaTypesSource = AnyColorLandsProduceManaAbility.getManaTypesFromPermanent(permanent, game); boolean tappedLands = false; for (Permanent opponentPermanent : game.getBattlefield().getActivePermanents(filter, permanent.getControllerId(), game)) { if (Objects.equals(opponentPermanent.getControllerId(), permanent.getControllerId())) { - Mana opponentLandMana = new Mana(); - - for (ActivatedManaAbilityImpl ability : opponentPermanent.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { - for (Mana netMana : ability.getNetMana(game)) { - opponentLandMana.add(netMana); + Set manaTypes = AnyColorLandsProduceManaAbility.getManaTypesFromPermanent(opponentPermanent, game); + for (ManaType manaType : manaTypes) { + if (manaTypesSource.contains(manaType)) { + tappedLands = opponentPermanent.tap(game) || tappedLands; + break; } } - - if (mana.containsAny(opponentLandMana, true)) { - tappedLands = opponentPermanent.tap(game) || tappedLands; - } } } return tappedLands; diff --git a/Mage.Sets/src/mage/cards/m/ManascapeRefractor.java b/Mage.Sets/src/mage/cards/m/ManascapeRefractor.java new file mode 100644 index 0000000000..632b6bbb1e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/ManascapeRefractor.java @@ -0,0 +1,135 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.AsThoughManaEffect; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.mana.BasicManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.ManaPoolItem; +import mage.util.CardUtil; + +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author AsterAether + */ +public final class ManascapeRefractor extends CardImpl { + + public ManascapeRefractor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // Manascape Refractor enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Manascape Refractor has all activated abilities of all lands on the battlefield. + this.addAbility(new SimpleStaticAbility(new ManascapeRefractorGainAbilitiesEffect())); + + // You may spend mana as though it were mana of any color to pay the activation costs of Manascape Refractor's abilities. + this.addAbility(new SimpleStaticAbility(new ManascapeRefractorSpendAnyManaEffect())); + } + + private ManascapeRefractor(final ManascapeRefractor card) { + super(card); + } + + @Override + public ManascapeRefractor copy() { + return new ManascapeRefractor(this); + } +} + +class ManascapeRefractorGainAbilitiesEffect extends ContinuousEffectImpl { + + private static final FilterPermanent filter = new FilterLandPermanent(); + + static { + filter.add(AnotherPredicate.instance); + } + + ManascapeRefractorGainAbilitiesEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "{this} has all activated abilities of all lands on the battlefield."; + this.addDependencyType(DependencyType.AddingAbility); + } + + private ManascapeRefractorGainAbilitiesEffect(final ManascapeRefractorGainAbilitiesEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent perm = game.getPermanent(source.getSourceId()); + if (perm == null) { + return true; + } + for (Ability ability : game.getState() + .getBattlefield() + .getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game) + .stream() + .map(permanent -> permanent.getAbilities(game)) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .filter(ability -> ability.getAbilityType() == AbilityType.ACTIVATED + || ability.getAbilityType() == AbilityType.MANA) + .collect(Collectors.toList())) { + if (!(ability instanceof BasicManaAbility) + || perm.getAbilities(game) + .stream() + .noneMatch(ability.getClass()::isInstance)) { + perm.addAbility(ability, source.getSourceId(), game); + } + } + return true; + } + + @Override + public ManascapeRefractorGainAbilitiesEffect copy() { + return new ManascapeRefractorGainAbilitiesEffect(this); + } +} + +class ManascapeRefractorSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect { + + ManascapeRefractorSpendAnyManaEffect() { + super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "You may spend mana as though it were mana of any color to pay the activation costs of {this}'s abilities."; + } + + private ManascapeRefractorSpendAnyManaEffect(final ManascapeRefractorSpendAnyManaEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public ManascapeRefractorSpendAnyManaEffect copy() { + return new ManascapeRefractorSpendAnyManaEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + objectId = CardUtil.getMainCardId(game, objectId); // for split cards + return objectId.equals(source.getSourceId()) && affectedControllerId.equals(source.getControllerId()); + } + + @Override + public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) { + return mana.getFirstAvailable(); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MandateOfPeace.java b/Mage.Sets/src/mage/cards/m/MandateOfPeace.java index 4e98d4afb1..c763b0c8f9 100644 --- a/Mage.Sets/src/mage/cards/m/MandateOfPeace.java +++ b/Mage.Sets/src/mage/cards/m/MandateOfPeace.java @@ -54,7 +54,7 @@ class MandateOfPeaceOpponentsCantCastSpellsEffect extends ContinuousRuleModifyin public MandateOfPeaceOpponentsCantCastSpellsEffect() { super(Duration.EndOfTurn, Outcome.Benefit); - staticText = "Your opponents can't cast spells this turn."; + staticText = "Your opponents can't cast spells this turn"; } public MandateOfPeaceOpponentsCantCastSpellsEffect(final MandateOfPeaceOpponentsCantCastSpellsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/ManedServal.java b/Mage.Sets/src/mage/cards/m/ManedServal.java new file mode 100644 index 0000000000..8c480f02f2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/ManedServal.java @@ -0,0 +1,36 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ManedServal extends CardImpl { + + public ManedServal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + } + + private ManedServal(final ManedServal card) { + super(card); + } + + @Override + public ManedServal copy() { + return new ManedServal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java b/Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java new file mode 100644 index 0000000000..6ecfbad160 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java @@ -0,0 +1,131 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.watchers.common.CastSpellLastTurnWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MangaraTheDiplomat extends CardImpl { + + public MangaraTheDiplomat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card. + this.addAbility(new MangaraTheDiplomatAttackTriggeredAbility()); + + // Whenever an opponent casts their second spell each turn, draw a card. + this.addAbility(new MangaraTheDiplomatCastTriggeredAbility()); + } + + private MangaraTheDiplomat(final MangaraTheDiplomat card) { + super(card); + } + + @Override + public MangaraTheDiplomat copy() { + return new MangaraTheDiplomat(this); + } +} + +class MangaraTheDiplomatAttackTriggeredAbility extends TriggeredAbilityImpl { + + MangaraTheDiplomatAttackTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); + } + + private MangaraTheDiplomatAttackTriggeredAbility(final MangaraTheDiplomatAttackTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Player player = game.getPlayer(getControllerId()); + return player != null + && player.hasOpponent(game.getActivePlayerId(), game) + && game + .getCombat() + .getAttackers() + .stream() + .map(uuid -> game.getCombat().getDefendingPlayerId(uuid, game)) + .filter(getControllerId()::equals) + .count() >= 2; + } + + @Override + public MangaraTheDiplomatAttackTriggeredAbility copy() { + return new MangaraTheDiplomatAttackTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever an opponent attacks with creatures, " + + "if two or more of those creatures are attacking you " + + "and/or a planeswalker you control, draw a card."; + } +} + + +class MangaraTheDiplomatCastTriggeredAbility extends TriggeredAbilityImpl { + + MangaraTheDiplomatCastTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); + } + + private MangaraTheDiplomatCastTriggeredAbility(final MangaraTheDiplomatCastTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Player player = game.getPlayer(getControllerId()); + CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + return player != null + && watcher != null + && player.hasOpponent(event.getPlayerId(), game) + && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 2; + } + + @Override + public MangaraTheDiplomatCastTriggeredAbility copy() { + return new MangaraTheDiplomatCastTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever an opponent casts their second spell each turn, draw a card."; + } +} diff --git a/Mage.Sets/src/mage/cards/m/ManicScribe.java b/Mage.Sets/src/mage/cards/m/ManicScribe.java index 26db45a69d..775f264a51 100644 --- a/Mage.Sets/src/mage/cards/m/ManicScribe.java +++ b/Mage.Sets/src/mage/cards/m/ManicScribe.java @@ -1,7 +1,5 @@ package mage.cards.m; -import java.util.UUID; - import mage.MageInt; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -17,6 +15,8 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.constants.Zone; +import java.util.UUID; + /** * @author fireshoes */ @@ -30,19 +30,22 @@ public final class ManicScribe extends CardImpl { this.toughness = new MageInt(3); // When Manic Scribe enters the battlefield, each opponent puts the top three cards of their library into their graveyard. - this.addAbility(new EntersBattlefieldTriggeredAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(3, TargetController.OPPONENT), false)); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new PutTopCardOfLibraryIntoGraveEachPlayerEffect(3, TargetController.OPPONENT), false + )); // Delirium — At the beginning of each opponent's upkeep, if there are four or more card types among cards in your graveyard, // that player puts the top three cards of their library into their graveyard. this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new PutTopCardOfLibraryIntoGraveTargetEffect(3), TargetController.OPPONENT, false, true), - DeliriumCondition.instance, - "Delirium — At the beginning of each opponent's upkeep, if there are four or more card types among cards in your graveyard, " - + "that player puts the top three cards of their library into their graveyard.") - .addHint(DeliriumHint.instance)); + new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, new PutTopCardOfLibraryIntoGraveTargetEffect(3), + TargetController.OPPONENT, false, true + ), DeliriumCondition.instance, "Delirium — At the beginning of each opponent's upkeep, " + + "if there are four or more card types among cards in your graveyard, that player mills three cards." + ).addHint(DeliriumHint.instance)); } - public ManicScribe(final ManicScribe card) { + private ManicScribe(final ManicScribe card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/m/ManipulateFate.java b/Mage.Sets/src/mage/cards/m/ManipulateFate.java index 5c342c1cc8..2883532f69 100644 --- a/Mage.Sets/src/mage/cards/m/ManipulateFate.java +++ b/Mage.Sets/src/mage/cards/m/ManipulateFate.java @@ -69,7 +69,7 @@ class ManipulateFateEffect extends SearchEffect { } } player.shuffleLibrary(source, game); - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/m/MaraudingLooter.java b/Mage.Sets/src/mage/cards/m/MaraudingLooter.java index 97f12cf25a..e0e52993ca 100644 --- a/Mage.Sets/src/mage/cards/m/MaraudingLooter.java +++ b/Mage.Sets/src/mage/cards/m/MaraudingLooter.java @@ -1,22 +1,23 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class MaraudingLooter extends CardImpl { @@ -29,13 +30,15 @@ public final class MaraudingLooter extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(3); - // Raid - At the beginning of your end step, if you attacked with a creature this turn, you may draw a card. If you do, discard a card. + // Raid - At the beginning of your end step, if you attacked this turn, you may draw a card. If you do, discard a card. Ability ability = new ConditionalInterveningIfTriggeredAbility( new BeginningOfEndStepTriggeredAbility(new DrawDiscardControllerEffect(1, 1, true), TargetController.YOU, false), RaidCondition.instance, "Raid — At the beginning of your end step, " - + "if you attacked with a creature this turn, " - + "you may draw a card. If you do, discard a card."); + + "if you attacked this turn, " + + "you may draw a card. If you do, discard a card."); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/m/MarduHeartPiercer.java b/Mage.Sets/src/mage/cards/m/MarduHeartPiercer.java index 2be60fad5a..d33732125d 100644 --- a/Mage.Sets/src/mage/cards/m/MarduHeartPiercer.java +++ b/Mage.Sets/src/mage/cards/m/MarduHeartPiercer.java @@ -1,38 +1,41 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetAnyTarget; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MarduHeartPiercer extends CardImpl { public MarduHeartPiercer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ARCHER); this.power = new MageInt(2); this.toughness = new MageInt(3); - // Raid - When Mardu Heart-Piercer enters the battlefield, if you attacked with a creature this turn, Mardu Heart-Piercer deals 2 damage to any target. + // Raid - When Mardu Heart-Piercer enters the battlefield, if you attacked this turn, Mardu Heart-Piercer deals 2 damage to any target. Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(2)), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, {this} deals 2 damage to any target."); + "Raid — When {this} enters the battlefield, if you attacked this turn, {this} deals 2 damage to any target."); ability.addTarget(new TargetAnyTarget()); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/m/MarduHordechief.java b/Mage.Sets/src/mage/cards/m/MarduHordechief.java index 7c01254d9f..891f06fe6d 100644 --- a/Mage.Sets/src/mage/cards/m/MarduHordechief.java +++ b/Mage.Sets/src/mage/cards/m/MarduHordechief.java @@ -1,21 +1,22 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.game.permanent.token.WarriorToken; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author emerald000 */ public final class MarduHordechief extends CardImpl { @@ -28,9 +29,13 @@ public final class MarduHordechief extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - // Raid — When Mardu Hordechief enters the battlefield, if you attacked with a creature this turn, create a 1/1 white Warrior creature token - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WarriorToken())), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, create a 1/1 white Warrior creature token."), + // Raid — When Mardu Hordechief enters the battlefield, if you attacked this turn, create a 1/1 white Warrior creature token + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility( + new CreateTokenEffect(new WarriorToken())), + RaidCondition.instance, + "Raid — When {this} enters the battlefield, if you attacked this turn, create a 1/1 white Warrior creature token.") + .setAbilityWord(AbilityWord.RAID) + .addHint(RaidHint.instance), new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/m/MarduSkullhunter.java b/Mage.Sets/src/mage/cards/m/MarduSkullhunter.java index bfbafab314..032141f31d 100644 --- a/Mage.Sets/src/mage/cards/m/MarduSkullhunter.java +++ b/Mage.Sets/src/mage/cards/m/MarduSkullhunter.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTappedAbility; @@ -9,21 +7,24 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetOpponent; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MarduSkullhunter extends CardImpl { public MarduSkullhunter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WARRIOR); @@ -33,10 +34,12 @@ public final class MarduSkullhunter extends CardImpl { // Mardu Skullhunter enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); - // Raid - When Mardu Skullhunter enters the battlefield, if you attacked with a creature this turn, target opponent discards a card. + // Raid - When Mardu Skullhunter enters the battlefield, if you attacked this turn, target opponent discards a card. Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new DiscardTargetEffect(1)), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, target opponent discards a card."); + "Raid — When {this} enters the battlefield, if you attacked this turn, target opponent discards a card."); ability.addTarget(new TargetOpponent()); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/m/MarduWarshrieker.java b/Mage.Sets/src/mage/cards/m/MarduWarshrieker.java index c573048114..7f05f3fa64 100644 --- a/Mage.Sets/src/mage/cards/m/MarduWarshrieker.java +++ b/Mage.Sets/src/mage/cards/m/MarduWarshrieker.java @@ -1,21 +1,22 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.mana.AddManaToManaPoolSourceControllerEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MarduWarshrieker extends CardImpl { @@ -28,9 +29,13 @@ public final class MarduWarshrieker extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - // Raid - When Mardu Warshrieker enters the battlefield, if you attacked with a creature this turn, add {R}{W}{B}. - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new AddManaToManaPoolSourceControllerEffect(new Mana(1, 0, 0, 1, 1, 0, 0, 0))), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, add {R}{W}{B}."), + // Raid - When Mardu Warshrieker enters the battlefield, if you attacked this turn, add {R}{W}{B}. + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility( + new AddManaToManaPoolSourceControllerEffect(new Mana(1, 0, 0, 1, 1, 0, 0, 0))), + RaidCondition.instance, + "Raid — When {this} enters the battlefield, if you attacked this turn, add {R}{W}{B}.") + .setAbilityWord(AbilityWord.RAID) + .addHint(RaidHint.instance), new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/m/MarduWoeReaper.java b/Mage.Sets/src/mage/cards/m/MarduWoeReaper.java index c87fd0e508..4a9e2061cc 100644 --- a/Mage.Sets/src/mage/cards/m/MarduWoeReaper.java +++ b/Mage.Sets/src/mage/cards/m/MarduWoeReaper.java @@ -1,45 +1,46 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInGraveyard; +import java.util.UUID; + /** - * * @author emerald000 */ public final class MarduWoeReaper extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent(SubType.WARRIOR, "Warrior"); + public MarduWoeReaper(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WARRIOR); this.power = new MageInt(2); this.toughness = new MageInt(1); // Whenever Mardu Woe-Reaper or another Warrior enters the battlefield under your control, you may exile target creature card from a graveyard. If you do, you gain 1 life. - Ability ability = new MarduWoeReaperTriggeredAbility(); - ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard"))); + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new ExileTargetEffect("exile target creature card from a graveyard.") + , filter, true, true + ); + ability.addEffect(new GainLifeEffect(1).concatBy("If you do,")); + ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE)); this.addAbility(ability); } - public MarduWoeReaper(final MarduWoeReaper card) { + private MarduWoeReaper(final MarduWoeReaper card) { super(card); } @@ -48,70 +49,3 @@ public final class MarduWoeReaper extends CardImpl { return new MarduWoeReaper(this); } } - -class MarduWoeReaperTriggeredAbility extends TriggeredAbilityImpl { - - MarduWoeReaperTriggeredAbility() { - super(Zone.BATTLEFIELD, new MarduWoeReaperEffect(), true); - } - - MarduWoeReaperTriggeredAbility(final MarduWoeReaperTriggeredAbility ability) { - super(ability); - } - - @Override - public MarduWoeReaperTriggeredAbility copy() { - return new MarduWoeReaperTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(this.getControllerId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && (permanent.getId().equals(this.getSourceId()) || permanent.hasSubtype(SubType.WARRIOR, game))) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} or another Warrior enters the battlefield under your control, " + super.getRule(); - } -} - -class MarduWoeReaperEffect extends OneShotEffect { - - MarduWoeReaperEffect() { - super(Outcome.GainLife); - this.staticText = "you may exile target creature card from a graveyard. If you do, you gain 1 life"; - } - - MarduWoeReaperEffect(final MarduWoeReaperEffect effect) { - super(effect); - } - - @Override - public MarduWoeReaperEffect copy() { - return new MarduWoeReaperEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); - if (player != null && card != null) { - if (player.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true)) { - player.gainLife(1, game, source); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java b/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java index 4588829ab9..5f2890ac7d 100644 --- a/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java +++ b/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java @@ -1,7 +1,7 @@ package mage.cards.m; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.common.SacrificeSourceCost; @@ -27,7 +27,7 @@ import java.util.UUID; public final class MaritLagesSlumber extends CardImpl { private static final FilterPermanent filter - = new FilterControlledPermanent("{this} or another snow permanent"); + = new FilterControlledPermanent("snow permanent"); static { filter.add(SuperType.SNOW.getPredicate()); @@ -43,7 +43,9 @@ public final class MaritLagesSlumber extends CardImpl { this.addSuperType(SuperType.SNOW); // Whenever Marit Lage's Slumber or another snow permanent enters the battlefield under your control, scry 1. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new ScryEffect(1), filter)); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new ScryEffect(1), filter, false, true + )); // At the beginning of your upkeep, if you control ten or more snow permanents, sacrifice Marit Lage's Slumber. If you do, create Marit Lage, a legendary 20/20 black Avatar creature token with flying and indestructible. this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/m/MarkerBeetles.java b/Mage.Sets/src/mage/cards/m/MarkerBeetles.java index 4e4f6b2af8..14c4c85748 100644 --- a/Mage.Sets/src/mage/cards/m/MarkerBeetles.java +++ b/Mage.Sets/src/mage/cards/m/MarkerBeetles.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -31,7 +31,7 @@ public final class MarkerBeetles extends CardImpl { this.toughness = new MageInt(3); // When Marker Beetles dies, target creature gets +1/+1 until end of turn. - Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(1, 1, Duration.EndOfTurn), false); + Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(1, 1, Duration.EndOfTurn), false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // {2}, Sacrifice Marker Beetles: Draw a card. diff --git a/Mage.Sets/src/mage/cards/m/MarrowGnawer.java b/Mage.Sets/src/mage/cards/m/MarrowGnawer.java index cdf48890ef..487d5f3e4c 100644 --- a/Mage.Sets/src/mage/cards/m/MarrowGnawer.java +++ b/Mage.Sets/src/mage/cards/m/MarrowGnawer.java @@ -2,7 +2,6 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -21,6 +20,8 @@ import mage.filter.common.FilterCreaturePermanent; import mage.game.permanent.token.RatToken; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** * * @author LevelX @@ -50,9 +51,10 @@ public final class MarrowGnawer extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(FearAbility.getInstance(), Duration.WhileOnBattlefield, filterFear))); // {T}, Sacrifice a Rat: create X 1/1 black Rat creature tokens, where X is the number of Rats you control. - Ability ability; - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new RatToken(),new PermanentsOnBattlefieldCount(filter3)), new SacrificeTargetCost(new TargetControlledPermanent(filterSacrifice))); - ability.addCost(new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new CreateTokenEffect(new RatToken(), new PermanentsOnBattlefieldCount(filter3, null)), + new TapSourceCost()); + ability.addCost( new SacrificeTargetCost(new TargetControlledPermanent(filterSacrifice))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MarshmistTitan.java b/Mage.Sets/src/mage/cards/m/MarshmistTitan.java index 71df001f00..8c380f606e 100644 --- a/Mage.Sets/src/mage/cards/m/MarshmistTitan.java +++ b/Mage.Sets/src/mage/cards/m/MarshmistTitan.java @@ -1,16 +1,15 @@ package mage.cards.m; import mage.MageInt; -import mage.Mana; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.DevotionCount; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; import java.util.UUID; @@ -26,9 +25,11 @@ public final class MarshmistTitan extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(5); - // Marshmist Titan costs {X} less to cast, where X is your devotion to black. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new MarshmistTitanCostReductionEffect()) - .addHint(DevotionCount.B.getHint())); + // This spell costs {X} less to cast, where X is your devotion to black. + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(DevotionCount.B)); + ability.addHint(DevotionCount.B.getHint()); + this.addAbility(ability); + } private MarshmistTitan(final MarshmistTitan card) { @@ -40,40 +41,3 @@ public final class MarshmistTitan extends CardImpl { return new MarshmistTitan(this); } } - -class MarshmistTitanCostReductionEffect extends CostModificationEffectImpl { - - MarshmistTitanCostReductionEffect() { - super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "This spell costs {X} less to cast, where X is your devotion to black. " + - "(Each {B} in the mana costs of permanents you control counts toward your devotion to black.) "; - } - - private MarshmistTitanCostReductionEffect(final MarshmistTitanCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - Mana mana = spellAbility.getManaCostsToPay().getMana(); - if (mana.getGeneric() == 0) { - return false; - } - int count = DevotionCount.B.calculate(game, source, this); - mana.setGeneric(Math.max(mana.getGeneric() - count, 0)); - spellAbility.getManaCostsToPay().load(mana.toString()); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify instanceof SpellAbility - && abilityToModify.getSourceId().equals(source.getSourceId()); - } - - @Override - public MarshmistTitanCostReductionEffect copy() { - return new MarshmistTitanCostReductionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/m/MartialImpetus.java b/Mage.Sets/src/mage/cards/m/MartialImpetus.java new file mode 100644 index 0000000000..fb3359b5be --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MartialImpetus.java @@ -0,0 +1,77 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksAttachedTriggeredAbility; +import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MartialImpetus extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(MartialImpetusPredicate.instance); + } + + public MartialImpetus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +1/+1 and is goaded. + this.addAbility(new GoadAttachedAbility(new BoostEnchantedEffect(1, 1))); + + // Whenever enchanted creature attacks, each other creature that's attacking one of your opponents gets +1/+1 until end of turn. + this.addAbility(new AttacksAttachedTriggeredAbility( + new BoostAllEffect(1, 1, Duration.EndOfTurn, filter, true).setText( + "each other creature that's attacking one of your opponents gets +1/+1 until end of turn" + ), AttachmentType.AURA, false) + ); + } + + private MartialImpetus(final MartialImpetus card) { + super(card); + } + + @Override + public MartialImpetus copy() { + return new MartialImpetus(this); + } +} + +enum MartialImpetusPredicate implements ObjectSourcePlayerPredicate> { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return input.getObject() != null && input.getObject().isAttacking() && + game.getCombat() + .getDefendingPlayerId(input.getObject().getId(), game) + .equals(game.getControllerId(input.getSourceId())); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MartyrForTheCause.java b/Mage.Sets/src/mage/cards/m/MartyrForTheCause.java index 19a17d1ec8..0cf33c2cd7 100644 --- a/Mage.Sets/src/mage/cards/m/MartyrForTheCause.java +++ b/Mage.Sets/src/mage/cards/m/MartyrForTheCause.java @@ -1,7 +1,7 @@ package mage.cards.m; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.ProliferateEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class MartyrForTheCause extends CardImpl { this.toughness = new MageInt(2); // When Martyr for the Cause dies, proliferate. (Choose any number of permanents and/or players, then give each another counter of each kind already there.) - this.addAbility(new DiesTriggeredAbility(new ProliferateEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new ProliferateEffect())); } private MartyrForTheCause(final MartyrForTheCause card) { diff --git a/Mage.Sets/src/mage/cards/m/MartyrOfBones.java b/Mage.Sets/src/mage/cards/m/MartyrOfBones.java index a90e986f1b..dd47c908df 100644 --- a/Mage.Sets/src/mage/cards/m/MartyrOfBones.java +++ b/Mage.Sets/src/mage/cards/m/MartyrOfBones.java @@ -1,4 +1,3 @@ - package mage.cards.m; import mage.MageInt; @@ -11,7 +10,6 @@ import mage.abilities.costs.VariableCostImpl; import mage.abilities.costs.common.RevealTargetFromHandCost; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -19,6 +17,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.players.Player; @@ -41,13 +40,12 @@ public final class MartyrOfBones extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - //TODO: Make ability properly copiable + // TODO: Make ability properly copiable // {1}, Reveal X black cards from your hand, Sacrifice Martyr of Bones: Exile up to X target cards from a single graveyard. - Effect effect = new ExileTargetEffect(null, null, Zone.GRAVEYARD); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new GenericManaCost(1)); ability.addCost(new RevealVariableBlackCardsFromHandCost()); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetCardInASingleGraveyard(0, 1, new FilterCard("cards in a single graveyard"))); + ability.addTarget(new TargetCardInASingleGraveyard(0, 1, StaticFilters.FILTER_CARD_CARDS)); ability.setTargetAdjuster(MartyrOfBonesAdjuster.instance); this.addAbility(ability); } @@ -74,7 +72,7 @@ enum MartyrOfBonesAdjuster implements TargetAdjuster { } } ability.getTargets().clear(); - ability.addTarget(new TargetCardInASingleGraveyard(0, amount, new FilterCard())); + ability.addTarget(new TargetCardInASingleGraveyard(0, amount, StaticFilters.FILTER_CARD_CARDS)); } } diff --git a/Mage.Sets/src/mage/cards/m/MartyrOfDusk.java b/Mage.Sets/src/mage/cards/m/MartyrOfDusk.java index 5e3591429d..fcbdf48f5d 100644 --- a/Mage.Sets/src/mage/cards/m/MartyrOfDusk.java +++ b/Mage.Sets/src/mage/cards/m/MartyrOfDusk.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class MartyrOfDusk extends CardImpl { this.toughness = new MageInt(1); // When Martyr of Dusk dies, create a 1/1 white Vampire creature token with lifelink. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new IxalanVampireToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new IxalanVampireToken()))); } public MartyrOfDusk(final MartyrOfDusk card) { diff --git a/Mage.Sets/src/mage/cards/m/Martyrdom.java b/Mage.Sets/src/mage/cards/m/Martyrdom.java index 067ad07278..a693c43277 100644 --- a/Mage.Sets/src/mage/cards/m/Martyrdom.java +++ b/Mage.Sets/src/mage/cards/m/Martyrdom.java @@ -66,7 +66,7 @@ class MartyrdomGainAbilityTargetEffect extends ContinuousEffectImpl { if (permanent != null) { ActivatedAbilityImpl ability = new MartyrdomActivatedAbility(source.getControllerId()); ability.setMayActivate(TargetController.ANY); - permanent.addAbility(ability, source.getSourceId(), game, false); + permanent.addAbility(ability, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/m/MartyrsBond.java b/Mage.Sets/src/mage/cards/m/MartyrsBond.java index 5461588c7b..31d5a450f5 100644 --- a/Mage.Sets/src/mage/cards/m/MartyrsBond.java +++ b/Mage.Sets/src/mage/cards/m/MartyrsBond.java @@ -75,7 +75,7 @@ class MartyrsBondTriggeredAbility extends TriggeredAbilityImpl { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (permanent.isControlledBy(this.getControllerId()) && !permanent.isLand()) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); } return true; } @@ -110,7 +110,7 @@ class MartyrsBondEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { List perms = new ArrayList<>(); if (source != null) { - Permanent saccedPermanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent saccedPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && saccedPermanent != null) { FilterControlledPermanent filter = new FilterControlledPermanent(); diff --git a/Mage.Sets/src/mage/cards/m/MartyrsCry.java b/Mage.Sets/src/mage/cards/m/MartyrsCry.java index d7f765b357..01b553475c 100644 --- a/Mage.Sets/src/mage/cards/m/MartyrsCry.java +++ b/Mage.Sets/src/mage/cards/m/MartyrsCry.java @@ -67,7 +67,7 @@ class MartyrsCryEffect extends OneShotEffect { for (UUID playerId : game.getPlayerList().toList()) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(playerCrtCount.getOrDefault(playerId, 0), game); + player.drawCards(playerCrtCount.getOrDefault(playerId, 0), source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/m/MaskOfImmolation.java b/Mage.Sets/src/mage/cards/m/MaskOfImmolation.java index 97688431cd..60cb9da3b8 100644 --- a/Mage.Sets/src/mage/cards/m/MaskOfImmolation.java +++ b/Mage.Sets/src/mage/cards/m/MaskOfImmolation.java @@ -16,7 +16,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.YoungPyromancerElementalToken; +import mage.game.permanent.token.RedElementalToken; import mage.players.Player; import mage.target.common.TargetAnyTarget; @@ -59,7 +59,7 @@ public final class MaskOfImmolation extends CardImpl { class MaskOfImmolationEffect extends CreateTokenEffect { MaskOfImmolationEffect() { - super(new YoungPyromancerElementalToken()); + super(new RedElementalToken()); staticText = "create a 1/1 red Elemental creature token, then attach {this} to it."; } diff --git a/Mage.Sets/src/mage/cards/m/MaskedBlackguard.java b/Mage.Sets/src/mage/cards/m/MaskedBlackguard.java new file mode 100644 index 0000000000..345956e6ce --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MaskedBlackguard.java @@ -0,0 +1,46 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MaskedBlackguard extends CardImpl { + + public MaskedBlackguard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // {2}{B}: Masked Blackguard gets +1/+1 until end of turn. + this.addAbility(new SimpleActivatedAbility( + new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{2}{B}") + )); + } + + private MaskedBlackguard(final MaskedBlackguard card) { + super(card); + } + + @Override + public MaskedBlackguard copy() { + return new MaskedBlackguard(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MasterThief.java b/Mage.Sets/src/mage/cards/m/MasterThief.java index f712566544..c71f95d1a2 100644 --- a/Mage.Sets/src/mage/cards/m/MasterThief.java +++ b/Mage.Sets/src/mage/cards/m/MasterThief.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -11,9 +10,10 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.target.common.TargetArtifactPermanent; +import mage.watchers.common.LostControlWatcher; /** * @author Loki, JayDi85 @@ -33,9 +33,10 @@ public final class MasterThief extends CardImpl { new GainControlTargetEffect(Duration.Custom), new SourceOnBattlefieldControlUnchangedCondition(), "gain control of target artifact for as long as you control {this}"), - false); + false); ability.addTarget(new TargetArtifactPermanent()); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index be36268265..a436ebd0b8 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -168,7 +168,7 @@ class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectI if (target.getTargets().contains(permanent.getId())) { RequirementEffect effect = new AttacksIfAbleTargetEffect(Duration.EndOfCombat); effect.setText(""); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); game.informPlayers(controller.getLogName() + " has decided that " + permanent.getLogName() + " attacks this combat if able"); @@ -184,7 +184,7 @@ class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectI if (!hasToAttack) { RestrictionEffect effect = new CantAttackTargetEffect(Duration.EndOfCombat); effect.setText(""); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java b/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java index 97ef3aca9a..e8c020953c 100644 --- a/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java +++ b/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardAllEffect; @@ -54,7 +54,7 @@ public final class MathasFiendSeeker extends CardImpl { // At the beginning of your end step, put a bounty counter on target creature an opponent controls. For as long as that creature has a bounty counter on it, it has "When this creature dies, each opponent draws a card and gains 2 life." Ability ability = new BeginningOfYourEndStepTriggeredAbility(new AddCountersTargetEffect(CounterType.BOUNTY.createInstance()), false); ability.addTarget(new TargetCreaturePermanent(filter)); - Ability ability2 = new DiesTriggeredAbility(new DrawCardAllEffect(1, TargetController.OPPONENT)); + Ability ability2 = new DiesSourceTriggeredAbility(new DrawCardAllEffect(1, TargetController.OPPONENT)); ability2.addEffect(new OpponentsGainLifeEffect()); Effect effect = new MathasFiendSeekerGainAbilityEffect(ability2, Duration.Custom, rule); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/m/MatterReshaper.java b/Mage.Sets/src/mage/cards/m/MatterReshaper.java index 164315f45e..5d4455be7c 100644 --- a/Mage.Sets/src/mage/cards/m/MatterReshaper.java +++ b/Mage.Sets/src/mage/cards/m/MatterReshaper.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -34,7 +34,7 @@ public final class MatterReshaper extends CardImpl { // When Matter Reshaper dies, reveal the top card of your library. You may put that card onto the battlefield // if it's a permanent card with converted mana cost 3 or less. Otherwise, put that card into your hand. - this.addAbility(new DiesTriggeredAbility(new MatterReshaperEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new MatterReshaperEffect(), false)); } public MatterReshaper(final MatterReshaper card) { diff --git a/Mage.Sets/src/mage/cards/m/MausoleumGuard.java b/Mage.Sets/src/mage/cards/m/MausoleumGuard.java index e25cd9df41..0d64b008ce 100644 --- a/Mage.Sets/src/mage/cards/m/MausoleumGuard.java +++ b/Mage.Sets/src/mage/cards/m/MausoleumGuard.java @@ -1,9 +1,7 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -11,13 +9,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** * @author nantuko */ public final class MausoleumGuard extends CardImpl { public MausoleumGuard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SCOUT); @@ -25,7 +25,7 @@ public final class MausoleumGuard extends CardImpl { this.toughness = new MageInt(2); // When Mausoleum Guard dies, create two 1/1 white Spirit creature tokens with flying. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken("ISD"), 2))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken(), 2))); } public MausoleumGuard(final MausoleumGuard card) { diff --git a/Mage.Sets/src/mage/cards/m/MayaelsAria.java b/Mage.Sets/src/mage/cards/m/MayaelsAria.java index 027b039b67..2344735fc1 100644 --- a/Mage.Sets/src/mage/cards/m/MayaelsAria.java +++ b/Mage.Sets/src/mage/cards/m/MayaelsAria.java @@ -75,7 +75,7 @@ class MayaelsAriaEffect extends OneShotEffect { creature.addCounters(CounterType.P1P1.createInstance(), source, game); } } - game.applyEffects(); // needed because otehrwise the +1/+1 counters wouldn't be taken into account + game.getState().processAction(game); // needed because otehrwise the +1/+1 counters wouldn't be taken into account // Then you gain 10 life if you control a creature with power 10 or greater. filter = new FilterCreaturePermanent(); diff --git a/Mage.Sets/src/mage/cards/m/MazeOfIth.java b/Mage.Sets/src/mage/cards/m/MazeOfIth.java index eed1ac36a6..fac15b9e87 100644 --- a/Mage.Sets/src/mage/cards/m/MazeOfIth.java +++ b/Mage.Sets/src/mage/cards/m/MazeOfIth.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -13,6 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.Zone; import mage.target.common.TargetAttackingCreature; @@ -21,26 +21,29 @@ import mage.target.common.TargetAttackingCreature; * @author Plopman */ public final class MazeOfIth extends CardImpl { - + public MazeOfIth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - // {tap}: Untap target attacking creature. Prevent all combat damage that would be dealt to and dealt by that creature this turn. + // {T}: Untap target attacking creature. Prevent all combat damage that + // would be dealt to and dealt by that creature this turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapTargetEffect(), new TapSourceCost()); Effect effect = new PreventDamageByTargetEffect(Duration.EndOfTurn, true); effect.setText("Prevent all combat damage that would be dealt to"); + effect.setOutcome(Outcome.Detriment); ability.addEffect(effect); effect = new PreventDamageToTargetEffect(Duration.EndOfTurn, Integer.MAX_VALUE, true); effect.setText("and dealt by that creature this turn"); + effect.setOutcome(Outcome.Detriment); ability.addEffect(effect); ability.addTarget(new TargetAttackingCreature()); this.addAbility(ability); } - + public MazeOfIth(final MazeOfIth card) { super(card); } - + @Override public MazeOfIth copy() { return new MazeOfIth(this); diff --git a/Mage.Sets/src/mage/cards/m/MazemindTome.java b/Mage.Sets/src/mage/cards/m/MazemindTome.java new file mode 100644 index 0000000000..282b220a4e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MazemindTome.java @@ -0,0 +1,86 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.StateTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileSourceCost; +import mage.abilities.costs.common.PutCountersSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MazemindTome extends CardImpl { + + public MazemindTome(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + // {T}, Put a page counter on Mazemind Tome: Scry 1. + Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new TapSourceCost()); + ability.addCost(new PutCountersSourceCost(CounterType.PAGE.createInstance())); + this.addAbility(ability); + + // {2}, {T}, Put a page counter on Mazemind Tome: Draw a card. + ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + ability.addCost(new PutCountersSourceCost(CounterType.PAGE.createInstance())); + this.addAbility(ability); + + // When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life. + this.addAbility(new MazemindTomeTriggeredAbility()); + } + + private MazemindTome(final MazemindTome card) { + super(card); + } + + @Override + public MazemindTome copy() { + return new MazemindTome(this); + } +} + +class MazemindTomeTriggeredAbility extends StateTriggeredAbility { + + MazemindTomeTriggeredAbility() { + super(Zone.BATTLEFIELD, new DoIfCostPaid( + new GainLifeEffect(4), new ExileSourceCost(), null, false + )); + } + + private MazemindTomeTriggeredAbility(final MazemindTomeTriggeredAbility ability) { + super(ability); + } + + @Override + public MazemindTomeTriggeredAbility copy() { + return new MazemindTomeTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(getSourceId()); + return permanent != null && permanent.getCounters(game).getCount(CounterType.PAGE) >= 4; + } + + @Override + public String getRule() { + return "When there are four or more page counters on {this}, exile it. If you do, you gain 4 life."; + } + +} diff --git a/Mage.Sets/src/mage/cards/m/MeddlingMage.java b/Mage.Sets/src/mage/cards/m/MeddlingMage.java index c2afa5dce5..cd912639bc 100644 --- a/Mage.Sets/src/mage/cards/m/MeddlingMage.java +++ b/Mage.Sets/src/mage/cards/m/MeddlingMage.java @@ -1,6 +1,5 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -10,17 +9,15 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.ChooseACardNameEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author Plopman */ public final class MeddlingMage extends CardImpl { @@ -33,10 +30,10 @@ public final class MeddlingMage extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // As Meddling Mage enters the battlefield, name a nonland card. + // As Meddling Mage enters the battlefield, choose a nonland card name. this.addAbility(new AsEntersBattlefieldAbility(new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME))); - //The named card can't be cast. + // Spells with the chosen name can't be cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MeddlingMageReplacementEffect())); } @@ -75,21 +72,22 @@ class MeddlingMageReplacementEffect extends ContinuousRuleModifyingEffectImpl { public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source.getSourceId()); if (mageObject != null) { - return "You can't cast a spell with that name (" + mageObject.getLogName() + " in play)."; + return "You can't cast a spell with that name (" + mageObject.getName() + " in play)."; } return null; } @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.CAST_SPELL; + return event.getType() == EventType.CAST_SPELL_LATE; } @Override public boolean applies(GameEvent event, Ability source, Game game) { MageObject object = game.getObject(event.getSourceId()); - // fixes issue #1072 - return object != null && !object.isCopy() && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY)); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return object != null + && !object.isCopy() + && CardUtil.haveSameNames(object, cardName, game); } - } diff --git a/Mage.Sets/src/mage/cards/m/MelekIzzetParagon.java b/Mage.Sets/src/mage/cards/m/MelekIzzetParagon.java index d6e8b40bf0..bf9dff2fd8 100644 --- a/Mage.Sets/src/mage/cards/m/MelekIzzetParagon.java +++ b/Mage.Sets/src/mage/cards/m/MelekIzzetParagon.java @@ -1,4 +1,3 @@ - package mage.cards.m; import mage.MageInt; @@ -25,21 +24,21 @@ import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * * @author jeffwadsworth */ public final class MelekIzzetParagon extends CardImpl { - private static final FilterCard filter = new FilterCard("instant or sorcery card"); + private static final FilterCard filter = new FilterCard("cast instant or sorcery spells"); static { filter.add(Predicates.or( CardType.INSTANT.getPredicate(), - CardType.SORCERY.getPredicate())); + CardType.SORCERY.getPredicate() + )); } public MelekIzzetParagon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{R}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.WEIRD); this.subtype.add(SubType.WIZARD); diff --git a/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java b/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java index 3c6124c946..7565cfdca9 100644 --- a/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java +++ b/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java @@ -149,7 +149,7 @@ class MeliraSylvokOutcastEffect3 extends ContinuousEffectImpl { Set opponents = game.getOpponents(source.getControllerId()); for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { if (opponents.contains(perm.getControllerId())) { - perm.getAbilities().remove(InfectAbility.getInstance()); + perm.removeAbility(InfectAbility.getInstance(), source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/m/Memnarch.java b/Mage.Sets/src/mage/cards/m/Memnarch.java index 75129bda42..09a585fdfe 100644 --- a/Mage.Sets/src/mage/cards/m/Memnarch.java +++ b/Mage.Sets/src/mage/cards/m/Memnarch.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -12,8 +11,8 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; import mage.target.TargetPermanent; @@ -26,7 +25,7 @@ import mage.target.common.TargetArtifactPermanent; public final class Memnarch extends CardImpl { public Memnarch(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{7}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{7}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.WIZARD); @@ -34,7 +33,7 @@ public final class Memnarch extends CardImpl { this.toughness = new MageInt(5); // {1}{U}{U}: Target permanent becomes an artifact in addition to its other types. - Effect effect = new AddCardTypeTargetEffect(Duration.WhileOnBattlefield, CardType.ARTIFACT); + Effect effect = new AddCardTypeTargetEffect(Duration.Custom, CardType.ARTIFACT); effect.setText("Target permanent becomes an artifact in addition to its other types"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{1}{U}{U}")); ability.addTarget(new TargetPermanent()); @@ -46,8 +45,12 @@ public final class Memnarch extends CardImpl { this.addAbility(ability2); } - public Memnarch(final Memnarch card){ super(card); } + public Memnarch(final Memnarch card) { + super(card); + } @Override - public Memnarch copy() { return new Memnarch(this); } + public Memnarch copy() { + return new Memnarch(this); + } } diff --git a/Mage.Sets/src/mage/cards/m/MemoryErosion.java b/Mage.Sets/src/mage/cards/m/MemoryErosion.java index dc6bfd720c..5aa6273b58 100644 --- a/Mage.Sets/src/mage/cards/m/MemoryErosion.java +++ b/Mage.Sets/src/mage/cards/m/MemoryErosion.java @@ -68,7 +68,7 @@ class SpellCastTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever an opponent casts a spell, that player puts the top two cards of their library into their graveyard"; + return "Whenever an opponent casts a spell, that player mills two cards"; } @Override diff --git a/Mage.Sets/src/mage/cards/m/MemoryJar.java b/Mage.Sets/src/mage/cards/m/MemoryJar.java index 8b1c6c052a..d1709a4b19 100644 --- a/Mage.Sets/src/mage/cards/m/MemoryJar.java +++ b/Mage.Sets/src/mage/cards/m/MemoryJar.java @@ -79,7 +79,7 @@ class MemoryJarEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(7, game); + player.drawCards(7, source.getSourceId(), game); } } //Delayed ability diff --git a/Mage.Sets/src/mage/cards/m/MemoryLeak.java b/Mage.Sets/src/mage/cards/m/MemoryLeak.java new file mode 100644 index 0000000000..39e326ead0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MemoryLeak.java @@ -0,0 +1,96 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterNonlandCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MemoryLeak extends CardImpl { + + public MemoryLeak(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); + + // Target opponent reveals their hand. You choose a nonland card from that player's graveyard or hand and exile it. + this.getSpellAbility().addEffect(new MemoryLeakEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + + // Cycling {1} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}"))); + } + + private MemoryLeak(final MemoryLeak card) { + super(card); + } + + @Override + public MemoryLeak copy() { + return new MemoryLeak(this); + } +} + +class MemoryLeakEffect extends OneShotEffect { + + MemoryLeakEffect() { + super(Outcome.Benefit); + this.staticText = "Target opponent reveals their hand. " + + "You choose a nonland card from that player's graveyard " + + "or hand and exile it."; + } + + private MemoryLeakEffect(final MemoryLeakEffect effect) { + super(effect); + } + + @Override + public MemoryLeakEffect copy() { + return new MemoryLeakEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller == null || opponent == null) { + return false; + } + opponent.revealCards(source, opponent.getHand(), game); + TargetCard target; + Cards cards; + if (controller.chooseUse(outcome, "Exile a card from hand or graveyard?", null, "Hand", "Graveyard", source, game)) { + FilterCard filter = new FilterNonlandCard("nonland card in " + opponent.getName() + "'s hand"); + target = new TargetCard(Zone.HAND, filter); + target.setNotTarget(true); + cards = opponent.getHand(); + } else { + FilterCard filter = new FilterNonlandCard("nonland card in " + opponent.getName() + "'s graveyard"); + target = new TargetCard(Zone.GRAVEYARD, filter); + target.setNotTarget(true); + cards = opponent.getGraveyard(); + } + if (controller.choose(outcome, cards, target, game)) { + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.EXILED, source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MemorysJourney.java b/Mage.Sets/src/mage/cards/m/MemorysJourney.java index a54963a803..4893700f1b 100644 --- a/Mage.Sets/src/mage/cards/m/MemorysJourney.java +++ b/Mage.Sets/src/mage/cards/m/MemorysJourney.java @@ -10,6 +10,7 @@ import mage.abilities.keyword.FlashbackAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TimingRule; @@ -65,24 +66,9 @@ class MemorysJourneyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - List targets = source.getTargets().get(1).getTargets(); - boolean shuffle = false; - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - if (player.getGraveyard().contains(card.getId())) { - player.getGraveyard().remove(card); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - shuffle = true; - } - } - } - if (shuffle) { - player.shuffleLibrary(source, game); - } - return true; + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (targetPlayer != null) { + return targetPlayer.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/m/MephiticOoze.java b/Mage.Sets/src/mage/cards/m/MephiticOoze.java index a89562f39e..f90dbcc6d7 100644 --- a/Mage.Sets/src/mage/cards/m/MephiticOoze.java +++ b/Mage.Sets/src/mage/cards/m/MephiticOoze.java @@ -1,45 +1,37 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class MephiticOoze extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifact you control"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } - public MephiticOoze(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); this.subtype.add(SubType.OOZE); this.power = new MageInt(0); this.toughness = new MageInt(5); // Mephitic Ooze gets +1/+0 for each artifact you control. - Effect effect = new BoostSourceEffect(new PermanentsOnBattlefieldCount(filter), StaticValue.get(0), Duration.WhileOnBattlefield); - effect.setText("{this} gets +1/+0 for each artifact you control"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + Effect effect = new BoostSourceEffect(ArtifactYouControlCount.instance, StaticValue.get(0), Duration.WhileOnBattlefield); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect).addHint(ArtifactYouControlHint.instance)); // Whenever Mephitic Ooze deals combat damage to a creature, destroy that creature. The creature can't be regenerated. this.addAbility(new DealsCombatDamageToACreatureTriggeredAbility(new DestroyTargetEffect(true), false, true)); diff --git a/Mage.Sets/src/mage/cards/m/MercadianLift.java b/Mage.Sets/src/mage/cards/m/MercadianLift.java index 486b01d41f..27aae04182 100644 --- a/Mage.Sets/src/mage/cards/m/MercadianLift.java +++ b/Mage.Sets/src/mage/cards/m/MercadianLift.java @@ -1,101 +1,101 @@ -package mage.cards.m; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.RemoveVariableCountersSourceCost; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.FilterCard; -import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetCardInHand; - -/** - * - * @author jeffwadsworth - */ -public final class MercadianLift extends CardImpl { - - public MercadianLift(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); - - // {1}, {tap}: Put a winch counter on Mercadian Lift. - Ability ability = new SimpleActivatedAbility(new AddCountersSourceEffect(CounterType.WINCH.createInstance()), new ManaCostsImpl("{1}")); - ability.addCost(new TapSourceCost()); - this.addAbility(ability); - - // {tap}, Remove X winch counters from Mercadian Lift: You may put a creature card with converted mana cost X from your hand onto the battlefield. - Ability ability2 = new SimpleActivatedAbility(new MercadianLiftEffect(), new TapSourceCost()); - ability2.addCost(new RemoveVariableCountersSourceCost(CounterType.WINCH.createInstance(1))); - this.addAbility(ability2); - - } - - private MercadianLift(final MercadianLift card) { - super(card); - } - - @Override - public MercadianLift copy() { - return new MercadianLift(this); - } -} - -class MercadianLiftEffect extends OneShotEffect { - - public MercadianLiftEffect() { - super(Outcome.PutCardInPlay); - staticText = "You may put a creature card with converted mana cost X from your hand onto the battlefield"; - } - - public MercadianLiftEffect(final MercadianLiftEffect effect) { - super(effect); - } - - @Override - public MercadianLiftEffect copy() { - return new MercadianLiftEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int numberOfCounters = 0; - for (Cost cost : source.getCosts()) { - if (cost instanceof RemoveVariableCountersSourceCost) { - numberOfCounters = ((RemoveVariableCountersSourceCost) cost).getAmount(); - } - } - System.out.println("The number is " + numberOfCounters); - FilterCreatureCard filter = new FilterCreatureCard(); - filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, numberOfCounters)); - filter.setMessage("creature card with converted mana cost " + numberOfCounters); - TargetCardInHand target = new TargetCardInHand(filter); - if (target.canChoose(controller.getId(), game) - && controller.chooseUse(Outcome.PutCardInPlay, "Put " + filter.getMessage() + " from your hand onto the battlefield?", source, game) - && controller.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) { - target.setRequired(false); - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - return controller.moveCards(card, Zone.BATTLEFIELD, source, game); - } - } - } - return false; - } -} +package mage.cards.m; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.RemoveVariableCountersSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +/** + * + * @author jeffwadsworth + */ +public final class MercadianLift extends CardImpl { + + public MercadianLift(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + // {1}, {tap}: Put a winch counter on Mercadian Lift. + Ability ability = new SimpleActivatedAbility(new AddCountersSourceEffect(CounterType.WINCH.createInstance()), new ManaCostsImpl("{1}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // {tap}, Remove X winch counters from Mercadian Lift: You may put a creature card with converted mana cost X from your hand onto the battlefield. + Ability ability2 = new SimpleActivatedAbility(new MercadianLiftEffect(), new TapSourceCost()); + ability2.addCost(new RemoveVariableCountersSourceCost(CounterType.WINCH.createInstance(1))); + this.addAbility(ability2); + + } + + private MercadianLift(final MercadianLift card) { + super(card); + } + + @Override + public MercadianLift copy() { + return new MercadianLift(this); + } +} + +class MercadianLiftEffect extends OneShotEffect { + + public MercadianLiftEffect() { + super(Outcome.PutCardInPlay); + staticText = "You may put a creature card with converted mana cost X from your hand onto the battlefield"; + } + + public MercadianLiftEffect(final MercadianLiftEffect effect) { + super(effect); + } + + @Override + public MercadianLiftEffect copy() { + return new MercadianLiftEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int numberOfCounters = 0; + for (Cost cost : source.getCosts()) { + if (cost instanceof RemoveVariableCountersSourceCost) { + numberOfCounters = ((RemoveVariableCountersSourceCost) cost).getAmount(); + } + } + System.out.println("The number is " + numberOfCounters); + FilterCreatureCard filter = new FilterCreatureCard(); + filter.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, numberOfCounters)); + filter.setMessage("creature card with converted mana cost " + numberOfCounters); + TargetCardInHand target = new TargetCardInHand(filter); + if (target.canChoose(controller.getId(), game) + && controller.chooseUse(Outcome.PutCardInPlay, "Put " + filter.getMessage() + " from your hand onto the battlefield?", source, game) + && controller.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) { + target.setRequired(false); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + return controller.moveCards(card, Zone.BATTLEFIELD, source, game); + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MercurialPretender.java b/Mage.Sets/src/mage/cards/m/MercurialPretender.java index 250feb98cb..36145c0c65 100644 --- a/Mage.Sets/src/mage/cards/m/MercurialPretender.java +++ b/Mage.Sets/src/mage/cards/m/MercurialPretender.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -17,16 +15,17 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.util.functions.AbilityApplier; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MercurialPretender extends CardImpl { - private static final String effectText = "as a copy of any creature you control, except it has \"{2}{U}{U}: Return this creature to its owner's hand.\""; + private static final String effectText = "as a copy of a creature you control, except it has \"{2}{U}{U}: Return this creature to its owner's hand.\""; public MercurialPretender(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); this.subtype.add(SubType.SHAPESHIFTER); this.color.setBlue(true); @@ -41,7 +40,7 @@ public final class MercurialPretender extends CardImpl { this.addAbility(new EntersBattlefieldAbility(effect, true)); } - public MercurialPretender(final MercurialPretender card) { + private MercurialPretender(final MercurialPretender card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/m/MercyKilling.java b/Mage.Sets/src/mage/cards/m/MercyKilling.java index af41b4f1ff..57d825141d 100644 --- a/Mage.Sets/src/mage/cards/m/MercyKilling.java +++ b/Mage.Sets/src/mage/cards/m/MercyKilling.java @@ -56,7 +56,7 @@ class MercyKillingTokenEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { int power = permanent.getPower().getValue(); return new MercyKillingToken().putOntoBattlefield(power, game, source.getSourceId(), permanent.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/m/MerfolkSeer.java b/Mage.Sets/src/mage/cards/m/MerfolkSeer.java index f7eef20f97..66de865d95 100644 --- a/Mage.Sets/src/mage/cards/m/MerfolkSeer.java +++ b/Mage.Sets/src/mage/cards/m/MerfolkSeer.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -26,7 +26,7 @@ public final class MerfolkSeer extends CardImpl { this.toughness = new MageInt(2); // When Merfolk Seer dies, you may pay {1}{U}. If you do, draw a card. - this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}{U}")))); + this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}{U}")))); } public MerfolkSeer(final MerfolkSeer card) { diff --git a/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java b/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java index 1e2a0c4a5f..7e20fbf928 100644 --- a/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java +++ b/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -25,6 +24,7 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.LostControlWatcher; /** * @@ -50,6 +50,7 @@ public final class MeriekeRiBerit extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, MeriekeRiBeritGainControlEffect, new TapSourceCost()); ability.addTarget(new TargetPermanent(new FilterCreaturePermanent("target creature"))); ability.addEffect(new MeriekeRiBeritCreateDelayedTriggerEffect()); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MesmericOrb.java b/Mage.Sets/src/mage/cards/m/MesmericOrb.java index 6b8608259b..9dae5bf385 100644 --- a/Mage.Sets/src/mage/cards/m/MesmericOrb.java +++ b/Mage.Sets/src/mage/cards/m/MesmericOrb.java @@ -1,9 +1,7 @@ package mage.cards.m; -import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -15,22 +13,21 @@ import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MesmericOrb extends CardImpl { public MesmericOrb(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Whenever a permanent becomes untapped, that permanent's controller puts the top card of their library into their graveyard. - Effect effect = new PutTopCardOfLibraryIntoGraveTargetEffect(1); - effect.setText("that permanent's controller puts the top card of their library into their graveyard"); - this.addAbility(new BecomesUntappedPermanentTriggeredAbility(effect, false)); + this.addAbility(new MesmericOrbTriggeredAbility()); } - public MesmericOrb(final MesmericOrb card) { + private MesmericOrb(final MesmericOrb card) { super(card); } @@ -40,19 +37,19 @@ public final class MesmericOrb extends CardImpl { } } -class BecomesUntappedPermanentTriggeredAbility extends TriggeredAbilityImpl{ +class MesmericOrbTriggeredAbility extends TriggeredAbilityImpl { - public BecomesUntappedPermanentTriggeredAbility(Effect effect, boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); + MesmericOrbTriggeredAbility() { + super(Zone.BATTLEFIELD, new PutTopCardOfLibraryIntoGraveTargetEffect(1), false); } - public BecomesUntappedPermanentTriggeredAbility(final BecomesUntappedPermanentTriggeredAbility ability) { + private MesmericOrbTriggeredAbility(final MesmericOrbTriggeredAbility ability) { super(ability); } @Override - public BecomesUntappedPermanentTriggeredAbility copy() { - return new BecomesUntappedPermanentTriggeredAbility(this); + public MesmericOrbTriggeredAbility copy() { + return new MesmericOrbTriggeredAbility(this); } @Override @@ -72,7 +69,7 @@ class BecomesUntappedPermanentTriggeredAbility extends TriggeredAbilityImpl{ @Override public String getRule() { - return "Whenever a permanent becomes untapped, " + super.getRule(); + return "Whenever a permanent becomes untapped, that permanent's controller mills a card."; } } diff --git a/Mage.Sets/src/mage/cards/m/MessengerDrake.java b/Mage.Sets/src/mage/cards/m/MessengerDrake.java index 1c15bcf7b2..d2b1c1e81a 100644 --- a/Mage.Sets/src/mage/cards/m/MessengerDrake.java +++ b/Mage.Sets/src/mage/cards/m/MessengerDrake.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class MessengerDrake extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Messenger Drake dies, draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1))); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1))); } public MessengerDrake(final MessengerDrake card) { diff --git a/Mage.Sets/src/mage/cards/m/MetalworkColossus.java b/Mage.Sets/src/mage/cards/m/MetalworkColossus.java index 6052cf4bb7..47895a12a5 100644 --- a/Mage.Sets/src/mage/cards/m/MetalworkColossus.java +++ b/Mage.Sets/src/mage/cards/m/MetalworkColossus.java @@ -39,7 +39,7 @@ public final class MetalworkColossus extends CardImpl { this.toughness = new MageInt(10); // Metalwork Colossus costs {X} less to cast, where X is the total converted mana cost of noncreature artifacts you control. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new MetalworkColossusCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new MetalworkColossusCostReductionEffect())); // Sacrifice two artifacts: Return Metalwork Colossus from your graveyard to your hand. this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledArtifactPermanent("two artifacts"), true)))); diff --git a/Mage.Sets/src/mage/cards/m/Metalworker.java b/Mage.Sets/src/mage/cards/m/Metalworker.java index 5a363c383d..ad17878097 100644 --- a/Mage.Sets/src/mage/cards/m/Metalworker.java +++ b/Mage.Sets/src/mage/cards/m/Metalworker.java @@ -35,8 +35,10 @@ public final class Metalworker extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - // {tap}: Reveal any number of artifact cards in your hand. Add {C}{C} for each card revealed this way. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new MetalworkerManaEffect(), new TapSourceCost())); + // {T}: Reveal any number of artifact cards in your hand. Add {C}{C} for each card revealed this way. + SimpleManaAbility ability = new SimpleManaAbility(Zone.BATTLEFIELD, new MetalworkerManaEffect(), new TapSourceCost()); + ability.setUndoPossible(false); + this.addAbility(ability); } public Metalworker(final Metalworker card) { diff --git a/Mage.Sets/src/mage/cards/m/Metamorphose.java b/Mage.Sets/src/mage/cards/m/Metamorphose.java index ab78e2ef56..ffb1e83314 100644 --- a/Mage.Sets/src/mage/cards/m/Metamorphose.java +++ b/Mage.Sets/src/mage/cards/m/Metamorphose.java @@ -74,7 +74,7 @@ class MetamorphoseEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player controller = game.getPlayer(permanent.getControllerId()); if (controller != null && controller.canRespond() && controller.chooseUse(Outcome.PutCardInPlay, "Do you wish to put an artifact, creature, enchantment, or land card onto the battlefield?", source, game)) { diff --git a/Mage.Sets/src/mage/cards/m/MetathranElite.java b/Mage.Sets/src/mage/cards/m/MetathranElite.java index cfeed06ccf..4331ab2b46 100644 --- a/Mage.Sets/src/mage/cards/m/MetathranElite.java +++ b/Mage.Sets/src/mage/cards/m/MetathranElite.java @@ -1,47 +1,47 @@ -package mage.cards.m; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.EnchantedSourceCondition; -import mage.abilities.decorator.ConditionalRestrictionEffect; -import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Zone; - -/** - * - * @author jeffwadsworth - */ -public final class MetathranElite extends CardImpl { - - private static final String rule = "{this} is unblockable as long as it's enchanted."; - - public MetathranElite(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); - - this.subtype.add(SubType.METATHRAN); - this.subtype.add(SubType.SOLDIER); - this.power = new MageInt(2); - this.toughness = new MageInt(3); - - // Metathran Elite is unblockable as long as it's enchanted. - ConditionalRestrictionEffect effect = new ConditionalRestrictionEffect( - new CantBeBlockedSourceEffect(), new EnchantedSourceCondition()); - effect.setText(rule); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); - - } - - public MetathranElite(final MetathranElite card) { - super(card); - } - - @Override - public MetathranElite copy() { - return new MetathranElite(this); - } -} +package mage.cards.m; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.EnchantedSourceCondition; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; + +/** + * + * @author jeffwadsworth + */ +public final class MetathranElite extends CardImpl { + + private static final String rule = "{this} is unblockable as long as it's enchanted."; + + public MetathranElite(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + + this.subtype.add(SubType.METATHRAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Metathran Elite is unblockable as long as it's enchanted. + ConditionalRestrictionEffect effect = new ConditionalRestrictionEffect( + new CantBeBlockedSourceEffect(), new EnchantedSourceCondition()); + effect.setText(rule); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + + } + + public MetathranElite(final MetathranElite card) { + super(card); + } + + @Override + public MetathranElite copy() { + return new MetathranElite(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MidnightClock.java b/Mage.Sets/src/mage/cards/m/MidnightClock.java index 5b79870930..97ac36038f 100644 --- a/Mage.Sets/src/mage/cards/m/MidnightClock.java +++ b/Mage.Sets/src/mage/cards/m/MidnightClock.java @@ -135,7 +135,7 @@ class MidnightClockEffect extends OneShotEffect { cards.addAll(player.getGraveyard()); player.putCardsOnTopOfLibrary(cards, game, source, false); player.shuffleLibrary(source, game); - player.drawCards(7, game); + player.drawCards(7, source.getSourceId(), game); return effect.apply(game, source); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MidnightHaunting.java b/Mage.Sets/src/mage/cards/m/MidnightHaunting.java index d54a8eef61..93e947eacd 100644 --- a/Mage.Sets/src/mage/cards/m/MidnightHaunting.java +++ b/Mage.Sets/src/mage/cards/m/MidnightHaunting.java @@ -1,25 +1,24 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** - * * @author nantuko */ public final class MidnightHaunting extends CardImpl { public MidnightHaunting(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); // Create two 1/1 white Spirit creature tokens with flying. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken("ISD"), 2)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken(), 2)); } public MidnightHaunting(final MidnightHaunting card) { diff --git a/Mage.Sets/src/mage/cards/m/MidnightOil.java b/Mage.Sets/src/mage/cards/m/MidnightOil.java index 954d340896..97280100ae 100644 --- a/Mage.Sets/src/mage/cards/m/MidnightOil.java +++ b/Mage.Sets/src/mage/cards/m/MidnightOil.java @@ -1,14 +1,11 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.BeginningOfDrawTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DiscardCardControllerTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; @@ -20,42 +17,48 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.TargetController; -import mage.constants.Zone; -import mage.counters.Counter; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class MidnightOil extends CardImpl { public MidnightOil(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}"); // Midnight Oil enters the battlefield with seven hour counters on it. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(new Counter(CounterType.HOUR.createInstance(7))), - "with seven hour counters on it")); + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect( + CounterType.HOUR.createInstance(7) + ), "with seven hour counters on it" + )); // At the beginning of your draw step, draw an additional card and remove two hour counters from Midnight Oil. - Ability ability = new BeginningOfDrawTriggeredAbility(new DrawCardSourceControllerEffect(1), TargetController.YOU, false); - Effect effect = new RemoveCounterSourceEffect(CounterType.HOUR.createInstance(2)); - effect.setText("and remove two hour counters from {this}"); - ability.addEffect(effect); + Ability ability = new BeginningOfDrawTriggeredAbility( + new DrawCardSourceControllerEffect(1), + TargetController.YOU, false + ); + ability.addEffect(new RemoveCounterSourceEffect( + CounterType.HOUR.createInstance(2) + ).concatBy("and")); this.addAbility(ability); // Your maximum hand size is equal to the number of hour counters on Midnight Oil. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MaximumHandSizeControllerEffect(new CountersSourceCount(CounterType.HOUR), Duration.WhileOnBattlefield, HandSizeModification.SET, TargetController.YOU))); + this.addAbility(new SimpleStaticAbility(new MaximumHandSizeControllerEffect( + new CountersSourceCount(CounterType.HOUR), Duration.WhileOnBattlefield, + HandSizeModification.SET, TargetController.YOU + ).setText("your maximum hand size is equal to the number of hour counters on {this}"))); // Whenever you discard a card, you lose 1 life. - this.addAbility(new MidnightOilTriggeredAbility(new LoseLifeSourceControllerEffect(1))); - + this.addAbility(new DiscardCardControllerTriggeredAbility( + new LoseLifeSourceControllerEffect(1), false + )); } - public MidnightOil(final MidnightOil card) { + private MidnightOil(final MidnightOil card) { super(card); } @@ -64,35 +67,3 @@ public final class MidnightOil extends CardImpl { return new MidnightOil(this); } } - -class MidnightOilTriggeredAbility extends TriggeredAbilityImpl { - - MidnightOilTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, false); - } - - MidnightOilTriggeredAbility(final MidnightOilTriggeredAbility ability) { - super(ability); - } - - @Override - public MidnightOilTriggeredAbility copy() { - return new MidnightOilTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DISCARDED_CARD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return isControlledBy(event.getPlayerId()); - - } - - @Override - public String getRule() { - return "Whenever you discard a card, " + super.getRule(); - } -} diff --git a/Mage.Sets/src/mage/cards/m/MigrationPath.java b/Mage.Sets/src/mage/cards/m/MigrationPath.java new file mode 100644 index 0000000000..65ab7d7b20 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MigrationPath.java @@ -0,0 +1,42 @@ +package mage.cards.m; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.common.FilterBasicLandCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MigrationPath extends CardImpl { + + private static final FilterCard filter = new FilterBasicLandCard("basic land cards"); + + public MigrationPath(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); + + // Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library. + this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(0, 2, filter), true + )); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private MigrationPath(final MigrationPath card) { + super(card); + } + + @Override + public MigrationPath copy() { + return new MigrationPath(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MigratoryGreathorn.java b/Mage.Sets/src/mage/cards/m/MigratoryGreathorn.java new file mode 100644 index 0000000000..229ad70355 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MigratoryGreathorn.java @@ -0,0 +1,45 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MigratoryGreathorn extends CardImpl { + + public MigratoryGreathorn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Mutate {2}{G} + this.addAbility(new MutateAbility(this, "{2}{G}")); + + // Whenever this creature mutates, search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. + this.addAbility(new MutatesSourceTriggeredAbility(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true + ))); + } + + private MigratoryGreathorn(final MigratoryGreathorn card) { + super(card); + } + + @Override + public MigratoryGreathorn copy() { + return new MigratoryGreathorn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MikaeusTheUnhallowed.java b/Mage.Sets/src/mage/cards/m/MikaeusTheUnhallowed.java index c42b1c8cdb..c10d4266d2 100644 --- a/Mage.Sets/src/mage/cards/m/MikaeusTheUnhallowed.java +++ b/Mage.Sets/src/mage/cards/m/MikaeusTheUnhallowed.java @@ -86,7 +86,7 @@ class MikaeusTheUnhallowedAbility extends TriggeredAbilityImpl { if (event.getTargetId().equals(this.controllerId)) { Permanent permanent = game.getPermanent(event.getSourceId()); if (permanent != null && permanent.hasSubtype(SubType.HUMAN, game)) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + this.getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/m/MimicVat.java b/Mage.Sets/src/mage/cards/m/MimicVat.java index 2e1d4bbfc4..f0ae7a737c 100644 --- a/Mage.Sets/src/mage/cards/m/MimicVat.java +++ b/Mage.Sets/src/mage/cards/m/MimicVat.java @@ -181,7 +181,7 @@ class MimicVatCreateTokenEffect extends OneShotEffect { Card card = game.getCard(permanent.getImprinted().get(0)); if (card != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.apply(game, source); for (Permanent addedToken : effect.getAddedPermanent()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); diff --git a/Mage.Sets/src/mage/cards/m/MinamosMeddling.java b/Mage.Sets/src/mage/cards/m/MinamosMeddling.java index bed01234fd..11887b5046 100644 --- a/Mage.Sets/src/mage/cards/m/MinamosMeddling.java +++ b/Mage.Sets/src/mage/cards/m/MinamosMeddling.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -10,29 +8,27 @@ import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SpellAbilityType; -import mage.filter.StaticFilters; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetSpell; +import java.util.UUID; /** - * * @author LevelX2 */ public final class MinamosMeddling extends CardImpl { public MinamosMeddling(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{U}"); // Counter target spell. That spell's controller reveals their hand, then discards each card with the same name as a card spliced onto that spell. - this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL)); this.getSpellAbility().addEffect(new MinamosMeddlingCounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); } - public MinamosMeddling(final MinamosMeddling card) { + private MinamosMeddling(final MinamosMeddling card) { super(card); } @@ -44,12 +40,12 @@ public final class MinamosMeddling extends CardImpl { class MinamosMeddlingCounterTargetEffect extends OneShotEffect { - public MinamosMeddlingCounterTargetEffect() { + MinamosMeddlingCounterTargetEffect() { super(Outcome.Benefit); staticText = "Counter target spell. That spell's controller reveals their hand, then discards each card with the same name as a card spliced onto that spell"; } - public MinamosMeddlingCounterTargetEffect(final MinamosMeddlingCounterTargetEffect effect) { + private MinamosMeddlingCounterTargetEffect(final MinamosMeddlingCounterTargetEffect effect) { super(effect); } @@ -61,36 +57,34 @@ class MinamosMeddlingCounterTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { MageObject sourceObject = game.getObject(source.getSourceId()); - if (sourceObject != null) { - for (UUID targetId : getTargetPointer().getTargets(game, source) ) { - Spell spell = game.getStack().getSpell(targetId); - if (spell != null) { - game.getStack().counter(targetId, source.getSourceId(), game); - Player spellController = game.getPlayer(spell.getControllerId()); - if (spellController != null) { - spellController.revealCards(sourceObject.getName(), spellController.getHand(), game); - Cards cardsToDiscard = new CardsImpl(); - for (SpellAbility spellAbility : spell.getSpellAbilities()) { - if (spellAbility.getSpellAbilityType() == SpellAbilityType.SPLICE) { - for (Card card: spellController.getHand().getCards(game)) { - if (card.getName().equals(spellAbility.getCardName())) { - cardsToDiscard.add(card); - } - } - } - } - if (!cardsToDiscard.isEmpty()) { - for (Card card :cardsToDiscard.getCards(game)) { - spellController.discard(card, source, game); - } + if (sourceObject == null) { + return false; + } + for (UUID targetId : getTargetPointer().getTargets(game, source)) { + Spell spell = game.getStack().getSpell(targetId); + if (spell == null) { + continue; + } + game.getStack().counter(targetId, source.getSourceId(), game); + Player spellController = game.getPlayer(spell.getControllerId()); + if (spellController == null) { + continue; + } + spellController.revealCards(sourceObject.getName(), spellController.getHand(), game); + Cards cardsToDiscard = new CardsImpl(); + for (SpellAbility spellAbility : spell.getSpellAbilities()) { + if (spellAbility.getSpellAbilityType() == SpellAbilityType.SPLICE) { + for (Card card : spellController.getHand().getCards(game)) { + if (card.getName().equals(spellAbility.getCardName())) { + cardsToDiscard.add(card); } } - } } - return true; + if (!cardsToDiscard.isEmpty()) { + spellController.discard(cardsToDiscard, source, game); + } } - return false; + return true; } - } diff --git a/Mage.Sets/src/mage/cards/m/MindBomb.java b/Mage.Sets/src/mage/cards/m/MindBomb.java index 16074428a2..4fa1119c70 100644 --- a/Mage.Sets/src/mage/cards/m/MindBomb.java +++ b/Mage.Sets/src/mage/cards/m/MindBomb.java @@ -3,7 +3,10 @@ package mage.cards.m; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterCard; @@ -28,7 +31,7 @@ public final class MindBomb extends CardImpl { this.getSpellAbility().addEffect(new MindBombEffect()); } - public MindBomb(final MindBomb card) { + private MindBomb(final MindBomb card) { super(card); } @@ -40,13 +43,13 @@ public final class MindBomb extends CardImpl { class MindBombEffect extends OneShotEffect { - public MindBombEffect() { + MindBombEffect() { super(Outcome.Neutral); this.staticText = "Each player may discard up to three cards." + " {this} deals damage to each player equal to 3 minus the number of cards they discarded this way"; } - public MindBombEffect(final MindBombEffect effect) { + private MindBombEffect(final MindBombEffect effect) { super(effect); } @@ -59,48 +62,44 @@ class MindBombEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - Map cardsToDiscard = new HashMap<>(); - - // choose - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Cards cards = new CardsImpl(); - Target target = new TargetDiscard(0, 3, new FilterCard(), playerId); - player.chooseTarget(outcome, target, source, game); - cards.addAll(target.getTargets()); - cardsToDiscard.put(playerId, cards); - } - } - - // discard - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Cards cardsPlayer = cardsToDiscard.get(playerId); - if (cardsPlayer != null) { - for (UUID cardId : cardsPlayer) { - Card card = game.getCard(cardId); - player.discard(card, source, game); - - } - } - } - } - - // damage - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Cards cardsPlayer = cardsToDiscard.get(playerId); - if (cardsPlayer != null) { - player.damage(3 - cardsPlayer.size(), source.getId(), game); - } - } - } - return true; + if (controller == null || sourceObject == null) { + return false; } - return false; + Map cardsToDiscard = new HashMap<>(); + + // choose + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + Cards cards = new CardsImpl(); + Target target = new TargetDiscard(0, 3, new FilterCard(), playerId); + player.chooseTarget(Outcome.Discard, target, source, game); + cards.addAll(target.getTargets()); + cardsToDiscard.put(playerId, cards); + } + + // discard + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + Cards cardsPlayer = cardsToDiscard.get(playerId); + cardsToDiscard.put(playerId, player.discard(cardsPlayer, source, game)); + } + } + + // damage + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + Cards cardsPlayer = cardsToDiscard.get(playerId); + if (cardsPlayer != null) { + player.damage(3 - cardsPlayer.size(), source.getId(), game); + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/m/MindExtraction.java b/Mage.Sets/src/mage/cards/m/MindExtraction.java index 485c0fa380..7a37732146 100644 --- a/Mage.Sets/src/mage/cards/m/MindExtraction.java +++ b/Mage.Sets/src/mage/cards/m/MindExtraction.java @@ -99,9 +99,7 @@ class MindExtractionEffect extends OneShotEffect { .stream() .filter(card -> card.getColor(game).shares(color)) .forEach(toDiscard::add); - toDiscard.getCards(game) - .stream() - .forEach(card -> player.discard(card, source, game)); + player.discard(toDiscard, source, game); return true; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MindMaggots.java b/Mage.Sets/src/mage/cards/m/MindMaggots.java index e98e414ce8..c68088cb88 100644 --- a/Mage.Sets/src/mage/cards/m/MindMaggots.java +++ b/Mage.Sets/src/mage/cards/m/MindMaggots.java @@ -1,25 +1,26 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.cards.Card; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetCardInHand; +import mage.target.TargetCard; +import mage.target.common.TargetDiscard; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class MindMaggots extends CardImpl { @@ -34,10 +35,9 @@ public final class MindMaggots extends CardImpl { // When Mind Maggots enters the battlefield, discard any number of creature cards. // For each card discarded this way, put two +1/+1 counters on Mind Maggots. this.addAbility(new EntersBattlefieldTriggeredAbility(new MindMaggotsEffect())); - } - public MindMaggots(final MindMaggots card) { + private MindMaggots(final MindMaggots card) { super(card); } @@ -51,10 +51,11 @@ class MindMaggotsEffect extends OneShotEffect { MindMaggotsEffect() { super(Outcome.BoostCreature); - this.staticText = "discard any number of creature cards. For each card discarded this way, put two +1/+1 counters on {this}"; + this.staticText = "discard any number of creature cards. " + + "For each card discarded this way, put two +1/+1 counters on {this}"; } - MindMaggotsEffect(final MindMaggotsEffect effect) { + private MindMaggotsEffect(final MindMaggotsEffect effect) { super(effect); } @@ -66,23 +67,17 @@ class MindMaggotsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int numToDiscard = controller.getAmount(0, - controller.getHand().getCards(StaticFilters.FILTER_CARD_CREATURE, game).size(), - "Discard how many creature cards?", - game); - TargetCardInHand target = new TargetCardInHand(numToDiscard, StaticFilters.FILTER_CARD_CREATURE); - if (controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) { - for (UUID targetId : target.getTargets()) { - Card card = game.getCard(targetId); - if (card != null - && controller.discard(card, source, game)) { - new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)).apply(game, source); - } - } - } + if (controller == null) { + return false; + } + TargetCard target = new TargetDiscard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURE, controller.getId()); + controller.choose(outcome, controller.getHand(), target, game); + int counters = controller.discard(new CardsImpl(target.getTargets()), source, game).size(); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null || counters < 1) { return true; } - return false; + permanent.addCounters(CounterType.P1P1.createInstance(counters), source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/m/MindWarp.java b/Mage.Sets/src/mage/cards/m/MindWarp.java index afef657aeb..e67eddd1c4 100644 --- a/Mage.Sets/src/mage/cards/m/MindWarp.java +++ b/Mage.Sets/src/mage/cards/m/MindWarp.java @@ -1,21 +1,18 @@ - package mage.cards.m; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPlayer; +import mage.target.common.TargetCardInHand; import java.util.UUID; @@ -28,11 +25,11 @@ public final class MindWarp extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{3}{B}"); // Look at target player's hand and choose X cards from it. That player discards those cards. - this.getSpellAbility().addEffect(new MindWarpEffect(ManacostVariableValue.instance)); + this.getSpellAbility().addEffect(new MindWarpEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); } - public MindWarp(final MindWarp card) { + private MindWarp(final MindWarp card) { super(card); } @@ -44,39 +41,27 @@ public final class MindWarp extends CardImpl { class MindWarpEffect extends OneShotEffect { - private final DynamicValue xValue; - - public MindWarpEffect(DynamicValue xValue) { + MindWarpEffect() { super(Outcome.Discard); - this.xValue = xValue; - staticText = "Look at target player's hand and choose X cards from it. That player discards those card."; + staticText = "Look at target player's hand and choose X cards from it. That player discards those cards."; } - public MindWarpEffect(final MindWarpEffect effect) { + private MindWarpEffect(final MindWarpEffect effect) { super(effect); - this.xValue = effect.xValue; } @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(source.getFirstTarget()); Player you = game.getPlayer(source.getControllerId()); - if (targetPlayer != null && you != null) { - int amountToDiscard = Math.min( - xValue.calculate(game, source, this), - targetPlayer.getHand().size() - ); - you.lookAtCards("Discard", targetPlayer.getHand(), game); - TargetCard target = new TargetCard(amountToDiscard, Zone.HAND, new FilterCard()); - target.setNotTarget(true); - if (you.choose(Outcome.Benefit, targetPlayer.getHand(), target, game)) { - Card card = targetPlayer.getHand().get(target.getFirstTarget(), game); - return targetPlayer.discard(card, source, game); - - } - + if (targetPlayer == null || you == null) { + return false; } - return false; + int amountToDiscard = source.getManaCostsToPay().getX(); + TargetCard target = new TargetCardInHand(amountToDiscard, StaticFilters.FILTER_CARD_CARDS); + you.choose(outcome, targetPlayer.getHand(), target, game); + targetPlayer.discard(new CardsImpl(target.getTargets()), source, game); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/m/MindWhip.java b/Mage.Sets/src/mage/cards/m/MindWhip.java index d3eea1bf1a..447b89154a 100644 --- a/Mage.Sets/src/mage/cards/m/MindWhip.java +++ b/Mage.Sets/src/mage/cards/m/MindWhip.java @@ -1,99 +1,99 @@ -package mage.cards.m; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.DoUnlessTargetPlayerOrTargetsControllerPaysEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.TargetController; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author jeffwadsworth - */ -public final class MindWhip extends CardImpl { - - public MindWhip(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // At the beginning of the upkeep of enchanted creature's controller, that player may pay {3}. If they don't, Mind Whip deals 2 damage to that player and you tap that creature. - Effect effect = new DoUnlessTargetPlayerOrTargetsControllerPaysEffect(new MindWhipEffect(), - new ManaCostsImpl("{3}"), - ""); - effect.setText("that player may pay {3}. If they don't, {this} deals 2 damage to that player and you tap that creature."); - this.addAbility(new BeginningOfUpkeepTriggeredAbility( - effect, - TargetController.CONTROLLER_ATTACHED_TO, false)); - - } - - private MindWhip(final MindWhip card) { - super(card); - } - - @Override - public MindWhip copy() { - return new MindWhip(this); - } -} - -class MindWhipEffect extends OneShotEffect { - - public MindWhipEffect() { - super(Outcome.Neutral); - staticText = ""; - } - - public MindWhipEffect(final MindWhipEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controllerOfEnchantedCreature = game.getPlayer(targetPointer.getFirst(game, source)); - Permanent mindWhip = game.getPermanent(source.getSourceId()); - if (controllerOfEnchantedCreature != null - && mindWhip != null) { - Permanent enchantedCreature = game.getPermanent(mindWhip.getAttachedTo()); - if (enchantedCreature != null) { - Effect effect = new DamageTargetEffect(2); - effect.setTargetPointer(new FixedTarget(controllerOfEnchantedCreature.getId())); - effect.apply(game, source); - enchantedCreature.tap(game); - return true; - } - } - return false; - } - - @Override - public MindWhipEffect copy() { - return new MindWhipEffect(this); - } - -} +package mage.cards.m; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DoUnlessTargetPlayerOrTargetsControllerPaysEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jeffwadsworth + */ +public final class MindWhip extends CardImpl { + + public MindWhip(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of the upkeep of enchanted creature's controller, that player may pay {3}. If they don't, Mind Whip deals 2 damage to that player and you tap that creature. + Effect effect = new DoUnlessTargetPlayerOrTargetsControllerPaysEffect(new MindWhipEffect(), + new ManaCostsImpl("{3}"), + ""); + effect.setText("that player may pay {3}. If they don't, {this} deals 2 damage to that player and you tap that creature."); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + effect, + TargetController.CONTROLLER_ATTACHED_TO, false)); + + } + + private MindWhip(final MindWhip card) { + super(card); + } + + @Override + public MindWhip copy() { + return new MindWhip(this); + } +} + +class MindWhipEffect extends OneShotEffect { + + public MindWhipEffect() { + super(Outcome.Neutral); + staticText = ""; + } + + public MindWhipEffect(final MindWhipEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controllerOfEnchantedCreature = game.getPlayer(targetPointer.getFirst(game, source)); + Permanent mindWhip = game.getPermanent(source.getSourceId()); + if (controllerOfEnchantedCreature != null + && mindWhip != null) { + Permanent enchantedCreature = game.getPermanent(mindWhip.getAttachedTo()); + if (enchantedCreature != null) { + Effect effect = new DamageTargetEffect(2); + effect.setTargetPointer(new FixedTarget(controllerOfEnchantedCreature.getId())); + effect.apply(game, source); + enchantedCreature.tap(game); + return true; + } + } + return false; + } + + @Override + public MindWhipEffect copy() { + return new MindWhipEffect(this); + } + +} diff --git a/Mage.Sets/src/mage/cards/m/Mindcrank.java b/Mage.Sets/src/mage/cards/m/Mindcrank.java index 435ee718a6..f2610c32dc 100644 --- a/Mage.Sets/src/mage/cards/m/Mindcrank.java +++ b/Mage.Sets/src/mage/cards/m/Mindcrank.java @@ -104,7 +104,7 @@ class MindcrankEffect extends OneShotEffect { if (amount == null) { amount = 0; } - targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game); + targetPlayer.millCards(amount, source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/m/MindeyeDrake.java b/Mage.Sets/src/mage/cards/m/MindeyeDrake.java index a2410f56b8..d693c35d37 100644 --- a/Mage.Sets/src/mage/cards/m/MindeyeDrake.java +++ b/Mage.Sets/src/mage/cards/m/MindeyeDrake.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -29,7 +29,7 @@ public final class MindeyeDrake extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Mindeye Drake dies, target player puts the top five cards of their library into their graveyard. - Ability ability = new DiesTriggeredAbility(new PutLibraryIntoGraveTargetEffect(5)); + Ability ability = new DiesSourceTriggeredAbility(new PutLibraryIntoGraveTargetEffect(5)); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/Mindleecher.java b/Mage.Sets/src/mage/cards/m/Mindleecher.java new file mode 100644 index 0000000000..c4f756ed5f --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/Mindleecher.java @@ -0,0 +1,174 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.*; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Mindleecher extends CardImpl { + + protected static final String VALUE_PREFIX = "ExileZones"; + + public Mindleecher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); + + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Mutate {4}{B} + this.addAbility(new MutateAbility(this, "{4}{B}")); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever this creature mutates, exile the top card of each opponent's library face down. You may look at and play those cards for as long as they remain exiled. + this.addAbility(new MutatesSourceTriggeredAbility(new MindleecherEffect())); + } + + private Mindleecher(final Mindleecher card) { + super(card); + } + + @Override + public Mindleecher copy() { + return new Mindleecher(this); + } +} + +class MindleecherEffect extends OneShotEffect { + + MindleecherEffect() { + super(Outcome.Benefit); + staticText = "exile the top card of each opponent's library face down. " + + "You may look at and play those cards for as long as they remain exiled."; + } + + private MindleecherEffect(final MindleecherEffect effect) { + super(effect); + } + + @Override + public MindleecherEffect copy() { + return new MindleecherEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + game.getOpponents(controller.getId()) + .stream() + .map(game::getPlayer) + .map(Player::getLibrary) + .map(library -> library.getFromTop(game)) + .forEach(cards::add); + if (cards.isEmpty()) { + return false; + } + controller.moveCards(cards, Zone.EXILED, source, game); + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); + if (cards.isEmpty()) { + return false; + } + cards.getCards(game).stream().forEach(card -> card.setFaceDown(true, game)); + for (Card card : cards.getCards(game)) { + game.addEffect(new MindleecherCastFromExileEffect(controller.getId()) + .setTargetPointer(new FixedTarget(card, game)), source); + game.addEffect(new MindleecherLookEffect(controller.getId()) + .setTargetPointer(new FixedTarget(card, game)), source); + } + return true; + } +} + +class MindleecherCastFromExileEffect extends AsThoughEffectImpl { + + private final UUID authorizedPlayerId; + + MindleecherCastFromExileEffect(UUID authorizedPlayerId) { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); + this.authorizedPlayerId = authorizedPlayerId; + staticText = "For as long as that card remains exiled, you may play it"; + } + + private MindleecherCastFromExileEffect(final MindleecherCastFromExileEffect effect) { + super(effect); + this.authorizedPlayerId = effect.authorizedPlayerId; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public MindleecherCastFromExileEffect copy() { + return new MindleecherCastFromExileEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + UUID cardId = getTargetPointer().getFirst(game, source); + if (cardId == null) { + this.discard(); // card is no longer in the origin zone, effect can be discarded + return false; + } + return objectId.equals(cardId) + && affectedControllerId.equals(authorizedPlayerId) + && game.getCard(objectId) != null; + } +} + +class MindleecherLookEffect extends AsThoughEffectImpl { + + private final UUID authorizedPlayerId; + + MindleecherLookEffect(UUID authorizedPlayerId) { + super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); + this.authorizedPlayerId = authorizedPlayerId; + staticText = "For as long as that card remains exiled, you may look at it"; + } + + private MindleecherLookEffect(final MindleecherLookEffect effect) { + super(effect); + this.authorizedPlayerId = effect.authorizedPlayerId; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public MindleecherLookEffect copy() { + return new MindleecherLookEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + UUID cardId = getTargetPointer().getFirst(game, source); + if (cardId == null) { + this.discard(); // card is no longer in the origin zone, effect can be discarded + return false; + } + return affectedControllerId.equals(authorizedPlayerId) + && objectId.equals(cardId); + } +} diff --git a/Mage.Sets/src/mage/cards/m/Mindmoil.java b/Mage.Sets/src/mage/cards/m/Mindmoil.java index bb0dda60d3..b4ba0dadc1 100644 --- a/Mage.Sets/src/mage/cards/m/Mindmoil.java +++ b/Mage.Sets/src/mage/cards/m/Mindmoil.java @@ -53,7 +53,7 @@ class MindmoilEffect extends OneShotEffect { if (you != null) { int count = you.getHand().size(); you.putCardsOnBottomOfLibrary(you.getHand(), game, source, true); - you.drawCards(count, game); + you.drawCards(count, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/m/MindsAglow.java b/Mage.Sets/src/mage/cards/m/MindsAglow.java index b5eaadc85b..7614b133e2 100644 --- a/Mage.Sets/src/mage/cards/m/MindsAglow.java +++ b/Mage.Sets/src/mage/cards/m/MindsAglow.java @@ -70,7 +70,7 @@ class MindsAglowEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(xSum, game); + player.drawCards(xSum, source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/cards/m/MindsDesire.java b/Mage.Sets/src/mage/cards/m/MindsDesire.java index eea1c4c7fa..d5cf67afd6 100644 --- a/Mage.Sets/src/mage/cards/m/MindsDesire.java +++ b/Mage.Sets/src/mage/cards/m/MindsDesire.java @@ -1,23 +1,19 @@ package mage.cards.m; import mage.abilities.Ability; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.StormAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; -import mage.target.targetpointer.FixedTargets; -import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.constants.TargetController; /** * @author emerald000 @@ -65,66 +61,9 @@ class MindsDesireEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { controller.shuffleLibrary(source, game); - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - UUID exileId = CardUtil.getExileZoneId(controller.getId().toString() + "-" + game.getState().getTurnNum() + "-" + MindsDesire.class.toString(), game); - String exileName = "Mind's Desire free cast on " + game.getState().getTurnNum() + " turn for " + controller.getName(); - game.getExile().createZone(exileId, exileName).setCleanupOnEndTurn(true); - if (controller.moveCardsToExile(card, source, game, true, exileId, exileName)) { - ContinuousEffect effect = new MindsDesireCastFromExileEffect(); - effect.setTargetPointer(new FixedTargets(game.getExile().getExileZone(exileId).getCards(game), game)); - game.addEffect(effect, source); - } - } - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, controller.getLibrary().getFromTop(game), + TargetController.YOU, Duration.EndOfTurn, true); } return false; } -} - -class MindsDesireCastFromExileEffect extends AsThoughEffectImpl { - - MindsDesireCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "you may play that card without paying its mana cost"; - } - - MindsDesireCastFromExileEffect(final MindsDesireCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public MindsDesireCastFromExileEffect copy() { - return new MindsDesireCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return applies(objectId, null, source, game, affectedControllerId); - } - - @Override - public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { - Card cardToCheck = game.getCard(objectId); - objectId = CardUtil.getMainCardId(game, objectId); // for split cards - - if (!isAbilityAppliedForAlternateCast(cardToCheck, affectedAbility, playerId, source)) { - return false; - } - - Player controller = game.getPlayer(cardToCheck.getOwnerId()); - if (controller != null - && getTargetPointer().getTargets(game, source).contains(objectId)) { - controller.setCastSourceIdWithAlternateMana(affectedAbility.getSourceId(), null, affectedAbility.getCosts()); - return true; - } - - - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/Mindshrieker.java b/Mage.Sets/src/mage/cards/m/Mindshrieker.java index b8effb0479..ad0be56807 100644 --- a/Mage.Sets/src/mage/cards/m/Mindshrieker.java +++ b/Mage.Sets/src/mage/cards/m/Mindshrieker.java @@ -1,20 +1,24 @@ package mage.cards.m; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.Objects; import java.util.UUID; /** @@ -34,10 +38,9 @@ public final class Mindshrieker extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // {2}: Target player puts the top card of their library into their graveyard. Mindshrieker gets +X/+X until end of turn, where X is that card's converted mana cost. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MindshriekerEffect(), new ManaCostsImpl("{2}")); + Ability ability = new SimpleActivatedAbility(new MindshriekerEffect(), new GenericManaCost(2)); ability.addTarget(new TargetPlayer()); this.addAbility(ability); - } public Mindshrieker(final Mindshrieker card) { @@ -52,30 +55,31 @@ public final class Mindshrieker extends CardImpl { class MindshriekerEffect extends OneShotEffect { - public MindshriekerEffect() { + MindshriekerEffect() { super(Outcome.Detriment); - staticText = "Target player puts the top card of their library into their graveyard. {this} gets +X/+X until end of turn, where X is that card's converted mana cost"; + staticText = "Target player mills a card. {this} gets +X/+X until end of turn, " + + "where X is the milled card's converted mana cost"; } - public MindshriekerEffect(final MindshriekerEffect effect) { + private MindshriekerEffect(final MindshriekerEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (targetPlayer != null) { - if (targetPlayer.getLibrary().hasCards()) { - Card card = targetPlayer.getLibrary().getFromTop(game); - if (card != null) { - targetPlayer.moveCards(card, Zone.GRAVEYARD, source, game); - int amount = card.getConvertedManaCost(); - if (amount > 0) { - game.addEffect(new BoostSourceEffect(amount, amount, Duration.EndOfTurn), source); - } - } - } - return true; + if (targetPlayer == null) { + return false; + } + int totalCMC = targetPlayer + .millCards(1, source, game) + .getCards(game) + .stream() + .filter(Objects::nonNull) + .mapToInt(MageObject::getConvertedManaCost) + .sum(); + if (totalCMC > 0) { + game.addEffect(new BoostSourceEffect(totalCMC, totalCMC, Duration.EndOfTurn), source); } return false; } diff --git a/Mage.Sets/src/mage/cards/m/Mindslicer.java b/Mage.Sets/src/mage/cards/m/Mindslicer.java index 3d3bab1a09..160481b70c 100644 --- a/Mage.Sets/src/mage/cards/m/Mindslicer.java +++ b/Mage.Sets/src/mage/cards/m/Mindslicer.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.discard.DiscardHandAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class Mindslicer extends CardImpl { this.toughness = new MageInt(3); // When Mindslicer dies, each player discards their hand. - this.addAbility(new DiesTriggeredAbility(new DiscardHandAllEffect(),false)); + this.addAbility(new DiesSourceTriggeredAbility(new DiscardHandAllEffect(),false)); } public Mindslicer(final Mindslicer card) { diff --git a/Mage.Sets/src/mage/cards/m/MindstormCrown.java b/Mage.Sets/src/mage/cards/m/MindstormCrown.java index d07e17dd1c..533486457d 100644 --- a/Mage.Sets/src/mage/cards/m/MindstormCrown.java +++ b/Mage.Sets/src/mage/cards/m/MindstormCrown.java @@ -65,7 +65,7 @@ class MindstormCrownEffect extends OneShotEffect { MindstormCrownWatcher watcher = game.getState().getWatcher(MindstormCrownWatcher.class); if (watcher != null && watcher.getCardsInHandCount() == 0) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } else { if (permanent != null) { controller.damage(1, permanent.getId(), game); diff --git a/Mage.Sets/src/mage/cards/m/MinionReflector.java b/Mage.Sets/src/mage/cards/m/MinionReflector.java index 31da2e2881..a06489cf4c 100644 --- a/Mage.Sets/src/mage/cards/m/MinionReflector.java +++ b/Mage.Sets/src/mage/cards/m/MinionReflector.java @@ -83,7 +83,7 @@ class MinionReflectorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/m/MinionsMurmurs.java b/Mage.Sets/src/mage/cards/m/MinionsMurmurs.java index 94fb18394a..ae171e9a0e 100644 --- a/Mage.Sets/src/mage/cards/m/MinionsMurmurs.java +++ b/Mage.Sets/src/mage/cards/m/MinionsMurmurs.java @@ -56,7 +56,7 @@ public final class MinionsMurmurs extends CardImpl { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int creaturesControlled = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, controller.getId(), game); - controller.drawCards(creaturesControlled, game); + controller.drawCards(creaturesControlled, source.getSourceId(), game); controller.loseLife(creaturesControlled, game, false); return true; } diff --git a/Mage.Sets/src/mage/cards/m/Mirari.java b/Mage.Sets/src/mage/cards/m/Mirari.java index 7a209fa01a..20482a095f 100644 --- a/Mage.Sets/src/mage/cards/m/Mirari.java +++ b/Mage.Sets/src/mage/cards/m/Mirari.java @@ -79,13 +79,7 @@ class MirariTriggeredAbility extends TriggeredAbilityImpl { if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (isControlledInstantOrSorcery(spell)) { - for (Effect effect : getEffects()) { - if (effect instanceof DoIfCostPaid) { - for (Effect execEffect : ((DoIfCostPaid) effect).getExecutingEffects()) { - execEffect.setTargetPointer(new FixedTarget(spell.getId())); - } - } - } + getEffects().setTargetPointer(new FixedTarget(spell.getId())); return true; } } diff --git a/Mage.Sets/src/mage/cards/m/MirranMettle.java b/Mage.Sets/src/mage/cards/m/MirranMettle.java index 50b66bd8e0..07e13bbb3b 100644 --- a/Mage.Sets/src/mage/cards/m/MirranMettle.java +++ b/Mage.Sets/src/mage/cards/m/MirranMettle.java @@ -1,19 +1,20 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.condition.LockedInCondition; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class MirranMettle extends CardImpl { @@ -21,14 +22,17 @@ public final class MirranMettle extends CardImpl { private static final String effectText = "Metalcraft — That creature gets +4/+4 until end of turn instead if you control three or more artifacts."; public MirranMettle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + // Target creature gets +2/+2 until end of turn. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); - this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn), + // Metalcraft — That creature gets +4/+4 until end of turn instead if you control three or more artifacts. + this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn), new LockedInCondition(MetalcraftCondition.instance), effectText)); + this.getSpellAbility().setAbilityWord(AbilityWord.METALCRAFT); + this.getSpellAbility().addHint(MetalcraftHint.instance); } public MirranMettle(final MirranMettle card) { diff --git a/Mage.Sets/src/mage/cards/m/MirrorImage.java b/Mage.Sets/src/mage/cards/m/MirrorImage.java index ffed21f69f..e45a870f88 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorImage.java +++ b/Mage.Sets/src/mage/cards/m/MirrorImage.java @@ -1,17 +1,17 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.common.CopyPermanentEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class MirrorImage extends CardImpl { @@ -27,12 +27,12 @@ public final class MirrorImage extends CardImpl { this.addAbility(new EntersBattlefieldAbility( new CopyPermanentEffect(StaticFilters.FILTER_CONTROLLED_CREATURE) .setText("you may have {this} enter the battlefield " - + "as a copy of any creature you control"), + + "as a copy of a creature you control"), true )); } - public MirrorImage(final MirrorImage card) { + private MirrorImage(final MirrorImage card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java b/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java index 34511c3480..5d8a5207f3 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java +++ b/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java @@ -69,10 +69,7 @@ class MirrorMadPhantasmEffect extends OneShotEffect { if (owner == null) { return false; } - if (owner.moveCards(perm, Zone.LIBRARY, source, game)) { - owner.shuffleLibrary(source, game); - perm.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - owner.shuffleLibrary(source, game); + if (owner.shuffleCardsToLibrary(perm, game, source)) { Cards cards = new CardsImpl(); Card phantasmCard = null; for (Card card : owner.getLibrary().getCards(game)) { diff --git a/Mage.Sets/src/mage/cards/m/MirrorOfFate.java b/Mage.Sets/src/mage/cards/m/MirrorOfFate.java index bc8bd0e12f..5eb29e5592 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorOfFate.java +++ b/Mage.Sets/src/mage/cards/m/MirrorOfFate.java @@ -67,42 +67,20 @@ class MirrorOfFateEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } - + // Choose up to seven face-up exiled cards you own CardsImpl cards = new CardsImpl(); MirrorOfFateTarget targetExile = new MirrorOfFateTarget(); - if (player.choose(outcome, targetExile, source.getSourceId(), game)) { - for (UUID cardId : targetExile.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - cards.add(card); - } - } + if (controller.choose(outcome, targetExile, source.getSourceId(), game)) { + cards.addAll(targetExile.getTargets()); } - - for (Card card : player.getLibrary().getCards(game)) { - card.moveToExile(null, null, source.getSourceId(), game); - } - - TargetCard target = new TargetCard(Zone.EXILED, new FilterCard("card to put on top of your library")); - while (player.canRespond() && cards.size() > 1) { - player.choose(Outcome.Neutral, cards, target, game); - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - cards.remove(card); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - target.clearChosen(); - } - if (cards.size() == 1) { - Card card = cards.get(cards.iterator().next(), game); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - - return true; + // Exile all the cards from your library + controller.moveCards(new CardsImpl(controller.getLibrary().getCardList()), Zone.EXILED, source, game); + // put the chosen cards on top of your library" + return controller.putCardsOnTopOfLibrary(cards, game, source, true); } } diff --git a/Mage.Sets/src/mage/cards/m/MirrorShield.java b/Mage.Sets/src/mage/cards/m/MirrorShield.java index e2d4db1cde..27d9368dc8 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorShield.java +++ b/Mage.Sets/src/mage/cards/m/MirrorShield.java @@ -1,7 +1,7 @@ package mage.cards.m; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; @@ -44,7 +44,7 @@ public final class MirrorShield extends CardImpl { ability.addEffect(new GainAbilityAttachedEffect( HexproofAbility.getInstance(), AttachmentType.EQUIPMENT ).setText("and has hexproof")); - ability.addEffect(new GainAbilityAttachedEffect(new BlocksOrBecomesBlockedTriggeredAbility( + ability.addEffect(new GainAbilityAttachedEffect(new BlocksOrBecomesBlockedSourceTriggeredAbility( new DestroyTargetEffect(), filter, false, rule + "", true ), AttachmentType.EQUIPMENT).setText("and \"" + rule + "\"")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/Mirrorweave.java b/Mage.Sets/src/mage/cards/m/Mirrorweave.java index 9be7608464..07c32b9a1f 100644 --- a/Mage.Sets/src/mage/cards/m/Mirrorweave.java +++ b/Mage.Sets/src/mage/cards/m/Mirrorweave.java @@ -72,7 +72,7 @@ class MirrorWeaveEffect extends OneShotEffect { FilterCreaturePermanent filter = new FilterCreaturePermanent(); if (controller != null) { - Permanent copyFromCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent copyFromCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (copyFromCreature != null) { filter.add(Predicates.not(new PermanentIdPredicate(copyFromCreature.getId()))); for (Permanent copyToCreature : game.getBattlefield().getAllActivePermanents(filter, game)) { diff --git a/Mage.Sets/src/mage/cards/m/Miscast.java b/Mage.Sets/src/mage/cards/m/Miscast.java new file mode 100644 index 0000000000..1bfb7ff2ce --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/Miscast.java @@ -0,0 +1,34 @@ +package mage.cards.m; + +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Miscast extends CardImpl { + + public Miscast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // Counter target instant or sorcery spell unless its controller pays {3}. + this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(new GenericManaCost(3))); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY)); + } + + private Miscast(final Miscast card) { + super(card); + } + + @Override + public Miscast copy() { + return new Miscast(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MischievousChimera.java b/Mage.Sets/src/mage/cards/m/MischievousChimera.java index 09bf177b4e..0806c73797 100644 --- a/Mage.Sets/src/mage/cards/m/MischievousChimera.java +++ b/Mage.Sets/src/mage/cards/m/MischievousChimera.java @@ -33,7 +33,7 @@ public final class MischievousChimera extends CardImpl { Ability ability = new FirstSpellOpponentsTurnTriggeredAbility( new DamagePlayersEffect(1, TargetController.OPPONENT), false ); - ability.addEffect(new ScryEffect(1).setText("Scry 1")); + ability.addEffect(new ScryEffect(1, false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/Mise.java b/Mage.Sets/src/mage/cards/m/Mise.java index 06074b560f..b09b753876 100644 --- a/Mage.Sets/src/mage/cards/m/Mise.java +++ b/Mage.Sets/src/mage/cards/m/Mise.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ChooseACardNameEffect; @@ -13,16 +11,18 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author L_J */ public final class Mise extends CardImpl { public Mise(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + // Choose a nonland card name, then reveal the top card of your library. If that card has the chosen name, you draw three cards. this.getSpellAbility().addEffect(new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME)); this.getSpellAbility().addEffect(new MiseEffect()); @@ -52,15 +52,14 @@ class MiseEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Object object = game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - if (player != null && object instanceof String) { + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + if (player != null && cardName != null) { Card card = player.getLibrary().getFromTop(game); - String namedCard = (String) object; CardsImpl cards = new CardsImpl(card); if (card != null) { player.revealCards("Mise", cards, game, true); - if (card.getName().equals(namedCard)) { - player.drawCards(3, game); + if (CardUtil.haveSameNames(card, cardName, game)) { + player.drawCards(3, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/m/Misfortune.java b/Mage.Sets/src/mage/cards/m/Misfortune.java index a08781332f..b2ebd15c9f 100644 --- a/Mage.Sets/src/mage/cards/m/Misfortune.java +++ b/Mage.Sets/src/mage/cards/m/Misfortune.java @@ -1,88 +1,88 @@ -package mage.cards.m; - -import mage.abilities.Ability; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.counter.AddCountersAllEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetOpponent; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class Misfortune extends CardImpl { - - public Misfortune(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{R}{G}"); - - // An opponent chooses one - You put a +1/+1 counter on each creature you control and gain 4 life; or you put a -1/-1 counter on each creature that player controls and Misfortune deals 4 damage to that player. - this.getSpellAbility().addEffect(new MisfortuneEffect()); - this.getSpellAbility().addTarget(new TargetOpponent(true)); - } - - private Misfortune(final Misfortune card) { - super(card); - } - - @Override - public Misfortune copy() { - return new Misfortune(this); - } -} - -class MisfortuneEffect extends OneShotEffect { - - public MisfortuneEffect() { - super(Outcome.Neutral); - staticText = "An opponent chooses one - " - + "You put a +1/+1 counter on each creature you control and gain " - + "4 life; or you put a -1/-1 counter on each creature that player " - + "controls and Misfortune deals 4 damage to that player"; - } - - public MisfortuneEffect(final MisfortuneEffect effect) { - super(effect); - } - - @Override - public MisfortuneEffect copy() { - return new MisfortuneEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Player chosenOpponent = game.getPlayer(targetPointer.getFirst(game, source)); - if (controller != null - && chosenOpponent != null) { - if (chosenOpponent.chooseUse(Outcome.Neutral, "If you choose Yes, the controller puts a +1/+1 counter" - + "on each creature they control and they gain 4 life. If no, the controller puts a -1/-1 counter" - + "on each creature you control and {this} deals 4 damage to you.", source, game)) { - Effect putP1P1CounterOnEachControlledCreature = new AddCountersAllEffect( - CounterType.P1P1.createInstance(), new FilterControlledCreaturePermanent()); - putP1P1CounterOnEachControlledCreature.apply(game, source); - controller.gainLife(4, game, source); - } else { - FilterCreaturePermanent filterOpponentCreatures = new FilterCreaturePermanent(); - filterOpponentCreatures.add(new ControllerIdPredicate(chosenOpponent.getId())); - Effect putM1M1CounterOnEachOpponentCreature = new AddCountersAllEffect( - CounterType.M1M1.createInstance(), filterOpponentCreatures); - putM1M1CounterOnEachOpponentCreature.apply(game, source); - chosenOpponent.damage(4, source.getSourceId(), game); - } - return true; - } - return false; - } -} +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class Misfortune extends CardImpl { + + public Misfortune(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{R}{G}"); + + // An opponent chooses one - You put a +1/+1 counter on each creature you control and gain 4 life; or you put a -1/-1 counter on each creature that player controls and Misfortune deals 4 damage to that player. + this.getSpellAbility().addEffect(new MisfortuneEffect()); + this.getSpellAbility().addTarget(new TargetOpponent(true)); + } + + private Misfortune(final Misfortune card) { + super(card); + } + + @Override + public Misfortune copy() { + return new Misfortune(this); + } +} + +class MisfortuneEffect extends OneShotEffect { + + public MisfortuneEffect() { + super(Outcome.Neutral); + staticText = "An opponent chooses one - " + + "You put a +1/+1 counter on each creature you control and gain " + + "4 life; or you put a -1/-1 counter on each creature that player " + + "controls and Misfortune deals 4 damage to that player"; + } + + public MisfortuneEffect(final MisfortuneEffect effect) { + super(effect); + } + + @Override + public MisfortuneEffect copy() { + return new MisfortuneEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player chosenOpponent = game.getPlayer(targetPointer.getFirst(game, source)); + if (controller != null + && chosenOpponent != null) { + if (chosenOpponent.chooseUse(Outcome.Neutral, "If you choose Yes, the controller puts a +1/+1 counter" + + "on each creature they control and they gain 4 life. If no, the controller puts a -1/-1 counter" + + "on each creature you control and {this} deals 4 damage to you.", source, game)) { + Effect putP1P1CounterOnEachControlledCreature = new AddCountersAllEffect( + CounterType.P1P1.createInstance(), new FilterControlledCreaturePermanent()); + putP1P1CounterOnEachControlledCreature.apply(game, source); + controller.gainLife(4, game, source); + } else { + FilterCreaturePermanent filterOpponentCreatures = new FilterCreaturePermanent(); + filterOpponentCreatures.add(new ControllerIdPredicate(chosenOpponent.getId())); + Effect putM1M1CounterOnEachOpponentCreature = new AddCountersAllEffect( + CounterType.M1M1.createInstance(), filterOpponentCreatures); + putM1M1CounterOnEachOpponentCreature.apply(game, source); + chosenOpponent.damage(4, source.getSourceId(), game); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MistcutterHydra.java b/Mage.Sets/src/mage/cards/m/MistcutterHydra.java index 92fb0c947a..6078203b18 100644 --- a/Mage.Sets/src/mage/cards/m/MistcutterHydra.java +++ b/Mage.Sets/src/mage/cards/m/MistcutterHydra.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; import mage.abilities.keyword.HasteAbility; @@ -29,7 +29,7 @@ public final class MistcutterHydra extends CardImpl { this.toughness = new MageInt(0); // Mistcutter Hydra can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Haste this.addAbility(HasteAbility.getInstance()); // protection from blue diff --git a/Mage.Sets/src/mage/cards/m/MistformSliver.java b/Mage.Sets/src/mage/cards/m/MistformSliver.java index d7de8a0b92..1e471dee58 100644 --- a/Mage.Sets/src/mage/cards/m/MistformSliver.java +++ b/Mage.Sets/src/mage/cards/m/MistformSliver.java @@ -72,7 +72,7 @@ class MistformSliverEffect extends OneShotEffect { } game.informPlayers(permanent.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoice()); ContinuousEffect effect = new AddCardSubTypeTargetEffect(SubType.byDescription(typeChoice.getChoice()), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } return false; diff --git a/Mage.Sets/src/mage/cards/m/MistformWakecaster.java b/Mage.Sets/src/mage/cards/m/MistformWakecaster.java index eca64b2369..62811464ae 100644 --- a/Mage.Sets/src/mage/cards/m/MistformWakecaster.java +++ b/Mage.Sets/src/mage/cards/m/MistformWakecaster.java @@ -93,7 +93,7 @@ class BecomesChosenCreatureTypeControlledEffect extends OneShotEffect { if (chosenType != null && !chosenType.isEmpty()) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), player.getId(), game)) { ContinuousEffect effect = new BecomesCreatureTypeTargetEffect(Duration.EndOfTurn, SubType.byDescription(chosenType)); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/m/MistmoonGriffin.java b/Mage.Sets/src/mage/cards/m/MistmoonGriffin.java index 471aaa6753..98c2e47201 100644 --- a/Mage.Sets/src/mage/cards/m/MistmoonGriffin.java +++ b/Mage.Sets/src/mage/cards/m/MistmoonGriffin.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -34,7 +34,7 @@ public final class MistmoonGriffin extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Mistmoon Griffin dies, exile Mistmoon Griffin, then return the top creature card of your graveyard to the battlefield. - Ability ability = new DiesTriggeredAbility(new ExileSourceEffect()); + Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect()); ability.addEffect(new MistmoonGriffinEffect()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MistralSinger.java b/Mage.Sets/src/mage/cards/m/MistralSinger.java new file mode 100644 index 0000000000..fb669618d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MistralSinger.java @@ -0,0 +1,40 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MistralSinger extends CardImpl { + + public MistralSinger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.SIREN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Prowess + this.addAbility(new ProwessAbility()); + } + + private MistralSinger(final MistralSinger card) { + super(card); + } + + @Override + public MistralSinger copy() { + return new MistralSinger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MitoticSlime.java b/Mage.Sets/src/mage/cards/m/MitoticSlime.java index 73b29d2cf0..1118a07e55 100644 --- a/Mage.Sets/src/mage/cards/m/MitoticSlime.java +++ b/Mage.Sets/src/mage/cards/m/MitoticSlime.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class MitoticSlime extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new Ooze2Token(), 2), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new Ooze2Token(), 2), false)); } public MitoticSlime(final MitoticSlime card) { diff --git a/Mage.Sets/src/mage/cards/m/MizziumMortars.java b/Mage.Sets/src/mage/cards/m/MizziumMortars.java index 20aa10d60c..de61755de3 100644 --- a/Mage.Sets/src/mage/cards/m/MizziumMortars.java +++ b/Mage.Sets/src/mage/cards/m/MizziumMortars.java @@ -7,8 +7,7 @@ import mage.abilities.keyword.OverloadAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -18,25 +17,18 @@ import java.util.UUID; */ public final class MizziumMortars extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public MizziumMortars(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); - // MizziumMortars deals 4 damage to target creature you don't control. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.getSpellAbility().addEffect(new DamageTargetEffect(4)); // Overload {3}{R}{R}{R} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.") - this.addAbility(new OverloadAbility(this, new DamageAllEffect(4, filter), new ManaCostsImpl("{3}{R}{R}{R}"))); + this.addAbility(new OverloadAbility(this, new DamageAllEffect(4, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL), new ManaCostsImpl("{3}{R}{R}{R}"))); } - public MizziumMortars(final MizziumMortars card) { + private MizziumMortars(final MizziumMortars card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java b/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java index 07101bdc0d..72a2393d7b 100644 --- a/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java +++ b/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java @@ -13,7 +13,6 @@ import mage.game.events.GameEvent; import mage.players.ManaPoolItem; import mage.players.Player; import mage.util.CardUtil; - import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -26,7 +25,10 @@ public final class MnemonicBetrayal extends CardImpl { public MnemonicBetrayal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}{B}"); - // Exile all cards from all opponents' graveyards. You may cast those cards this turn, and you may spend mana as though it were mana of any type to cast those spells. At the beginning of the next end step, if any of those cards remain exiled, return them to their owner's graveyards. + // Exile all cards from all opponents' graveyards. You may cast those cards + // this turn, and you may spend mana as though it were mana of any type to cast + // those spells. At the beginning of the next end step, if any of those + // cards remain exiled, return them to their owner's graveyards. this.getSpellAbility().addEffect(new MnemonicBetrayalExileEffect()); // Exile Mnemonic Betrayal. @@ -72,7 +74,8 @@ class MnemonicBetrayalExileEffect extends OneShotEffect { } Cards cards = new CardsImpl(); Map cardMap = new HashMap<>(); - game.getOpponents(source.getControllerId()).stream().map((playerId) -> game.getPlayer(playerId)).filter((player) -> (player != null)).forEachOrdered((player) -> { + game.getOpponents(source.getControllerId()).stream().map((playerId) + -> game.getPlayer(playerId)).filter((player) -> (player != null)).forEachOrdered((player) -> { cards.addAll(player.getGraveyard()); }); cards.getCards(game).stream().map((card) -> { @@ -84,7 +87,8 @@ class MnemonicBetrayalExileEffect extends OneShotEffect { }).forEachOrdered((card) -> { game.addEffect(new MnemonicBetrayalAnyColorEffect(card, game), source); }); - controller.moveCardsToExile(cards.getCards(game), source, game, true, source.getSourceId(), source.getSourceObjectIfItStillExists(game).getName()); + controller.moveCardsToExile(cards.getCards(game), source, game, true, + source.getSourceId(), source.getSourceObjectIfItStillExists(game).getName()); game.addDelayedTriggeredAbility(new MnemonicBetrayalDelayedTriggeredAbility(cards, cardMap), source); return true; } @@ -125,7 +129,8 @@ class MnemonicBetrayalCastFromExileEffect extends AsThoughEffectImpl { } return objectId.equals(card.getId()) && card.getZoneChangeCounter(game) == zoneCounter - && affectedControllerId.equals(source.getControllerId()); + && affectedControllerId.equals(source.getControllerId()) + && !card.isLand(); // cast only not play } } diff --git a/Mage.Sets/src/mage/cards/m/MnemonicNexus.java b/Mage.Sets/src/mage/cards/m/MnemonicNexus.java index 8acec9890e..838cf76518 100644 --- a/Mage.Sets/src/mage/cards/m/MnemonicNexus.java +++ b/Mage.Sets/src/mage/cards/m/MnemonicNexus.java @@ -1,15 +1,12 @@ - package mage.cards.m; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; @@ -20,7 +17,7 @@ import mage.players.Player; public final class MnemonicNexus extends CardImpl { public MnemonicNexus(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}"); // Each player shuffles their graveyard into their library. this.getSpellAbility().addEffect(new MnemonicNexusEffect()); @@ -50,16 +47,13 @@ class MnemonicNexusEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player sourcePlayer = game.getPlayer(source.getControllerId()); - if(sourcePlayer == null){ + if (sourcePlayer == null) { return false; } - for (UUID playerId: game.getState().getPlayersInRange(sourcePlayer.getId(), game)) { + for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - for (Card card: player.getGraveyard().getCards(game)) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - player.shuffleLibrary(source, game); + player.shuffleCardsToLibrary(player.getGraveyard(), game, source); } } return true; @@ -70,4 +64,4 @@ class MnemonicNexusEffect extends OneShotEffect { return new MnemonicNexusEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/m/MnemonicSliver.java b/Mage.Sets/src/mage/cards/m/MnemonicSliver.java index bd7ce768d5..1e3dc66c4a 100644 --- a/Mage.Sets/src/mage/cards/m/MnemonicSliver.java +++ b/Mage.Sets/src/mage/cards/m/MnemonicSliver.java @@ -1,11 +1,10 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -13,11 +12,13 @@ import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; +import java.util.UUID; + /** * @author Loki */ @@ -29,8 +30,10 @@ public final class MnemonicSliver extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); + Cost cost = new SacrificeSourceCost(); + cost.setText("sacrifice this permanent"); Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new GenericManaCost(2)); - gainedAbility.addCost(new SacrificeSourceCost()); + gainedAbility.addCost(cost); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(gainedAbility, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, false))); } diff --git a/Mage.Sets/src/mage/cards/m/MoggBombers.java b/Mage.Sets/src/mage/cards/m/MoggBombers.java index b0635fc2eb..1bd116ccdd 100644 --- a/Mage.Sets/src/mage/cards/m/MoggBombers.java +++ b/Mage.Sets/src/mage/cards/m/MoggBombers.java @@ -1,60 +1,60 @@ -package mage.cards.m; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.SacrificeSourceEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; -import mage.target.TargetPlayer; - -/** - * - * @author jeffwadsworth - */ -public final class MoggBombers extends CardImpl { - - private static final String rule = "When another creature enters the battlefield, sacrifice {this} and it deals 3 damage to target player."; - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature"); - - static { - filter.add(AnotherPredicate.instance); - } - - public MoggBombers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); - - this.subtype.add(SubType.GOBLIN); - this.power = new MageInt(3); - this.toughness = new MageInt(4); - - // When another creature enters the battlefield, sacrifice Mogg Bombers and it deals 3 damage to target player. - Effect sacrificeMoggBombers = new SacrificeSourceEffect(); - Effect damageTargetPlayer = new DamageTargetEffect(3); - Ability ability = new EntersBattlefieldAllTriggeredAbility( - Zone.BATTLEFIELD, - sacrificeMoggBombers, - filter, false, rule); - ability.addEffect(damageTargetPlayer); - ability.addTarget(new TargetPlayer()); - this.addAbility(ability); - - } - - public MoggBombers(final MoggBombers card) { - super(card); - } - - @Override - public MoggBombers copy() { - return new MoggBombers(this); - } -} +package mage.cards.m; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.target.TargetPlayer; + +/** + * + * @author jeffwadsworth + */ +public final class MoggBombers extends CardImpl { + + private static final String rule = "When another creature enters the battlefield, sacrifice {this} and it deals 3 damage to target player."; + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public MoggBombers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When another creature enters the battlefield, sacrifice Mogg Bombers and it deals 3 damage to target player. + Effect sacrificeMoggBombers = new SacrificeSourceEffect(); + Effect damageTargetPlayer = new DamageTargetEffect(3); + Ability ability = new EntersBattlefieldAllTriggeredAbility( + Zone.BATTLEFIELD, + sacrificeMoggBombers, + filter, false, rule); + ability.addEffect(damageTargetPlayer); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + + } + + public MoggBombers(final MoggBombers card) { + super(card); + } + + @Override + public MoggBombers copy() { + return new MoggBombers(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MogissWarhound.java b/Mage.Sets/src/mage/cards/m/MogissWarhound.java index 3ac5c7cc25..76b79af5e0 100644 --- a/Mage.Sets/src/mage/cards/m/MogissWarhound.java +++ b/Mage.Sets/src/mage/cards/m/MogissWarhound.java @@ -26,7 +26,7 @@ public final class MogissWarhound extends CardImpl { public MogissWarhound(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/m/Molder.java b/Mage.Sets/src/mage/cards/m/Molder.java index cffc75b81a..07c7518a4a 100644 --- a/Mage.Sets/src/mage/cards/m/Molder.java +++ b/Mage.Sets/src/mage/cards/m/Molder.java @@ -27,7 +27,7 @@ public final class Molder extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{G}"); // Destroy target artifact or enchantment with converted mana cost X. It can't be regenerated. You gain X life. - this.getSpellAbility().addEffect(new DestroyTargetEffect("Destroy target artifact or enchantment with converted mana cost X.", true)); + this.getSpellAbility().addEffect(new DestroyTargetEffect("Destroy target artifact or enchantment with converted mana cost X", true)); this.getSpellAbility().addEffect(new GainLifeEffect(ManacostVariableValue.instance)); this.getSpellAbility().setTargetAdjuster(MolderAdjuster.instance); } diff --git a/Mage.Sets/src/mage/cards/m/Molderhulk.java b/Mage.Sets/src/mage/cards/m/Molderhulk.java index 9f65632312..9f38fec52a 100644 --- a/Mage.Sets/src/mage/cards/m/Molderhulk.java +++ b/Mage.Sets/src/mage/cards/m/Molderhulk.java @@ -1,25 +1,28 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; -import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect; -import mage.constants.SubType; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityWord; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterLandCard; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class Molderhulk extends CardImpl { @@ -36,8 +39,11 @@ public final class Molderhulk extends CardImpl { this.toughness = new MageInt(6); // Undergrowth — This spell costs {1} less to cast for each creature card in your graveyard. - Ability ability = new SimpleStaticAbility(Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(StaticFilters.FILTER_CARD_CREATURE)); + DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)); ability.setAbilityWord(AbilityWord.UNDERGROWTH); + ability.setRuleAtTheTop(true); + ability.addHint(new ValueHint("Creature card in your graveyard", xValue)); this.addAbility(ability); // When Molderhulk enters the battlefield, return target land card from your graveyard to the battlefield. diff --git a/Mage.Sets/src/mage/cards/m/MoldgrafMonstrosity.java b/Mage.Sets/src/mage/cards/m/MoldgrafMonstrosity.java index 8cbb8e4271..90ee145c1c 100644 --- a/Mage.Sets/src/mage/cards/m/MoldgrafMonstrosity.java +++ b/Mage.Sets/src/mage/cards/m/MoldgrafMonstrosity.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSourceEffect; @@ -39,7 +39,7 @@ public final class MoldgrafMonstrosity extends CardImpl { // When Moldgraf Monstrosity dies, exile it, then return two creature cards at random from your graveyard to the battlefield. Effect effect = new ExileSourceEffect(); effect.setText(""); - DiesTriggeredAbility ability = new DiesTriggeredAbility(effect); + DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(effect); ability.addEffect(new MoldgrafMonstrosityEffect()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MoltenEchoes.java b/Mage.Sets/src/mage/cards/m/MoltenEchoes.java new file mode 100644 index 0000000000..446c6aab06 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoltenEchoes.java @@ -0,0 +1,99 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseCreatureTypeEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ChosenSubtypePredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * + * @author ciaccona007 + */ +public final class MoltenEchoes extends CardImpl { + + public MoltenEchoes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}{R}"); + + // As Molten Echoes enters the battlefield, choose a creature type. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.Copy))); + + // Whenever a nontoken creature of the chosen type enters the battlefield under your control, create a token that's a copy of that creature. That token gains haste. Exile it at the beginning of the next end step. + FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("nontoken creature of the chosen type"); + filter.add(Predicates.not(TokenPredicate.instance)); + filter.add(ChosenSubtypePredicate.instance); + + Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new MoltenEchoesEffect(), + filter, false, SetTargetPointer.PERMANENT, + "Whenever a nontoken creature of the chosen type enters the battlefield under your control, " + + "create a token that's a copy of that creature. " + + "That token gains haste. Exile it at the beginning of the next end step."); + this.addAbility(ability); + } + + private MoltenEchoes(final MoltenEchoes card) { + super(card); + } + + @Override + public MoltenEchoes copy() { + return new MoltenEchoes(this); + } +} + +class MoltenEchoesEffect extends OneShotEffect { + + public MoltenEchoesEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "create a token that's a copy of that creature. That token gains haste. Exile it at the beginning of the next end step"; + } + + public MoltenEchoesEffect(final MoltenEchoesEffect effect) { + super(effect); + } + + @Override + public MoltenEchoesEffect copy() { + return new MoltenEchoesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (permanent != null) { + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true); + effect.setTargetPointer(getTargetPointer()); + if (effect.apply(game, source)) { + for (Permanent tokenPermanent : effect.getAddedPermanent()) { + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + game.addDelayedTriggeredAbility(delayedAbility, source); + } + return true; + } + } + + return false; + } +} + diff --git a/Mage.Sets/src/mage/cards/m/MoltenFirebird.java b/Mage.Sets/src/mage/cards/m/MoltenFirebird.java index 81001ce6ab..d89a6837bc 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenFirebird.java +++ b/Mage.Sets/src/mage/cards/m/MoltenFirebird.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -32,7 +32,7 @@ public final class MoltenFirebird extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Molten Firebird dies, return it to the battlefield under its owner’s control at the beginning of the next end step and you skip your next draw step. - Ability ability = new DiesTriggeredAbility(new CreateDelayedTriggeredAbilityEffect( + Ability ability = new DiesSourceTriggeredAbility(new CreateDelayedTriggeredAbilityEffect( new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect()))); ability.addEffect(new SkipNextDrawStepControllerEffect()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MoltenPsyche.java b/Mage.Sets/src/mage/cards/m/MoltenPsyche.java index cd0acb81ab..642ff01897 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenPsyche.java +++ b/Mage.Sets/src/mage/cards/m/MoltenPsyche.java @@ -3,12 +3,10 @@ package mage.cards.m; import mage.abilities.Ability; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.WatcherScope; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; @@ -31,6 +29,8 @@ public final class MoltenPsyche extends CardImpl { // Metalcraft — If you control three or more artifacts, Molten Psyche deals damage to each opponent equal to the number of cards that player has drawn this turn. this.getSpellAbility().addEffect(new MoltenPsycheEffect()); this.getSpellAbility().addWatcher(new MoltenPsycheWatcher()); + this.getSpellAbility().setAbilityWord(AbilityWord.METALCRAFT); + this.getSpellAbility().addHint(MetalcraftHint.instance); } public MoltenPsyche(final MoltenPsyche card) { @@ -73,12 +73,12 @@ class MoltenPsycheEffect extends OneShotEffect { } } - game.applyEffects(); // so effects from creatures that were on the battlefield won't trigger from draw action + game.getState().processAction(game); // so effects from creatures that were on the battlefield won't trigger from draw action for (UUID playerId : cardsToDraw.keySet()) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(cardsToDraw.get(playerId), game); + player.drawCards(cardsToDraw.get(playerId), source.getSourceId(), game); } } if (MetalcraftCondition.instance.apply(game, source)) { diff --git a/Mage.Sets/src/mage/cards/m/MoltenSlagheap.java b/Mage.Sets/src/mage/cards/m/MoltenSlagheap.java index 5d339c274a..7bdc03fbdd 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenSlagheap.java +++ b/Mage.Sets/src/mage/cards/m/MoltenSlagheap.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -7,9 +6,10 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; -import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ import mage.counters.CounterType; public final class MoltenSlagheap extends CardImpl { public MoltenSlagheap(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {tap}: Add {C}. this.addAbility(new ColorlessManaAbility()); @@ -36,7 +36,8 @@ public final class MoltenSlagheap extends CardImpl { this.addAbility(ability); // {1}, Remove X storage counters from Molten Slagheap: Add X mana in any combination of {B} and/or {R}. ability = new SimpleManaAbility(Zone.BATTLEFIELD, - new AddManaInAnyCombinationEffect(RemovedCountersForCostValue.instance, ColoredManaSymbol.B, ColoredManaSymbol.R), + new AddManaInAnyCombinationEffect(RemovedCountersForCostValue.instance, + new CountersSourceCount(CounterType.STORAGE), ColoredManaSymbol.B, ColoredManaSymbol.R), new GenericManaCost(1)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MomentaryBlink.java b/Mage.Sets/src/mage/cards/m/MomentaryBlink.java index cbc83608a6..281283eb49 100644 --- a/Mage.Sets/src/mage/cards/m/MomentaryBlink.java +++ b/Mage.Sets/src/mage/cards/m/MomentaryBlink.java @@ -1,11 +1,8 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; -import mage.abilities.effects.common.ReturnToBattlefieldUnderYourControlTargetEffect; import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -13,18 +10,20 @@ import mage.constants.CardType; import mage.constants.TimingRule; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** * @author noxx */ public final class MomentaryBlink extends CardImpl { public MomentaryBlink(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); // Exile target creature you control, then return it to the battlefield under its owner's control. this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addEffect(new ExileTargetForSourceEffect()); - this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); // Flashback {3}{U} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{3}{U}"), TimingRule.INSTANT)); diff --git a/Mage.Sets/src/mage/cards/m/MomentousFall.java b/Mage.Sets/src/mage/cards/m/MomentousFall.java index 0685bdda5f..05a9442678 100644 --- a/Mage.Sets/src/mage/cards/m/MomentousFall.java +++ b/Mage.Sets/src/mage/cards/m/MomentousFall.java @@ -68,7 +68,7 @@ class MomentousFallEffect extends OneShotEffect { } } if (power > 0) { - controller.drawCards(power, game); + controller.drawCards(power, source.getSourceId(), game); } if (toughness > 0) { controller.gainLife(toughness, game, source); diff --git a/Mage.Sets/src/mage/cards/m/MomentumRumbler.java b/Mage.Sets/src/mage/cards/m/MomentumRumbler.java new file mode 100644 index 0000000000..b6875666aa --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MomentumRumbler.java @@ -0,0 +1,80 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MomentumRumbler extends CardImpl { + + public MomentumRumbler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever Momentum Rumbler attacks, if it doesn't have first strike, put a first strike counter on it. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new AttacksTriggeredAbility(new AddCountersSourceEffect( + CounterType.FIRST_STRIKE.createInstance() + ), false), MomentumRumblerCondition.FALSE, "Whenever {this} attacks, " + + "if it doesn't have first strike, put a first strike counter on it." + )); + + // Whenever Momentum Rumbler attacks, if it has first strike, it gains double strike until end of turn. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new AttacksTriggeredAbility(new GainAbilitySourceEffect( + DoubleStrikeAbility.getInstance(), Duration.EndOfTurn + ), false), MomentumRumblerCondition.TRUE, "Whenever {this} attacks, " + + "if it has first strike, it gains double strike until end of turn." + )); + } + + private MomentumRumbler(final MomentumRumbler card) { + super(card); + } + + @Override + public MomentumRumbler copy() { + return new MomentumRumbler(this); + } +} + +enum MomentumRumblerCondition implements Condition { + TRUE(true), + FALSE(false); + + private final boolean hasAbility; + + MomentumRumblerCondition(boolean hasAbility) { + this.hasAbility = hasAbility; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (permanent == null) { + return false; + } + return hasAbility == permanent.hasAbility(FirstStrikeAbility.getInstance(), game); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MonasterySiege.java b/Mage.Sets/src/mage/cards/m/MonasterySiege.java index 162abdd40c..f72fc379d9 100644 --- a/Mage.Sets/src/mage/cards/m/MonasterySiege.java +++ b/Mage.Sets/src/mage/cards/m/MonasterySiege.java @@ -1,8 +1,6 @@ - package mage.cards.m; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.common.BeginningOfDrawTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; @@ -17,19 +15,20 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.Target; +import mage.game.stack.Spell; +import mage.players.Player; import mage.util.CardUtil; +import java.util.Set; import java.util.UUID; /** - * * @author emerald000 */ public final class MonasterySiege extends CardImpl { public MonasterySiege(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); // As Monastery Siege enters the battlefield, choose Khans or Dragons. this.addAbility(new EntersBattlefieldAbility(new ChooseModeEffect("Khans or Dragons?", "Khans", "Dragons"), null, @@ -57,6 +56,8 @@ public final class MonasterySiege extends CardImpl { class MonasterySiegeCostIncreaseEffect extends CostModificationEffectImpl { + private static ModeChoiceSourceCondition modeDragons = new ModeChoiceSourceCondition("Dragons"); + MonasterySiegeCostIncreaseEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); staticText = "• Dragons — Spells your opponents cast that target you or a permanent you control cost {2} more to cast"; @@ -68,33 +69,55 @@ class MonasterySiegeCostIncreaseEffect extends CostModificationEffectImpl { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -2); + CardUtil.increaseCost(abilityToModify, 2); return true; } @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (new ModeChoiceSourceCondition("Dragons").apply(game, source)) { - if (abilityToModify instanceof SpellAbility) { - if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - if (targetUUID.equals(source.getControllerId())) { - return true; - } - Permanent permanent = game.getPermanent(targetUUID); - if (permanent != null && permanent.isControlledBy(source.getControllerId())) { - return true; - } - } - } - } - } - } + if (!modeDragons.apply(game, source)) { + return false; } + + if (!(abilityToModify instanceof SpellAbility)) { + return false; + } + + if (!game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { + return false; + } + + Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); + Set allTargets; + if (spell != null) { + // real cast + allTargets = CardUtil.getAllSelectedTargets(abilityToModify, game); + } else { + // playable + allTargets = CardUtil.getAllPossibleTargets(abilityToModify, game); + + // can target without cost increase + if (allTargets.stream().anyMatch(target -> !isTargetCompatible(target, source, game))) { + return false; + } + ; + } + + return allTargets.stream().anyMatch(target -> isTargetCompatible(target, source, game)); + } + + private boolean isTargetCompatible(UUID target, Ability source, Game game) { + // target you or a permanent you control + Player targetPlayer = game.getPlayer(target); + if (targetPlayer != null && targetPlayer.getId().equals(source.getControllerId())) { + return true; + } + + Permanent targetPermanent = game.getPermanent(target); + if (targetPermanent != null && targetPermanent.isControlledBy(source.getControllerId())) { + return true; + } + return false; } diff --git a/Mage.Sets/src/mage/cards/m/MongrelPack.java b/Mage.Sets/src/mage/cards/m/MongrelPack.java index e2a0f56e40..94d40ab16d 100644 --- a/Mage.Sets/src/mage/cards/m/MongrelPack.java +++ b/Mage.Sets/src/mage/cards/m/MongrelPack.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -13,18 +11,18 @@ import mage.constants.TurnPhase; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.token.TokenImpl; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.GreenDogToken; + +import java.util.UUID; /** - * * @author North */ public final class MongrelPack extends CardImpl { public MongrelPack(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); - this.subtype.add(SubType.HOUND); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + this.subtype.add(SubType.DOG); this.power = new MageInt(4); this.toughness = new MageInt(1); @@ -33,7 +31,7 @@ public final class MongrelPack extends CardImpl { this.addAbility(new MongrelPackAbility()); } - public MongrelPack(final MongrelPack card) { + private MongrelPack(final MongrelPack card) { super(card); } @@ -45,11 +43,11 @@ public final class MongrelPack extends CardImpl { class MongrelPackAbility extends ZoneChangeTriggeredAbility { - public MongrelPackAbility() { - super(Zone.BATTLEFIELD, Zone.GRAVEYARD, new CreateTokenEffect(new HoundToken(), 4), "When {this} dies during combat, ", false); + MongrelPackAbility() { + super(Zone.BATTLEFIELD, Zone.GRAVEYARD, new CreateTokenEffect(new GreenDogToken(), 4), "When {this} dies during combat, ", false); } - public MongrelPackAbility(MongrelPackAbility ability) { + private MongrelPackAbility(MongrelPackAbility ability) { super(ability); } @@ -68,24 +66,3 @@ class MongrelPackAbility extends ZoneChangeTriggeredAbility { return false; } } - -class HoundToken extends TokenImpl { - - public HoundToken() { - super("Hound", "1/1 green Hound creature token"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.HOUND); - - color.setGreen(true); - power = new MageInt(1); - toughness = new MageInt(1); - } - - public HoundToken(final HoundToken token) { - super(token); - } - - public HoundToken copy() { - return new HoundToken(this); - } -} diff --git a/Mage.Sets/src/mage/cards/m/MonkeyCage.java b/Mage.Sets/src/mage/cards/m/MonkeyCage.java index 4f33c49874..3342f74e5b 100644 --- a/Mage.Sets/src/mage/cards/m/MonkeyCage.java +++ b/Mage.Sets/src/mage/cards/m/MonkeyCage.java @@ -61,7 +61,7 @@ class MonkeyCageEffect extends OneShotEffect { } public boolean apply(Game game, Ability source) { - Permanent creature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (creature != null) { int cmc = creature.getConvertedManaCost(); return new CreateTokenEffect(new ApeToken(), cmc).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/m/Monomania.java b/Mage.Sets/src/mage/cards/m/Monomania.java index 8e27d39857..86f4ac069f 100644 --- a/Mage.Sets/src/mage/cards/m/Monomania.java +++ b/Mage.Sets/src/mage/cards/m/Monomania.java @@ -1,37 +1,35 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPlayer; +import mage.target.common.TargetDiscard; + +import java.util.UUID; /** - * * @author nantuko */ public final class Monomania extends CardImpl { public Monomania(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}"); // Target player chooses a card in their hand and discards the rest. this.getSpellAbility().addEffect(new MonomaniaEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); } - public Monomania(final Monomania card) { + private Monomania(final Monomania card) { super(card); } @@ -45,41 +43,30 @@ class MonomaniaEffect extends OneShotEffect { private static final FilterCard filter = new FilterCard("a card"); - public MonomaniaEffect() { + MonomaniaEffect() { super(Outcome.Discard); staticText = "Target player chooses a card in their hand and discards the rest"; } - public MonomaniaEffect(final MonomaniaEffect effect) { + private MonomaniaEffect(final MonomaniaEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - TargetCard target = new TargetCard(Zone.HAND, filter); - if (player.choose(Outcome.Detriment, player.getHand(), target, game)) { - while (player.getHand().size() > 1) { - for (UUID uuid : player.getHand()) { - if (!uuid.equals(target.getFirstTarget())) { - Card card = player.getHand().get(uuid, game); - if (card != null) { - player.discard(card, source, game); - break; - } - } - } - } - return true; - } + if (player == null || player.getHand().isEmpty()) { + return false; } - return false; + TargetCard target = new TargetDiscard(player.getId()); + player.choose(Outcome.Benefit, player.getHand(), target, game); + Cards cards = player.getHand().copy(); + cards.removeIf(target.getTargets()::contains); + return !player.discard(cards, source, game).isEmpty(); } @Override public MonomaniaEffect copy() { return new MonomaniaEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/m/MonstrousHound.java b/Mage.Sets/src/mage/cards/m/MonstrousHound.java index cdebb8caa7..967df9d7f0 100644 --- a/Mage.Sets/src/mage/cards/m/MonstrousHound.java +++ b/Mage.Sets/src/mage/cards/m/MonstrousHound.java @@ -1,138 +1,138 @@ -package mage.cards.m; - -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.RestrictionEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledLandPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class MonstrousHound extends CardImpl { - - public MonstrousHound(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); - - this.subtype.add(SubType.HOUND); - this.power = new MageInt(4); - this.toughness = new MageInt(4); - - // Monstrous Hound can't attack unless you control more lands than defending player. - Effect effect = new CantAttackUnlessControllerControlsMoreLandsEffect(); - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - effect.setText("{this} can't attack unless you control more lands than defending player"))); - - // Monstrous Hound can't block unless you control more lands than attacking player. - Effect effect2 = new CantBlockUnlessControllerControlsMoreLandsEffect(); - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - effect2.setText("{this} can't block unless you control more lands than attacking player"))); - - } - - public MonstrousHound(final MonstrousHound card) { - super(card); - } - - @Override - public MonstrousHound copy() { - return new MonstrousHound(this); - } -} - -class CantAttackUnlessControllerControlsMoreLandsEffect extends RestrictionEffect { - - CantAttackUnlessControllerControlsMoreLandsEffect() { - super(Duration.WhileOnBattlefield); - } - - CantAttackUnlessControllerControlsMoreLandsEffect(final CantAttackUnlessControllerControlsMoreLandsEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return permanent.getId().equals(source.getSourceId()); - } - - @Override - public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { - if (defenderId == null) { - return true; - } - - UUID defendingPlayerId; - Player defender = game.getPlayer(defenderId); - if (defender == null) { - Permanent permanent = game.getPermanent(defenderId); - if (permanent != null) { - defendingPlayerId = permanent.getControllerId(); - } else { - return false; - } - } else { - defendingPlayerId = defenderId; - } - if (defendingPlayerId != null) { - return game.getBattlefield().countAll(new FilterControlledLandPermanent(), - source.getControllerId(), game) > game.getBattlefield().countAll(new FilterControlledLandPermanent(), - defendingPlayerId, game); - } else { - return true; - } - } - - @Override - public CantAttackUnlessControllerControlsMoreLandsEffect copy() { - return new CantAttackUnlessControllerControlsMoreLandsEffect(this); - } -} - -class CantBlockUnlessControllerControlsMoreLandsEffect extends RestrictionEffect { - - CantBlockUnlessControllerControlsMoreLandsEffect() { - super(Duration.WhileOnBattlefield); - } - - CantBlockUnlessControllerControlsMoreLandsEffect(final CantBlockUnlessControllerControlsMoreLandsEffect effect) { - super(effect); - } - - @Override - public CantBlockUnlessControllerControlsMoreLandsEffect copy() { - return new CantBlockUnlessControllerControlsMoreLandsEffect(this); - } - - @Override - public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { - if (attacker == null) { - return true; - } - UUID attackingPlayerId = attacker.getControllerId(); - if (attackingPlayerId != null) { - return game.getBattlefield().countAll(new FilterControlledLandPermanent(), - source.getControllerId(), game) > game.getBattlefield().countAll(new FilterControlledLandPermanent(), - attackingPlayerId, game); - } - return true; - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return permanent.getId().equals(source.getSourceId()); - } -} +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.RestrictionEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.common.FilterControlledLandPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class MonstrousHound extends CardImpl { + + public MonstrousHound(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.DOG); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Monstrous Hound can't attack unless you control more lands than defending player. + Effect effect = new CantAttackUnlessControllerControlsMoreLandsEffect(); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + effect.setText("{this} can't attack unless you control more lands than defending player"))); + + // Monstrous Hound can't block unless you control more lands than attacking player. + Effect effect2 = new CantBlockUnlessControllerControlsMoreLandsEffect(); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + effect2.setText("{this} can't block unless you control more lands than attacking player"))); + + } + + public MonstrousHound(final MonstrousHound card) { + super(card); + } + + @Override + public MonstrousHound copy() { + return new MonstrousHound(this); + } +} + +class CantAttackUnlessControllerControlsMoreLandsEffect extends RestrictionEffect { + + CantAttackUnlessControllerControlsMoreLandsEffect() { + super(Duration.WhileOnBattlefield); + } + + CantAttackUnlessControllerControlsMoreLandsEffect(final CantAttackUnlessControllerControlsMoreLandsEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.getId().equals(source.getSourceId()); + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { + if (defenderId == null) { + return true; + } + + UUID defendingPlayerId; + Player defender = game.getPlayer(defenderId); + if (defender == null) { + Permanent permanent = game.getPermanent(defenderId); + if (permanent != null) { + defendingPlayerId = permanent.getControllerId(); + } else { + return false; + } + } else { + defendingPlayerId = defenderId; + } + if (defendingPlayerId != null) { + return game.getBattlefield().countAll(new FilterControlledLandPermanent(), + source.getControllerId(), game) > game.getBattlefield().countAll(new FilterControlledLandPermanent(), + defendingPlayerId, game); + } else { + return true; + } + } + + @Override + public CantAttackUnlessControllerControlsMoreLandsEffect copy() { + return new CantAttackUnlessControllerControlsMoreLandsEffect(this); + } +} + +class CantBlockUnlessControllerControlsMoreLandsEffect extends RestrictionEffect { + + CantBlockUnlessControllerControlsMoreLandsEffect() { + super(Duration.WhileOnBattlefield); + } + + CantBlockUnlessControllerControlsMoreLandsEffect(final CantBlockUnlessControllerControlsMoreLandsEffect effect) { + super(effect); + } + + @Override + public CantBlockUnlessControllerControlsMoreLandsEffect copy() { + return new CantBlockUnlessControllerControlsMoreLandsEffect(this); + } + + @Override + public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { + if (attacker == null) { + return true; + } + UUID attackingPlayerId = attacker.getControllerId(); + if (attackingPlayerId != null) { + return game.getBattlefield().countAll(new FilterControlledLandPermanent(), + source.getControllerId(), game) > game.getBattlefield().countAll(new FilterControlledLandPermanent(), + attackingPlayerId, game); + } + return true; + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.getId().equals(source.getSourceId()); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MonstrousStep.java b/Mage.Sets/src/mage/cards/m/MonstrousStep.java new file mode 100644 index 0000000000..3f24695155 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MonstrousStep.java @@ -0,0 +1,104 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.RequirementEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.BlockedAttackerWatcher; + +import java.util.UUID; + +/** + * @author drowinternet + */ +public final class MonstrousStep extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("another creature (must block this turn)"); + + static { + filter.add(new AnotherTargetPredicate(2)); + } + + public MonstrousStep(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}"); + + // Target creature gets +7/+7 until end of turn. Up to one target creature blocks it this turn if able. + this.getSpellAbility().addEffect(new BoostTargetEffect(7, 7)); + this.getSpellAbility().addEffect(new MonstrousStepEffect()); + this.getSpellAbility().addWatcher(new BlockedAttackerWatcher()); + + TargetPermanent target = new TargetCreaturePermanent(); + target.setTargetTag(1); + this.getSpellAbility().addTarget(target); + target = new TargetPermanent(0, 1, filter, false); + target.setTargetTag(2); + this.getSpellAbility().addTarget(target); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private MonstrousStep(final MonstrousStep card) { + super(card); + } + + @Override + public MonstrousStep copy() { + return new MonstrousStep(this); + } +} + +class MonstrousStepEffect extends RequirementEffect { + + MonstrousStepEffect() { + super(Duration.EndOfTurn); + staticText = "Up to one other target creature blocks it this turn if able"; + } + + private MonstrousStepEffect(final MonstrousStepEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + BlockedAttackerWatcher watcher = game.getState().getWatcher(BlockedAttackerWatcher.class); + return permanent != null + && watcher != null + && !watcher.creatureHasBlockedAttacker(game.getPermanent(source.getFirstTarget()), permanent, game) + && permanent.getId().equals(source.getTargets().get(1).getFirstTarget()) + && permanent.canBlock(source.getFirstTarget(), game); + } + + @Override + public boolean mustAttack(Game game) { + return false; + } + + @Override + public boolean mustBlock(Game game) { + return true; + } + + @Override + public UUID mustBlockAttacker(Ability source, Game game) { + return source.getFirstTarget(); + } + + @Override + public MonstrousStepEffect copy() { + return new MonstrousStepEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoonEatingDog.java b/Mage.Sets/src/mage/cards/m/MoonEatingDog.java index 1467030f59..d5d2a004f1 100644 --- a/Mage.Sets/src/mage/cards/m/MoonEatingDog.java +++ b/Mage.Sets/src/mage/cards/m/MoonEatingDog.java @@ -30,7 +30,7 @@ public final class MoonEatingDog extends CardImpl { public MoonEatingDog(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/m/MoonlightHunt.java b/Mage.Sets/src/mage/cards/m/MoonlightHunt.java index a2c39505f9..67aa8308e1 100644 --- a/Mage.Sets/src/mage/cards/m/MoonlightHunt.java +++ b/Mage.Sets/src/mage/cards/m/MoonlightHunt.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -9,7 +7,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.game.Game; @@ -17,27 +15,22 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MoonlightHunt extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public MoonlightHunt(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Choose target creature you don't control. Each creature you control that's a Wolf or Werewolf deals damage equal to its power to that creature. this.getSpellAbility().addEffect(new MoonlightHuntEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public MoonlightHunt(final MoonlightHunt card) { + private MoonlightHunt(final MoonlightHunt card) { super(card); } @@ -55,12 +48,12 @@ class MoonlightHuntEffect extends OneShotEffect { filter.add(Predicates.or(SubType.WOLF.getPredicate(), SubType.WEREWOLF.getPredicate())); } - public MoonlightHuntEffect() { + MoonlightHuntEffect() { super(Outcome.Damage); this.staticText = "Choose target creature you don't control. Each creature you control that's a Wolf or Werewolf deals damage equal to its power to that creature"; } - public MoonlightHuntEffect(final MoonlightHuntEffect effect) { + private MoonlightHuntEffect(final MoonlightHuntEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/m/MorselTheft.java b/Mage.Sets/src/mage/cards/m/MorselTheft.java index 4573e94d4a..d56a4e25dc 100644 --- a/Mage.Sets/src/mage/cards/m/MorselTheft.java +++ b/Mage.Sets/src/mage/cards/m/MorselTheft.java @@ -1,13 +1,12 @@ - package mage.cards.m; -import java.util.UUID; -import mage.abilities.condition.common.ProwlCondition; +import mage.abilities.condition.common.ProwlCostWasPaidCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.hint.common.ProwlCostWasPaidHint; import mage.abilities.keyword.ProwlAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -15,14 +14,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MorselTheft extends CardImpl { public MorselTheft(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.SORCERY},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.SORCERY}, "{2}{B}{B}"); this.subtype.add(SubType.ROGUE); // Prowl {1}{B} @@ -34,7 +34,8 @@ public final class MorselTheft extends CardImpl { effect.setText("and you gain 3 life"); getSpellAbility().addEffect(effect); getSpellAbility().addTarget(new TargetPlayer()); - getSpellAbility().addEffect(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), ProwlCondition.instance)); + getSpellAbility().addEffect(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), ProwlCostWasPaidCondition.instance)); + getSpellAbility().addHint(ProwlCostWasPaidHint.instance); } diff --git a/Mage.Sets/src/mage/cards/m/Morselhoarder.java b/Mage.Sets/src/mage/cards/m/Morselhoarder.java index f78948cad3..709035548e 100644 --- a/Mage.Sets/src/mage/cards/m/Morselhoarder.java +++ b/Mage.Sets/src/mage/cards/m/Morselhoarder.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -7,8 +6,9 @@ import mage.Mana; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.RemoveCountersSourceCost; -import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,17 +24,17 @@ import mage.counters.CounterType; public final class Morselhoarder extends CardImpl { public Morselhoarder(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R/G}{R/G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R/G}{R/G}"); this.subtype.add(SubType.ELEMENTAL); this.power = new MageInt(6); this.toughness = new MageInt(4); // Morselhoarder enters the battlefield with two -1/-1 counters on it. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance(2), false))); - + // Remove a -1/-1 counter from Morselhoarder: Add one mana of any color. this.addAbility(new MorselhoarderAbility()); - + } public Morselhoarder(final Morselhoarder card) { @@ -48,13 +48,14 @@ public final class Morselhoarder extends CardImpl { } class MorselhoarderAbility extends ActivatedManaAbilityImpl { + public MorselhoarderAbility() { this(new RemoveCountersSourceCost(CounterType.M1M1.createInstance())); } public MorselhoarderAbility(Cost cost) { - super(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(), cost); - this.netMana.add(new Mana(0,0,0,0,0,0,1, 0)); + super(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(1, new CountersSourceCount(CounterType.M1M1), false), cost); + this.netMana.add(new Mana(0, 0, 0, 0, 0, 0, 1, 0)); } public MorselhoarderAbility(final MorselhoarderAbility ability) { diff --git a/Mage.Sets/src/mage/cards/m/MortisDogs.java b/Mage.Sets/src/mage/cards/m/MortisDogs.java index 310a8b8867..452a6319c5 100644 --- a/Mage.Sets/src/mage/cards/m/MortisDogs.java +++ b/Mage.Sets/src/mage/cards/m/MortisDogs.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -24,7 +24,7 @@ public final class MortisDogs extends CardImpl { public MortisDogs(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -32,7 +32,7 @@ public final class MortisDogs extends CardImpl { // Whenever Mortis Dogs attacks, it gets +2/+0 until end of turn. this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn), false)); // When Mortis Dogs dies, target player loses life equal to its power. - Ability ability = new DiesTriggeredAbility(new LoseLifeTargetEffect(new SourcePermanentPowerCount(false))); + Ability ability = new DiesSourceTriggeredAbility(new LoseLifeTargetEffect(new SourcePermanentPowerCount(false))); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MortuaryMire.java b/Mage.Sets/src/mage/cards/m/MortuaryMire.java index 841529273d..17d2473647 100644 --- a/Mage.Sets/src/mage/cards/m/MortuaryMire.java +++ b/Mage.Sets/src/mage/cards/m/MortuaryMire.java @@ -26,7 +26,7 @@ public final class MortuaryMire extends CardImpl { this.addAbility(new EntersBattlefieldTappedAbility()); // When Mortuary Mire enters the battlefield, you may put target creature card from your graveyard on top of your library. - Ability ability = new EntersBattlefieldTriggeredAbility(new PutOnLibraryTargetEffect(true), true); + Ability ability = new EntersBattlefieldTriggeredAbility(new PutOnLibraryTargetEffect(true).setText("put target creature card from your graveyard on top of your library"), true); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MortusStrider.java b/Mage.Sets/src/mage/cards/m/MortusStrider.java index 664bd3ea85..fb7cd29490 100644 --- a/Mage.Sets/src/mage/cards/m/MortusStrider.java +++ b/Mage.Sets/src/mage/cards/m/MortusStrider.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class MortusStrider extends CardImpl { this.toughness = new MageInt(1); // When Mortus Strider dies, return it to its owner's hand. - this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect(false))); + this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect(false))); } public MortusStrider(final MortusStrider card) { diff --git a/Mage.Sets/src/mage/cards/m/MossbridgeTroll.java b/Mage.Sets/src/mage/cards/m/MossbridgeTroll.java index 2218db67e6..554082e569 100644 --- a/Mage.Sets/src/mage/cards/m/MossbridgeTroll.java +++ b/Mage.Sets/src/mage/cards/m/MossbridgeTroll.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,11 +10,7 @@ import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; @@ -27,8 +21,9 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class MossbridgeTroll extends CardImpl { @@ -81,7 +76,7 @@ class MossbridgeTrollReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent mossbridgeTroll = game.getPermanent(event.getTargetId()); if (mossbridgeTroll != null && event.getAmount() == 0) { // 1=noRegen - return mossbridgeTroll.regenerate(source.getSourceId(), game); + return mossbridgeTroll.regenerate(source, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/m/MosscoatGoriak.java b/Mage.Sets/src/mage/cards/m/MosscoatGoriak.java new file mode 100644 index 0000000000..fab878f2a6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MosscoatGoriak.java @@ -0,0 +1,36 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MosscoatGoriak extends CardImpl { + + public MosscoatGoriak(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + } + + private MosscoatGoriak(final MosscoatGoriak card) { + super(card); + } + + @Override + public MosscoatGoriak copy() { + return new MosscoatGoriak(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/Mossdog.java b/Mage.Sets/src/mage/cards/m/Mossdog.java index 6f3bff2827..b052767d07 100644 --- a/Mage.Sets/src/mage/cards/m/Mossdog.java +++ b/Mage.Sets/src/mage/cards/m/Mossdog.java @@ -24,7 +24,7 @@ public final class Mossdog extends CardImpl { public Mossdog(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); this.subtype.add(SubType.PLANT); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/m/MountainStronghold.java b/Mage.Sets/src/mage/cards/m/MountainStronghold.java index c89143fd1b..e96a5b764c 100644 --- a/Mage.Sets/src/mage/cards/m/MountainStronghold.java +++ b/Mage.Sets/src/mage/cards/m/MountainStronghold.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -15,8 +13,9 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author L_J */ public final class MountainStronghold extends CardImpl { @@ -32,7 +31,10 @@ public final class MountainStronghold extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Red legendary creatures you control have "bands with other legendary creatures." - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(new BandsWithOtherAbility(SuperType.LEGENDARY), Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect( + new BandsWithOtherAbility(SuperType.LEGENDARY), Duration.WhileOnBattlefield, filter) + .withForceQuotes() + )); } public MountainStronghold(final MountainStronghold card) { diff --git a/Mage.Sets/src/mage/cards/m/MouseDroid.java b/Mage.Sets/src/mage/cards/m/MouseDroid.java index 1d911de668..f3ee4dbd2f 100644 --- a/Mage.Sets/src/mage/cards/m/MouseDroid.java +++ b/Mage.Sets/src/mage/cards/m/MouseDroid.java @@ -2,7 +2,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.RepairAbility; import mage.constants.SubType; @@ -24,7 +24,7 @@ public final class MouseDroid extends CardImpl { this.toughness = new MageInt(1); // When Mouse Droid dies, draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1))); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1))); // Repair 3 this.addAbility(new RepairAbility(3)); diff --git a/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java b/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java index 946b515178..03ec484dcc 100644 --- a/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java +++ b/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java @@ -25,7 +25,7 @@ public final class MowuLoyalCompanion extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); this.addSuperType(SuperType.LEGENDARY); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/m/MoxAmber.java b/Mage.Sets/src/mage/cards/m/MoxAmber.java index ae6d86bd9d..83093f734d 100644 --- a/Mage.Sets/src/mage/cards/m/MoxAmber.java +++ b/Mage.Sets/src/mage/cards/m/MoxAmber.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.mana.AnyColorPermanentTypesManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -11,24 +9,25 @@ import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; +import java.util.UUID; + /** - * * @author CountAndromalius */ public final class MoxAmber extends CardImpl { public MoxAmber(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{0}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); addSuperType(SuperType.LEGENDARY); - // {tap}: Add one mana pool of any color among legendary creatures and planeswalkers you control. + // {T}: Add one mana pool of any color among legendary creatures and planeswalkers you control. FilterPermanent filter = new FilterPermanent("legendary creatures and planeswalkers"); filter.add(Predicates.or( Predicates.and( - CardType.CREATURE.getPredicate(), - SuperType.LEGENDARY.getPredicate() + CardType.CREATURE.getPredicate(), + SuperType.LEGENDARY.getPredicate() ), - CardType.PLANESWALKER.getPredicate()) + CardType.PLANESWALKER.getPredicate()) ); this.addAbility(new AnyColorPermanentTypesManaAbility(TargetController.YOU, filter)); } diff --git a/Mage.Sets/src/mage/cards/m/MoxOpal.java b/Mage.Sets/src/mage/cards/m/MoxOpal.java index e0e2335775..0177e7c0ae 100644 --- a/Mage.Sets/src/mage/cards/m/MoxOpal.java +++ b/Mage.Sets/src/mage/cards/m/MoxOpal.java @@ -1,11 +1,10 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.mana.ActivateIfConditionManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -14,14 +13,15 @@ import mage.constants.CardType; import mage.constants.SuperType; import mage.constants.Zone; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com, Loki */ public final class MoxOpal extends CardImpl { public MoxOpal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{0}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); addSuperType(SuperType.LEGENDARY); Ability ability = new ActivateIfConditionManaAbility( @@ -30,6 +30,7 @@ public final class MoxOpal extends CardImpl { new TapSourceCost(), MetalcraftCondition.instance); ability.setAbilityWord(AbilityWord.METALCRAFT); + ability.addHint(MetalcraftHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MudTrooper.java b/Mage.Sets/src/mage/cards/m/MudTrooper.java index 51472b51e2..b1615d499e 100644 --- a/Mage.Sets/src/mage/cards/m/MudTrooper.java +++ b/Mage.Sets/src/mage/cards/m/MudTrooper.java @@ -1,6 +1,5 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -8,16 +7,17 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.constants.Duration; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author NinthWorld */ public final class MudTrooper extends CardImpl { @@ -30,7 +30,7 @@ public final class MudTrooper extends CardImpl { public MudTrooper(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); - + this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.TROOPER); this.power = new MageInt(1); @@ -39,10 +39,12 @@ public final class MudTrooper extends CardImpl { // Trooper creatures you control have "2: This creature gets +1/+1 until end of turn." Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn) - .setText("This creature gets +1/+1 until end of turn"), + .setText("This creature gets +1/+1 until end of turn"), new GenericManaCost(2)); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(ability, Duration.WhileOnBattlefield, filter, false))); + new GainAbilityControlledEffect(ability, Duration.WhileOnBattlefield, filter, false) + .withForceQuotes() + )); } public MudTrooper(final MudTrooper card) { diff --git a/Mage.Sets/src/mage/cards/m/MudbuttonClanger.java b/Mage.Sets/src/mage/cards/m/MudbuttonClanger.java index d90ffa96cb..761b840c55 100644 --- a/Mage.Sets/src/mage/cards/m/MudbuttonClanger.java +++ b/Mage.Sets/src/mage/cards/m/MudbuttonClanger.java @@ -4,12 +4,12 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.abilityword.KinshipAbility; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.SubType; -import mage.counters.CounterType; /** * @@ -27,7 +27,7 @@ public final class MudbuttonClanger extends CardImpl { // Kinship - At the beginning of your upkeep, you may look at the top card of your library. If it shares a creature type with Mudbutton Clanger, you may reveal it. // If you do, Mudbutton Clanger gets +1/+1 until end of turn. - this.addAbility(new KinshipAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()))); + this.addAbility(new KinshipAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn))); } public MudbuttonClanger(final MudbuttonClanger card) { diff --git a/Mage.Sets/src/mage/cards/m/MudbuttonTorchrunner.java b/Mage.Sets/src/mage/cards/m/MudbuttonTorchrunner.java index 577e64980d..07f76c18f0 100644 --- a/Mage.Sets/src/mage/cards/m/MudbuttonTorchrunner.java +++ b/Mage.Sets/src/mage/cards/m/MudbuttonTorchrunner.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class MudbuttonTorchrunner extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); // When Mudbutton Torchrunner dies, it deals 3 damage to any target. - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(3, "it"), false); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(3, "it"), false); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java b/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java index 257e555abf..6271916674 100644 --- a/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java +++ b/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java @@ -1,8 +1,5 @@ - package mage.cards.m; -import java.util.Objects; -import java.util.UUID; import mage.MageObject; import mage.abilities.Abilities; import mage.abilities.Ability; @@ -20,8 +17,10 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicate; import mage.game.Game; +import java.util.Objects; +import java.util.UUID; + /** - * * @author anonymous */ public final class MuragandaPetroglyphs extends CardImpl { @@ -54,6 +53,12 @@ public final class MuragandaPetroglyphs extends CardImpl { class NoAbilityPredicate implements Predicate { + // Muraganda Petroglyphs gives a bonus only to creatures that have no rules text at all. This includes true vanilla + // creatures (such as Grizzly Bears), face-down creatures, many tokens, and creatures that have lost their abilities + // (due to Ovinize, for example). Any ability of any kind, whether or not the ability functions in the on the + // battlefield zone, including things like “Cycling {2}” means the creature doesn’t get the bonus. + // (2007-05-01) + @Override public boolean apply(MageObject input, Game game) { boolean isFaceDown = false; @@ -65,8 +70,20 @@ class NoAbilityPredicate implements Predicate { abilities = input.getAbilities(); } if (isFaceDown) { + // Some Auras and Equipment grant abilities to creatures, meaning the affected creature would no longer + // get the +2/+2 bonus. For example, Flight grants flying to the enchanted creature. Other Auras and + // Equipment do not, meaning the affected creature would continue to get the +2/+2 bonus. For example, + // Dehydration states something now true about the enchanted creature, but doesn’t give it any abilities. + // Auras and Equipment that grant abilities will use the words “gains” or “has,” and they’ll list a keyword + // ability or an ability in quotation marks. + // (2007-05-01) + for (Ability ability : abilities) { - if (!ability.getSourceId().equals(input.getId()) && !ability.getClass().equals(JohanVigilanceAbility.class)) { + if (ability.getWorksFaceDown()) { + // inner face down abilities like turn up and becomes creature + continue; + } + if (!Objects.equals(ability.getClass(), SpellAbility.class) && !ability.getClass().equals(JohanVigilanceAbility.class)) { return false; } } diff --git a/Mage.Sets/src/mage/cards/m/MurderOfCrows.java b/Mage.Sets/src/mage/cards/m/MurderOfCrows.java index 8b02356dc6..462a0e926b 100644 --- a/Mage.Sets/src/mage/cards/m/MurderOfCrows.java +++ b/Mage.Sets/src/mage/cards/m/MurderOfCrows.java @@ -63,7 +63,7 @@ class MurderOfCrowsEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null && player.chooseUse(Outcome.DrawCard, "Do you wish to draw a card? If you do, discard a card.", source, game)) { - if (player.drawCards(1, game) > 0) { + if (player.drawCards(1, source.getSourceId(), game) > 0) { player.discard(1, false, source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/m/MurderousBetrayal.java b/Mage.Sets/src/mage/cards/m/MurderousBetrayal.java index 51cba4f02d..17388b1ab6 100644 --- a/Mage.Sets/src/mage/cards/m/MurderousBetrayal.java +++ b/Mage.Sets/src/mage/cards/m/MurderousBetrayal.java @@ -67,7 +67,7 @@ class MurderousBetrayalCost extends CostImpl { @Override public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { Player controller = game.getPlayer(controllerId); - return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost()); + return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost(ability)); } @Override diff --git a/Mage.Sets/src/mage/cards/m/MurderousRider.java b/Mage.Sets/src/mage/cards/m/MurderousRider.java index 7e7864389f..cc42e69eaa 100644 --- a/Mage.Sets/src/mage/cards/m/MurderousRider.java +++ b/Mage.Sets/src/mage/cards/m/MurderousRider.java @@ -1,7 +1,7 @@ package mage.cards.m; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.keyword.LifelinkAbility; @@ -30,7 +30,7 @@ public final class MurderousRider extends AdventureCard { this.addAbility(LifelinkAbility.getInstance()); // When Murderous Rider dies, put it on the bottom of its owner's library. - this.addAbility(new DiesTriggeredAbility(new PutOnLibrarySourceEffect( + this.addAbility(new DiesSourceTriggeredAbility(new PutOnLibrarySourceEffect( false, "put it on the bottom of its owner's library" ), false)); diff --git a/Mage.Sets/src/mage/cards/m/Musician.java b/Mage.Sets/src/mage/cards/m/Musician.java index 886e9c8897..b937ba9ff4 100644 --- a/Mage.Sets/src/mage/cards/m/Musician.java +++ b/Mage.Sets/src/mage/cards/m/Musician.java @@ -1,80 +1,80 @@ -package mage.cards.m; - -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.DynamicValueGenericManaCost; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.DestroySourceEffect; -import mage.abilities.effects.common.DoUnlessControllerPaysEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.abilities.keyword.CumulativeUpkeepAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.counters.CounterType; -import mage.target.common.TargetCreaturePermanent; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class Musician extends CardImpl { - - public Musician(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); - - this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.WIZARD); - this.power = new MageInt(1); - this.toughness = new MageInt(3); - - // Cumulative upkeep {1} - this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl("{1}"))); - - // {tap}: Put a music counter on target creature. If it doesn't have "At the beginning of your upkeep, destroy this creature unless you pay {1} for each music counter on it," it gains that ability. - Effect effect = new DoUnlessControllerPaysEffect( - new DestroySourceEffect(), - new DynamicValueGenericManaCost( - new CountersSourceCount(CounterType.MUSIC), - "{1} for each music counter on {this}")); - effect.setText("destroy this creature unless you pay {1} for each music counter on it"); - Ability ability = new BeginningOfUpkeepTriggeredAbility( - Zone.BATTLEFIELD, - effect, - TargetController.YOU, - false, - false, - "At the beginning of your upkeep, "); - Effect effect2 = new AddCountersTargetEffect(CounterType.MUSIC.createInstance()); - effect2.setText("Put a music counter on target creature"); - Effect effect3 = new GainAbilityTargetEffect( - ability, - Duration.WhileOnBattlefield); - effect3.setText("If it doesn't have \"At the beginning of your upkeep, destroy this creature unless you pay {1} for each music counter on it,\" it gains that ability"); - Ability ability2 = new SimpleActivatedAbility( - Zone.BATTLEFIELD, - effect2, - new TapSourceCost()); - ability2.addTarget(new TargetCreaturePermanent()); - ability2.addEffect(effect3); - this.addAbility(ability2); - - } - - private Musician(final Musician card) { - super(card); - } - - @Override - public Musician copy() { - return new Musician(this); - } -} - +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DynamicValueGenericManaCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DestroySourceEffect; +import mage.abilities.effects.common.DoUnlessControllerPaysEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.CumulativeUpkeepAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class Musician extends CardImpl { + + public Musician(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Cumulative upkeep {1} + this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl("{1}"))); + + // {tap}: Put a music counter on target creature. If it doesn't have "At the beginning of your upkeep, destroy this creature unless you pay {1} for each music counter on it," it gains that ability. + Effect effect = new DoUnlessControllerPaysEffect( + new DestroySourceEffect(), + new DynamicValueGenericManaCost( + new CountersSourceCount(CounterType.MUSIC), + "{1} for each music counter on {this}")); + effect.setText("destroy this creature unless you pay {1} for each music counter on it"); + Ability ability = new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, + effect, + TargetController.YOU, + false, + false, + "At the beginning of your upkeep, "); + Effect effect2 = new AddCountersTargetEffect(CounterType.MUSIC.createInstance()); + effect2.setText("Put a music counter on target creature"); + Effect effect3 = new GainAbilityTargetEffect( + ability, + Duration.WhileOnBattlefield); + effect3.setText("If it doesn't have \"At the beginning of your upkeep, destroy this creature unless you pay {1} for each music counter on it,\" it gains that ability"); + Ability ability2 = new SimpleActivatedAbility( + Zone.BATTLEFIELD, + effect2, + new TapSourceCost()); + ability2.addTarget(new TargetCreaturePermanent()); + ability2.addEffect(effect3); + this.addAbility(ability2); + + } + + private Musician(final Musician card) { + super(card); + } + + @Override + public Musician copy() { + return new Musician(this); + } +} + diff --git a/Mage.Sets/src/mage/cards/m/MutualDestruction.java b/Mage.Sets/src/mage/cards/m/MutualDestruction.java new file mode 100644 index 0000000000..59c83e6ea3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MutualDestruction.java @@ -0,0 +1,67 @@ +package mage.cards.m; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; + +/** + * @author TheElk801 + */ +public final class MutualDestruction extends CardImpl { + private static final FilterPermanent filter = new FilterControlledPermanent(); + private static final FilterPermanent filter2 = new FilterCreaturePermanent("creature (to destoy)"); + + static { + filter.add(new AbilityPredicate(FlashAbility.class)); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + + public MutualDestruction(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}"); + + // This spell has flash as long as you control a permanent with flash. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FlashAbility.getInstance(), Duration.Custom, true), + condition, "This spell has flash as long as you control a permanent with flash." + )).setRuleAtTheTop(true)); + + // As an additional cost to cast this spell, sacrifice a creature. + this.getSpellAbility().addCost(new SacrificeTargetCost( + new TargetControlledPermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + )); + + // Destroy target creature. + this.getSpellAbility().addEffect(new DestroyTargetEffect("Destroy target creature")); + this.getSpellAbility().addTarget(new TargetPermanent(filter2)); + } + + private MutualDestruction(final MutualDestruction card) { + super(card); + } + + @Override + public MutualDestruction copy() { + return new MutualDestruction(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java b/Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java new file mode 100644 index 0000000000..ca54830561 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java @@ -0,0 +1,115 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.players.Player; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MuxusGoblinGrandee extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.GOBLIN); + + static { + filter.add(AnotherPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + + public MuxusGoblinGrandee(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // When Muxus, Goblin Grandee enters the battlefield, reveal the top six cards of your library. Put all Goblin creature cards with converted mana cost 5 or less from among them onto the battlefield and the rest on the bottom of your library in a random order. + this.addAbility(new EntersBattlefieldTriggeredAbility(new MuxusGoblinGrandeeEffect())); + + // Whenever Muxus attacks, it gets +1/+1 until end of turn for each other Goblin you control. + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect( + xValue, xValue, Duration.EndOfTurn + ).setText("it gets +1/+1 until end of turn for each other Goblin you control"), false)); + } + + private MuxusGoblinGrandee(final MuxusGoblinGrandee card) { + super(card); + } + + @Override + public MuxusGoblinGrandee copy() { + return new MuxusGoblinGrandee(this); + } +} + +class MuxusGoblinGrandeeEffect extends OneShotEffect { + + MuxusGoblinGrandeeEffect() { + super(Outcome.Benefit); + staticText = "reveal the top six cards of your library. " + + "Put all Goblin creature cards with converted mana cost 5 or less " + + "from among them onto the battlefield and the rest on the bottom of your library in a random order."; + } + + private MuxusGoblinGrandeeEffect(final MuxusGoblinGrandeeEffect effect) { + super(effect); + } + + @Override + public MuxusGoblinGrandeeEffect copy() { + return new MuxusGoblinGrandeeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 6)); + player.revealCards(source, cards, game); + Cards toBattlfield = new CardsImpl(); + Cards toBottom = new CardsImpl(); + cards.getCards(game) + .stream() + .filter(Objects::nonNull) + .forEach(card -> { + if (card.isCreature() + && card.hasSubtype(SubType.GOBLIN, game) + && card.getConvertedManaCost() <= 5) { + toBattlfield.add(card); + } else { + toBottom.add(card); + } + }); + player.moveCards(toBattlfield, Zone.BATTLEFIELD, source, game); + // need to account for effects like Grafdigger's Cage + toBattlfield + .stream() + .filter(uuid -> game.getState().getZone(uuid) == Zone.LIBRARY) + .forEach(toBottom::add); + player.putCardsOnBottomOfLibrary(toBottom, game, source, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MycosynthGolem.java b/Mage.Sets/src/mage/cards/m/MycosynthGolem.java index d9b8d149ce..9d98492251 100644 --- a/Mage.Sets/src/mage/cards/m/MycosynthGolem.java +++ b/Mage.Sets/src/mage/cards/m/MycosynthGolem.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect; @@ -11,15 +9,16 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterSpell; +import mage.filter.FilterCard; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class MycosynthGolem extends CardImpl { - private static final FilterSpell filter = new FilterSpell("Artifact creature spells"); + private static final FilterCard filter = new FilterCard("Artifact creature spells"); static { filter.add(CardType.ARTIFACT.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/m/MycosynthLattice.java b/Mage.Sets/src/mage/cards/m/MycosynthLattice.java index 61ac8b0fae..7053ad1eab 100644 --- a/Mage.Sets/src/mage/cards/m/MycosynthLattice.java +++ b/Mage.Sets/src/mage/cards/m/MycosynthLattice.java @@ -8,6 +8,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.AsThoughManaEffect; import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.hint.StaticHint; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -34,7 +35,9 @@ public final class MycosynthLattice extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new EverythingIsColorlessEffect())); // Players may spend mana as though it were mana of any color. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ManaCanBeSpentAsAnyColorEffect())); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ManaCanBeSpentAsAnyColorEffect()); + ability.addHint(new StaticHint("(XMage hint: You can use floating mana by clicking on the related symbol of the needed mana type in your mana pool player area.)")); + this.addAbility(ability); } public MycosynthLattice(final MycosynthLattice card) { diff --git a/Mage.Sets/src/mage/cards/m/MycosynthWellspring.java b/Mage.Sets/src/mage/cards/m/MycosynthWellspring.java index a79e7bf812..bcf4918447 100644 --- a/Mage.Sets/src/mage/cards/m/MycosynthWellspring.java +++ b/Mage.Sets/src/mage/cards/m/MycosynthWellspring.java @@ -1,7 +1,7 @@ package mage.cards.m; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; @@ -24,7 +24,7 @@ public final class MycosynthWellspring extends CardImpl { // When Mycosynth Wellspring enters the battlefield or is put into a graveyard from the battlefield, // you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library. this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), true)); - this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), true)); + this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), true)); } public MycosynthWellspring(final MycosynthWellspring card) { diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java b/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java index 6bad3165ae..f3da7615d1 100644 --- a/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java +++ b/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java @@ -6,7 +6,7 @@ import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -48,7 +48,7 @@ public final class MyojinOfCleansingFire extends CardImpl { this.getSpellAbility().addWatcher(new CastFromHandWatcher()); // Myojin of Cleansing Fire enters the battlefield with a divinity counter on it if you cast it from your hand. - this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourceCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); + this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourcePermanentCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); // Myojin of Cleansing Fire is indestructible as long as it has a divinity counter on it. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield), new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it"))); diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfInfiniteRage.java b/Mage.Sets/src/mage/cards/m/MyojinOfInfiniteRage.java index 0c664bb6c5..f217d6ca29 100644 --- a/Mage.Sets/src/mage/cards/m/MyojinOfInfiniteRage.java +++ b/Mage.Sets/src/mage/cards/m/MyojinOfInfiniteRage.java @@ -6,7 +6,7 @@ import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -42,7 +42,7 @@ public final class MyojinOfInfiniteRage extends CardImpl { this.getSpellAbility().addWatcher(new CastFromHandWatcher()); // Myojin of Infinite Rage enters the battlefield with a divinity counter on it if you cast it from your hand. - this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourceCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); + this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourcePermanentCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); // Myojin of Infinite Rage is indestructible as long as it has a divinity counter on it. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield), new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it"))); diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfLifesWeb.java b/Mage.Sets/src/mage/cards/m/MyojinOfLifesWeb.java index a9a628b5ed..3f5403b25e 100644 --- a/Mage.Sets/src/mage/cards/m/MyojinOfLifesWeb.java +++ b/Mage.Sets/src/mage/cards/m/MyojinOfLifesWeb.java @@ -7,7 +7,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -43,7 +43,7 @@ public final class MyojinOfLifesWeb extends CardImpl { this.getSpellAbility().addWatcher(new CastFromHandWatcher()); // Myojin of Life's Web enters the battlefield with a divinity counter on it if you cast it from your hand. - this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourceCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); + this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourcePermanentCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); // Myojin of Life's Web is indestructible as long as it has a divinity counter on it. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield), diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java b/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java index 9f6f424fd3..dee0e16457 100644 --- a/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java +++ b/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java @@ -1,13 +1,11 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -16,7 +14,6 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.IndestructibleAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -25,13 +22,15 @@ import mage.game.Game; import mage.players.Player; import mage.watchers.common.CastFromHandWatcher; +import java.util.UUID; + /** * @author LevelX */ public final class MyojinOfNightsReach extends CardImpl { public MyojinOfNightsReach(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{B}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}{B}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SPIRIT); @@ -41,7 +40,7 @@ public final class MyojinOfNightsReach extends CardImpl { this.getSpellAbility().addWatcher(new CastFromHandWatcher()); // Myojin of Night's Reach enters the battlefield with a divinity counter on it if you cast it from your hand. - this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourceCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); + this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourcePermanentCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); // Myojin of Night's Reach is indestructible as long as it has a divinity counter on it. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield), new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it"))); @@ -50,7 +49,7 @@ public final class MyojinOfNightsReach extends CardImpl { this.addAbility(ability); } - public MyojinOfNightsReach(final MyojinOfNightsReach card) { + private MyojinOfNightsReach(final MyojinOfNightsReach card) { super(card); } @@ -61,12 +60,13 @@ public final class MyojinOfNightsReach extends CardImpl { } class MyojinOfNightsReachEffect extends OneShotEffect { - public MyojinOfNightsReachEffect() { + + MyojinOfNightsReachEffect() { super(Outcome.Discard); staticText = "Each opponent discards their hand"; } - public MyojinOfNightsReachEffect(final MyojinOfNightsReachEffect effect) { + private MyojinOfNightsReachEffect(final MyojinOfNightsReachEffect effect) { super(effect); } @@ -74,10 +74,8 @@ class MyojinOfNightsReachEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (UUID opponentId : game.getOpponents(source.getControllerId())) { Player opponent = game.getPlayer(opponentId); - if(opponent != null) { - for (Card c : opponent.getHand().getCards(game)) { - opponent.discard(c, source, game); - } + if (opponent != null) { + opponent.discard(opponent.getHand(), source, game); } } return true; @@ -87,5 +85,4 @@ class MyojinOfNightsReachEffect extends OneShotEffect { public MyojinOfNightsReachEffect copy() { return new MyojinOfNightsReachEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfSeeingWinds.java b/Mage.Sets/src/mage/cards/m/MyojinOfSeeingWinds.java index a2ffdec473..319780ad6b 100644 --- a/Mage.Sets/src/mage/cards/m/MyojinOfSeeingWinds.java +++ b/Mage.Sets/src/mage/cards/m/MyojinOfSeeingWinds.java @@ -7,7 +7,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -45,7 +45,7 @@ public final class MyojinOfSeeingWinds extends CardImpl { this.getSpellAbility().addWatcher(new CastFromHandWatcher()); // Myojin of Seeing Winds enters the battlefield with a divinity counter on it if you cast it from your hand. - this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourceCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); + this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourcePermanentCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); // Myojin of Seeing Winds is indestructible as long as it has a divinity counter on it. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield), new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it"))); diff --git a/Mage.Sets/src/mage/cards/m/MyrMoonvessel.java b/Mage.Sets/src/mage/cards/m/MyrMoonvessel.java index b25556c35a..b31ea7095f 100644 --- a/Mage.Sets/src/mage/cards/m/MyrMoonvessel.java +++ b/Mage.Sets/src/mage/cards/m/MyrMoonvessel.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.Mana; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.mana.BasicManaEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -21,7 +21,7 @@ public final class MyrMoonvessel extends CardImpl { this.subtype.add(SubType.MYR); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new DiesTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(1)))); + this.addAbility(new DiesSourceTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(1)))); } public MyrMoonvessel(final MyrMoonvessel card) { diff --git a/Mage.Sets/src/mage/cards/m/MyrReservoir.java b/Mage.Sets/src/mage/cards/m/MyrReservoir.java index d6d66099a1..6f4acfc103 100644 --- a/Mage.Sets/src/mage/cards/m/MyrReservoir.java +++ b/Mage.Sets/src/mage/cards/m/MyrReservoir.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -10,9 +9,9 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; -import mage.abilities.mana.BasicManaAbility; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -34,19 +33,19 @@ public final class MyrReservoir extends CardImpl { } public MyrReservoir(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - // {tap}: Add {C}{C}. Spend this mana only to cast Myr spells or activate abilities of Myr. + // {T}: Add {C}{C}. Spend this mana only to cast Myr spells or activate abilities of Myr. this.addAbility(new MyrReservoirManaAbility()); - // {3}, {tap}: Return target Myr card from your graveyard to your hand. + // {3}, {T}: Return target Myr card from your graveyard to your hand. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCardInYourGraveyard(myrCardFilter)); this.addAbility(ability); } - public MyrReservoir(final MyrReservoir card) { + private MyrReservoir(final MyrReservoir card) { super(card); } @@ -56,14 +55,14 @@ public final class MyrReservoir extends CardImpl { } } -class MyrReservoirManaAbility extends BasicManaAbility { +class MyrReservoirManaAbility extends ActivatedManaAbilityImpl { MyrReservoirManaAbility() { - super(new BasicManaEffect(new MyrConditionalMana())); + super(Zone.BATTLEFIELD, new BasicManaEffect(new MyrConditionalMana()), new TapSourceCost()); this.netMana.add(Mana.ColorlessMana(2)); } - MyrReservoirManaAbility(MyrReservoirManaAbility ability) { + private MyrReservoirManaAbility(MyrReservoirManaAbility ability) { super(ability); } @@ -75,7 +74,7 @@ class MyrReservoirManaAbility extends BasicManaAbility { class MyrConditionalMana extends ConditionalMana { - public MyrConditionalMana() { + MyrConditionalMana() { super(Mana.ColorlessMana(2)); staticText = "Spend this mana only to cast Myr spells or activate abilities of Myr"; addCondition(new MyrManaCondition()); diff --git a/Mage.Sets/src/mage/cards/m/MyrRetriever.java b/Mage.Sets/src/mage/cards/m/MyrRetriever.java index 5d0560a459..2bb166aa7c 100644 --- a/Mage.Sets/src/mage/cards/m/MyrRetriever.java +++ b/Mage.Sets/src/mage/cards/m/MyrRetriever.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; @@ -36,7 +36,7 @@ public final class MyrRetriever extends CardImpl { // When Myr Retriever dies, return another target artifact card from your graveyard to your hand. Effect effect = new ReturnFromGraveyardToHandTargetEffect(); effect.setText("return another target artifact card from your graveyard to your hand"); - Ability ability = new DiesTriggeredAbility(effect); + Ability ability = new DiesSourceTriggeredAbility(effect); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MyrSire.java b/Mage.Sets/src/mage/cards/m/MyrSire.java index 2d13b14161..80b28b593c 100644 --- a/Mage.Sets/src/mage/cards/m/MyrSire.java +++ b/Mage.Sets/src/mage/cards/m/MyrSire.java @@ -4,7 +4,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class MyrSire extends CardImpl { this.subtype.add(SubType.MYR); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new MyrToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new MyrToken()))); } public MyrSire (final MyrSire card) { diff --git a/Mage.Sets/src/mage/cards/m/MysteriousEgg.java b/Mage.Sets/src/mage/cards/m/MysteriousEgg.java new file mode 100644 index 0000000000..13cbee1429 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MysteriousEgg.java @@ -0,0 +1,40 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MysteriousEgg extends CardImpl { + + public MysteriousEgg(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}"); + + this.subtype.add(SubType.EGG); + this.power = new MageInt(0); + this.toughness = new MageInt(2); + + // Whenever this creature mutates, put a +1/+1 counter on it. + this.addAbility(new MutatesSourceTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()).setText("put a +1/+1 counter on it") + )); + } + + private MysteriousEgg(final MysteriousEgg card) { + super(card); + } + + @Override + public MysteriousEgg copy() { + return new MysteriousEgg(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MysticForge.java b/Mage.Sets/src/mage/cards/m/MysticForge.java index 80eeee2890..2cfee980ed 100644 --- a/Mage.Sets/src/mage/cards/m/MysticForge.java +++ b/Mage.Sets/src/mage/cards/m/MysticForge.java @@ -5,13 +5,18 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; -import mage.cards.Card; +import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterNonlandCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorlessPredicate; import mage.game.Game; import mage.players.Player; @@ -22,14 +27,23 @@ import java.util.UUID; */ public final class MysticForge extends CardImpl { + private static final FilterCard filter = new FilterNonlandCard("cast artifact spells and colorless spells"); + + static { + filter.add(Predicates.or( + ColorlessPredicate.instance, + CardType.ARTIFACT.getPredicate() + )); + } + public MysticForge(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // You may look at the top card of your library any time. this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); - // You may cast the top card of your library if it's an artifact card or a colorless nonland card. - this.addAbility(new SimpleStaticAbility(new MysticForgeTopCardCastEffect())); + // You may cast artifact spells and colorless spells from the top of your library. + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); // {T}, Pay 1 life: Exile the top card of your library. Ability ability = new SimpleActivatedAbility(new MysticForgeExileEffect(), new TapSourceCost()); @@ -47,50 +61,6 @@ public final class MysticForge extends CardImpl { } } -class MysticForgeTopCardCastEffect extends AsThoughEffectImpl { - - MysticForgeTopCardCastEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "You may cast the top card of your library if it's an artifact card or a colorless nonland card."; - } - - private MysticForgeTopCardCastEffect(final MysticForgeTopCardCastEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public MysticForgeTopCardCastEffect copy() { - return new MysticForgeTopCardCastEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!affectedControllerId.equals(source.getControllerId())) { - return false; - } - Card card = game.getCard(objectId); - if (card == null) { - return false; - } - Player controller = game.getPlayer(affectedControllerId); - if (controller == null) { - return false; - } - Card topCard = controller.getLibrary().getFromTop(game); - return game.getObject(source.getSourceId()) != null - && topCard != null - && topCard == card - && (topCard.isArtifact() || (!topCard.isLand() && topCard.getColor(game).isColorless())) - && topCard.getSpellAbility() != null - && topCard.getSpellAbility().spellCanBeActivatedRegularlyNow(controller.getId(), game); - } -} - class MysticForgeExileEffect extends OneShotEffect { MysticForgeExileEffect() { diff --git a/Mage.Sets/src/mage/cards/m/MysticRemora.java b/Mage.Sets/src/mage/cards/m/MysticRemora.java index 409ae14e81..ff57096a5d 100644 --- a/Mage.Sets/src/mage/cards/m/MysticRemora.java +++ b/Mage.Sets/src/mage/cards/m/MysticRemora.java @@ -123,7 +123,7 @@ class MysticRemoraEffect extends OneShotEffect { && cost.pay(source, game, source.getSourceId(), opponent.getId(), false, null)) { return true; } - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/m/MysticSkyfish.java b/Mage.Sets/src/mage/cards/m/MysticSkyfish.java new file mode 100644 index 0000000000..9c75a23b5c --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MysticSkyfish.java @@ -0,0 +1,41 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.DrawSecondCardTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MysticSkyfish extends CardImpl { + + public MysticSkyfish(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.FISH); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Whenever you draw your second card each turn, Mystic Skyfish gains flying until end of turn. + this.addAbility(new DrawSecondCardTriggeredAbility( + new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), false) + ); + } + + private MysticSkyfish(final MysticSkyfish card) { + super(card); + } + + @Override + public MysticSkyfish copy() { + return new MysticSkyfish(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MysticSubdual.java b/Mage.Sets/src/mage/cards/m/MysticSubdual.java new file mode 100644 index 0000000000..3393034bb8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MysticSubdual.java @@ -0,0 +1,85 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MysticSubdual extends CardImpl { + + public MysticSubdual(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.AURA); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets -2/-0 and loses all abilities. + ability = new SimpleStaticAbility(new BoostEnchantedEffect(-2, 0)); + ability.addEffect(new MysticSubdualEffect()); + this.addAbility(ability); + } + + private MysticSubdual(final MysticSubdual card) { + super(card); + } + + @Override + public MysticSubdual copy() { + return new MysticSubdual(this); + } +} + +class MysticSubdualEffect extends ContinuousEffectImpl { + + MysticSubdualEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "and loses all abilities"; + } + + private MysticSubdualEffect(final MysticSubdualEffect effect) { + super(effect); + } + + @Override + public MysticSubdualEffect copy() { + return new MysticSubdualEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return true; + } + Permanent creature = game.getPermanent(permanent.getAttachedTo()); + if (creature == null) { + return true; + } + creature.removeAllAbilities(source.getSourceId(), game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MysticalDispute.java b/Mage.Sets/src/mage/cards/m/MysticalDispute.java index f2c290d95e..61c8d08775 100644 --- a/Mage.Sets/src/mage/cards/m/MysticalDispute.java +++ b/Mage.Sets/src/mage/cards/m/MysticalDispute.java @@ -28,7 +28,7 @@ public final class MysticalDispute extends CardImpl { // This spell costs {2} less to cast if it targets a blue spell. this.addAbility(new SimpleStaticAbility( - Zone.STACK, new SpellCostReductionSourceEffect(2, MysticalDisputeCondition.instance) + Zone.ALL, new SpellCostReductionSourceEffect(2, MysticalDisputeCondition.instance).setCanWorksOnStackOnly(true) ).setRuleAtTheTop(true)); // Counter target spell unless its controller pays {3}. diff --git a/Mage.Sets/src/mage/cards/m/MystifyingMaze.java b/Mage.Sets/src/mage/cards/m/MystifyingMaze.java index a496ebf130..9af9f85659 100644 --- a/Mage.Sets/src/mage/cards/m/MystifyingMaze.java +++ b/Mage.Sets/src/mage/cards/m/MystifyingMaze.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -24,8 +22,9 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public final class MystifyingMaze extends CardImpl { @@ -77,7 +76,7 @@ class MystifyingMazeEffect extends OneShotEffect { if (permanent != null && sourceObject != null) { if (permanent.moveToExile(source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game)) { //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(true); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(true, false); effect.setText("At the beginning of the next end step, return it to the battlefield tapped under its owner's control"); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/m/MythUnbound.java b/Mage.Sets/src/mage/cards/m/MythUnbound.java index 08e62af7f6..0b2e0ea26d 100644 --- a/Mage.Sets/src/mage/cards/m/MythUnbound.java +++ b/Mage.Sets/src/mage/cards/m/MythUnbound.java @@ -77,10 +77,8 @@ class MythUnboundCostReductionEffect extends CostModificationEffectImpl { if (spellAbility != null) { CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); int castCount = watcher.getPlaysCount(abilityToModify.getSourceId()); - if (castCount > 0) { - CardUtil.reduceCost(spellAbility, castCount); - return true; - } + CardUtil.reduceCost(spellAbility, castCount); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/m/MythosOfBrokkos.java b/Mage.Sets/src/mage/cards/m/MythosOfBrokkos.java new file mode 100644 index 0000000000..9413d293cc --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MythosOfBrokkos.java @@ -0,0 +1,93 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.ManaWasSpentCondition; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInYourGraveyard; +import mage.watchers.common.ManaSpentToCastWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MythosOfBrokkos extends CardImpl { + + public MythosOfBrokkos(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}{G}"); + + // If {U}{B} was spent to cast Mythos of Brokkos, search your library for a card, put that card into your graveyard, then shuffle your library. + // Return up to two permanent cards from your graveyard to your hand. + this.getSpellAbility().addEffect(new MythosOfBrokkosEffect()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); + } + + private MythosOfBrokkos(final MythosOfBrokkos card) { + super(card); + } + + @Override + public MythosOfBrokkos copy() { + return new MythosOfBrokkos(this); + } +} + +class MythosOfBrokkosEffect extends OneShotEffect { + + private static final Condition condition = new CompoundCondition( + new ManaWasSpentCondition(ColoredManaSymbol.U), + new ManaWasSpentCondition(ColoredManaSymbol.B) + ); + private static final FilterCard filter = new FilterPermanentCard("permanent cards"); + + MythosOfBrokkosEffect() { + super(Outcome.Benefit); + staticText = "If {U}{B} was spent to cast Mythos of Brokkos, search your library for a card, " + + "put that card into your graveyard, then shuffle your library.
" + + "Return up to two permanent cards from your graveyard to your hand."; + } + + private MythosOfBrokkosEffect(final MythosOfBrokkosEffect effect) { + super(effect); + } + + @Override + public MythosOfBrokkosEffect copy() { + return new MythosOfBrokkosEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + if (condition.apply(game, source)) { + TargetCardInLibrary targetCardInLibrary = new TargetCardInLibrary(); + if (player.searchLibrary(targetCardInLibrary, source, game)) { + Card card = game.getCard(targetCardInLibrary.getFirstTarget()); + if (card != null) { + player.moveCards(card, Zone.GRAVEYARD, source, game); + } + player.shuffleLibrary(source, game); + } + } + TargetCard targetCard = new TargetCardInYourGraveyard(0, 2, filter, true); + player.choose(outcome, player.getGraveyard(), targetCard, game); + Cards cards = new CardsImpl(targetCard.getTargets()); + return player.moveCards(cards, Zone.HAND, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MythosOfIlluna.java b/Mage.Sets/src/mage/cards/m/MythosOfIlluna.java new file mode 100644 index 0000000000..317324f6b0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MythosOfIlluna.java @@ -0,0 +1,101 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.ManaWasSpentCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.FightTargetSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.watchers.common.ManaSpentToCastWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MythosOfIlluna extends CardImpl { + + public MythosOfIlluna(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}"); + + // Create a token that's a copy of target permanent. If {R}{G} was spent to cast this spell, instead create a token that's a copy of that permanent, except the token has "When this permanent enters the battlefield, if it's a creature, it fights up to one target creature you don't control." + this.getSpellAbility().addEffect(new MythosOfIllunaEffect()); + this.getSpellAbility().addTarget(new TargetPermanent()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); + } + + private MythosOfIlluna(final MythosOfIlluna card) { + super(card); + } + + @Override + public MythosOfIlluna copy() { + return new MythosOfIlluna(this); + } +} + +class MythosOfIllunaEffect extends OneShotEffect { + + private static final Condition condition = new CompoundCondition( + new ManaWasSpentCondition(ColoredManaSymbol.R), + new ManaWasSpentCondition(ColoredManaSymbol.G) + ); + + MythosOfIllunaEffect() { + super(Outcome.Benefit); + staticText = "Create a token that's a copy of target permanent. " + + "If {R}{G} was spent to cast this spell, instead create a token that's a copy of that permanent, " + + "except the token has \"When this permanent enters the battlefield, if it's a creature, " + + "it fights up to one target creature you don't control.\""; + } + + private MythosOfIllunaEffect(final MythosOfIllunaEffect effect) { + super(effect); + } + + @Override + public MythosOfIllunaEffect copy() { + return new MythosOfIllunaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId()); + if (condition.apply(game, source)) { + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect()), + MythosOfIllunaCondition.instance, "When this permanent enters the battlefield, " + + "if it's a creature, it fights up to one target creature you don't control." + ); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); + effect.addAdditionalAbilities(ability); + } + return effect.apply(game, source); + } +} + +enum MythosOfIllunaCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + return permanent != null && permanent.isCreature(); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MythosOfNethroi.java b/Mage.Sets/src/mage/cards/m/MythosOfNethroi.java new file mode 100644 index 0000000000..17c951f86e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MythosOfNethroi.java @@ -0,0 +1,73 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.ManaWasSpentCondition; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetNonlandPermanent; +import mage.watchers.common.ManaSpentToCastWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MythosOfNethroi extends CardImpl { + + public MythosOfNethroi(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); + + // Destroy target nonland permanent if it's a creature or if {G}{W} was spent to cast this spell. + this.getSpellAbility().addEffect(new MythosOfNethroiEffect()); + this.getSpellAbility().addTarget(new TargetNonlandPermanent()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); + } + + private MythosOfNethroi(final MythosOfNethroi card) { + super(card); + } + + @Override + public MythosOfNethroi copy() { + return new MythosOfNethroi(this); + } +} + +class MythosOfNethroiEffect extends OneShotEffect { + + private static final Condition condition = new CompoundCondition( + new ManaWasSpentCondition(ColoredManaSymbol.G), + new ManaWasSpentCondition(ColoredManaSymbol.W) + ); + + MythosOfNethroiEffect() { + super(Outcome.Benefit); + staticText = "Destroy target nonland permanent if it's a creature or if {G}{W} was spent to cast this spell."; + } + + private MythosOfNethroiEffect(final MythosOfNethroiEffect effect) { + super(effect); + } + + @Override + public MythosOfNethroiEffect copy() { + return new MythosOfNethroiEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null || (!permanent.isCreature() && !condition.apply(game, source))) { + return false; + } + return permanent.destroy(source.getSourceId(), game, false); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MythosOfSnapdax.java b/Mage.Sets/src/mage/cards/m/MythosOfSnapdax.java new file mode 100644 index 0000000000..49ac10a33b --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MythosOfSnapdax.java @@ -0,0 +1,126 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.ManaWasSpentCondition; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.watchers.common.ManaSpentToCastWatcher; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Emigara + */ +public class MythosOfSnapdax extends CardImpl { + + public MythosOfSnapdax(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}{W}"); + + // Each player chooses an artifact, a creature, an enchantment, and a planeswalker from among the nonland permanents they control, then sacrifices the rest. If {B}{R} was spent to cast this spell, you choose the permanents for each player instead. + this.getSpellAbility().addEffect(new MythosOfSnapdaxEffect()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); + } + + private MythosOfSnapdax(final MythosOfSnapdax card) { + super(card); + } + + @Override + public MythosOfSnapdax copy() { + return new MythosOfSnapdax(this); + } +} + +class MythosOfSnapdaxEffect extends OneShotEffect { + + private static final Condition condition = new CompoundCondition( + new ManaWasSpentCondition(ColoredManaSymbol.R), + new ManaWasSpentCondition(ColoredManaSymbol.B) + ); + private static final List cardTypes = Arrays.asList( + CardType.ARTIFACT, + CardType.CREATURE, + CardType.ENCHANTMENT, + CardType.PLANESWALKER + ); + + MythosOfSnapdaxEffect() { + super(Outcome.Benefit); + this.staticText = "Each player chooses an artifact, a creature, an enchantment, and a planeswalker " + + "from among the nonland permanents they control, then sacrifices the rest. " + + "If {B}{R} was spent to cast this spell, you choose the permanents for each player instead."; + } + + private MythosOfSnapdaxEffect(final MythosOfSnapdaxEffect effect) { + super(effect); + } + + @Override + public MythosOfSnapdaxEffect copy() { + return new MythosOfSnapdaxEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + boolean conditionMet = condition.apply(game, source); + + List playerList = game + .getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + Set toKeep = new HashSet(); + for (Player player : playerList) { + for (CardType cardType : cardTypes) { + String message = cardType.toString().equals("Artifact") ? "an " : "a "; + message += cardType.toString().toLowerCase(Locale.ENGLISH); + message += (conditionMet && player != controller) ? " controlled by " + player.getName() : " you control"; + FilterPermanent filter = new FilterNonlandPermanent(message); + filter.add(cardType.getPredicate()); + filter.add(new ControllerIdPredicate(player.getId())); + if (game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) == 0) { + continue; + } + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + if (conditionMet) { + controller.choose(outcome, target, source.getSourceId(), game); + } else { + player.choose(outcome, target, source.getSourceId(), game); + } + toKeep.add(target.getFirstTarget()); + } + } + + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_PERMANENT_NON_LAND, source.getControllerId(), game)) { + if (permanent == null || toKeep.contains(permanent.getId())) { + continue; + } + permanent.sacrifice(source.getSourceId(), game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MythosOfVadrok.java b/Mage.Sets/src/mage/cards/m/MythosOfVadrok.java new file mode 100644 index 0000000000..ad1850d98f --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MythosOfVadrok.java @@ -0,0 +1,50 @@ +package mage.cards.m; + +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.ManaWasSpentCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DamageMultiEffect; +import mage.abilities.effects.common.DetainTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.target.common.TargetCreatureOrPlaneswalkerAmount; +import mage.watchers.common.ManaSpentToCastWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MythosOfVadrok extends CardImpl { + + private static final Condition condition = new CompoundCondition( + new ManaWasSpentCondition(ColoredManaSymbol.W), + new ManaWasSpentCondition(ColoredManaSymbol.U) + ); + + public MythosOfVadrok(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}"); + + // Mythos of Vadrok deals 5 damage divided as you choose among any number of target creatures and/or planeswalkers. If {W}{U} was spent to cast this spell, until your next turn, those permanents can't attack or block and their activated abilities can't be activated. + this.getSpellAbility().addEffect(new DamageMultiEffect(5)); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalkerAmount(5)); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new DetainTargetEffect(), condition, "If {W}{U} was spent to cast this spell, " + + "until your next turn, those permanents can't attack or block " + + "and their activated abilities can't be activated." + )); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); + } + + private MythosOfVadrok(final MythosOfVadrok card) { + super(card); + } + + @Override + public MythosOfVadrok copy() { + return new MythosOfVadrok(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/N1Starfighter.java b/Mage.Sets/src/mage/cards/n/N1Starfighter.java index c760ea5172..8863e07bf8 100644 --- a/Mage.Sets/src/mage/cards/n/N1Starfighter.java +++ b/Mage.Sets/src/mage/cards/n/N1Starfighter.java @@ -43,7 +43,7 @@ public final class N1Starfighter extends CardImpl { // P.S. original card have error with missing target word (another target creature) Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DoIfCostPaid( new ExileTargetForSourceEffect(), new ManaCostsImpl("{1}{W/U}")), false); - ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, true).concatBy(", then")); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false).concatBy(", then")); ability.addTarget(new TargetControlledCreaturePermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java b/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java index be2690ceb5..bc03ebc017 100644 --- a/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java +++ b/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java @@ -1,24 +1,24 @@ package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.events.NumberOfTriggersEvent; +import java.util.UUID; + /** * * @author TheElk801 @@ -35,7 +35,7 @@ public final class NabanDeanOfIteration extends CardImpl { this.toughness = new MageInt(1); // If a Wizard entering the battlefield under your control causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NabanDeanOfIterationEffect())); + this.addAbility(new SimpleStaticAbility(new NabanDeanOfIterationEffect())); } public NabanDeanOfIteration(final NabanDeanOfIteration card) { @@ -84,9 +84,7 @@ class NabanDeanOfIterationEffect extends ReplacementEffectImpl { // Only for entering artifacts or creatures if (entersTheBattlefieldEvent.getTarget().hasSubtype(SubType.WIZARD, game)) { // Only for triggers of permanents - if (game.getPermanent(numberOfTriggersEvent.getSourceId()) != null) { - return true; - } + return game.getPermanent(numberOfTriggersEvent.getSourceId()) != null; } } } diff --git a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java index 0bf4c1f9a2..3cb3c031b4 100644 --- a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java +++ b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java @@ -23,9 +23,11 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.targetpointer.FixedTargets; -import mage.util.CardUtil; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.CreatureAttackedWhichPlayerWatcher; /** * @@ -45,7 +47,10 @@ public final class NacatlWarPride extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect(Duration.WhileOnBattlefield))); // Whenever Nacatl War-Pride attacks, create X tokens that are copies of Nacatl War-Pride tapped and attacking, where X is the number of creatures defending player controls. Exile the tokens at the beginning of the next end step. - this.addAbility(new AttacksTriggeredAbility(new NacatlWarPrideEffect(), false)); + Ability ability = new AttacksTriggeredAbility(new NacatlWarPrideEffect(), false); + ability.addWatcher(new CreatureAttackedWhichPlayerWatcher()); + this.addAbility(ability); + } public NacatlWarPride(final NacatlWarPride card) { @@ -62,7 +67,7 @@ class NacatlWarPrideEffect extends OneShotEffect { public NacatlWarPrideEffect() { super(Outcome.Benefit); - this.staticText = "create X tokens that are copies of Nacatl War-Pride tapped and attacking, where X is the number of creatures defending player controls. Exile the tokens at the beginning of the next end step."; + this.staticText = "create X tokens that are copies of {this} tapped and attacking, where X is the number of creatures defending player controls. Exile the tokens at the beginning of the next end step"; } public NacatlWarPrideEffect(final NacatlWarPrideEffect effect) { @@ -76,13 +81,16 @@ class NacatlWarPrideEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent origNactalWarPride = game.getPermanent(source.getSourceId()); + Permanent origNactalWarPride = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (origNactalWarPride == null) { return false; } + + CreatureAttackedWhichPlayerWatcher PlayerAttackedWatcher = game.getState().getWatcher(CreatureAttackedWhichPlayerWatcher.class); // Count the number of creatures attacked opponent controls - UUID defenderId = game.getCombat().getDefendingPlayerId(origNactalWarPride.getId(), game); + UUID defenderId = PlayerAttackedWatcher.getPlayerAttackedThisTurnByCreature(source.getSourceId()); + int count = 0; if (defenderId != null) { count = game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), defenderId, game); @@ -92,24 +100,17 @@ class NacatlWarPrideEffect extends OneShotEffect { return false; } - List copies = new ArrayList<>(); - for (int i = 0; i < count; i++) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(origNactalWarPride); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId(), true, true); - - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - copies.add(tokenPermanent); - } - } - } - + List copies = new ArrayList<>(); + Player controller = game.getPlayer(source.getControllerId()); + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(controller.getId(), null, false, count, true, true); + effect.setTargetPointer(new FixedTarget(origNactalWarPride, game)); + effect.apply(game, source); + copies.addAll(effect.getAddedPermanent()); + if (!copies.isEmpty()) { FixedTargets fixedTargets = new FixedTargets(copies, game); ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(fixedTargets); + exileEffect.setTargetPointer(fixedTargets).setText("exile the tokens"); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source); return true; } diff --git a/Mage.Sets/src/mage/cards/n/NajeelaTheBladeBlossom.java b/Mage.Sets/src/mage/cards/n/NajeelaTheBladeBlossom.java index 8b268d3ff5..7cb872a6e7 100644 --- a/Mage.Sets/src/mage/cards/n/NajeelaTheBladeBlossom.java +++ b/Mage.Sets/src/mage/cards/n/NajeelaTheBladeBlossom.java @@ -64,17 +64,17 @@ public final class NajeelaTheBladeBlossom extends CardImpl { TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES - ).setText("They gain trample,")); + ).setText("They gain trample")); ability.addEffect(new GainAbilityAllEffect( LifelinkAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES - ).setText("lifelink,")); + ).setText(", lifelink")); ability.addEffect(new GainAbilityAllEffect( HasteAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES - ).setText("and haste until end of turn.")); + ).setText(", and haste until end of turn")); ability.addEffect(new AdditionalCombatPhaseEffect()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NantukoCultivator.java b/Mage.Sets/src/mage/cards/n/NantukoCultivator.java index 4395d9e3a3..1372acb7b2 100644 --- a/Mage.Sets/src/mage/cards/n/NantukoCultivator.java +++ b/Mage.Sets/src/mage/cards/n/NantukoCultivator.java @@ -1,42 +1,41 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.common.FilterLandCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; -/** - * - * @author LoneFox +import java.util.UUID; +/** + * @author LoneFox */ public final class NantukoCultivator extends CardImpl { public NantukoCultivator(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); this.subtype.add(SubType.INSECT); this.subtype.add(SubType.DRUID); this.power = new MageInt(2); this.toughness = new MageInt(2); // When Nantuko Cultivator enters the battlefield, you may discard any number of land cards. Put that many +1/+1 counters on Nantuko Cultivator and draw that many cards. - this.addAbility(new EntersBattlefieldTriggeredAbility(new NantukoCultivatorEffect(), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new NantukoCultivatorEffect())); } - public NantukoCultivator(final NantukoCultivator card) { + private NantukoCultivator(final NantukoCultivator card) { super(card); } @@ -48,40 +47,38 @@ public final class NantukoCultivator extends CardImpl { class NantukoCultivatorEffect extends OneShotEffect { - public NantukoCultivatorEffect() { + NantukoCultivatorEffect() { super(Outcome.BoostCreature); - staticText = "you may discard any number of land cards. Put that many +1/+1 counters on {this} and draw that many cards."; + staticText = "you may discard any number of land cards. " + + "Put that many +1/+1 counters on {this} and draw that many cards."; + } + + private NantukoCultivatorEffect(final NantukoCultivatorEffect effect) { + super(effect); } @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if(player != null) { - TargetCardInHand toDiscard = new TargetCardInHand(0, Integer.MAX_VALUE, new FilterLandCard()); - if(player.chooseTarget(Outcome.Discard, toDiscard, source, game)) { - int count = 0; - for(UUID targetId: toDiscard.getTargets()) { - player.discard(game.getCard(targetId), source, game); - count++; - } - Permanent permanent = game.getPermanent(source.getSourceId()); - if(permanent != null) { - permanent.addCounters(CounterType.P1P1.createInstance(count), source, game); - } - player.drawCards(count, game); - } - return true; + if (player == null || player.getHand().count(StaticFilters.FILTER_CARD_LAND, game) < 1) { + return false; } - return false; - } - - public NantukoCultivatorEffect(final NantukoCultivatorEffect effect) { - super(effect); + TargetCardInHand toDiscard = new TargetCardInHand(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_LAND); + player.chooseTarget(Outcome.AIDontUseIt, toDiscard, source, game); + int count = player.discard(new CardsImpl(toDiscard.getTargets()), source, game).size(); + if (count < 1) { + return false; + } + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(count), source, game); + } + player.drawCards(count, source.getSourceId(), game); + return true; } @Override public NantukoCultivatorEffect copy() { return new NantukoCultivatorEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java b/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java index 8301068ad2..195dea2f7b 100644 --- a/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java +++ b/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java @@ -116,13 +116,9 @@ class NarsetEnlightenedMasterCastFromExileEffect extends AsThoughEffectImpl { public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { if (objectId.equals(getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(objectId); - if (card != null) { - Player player = game.getPlayer(affectedControllerId); - if (player != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - return true; - } + Player player = game.getPlayer(affectedControllerId); + if (player != null) { + return allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java b/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java new file mode 100644 index 0000000000..1ef622e1c1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java @@ -0,0 +1,153 @@ +package mage.cards.n; + +import mage.ConditionalMana; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.SpellAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.Cost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.mana.conditional.ManaCondition; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.ChoiceColor; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Game; +import mage.game.command.emblems.NarsetOfTheAncientWayEmblem; +import mage.players.Player; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NarsetOfTheAncientWay extends CardImpl { + + public NarsetOfTheAncientWay(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{U}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.NARSET); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // +1: You gain 2 life. Add {U}, {R}, or {W}. Spend this mana only to cast a noncreature spell. + this.addAbility(new LoyaltyAbility(new NarsetOfTheAncientWayManaEffect(), 1)); + + // −2: Draw a card, then you may discard a card. When you discard a nonland card this way, Narset of the Ancient Way deals damage equal to that card's converted mana cost to target creature or planeswalker. + this.addAbility(new LoyaltyAbility(new NarsetOfTheAncientWayDrawEffect(), -2)); + + // −6: You get an emblem with "Whenever you cast a noncreature spell, this emblem deals 2 damage to any target." + this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new NarsetOfTheAncientWayEmblem()), -6)); + } + + private NarsetOfTheAncientWay(final NarsetOfTheAncientWay card) { + super(card); + } + + @Override + public NarsetOfTheAncientWay copy() { + return new NarsetOfTheAncientWay(this); + } +} + +class NarsetOfTheAncientWayManaEffect extends OneShotEffect { + + NarsetOfTheAncientWayManaEffect() { + super(Outcome.Benefit); + staticText = "You gain 2 life. Add {U}, {R}, or {W}. Spend this mana only to cast a noncreature spell."; + } + + private NarsetOfTheAncientWayManaEffect(final NarsetOfTheAncientWayManaEffect effect) { + super(effect); + } + + @Override + public NarsetOfTheAncientWayManaEffect copy() { + return new NarsetOfTheAncientWayManaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.gainLife(2, game, source); + ChoiceColor choice = new ChoiceColor(); + choice.getChoices().remove("Green"); + choice.getChoices().remove("Black"); + player.choose(Outcome.PutManaInPool, choice, game); + ConditionalMana mana = new ConditionalMana(choice.getMana(1)); + mana.addCondition(new NarsetOfTheAncientWayManaCondition()); + player.getManaPool().addMana(mana, game, source); + return true; + } +} + +class NarsetOfTheAncientWayManaCondition extends ManaCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + if (!(source instanceof SpellAbility)) { + return false; + } + MageObject object = game.getObject(source.getSourceId()); + return object != null && !object.isCreature(); + } + + @Override + public boolean apply(Game game, Ability source, UUID originalId, Cost costToPay) { + return apply(game, source); + } +} + +class NarsetOfTheAncientWayDrawEffect extends OneShotEffect { + + NarsetOfTheAncientWayDrawEffect() { + super(Outcome.Benefit); + staticText = "Draw a card, then you may discard a card. When you discard a nonland card this way, " + + "{this} deals damage equal to that card's converted mana cost to target creature or planeswalker."; + } + + private NarsetOfTheAncientWayDrawEffect(final NarsetOfTheAncientWayDrawEffect effect) { + super(effect); + } + + @Override + public NarsetOfTheAncientWayDrawEffect copy() { + return new NarsetOfTheAncientWayDrawEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.drawCards(1, source.getSourceId(), game); + if (player.getHand().isEmpty() || !player.chooseUse(Outcome.Discard, "Discard a card?", source, game)) { + return false; + } + Card card = player.discardOne(false, source, game); + if (card == null || card.isLand()) { + return false; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(card.getConvertedManaCost()), false, "{this} deals damage " + + "to target creature or planeswalker equal to the discarded card's converted mana cost" + ); + ability.addTarget(new TargetCreatureOrPlaneswalker()); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NascentMetamorph.java b/Mage.Sets/src/mage/cards/n/NascentMetamorph.java new file mode 100644 index 0000000000..7f135eb61f --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NascentMetamorph.java @@ -0,0 +1,95 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksOrBlocksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentCard; +import mage.players.Player; +import mage.target.common.TargetOpponent; +import mage.util.functions.EmptyApplyToPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NascentMetamorph extends CardImpl { + + public NascentMetamorph(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.SHAPESHIFTER); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever Nascent Metamorph attacks or blocks, target opponent reveals cards from the top of their library until they reveal a creature card. Nascent Metamorph becomes a copy of that card until end of turn. Then that player puts all cards revealed this way on the bottom of their library in a random order. + Ability ability = new AttacksOrBlocksTriggeredAbility( + new NascentMetamorphEffect(), false + ); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private NascentMetamorph(final NascentMetamorph card) { + super(card); + } + + @Override + public NascentMetamorph copy() { + return new NascentMetamorph(this); + } +} + +class NascentMetamorphEffect extends OneShotEffect { + + NascentMetamorphEffect() { + super(Outcome.Benefit); + staticText = "target opponent reveals cards from the top of their library until they reveal a creature card. " + + "{this} becomes a copy of that card until end of turn. " + + "Then that player puts all cards revealed this way on the bottom of their library in a random order."; + } + + private NascentMetamorphEffect(final NascentMetamorphEffect effect) { + super(effect); + } + + @Override + public NascentMetamorphEffect copy() { + return new NascentMetamorphEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + Cards toReveal = new CardsImpl(); + Card toCopy = null; + for (Card card : player.getLibrary().getCards(game)) { + toReveal.add(card); + if (card == null || !card.isCreature()) { + continue; + } + toCopy = card; + break; + } + Permanent permanent = game.getPermanent(source.getSourceId()); + if (toCopy != null && permanent != null) { + game.copyPermanent(Duration.EndOfTurn, new PermanentCard( + toCopy, source.getControllerId(), game + ), permanent.getId(), source, new EmptyApplyToPermanent()); + } + player.revealCards(source, toReveal, game); + player.putCardsOnBottomOfLibrary(toReveal, game, source, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NaturalBalance.java b/Mage.Sets/src/mage/cards/n/NaturalBalance.java index 1dfc2c3bc2..7d9b4c3513 100644 --- a/Mage.Sets/src/mage/cards/n/NaturalBalance.java +++ b/Mage.Sets/src/mage/cards/n/NaturalBalance.java @@ -1,4 +1,3 @@ - package mage.cards.n; import mage.abilities.Ability; @@ -24,7 +23,6 @@ import java.util.List; import java.util.UUID; /** - * * @author Rene - bugisemail at gmail dot com */ public final class NaturalBalance extends CardImpl { @@ -100,9 +98,7 @@ public final class NaturalBalance extends CardImpl { } } for (Player player : toShuffle) { - if (player.isInGame()) { - player.shuffleLibrary(source, game); - } + player.shuffleLibrary(source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/n/NaturesClaim.java b/Mage.Sets/src/mage/cards/n/NaturesClaim.java index ff65e52242..2358f9f13a 100644 --- a/Mage.Sets/src/mage/cards/n/NaturesClaim.java +++ b/Mage.Sets/src/mage/cards/n/NaturesClaim.java @@ -54,7 +54,7 @@ class NaturesClaimEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent target = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent target = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (target != null) { Player player = game.getPlayer(target.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/n/NaturesPanoply.java b/Mage.Sets/src/mage/cards/n/NaturesPanoply.java index 83f0a58377..68c38ff43a 100644 --- a/Mage.Sets/src/mage/cards/n/NaturesPanoply.java +++ b/Mage.Sets/src/mage/cards/n/NaturesPanoply.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -11,18 +9,19 @@ import mage.constants.CardType; import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class NaturesPanoply extends CardImpl { public NaturesPanoply(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); // Strive - Nature's Panoply costs {2}{G} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{G}")); + // Choose any number of target creatures. Put a +1/+1 counter on each of them. Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); effect.setText("Choose any number of target creatures. Put a +1/+1 counter on each of them."); diff --git a/Mage.Sets/src/mage/cards/n/NaturesResurgence.java b/Mage.Sets/src/mage/cards/n/NaturesResurgence.java index c1c416b2f3..2c6eb292f7 100644 --- a/Mage.Sets/src/mage/cards/n/NaturesResurgence.java +++ b/Mage.Sets/src/mage/cards/n/NaturesResurgence.java @@ -63,7 +63,7 @@ class NaturesResurgenceEffect extends OneShotEffect { Player player = game.getPlayer(playerId); if (player != null) { int amount = player.getGraveyard().count(filter, game); - player.drawCards(amount, game); + player.drawCards(amount, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/n/NaturesWay.java b/Mage.Sets/src/mage/cards/n/NaturesWay.java index 3264e7d521..451ade62ee 100644 --- a/Mage.Sets/src/mage/cards/n/NaturesWay.java +++ b/Mage.Sets/src/mage/cards/n/NaturesWay.java @@ -8,8 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -20,12 +19,6 @@ import java.util.UUID; */ public final class NaturesWay extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public NaturesWay(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); @@ -38,10 +31,10 @@ public final class NaturesWay extends CardImpl { // It deals damage equal to its power to target creature you don't control. this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("It")); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); // second target + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); // second target } - public NaturesWay(final NaturesWay card) { + private NaturesWay(final NaturesWay card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/n/NavigatorsRuin.java b/Mage.Sets/src/mage/cards/n/NavigatorsRuin.java index d7ce59cf37..225e123880 100644 --- a/Mage.Sets/src/mage/cards/n/NavigatorsRuin.java +++ b/Mage.Sets/src/mage/cards/n/NavigatorsRuin.java @@ -1,21 +1,22 @@ - package mage.cards.n; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.TargetController; import mage.target.common.TargetOpponent; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class NavigatorsRuin extends CardImpl { @@ -24,15 +25,19 @@ public final class NavigatorsRuin extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); // Raid - At the beginning of your end step, if you attacked with a creature this turm, target opponent puts the top four cards of their library into their graveyard. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility(new PutLibraryIntoGraveTargetEffect(4), TargetController.YOU, false), + Ability ability = new ConditionalInterveningIfTriggeredAbility(new BeginningOfEndStepTriggeredAbility( + new PutLibraryIntoGraveTargetEffect(4), TargetController.YOU, false), RaidCondition.instance, - "Raid — At the beginning of your end step, if you attacked with a creature this turn, target opponent puts the top four cards of their library into their graveyard."); + "Raid — At the beginning of your end step, " + + "if you attacked this turn, target opponent mills four cards." + ); ability.addTarget(new TargetOpponent()); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } - public NavigatorsRuin(final NavigatorsRuin card) { + private NavigatorsRuin(final NavigatorsRuin card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/n/NayaSojourners.java b/Mage.Sets/src/mage/cards/n/NayaSojourners.java index 11b95f9caf..a3b52564ea 100644 --- a/Mage.Sets/src/mage/cards/n/NayaSojourners.java +++ b/Mage.Sets/src/mage/cards/n/NayaSojourners.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CycleTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.CyclingAbility; @@ -35,7 +35,7 @@ public final class NayaSojourners extends CardImpl { // When you cycle Naya Sojourners or it dies, you may put a +1/+1 counter on target creature. Ability ability1 = new CycleTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); - Ability ability2 = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + Ability ability2 = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); ability1.addTarget(new TargetCreaturePermanent()); ability2.addTarget(new TargetCreaturePermanent()); this.addAbility(ability1); diff --git a/Mage.Sets/src/mage/cards/n/NebelgastHerald.java b/Mage.Sets/src/mage/cards/n/NebelgastHerald.java index 833bae9953..115a9c4d25 100644 --- a/Mage.Sets/src/mage/cards/n/NebelgastHerald.java +++ b/Mage.Sets/src/mage/cards/n/NebelgastHerald.java @@ -1,10 +1,9 @@ package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; @@ -12,39 +11,35 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.FilterPermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class NebelgastHerald extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("{this} or another Spirit"); - private static final FilterCreaturePermanent filterTarget = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(SubType.SPIRIT.getPredicate()); - filterTarget.add(TargetController.OPPONENT.getControllerPredicate()); - } + private static final FilterPermanent filter = new FilterPermanent(SubType.SPIRIT, "Spirit"); public NebelgastHerald(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.SPIRIT); this.power = new MageInt(2); this.toughness = new MageInt(1); // Flash this.addAbility(FlashAbility.getInstance()); + // Flying this.addAbility(FlyingAbility.getInstance()); + // Whenever Nebelgast Herald or another Spirit enters the battlefield under your control, tap target creature an opponent controls. - Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new TapTargetEffect(), filter, false); - ability.addTarget(new TargetCreaturePermanent(filterTarget)); + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new TapTargetEffect(), filter, false, true + ); + ability.addTarget(new TargetOpponentsCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/Nebuchadnezzar.java b/Mage.Sets/src/mage/cards/n/Nebuchadnezzar.java index e1ba7288f1..7eee5eec44 100644 --- a/Mage.Sets/src/mage/cards/n/Nebuchadnezzar.java +++ b/Mage.Sets/src/mage/cards/n/Nebuchadnezzar.java @@ -66,30 +66,28 @@ class NebuchadnezzarEffect extends OneShotEffect { Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); MageObject sourceObject = game.getObject(source.getSourceId()); String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - if (opponent != null && sourceObject != null && !cardName.isEmpty()) { - int costX = source.getManaCostsToPay().getX(); - if (costX > 0 && !opponent.getHand().isEmpty()) { - Cards cards = new CardsImpl(); - while (costX > 0) { - Card card = opponent.getHand().getRandom(game); - if (!cards.contains(card.getId())) { - cards.add(card); - costX--; - } - if (opponent.getHand().size() <= cards.size()) { - break; - } - } - opponent.revealCards(sourceObject.getIdName(), cards, game); - for (Card cardToDiscard : cards.getCards(game)) { - if (cardToDiscard.getName().equals(cardName)) { - opponent.discard(cardToDiscard, source, game); - } - } - } + if (opponent == null || sourceObject == null || cardName.isEmpty()) { + return false; + } + int costX = source.getManaCostsToPay().getX(); + if (costX <= 0 || opponent.getHand().isEmpty()) { return true; } - return false; + Cards cards = new CardsImpl(); + while (costX > 0) { + Card card = opponent.getHand().getRandom(game); + if (!cards.contains(card.getId())) { + cards.add(card); + costX--; + } + if (opponent.getHand().size() <= cards.size()) { + break; + } + } + opponent.revealCards(sourceObject.getIdName(), cards, game); + cards.removeIf(uuid -> !cardName.equals(game.getCard(uuid).getName())); + opponent.discard(cards, source, game); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/n/NecromanticSummons.java b/Mage.Sets/src/mage/cards/n/NecromanticSummons.java index 772ec918db..ef4c4f2ea1 100644 --- a/Mage.Sets/src/mage/cards/n/NecromanticSummons.java +++ b/Mage.Sets/src/mage/cards/n/NecromanticSummons.java @@ -1,4 +1,3 @@ - package mage.cards.n; import java.util.UUID; @@ -34,8 +33,11 @@ public final class NecromanticSummons extends CardImpl { this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard"))); - // Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, that creature enters the battlefield with two additional +1/+1 counters on it. - this.getSpellAbility().addEffect(new InfoEffect("\"
Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, that creature enters the battlefield with two additional +1/+1 counters on it\"")); + // Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, + // that creature enters the battlefield with two additional +1/+1 counters on it. + this.getSpellAbility().addEffect(new InfoEffect("
Spell mastery — If there are two or more " + + "instant and/or sorcery cards in your graveyard, that creature enters the " + + "battlefield with two additional +1/+1 counters on it.")); } public NecromanticSummons(final NecromanticSummons card) { diff --git a/Mage.Sets/src/mage/cards/n/Necromentia.java b/Mage.Sets/src/mage/cards/n/Necromentia.java new file mode 100644 index 0000000000..888d90311d --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/Necromentia.java @@ -0,0 +1,127 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseACardNameEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.game.permanent.token.Token; +import mage.game.permanent.token.ZombieToken; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetOpponent; + +import java.util.HashSet; +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class Necromentia extends CardImpl { + + public Necromentia(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); + + // Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way. + this.getSpellAbility().addEffect( + new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NOT_BASIC_LAND_NAME) + ); + this.getSpellAbility().addEffect(new NecromentiaEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + private Necromentia(final Necromentia card) { + super(card); + } + + @Override + public Necromentia copy() { + return new Necromentia(this); + } +} + +class NecromentiaEffect extends OneShotEffect { + + NecromentiaEffect() { + super(Outcome.Benefit); + staticText = "Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way"; + } + + private NecromentiaEffect(NecromentiaEffect effect) { + super(effect); + } + + @Override + public NecromentiaEffect copy() { + return new NecromentiaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + Player controller = game.getPlayer(source.getControllerId()); + if (cardName != null && controller != null) { + FilterCard filter = new FilterCard("card named " + cardName); + filter.add(new NamePredicate(cardName)); + Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); + + // cards in Graveyard + int cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getGraveyard().count(filter, game)); + if (cardsCount > 0) { + filter.setMessage("card named " + cardName + " in the graveyard of " + targetPlayer.getName()); + TargetCard target = new TargetCard(0, cardsCount, Zone.GRAVEYARD, filter); + if (controller.choose(Outcome.Exile, targetPlayer.getGraveyard(), target, game)) { + controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); + } + } + + // cards in Hand + int numberOfCardsExiledFromHand = 0; + cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getHand().count(filter, game)); + if (cardsCount > 0) { + filter.setMessage("card named " + cardName + " in the hand of " + targetPlayer.getName()); + TargetCard target = new TargetCard(0, cardsCount, Zone.HAND, filter); + if (controller.choose(Outcome.Exile, targetPlayer.getHand(), target, game)) { + numberOfCardsExiledFromHand = target.getTargets().size(); + controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); + } + } else { + targetPlayer.revealCards(targetPlayer.getName() + "'s Hand", targetPlayer.getHand(), game); + } + + // cards in Library + Cards cardsInLibrary = new CardsImpl(); + cardsInLibrary.addAll(targetPlayer.getLibrary().getCards(game)); + cardsCount = (cardName.isEmpty() ? 0 : cardsInLibrary.count(filter, game)); + if (cardsCount > 0) { + filter.setMessage("card named " + cardName + " in the library of " + targetPlayer.getLogName()); + TargetCardInLibrary targetLib = new TargetCardInLibrary(0, cardsCount, filter); + if (controller.choose(Outcome.Exile, cardsInLibrary, targetLib, game)) { + controller.moveCards(new CardsImpl(targetLib.getTargets()), Zone.EXILED, source, game); + } + } else { + targetPlayer.revealCards(targetPlayer.getName() + "'s Library", new CardsImpl(new HashSet<>(targetPlayer.getLibrary().getCards(game))), game); + } + + targetPlayer.shuffleLibrary(source, game); + + if (numberOfCardsExiledFromHand > 0) { + game.getState().applyEffects(game); + Token zombieToken = new ZombieToken(); + zombieToken.putOntoBattlefield(numberOfCardsExiledFromHand, game, source.getId(), targetPlayer.getId()); + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/Necropanther.java b/Mage.Sets/src/mage/cards/n/Necropanther.java new file mode 100644 index 0000000000..4edad9309e --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/Necropanther.java @@ -0,0 +1,57 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Necropanther extends CardImpl { + + private static final FilterCard filter + = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + + static { + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + } + + public Necropanther(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); + + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Mutate {2}{W/B}{W/B} + this.addAbility(new MutateAbility(this, "{2}{W/B}{W/B}")); + + // Whenever this creature mutates, return target creature card with converted mana cost 3 or less from your graveyard to the battlefield. + Ability ability = new MutatesSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private Necropanther(final Necropanther card) { + super(card); + } + + @Override + public Necropanther copy() { + return new Necropanther(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/Necropede.java b/Mage.Sets/src/mage/cards/n/Necropede.java index 8819ff3e5e..5ec4b67ac1 100644 --- a/Mage.Sets/src/mage/cards/n/Necropede.java +++ b/Mage.Sets/src/mage/cards/n/Necropede.java @@ -5,7 +5,7 @@ package mage.cards.n; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class Necropede extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(InfectAbility.getInstance()); - Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance()), true); + Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance()), true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/Necropotence.java b/Mage.Sets/src/mage/cards/n/Necropotence.java index 3c1cc4bf28..107a5cae8a 100644 --- a/Mage.Sets/src/mage/cards/n/Necropotence.java +++ b/Mage.Sets/src/mage/cards/n/Necropotence.java @@ -119,7 +119,7 @@ class NecropotenceEffect extends OneShotEffect { card.setFaceDown(true, game); Effect returnToHandEffect = new ReturnToHandTargetEffect(); returnToHandEffect.setText("put that face down card into your hand"); - returnToHandEffect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + returnToHandEffect.setTargetPointer(new FixedTarget(card, game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnToHandEffect, TargetController.YOU), source); return true; } diff --git a/Mage.Sets/src/mage/cards/n/NefariousLich.java b/Mage.Sets/src/mage/cards/n/NefariousLich.java index 3a41d772f6..7b43335648 100644 --- a/Mage.Sets/src/mage/cards/n/NefariousLich.java +++ b/Mage.Sets/src/mage/cards/n/NefariousLich.java @@ -130,7 +130,7 @@ class NefariousLichLifeGainReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(event.getPlayerId()); if (controller != null) { - controller.drawCards(event.getAmount(), game); + controller.drawCards(event.getAmount(), source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/n/NefaroxOverlordOfGrixis.java b/Mage.Sets/src/mage/cards/n/NefaroxOverlordOfGrixis.java index be94372beb..0041df0be5 100644 --- a/Mage.Sets/src/mage/cards/n/NefaroxOverlordOfGrixis.java +++ b/Mage.Sets/src/mage/cards/n/NefaroxOverlordOfGrixis.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -34,7 +34,7 @@ public final class NefaroxOverlordOfGrixis extends CardImpl { this.addAbility(new ExaltedAbility()); // Whenever Nefarox, Overlord of Grixis attacks alone, defending player sacrifices a creature. this.addAbility(new AttacksAloneTriggeredAbility(new SacrificeEffect( - new FilterControlledCreaturePermanent("a creature"), 1, "defending player"))); + StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, 1, "defending player"))); } public NefaroxOverlordOfGrixis(final NefaroxOverlordOfGrixis card) { diff --git a/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java b/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java index bb93391c24..535dcff7a9 100644 --- a/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java +++ b/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java @@ -6,7 +6,9 @@ import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TrampleAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; @@ -18,6 +20,7 @@ import mage.target.TargetCard; import mage.target.common.TargetCardInHand; import java.util.UUID; +import java.util.stream.IntStream; /** * @author TheElk801 @@ -80,19 +83,13 @@ class NehebDreadhordeChampionEffect extends OneShotEffect { if (!player.choose(outcome, target, source.getSourceId(), game)) { return false; } - Cards cards = new CardsImpl(target.getTargets()); - int counter = 0; + int counter = player.discard(new CardsImpl(target.getTargets()), source, game).size(); Mana mana = new Mana(); - for (Card card : cards.getCards(game)) { - if (player.discard(card, source, game)) { - counter++; - mana.increaseRed(); - } - } - if (counter == 0) { + if (counter < 1) { return true; } - player.drawCards(counter, game); + IntStream.range(0, counter).forEach(x -> mana.increaseRed()); + player.drawCards(counter, source.getSourceId(), game); player.getManaPool().addMana(mana, game, source, true); return true; } diff --git a/Mage.Sets/src/mage/cards/n/NekoTe.java b/Mage.Sets/src/mage/cards/n/NekoTe.java index 3b056ae55c..656ce23f3a 100644 --- a/Mage.Sets/src/mage/cards/n/NekoTe.java +++ b/Mage.Sets/src/mage/cards/n/NekoTe.java @@ -6,6 +6,7 @@ import mage.abilities.Ability; import mage.abilities.common.DealsDamageToACreatureAttachedTriggeredAbility; import mage.abilities.common.DealsDamageToAPlayerAttachedTriggeredAbility; import mage.abilities.condition.common.SourceOnBattlefieldCondition; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; import mage.abilities.effects.ContinuousRuleModifyingEffect; @@ -19,6 +20,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.Zone; /** * @@ -33,7 +35,8 @@ public final class NekoTe extends CardImpl { // Whenever equipped creature deals damage to a creature, tap that creature. That creature doesn't untap during its controller's untap step for as long as Neko-Te remains on the battlefield. ContinuousRuleModifyingEffect skipUntapEffect = new DontUntapInControllersUntapStepTargetEffect(Duration.WhileOnBattlefield); skipUntapEffect.setText("That creature doesn't untap during its controller's untap step for as long as {this} remains on the battlefield"); - ConditionalContinuousRuleModifyingEffect effect = new ConditionalContinuousRuleModifyingEffect(skipUntapEffect, SourceOnBattlefieldCondition.instance); + ConditionalContinuousRuleModifyingEffect effect = new ConditionalContinuousRuleModifyingEffect(skipUntapEffect, + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD)); Ability ability = new DealsDamageToACreatureAttachedTriggeredAbility(new TapTargetEffect("that creature"), false, "equipped creature", false, true); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/n/NemesisOfMortals.java b/Mage.Sets/src/mage/cards/n/NemesisOfMortals.java index 45bcc9b02d..4f96bbbc4c 100644 --- a/Mage.Sets/src/mage/cards/n/NemesisOfMortals.java +++ b/Mage.Sets/src/mage/cards/n/NemesisOfMortals.java @@ -1,12 +1,14 @@ package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.MonstrosityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -16,6 +18,8 @@ import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; +import java.util.UUID; + /** * @author LevelX2 */ @@ -29,8 +33,10 @@ public final class NemesisOfMortals extends CardImpl { this.toughness = new MageInt(5); // Nemesis of Mortals costs {1} less to cast for each creature card in your graveyard. - Ability ability = new SimpleStaticAbility(Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(StaticFilters.FILTER_CARD_CREATURE)); + DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)); ability.setRuleAtTheTop(true); + ability.addHint(new ValueHint("Creature card in your graveyard", xValue)); this.addAbility(ability); // {7}{G}{G}: Monstrosity 5. This ability costs {1} less to activate for each creature card in your graveyard. diff --git a/Mage.Sets/src/mage/cards/n/NemesisOfReason.java b/Mage.Sets/src/mage/cards/n/NemesisOfReason.java index e3dd3529c9..bb8296c7b6 100644 --- a/Mage.Sets/src/mage/cards/n/NemesisOfReason.java +++ b/Mage.Sets/src/mage/cards/n/NemesisOfReason.java @@ -2,7 +2,6 @@ package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; @@ -16,27 +15,26 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author Loki */ public final class NemesisOfReason extends CardImpl { - public NemesisOfReason (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{B}"); + public NemesisOfReason(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}"); this.subtype.add(SubType.LEVIATHAN); this.subtype.add(SubType.HORROR); - + this.power = new MageInt(3); this.toughness = new MageInt(7); - + // Whenever Nemesis of Reason attacks, defending player puts the top ten cards of their library into their graveyard. - Effect effect = new PutLibraryIntoGraveTargetEffect(10); - effect.setText("defending player puts the top ten cards of their library into their graveyard"); - this.addAbility(new NemesisOfReasonTriggeredAbility(effect)); + this.addAbility(new NemesisOfReasonTriggeredAbility(new PutLibraryIntoGraveTargetEffect(10))); } - public NemesisOfReason (final NemesisOfReason card) { + public NemesisOfReason(final NemesisOfReason card) { super(card); } @@ -47,12 +45,12 @@ public final class NemesisOfReason extends CardImpl { } class NemesisOfReasonTriggeredAbility extends TriggeredAbilityImpl { - + NemesisOfReasonTriggeredAbility(Effect effect) { super(Zone.BATTLEFIELD, effect); } - NemesisOfReasonTriggeredAbility(final NemesisOfReasonTriggeredAbility ability) { + private NemesisOfReasonTriggeredAbility(final NemesisOfReasonTriggeredAbility ability) { super(ability); } @@ -68,9 +66,9 @@ class NemesisOfReasonTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.getSourceId()) ) { + if (event.getSourceId().equals(this.getSourceId())) { UUID defenderId = game.getCombat().getDefendingPlayerId(this.getSourceId(), game); - for (Effect effect : this.getEffects()) { + for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(defenderId)); } return true; @@ -80,6 +78,6 @@ class NemesisOfReasonTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever {this} attacks, defending player puts the top ten cards of their library into their graveyard."; + return "Whenever {this} attacks, defending player mills ten cards."; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NemesisTrap.java b/Mage.Sets/src/mage/cards/n/NemesisTrap.java index 3951249678..e930b75f3d 100644 --- a/Mage.Sets/src/mage/cards/n/NemesisTrap.java +++ b/Mage.Sets/src/mage/cards/n/NemesisTrap.java @@ -79,7 +79,7 @@ class NemesisTrapEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetedCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent targetedCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && targetedCreature != null) { // exile target diff --git a/Mage.Sets/src/mage/cards/n/NephaliaSmuggler.java b/Mage.Sets/src/mage/cards/n/NephaliaSmuggler.java index 04135db5ba..fbeed238d0 100644 --- a/Mage.Sets/src/mage/cards/n/NephaliaSmuggler.java +++ b/Mage.Sets/src/mage/cards/n/NephaliaSmuggler.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -18,18 +16,21 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** * @author noxx */ public final class NephaliaSmuggler extends CardImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another target creature you control"); + static { filter.add(AnotherPredicate.instance); } public NephaliaSmuggler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ROGUE); @@ -39,7 +40,7 @@ public final class NephaliaSmuggler extends CardImpl { // {3}{U}, {tap}: Exile another target creature you control, then return that card to the battlefield under your control. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetForSourceEffect(), new ManaCostsImpl("{3}{U}")); ability.addCost(new TapSourceCost()); - ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(true)); + ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(false)); ability.addTarget(new TargetControlledCreaturePermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NessianBoar.java b/Mage.Sets/src/mage/cards/n/NessianBoar.java index a81dac3410..56d6c7b15a 100644 --- a/Mage.Sets/src/mage/cards/n/NessianBoar.java +++ b/Mage.Sets/src/mage/cards/n/NessianBoar.java @@ -69,6 +69,6 @@ class NessianBoarEffect extends OneShotEffect { return false; } Player player = game.getPlayer(permanent.getControllerId()); - return player != null && player.drawCards(1, game) > 0; + return player != null && player.drawCards(1, source.getSourceId(), game) > 0; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NestOfScarabs.java b/Mage.Sets/src/mage/cards/n/NestOfScarabs.java index 2086264318..12109cab87 100644 --- a/Mage.Sets/src/mage/cards/n/NestOfScarabs.java +++ b/Mage.Sets/src/mage/cards/n/NestOfScarabs.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.dynamicvalue.common.EffectKeyValue; import mage.abilities.effects.common.CreateTokenEffect; @@ -15,8 +13,9 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.NestOfScarabsBlackInsectToken; +import java.util.UUID; + /** - * * @author stravant */ public final class NestOfScarabs extends CardImpl { @@ -63,7 +62,7 @@ class NestOfScarabsTriggeredAbility extends TriggeredAbilityImpl { if (permanent == null) { permanent = game.getPermanentEntering(event.getTargetId()); } - if (permanent.isCreature()) { + if (permanent != null && permanent.isCreature()) { getEffects().forEach(effect -> effect.setValue("countersAdded", event.getAmount())); return true; } diff --git a/Mage.Sets/src/mage/cards/n/NestingGrounds.java b/Mage.Sets/src/mage/cards/n/NestingGrounds.java new file mode 100644 index 0000000000..5a568efe2d --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NestingGrounds.java @@ -0,0 +1,117 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.Counter; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author anonymous + */ +public final class NestingGrounds extends CardImpl { + + public NestingGrounds(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {1}, {T}: Move a counter from target permanent you control onto another target permanent. Activate this ability only any time you could cast a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new NestingGroundsEffect(), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + // target 1 + TargetControlledPermanent target1 = new TargetControlledPermanent(new FilterControlledPermanent("permanent to remove counter from")); + target1.setTargetTag(1); + ability.addTarget(target1); + // target 2 + FilterPermanent filter = new FilterPermanent("permanent to put counter on"); + filter.add(new AnotherTargetPredicate(2)); + TargetPermanent target2 = new TargetPermanent(filter); + target2.setTargetTag(2); + ability.addTarget(target2); + + this.addAbility(ability); + } + + private NestingGrounds(final NestingGrounds card) { + super(card); + } + + @Override + public NestingGrounds copy() { + return new NestingGrounds(this); + } +} + +class NestingGroundsEffect extends OneShotEffect { + + public NestingGroundsEffect() { + super(Outcome.AIDontUseIt); + this.staticText = "Move a counter from target permanent you control onto another target permanent"; + } + + public NestingGroundsEffect(final NestingGroundsEffect effect) { + super(effect); + } + + @Override + public NestingGroundsEffect copy() { + return new NestingGroundsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent fromPermanent = game.getPermanent(source.getFirstTarget()); + Permanent toPermanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + if (fromPermanent == null + || toPermanent == null + || controller == null) { + return false; + } + + Set possibleChoices = new HashSet<>(fromPermanent.getCounters(game).keySet()); + if (possibleChoices.size() == 0) { + return false; + } + + Choice choice = new ChoiceImpl(); + choice.setChoices(possibleChoices); + if (controller.choose(outcome, choice, game)) { + String chosen = choice.getChoice(); + if (fromPermanent.getCounters(game).containsKey(chosen)) { + CounterType counterType = CounterType.findByName(chosen); + if (counterType != null) { + Counter counter = counterType.createInstance(); + fromPermanent.removeCounters(counterType.getName(), 1, game); + toPermanent.addCounters(counter, source, game); + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NetherbornAltar.java b/Mage.Sets/src/mage/cards/n/NetherbornAltar.java new file mode 100644 index 0000000000..aec050c7cc --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NetherbornAltar.java @@ -0,0 +1,96 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PutCountersSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.CommanderCardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class NetherbornAltar extends CardImpl { + + public NetherbornAltar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{B}"); + + // {T}, Put a soul counter on Netherborn Altar: Put your commander into your hand from the command zone. Then you lose 3 life for each soul counter on Netherborn Altar. + Ability ability = new SimpleActivatedAbility(new NetherbornAltarEffect(), new TapSourceCost()); + ability.addCost(new PutCountersSourceCost(CounterType.SOUL.createInstance())); + this.addAbility(ability); + } + + private NetherbornAltar(final NetherbornAltar card) { + super(card); + } + + @Override + public NetherbornAltar copy() { + return new NetherbornAltar(this); + } +} + +class NetherbornAltarEffect extends OneShotEffect { + + NetherbornAltarEffect() { + super(Outcome.Benefit); + staticText = "Put your commander into your hand from the command zone. Then you lose 3 life for each soul counter on {this}."; + } + + private NetherbornAltarEffect(final NetherbornAltarEffect effect) { + super(effect); + } + + @Override + public NetherbornAltarEffect copy() { + return new NetherbornAltarEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + List commandersInCommandZone = game + .getCommandersIds(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER) + .stream() + .map(game::getCard) + .filter(Objects::nonNull) + .filter(commander -> game.getState().getZone(commander.getId()) == Zone.COMMAND) + .collect(Collectors.toList()); + if (commandersInCommandZone.size() == 1) { + controller.moveCards(commandersInCommandZone.get(0), Zone.HAND, source, game); + } else if (commandersInCommandZone.size() == 2) { + Card firstCommander = commandersInCommandZone.get(0); + Card secondCommander = commandersInCommandZone.get(1); + if (controller.chooseUse(Outcome.ReturnToHand, "Return which commander to hand?", null, firstCommander.getName(), secondCommander.getName(), source, game)) { + controller.moveCards(firstCommander, Zone.HAND, source, game); + } else { + controller.moveCards(secondCommander, Zone.HAND, source, game); + } + } + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (permanent != null) { + int counterCount = permanent.getCounters(game).getCount(CounterType.SOUL); + controller.loseLife(3 * counterCount, game, false); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NethroiApexOfDeath.java b/Mage.Sets/src/mage/cards/n/NethroiApexOfDeath.java new file mode 100644 index 0000000000..deca60b786 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NethroiApexOfDeath.java @@ -0,0 +1,100 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.game.Game; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NethroiApexOfDeath extends CardImpl { + + public NethroiApexOfDeath(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Mutate {4}{G/W}{B}{B} + this.addAbility(new MutateAbility(this, "{4}{G/W}{B}{B}")); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Whenever this creature mutates, return any number of target creature cards with total power 10 or less from your graveyard to the battlefield. + Ability ability = new MutatesSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect() + .setText("return any number of target creature cards with total power 10 or less from your graveyard to the battlefield")); + ability.addTarget(new NethroiApexOfDeathTarget()); + this.addAbility(ability); + } + + private NethroiApexOfDeath(final NethroiApexOfDeath card) { + super(card); + } + + @Override + public NethroiApexOfDeath copy() { + return new NethroiApexOfDeath(this); + } +} + +class NethroiApexOfDeathTarget extends TargetCardInYourGraveyard { + + private static final FilterCard filter + = new FilterCreatureCard("creature cards with total power 10 or less from your graveyard"); + + NethroiApexOfDeathTarget() { + super(0, Integer.MAX_VALUE, filter); + } + + private NethroiApexOfDeathTarget(final NethroiApexOfDeathTarget target) { + super(target); + } + + @Override + public NethroiApexOfDeathTarget copy() { + return new NethroiApexOfDeathTarget(this); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + if (!super.canTarget(controllerId, id, source, game)) { + return false; + } + Card card = game.getCard(id); + if (card == null) { + return false; + } + int powerSum = this + .getTargets() + .stream() + .map(game::getCard) + .map(Card::getPower) + .mapToInt(MageInt::getValue) + .sum(); + return card.getPower().getValue() + powerSum <= 10; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/Neutralize.java b/Mage.Sets/src/mage/cards/n/Neutralize.java new file mode 100644 index 0000000000..80450d258c --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/Neutralize.java @@ -0,0 +1,37 @@ +package mage.cards.n; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Neutralize extends CardImpl { + + public Neutralize(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}"); + + // Counter target spell. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private Neutralize(final Neutralize card) { + super(card); + } + + @Override + public Neutralize copy() { + return new Neutralize(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/Nevermore.java b/Mage.Sets/src/mage/cards/n/Nevermore.java index 755bf786cd..36557baebe 100644 --- a/Mage.Sets/src/mage/cards/n/Nevermore.java +++ b/Mage.Sets/src/mage/cards/n/Nevermore.java @@ -1,6 +1,5 @@ package mage.cards.n; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; @@ -16,9 +15,11 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class Nevermore extends CardImpl { @@ -70,9 +71,8 @@ class NevermoreEffect2 extends ContinuousRuleModifyingEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == EventType.CAST_SPELL) { MageObject object = game.getObject(event.getSourceId()); - if (object != null && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) { - return true; - } + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(object, cardName, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java b/Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java new file mode 100644 index 0000000000..65f74d1832 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java @@ -0,0 +1,164 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.Collection; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NeyithOfTheDireHunt extends CardImpl { + + public NeyithOfTheDireHunt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever one or more creatures you control fight or become blocked, draw a card. + this.addAbility(new NeyithOfTheDireHuntTriggeredAbility()); + + // At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature's power until end of turn. That creature must be blocked this combat if able. + Ability ability = new BeginningOfCombatTriggeredAbility(new DoIfCostPaid( + new NeyithOfTheDireHuntEffect(), new ManaCostsImpl<>("{2}{R/G}") + ), TargetController.YOU, false); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private NeyithOfTheDireHunt(final NeyithOfTheDireHunt card) { + super(card); + } + + @Override + public NeyithOfTheDireHunt copy() { + return new NeyithOfTheDireHunt(this); + } +} + +// TODO: this needs to work with cards like Choking Vines +class NeyithOfTheDireHuntTriggeredAbility extends TriggeredAbilityImpl { + + NeyithOfTheDireHuntTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); + } + + private NeyithOfTheDireHuntTriggeredAbility(final NeyithOfTheDireHuntTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.BATCH_FIGHT + || event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP + || event.getType() == GameEvent.EventType.BATCH_BLOCK_NONCOMBAT; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Object value; + Set permanents; + switch (event.getType()) { + case BATCH_FIGHT: + value = game.getState().getValue("batchFight_" + event.getData()); + if (!(value instanceof Set)) { + return false; + } + permanents = (Set) value; + return permanents + .stream() + .map(mor -> mor.getPermanentOrLKIBattlefield(game)) + .filter(Objects::nonNull) + .map(Controllable::getControllerId) + .anyMatch(this.getControllerId()::equals); + case BATCH_BLOCK_NONCOMBAT: + value = game.getState().getValue("becameBlocked_" + event.getData()); + if (!(value instanceof Set)) { + return false; + } + permanents = (Set) value; + return permanents + .stream() + .map(mor -> mor.getPermanentOrLKIBattlefield(game)) + .filter(Objects::nonNull) + .map(Controllable::getControllerId) + .anyMatch(this.getControllerId()::equals); + case DECLARE_BLOCKERS_STEP: + return game.getCombat() + .getGroups() + .stream() + .filter(CombatGroup::getBlocked) + .map(CombatGroup::getAttackers) + .flatMap(Collection::stream) + .map(game::getControllerId) + .anyMatch(this.getControllerId()::equals); + default: + return false; + } + } + + @Override + public NeyithOfTheDireHuntTriggeredAbility copy() { + return new NeyithOfTheDireHuntTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever one or more creatures you control fight or become blocked, draw a card."; + } +} + +class NeyithOfTheDireHuntEffect extends OneShotEffect { + + NeyithOfTheDireHuntEffect() { + super(Outcome.Benefit); + staticText = "double target creature's power until end of turn. " + + "That creature must be blocked this combat if able"; + } + + private NeyithOfTheDireHuntEffect(final NeyithOfTheDireHuntEffect effect) { + super(effect); + } + + @Override + public NeyithOfTheDireHuntEffect copy() { + return new NeyithOfTheDireHuntEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + int power = permanent.getPower().getValue(); + game.addEffect(new BoostTargetEffect(power, 0, Duration.EndOfTurn), source); + game.addEffect(new MustBeBlockedByAtLeastOneTargetEffect(Duration.EndOfCombat), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NezahalPrimalTide.java b/Mage.Sets/src/mage/cards/n/NezahalPrimalTide.java index 3a28e370c3..4332784352 100644 --- a/Mage.Sets/src/mage/cards/n/NezahalPrimalTide.java +++ b/Mage.Sets/src/mage/cards/n/NezahalPrimalTide.java @@ -3,7 +3,7 @@ package mage.cards.n; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastOpponentTriggeredAbility; @@ -47,7 +47,7 @@ public final class NezahalPrimalTide extends CardImpl { this.toughness = new MageInt(7); // Nezahal, Primal Tide can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // You have no maximum hand size. Effect effect = new MaximumHandSizeControllerEffect(Integer.MAX_VALUE, Duration.WhileOnBattlefield, MaximumHandSizeControllerEffect.HandSizeModification.SET); diff --git a/Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java b/Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java new file mode 100644 index 0000000000..83a79d8d9e --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java @@ -0,0 +1,109 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NiambiEsteemedSpeaker extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another target creature you control"); + private static final FilterCard filter2 = new FilterCard("a legendary card"); + + static { + filter.add(AnotherPredicate.instance); + filter2.add(SuperType.LEGENDARY.getPredicate()); + } + + public NiambiEsteemedSpeaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Niambi, Esteemed Speaker enters the battlefield, you may return another target creature you control to its owner's hand. If you do, you gain life equal to that creature's converted mana cost. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), true); + ability.addEffect(new NiambiEsteemedSpeakerEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {1}{W}{U}, {T}, Discard a legendary card: Draw two cards. + ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(2), new ManaCostsImpl("{1}{W}{U}") + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardTargetCost(new TargetCardInHand(filter2))); + this.addAbility(ability); + } + + private NiambiEsteemedSpeaker(final NiambiEsteemedSpeaker card) { + super(card); + } + + @Override + public NiambiEsteemedSpeaker copy() { + return new NiambiEsteemedSpeaker(this); + } +} + +class NiambiEsteemedSpeakerEffect extends OneShotEffect { + + NiambiEsteemedSpeakerEffect() { + super(Outcome.Benefit); + staticText = "If you do, you gain life equal to that creature's converted mana cost."; + } + + private NiambiEsteemedSpeakerEffect(final NiambiEsteemedSpeakerEffect effect) { + super(effect); + } + + @Override + public NiambiEsteemedSpeakerEffect copy() { + return new NiambiEsteemedSpeakerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + Player player = game.getPlayer(source.getControllerId()); + if (permanent == null || player == null) { + return false; + } + return permanent.getConvertedManaCost() > 0 + && player.gainLife(permanent.getConvertedManaCost(), game, source) > 0; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java b/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java index f8b65004fe..64dbc70f1e 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java @@ -129,7 +129,7 @@ class NicolBolasDragonGodPlusOneEffect extends OneShotEffect { if (player == null) { return false; } - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); Set cardsOnBattlefield = new LinkedHashSet<>(); Set cards = new LinkedHashSet<>(); for (UUID opponentId : game.getState().getPlayersInRange(player.getId(), game)) { diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java b/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java index 476f219ad1..9fdcb6b28f 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java @@ -21,11 +21,12 @@ import mage.target.Target; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetCardInHand; import mage.target.common.TargetOpponent; -import mage.target.targetpointer.FixedTarget; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.target.targetpointer.FixedTarget; /** * @author Will @@ -168,9 +169,8 @@ class NicolBolasGodPharaohPlusTwoEffect extends OneShotEffect { if (card.isLand()) { continue; } - ContinuousEffect effect = new NicolBolasGodPharaohFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), - game.getState().getZoneChangeCounter(card.getId()))); + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.EndOfTurn, true); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); break; } while (library.hasCards() @@ -178,48 +178,3 @@ class NicolBolasGodPharaohPlusTwoEffect extends OneShotEffect { return true; } } - -class NicolBolasGodPharaohFromExileEffect extends AsThoughEffectImpl { - - NicolBolasGodPharaohFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "You may cast card from exile"; - } - - private NicolBolasGodPharaohFromExileEffect(final NicolBolasGodPharaohFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public NicolBolasGodPharaohFromExileEffect copy() { - return new NicolBolasGodPharaohFromExileEffect(this); - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId == null - || !sourceId.equals(getTargetPointer().getFirst(game, source)) - || !affectedControllerId.equals(source.getControllerId())) { - return false; - } - Card card = game.getCard(sourceId); - if (card == null - || game.getState().getZone(sourceId) != Zone.EXILED) { - return false; - } - Player controller = game.getPlayer(affectedControllerId); - if (controller == null) { - return false; - } - controller.setCastSourceIdWithAlternateMana( - sourceId, - null, - card.getSpellAbility().getCosts()); - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/n/NightSoil.java b/Mage.Sets/src/mage/cards/n/NightSoil.java index 7817ea9b16..4e72c20f7e 100644 --- a/Mage.Sets/src/mage/cards/n/NightSoil.java +++ b/Mage.Sets/src/mage/cards/n/NightSoil.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.ExileFromGraveCost; @@ -15,19 +13,19 @@ import mage.filter.common.FilterCreatureCard; import mage.game.permanent.token.SaprolingToken; import mage.target.common.TargetCardInASingleGraveyard; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class NightSoil extends CardImpl { public NightSoil(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{G}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}{G}"); // {1}, Exile two creature cards from a single graveyard: Create a 1/1 green Saproling creature token. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken()), new GenericManaCost(1)); - ability.addCost(new ExileFromGraveCost(new TargetCardInASingleGraveyard(2,2, new FilterCreatureCard("two creature cards from a single graveyard")))); + ability.addCost(new ExileFromGraveCost(new TargetCardInASingleGraveyard(2, 2, new FilterCreatureCard("two creature cards")))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NightscapeFamiliar.java b/Mage.Sets/src/mage/cards/n/NightscapeFamiliar.java index ad4ec58da7..2a7ae10e1d 100644 --- a/Mage.Sets/src/mage/cards/n/NightscapeFamiliar.java +++ b/Mage.Sets/src/mage/cards/n/NightscapeFamiliar.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; @@ -18,8 +16,9 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author North */ public final class NightscapeFamiliar extends CardImpl { @@ -33,7 +32,7 @@ public final class NightscapeFamiliar extends CardImpl { } public NightscapeFamiliar(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(1); @@ -41,6 +40,7 @@ public final class NightscapeFamiliar extends CardImpl { // Blue spells and red spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); + // {1}{B}: Regenerate Nightscape Familiar. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{1}{B}"))); } diff --git a/Mage.Sets/src/mage/cards/n/Nightsnare.java b/Mage.Sets/src/mage/cards/n/Nightsnare.java index a0326c9f49..e0535648e9 100644 --- a/Mage.Sets/src/mage/cards/n/Nightsnare.java +++ b/Mage.Sets/src/mage/cards/n/Nightsnare.java @@ -52,32 +52,27 @@ class NightsnareDiscardEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(targetPointer.getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); - if (player != null && controller != null) { - if (!player.getHand().isEmpty()) { - Cards revealedCards = new CardsImpl(); - revealedCards.addAll(player.getHand()); - Card sourceCard = game.getCard(source.getSourceId()); - player.revealCards(sourceCard != null ? sourceCard.getIdName() : "Discard", revealedCards, game); - // You may choose a nonland card from it. - if (controller.chooseUse(outcome, "Choose a a card to discard? (Otherwise " + player.getLogName() + " has to discard 2 cards).", source, game)) { - TargetCard target = new TargetCard(1, Zone.HAND, new FilterNonlandCard()); - if (controller.choose(Outcome.Benefit, revealedCards, target, game)) { - for (UUID targetId : target.getTargets()) { - Card card = revealedCards.get(targetId, game); - player.discard(card, source, game); - - } - } - - } else { - player.discard(2, false, source, game); - } - } - return true; - + if (player == null || controller == null) { + return false; } - return false; - + if (player.getHand().isEmpty()) { + return true; + } + Cards revealedCards = new CardsImpl(); + revealedCards.addAll(player.getHand()); + Card sourceCard = game.getCard(source.getSourceId()); + player.revealCards(sourceCard != null ? sourceCard.getIdName() : "Discard", revealedCards, game); + // You may choose a nonland card from it. + if (!controller.chooseUse(outcome, "Choose a card to discard? (Otherwise " + player.getLogName() + " has to discard 2 cards).", source, game)) { + player.discard(2, false, source, game); + return true; + } + TargetCard target = new TargetCard(1, Zone.HAND, new FilterNonlandCard()); + if (controller.choose(Outcome.Benefit, revealedCards, target, game)) { + Card card = revealedCards.get(target.getFirstTarget(), game); + player.discard(card, source, game); + } + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/n/NightsquadCommando.java b/Mage.Sets/src/mage/cards/n/NightsquadCommando.java new file mode 100644 index 0000000000..8ae8c016bc --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NightsquadCommando.java @@ -0,0 +1,50 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.RaidCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.common.RaidHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.HumanSoldierToken; +import mage.watchers.common.PlayerAttackedWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NightsquadCommando extends CardImpl { + + public NightsquadCommando(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // When Nightsquad Commando enters the battlefield, if you attacked this turn, create a 1/1 white Human Soldier creature token. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken())), + RaidCondition.instance, "When {this} enters the battlefield, " + + "if you attacked this turn, create a 1/1 white Human Soldier creature token.") + .setAbilityWord(AbilityWord.RAID) + .addHint(RaidHint.instance), + new PlayerAttackedWatcher()); + } + + private NightsquadCommando(final NightsquadCommando card) { + super(card); + } + + @Override + public NightsquadCommando copy() { + return new NightsquadCommando(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java b/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java index ecc51d332a..e26f856ce8 100644 --- a/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java +++ b/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java @@ -2,7 +2,7 @@ package mage.cards.n; import java.util.UUID; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -31,7 +31,7 @@ public final class NihilSpellbomb extends CardImpl { ability.addTarget(new TargetPlayer()); this.addAbility(ability); // When Nihil Spellbomb is put into a graveyard from the battlefield, you may pay {B}. If you do, draw a card. - this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{B}")), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{B}")), false)); } public NihilSpellbomb(final NihilSpellbomb card) { diff --git a/Mage.Sets/src/mage/cards/n/NihilisticGlee.java b/Mage.Sets/src/mage/cards/n/NihilisticGlee.java index 5590c72316..5cd67d38c8 100644 --- a/Mage.Sets/src/mage/cards/n/NihilisticGlee.java +++ b/Mage.Sets/src/mage/cards/n/NihilisticGlee.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.common.HellbentCondition; @@ -20,8 +18,9 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class NihilisticGlee extends CardImpl { @@ -34,7 +33,7 @@ public final class NihilisticGlee extends CardImpl { new LoseLifeTargetEffect(1), new ManaCostsImpl("{2}{B}") ); - ability.addEffect(new GainLifeEffect(1).setText("and you gain 1 life")); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addCost(new DiscardCardCost()); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java b/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java new file mode 100644 index 0000000000..f93bd3e489 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java @@ -0,0 +1,65 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldAllTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.CounterAnyPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NikaraLairScavenger extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another creature you control"); + + static { + filter.add(CounterAnyPredicate.instance); + filter.add(AnotherPredicate.instance); + } + + public NikaraLairScavenger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Partner with Yannik, Scavenging Sentinel + this.addAbility(new PartnerWithAbility("Yannik, Scavenging Sentinel")); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever another creature you control leaves the battlefield, if it had one or more counters on it, you draw a card and lose 1 life. + Ability ability = new LeavesBattlefieldAllTriggeredAbility(new DrawCardSourceControllerEffect(1) + .setText("if it had one or more counters on it, you draw a card"), filter); + ability.addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and")); + this.addAbility(ability); + } + + private NikaraLairScavenger(final NikaraLairScavenger card) { + super(card); + } + + @Override + public NikaraLairScavenger copy() { + return new NikaraLairScavenger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NimDeathmantle.java b/Mage.Sets/src/mage/cards/n/NimDeathmantle.java index 8815d4b752..005c8c843b 100644 --- a/Mage.Sets/src/mage/cards/n/NimDeathmantle.java +++ b/Mage.Sets/src/mage/cards/n/NimDeathmantle.java @@ -91,7 +91,7 @@ class NimDeathmantleTriggeredAbility extends TriggeredAbilityImpl { && !(permanent instanceof PermanentToken) && permanent.isCreature()) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/n/NimDevourer.java b/Mage.Sets/src/mage/cards/n/NimDevourer.java index d386661b5c..80c31583b9 100644 --- a/Mage.Sets/src/mage/cards/n/NimDevourer.java +++ b/Mage.Sets/src/mage/cards/n/NimDevourer.java @@ -1,59 +1,50 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.IsStepCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.PhaseStep; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class NimDevourer extends CardImpl { - - private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifact you control"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } public NimDevourer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(4); this.toughness = new MageInt(1); // Nim Devourer gets +1/+0 for each artifact you control. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(new PermanentsOnBattlefieldCount(filter), StaticValue.get(0), Duration.WhileOnBattlefield))); - + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new BoostSourceEffect(ArtifactYouControlCount.instance, StaticValue.get(0), Duration.WhileOnBattlefield)) + .addHint(ArtifactYouControlHint.instance) + ); + // {B}{B}: Return Nim Devourer from your graveyard to the battlefield, then sacrifice a creature. Activate this ability only during your upkeep. - Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, - new ReturnSourceFromGraveyardToBattlefieldEffect(), - new ManaCostsImpl("{B}{B}"), + Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, + new ReturnSourceFromGraveyardToBattlefieldEffect(), + new ManaCostsImpl("{B}{B}"), new IsStepCondition(PhaseStep.UPKEEP), null); ability.addEffect(new NimDevourerEffect()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/n/NimGrotesque.java b/Mage.Sets/src/mage/cards/n/NimGrotesque.java index 03442660b8..63cb3360fb 100644 --- a/Mage.Sets/src/mage/cards/n/NimGrotesque.java +++ b/Mage.Sets/src/mage/cards/n/NimGrotesque.java @@ -1,35 +1,37 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterControlledArtifactPermanent; + +import java.util.UUID; /** - * * @author Plopman */ public final class NimGrotesque extends CardImpl { public NimGrotesque(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{6}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(3); this.toughness = new MageInt(6); // Nim Grotesque gets +1/+0 for each artifact you control. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent()), StaticValue.get(0), Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new BoostSourceEffect(ArtifactYouControlCount.instance, StaticValue.get(0), Duration.WhileOnBattlefield)) + .addHint(ArtifactYouControlHint.instance) + ); } public NimGrotesque(final NimGrotesque card) { diff --git a/Mage.Sets/src/mage/cards/n/NinThePainArtist.java b/Mage.Sets/src/mage/cards/n/NinThePainArtist.java index 945c3047f5..c21c6fd23e 100644 --- a/Mage.Sets/src/mage/cards/n/NinThePainArtist.java +++ b/Mage.Sets/src/mage/cards/n/NinThePainArtist.java @@ -75,7 +75,7 @@ class NinThePainArtistEffect extends OneShotEffect { permanent.damage(source.getManaCostsToPay().getX(), source.getSourceId(), game, false, true); Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { - player.drawCards(source.getManaCostsToPay().getX(), game); + player.drawCards(source.getManaCostsToPay().getX(), source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/n/NineLives.java b/Mage.Sets/src/mage/cards/n/NineLives.java new file mode 100644 index 0000000000..aa28b27648 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NineLives.java @@ -0,0 +1,127 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.StateTriggeredAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.LoseGameSourceControllerEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; +import mage.game.events.PreventDamageEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author arcox + */ +public final class NineLives extends CardImpl { + + public NineLives(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); + + // Hexproof + this.addAbility(HexproofAbility.getInstance()); + + // If a source would deal damage to you, prevent that damage and put an incarnation counter on Nine Lives. + this.addAbility(new SimpleStaticAbility(new NineLivesPreventionEffect())); + + // When there are nine or more incarnation counters on Nine Lives, exile it. + this.addAbility(new NineLivesStateTriggeredAbility()); + + // When Nine Lives leaves the battlefield, you lose the game. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new LoseGameSourceControllerEffect(), false)); + } + + public NineLives(final NineLives card) { + super(card); + } + + @Override + public NineLives copy() { + return new NineLives(this); + } +} + + +class NineLivesPreventionEffect extends PreventionEffectImpl { + + public NineLivesPreventionEffect() { + super(Duration.WhileOnBattlefield); + staticText = "If a source would deal damage to you, prevent that damage and put an incarnation counter on {this}"; + } + + public NineLivesPreventionEffect(final NineLivesPreventionEffect effect) { + super(effect); + } + + @Override + public NineLivesPreventionEffect copy() { + return new NineLivesPreventionEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + GameEvent preventEvent = new PreventDamageEvent(source.getFirstTarget(), source.getSourceId(), source.getControllerId(), event.getAmount(), ((DamageEvent) event).isCombatDamage()); + if (!game.replaceEvent(preventEvent)) { + int damage = event.getAmount(); + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + Permanent nineLives = source.getSourcePermanentIfItStillExists(game); + if (nineLives != null) { + nineLives.addCounters(CounterType.INCARNATION.createInstance(1), source, game); + } + } + event.setAmount(0); + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), damage)); + } + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game)) { + return event.getTargetId().equals(source.getControllerId()); + } + return false; + } +} + + +class NineLivesStateTriggeredAbility extends StateTriggeredAbility { + + public NineLivesStateTriggeredAbility() { + super(Zone.BATTLEFIELD, new ExileSourceEffect()); + } + + public NineLivesStateTriggeredAbility(final NineLivesStateTriggeredAbility ability) { + super(ability); + } + + @Override + public NineLivesStateTriggeredAbility copy() { + return new NineLivesStateTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(getSourceId()); + return permanent != null && permanent.getCounters(game).getCount(CounterType.INCARNATION) >= 9; + } + + @Override + public String getRule() { + return "When there are nine or more incarnation counters on {this}, exile it."; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NissaNaturesArtisan.java b/Mage.Sets/src/mage/cards/n/NissaNaturesArtisan.java index 9403be8418..7fe5ec0999 100644 --- a/Mage.Sets/src/mage/cards/n/NissaNaturesArtisan.java +++ b/Mage.Sets/src/mage/cards/n/NissaNaturesArtisan.java @@ -1,4 +1,3 @@ - package mage.cards.n; import java.util.LinkedHashSet; @@ -36,7 +35,7 @@ import mage.players.Player; public final class NissaNaturesArtisan extends CardImpl { public NissaNaturesArtisan(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{4}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{G}{G}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NISSA); @@ -45,14 +44,18 @@ public final class NissaNaturesArtisan extends CardImpl { // +3: You gain 3 life. this.addAbility(new LoyaltyAbility(new GainLifeEffect(3), 3)); - // -4: Reveal the top two cards of your library. Put all land cards from among them onto the battlefield and the rest into your hand. + // -4: Reveal the top two cards of your library. Put all land cards from among + // them onto the battlefield and the rest into your hand. this.addAbility(new LoyaltyAbility(new NissaNaturesArtisanEffect(), -4)); // -12: Creatures you control get +5/+5 and gain trample until end of turn. Effect effect = new BoostControlledEffect(5, 5, Duration.EndOfTurn); effect.setText("Creature you control get +5/+5"); LoyaltyAbility ability = new LoyaltyAbility(effect, -12); - ability.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn)); + Effect effect2 = new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn); + effect2.setText("and gain trample until end of turn"); + ability.addEffect(effect2); this.addAbility(ability); } @@ -70,7 +73,8 @@ class NissaNaturesArtisanEffect extends OneShotEffect { public NissaNaturesArtisanEffect() { super(Outcome.PutCardInPlay); - staticText = "Reveal the top two cards of your library. Put all land cards from among them onto the battlefield and the rest into your hand"; + staticText = "Reveal the top two cards of your library. Put all land cards " + + "from among them onto the battlefield and the rest into your hand"; } public NissaNaturesArtisanEffect(final NissaNaturesArtisanEffect effect) { @@ -88,11 +92,13 @@ class NissaNaturesArtisanEffect extends OneShotEffect { if (!cards.isEmpty()) { controller.revealCards(sourceObject.getIdName(), cards, game); Set toBattlefield = new LinkedHashSet<>(); - for (Card card : cards.getCards(new FilterLandCard(), source.getSourceId(), source.getControllerId(), game)) { + for (Card card : cards.getCards(new FilterLandCard(), + source.getSourceId(), source.getControllerId(), game)) { cards.remove(card); toBattlefield.add(card); } - controller.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game, false, false, true, null); + controller.moveCards(toBattlefield, Zone.BATTLEFIELD, source, + game, false, false, true, null); controller.moveCards(cards, Zone.HAND, source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/n/NissasDefeat.java b/Mage.Sets/src/mage/cards/n/NissasDefeat.java index ca228c724b..09c1c4513b 100644 --- a/Mage.Sets/src/mage/cards/n/NissasDefeat.java +++ b/Mage.Sets/src/mage/cards/n/NissasDefeat.java @@ -83,7 +83,7 @@ class NissasDefeatEffect extends OneShotEffect { // If it was a Nissa planeswalker, draw a card if (filter.match(permanent, game) && controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/n/NissasRevelation.java b/Mage.Sets/src/mage/cards/n/NissasRevelation.java index 220dd0fe86..77f0c5cacd 100644 --- a/Mage.Sets/src/mage/cards/n/NissasRevelation.java +++ b/Mage.Sets/src/mage/cards/n/NissasRevelation.java @@ -69,7 +69,7 @@ class NissasRevelationEffect extends OneShotEffect { cards.add(card); controller.revealCards(sourceObject.getIdName(), cards, game); if (card.isCreature()) { - controller.drawCards(card.getPower().getValue(), game); + controller.drawCards(card.getPower().getValue(), source.getSourceId(), game); controller.gainLife(card.getToughness().getValue(), game, source); } } diff --git a/Mage.Sets/src/mage/cards/n/NivMizzetParun.java b/Mage.Sets/src/mage/cards/n/NivMizzetParun.java index 8cfccd5137..eabca9b30c 100644 --- a/Mage.Sets/src/mage/cards/n/NivMizzetParun.java +++ b/Mage.Sets/src/mage/cards/n/NivMizzetParun.java @@ -3,7 +3,7 @@ package mage.cards.n; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.DrawCardControllerTriggeredAbility; import mage.abilities.common.SpellCastAllTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; @@ -33,7 +33,7 @@ public final class NivMizzetParun extends CardImpl { this.toughness = new MageInt(5); // This spell can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Flying this.addAbility(FlyingAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/n/NobleBenefactor.java b/Mage.Sets/src/mage/cards/n/NobleBenefactor.java index d223291a3c..7abb0ccf4b 100644 --- a/Mage.Sets/src/mage/cards/n/NobleBenefactor.java +++ b/Mage.Sets/src/mage/cards/n/NobleBenefactor.java @@ -3,7 +3,7 @@ package mage.cards.n; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -33,7 +33,7 @@ public final class NobleBenefactor extends CardImpl { this.toughness = new MageInt(2); // When Noble Benefactor dies, each player may search their library for a card and put that card into their hand. Then each player who searched their library this way shuffles it. - this.addAbility(new DiesTriggeredAbility(new NobleBenefactorEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new NobleBenefactorEffect())); } public NobleBenefactor(final NobleBenefactor card) { diff --git a/Mage.Sets/src/mage/cards/n/NocturnalFeeder.java b/Mage.Sets/src/mage/cards/n/NocturnalFeeder.java new file mode 100644 index 0000000000..6f1dd06950 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NocturnalFeeder.java @@ -0,0 +1,46 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NocturnalFeeder extends CardImpl { + + public NocturnalFeeder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life. + Ability ability = new DiesSourceTriggeredAbility(new LoseLifeOpponentsEffect(2)); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); + this.addAbility(ability); + } + + private NocturnalFeeder(final NocturnalFeeder card) { + super(card); + } + + @Override + public NocturnalFeeder copy() { + return new NocturnalFeeder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NogginWhack.java b/Mage.Sets/src/mage/cards/n/NogginWhack.java index 5aa88225c4..f7627b5526 100644 --- a/Mage.Sets/src/mage/cards/n/NogginWhack.java +++ b/Mage.Sets/src/mage/cards/n/NogginWhack.java @@ -1,19 +1,12 @@ - package mage.cards.n; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.ProwlAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; @@ -21,14 +14,16 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPlayer; +import java.util.List; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class NogginWhack extends CardImpl { public NogginWhack(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.SORCERY},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.SORCERY}, "{2}{B}{B}"); this.subtype.add(SubType.ROGUE); @@ -40,7 +35,7 @@ public final class NogginWhack extends CardImpl { } - public NogginWhack(final NogginWhack card) { + private NogginWhack(final NogginWhack card) { super(card); } @@ -52,12 +47,12 @@ public final class NogginWhack extends CardImpl { class NogginWhackEffect extends OneShotEffect { - public NogginWhackEffect() { + NogginWhackEffect() { super(Outcome.Benefit); this.staticText = "Target player reveals three cards from their hand. You choose two of them. That player discards those cards"; } - public NogginWhackEffect(final NogginWhackEffect effect) { + private NogginWhackEffect(final NogginWhackEffect effect) { super(effect); } @@ -71,40 +66,35 @@ class NogginWhackEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(source.getFirstTarget()); Card sourceCard = game.getCard(source.getSourceId()); - if (controller != null && targetPlayer != null && sourceCard != null) { - Cards cardsInHand = new CardsImpl(); - cardsInHand.addAll(targetPlayer.getHand()); - - int count = Math.min(cardsInHand.size(), 3); - - TargetCard target = new TargetCard(count, Zone.HAND, new FilterCard()); - Cards revealedCards = new CardsImpl(); - - if (targetPlayer.chooseTarget(Outcome.Discard, cardsInHand, target, source, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - revealedCards.add(card); - } - } - } - - int cardsToDiscard = Math.min(revealedCards.size(), 2); - TargetCard targetInHand = new TargetCard(cardsToDiscard, cardsToDiscard, Zone.HAND, new FilterCard("card to discard")); - - if (!revealedCards.isEmpty()) { - targetPlayer.revealCards("Noggin Whack", revealedCards, game); - controller.chooseTarget(Outcome.Exile, revealedCards, targetInHand, source, game); - for (UUID cardId : targetInHand.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - controller.discard(card, source, game); - } - } - } - return true; + if (controller == null || targetPlayer == null || sourceCard == null) { + return false; } - return false; + Cards cardsInHand = new CardsImpl(); + cardsInHand.addAll(targetPlayer.getHand()); + + int count = Math.min(cardsInHand.size(), 3); + + TargetCard target = new TargetCard(count, Zone.HAND, new FilterCard()); + Cards revealedCards = new CardsImpl(); + + if (targetPlayer.chooseTarget(Outcome.Discard, cardsInHand, target, source, game)) { + List targets = target.getTargets(); + for (UUID targetId : targets) { + Card card = game.getCard(targetId); + if (card != null) { + revealedCards.add(card); + } + } + } + + int cardsToDiscard = Math.min(revealedCards.size(), 2); + TargetCard targetInHand = new TargetCard(cardsToDiscard, cardsToDiscard, Zone.HAND, new FilterCard("card to discard")); + + if (!revealedCards.isEmpty()) { + targetPlayer.revealCards(source, revealedCards, game); + controller.chooseTarget(Outcome.Exile, revealedCards, targetInHand, source, game); + targetPlayer.discard(new CardsImpl(targetInHand.getTargets()), source, game); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/n/NorwoodWarrior.java b/Mage.Sets/src/mage/cards/n/NorwoodWarrior.java index 5dcf344868..d45ecb8603 100644 --- a/Mage.Sets/src/mage/cards/n/NorwoodWarrior.java +++ b/Mage.Sets/src/mage/cards/n/NorwoodWarrior.java @@ -3,7 +3,7 @@ package mage.cards.n; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class NorwoodWarrior extends CardImpl { // Whenever Norwood Warrior becomes blocked, it gets +1/+1 until end of turn. Effect effect = new BoostSourceEffect(1, 1, Duration.EndOfTurn); effect.setText("it gets +1/+1 until end of turn"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public NorwoodWarrior(final NorwoodWarrior card) { diff --git a/Mage.Sets/src/mage/cards/n/NostalgicDreams.java b/Mage.Sets/src/mage/cards/n/NostalgicDreams.java index a9d8eec1ae..1cb7de7649 100644 --- a/Mage.Sets/src/mage/cards/n/NostalgicDreams.java +++ b/Mage.Sets/src/mage/cards/n/NostalgicDreams.java @@ -1,4 +1,3 @@ - package mage.cards.n; import mage.abilities.Ability; @@ -27,7 +26,8 @@ public final class NostalgicDreams extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}{G}"); // As an additional cost to cast Nostalgic Dreams, discard X cards. - this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), true)); + this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), false)); + // Return X target cards from your graveyard to your hand. Effect effect = new ReturnFromGraveyardToHandTargetEffect(); effect.setText("Return X target cards from your graveyard to your hand"); diff --git a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java index 651ff4a65d..edc5b0ed96 100644 --- a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java +++ b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java @@ -42,7 +42,9 @@ public final class NotOfThisWorld extends CardImpl { this.getSpellAbility().addTarget(new TargetStackObject(filter)); // Not of This World costs {7} less to cast if it targets a spell or ability that targets a creature you control with power 7 or greater. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(7, NotOfThisWorldCondition.instance))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, + new SpellCostReductionSourceEffect(7, NotOfThisWorldCondition.instance).setCanWorksOnStackOnly(true)) + ); } private NotOfThisWorld(final NotOfThisWorld card) { @@ -65,7 +67,6 @@ enum NotOfThisWorldCondition implements Condition { filter.add(TargetController.YOU.getControllerPredicate()); } - @Override public boolean apply(Game game, Ability source) { StackObject sourceSpell = game.getStack().getStackObject(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/n/NotionThief.java b/Mage.Sets/src/mage/cards/n/NotionThief.java index 79b0045f7f..697baa67ba 100644 --- a/Mage.Sets/src/mage/cards/n/NotionThief.java +++ b/Mage.Sets/src/mage/cards/n/NotionThief.java @@ -76,7 +76,7 @@ class NotionThiefReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(1, game, event.getAppliedEffects()); + player.drawCards(1, event.getSourceId(), game, event.getAppliedEffects()); } return true; } diff --git a/Mage.Sets/src/mage/cards/n/NotoriousThrong.java b/Mage.Sets/src/mage/cards/n/NotoriousThrong.java index cb553b3b77..a7fa06a59b 100644 --- a/Mage.Sets/src/mage/cards/n/NotoriousThrong.java +++ b/Mage.Sets/src/mage/cards/n/NotoriousThrong.java @@ -1,44 +1,47 @@ - package mage.cards.n; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.condition.common.ProwlCondition; +import mage.abilities.condition.common.ProwlCostWasPaidCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect; +import mage.abilities.hint.common.ProwlCostWasPaidHint; import mage.abilities.keyword.ProwlAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.token.FaerieRogueToken; import mage.players.Player; import mage.watchers.common.AmountOfDamageAPlayerReceivedThisTurnWatcher; +import java.util.UUID; + /** - * * @author LoneFox */ public final class NotoriousThrong extends CardImpl { public NotoriousThrong(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.SORCERY},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.SORCERY}, "{3}{U}"); this.subtype.add(SubType.ROGUE); // Prowl {5}{U} this.addAbility(new ProwlAbility(this, "{5}{U}")); + // create X 1/1 black Faerie Rogue creature tokens with flying, where X is the damage dealt to your opponents this turn. this.getSpellAbility().addEffect(new NotoriousThrongEffect()); this.getSpellAbility().addWatcher(new AmountOfDamageAPlayerReceivedThisTurnWatcher()); + // If Notorious Throng's prowl cost was paid, take an extra turn after this one. - Effect effect = new ConditionalOneShotEffect(new AddExtraTurnControllerEffect(), ProwlCondition.instance); + Effect effect = new ConditionalOneShotEffect(new AddExtraTurnControllerEffect(), ProwlCostWasPaidCondition.instance); effect.setText("If {this}'s prowl cost was paid, take an extra turn after this one."); this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addHint(ProwlCostWasPaidHint.instance); } public NotoriousThrong(final NotoriousThrong card) { @@ -71,12 +74,12 @@ class NotoriousThrongEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); AmountOfDamageAPlayerReceivedThisTurnWatcher watcher = game.getState().getWatcher(AmountOfDamageAPlayerReceivedThisTurnWatcher.class); - if(controller != null && watcher != null) { + if (controller != null && watcher != null) { int numTokens = 0; - for(UUID opponentId: game.getOpponents(controller.getId())) { + for (UUID opponentId : game.getOpponents(controller.getId())) { numTokens += watcher.getAmountOfDamageReceivedThisTurn(opponentId); } - if(numTokens > 0) { + if (numTokens > 0) { new CreateTokenEffect(new FaerieRogueToken(), numTokens).apply(game, source); } return true; diff --git a/Mage.Sets/src/mage/cards/n/NoxiousDragon.java b/Mage.Sets/src/mage/cards/n/NoxiousDragon.java index 0d029e3ad1..eafd0ece77 100644 --- a/Mage.Sets/src/mage/cards/n/NoxiousDragon.java +++ b/Mage.Sets/src/mage/cards/n/NoxiousDragon.java @@ -4,7 +4,7 @@ package mage.cards.n; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -38,7 +38,7 @@ public final class NoxiousDragon extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Noxious Dragon dies, you may destroy target creature with converted mana cost 3 or less. - Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect(), true); + Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), true); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/n/NoxiousGhoul.java b/Mage.Sets/src/mage/cards/n/NoxiousGhoul.java index c8bf3c0ffe..1409bedb6f 100644 --- a/Mage.Sets/src/mage/cards/n/NoxiousGhoul.java +++ b/Mage.Sets/src/mage/cards/n/NoxiousGhoul.java @@ -1,8 +1,7 @@ - package mage.cards.n; import mage.MageInt; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -11,11 +10,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.game.permanent.Permanent; import java.util.UUID; @@ -24,13 +19,11 @@ import java.util.UUID; */ public final class NoxiousGhoul extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - private static final FilterPermanent filter2 = new FilterPermanent(); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("all non-Zombie creatures"); + private static final FilterPermanent filter2 = new FilterPermanent(SubType.ZOMBIE, "Zombie"); static { - filter.add(CardType.CREATURE.getPredicate()); filter.add(Predicates.not(SubType.ZOMBIE.getPredicate())); - filter2.add(NoxiousGhoulPredicate.instance); } public NoxiousGhoul(UUID ownerId, CardSetInfo setInfo) { @@ -41,14 +34,12 @@ public final class NoxiousGhoul extends CardImpl { this.toughness = new MageInt(3); // Whenever Noxious Ghoul or another Zombie enters the battlefield, all non-Zombie creatures get -1/-1 until end of turn. - this.addAbility(new EntersBattlefieldAllTriggeredAbility( - new BoostAllEffect(-1, -1, Duration.EndOfTurn, filter, false), - filter2, "Whenever {this} or another Zombie enters the battlefield, " + - "all non-Zombie creatures get -1/-1 until end of turn." - )); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(new BoostAllEffect( + -1, -1, Duration.EndOfTurn, filter, false + ), filter2, false, true)); } - public NoxiousGhoul(final NoxiousGhoul card) { + private NoxiousGhoul(final NoxiousGhoul card) { super(card); } @@ -57,13 +48,3 @@ public final class NoxiousGhoul extends CardImpl { return new NoxiousGhoul(this); } } - -enum NoxiousGhoulPredicate implements ObjectSourcePlayerPredicate> { - instance; - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - return input.getObject().hasSubtype(SubType.ZOMBIE, game) - || input.getObject().getId().equals(input.getSourceId()); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NoxiousToad.java b/Mage.Sets/src/mage/cards/n/NoxiousToad.java index c05adfe612..cb9c93f579 100644 --- a/Mage.Sets/src/mage/cards/n/NoxiousToad.java +++ b/Mage.Sets/src/mage/cards/n/NoxiousToad.java @@ -3,7 +3,7 @@ package mage.cards.n; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class NoxiousToad extends CardImpl { this.toughness = new MageInt(1); // When Noxious Toad dies, each opponent discards a card. - this.addAbility(new DiesTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT), false)); } public NoxiousToad(final NoxiousToad card) { diff --git a/Mage.Sets/src/mage/cards/n/NoxiousVapors.java b/Mage.Sets/src/mage/cards/n/NoxiousVapors.java index 1e67b13521..1f5fdc483e 100644 --- a/Mage.Sets/src/mage/cards/n/NoxiousVapors.java +++ b/Mage.Sets/src/mage/cards/n/NoxiousVapors.java @@ -1,26 +1,28 @@ - package mage.cards.n; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.common.FilterNonlandCard; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInHand; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import mage.MageItem; /** - * * @author L_J */ public final class NoxiousVapors extends CardImpl { @@ -30,10 +32,9 @@ public final class NoxiousVapors extends CardImpl { // Each player reveals their hand, chooses one card of each color from it, then discards all other nonland cards. this.getSpellAbility().addEffect(new NoxiousVaporsEffect()); - } - public NoxiousVapors(final NoxiousVapors card) { + private NoxiousVapors(final NoxiousVapors card) { super(card); } @@ -47,12 +48,12 @@ class NoxiousVaporsEffect extends OneShotEffect { private static final FilterNonlandCard filter = new FilterNonlandCard(); - public NoxiousVaporsEffect() { + NoxiousVaporsEffect() { super(Outcome.Benefit); this.staticText = "Each player reveals their hand, chooses one card of each color from it, then discards all other nonland cards"; } - public NoxiousVaporsEffect(final NoxiousVaporsEffect effect) { + private NoxiousVaporsEffect(final NoxiousVaporsEffect effect) { super(effect); } @@ -64,37 +65,35 @@ class NoxiousVaporsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.revealCards(player.getName() + "'s hand", player.getHand(), game); - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Set chosenCards = new HashSet<>(); - chooseCardForColor(ObjectColor.WHITE, chosenCards, player, game, source); - chooseCardForColor(ObjectColor.BLUE, chosenCards, player, game, source); - chooseCardForColor(ObjectColor.BLACK, chosenCards, player, game, source); - chooseCardForColor(ObjectColor.RED, chosenCards, player, game, source); - chooseCardForColor(ObjectColor.GREEN, chosenCards, player, game, source); - - Set cards = player.getHand().getCards(game); - for (Card card : cards) { - if (card != null && !chosenCards.contains(card) && filter.match(card, game)) { - player.discard(card, source, game); - } - } - } - } - return true; + if (controller == null) { + return false; } - return false; + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + player.revealCards(player.getName() + "'s hand", player.getHand(), game); + } + } + + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + Set chosenCards = new HashSet<>(); + chooseCardForColor(ObjectColor.WHITE, chosenCards, player, game, source); + chooseCardForColor(ObjectColor.BLUE, chosenCards, player, game, source); + chooseCardForColor(ObjectColor.BLACK, chosenCards, player, game, source); + chooseCardForColor(ObjectColor.RED, chosenCards, player, game, source); + chooseCardForColor(ObjectColor.GREEN, chosenCards, player, game, source); + chosenCards.addAll(player.getHand().getCards(StaticFilters.FILTER_CARD_LAND, game)); + Cards cards = player.getHand().copy(); + cards.removeIf(chosenCards.stream().map(MageItem::getId).collect(Collectors.toSet())::contains); + player.discard(cards, source, game); + } + return true; } - + private void chooseCardForColor(ObjectColor color, Set chosenCards, Player player, Game game, Ability source) { FilterCard filter = new FilterCard(); filter.add(new ColorPredicate(color)); diff --git a/Mage.Sets/src/mage/cards/n/NullChamber.java b/Mage.Sets/src/mage/cards/n/NullChamber.java index c3562465a3..501e1a29cd 100644 --- a/Mage.Sets/src/mage/cards/n/NullChamber.java +++ b/Mage.Sets/src/mage/cards/n/NullChamber.java @@ -1,163 +1,159 @@ -package mage.cards.n; - -import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.common.AsEntersBattlefieldAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.OneShotEffect; -import mage.constants.SuperType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.repository.CardRepository; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetOpponent; -import mage.util.CardUtil; - -/** - * - * @author jeffwadsworth - */ -public final class NullChamber extends CardImpl { - - public NullChamber(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); - - this.addSuperType(SuperType.WORLD); - - // As Null Chamber enters the battlefield, you and an opponent each name a card other than a basic land card. - // The named cards can't be played. - this.addAbility(new AsEntersBattlefieldAbility(new NullChamberChooseEffect())); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NullChamberReplacementEffect())); - - } - - public NullChamber(final NullChamber card) { - super(card); - } - - @Override - public NullChamber copy() { - return new NullChamber(this); - } -} - -class NullChamberChooseEffect extends OneShotEffect { - - public static final String INFO_KEY_CONTROLLER = "CONTROLLER_NAMED_CARD"; - public static final String INFO_KEY_OPPONENT = "OPPONENT_NAMED_CARD"; - - public NullChamberChooseEffect() { - super(Outcome.AIDontUseIt); - staticText = "you and an opponent each name a card other than a basic land card"; - } - - public NullChamberChooseEffect(final NullChamberChooseEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - TargetOpponent chosenOpponent = new TargetOpponent(true); - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getPermanentEntering(source.getSourceId()); - if (sourceObject == null) { - sourceObject = game.getObject(source.getSourceId()); - } - if (controller != null - && sourceObject != null - && controller.choose(Outcome.Neutral, chosenOpponent, source.getSourceId(), game)) { - Player opponent = game.getPlayer(chosenOpponent.getFirstTarget()); - Choice cardChoice = new ChoiceImpl(); - cardChoice.setChoices(CardRepository.instance.getNotBasicLandNames()); - cardChoice.setMessage("Choose a card name other than a basic land card name"); - cardChoice.clearChoice(); - if (controller.choose(Outcome.Detriment, cardChoice, game)) { - String cardName = cardChoice.getChoice(); - if (!game.isSimulation()) { - game.informPlayers(sourceObject.getLogName() + ", controller named card: [" + cardName + ']'); - } - game.getState().setValue(source.getSourceId().toString() + INFO_KEY_CONTROLLER, cardName); - if (sourceObject instanceof Permanent) { - ((Permanent) sourceObject).addInfo(INFO_KEY_CONTROLLER, CardUtil.addToolTipMarkTags("Named card (Controller): " + cardName), game); - } - } - cardChoice.clearChoice(); - if (opponent != null - && opponent.choose(Outcome.Detriment, cardChoice, game)) { - String cardName = cardChoice.getChoice(); - if (!game.isSimulation()) { - game.informPlayers(sourceObject.getLogName() + ",chosen opponent named card: [" + cardName + ']'); - } - game.getState().setValue(source.getSourceId().toString() + INFO_KEY_OPPONENT, cardName); - if (sourceObject instanceof Permanent) { - ((Permanent) sourceObject).addInfo(INFO_KEY_OPPONENT, CardUtil.addToolTipMarkTags("Named card (Opponent): " + cardName), game); - } - return true; - } - } - return false; - } - - @Override - public NullChamberChooseEffect copy() { - return new NullChamberChooseEffect(this); - } -} - -class NullChamberReplacementEffect extends ContinuousRuleModifyingEffectImpl { - - public NullChamberReplacementEffect() { - super(Duration.WhileOnBattlefield, Outcome.AIDontUseIt); - staticText = "The named cards can't be played"; - } - - public NullChamberReplacementEffect(final NullChamberReplacementEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public NullChamberReplacementEffect copy() { - return new NullChamberReplacementEffect(this); - } - - @Override - public String getInfoMessage(Ability source, GameEvent event, Game game) { - MageObject mageObject = game.getObject(source.getSourceId()); - if (mageObject != null) { - return "You can't cast a spell with that name (" + mageObject.getLogName() + " in play)."; - } - return null; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.CAST_SPELL; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - MageObject object = game.getObject(event.getSourceId()); - if (object != null) { - return object.getName().equals(game.getState().getValue(source.getSourceId().toString() + NullChamberChooseEffect.INFO_KEY_CONTROLLER)) - || object.getName().equals(game.getState().getValue(source.getSourceId().toString() + NullChamberChooseEffect.INFO_KEY_OPPONENT)); - } - return false; - } -} +package mage.cards.n; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.repository.CardRepository; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetOpponent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class NullChamber extends CardImpl { + + public NullChamber(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); + + this.addSuperType(SuperType.WORLD); + + // As Null Chamber enters the battlefield, you and an opponent each name a card other than a basic land card. + // The named cards can't be played. + this.addAbility(new AsEntersBattlefieldAbility(new NullChamberChooseEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NullChamberReplacementEffect())); + + } + + public NullChamber(final NullChamber card) { + super(card); + } + + @Override + public NullChamber copy() { + return new NullChamber(this); + } +} + +class NullChamberChooseEffect extends OneShotEffect { + + public static final String INFO_KEY_CONTROLLER = "CONTROLLER_NAMED_CARD"; + public static final String INFO_KEY_OPPONENT = "OPPONENT_NAMED_CARD"; + + public NullChamberChooseEffect() { + super(Outcome.AIDontUseIt); + staticText = "you and an opponent each name a card other than a basic land card"; + } + + public NullChamberChooseEffect(final NullChamberChooseEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + TargetOpponent chosenOpponent = new TargetOpponent(true); + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getPermanentEntering(source.getSourceId()); + if (sourceObject == null) { + sourceObject = game.getObject(source.getSourceId()); + } + if (controller != null + && sourceObject != null + && controller.choose(Outcome.Neutral, chosenOpponent, source.getSourceId(), game)) { + Player opponent = game.getPlayer(chosenOpponent.getFirstTarget()); + Choice cardChoice = new ChoiceImpl(); + cardChoice.setChoices(CardRepository.instance.getNotBasicLandNames()); + cardChoice.setMessage("Choose a card name other than a basic land card name"); + cardChoice.clearChoice(); + if (controller.choose(Outcome.Detriment, cardChoice, game)) { + String cardName = cardChoice.getChoice(); + if (!game.isSimulation()) { + game.informPlayers(sourceObject.getLogName() + ", controller named card: [" + cardName + ']'); + } + game.getState().setValue(source.getSourceId().toString() + INFO_KEY_CONTROLLER, cardName); + if (sourceObject instanceof Permanent) { + ((Permanent) sourceObject).addInfo(INFO_KEY_CONTROLLER, CardUtil.addToolTipMarkTags("Named card (Controller): " + cardName), game); + } + } + cardChoice.clearChoice(); + if (opponent != null + && opponent.choose(Outcome.Detriment, cardChoice, game)) { + String cardName = cardChoice.getChoice(); + if (!game.isSimulation()) { + game.informPlayers(sourceObject.getLogName() + ",chosen opponent named card: [" + cardName + ']'); + } + game.getState().setValue(source.getSourceId().toString() + INFO_KEY_OPPONENT, cardName); + if (sourceObject instanceof Permanent) { + ((Permanent) sourceObject).addInfo(INFO_KEY_OPPONENT, CardUtil.addToolTipMarkTags("Named card (Opponent): " + cardName), game); + } + return true; + } + } + return false; + } + + @Override + public NullChamberChooseEffect copy() { + return new NullChamberChooseEffect(this); + } +} + +class NullChamberReplacementEffect extends ContinuousRuleModifyingEffectImpl { + + public NullChamberReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.AIDontUseIt); + staticText = "The named cards can't be played"; + } + + public NullChamberReplacementEffect(final NullChamberReplacementEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public NullChamberReplacementEffect copy() { + return new NullChamberReplacementEffect(this); + } + + @Override + public String getInfoMessage(Ability source, GameEvent event, Game game) { + MageObject mageObject = game.getObject(source.getSourceId()); + if (mageObject != null) { + return "You can't cast a spell with that name (" + mageObject.getName() + " in play)."; + } + return null; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.CAST_SPELL; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + MageObject object = game.getObject(event.getSourceId()); + if (object != null) { + return object.getName().equals(game.getState().getValue(source.getSourceId().toString() + NullChamberChooseEffect.INFO_KEY_CONTROLLER)) + || object.getName().equals(game.getState().getValue(source.getSourceId().toString() + NullChamberChooseEffect.INFO_KEY_OPPONENT)); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NykthosShrineToNyx.java b/Mage.Sets/src/mage/cards/n/NykthosShrineToNyx.java index 7c6194e059..0e4a2e3c77 100644 --- a/Mage.Sets/src/mage/cards/n/NykthosShrineToNyx.java +++ b/Mage.Sets/src/mage/cards/n/NykthosShrineToNyx.java @@ -1,5 +1,9 @@ package mage.cards.n; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.common.TapSourceCost; @@ -17,11 +21,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.players.Player; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - /** * @author LevelX2 */ @@ -69,14 +68,6 @@ class NykthosShrineToNyxManaAbility extends ActivatedManaAbilityImpl { return new NykthosShrineToNyxManaAbility(this); } - @Override - public List getNetMana(Game game) { - List netMana = new ArrayList<>(); - if (game != null) { - netMana.addAll(((ManaEffect) this.getEffects().get(0)).getNetMana(game, this)); - } - return netMana; - } } class NykthosDynamicManaEffect extends ManaEffect { diff --git a/Mage.Sets/src/mage/cards/n/NyxLotus.java b/Mage.Sets/src/mage/cards/n/NyxLotus.java index b657b8d0d4..577c1a76b1 100644 --- a/Mage.Sets/src/mage/cards/n/NyxLotus.java +++ b/Mage.Sets/src/mage/cards/n/NyxLotus.java @@ -1,5 +1,9 @@ package mage.cards.n; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTappedAbility; @@ -16,11 +20,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.players.Player; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - /** * @author TheElk801 */ @@ -68,14 +67,6 @@ class NyxLotusManaAbility extends ActivatedManaAbilityImpl { return new NyxLotusManaAbility(this); } - @Override - public List getNetMana(Game game) { - List netMana = new ArrayList<>(); - if (game != null) { - netMana.addAll(((ManaEffect) this.getEffects().get(0)).getNetMana(game, this)); - } - return netMana; - } } class NyxLotusDynamicManaEffect extends ManaEffect { diff --git a/Mage.Sets/src/mage/cards/o/OakhameAdversary.java b/Mage.Sets/src/mage/cards/o/OakhameAdversary.java index bfc25c3e5c..6cd85144ab 100644 --- a/Mage.Sets/src/mage/cards/o/OakhameAdversary.java +++ b/Mage.Sets/src/mage/cards/o/OakhameAdversary.java @@ -5,19 +5,21 @@ import mage.ObjectColor; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; +import mage.abilities.condition.common.OpponentControlsPermanentCondition; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.abilities.keyword.DeathtouchAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.ColorPredicate; + import java.util.UUID; -import mage.abilities.condition.common.OpponentControlsPermanentCondition; -import mage.constants.ComparisonType; /** * @author TheElk801 @@ -43,8 +45,8 @@ public final class OakhameAdversary extends CardImpl { // This spell costs {2} less to cast if your opponent controls a green permanent. this.addAbility(new SimpleStaticAbility( - Zone.STACK, new SpellCostReductionSourceEffect(2, condition) - ).setRuleAtTheTop(true)); + Zone.ALL, new SpellCostReductionSourceEffect(2, condition) + ).setRuleAtTheTop(true).addHint(new ConditionHint(condition, "Your opponent controls a green permanent"))); // Deathtouch this.addAbility(DeathtouchAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/o/OathOfLiliana.java b/Mage.Sets/src/mage/cards/o/OathOfLiliana.java index cfa8e7ebef..a5587a47bd 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfLiliana.java +++ b/Mage.Sets/src/mage/cards/o/OathOfLiliana.java @@ -14,7 +14,7 @@ import mage.abilities.effects.common.SacrificeOpponentsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -31,7 +31,7 @@ public final class OathOfLiliana extends CardImpl { addSuperType(SuperType.LEGENDARY); // When Oath of Liliana enters the battlefield, each opponent sacrifices a creature. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeOpponentsEffect(new FilterControlledCreaturePermanent("a creature")), false)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeOpponentsEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT), false)); // At the beginning of each end step, if a planeswalker entered the battlefield under your control this turn, create a 2/2 black Zombie creature token. this.addAbility(new ConditionalInterveningIfTriggeredAbility(new BeginningOfEndStepTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/o/OathOfLimDul.java b/Mage.Sets/src/mage/cards/o/OathOfLimDul.java index f650fe4815..ac972f5a18 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfLimDul.java +++ b/Mage.Sets/src/mage/cards/o/OathOfLimDul.java @@ -1,139 +1,143 @@ -package mage.cards.o; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.DiscardTargetCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCardInHand; -import mage.target.common.TargetControlledPermanent; - -/** - * - * @author jeffwadsworth - */ -public final class OathOfLimDul extends CardImpl { - - public OathOfLimDul(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}"); - - // Whenever you lose life, for each 1 life you lost, sacrifice a permanent other than Oath of Lim-Dul unless you discard a card. - this.addAbility(new OathOfLimDulTriggeredAbility()); - - // {B}{B}: Draw a card. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{B}{B}"))); - - } - - private OathOfLimDul(final OathOfLimDul card) { - super(card); - } - - @Override - public OathOfLimDul copy() { - return new OathOfLimDul(this); - } -} - -class OathOfLimDulTriggeredAbility extends TriggeredAbilityImpl { - - public OathOfLimDulTriggeredAbility() { - super(Zone.BATTLEFIELD, new OathOfLimDulEffect()); - } - - public OathOfLimDulTriggeredAbility(final OathOfLimDulTriggeredAbility ability) { - super(ability); - } - - @Override - public OathOfLimDulTriggeredAbility copy() { - return new OathOfLimDulTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.LOST_LIFE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(controllerId)) { - game.getState().setValue(sourceId.toString() + "oathOfLimDul", event.getAmount()); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever you lose life, for each 1 life you lost, sacrifice a permanent other than {this} unless you discard a card."; - } -} - -class OathOfLimDulEffect extends OneShotEffect { - - private static final FilterControlledPermanent filter = new FilterControlledPermanent("controlled permanent other than Oath of Lim-Dul"); - - static { - filter.add(AnotherPredicate.instance); - } - - public OathOfLimDulEffect() { - super(Outcome.Neutral); - } - - public OathOfLimDulEffect(final OathOfLimDulEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - int amount = (int) game.getState().getValue(source.getSourceId().toString() + "oathOfLimDul"); - if (amount > 0 - && controller != null) { - for (int i = 0; i < amount; i++) { - TargetControlledPermanent target = new TargetControlledPermanent(filter); - target.setNotTarget(true); - if (target.canChoose(controller.getId(), game) - && controller.choose(Outcome.Sacrifice, target, source.getSourceId(), game)) { - Cost cost = new DiscardTargetCost(new TargetCardInHand()); - if (cost.canPay(source, source.getSourceId(), controller.getId(), game) - && controller.chooseUse(Outcome.Benefit, - "Do you wish to discard a card rather than sacrifice the target permanent?", source, game)) { - cost.pay(source, game, source.getSourceId(), controller.getId(), true); - } else { - Permanent targetPermanent = game.getPermanent(target.getFirstTarget()); - if (targetPermanent != null) { - targetPermanent.sacrifice(source.getSourceId(), game); - } - } - } - } - return true; - } - return false; - } - - @Override - public OathOfLimDulEffect copy() { - return new OathOfLimDulEffect(this); - } - -} +package mage.cards.o; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author jeffwadsworth + */ +public final class OathOfLimDul extends CardImpl { + + public OathOfLimDul(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}"); + + // Whenever you lose life, for each 1 life you lost, sacrifice a permanent other than Oath of Lim-Dul unless you discard a card. + this.addAbility(new OathOfLimDulTriggeredAbility()); + + // {B}{B}: Draw a card. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{B}{B}"))); + + } + + private OathOfLimDul(final OathOfLimDul card) { + super(card); + } + + @Override + public OathOfLimDul copy() { + return new OathOfLimDul(this); + } +} + +class OathOfLimDulTriggeredAbility extends TriggeredAbilityImpl { + + public OathOfLimDulTriggeredAbility() { + super(Zone.BATTLEFIELD, new OathOfLimDulEffect()); + } + + public OathOfLimDulTriggeredAbility(final OathOfLimDulTriggeredAbility ability) { + super(ability); + } + + @Override + public OathOfLimDulTriggeredAbility copy() { + return new OathOfLimDulTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.LOST_LIFE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getPlayerId().equals(controllerId)) { + game.getState().setValue(sourceId.toString() + "oathOfLimDul", event.getAmount()); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever you lose life, for each 1 life you lost, sacrifice a permanent other than {this} unless you discard a card."; + } +} + +class OathOfLimDulEffect extends OneShotEffect { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("controlled permanent other than Oath of Lim-Dul to sacrifice"); + + static { + filter.add(AnotherPredicate.instance); + } + + public OathOfLimDulEffect() { + super(Outcome.Neutral); + } + + public OathOfLimDulEffect(final OathOfLimDulEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Boolean sacrificeDone = false; + int numberSacrificed = 0; + int numberToDiscard = 0; + int numberOfControlledPermanents = 0; + Player controller = game.getPlayer(source.getControllerId()); + int amountDamage = (int) game.getState().getValue(source.getSourceId().toString() + "oathOfLimDul"); + if (amountDamage > 0 + && controller != null) { + TargetControlledPermanent target = new TargetControlledPermanent(0, numberOfControlledPermanents, filter, true); + target.setNotTarget(true); + if (controller.choose(Outcome.Detriment, target, source.getSourceId(), game)) { + for (UUID targetPermanentId : target.getTargets()) { + Permanent permanent = game.getPermanent(targetPermanentId); + if (permanent != null + && permanent.sacrifice(source.getSourceId(), game)) { + numberSacrificed += 1; + sacrificeDone = true; + } + } + } + numberToDiscard = amountDamage - numberSacrificed; + Cost cost = new DiscardTargetCost(new TargetCardInHand(numberToDiscard, new FilterCard("card(s) in your hand to discard"))); + if (numberToDiscard > 0 + && cost.canPay(source, source.getSourceId(), controller.getId(), game)) { + return cost.pay(source, game, source.getSourceId(), controller.getId(), true); // discard cost paid simultaneously + } + } + return sacrificeDone; + } + + @Override + public OathOfLimDulEffect copy() { + return new OathOfLimDulEffect(this); + } + +} diff --git a/Mage.Sets/src/mage/cards/o/OathOfScholars.java b/Mage.Sets/src/mage/cards/o/OathOfScholars.java index f25287cc50..fb454039a7 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfScholars.java +++ b/Mage.Sets/src/mage/cards/o/OathOfScholars.java @@ -107,7 +107,7 @@ class OathOfScholarsEffect extends OneShotEffect { } if (firstPlayer.chooseUse(Outcome.AIDontUseIt, "Discard your hand and draw 3 cards?", source, game)) { firstPlayer.discard(firstPlayer.getHand().size(), true, source, game); - firstPlayer.drawCards(3, game); + firstPlayer.drawCards(3, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/o/OathOfTeferi.java b/Mage.Sets/src/mage/cards/o/OathOfTeferi.java index b71265aaf5..38f86916f9 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfTeferi.java +++ b/Mage.Sets/src/mage/cards/o/OathOfTeferi.java @@ -1,7 +1,5 @@ - package mage.cards.o; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -10,15 +8,9 @@ import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; -import mage.constants.SuperType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; @@ -27,8 +19,9 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class OathOfTeferi extends CardImpl { @@ -85,7 +78,7 @@ class OathOfTeferiBlinkEffect extends OneShotEffect { int zcc = permanent.getZoneChangeCounter(game); controller.moveCards(permanent, Zone.EXILED, source, game); //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setTargetPointer(new FixedTarget(permanent.getId(), zcc + 1)); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java b/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java index a2f1acfa26..8723578699 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java +++ b/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java @@ -1,44 +1,35 @@ - package mage.cards.o; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; -import mage.abilities.effects.Effect; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SetTargetPointer; -import mage.constants.TargetController; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterEnchantmentPermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class OathOfTheAncientWood extends CardImpl { - private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("Oath of the Ancient Wood or another enchantment"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - public OathOfTheAncientWood(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // Whenever Oath of the Ancient Wood or another enchantment enters the battlefield under your control, you may put a +1/+1 counter on target creature. - Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); - Ability ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, effect, filter, true, SetTargetPointer.NONE, null, true); + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_ENCHANTMENT_PERMANENT, true, true + ); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } - public OathOfTheAncientWood(final OathOfTheAncientWood card) { + private OathOfTheAncientWood(final OathOfTheAncientWood card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java b/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java index 43c3addb15..f635e8ea1f 100644 --- a/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java +++ b/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java @@ -79,7 +79,7 @@ class ObNixilisTheHateTwistedEffect extends OneShotEffect { if (player == null) { return false; } - player.drawCards(2, game); + player.drawCards(2, source.getSourceId(), game); return true; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/o/Oblation.java b/Mage.Sets/src/mage/cards/o/Oblation.java index 9abe58ad69..85af435e69 100644 --- a/Mage.Sets/src/mage/cards/o/Oblation.java +++ b/Mage.Sets/src/mage/cards/o/Oblation.java @@ -63,9 +63,9 @@ class OblationEffect extends OneShotEffect { player.moveCardToLibraryWithInfo(permanent, source.getSourceId(), game, Zone.BATTLEFIELD, true, true); player.shuffleLibrary(source, game); - game.applyEffects(); // so effects from creatures that were on the battlefield won't trigger from draw + game.getState().processAction(game); // so effects from creatures that were on the battlefield won't trigger from draw - player.drawCards(2, game); + player.drawCards(2, source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/o/Obliterate.java b/Mage.Sets/src/mage/cards/o/Obliterate.java index 8cfd66e0b3..03b8ddba0d 100644 --- a/Mage.Sets/src/mage/cards/o/Obliterate.java +++ b/Mage.Sets/src/mage/cards/o/Obliterate.java @@ -2,7 +2,7 @@ package mage.cards.o; import java.util.UUID; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.common.DestroyAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -30,7 +30,7 @@ public final class Obliterate extends CardImpl { // Obliterate can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Destroy all artifacts, creatures, and lands. They can't be regenerated. this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); } diff --git a/Mage.Sets/src/mage/cards/o/OboshThePreypiercer.java b/Mage.Sets/src/mage/cards/o/OboshThePreypiercer.java new file mode 100644 index 0000000000..721b6159da --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OboshThePreypiercer.java @@ -0,0 +1,108 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.keyword.CompanionAbility; +import mage.abilities.keyword.CompanionCondition; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OboshThePreypiercer extends CardImpl { + + public OboshThePreypiercer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B/R}{B/R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HELLION); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Companion — Your starting deck contains only cards with odd converted mana costs and land cards. + this.addAbility(new CompanionAbility(OboshThePreypiercerCompanionCondition.instance)); + + // If a source you control with an odd converted mana cost would deal damage to a permanent or player, it deals double that damage to that permanent or player instead. + this.addAbility(new SimpleStaticAbility(new OboshThePreypiercerEffect())); + } + + private OboshThePreypiercer(final OboshThePreypiercer card) { + super(card); + } + + @Override + public OboshThePreypiercer copy() { + return new OboshThePreypiercer(this); + } +} + +enum OboshThePreypiercerCompanionCondition implements CompanionCondition { + instance; + + @Override + public String getRule() { + return "Your starting deck contains only cards with odd converted mana costs and land cards."; + } + + @Override + public boolean isLegal(Set deck, int startingSize) { + return deck + .stream() + .filter(card -> !card.isLand()) + .mapToInt(MageObject::getConvertedManaCost) + .map(i -> i % 2) + .allMatch(i -> i == 1); + } +} + +class OboshThePreypiercerEffect extends ReplacementEffectImpl { + + OboshThePreypiercerEffect() { + super(Duration.WhileOnBattlefield, Outcome.Damage); + staticText = "If a source you control with an odd converted mana cost would deal damage " + + "to a permanent or player, it deals double that damage to that permanent or player instead."; + } + + private OboshThePreypiercerEffect(final OboshThePreypiercerEffect effect) { + super(effect); + } + + @Override + public OboshThePreypiercerEffect copy() { + return new OboshThePreypiercerEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType().equals(GameEvent.EventType.DAMAGE_PLAYER) + || event.getType().equals(GameEvent.EventType.DAMAGE_CREATURE) + || event.getType().equals(GameEvent.EventType.DAMAGE_PLANESWALKER); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + MageObject sourceObject = game.getObject(event.getSourceId()); + return sourceObject != null + && sourceObject.getConvertedManaCost() % 2 == 1 + && game.getControllerId(event.getSourceId()).equals(source.getControllerId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(CardUtil.addWithOverflowCheck(event.getAmount(), event.getAmount())); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/o/ObscuringAether.java b/Mage.Sets/src/mage/cards/o/ObscuringAether.java index 1b4e559253..6ede93fc40 100644 --- a/Mage.Sets/src/mage/cards/o/ObscuringAether.java +++ b/Mage.Sets/src/mage/cards/o/ObscuringAether.java @@ -1,7 +1,5 @@ - package mage.cards.o; -import java.util.UUID; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -14,10 +12,11 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.other.FaceDownCastablePredicate; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ObscuringAether extends CardImpl { @@ -25,7 +24,7 @@ public final class ObscuringAether extends CardImpl { private static final FilterCreatureCard filter = new FilterCreatureCard("Face-down creature spells"); static { - filter.add(FaceDownPredicate.instance); + filter.add(FaceDownCastablePredicate.instance); } public ObscuringAether(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/o/ObscuringHaze.java b/Mage.Sets/src/mage/cards/o/ObscuringHaze.java new file mode 100644 index 0000000000..7d6c23596d --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/ObscuringHaze.java @@ -0,0 +1,43 @@ +package mage.cards.o; + +import mage.abilities.condition.common.ControlACommanderCondition; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.effects.common.PreventAllDamageByAllObjectsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterObject; +import mage.filter.common.FilterOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ObscuringHaze extends CardImpl { + + private static final FilterObject filter + = new FilterOpponentsCreaturePermanent("creatures your opponents control"); + + public ObscuringHaze(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // If you control a commander, you may cast this spell without paying its mana cost. + this.addAbility(new AlternativeCostSourceAbility(null, ControlACommanderCondition.instance)); + + // Prevent all damage that would be dealt this turn by creatures your opponents control. + this.getSpellAbility().addEffect(new PreventAllDamageByAllObjectsEffect( + filter, Duration.EndOfTurn, false + )); + } + + private ObscuringHaze(final ObscuringHaze card) { + super(card); + } + + @Override + public ObscuringHaze copy() { + return new ObscuringHaze(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java b/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java new file mode 100644 index 0000000000..560a10f046 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java @@ -0,0 +1,54 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author mikalinn777 + */ +public final class ObsessiveStitcher extends CardImpl { + + public ObsessiveStitcher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // {T}: Draw a card, then discard a card. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawDiscardControllerEffect(), new TapSourceCost())); + + // {2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), new ManaCostsImpl("{2}{U}{B}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.addAbility(ability); + + } + + public ObsessiveStitcher(final ObsessiveStitcher card) { + super(card); + } + + @Override + public ObsessiveStitcher copy() { + return new ObsessiveStitcher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/ObsidianFireheart.java b/Mage.Sets/src/mage/cards/o/ObsidianFireheart.java index 8167bc6879..76badc1228 100644 --- a/Mage.Sets/src/mage/cards/o/ObsidianFireheart.java +++ b/Mage.Sets/src/mage/cards/o/ObsidianFireheart.java @@ -1,5 +1,3 @@ - - package mage.cards.o; import java.util.UUID; @@ -7,8 +5,10 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -17,6 +17,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; @@ -33,7 +34,9 @@ import mage.target.common.TargetLandPermanent; */ public final class ObsidianFireheart extends CardImpl { - private static final String rule = "For as long as that land has a blaze counter on it, it has \"At the beginning of your upkeep, this land deals 1 damage to you.\" (The land continues to burn after Obsidian Fireheart has left the battlefield.)"; + private static final String rule = "For as long as that land has a blaze counter " + + "on it, it has \"At the beginning of your upkeep, this land deals 1 damage " + + "to you.\" (The land continues to burn after Obsidian Fireheart has left the battlefield.)"; private static final FilterLandPermanent filter = new FilterLandPermanent("land without a blaze counter on it"); static { @@ -41,10 +44,9 @@ public final class ObsidianFireheart extends CardImpl { } public ObsidianFireheart(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}{R}"); this.subtype.add(SubType.ELEMENTAL); - this.power = new MageInt(4); this.toughness = new MageInt(4); @@ -52,14 +54,12 @@ public final class ObsidianFireheart extends CardImpl { // For as long as that land has a blaze counter on it, it has "At the beginning // of your upkeep, this land deals 1 damage to you." (The land continues to burn // after Obsidian Fireheart has left the battlefield.) - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.BLAZE.createInstance()),new ManaCostsImpl("{1}{R}{R}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new AddCountersTargetEffect(CounterType.BLAZE.createInstance()), + new ManaCostsImpl("{1}{R}{R}")); ability.addTarget(new TargetLandPermanent(filter)); - Effect effect = new ObsidianFireheartGainAbilityEffect( - new BeginningOfUpkeepTriggeredAbility( - new DamageControllerEffect(1), - TargetController.YOU, - false), - Duration.Custom, rule); + OneShotEffect effect = new ObsidianFireheartOneShotEffect(); + effect.setText(rule); ability.addEffect(effect); this.addAbility(ability); @@ -75,6 +75,53 @@ public final class ObsidianFireheart extends CardImpl { } } +class ObsidianFireheartOneShotEffect extends OneShotEffect { + + public ObsidianFireheartOneShotEffect() { + super(Outcome.Detriment); + } + + public ObsidianFireheartOneShotEffect(final ObsidianFireheartOneShotEffect effect) { + super(effect); + } + + @Override + public ObsidianFireheartOneShotEffect copy() { + return new ObsidianFireheartOneShotEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + // If the owner/controller of this card leaves the game, the blaze counters + // presence on the targeted land will continue to deal 1 damage every upkeep + // to the lands controller. + Permanent targetLand = game.getPermanent(source.getFirstTarget()); + if (targetLand != null + && source.getTargets().get(0) != null) { + ContinuousEffect effect = new ObsidianFireheartGainAbilityEffect( + new BeginningOfUpkeepTriggeredAbility( + new DamageControllerEffect(1), + TargetController.YOU, + false), + Duration.Custom, ""); + + // add a new independent ability that is not reliant on the source ability + SimpleStaticAbility gainAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + + // set sourcecard of the independent ability to the targeted permanent of the source ability + gainAbility.setSourceId(targetLand.getId()); + + // the target of the source ability is added to the new independent ability + gainAbility.getTargets().add(source.getTargets().get(0)); + + // add the continuous effect to the game with the independent ability + game.addEffect(effect, gainAbility); + + return true; + } + return false; + } +} class ObsidianFireheartGainAbilityEffect extends GainAbilityTargetEffect { @@ -88,8 +135,9 @@ class ObsidianFireheartGainAbilityEffect extends GainAbilityTargetEffect { @Override public boolean isInactive(Ability source, Game game) { - Permanent land = game.getPermanent(this.targetPointer.getFirst(game, source)); - if (land != null && land.getCounters(game).getCount(CounterType.BLAZE) < 1) { + Permanent targetLand = game.getPermanent(this.targetPointer.getFirst(game, source)); + if (targetLand != null + && targetLand.getCounters(game).getCount(CounterType.BLAZE) < 1) { return true; } return false; diff --git a/Mage.Sets/src/mage/cards/o/ObzedatGhostCouncil.java b/Mage.Sets/src/mage/cards/o/ObzedatGhostCouncil.java index 8600415009..f1700a55cf 100644 --- a/Mage.Sets/src/mage/cards/o/ObzedatGhostCouncil.java +++ b/Mage.Sets/src/mage/cards/o/ObzedatGhostCouncil.java @@ -42,6 +42,7 @@ public final class ObzedatGhostCouncil extends CardImpl { ability.addEffect(new GainLifeEffect(2).concatBy("and")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); + //At the beginning of your end step you may exile Obzedat. If you do, return it to the battlefield under its owner's //control at the beginning of your next upkeep. It gains haste. Ability ability2 = new BeginningOfYourEndStepTriggeredAbility(new ObzedatGhostCouncilExileSourceEffect(), true); diff --git a/Mage.Sets/src/mage/cards/o/Oculus.java b/Mage.Sets/src/mage/cards/o/Oculus.java index 77e73712e1..ee4129d7b9 100644 --- a/Mage.Sets/src/mage/cards/o/Oculus.java +++ b/Mage.Sets/src/mage/cards/o/Oculus.java @@ -4,7 +4,7 @@ package mage.cards.o; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class Oculus extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); } public Oculus (final Oculus card) { diff --git a/Mage.Sets/src/mage/cards/o/OdricLunarchMarshal.java b/Mage.Sets/src/mage/cards/o/OdricLunarchMarshal.java index 853b35b877..bd0a2c6af4 100644 --- a/Mage.Sets/src/mage/cards/o/OdricLunarchMarshal.java +++ b/Mage.Sets/src/mage/cards/o/OdricLunarchMarshal.java @@ -1,7 +1,6 @@ package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfCombatTriggeredAbility; @@ -15,14 +14,15 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; +import java.util.UUID; + /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class OdricLunarchMarshal extends CardImpl { public OdricLunarchMarshal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); @@ -44,34 +44,34 @@ public final class OdricLunarchMarshal extends CardImpl { } class OdricLunarchMarshalEffect extends OneShotEffect { - + private static final FilterControlledCreaturePermanent filterFirstStrike = new FilterControlledCreaturePermanent(); - private static final FilterControlledCreaturePermanent filterFlying = new FilterControlledCreaturePermanent(); + private static final FilterControlledCreaturePermanent filterFlying = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterDeathtouch = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterDoubleStrike = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterHaste = new FilterControlledCreaturePermanent(); - private static final FilterControlledCreaturePermanent filterHexproof = new FilterControlledCreaturePermanent(); + private static final FilterControlledCreaturePermanent filterHexproof = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterIndestructible = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterLifelink = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterMenace = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterReach = new FilterControlledCreaturePermanent(); - private static final FilterControlledCreaturePermanent filterSkulk = new FilterControlledCreaturePermanent(); + private static final FilterControlledCreaturePermanent filterSkulk = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterTrample = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterVigilance = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterCreatures = new FilterControlledCreaturePermanent(); - static { + static { filterFirstStrike.add(new AbilityPredicate(FirstStrikeAbility.class)); filterFlying.add(new AbilityPredicate(FlyingAbility.class)); filterDeathtouch.add(new AbilityPredicate(DeathtouchAbility.class)); filterDoubleStrike.add(new AbilityPredicate(DoubleStrikeAbility.class)); filterHaste.add(new AbilityPredicate(HasteAbility.class)); - filterHexproof.add(new AbilityPredicate(HexproofAbility.class)); + filterHexproof.add(new AbilityPredicate(HexproofBaseAbility.class)); filterIndestructible.add(new AbilityPredicate(IndestructibleAbility.class)); filterLifelink.add(new AbilityPredicate(LifelinkAbility.class)); filterMenace.add(new AbilityPredicate(MenaceAbility.class)); filterReach.add(new AbilityPredicate(ReachAbility.class)); - filterSkulk.add(new AbilityPredicate(SkulkAbility.class)); + filterSkulk.add(new AbilityPredicate(SkulkAbility.class)); filterTrample.add(new AbilityPredicate(TrampleAbility.class)); filterVigilance.add(new AbilityPredicate(VigilanceAbility.class)); } @@ -92,17 +92,17 @@ class OdricLunarchMarshalEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - + // First strike if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source); } - + // Flying if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source); } - + // Deathtouch if (game.getBattlefield().contains(filterDeathtouch, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source); @@ -112,37 +112,37 @@ class OdricLunarchMarshalEffect extends OneShotEffect { if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source); } - + // Haste if (game.getBattlefield().contains(filterHaste, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source); } - + // Hexproof if (game.getBattlefield().contains(filterHexproof, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(HexproofAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source); } - + // Indestructible if (game.getBattlefield().contains(filterIndestructible, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source); } - + // Lifelink if (game.getBattlefield().contains(filterLifelink, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source); } - + // Menace if (game.getBattlefield().contains(filterMenace, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(new MenaceAbility(), Duration.EndOfTurn, filterCreatures), source); } - + // Reach if (game.getBattlefield().contains(filterReach, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(ReachAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source); } - + // Skulk if (game.getBattlefield().contains(filterSkulk, source.getControllerId(), 1, game)) { game.addEffect(new GainAbilityControlledEffect(new SkulkAbility(), Duration.EndOfTurn, filterCreatures), source); diff --git a/Mage.Sets/src/mage/cards/o/OfOneMind.java b/Mage.Sets/src/mage/cards/o/OfOneMind.java new file mode 100644 index 0000000000..a38184af0d --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OfOneMind.java @@ -0,0 +1,60 @@ +package mage.cards.o; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OfOneMind extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.HUMAN); + private static final FilterPermanent filter2 = new FilterControlledCreaturePermanent(); + + static { + filter2.add(Predicates.not(SubType.HUMAN.getPredicate())); + } + + private static final Condition condition = new CompoundCondition( + "you control a Human creature and a non-Human creature", + new PermanentsOnTheBattlefieldCondition(filter), + new PermanentsOnTheBattlefieldCondition(filter2) + ); + + public OfOneMind(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); + + // This spell costs {2} less to cast if you control a Human creature and a non-Human creature. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, condition)) + .setRuleAtTheTop(true) + .addHint(new ConditionHint(condition, "You control a Human creature and a non-Human creature")) + ); + + // Draw two cards. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); + } + + private OfOneMind(final OfOneMind card) { + super(card); + } + + @Override + public OfOneMind copy() { + return new OfOneMind(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OffspringsRevenge.java b/Mage.Sets/src/mage/cards/o/OffspringsRevenge.java new file mode 100644 index 0000000000..43aad22f05 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OffspringsRevenge.java @@ -0,0 +1,103 @@ +package mage.cards.o; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OffspringsRevenge extends CardImpl { + + private static final FilterCard filter + = new FilterCreatureCard("red, white, or black creature card from your graveyard"); + + static { + filter.add(Predicates.or( + new ColorPredicate(ObjectColor.RED), + new ColorPredicate(ObjectColor.WHITE), + new ColorPredicate(ObjectColor.BLACK) + )); + } + + public OffspringsRevenge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}{W}{B}"); + + // At the beginning of combat on your turn, exile target red, white, or black creature card from your graveyard. Create a token that's a copy of that card, except it's 1/1. It gains haste until your next turn. + Ability ability = new BeginningOfCombatTriggeredAbility( + new OffspringsRevengeEffect(), TargetController.YOU, false + ); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private OffspringsRevenge(final OffspringsRevenge card) { + super(card); + } + + @Override + public OffspringsRevenge copy() { + return new OffspringsRevenge(this); + } +} + +class OffspringsRevengeEffect extends OneShotEffect { + + OffspringsRevengeEffect() { + super(Outcome.Benefit); + staticText = "exile target red, white, or black creature card from your graveyard. " + + "Create a token that's a copy of that card, except it's 1/1. It gains haste until your next turn."; + } + + private OffspringsRevengeEffect(final OffspringsRevengeEffect effect) { + super(effect); + } + + @Override + public OffspringsRevengeEffect copy() { + return new OffspringsRevengeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect( + source.getControllerId(), null, false, 1, false, + false, null, 1, 1, false + ); + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game) + 1)); + player.moveCards(card, Zone.EXILED, source, game); + effect.apply(game, source); + effect.getAddedPermanent().stream().forEach(permanent -> { + ContinuousEffect continuousEffect = new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.UntilYourNextTurn + ); + continuousEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(continuousEffect, source); + }); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OkoTheTrickster.java b/Mage.Sets/src/mage/cards/o/OkoTheTrickster.java index fba45954e1..3366b1c4e2 100644 --- a/Mage.Sets/src/mage/cards/o/OkoTheTrickster.java +++ b/Mage.Sets/src/mage/cards/o/OkoTheTrickster.java @@ -43,7 +43,7 @@ public final class OkoTheTrickster extends CardImpl { // 0: Until end of turn, Oko, the Trickster becomes a copy of target creature you control. Prevent all damage that would be dealt to him this turn. ability = new LoyaltyAbility(new OkoTheTricksterCopyEffect(), 0); ability.addEffect(new PreventAllDamageToSourceEffect(Duration.EndOfTurn) - .setText("Prevent all damage that would be dealt to him this turn.")); + .setText("Prevent all damage that would be dealt to him this turn")); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); @@ -72,7 +72,7 @@ class OkoTheTricksterCopyEffect extends OneShotEffect { OkoTheTricksterCopyEffect() { super(Outcome.Copy); - this.staticText = "Until end of turn, {this} becomes a copy of target creature you control."; + this.staticText = "Until end of turn, {this} becomes a copy of target creature you control"; } private OkoTheTricksterCopyEffect(final OkoTheTricksterCopyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java b/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java index 0c9b769960..bd8eafb3da 100644 --- a/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java +++ b/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java @@ -1,7 +1,6 @@ package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -24,6 +23,8 @@ import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.permanent.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author nantuko @@ -55,7 +56,11 @@ public final class OliviaVoldaren extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // {1}{R}: Olivia Voldaren deals 1 damage to another target creature. That creature becomes a Vampire in addition to its other types. Put a +1/+1 counter on Olivia Voldaren. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("{1}{R}")); + Ability ability = new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new DamageTargetEffect(1).setText("{this} deals 1 damage to another target creature"), + new ManaCostsImpl("{1}{R}") + ); ability.addTarget(new TargetCreaturePermanent(filter)); Effect effect = new AddCardSubTypeTargetEffect(SubType.VAMPIRE, Duration.WhileOnBattlefield); effect.setText("That creature becomes a Vampire in addition to its other types"); diff --git a/Mage.Sets/src/mage/cards/o/OmenOfTheSea.java b/Mage.Sets/src/mage/cards/o/OmenOfTheSea.java index 2cc325328b..6b9ddb1d34 100644 --- a/Mage.Sets/src/mage/cards/o/OmenOfTheSea.java +++ b/Mage.Sets/src/mage/cards/o/OmenOfTheSea.java @@ -26,8 +26,8 @@ public final class OmenOfTheSea extends CardImpl { this.addAbility(FlashAbility.getInstance()); // When Omen of the Sea enters the battlefield, scry 2, then draw a card. - Ability ability = new EntersBattlefieldTriggeredAbility(new ScryEffect(2).setText("scry 2,")); - ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("then")); + Ability ability = new EntersBattlefieldTriggeredAbility(new ScryEffect(2, false)); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy(", then")); this.addAbility(ability); // {2}{U}, Sacrifice Omen of the Sea: Scry 2. diff --git a/Mage.Sets/src/mage/cards/o/OminousSeas.java b/Mage.Sets/src/mage/cards/o/OminousSeas.java new file mode 100644 index 0000000000..7cdab82f17 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OminousSeas.java @@ -0,0 +1,49 @@ +package mage.cards.o; + +import mage.abilities.common.DrawCardControllerTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.game.permanent.token.KrakenToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OminousSeas extends CardImpl { + + public OminousSeas(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + // Whenever you draw a card, put a foreshadow counter on Ominous Seas. + this.addAbility(new DrawCardControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.FORESHADOW.createInstance()), false + )); + + // Remove eight foreshadow counters from Ominous Seas: Create an 8/8 blue Kraken creature token. + this.addAbility(new SimpleActivatedAbility( + new CreateTokenEffect(new KrakenToken()).setText("create an 8/8 blue Kraken creature token"), + new RemoveCountersSourceCost(CounterType.FORESHADOW.createInstance(8)) + )); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private OminousSeas(final OminousSeas card) { + super(card); + } + + @Override + public OminousSeas copy() { + return new OminousSeas(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/Onulet.java b/Mage.Sets/src/mage/cards/o/Onulet.java index 3a58d56823..ebf562bda7 100644 --- a/Mage.Sets/src/mage/cards/o/Onulet.java +++ b/Mage.Sets/src/mage/cards/o/Onulet.java @@ -3,7 +3,7 @@ package mage.cards.o; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class Onulet extends CardImpl { this.toughness = new MageInt(2); // When Onulet dies, you gain 2 life. - this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(2))); + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(2))); } public Onulet(final Onulet card) { diff --git a/Mage.Sets/src/mage/cards/o/OpalAcrolith.java b/Mage.Sets/src/mage/cards/o/OpalAcrolith.java index 1708c54e16..ec2e6b1094 100644 --- a/Mage.Sets/src/mage/cards/o/OpalAcrolith.java +++ b/Mage.Sets/src/mage/cards/o/OpalAcrolith.java @@ -1,76 +1,76 @@ -package mage.cards.o; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SpellCastOpponentTriggeredAbility; -import mage.abilities.condition.common.SourceMatchesFilterCondition; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.abilities.effects.common.continuous.BecomesEnchantmentSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterSpell; -import mage.filter.StaticFilters; -import mage.game.permanent.token.TokenImpl; - -/** - * - * @author jeffwadsworth - */ -public final class OpalAcrolith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("creature spell"); - - static { - filter.add(CardType.CREATURE.getPredicate()); - } - - public OpalAcrolith(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); - - // Whenever an opponent casts a creature spell, if Opal Acrolith is an enchantment, Opal Acrolith becomes a 2/4 Soldier creature. - TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new OpalAcrolithToken(), "", Duration.WhileOnBattlefield, true, false), - filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), - "Whenever an opponent casts a creature spell, if Opal Acrolith is an enchantment, Opal Acrolith becomes a 2/4 Soldier creature.")); - - // {0}: Opal Acrolith becomes an enchantment. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesEnchantmentSourceEffect(), new ManaCostsImpl("{0}"))); - - } - - public OpalAcrolith(final OpalAcrolith card) { - super(card); - } - - @Override - public OpalAcrolith copy() { - return new OpalAcrolith(this); - } -} - -class OpalAcrolithToken extends TokenImpl { - - public OpalAcrolithToken() { - super("Soldier", "2/4 Soldier creature"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.SOLDIER); - power = new MageInt(2); - toughness = new MageInt(4); - } - - public OpalAcrolithToken(final OpalAcrolithToken token) { - super(token); - } - - public OpalAcrolithToken copy() { - return new OpalAcrolithToken(this); - } -} +package mage.cards.o; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.effects.common.continuous.BecomesEnchantmentSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.game.permanent.token.TokenImpl; + +/** + * + * @author jeffwadsworth + */ +public final class OpalAcrolith extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("creature spell"); + + static { + filter.add(CardType.CREATURE.getPredicate()); + } + + public OpalAcrolith(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // Whenever an opponent casts a creature spell, if Opal Acrolith is an enchantment, Opal Acrolith becomes a 2/4 Soldier creature. + TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new OpalAcrolithToken(), "", Duration.WhileOnBattlefield, true, false), + filter, false); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + "Whenever an opponent casts a creature spell, if Opal Acrolith is an enchantment, Opal Acrolith becomes a 2/4 Soldier creature.")); + + // {0}: Opal Acrolith becomes an enchantment. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesEnchantmentSourceEffect(), new ManaCostsImpl("{0}"))); + + } + + public OpalAcrolith(final OpalAcrolith card) { + super(card); + } + + @Override + public OpalAcrolith copy() { + return new OpalAcrolith(this); + } +} + +class OpalAcrolithToken extends TokenImpl { + + public OpalAcrolithToken() { + super("Soldier", "2/4 Soldier creature"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.SOLDIER); + power = new MageInt(2); + toughness = new MageInt(4); + } + + public OpalAcrolithToken(final OpalAcrolithToken token) { + super(token); + } + + public OpalAcrolithToken copy() { + return new OpalAcrolithToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OpalAvenger.java b/Mage.Sets/src/mage/cards/o/OpalAvenger.java index 6374fa3848..085b2b40ee 100644 --- a/Mage.Sets/src/mage/cards/o/OpalAvenger.java +++ b/Mage.Sets/src/mage/cards/o/OpalAvenger.java @@ -1,122 +1,122 @@ -package mage.cards.o; - -import mage.MageInt; -import mage.abilities.StateTriggeredAbility; -import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.token.TokenImpl; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class OpalAvenger extends CardImpl { - - public OpalAvenger(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); - - // When you have 10 or less life, if Opal Avenger is an enchantment, Opal Avenger becomes a 3/5 Soldier creature. - this.addAbility(new OpalAvengerStateTriggeredAbility()); - } - - public OpalAvenger(final OpalAvenger card) { - super(card); - } - - @Override - public OpalAvenger copy() { - return new OpalAvenger(this); - } -} - -class OpalAvengerStateTriggeredAbility extends StateTriggeredAbility { - - public OpalAvengerStateTriggeredAbility() { - super(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new OpalAvengerToken(), "", Duration.Custom, true, false)); - } - - public OpalAvengerStateTriggeredAbility(final OpalAvengerStateTriggeredAbility ability) { - super(ability); - } - - @Override - public OpalAvengerStateTriggeredAbility copy() { - return new OpalAvengerStateTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.getState().getPlayer(getControllerId()) != null) { - return game.getState().getPlayer(getControllerId()).getLife() <= 10; - } - return false; - } - - @Override - public boolean checkInterveningIfClause(Game game) { - if (getSourcePermanentIfItStillExists(game) != null) { - return getSourcePermanentIfItStillExists(game).isEnchantment(); - } - return false; - } - - @Override - public boolean canTrigger(Game game) { - //20100716 - 603.8 - return !Boolean.TRUE.equals(game.getState().getValue(getSourceId().toString() + "triggered")); - } - - @Override - public void trigger(Game game, UUID controllerId) { - //20100716 - 603.8 - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); - } - - @Override - public boolean resolve(Game game) { - //20100716 - 603.8 - boolean result = super.resolve(game); - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); - return result; - } - - @Override - public void counter(Game game) { - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); - } - - @Override - public String getRule() { - return "When you have 10 or less life, if {this} is an enchantment, " + super.getRule(); - } - -} - -class OpalAvengerToken extends TokenImpl { - - public OpalAvengerToken() { - super("Soldier", "3/5 Soldier creature"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.SOLDIER); - power = new MageInt(3); - toughness = new MageInt(5); - } - - public OpalAvengerToken(final OpalAvengerToken token) { - super(token); - } - - @Override - public OpalAvengerToken copy() { - return new OpalAvengerToken(this); - } -} +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.StateTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.TokenImpl; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class OpalAvenger extends CardImpl { + + public OpalAvenger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // When you have 10 or less life, if Opal Avenger is an enchantment, Opal Avenger becomes a 3/5 Soldier creature. + this.addAbility(new OpalAvengerStateTriggeredAbility()); + } + + public OpalAvenger(final OpalAvenger card) { + super(card); + } + + @Override + public OpalAvenger copy() { + return new OpalAvenger(this); + } +} + +class OpalAvengerStateTriggeredAbility extends StateTriggeredAbility { + + public OpalAvengerStateTriggeredAbility() { + super(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new OpalAvengerToken(), "", Duration.Custom, true, false)); + } + + public OpalAvengerStateTriggeredAbility(final OpalAvengerStateTriggeredAbility ability) { + super(ability); + } + + @Override + public OpalAvengerStateTriggeredAbility copy() { + return new OpalAvengerStateTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (game.getState().getPlayer(getControllerId()) != null) { + return game.getState().getPlayer(getControllerId()).getLife() <= 10; + } + return false; + } + + @Override + public boolean checkInterveningIfClause(Game game) { + if (getSourcePermanentIfItStillExists(game) != null) { + return getSourcePermanentIfItStillExists(game).isEnchantment(); + } + return false; + } + + @Override + public boolean canTrigger(Game game) { + //20100716 - 603.8 + return !Boolean.TRUE.equals(game.getState().getValue(getSourceId().toString() + "triggered")); + } + + @Override + public void trigger(Game game, UUID controllerId) { + //20100716 - 603.8 + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); + super.trigger(game, controllerId); + } + + @Override + public boolean resolve(Game game) { + //20100716 - 603.8 + boolean result = super.resolve(game); + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); + return result; + } + + @Override + public void counter(Game game) { + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); + } + + @Override + public String getRule() { + return "When you have 10 or less life, if {this} is an enchantment, " + super.getRule(); + } + +} + +class OpalAvengerToken extends TokenImpl { + + public OpalAvengerToken() { + super("Soldier", "3/5 Soldier creature"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.SOLDIER); + power = new MageInt(3); + toughness = new MageInt(5); + } + + public OpalAvengerToken(final OpalAvengerToken token) { + super(token); + } + + @Override + public OpalAvengerToken copy() { + return new OpalAvengerToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OpalTitan.java b/Mage.Sets/src/mage/cards/o/OpalTitan.java index 5784ded4cf..bbbbc452f7 100644 --- a/Mage.Sets/src/mage/cards/o/OpalTitan.java +++ b/Mage.Sets/src/mage/cards/o/OpalTitan.java @@ -1,141 +1,141 @@ -package mage.cards.o; - -import java.util.UUID; -import mage.MageObjectReference; -import mage.ObjectColor; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.SpellCastOpponentTriggeredAbility; -import mage.abilities.condition.common.SourceMatchesFilterCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.common.continuous.SourceEffect; -import mage.abilities.keyword.ProtectionAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SetTargetPointer; -import mage.constants.SubLayer; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterSpell; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; - -/** - * - * @author jeffwadsworth - */ -public final class OpalTitan extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("creature spell"); - - static { - filter.add(CardType.CREATURE.getPredicate()); - } - - public OpalTitan(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); - - // When an opponent casts a creature spell, if Opal Titan is an enchantment, Opal Titan becomes a 4/4 Giant creature with protection from each of that spell's colors. - TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new OpalTitanBecomesCreatureEffect(), - filter, false, SetTargetPointer.SPELL); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), - "When an opponent casts a creature spell, if Opal Titan is an enchantment, Opal Titan becomes a 4/4 Giant creature with protection from each of that spell's colors.")); - - } - - public OpalTitan(final OpalTitan card) { - super(card); - } - - @Override - public OpalTitan copy() { - return new OpalTitan(this); - } -} - -class OpalTitanBecomesCreatureEffect extends ContinuousEffectImpl implements SourceEffect { - - public OpalTitanBecomesCreatureEffect() { - super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); - staticText = "{this} becomes a 4/4 Giant creature with protection from each of that spell's colors."; - this.addDependencyType(DependencyType.BecomeCreature); - } - - public OpalTitanBecomesCreatureEffect(final OpalTitanBecomesCreatureEffect effect) { - super(effect); - } - - @Override - public OpalTitanBecomesCreatureEffect copy() { - return new OpalTitanBecomesCreatureEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - affectedObjectList.add(new MageObjectReference(source.getSourceId(), game)); - Spell creatureSpellCast = game.getSpell(targetPointer.getFirst(game, source)); - if (creatureSpellCast != null - && creatureSpellCast.getColor(game).hasColor()) { - game.getState().setValue("opalTitanColor" + source.getSourceId(), creatureSpellCast.getColor(game)); - } - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent permanent = affectedObjectList.get(0).getPermanent(game); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getCardType().clear(); - permanent.addCardType(CardType.CREATURE); - permanent.getSubtype(game).add(SubType.GIANT); - } - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - if (game.getState().getValue("opalTitanColor" + source.getSourceId()) != null) { - for (ObjectColor color : ((ObjectColor) game.getState().getValue("opalTitanColor" + source.getSourceId())).getColors()) { - if (!permanent.getAbilities().contains(ProtectionAbility.from(color))) { - permanent.addAbility(ProtectionAbility.from(color)); - } - } - } - } - break; - case PTChangingEffects_7: - if ((sublayer == SubLayer.CharacteristicDefining_7a) - || (sublayer == SubLayer.SetPT_7b)) { - permanent.getPower().setValue(4); - permanent.getToughness().setValue(4); - } - break; - } - return true; - } - this.discard(); - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.PTChangingEffects_7 - || layer == Layer.AbilityAddingRemovingEffects_6 - || layer == Layer.TypeChangingEffects_4; - } - -} +package mage.cards.o; + +import java.util.UUID; +import mage.MageObjectReference; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.continuous.SourceEffect; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.DependencyType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.SubLayer; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; + +/** + * + * @author jeffwadsworth + */ +public final class OpalTitan extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("creature spell"); + + static { + filter.add(CardType.CREATURE.getPredicate()); + } + + public OpalTitan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); + + // When an opponent casts a creature spell, if Opal Titan is an enchantment, Opal Titan becomes a 4/4 Giant creature with protection from each of that spell's colors. + TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new OpalTitanBecomesCreatureEffect(), + filter, false, SetTargetPointer.SPELL); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + "When an opponent casts a creature spell, if Opal Titan is an enchantment, Opal Titan becomes a 4/4 Giant creature with protection from each of that spell's colors.")); + + } + + public OpalTitan(final OpalTitan card) { + super(card); + } + + @Override + public OpalTitan copy() { + return new OpalTitan(this); + } +} + +class OpalTitanBecomesCreatureEffect extends ContinuousEffectImpl implements SourceEffect { + + public OpalTitanBecomesCreatureEffect() { + super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); + staticText = "{this} becomes a 4/4 Giant creature with protection from each of that spell's colors."; + this.addDependencyType(DependencyType.BecomeCreature); + } + + public OpalTitanBecomesCreatureEffect(final OpalTitanBecomesCreatureEffect effect) { + super(effect); + } + + @Override + public OpalTitanBecomesCreatureEffect copy() { + return new OpalTitanBecomesCreatureEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + affectedObjectList.add(new MageObjectReference(source.getSourceId(), game)); + Spell creatureSpellCast = game.getSpell(targetPointer.getFirst(game, source)); + if (creatureSpellCast != null + && creatureSpellCast.getColor(game).hasColor()) { + game.getState().setValue("opalTitanColor" + source.getSourceId(), creatureSpellCast.getColor(game)); + } + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = affectedObjectList.get(0).getPermanent(game); + if (permanent != null) { + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == SubLayer.NA) { + permanent.getCardType().clear(); + permanent.addCardType(CardType.CREATURE); + permanent.getSubtype(game).add(SubType.GIANT); + } + break; + case AbilityAddingRemovingEffects_6: + if (sublayer == SubLayer.NA) { + if (game.getState().getValue("opalTitanColor" + source.getSourceId()) != null) { + for (ObjectColor color : ((ObjectColor) game.getState().getValue("opalTitanColor" + source.getSourceId())).getColors()) { + if (!permanent.getAbilities().contains(ProtectionAbility.from(color))) { + permanent.addAbility(ProtectionAbility.from(color)); + } + } + } + } + break; + case PTChangingEffects_7: + if ((sublayer == SubLayer.CharacteristicDefining_7a) + || (sublayer == SubLayer.SetPT_7b)) { + permanent.getPower().setValue(4); + permanent.getToughness().setValue(4); + } + break; + } + return true; + } + this.discard(); + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.PTChangingEffects_7 + || layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.TypeChangingEffects_4; + } + +} diff --git a/Mage.Sets/src/mage/cards/o/OpportunisticDragon.java b/Mage.Sets/src/mage/cards/o/OpportunisticDragon.java index aead5e1328..0195ee0ac1 100644 --- a/Mage.Sets/src/mage/cards/o/OpportunisticDragon.java +++ b/Mage.Sets/src/mage/cards/o/OpportunisticDragon.java @@ -1,6 +1,5 @@ package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -20,6 +19,8 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** * @author TheElk801 */ @@ -69,7 +70,7 @@ class OpportunisticDragonControlEffect extends GainControlTargetEffect { OpportunisticDragonControlEffect() { super(Duration.Custom); staticText = "choose target Human or artifact an opponent controls. " - + "For as long as {this} remains on the battlefield, gain control of that permanent,"; + + "For as long as {this} remains on the battlefield, gain control of that permanent"; } private OpportunisticDragonControlEffect(final OpportunisticDragonControlEffect effect) { @@ -95,7 +96,7 @@ class OpportunisticDragonLoseAbilitiesEffect extends LoseAllAbilitiesTargetEffec OpportunisticDragonLoseAbilitiesEffect() { super(Duration.Custom); - staticText = "it loses all abilities,"; + staticText = ", it loses all abilities"; } private OpportunisticDragonLoseAbilitiesEffect(final OpportunisticDragonLoseAbilitiesEffect effect) { @@ -121,7 +122,7 @@ class OpportunisticDragonAttackBlockEffect extends CantAttackBlockTargetEffect { OpportunisticDragonAttackBlockEffect() { super(Duration.Custom); - staticText = "and it can't attack or block"; + staticText = ", and it can't attack or block"; } private OpportunisticDragonAttackBlockEffect(final OpportunisticDragonAttackBlockEffect effect) { diff --git a/Mage.Sets/src/mage/cards/o/OppressiveRays.java b/Mage.Sets/src/mage/cards/o/OppressiveRays.java index ceb1fd488a..77df99c37b 100644 --- a/Mage.Sets/src/mage/cards/o/OppressiveRays.java +++ b/Mage.Sets/src/mage/cards/o/OppressiveRays.java @@ -39,7 +39,7 @@ public final class OppressiveRays extends CardImpl { // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/o/OracleEnVec.java b/Mage.Sets/src/mage/cards/o/OracleEnVec.java index c3b9e8f6c3..2e64b202d9 100644 --- a/Mage.Sets/src/mage/cards/o/OracleEnVec.java +++ b/Mage.Sets/src/mage/cards/o/OracleEnVec.java @@ -89,11 +89,11 @@ class OracleEnVecEffect extends OneShotEffect { for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), opponent.getId(), source.getSourceId(), game)) { if (target.getTargets().contains(permanent.getId())) { RequirementEffect effect = new OracleEnVecMustAttackRequirementEffect(); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } else { RestrictionEffect effect = new OracleEnVecCantAttackRestrictionEffect(); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/o/OracleOfMulDaya.java b/Mage.Sets/src/mage/cards/o/OracleOfMulDaya.java index d522ada0d5..dcebbc1b06 100644 --- a/Mage.Sets/src/mage/cards/o/OracleOfMulDaya.java +++ b/Mage.Sets/src/mage/cards/o/OracleOfMulDaya.java @@ -1,8 +1,5 @@ - - package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.PlayAdditionalLandsControllerEffect; @@ -11,35 +8,41 @@ import mage.abilities.effects.common.continuous.PlayWithTheTopCardRevealedEffect import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; +import mage.filter.FilterCard; import mage.filter.common.FilterLandCard; +import java.util.UUID; + /** - * * @author nantuko, BetaSteward_at_googlemail.com */ public final class OracleOfMulDaya extends CardImpl { + private static final FilterCard filter = new FilterLandCard("play land cards"); + public OracleOfMulDaya(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.SHAMAN); - this.power = new MageInt(2); this.toughness = new MageInt(2); // You may play an additional land on each of your turns. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayAdditionalLandsControllerEffect(1, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility( + new PlayAdditionalLandsControllerEffect(1, Duration.WhileOnBattlefield) + )); + // Play with the top card of your library revealed. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); + this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); + // You may play the top card of your library if it's a land card. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(new FilterLandCard()))); + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); } - public OracleOfMulDaya(final OracleOfMulDaya card) { + private OracleOfMulDaya(final OracleOfMulDaya card) { super(card); } @@ -47,5 +50,4 @@ public final class OracleOfMulDaya extends CardImpl { public OracleOfMulDaya copy() { return new OracleOfMulDaya(this); } - } diff --git a/Mage.Sets/src/mage/cards/o/OraclesVault.java b/Mage.Sets/src/mage/cards/o/OraclesVault.java index 57ed7bfd9d..e69ae9ac6e 100644 --- a/Mage.Sets/src/mage/cards/o/OraclesVault.java +++ b/Mage.Sets/src/mage/cards/o/OraclesVault.java @@ -1,4 +1,3 @@ - package mage.cards.o; import java.util.UUID; @@ -11,8 +10,10 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -21,11 +22,13 @@ import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.players.Library; import mage.players.Player; +import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; /** @@ -84,13 +87,8 @@ class OraclesVaultEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - controller.moveCardsToExile(card, source, game, true, source.getSourceId(), - CardUtil.createObjectRealtedWindowTitle(source, game, "")); - game.addEffect(new OraclesVaultPlayEffect(new MageObjectReference(card, game)), source); - } - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, controller.getLibrary().getFromTop(game), + TargetController.YOU, Duration.EndOfTurn, false); } return false; } @@ -114,95 +112,9 @@ class OraclesVaultFreeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null && controller.getLibrary().hasCards()) { - Library library = controller.getLibrary(); - Card card = library.getFromTop(game); - if (card != null) { - controller.moveCardsToExile(card, source, game, true, source.getSourceId(), - CardUtil.createObjectRealtedWindowTitle(source, game, " ")); - game.addEffect(new OraclesVaultPlayForFreeEffect(new MageObjectReference(card, game)), source); - } - return true; - } - return false; - } -} - -class OraclesVaultPlayEffect extends AsThoughEffectImpl { - - private final MageObjectReference objectReference; - - public OraclesVaultPlayEffect(MageObjectReference objectReference) { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - this.objectReference = objectReference; - staticText = "Until end of turn, you may play that card"; - } - - public OraclesVaultPlayEffect(final OraclesVaultPlayEffect effect) { - super(effect); - this.objectReference = effect.objectReference; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public OraclesVaultPlayEffect copy() { - return new OraclesVaultPlayEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (objectReference.refersTo(objectId, game) && affectedControllerId.equals(source.getControllerId())) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - return true; - } else { - discard(); - } - } - return false; - } -} - -class OraclesVaultPlayForFreeEffect extends AsThoughEffectImpl { - - private final MageObjectReference objectReference; - - public OraclesVaultPlayForFreeEffect(MageObjectReference objectReference) { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - this.objectReference = objectReference; - staticText = "Until end of turn, you may play that card without paying its mana cost"; - } - - public OraclesVaultPlayForFreeEffect(final OraclesVaultPlayForFreeEffect effect) { - super(effect); - this.objectReference = effect.objectReference; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public OraclesVaultPlayForFreeEffect copy() { - return new OraclesVaultPlayForFreeEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (objectReference.refersTo(objectId, game) && affectedControllerId.equals(source.getControllerId())) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.setCastSourceIdWithAlternateMana(objectId, null, null); - return true; - } else { - discard(); - } + if (controller != null) { + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, controller.getLibrary().getFromTop(game), + TargetController.YOU, Duration.EndOfTurn, true); } return false; } diff --git a/Mage.Sets/src/mage/cards/o/OranRiefHydra.java b/Mage.Sets/src/mage/cards/o/OranRiefHydra.java index c07bba7147..7a4c0aa520 100644 --- a/Mage.Sets/src/mage/cards/o/OranRiefHydra.java +++ b/Mage.Sets/src/mage/cards/o/OranRiefHydra.java @@ -117,7 +117,7 @@ class OranRiefHydraEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { // the LKI of the land to verify the last-known land type - Permanent landLKI = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent landLKI = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Permanent land = game.getPermanent(getTargetPointer().getFirst(game, source)); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); // the land must be on the battlefield when the trigger resolves diff --git a/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java b/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java index b11340a527..9362e0f909 100644 --- a/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java +++ b/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java @@ -136,7 +136,7 @@ class OratorOfOjutaiEffect extends OneShotEffect { if (sourcePermanent != null) { DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = game.getState().getWatcher(DragonOnTheBattlefieldWhileSpellWasCastWatcher.class); if (watcher != null && watcher.castWithConditionTrue(sourcePermanent.getSpellAbility().getId())) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/o/OrchardWarden.java b/Mage.Sets/src/mage/cards/o/OrchardWarden.java index 4888f45153..f773d5d122 100644 --- a/Mage.Sets/src/mage/cards/o/OrchardWarden.java +++ b/Mage.Sets/src/mage/cards/o/OrchardWarden.java @@ -68,7 +68,7 @@ class OrchardWardenffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && permanent != null) { controller.gainLife(permanent.getToughness().getValue(), game, source); return true; diff --git a/Mage.Sets/src/mage/cards/o/OrcishHellraiser.java b/Mage.Sets/src/mage/cards/o/OrcishHellraiser.java index 701bb8d750..a227b3ab60 100644 --- a/Mage.Sets/src/mage/cards/o/OrcishHellraiser.java +++ b/Mage.Sets/src/mage/cards/o/OrcishHellraiser.java @@ -2,7 +2,7 @@ package mage.cards.o; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.EchoAbility; import mage.cards.CardImpl; @@ -30,7 +30,7 @@ public final class OrcishHellraiser extends CardImpl { this.addAbility(new EchoAbility("{R}")); // When Orcish Hellraiser dies, it deals 2 damage to target player or planeswalker. - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it")); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2, "it")); ability.addTarget(new TargetPlayerOrPlaneswalker()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OrcishSquatters.java b/Mage.Sets/src/mage/cards/o/OrcishSquatters.java index fd20a8044b..cfcbddab65 100644 --- a/Mage.Sets/src/mage/cards/o/OrcishSquatters.java +++ b/Mage.Sets/src/mage/cards/o/OrcishSquatters.java @@ -1,4 +1,3 @@ - package mage.cards.o; import java.util.UUID; @@ -9,14 +8,15 @@ import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondit import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate; import mage.target.TargetPermanent; +import mage.watchers.common.LostControlWatcher; /** * @@ -45,6 +45,7 @@ public final class OrcishSquatters extends CardImpl { ), true); ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true)); ability.addTarget(new TargetPermanent(filter)); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/Order66.java b/Mage.Sets/src/mage/cards/o/Order66.java index 991ac95f64..2f6d341e67 100644 --- a/Mage.Sets/src/mage/cards/o/Order66.java +++ b/Mage.Sets/src/mage/cards/o/Order66.java @@ -1,38 +1,29 @@ - package mage.cards.o; -import java.util.UUID; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author Styxo */ public final class Order66 extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public Order66(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{7}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{7}{B}{B}"); // Put a bounty counter on each creature you don't control, then destroy all creatures you don't control. - this.getSpellAbility().addEffect(new AddCountersAllEffect(CounterType.BOUNTY.createInstance(), filter)); - this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); - + this.getSpellAbility().addEffect(new AddCountersAllEffect(CounterType.BOUNTY.createInstance(), StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.getSpellAbility().addEffect(new DestroyAllEffect(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public Order66(final Order66 card) { + private Order66(final Order66 card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/o/OreScaleGuardian.java b/Mage.Sets/src/mage/cards/o/OreScaleGuardian.java index cb8c54a0f6..95d09d2f8e 100644 --- a/Mage.Sets/src/mage/cards/o/OreScaleGuardian.java +++ b/Mage.Sets/src/mage/cards/o/OreScaleGuardian.java @@ -3,7 +3,10 @@ package mage.cards.o; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; @@ -28,10 +31,10 @@ public final class OreScaleGuardian extends CardImpl { this.toughness = new MageInt(4); // This spell costs {1} less to cast for each land card in your graveyard. - Ability ability = new SimpleStaticAbility( - Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(StaticFilters.FILTER_CARD_LAND) - ); + DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_LAND); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)); ability.setRuleAtTheTop(true); + ability.addHint(new ValueHint("Land card in your graveyard", xValue)); this.addAbility(ability); // Flying diff --git a/Mage.Sets/src/mage/cards/o/OriginSpellbomb.java b/Mage.Sets/src/mage/cards/o/OriginSpellbomb.java index 6b4dc0f0f8..eec623e987 100644 --- a/Mage.Sets/src/mage/cards/o/OriginSpellbomb.java +++ b/Mage.Sets/src/mage/cards/o/OriginSpellbomb.java @@ -4,7 +4,7 @@ package mage.cards.o; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -31,7 +31,7 @@ public final class OriginSpellbomb extends CardImpl { ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); - this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{W}")), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{W}")), false)); } public OriginSpellbomb (final OriginSpellbomb card) { diff --git a/Mage.Sets/src/mage/cards/o/OrimsPrayer.java b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java index 53eb06d5f0..68fd83e320 100644 --- a/Mage.Sets/src/mage/cards/o/OrimsPrayer.java +++ b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java @@ -38,8 +38,6 @@ public final class OrimsPrayer extends CardImpl { class OrimsPrayerTriggeredAbility extends TriggeredAbilityImpl { - int numberAttackingController = 0; - public OrimsPrayerTriggeredAbility() { super(Zone.BATTLEFIELD, null); } @@ -65,6 +63,7 @@ class OrimsPrayerTriggeredAbility extends TriggeredAbilityImpl { if (controller == null) { return false; } + int numberAttackingController = 0; for (UUID attackersId : game.getCombat().getAttackers()) { Permanent attackingCreature = game.getPermanent(attackersId); if (attackingCreature != null diff --git a/Mage.Sets/src/mage/cards/o/OrissSamiteGuardian.java b/Mage.Sets/src/mage/cards/o/OrissSamiteGuardian.java index 23a5129c54..7915fb8ccd 100644 --- a/Mage.Sets/src/mage/cards/o/OrissSamiteGuardian.java +++ b/Mage.Sets/src/mage/cards/o/OrissSamiteGuardian.java @@ -39,13 +39,13 @@ public final class OrissSamiteGuardian extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(3); - // {tap}: Prevent all damage that would be dealt to target creature this turn. + // {T}: Prevent all damage that would be dealt to target creature this turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventDamageToTargetEffect(Duration.EndOfTurn, Integer.MAX_VALUE), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // Grandeur - Discard another card named Oriss, Samite Guardian: Target player can't cast spells this turn, and creatures that player controls can't attack this turn. - ability = new GrandeurAbility(new OrissSamiteGuardianCantCastEffect(), "Oriss, Samite Guardian"); + ability = new GrandeurAbility(new OrissSamiteGuardianEffect(), "Oriss, Samite Guardian"); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java b/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java new file mode 100644 index 0000000000..fd311afd68 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java @@ -0,0 +1,147 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class OrmosArchiveKeeper extends CardImpl { + + public OrmosArchiveKeeper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPHINX); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // If you would draw a card while your library has no cards in it, instead put five +1/+1 counters on Ormos, Archive Keeper. + this.addAbility(new SimpleStaticAbility(new OrmosArchiveKeeperEffect())); + + // {1}{U}{U}, Discard three cards with different names: Draw five cards. + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(5), new ManaCostsImpl("{1}{U}{U}") + ); + ability.addCost(new DiscardTargetCost(new OrmosArchiveKeeperTarget())); + this.addAbility(ability); + } + + private OrmosArchiveKeeper(final OrmosArchiveKeeper card) { + super(card); + } + + @Override + public OrmosArchiveKeeper copy() { + return new OrmosArchiveKeeper(this); + } +} + +class OrmosArchiveKeeperEffect extends ReplacementEffectImpl { + + OrmosArchiveKeeperEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If you would draw a card while your library has no cards in it, " + + "instead put five +1/+1 counters on {this}"; + } + + private OrmosArchiveKeeperEffect(final OrmosArchiveKeeperEffect effect) { + super(effect); + } + + @Override + public OrmosArchiveKeeperEffect copy() { + return new OrmosArchiveKeeperEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(5), source, game); + } + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DRAW_CARD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getPlayerId().equals(source.getControllerId())) { + Player player = game.getPlayer(event.getPlayerId()); + if (player != null && !player.hasLost() && !player.getLibrary().hasCards()) { + return true; + } + } + return false; + } +} + +class OrmosArchiveKeeperTarget extends TargetCardInHand { + + private static final FilterCard filter = new FilterCard("three cards with different names"); + + OrmosArchiveKeeperTarget() { + super(3, filter); + } + + private OrmosArchiveKeeperTarget(final OrmosArchiveKeeperTarget target) { + super(target); + } + + @Override + public OrmosArchiveKeeperTarget copy() { + return new OrmosArchiveKeeperTarget(this); + } + + @Override + public Set possibleTargets(UUID sourceId, UUID playerId, Game game) { + Set possibleTargets = super.possibleTargets(sourceId, playerId, game); + Set names = this.getTargets() + .stream() + .map(game::getCard) + .map(MageObject::getName) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + possibleTargets.removeIf(uuid -> { + Card card = game.getCard(uuid); + return card != null && names.contains(card.getName()); + }); + return possibleTargets; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OrneryDilophosaur.java b/Mage.Sets/src/mage/cards/o/OrneryDilophosaur.java new file mode 100644 index 0000000000..fce5c2b72c --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OrneryDilophosaur.java @@ -0,0 +1,50 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.condition.common.FerociousCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.FerociousHint; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OrneryDilophosaur extends CardImpl { + + public OrneryDilophosaur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever Ornery Dilophosaur attacks, if you control a creature with power 4 or greater, Ornery Dilophosaur gets +2/+2 until end of turn. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new AttacksTriggeredAbility( + new BoostSourceEffect(2, 2, Duration.EndOfTurn), false + ), FerociousCondition.instance, "Whenever {this} attacks, " + + "if you control a creature with power 4 or greater, {this} gets +2/+2 until end of turn." + ).addHint(FerociousHint.instance)); + } + + private OrneryDilophosaur(final OrneryDilophosaur card) { + super(card); + } + + @Override + public OrneryDilophosaur copy() { + return new OrneryDilophosaur(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OrneryGoblin.java b/Mage.Sets/src/mage/cards/o/OrneryGoblin.java index 7e56c1249a..3f523c0f66 100644 --- a/Mage.Sets/src/mage/cards/o/OrneryGoblin.java +++ b/Mage.Sets/src/mage/cards/o/OrneryGoblin.java @@ -2,7 +2,7 @@ package mage.cards.o; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.constants.SubType; import mage.cards.CardImpl; @@ -24,7 +24,7 @@ public final class OrneryGoblin extends CardImpl { this.toughness = new MageInt(1); // Whenever Ornery Goblin blocks or becomes blocked by a creature, Ornery Goblin deals 1 damage to that creature. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility( + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility( new DamageTargetEffect(1, true, "that creature"), false )); } diff --git a/Mage.Sets/src/mage/cards/o/OrochiLeafcaller.java b/Mage.Sets/src/mage/cards/o/OrochiLeafcaller.java index 23ab53246a..3383987ba6 100644 --- a/Mage.Sets/src/mage/cards/o/OrochiLeafcaller.java +++ b/Mage.Sets/src/mage/cards/o/OrochiLeafcaller.java @@ -25,6 +25,8 @@ public final class OrochiLeafcaller extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); + + // {G}: Add one mana of any color. this.addAbility(new AnyColorManaAbility(new ColoredManaCost(ColoredManaSymbol.G))); } diff --git a/Mage.Sets/src/mage/cards/o/OrzhovKeyrune.java b/Mage.Sets/src/mage/cards/o/OrzhovKeyrune.java index debb0f28d1..66d63b8294 100644 --- a/Mage.Sets/src/mage/cards/o/OrzhovKeyrune.java +++ b/Mage.Sets/src/mage/cards/o/OrzhovKeyrune.java @@ -1,7 +1,5 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -12,22 +10,22 @@ import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.permanent.token.TokenImpl; -import mage.game.permanent.token.Token; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class OrzhovKeyrune extends CardImpl { public OrzhovKeyrune(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - // {{T}: Add {W} or {B}. + // {T}: Add {W} or {B}. this.addAbility(new WhiteManaAbility()); this.addAbility(new BlackManaAbility()); @@ -56,6 +54,7 @@ public final class OrzhovKeyrune extends CardImpl { toughness = new MageInt(4); this.addAbility(LifelinkAbility.getInstance()); } + public OrzhovKeyruneToken(final OrzhovKeyruneToken token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/o/OtherworldAtlas.java b/Mage.Sets/src/mage/cards/o/OtherworldAtlas.java index 660200e588..dfe9041edf 100644 --- a/Mage.Sets/src/mage/cards/o/OtherworldAtlas.java +++ b/Mage.Sets/src/mage/cards/o/OtherworldAtlas.java @@ -68,7 +68,7 @@ class OtherworldAtlasDrawEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(amount, game); + player.drawCards(amount, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/o/OtrimiTheEverPlayful.java b/Mage.Sets/src/mage/cards/o/OtrimiTheEverPlayful.java new file mode 100644 index 0000000000..39ff15cfca --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OtrimiTheEverPlayful.java @@ -0,0 +1,67 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.MutateAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OtrimiTheEverPlayful extends CardImpl { + + private static final FilterCard filter = new FilterCreatureCard("creature card with mutate"); + + static { + filter.add(new AbilityPredicate(MutateAbility.class)); + } + + private static final String rule = "Whenever this creature deals combat damage to a player, " + + "return target creature card with mutate from your graveyard to your hand."; + + public OtrimiTheEverPlayful(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Mutate {1}{B}{G}{U} + this.addAbility(new MutateAbility(this, "{1}{B}{G}{U}")); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever this creature deals combat damage to a player, return target creature card with mutate from your graveyard to your hand. + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility( + new ReturnFromGraveyardToHandTargetEffect(), + false, rule, false + ); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private OtrimiTheEverPlayful(final OtrimiTheEverPlayful card) { + super(card); + } + + @Override + public OtrimiTheEverPlayful copy() { + return new OtrimiTheEverPlayful(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/Oubliette.java b/Mage.Sets/src/mage/cards/o/Oubliette.java index 9e933be131..f9b9be4ff4 100644 --- a/Mage.Sets/src/mage/cards/o/Oubliette.java +++ b/Mage.Sets/src/mage/cards/o/Oubliette.java @@ -1,51 +1,40 @@ package mage.cards.o; -import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.SubType; import mage.constants.Zone; -import mage.counters.Counter; -import mage.counters.Counters; -import mage.filter.Filter; -import mage.game.ExileZone; import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; /** - * @author MarcoMarin + * @author TheElk801 */ public final class Oubliette extends CardImpl { public Oubliette(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{B}"); - // When Oubliette enters the battlefield, exile target creature and all Auras attached to it. Note the number and kind of counters that were on that creature. - Ability ability1 = new EntersBattlefieldTriggeredAbility(new OublietteEffect(), false); - ability1.addTarget(new TargetCreaturePermanent()); - this.addAbility(ability1); - - // When Oubliette leaves the battlefield, return the exiled card to the battlefield under its owner's control tapped with the noted number and kind of counters on it. If you do, return the exiled Aura cards to the battlefield under their owner's control attached to that permanent. - Ability ability2 = new LeavesBattlefieldTriggeredAbility(new OublietteReturnEffect(), false); - this.addAbility(ability2); + // When Oubliette enters the battlefield, target creature phases out until Oubliette leaves the battlefield. Tap that creature as it phases in this way. + Ability ability = new EntersBattlefieldTriggeredAbility(new OubliettePhaseOutEffect()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); } - public Oubliette(final Oubliette card) { + private Oubliette(final Oubliette card) { super(card); } @@ -55,120 +44,130 @@ public final class Oubliette extends CardImpl { } } -class OublietteEffect extends OneShotEffect { +class OubliettePhaseOutEffect extends OneShotEffect { - public OublietteEffect() { - super(Outcome.Detriment); - this.staticText = "exile target creature and all Auras attached to it. Note the number and kind of counters that were on that creature"; - } - - public OublietteEffect(final OublietteEffect effect) { - super(effect); - } - - @Override - public OublietteEffect copy() { - return new OublietteEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller == null || sourceObject == null) { - return false; - } - Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (targetCreature != null) { - game.getState().setValue(CardUtil.getCardZoneString("savedCounters", source.getSourceId(), game), targetCreature.getCounters(game).copy()); - game.getState().setValue(CardUtil.getCardZoneString("targetId", source.getSourceId(), game), targetCreature.getId()); - Set toExile = new HashSet<>(); - toExile.add(targetCreature); - for (UUID attachementId : targetCreature.getAttachments()) { - Permanent attachment = game.getPermanent(attachementId); - if (attachment != null && attachment.getSubtype(game).contains(SubType.AURA)) { - toExile.add(attachment); - } - } - controller.moveCardsToExile(toExile, source, game, true, CardUtil.getCardExileZoneId(game, source), sourceObject.getIdName()); - } - - return true; - } -} - -class OublietteReturnEffect extends OneShotEffect { - - public OublietteReturnEffect() { + OubliettePhaseOutEffect() { super(Outcome.Benefit); - this.staticText = "return the exiled card to the battlefield under its owner's control tapped with the noted number and kind of counters on it. If you do, return the exiled Aura cards to the battlefield under their owner's control attached to that permanent"; + staticText = "target creature phases out until {this} leaves the battlefield. " + + "Tap that creature as it phases in this way."; } - public OublietteReturnEffect(final OublietteReturnEffect effect) { + private OubliettePhaseOutEffect(final OubliettePhaseOutEffect effect) { super(effect); } @Override - public OublietteReturnEffect copy() { - return new OublietteReturnEffect(this); + public OubliettePhaseOutEffect copy() { + return new OubliettePhaseOutEffect(this); } @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (sourcePermanent == null || permanent == null) { return false; } - ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source.getSourceId(), true)); - if (exileZone == null) { - return true; - } - - Card exiledCreatureCard = exileZone.get((UUID) game.getState().getValue(CardUtil.getCardZoneString("targetId", source.getSourceId(), game, true)), game); - if (exiledCreatureCard == null) { - return false; - } - controller.moveCards(exiledCreatureCard, Zone.BATTLEFIELD, source, game, true, false, true, null); - Permanent newPermanent = game.getPermanent(exiledCreatureCard.getId()); - if (newPermanent != null) { - // Restore the counters - Counters counters = (Counters) game.getState().getValue(CardUtil.getCardZoneString("savedCounters", source.getSourceId(), game, true)); - if (counters != null) { - for (Counter counter : counters.values()) { - if (counter != null) { - newPermanent.getCounters(game).addCounter(counter); // it's restore counters, not add (e.g. without add events) - } - } - } - // readd the attachments - Set toBattlefield = new HashSet<>(); - for (Card enchantment : exileZone.getCards(game)) { - if (enchantment.getSubtype(game).contains(SubType.AURA)) { - boolean canTarget = false; - for (Target target : enchantment.getSpellAbility().getTargets()) { - Filter filter2 = target.getFilter(); - if (filter2.match(newPermanent, game)) { - canTarget = true; - break; - } - } - if (!canTarget) { - // Aura stays exiled - continue; - } - game.getState().setValue("attachTo:" + enchantment.getId(), newPermanent); - toBattlefield.add(enchantment); - } - } - controller.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game, true, false, true, null); - for (Card enchantmentCard : toBattlefield) { - Permanent permanent = game.getPermanent(enchantmentCard.getId()); - if (permanent != null) { - newPermanent.addAttachment(permanent.getId(), game); - } - } - - } + MageObjectReference mor = new MageObjectReference(permanent, game); + permanent.tap(game); + permanent.phaseOut(game); + game.addEffect(new OubliettePhasePreventEffect(mor), source); + game.addDelayedTriggeredAbility(new OublietteDelayedTriggeredAbility(mor), source); return true; } } + +class OubliettePhasePreventEffect extends ContinuousRuleModifyingEffectImpl { + + private final MageObjectReference mor; + + OubliettePhasePreventEffect(MageObjectReference mor) { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + this.mor = mor; + } + + private OubliettePhasePreventEffect(final OubliettePhasePreventEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public OubliettePhasePreventEffect copy() { + return new OubliettePhasePreventEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PHASE_IN; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.getSourcePermanentIfItStillExists(game) != null + && this.mor.refersTo(event.getTargetId(), game); + } +} + +class OublietteDelayedTriggeredAbility extends DelayedTriggeredAbility { + + OublietteDelayedTriggeredAbility(MageObjectReference mor) { + super(new OubliettePhaseInEffect(mor), Duration.Custom, true, false); + this.usesStack = false; + } + + private OublietteDelayedTriggeredAbility(final OublietteDelayedTriggeredAbility ability) { + super(ability); + } + + @Override + public OublietteDelayedTriggeredAbility copy() { + return new OublietteDelayedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getTargetId().equals(this.getSourceId())) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getFromZone() == Zone.BATTLEFIELD) { + return true; + } + } + return false; + } +} + +class OubliettePhaseInEffect extends OneShotEffect { + + private final MageObjectReference mor; + + OubliettePhaseInEffect(MageObjectReference mor) { + super(Outcome.Benefit); + this.mor = mor; + } + + private OubliettePhaseInEffect(final OubliettePhaseInEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public OubliettePhaseInEffect copy() { + return new OubliettePhaseInEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = mor.getPermanent(game); + return permanent != null && permanent.phaseIn(game); + } +} diff --git a/Mage.Sets/src/mage/cards/o/Outmaneuver.java b/Mage.Sets/src/mage/cards/o/Outmaneuver.java index 7e005e5bb7..cc1186864d 100644 --- a/Mage.Sets/src/mage/cards/o/Outmaneuver.java +++ b/Mage.Sets/src/mage/cards/o/Outmaneuver.java @@ -1,96 +1,96 @@ -package mage.cards.o; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.BlockedPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCreaturePermanent; - -/** - * - * @author jeffwadsworth - */ -public final class Outmaneuver extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(BlockedPredicate.instance); - } - - public Outmaneuver(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{R}"); - - // X target blocked creatures assign their combat damage this turn as though they weren't blocked. - this.getSpellAbility().addEffect(new OutmaneuverEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); - - } - - public Outmaneuver(final Outmaneuver card) { - super(card); - } - - @Override - public Outmaneuver copy() { - return new Outmaneuver(this); - } - - @Override - public void adjustTargets(Ability ability, Game game) { - if (ability instanceof SpellAbility) { - ability.getTargets().clear(); - int numberOfTargets = ability.getManaCostsToPay().getX(); - numberOfTargets = Math.min(game.getBattlefield().getAllActivePermanents(filter, - ability.getControllerId(), game).size(), numberOfTargets); - ability.addTarget(new TargetCreaturePermanent(numberOfTargets, - numberOfTargets, filter, false)); - } - } -} - -class OutmaneuverEffect extends AsThoughEffectImpl { - - public OutmaneuverEffect() { - super(AsThoughEffectType.DAMAGE_NOT_BLOCKED, Duration.EndOfTurn, Outcome.Damage); - this.staticText = "X target blocked creatures assign their combat damage this turn as though they weren't blocked."; - } - - public OutmaneuverEffect(OutmaneuverEffect effect) { - super(effect); - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - Permanent blockedCreature = game.getPermanent(sourceId); - if (blockedCreature != null) { - Player controller = game.getPlayer(blockedCreature.getControllerId()); - if (controller != null) { - return controller.chooseUse(Outcome.Damage, "Do you wish to assign combat damage for " - + blockedCreature.getLogName() + " as though it weren't blocked?", source, game); - } - } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public OutmaneuverEffect copy() { - return new OutmaneuverEffect(this); - } -} +package mage.cards.o; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AsThoughEffectType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.BlockedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author jeffwadsworth + */ +public final class Outmaneuver extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(BlockedPredicate.instance); + } + + public Outmaneuver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{R}"); + + // X target blocked creatures assign their combat damage this turn as though they weren't blocked. + this.getSpellAbility().addEffect(new OutmaneuverEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + + } + + public Outmaneuver(final Outmaneuver card) { + super(card); + } + + @Override + public Outmaneuver copy() { + return new Outmaneuver(this); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SpellAbility) { + ability.getTargets().clear(); + int numberOfTargets = ability.getManaCostsToPay().getX(); + numberOfTargets = Math.min(game.getBattlefield().getAllActivePermanents(filter, + ability.getControllerId(), game).size(), numberOfTargets); + ability.addTarget(new TargetCreaturePermanent(numberOfTargets, + numberOfTargets, filter, false)); + } + } +} + +class OutmaneuverEffect extends AsThoughEffectImpl { + + public OutmaneuverEffect() { + super(AsThoughEffectType.DAMAGE_NOT_BLOCKED, Duration.EndOfTurn, Outcome.Damage); + this.staticText = "X target blocked creatures assign their combat damage this turn as though they weren't blocked."; + } + + public OutmaneuverEffect(OutmaneuverEffect effect) { + super(effect); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + Permanent blockedCreature = game.getPermanent(sourceId); + if (blockedCreature != null) { + Player controller = game.getPlayer(blockedCreature.getControllerId()); + if (controller != null) { + return controller.chooseUse(Outcome.Damage, "Do you wish to assign combat damage for " + + blockedCreature.getLogName() + " as though it weren't blocked?", source, game); + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public OutmaneuverEffect copy() { + return new OutmaneuverEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/Outmuscle.java b/Mage.Sets/src/mage/cards/o/Outmuscle.java index 7d73f6677b..c35df58cd9 100644 --- a/Mage.Sets/src/mage/cards/o/Outmuscle.java +++ b/Mage.Sets/src/mage/cards/o/Outmuscle.java @@ -11,9 +11,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -28,13 +27,6 @@ import java.util.UUID; */ public final class Outmuscle extends CardImpl { - private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public Outmuscle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); @@ -42,7 +34,7 @@ public final class Outmuscle extends CardImpl { // Adamant — If at least three green mana was spent to cast this spell, the creature you control gains indestructible until end of turn. this.getSpellAbility().addEffect(new OutmuscleEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); } @@ -91,7 +83,7 @@ class OutmuscleEffect extends OneShotEffect { if (creature == null) { return true; } - game.applyEffects(); + game.getState().processAction(game); return creature.fight(permanent, source, game); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/o/OutpostSiege.java b/Mage.Sets/src/mage/cards/o/OutpostSiege.java index 4062fa1c78..61d2b18d12 100644 --- a/Mage.Sets/src/mage/cards/o/OutpostSiege.java +++ b/Mage.Sets/src/mage/cards/o/OutpostSiege.java @@ -99,7 +99,7 @@ class OutpostSiegeExileEffect extends OneShotEffect { controller.moveCardsToExile(card, source, game, true, source.getSourceId(), exileName); if (game.getState().getZone(card.getId()) == Zone.EXILED) { ContinuousEffect effect = new CastFromNonHandZoneTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/o/OvergrowthElemental.java b/Mage.Sets/src/mage/cards/o/OvergrowthElemental.java index 7e1a00a040..3f8f14806a 100644 --- a/Mage.Sets/src/mage/cards/o/OvergrowthElemental.java +++ b/Mage.Sets/src/mage/cards/o/OvergrowthElemental.java @@ -86,7 +86,7 @@ class OvergrowthElementalEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(targetPointer.getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent == null || !permanent.hasSubtype(SubType.ELEMENTAL, game)) { return false; } diff --git a/Mage.Sets/src/mage/cards/o/OverlaidTerrain.java b/Mage.Sets/src/mage/cards/o/OverlaidTerrain.java index c09e6b13d0..4c35c2e9d2 100644 --- a/Mage.Sets/src/mage/cards/o/OverlaidTerrain.java +++ b/Mage.Sets/src/mage/cards/o/OverlaidTerrain.java @@ -1,14 +1,12 @@ - package mage.cards.o; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.mana.AddManaOfAnyColorEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -22,19 +20,20 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author fireshoes */ public final class OverlaidTerrain extends CardImpl { public OverlaidTerrain(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}"); // As Overlaid Terrain enters the battlefield, sacrifice all lands you control. this.addAbility(new AsEntersBattlefieldAbility(new SacrificeAllLandEffect())); - - // Lands you control have "{tap}: Add two mana of any one color." + + // Lands you control have "{T}: Add two mana of any one color." SimpleManaAbility manaAbility = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(2), new TapSourceCost()); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(manaAbility, Duration.WhileOnBattlefield, new FilterLandPermanent(), false))); } @@ -52,20 +51,20 @@ public final class OverlaidTerrain extends CardImpl { class SacrificeAllLandEffect extends OneShotEffect { SacrificeAllLandEffect() { - super(Outcome.Detriment); + super(Outcome.Detriment); staticText = "sacrifice all lands you control"; } - + SacrificeAllLandEffect(final SacrificeAllLandEffect effect) { super(effect); } - @Override + @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - for(Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), source.getControllerId(), game)){ - permanent.sacrifice(source.getControllerId(), game); + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), source.getControllerId(), game)) { + permanent.sacrifice(source.getControllerId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/o/OverrideCard.java b/Mage.Sets/src/mage/cards/o/OverrideCard.java index d69ba56a7d..59ad499c0c 100644 --- a/Mage.Sets/src/mage/cards/o/OverrideCard.java +++ b/Mage.Sets/src/mage/cards/o/OverrideCard.java @@ -4,12 +4,12 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.costs.Cost; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.common.FilterArtifactPermanent; import mage.game.Game; import mage.game.stack.StackObject; import mage.players.Player; @@ -64,7 +64,7 @@ class OverrideEffect extends OneShotEffect { Player player = game.getPlayer(spell.getControllerId()); Player controller = game.getPlayer(source.getControllerId()); if (player != null && controller != null) { - int amount = game.getBattlefield().countAll(new FilterArtifactPermanent(), source.getControllerId(), game); + int amount = ArtifactYouControlCount.instance.calculate(game, source, this); if (amount > 0) { Cost cost = ManaUtil.createManaCost(amount, false); if (!cost.pay(source, game, spell.getControllerId(), spell.getControllerId(), false)) { diff --git a/Mage.Sets/src/mage/cards/o/OverwhelmedApprentice.java b/Mage.Sets/src/mage/cards/o/OverwhelmedApprentice.java index c62134e566..32f9fe239f 100644 --- a/Mage.Sets/src/mage/cards/o/OverwhelmedApprentice.java +++ b/Mage.Sets/src/mage/cards/o/OverwhelmedApprentice.java @@ -30,7 +30,7 @@ public final class OverwhelmedApprentice extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility( new PutTopCardOfLibraryIntoGraveEachPlayerEffect(2, TargetController.OPPONENT) ); - ability.addEffect(new ScryEffect(2).setText("Then you scry 2.")); + ability.addEffect(new ScryEffect(2, false).setText("Then you scry 2.")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OverwhelmingIntellect.java b/Mage.Sets/src/mage/cards/o/OverwhelmingIntellect.java index 1feb1afa92..dbc14c8166 100644 --- a/Mage.Sets/src/mage/cards/o/OverwhelmingIntellect.java +++ b/Mage.Sets/src/mage/cards/o/OverwhelmingIntellect.java @@ -61,7 +61,7 @@ class OverwhelmingIntellectEffect extends OneShotEffect { Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); if (controller != null && spell != null) { game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game); - controller.drawCards(spell.getConvertedManaCost(), game); + controller.drawCards(spell.getConvertedManaCost(), source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/p/PackLeader.java b/Mage.Sets/src/mage/cards/p/PackLeader.java new file mode 100644 index 0000000000..86fac27413 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PackLeader.java @@ -0,0 +1,55 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.PreventAllDamageToAllEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PackLeader extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.DOG, "Dogs"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public PackLeader(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.DOG); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Other Dogs you control get +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + ))); + + // Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control. + this.addAbility(new AttacksTriggeredAbility(new PreventAllDamageToAllEffect( + Duration.EndOfTurn, filter, true + ).setText("prevent all combat damage that would be dealt this turn to Dogs you control"), false)); + } + + private PackLeader(final PackLeader card) { + super(card); + } + + @Override + public PackLeader copy() { + return new PackLeader(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PackMastiff.java b/Mage.Sets/src/mage/cards/p/PackMastiff.java index 92a049678b..92413d6e2c 100644 --- a/Mage.Sets/src/mage/cards/p/PackMastiff.java +++ b/Mage.Sets/src/mage/cards/p/PackMastiff.java @@ -28,7 +28,7 @@ public final class PackMastiff extends CardImpl { public PackMastiff(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/p/PainfulTruths.java b/Mage.Sets/src/mage/cards/p/PainfulTruths.java index 7ce04d18b1..f0e585b4a7 100644 --- a/Mage.Sets/src/mage/cards/p/PainfulTruths.java +++ b/Mage.Sets/src/mage/cards/p/PainfulTruths.java @@ -26,7 +26,7 @@ public final class PainfulTruths extends CardImpl { effect.setText("You draw X cards"); getSpellAbility().addEffect(effect); effect = new LoseLifeSourceControllerEffect(ColorsOfManaSpentToCastCount.getInstance()); - effect.setText("and lose X life, where X is the number of colors of mana spent to cast {this}"); + effect.setText("and lose X life, where X is the number of colors of mana spent to cast this spell"); getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/p/PainsReward.java b/Mage.Sets/src/mage/cards/p/PainsReward.java index 7d121bd3cf..67d1e27a88 100644 --- a/Mage.Sets/src/mage/cards/p/PainsReward.java +++ b/Mage.Sets/src/mage/cards/p/PainsReward.java @@ -81,7 +81,7 @@ class PainsRewardEffect extends OneShotEffect { game.informPlayers(winner.getLogName() + " won the auction with a bid of " + highBid + " life" + (highBid > 1 ? "s" : "")); winner.loseLife(highBid, game, false); - winner.drawCards(4, game); + winner.drawCards(4, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java b/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java new file mode 100644 index 0000000000..f7abe82234 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java @@ -0,0 +1,136 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.*; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class PakoArcaneRetriever extends CardImpl { + + public PakoArcaneRetriever(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.DOG); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Partner with Haldan, Avid Arcanist + this.addAbility(new PartnerWithAbility("Haldan, Avid Arcanist")); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Pako, Arcane Retriever attacks, exile the top card of each player's library and put a fetch counter on each of them. Put a +1/+1 counter on Pako for each noncreature card exiled this way. + this.addAbility(new AttacksTriggeredAbility( + new PakoArcaneRetrieverEffect(), false + ), new PakoArcaneRetrieverWatcher()); + } + + private PakoArcaneRetriever(final PakoArcaneRetriever card) { + super(card); + } + + @Override + public PakoArcaneRetriever copy() { + return new PakoArcaneRetriever(this); + } + + public static boolean checkWatcher(UUID playerId, Card card, Game game) { + PakoArcaneRetrieverWatcher watcher = game.getState().getWatcher(PakoArcaneRetrieverWatcher.class); + return watcher != null && watcher.checkCard(playerId, card, game); + } +} + +class PakoArcaneRetrieverEffect extends OneShotEffect { + + PakoArcaneRetrieverEffect() { + super(Outcome.Benefit); + staticText = "exile the top card of each player's library and put a fetch counter on each of them. " + + "Put a +1/+1 counter on {this} for each noncreature card exiled this way."; + } + + private PakoArcaneRetrieverEffect(final PakoArcaneRetrieverEffect effect) { + super(effect); + } + + @Override + public PakoArcaneRetrieverEffect copy() { + return new PakoArcaneRetrieverEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + PakoArcaneRetrieverWatcher watcher = game.getState().getWatcher(PakoArcaneRetrieverWatcher.class); + if (controller == null || watcher == null) { + return false; + } + Cards cards = new CardsImpl(); + game.getState() + .getPlayersInRange(controller.getId(), game) + .stream() + .map(game::getPlayer) + .map(Player::getLibrary) + .map(library -> library.getFromTop(game)) + .forEach(cards::add); + controller.moveCards(cards, Zone.EXILED, source, game); + cards.removeIf(cardId -> game.getState().getZone(cardId) != Zone.EXILED); + int counters = cards.count(StaticFilters.FILTER_CARD_NON_CREATURE, game); + if (cards.isEmpty()) { + return true; + } + cards.getCards(game) + .stream() + .filter(card -> card.addCounters(CounterType.FETCH.createInstance(), source, game)) + .filter(card -> !card.isCreature()) + .forEach(card -> watcher.addCard(controller.getId(), card, game)); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null || counters == 0) { + return true; + } + return permanent.addCounters(CounterType.P1P1.createInstance(counters), source, game); + } +} + +class PakoArcaneRetrieverWatcher extends Watcher { + + private final Map> playerMap = new HashMap(); + + PakoArcaneRetrieverWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + } + + void addCard(UUID playerId, Card card, Game game) { + playerMap.computeIfAbsent(playerId, u -> new HashSet()).add(new MageObjectReference(card, game)); + } + + boolean checkCard(UUID playerId, Card card, Game game) { + return card != null && playerMap + .computeIfAbsent(playerId, u -> new HashSet()) + .stream() + .anyMatch(mageObjectReference -> mageObjectReference.refersTo(card.getId(), game)); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PalaceFamiliar.java b/Mage.Sets/src/mage/cards/p/PalaceFamiliar.java index 50afbd795d..7bf7245f86 100644 --- a/Mage.Sets/src/mage/cards/p/PalaceFamiliar.java +++ b/Mage.Sets/src/mage/cards/p/PalaceFamiliar.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class PalaceFamiliar extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Palace Familiar dies, draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } public PalaceFamiliar(final PalaceFamiliar card) { diff --git a/Mage.Sets/src/mage/cards/p/PaladinOfAtonement.java b/Mage.Sets/src/mage/cards/p/PaladinOfAtonement.java index 3b21dd98e7..e0c8ca2b25 100644 --- a/Mage.Sets/src/mage/cards/p/PaladinOfAtonement.java +++ b/Mage.Sets/src/mage/cards/p/PaladinOfAtonement.java @@ -4,7 +4,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.common.LiveLostLastTurnCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.SourcePermanentToughnessValue; @@ -38,7 +38,7 @@ public final class PaladinOfAtonement extends CardImpl { "At the beginning of each upkeep, if you lost life last turn, put a +1/+1 counter on {this}")); // When Paladin of Atonement dies, you gain life equal to it's toughness. - this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(SourcePermanentToughnessValue.getInstance(), + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(SourcePermanentToughnessValue.getInstance(), "you gain life equal to it's toughness"))); } diff --git a/Mage.Sets/src/mage/cards/p/PalisadeGiant.java b/Mage.Sets/src/mage/cards/p/PalisadeGiant.java index 0ceed13bfb..f1c808fbe9 100644 --- a/Mage.Sets/src/mage/cards/p/PalisadeGiant.java +++ b/Mage.Sets/src/mage/cards/p/PalisadeGiant.java @@ -65,7 +65,7 @@ class PalisadeGiantReplacementEffect extends ReplacementEffectImpl { PalisadeGiantReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.RedirectDamage); - staticText = "All damage that would be dealt to you or another permanent you control is dealt to Palisade Giant instead"; + staticText = "All damage that would be dealt to you or another permanent you control is dealt to {this} instead"; } PalisadeGiantReplacementEffect(final PalisadeGiantReplacementEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/Pandemonium.java b/Mage.Sets/src/mage/cards/p/Pandemonium.java index a3b977b19c..f835bcdf11 100644 --- a/Mage.Sets/src/mage/cards/p/Pandemonium.java +++ b/Mage.Sets/src/mage/cards/p/Pandemonium.java @@ -80,7 +80,7 @@ class PandemoniumEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Permanent enteringCreature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent enteringCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (enteringCreature != null) { Permanent targetPermanent = game.getPermanent(source.getTargets().getFirstTarget()); if (targetPermanent != null) { diff --git a/Mage.Sets/src/mage/cards/p/ParasiticImpetus.java b/Mage.Sets/src/mage/cards/p/ParasiticImpetus.java new file mode 100644 index 0000000000..9a0dad6d1e --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ParasiticImpetus.java @@ -0,0 +1,58 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksAttachedTriggeredAbility; +import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeControllerAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ParasiticImpetus extends CardImpl { + + public ParasiticImpetus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 and is goaded. + this.addAbility(new GoadAttachedAbility(new BoostEnchantedEffect(2, 2))); + + // Whenever enchanted creature attacks, its controller loses 2 life and you gain 2 life. + ability = new AttacksAttachedTriggeredAbility( + new LoseLifeControllerAttachedEffect(2), AttachmentType.AURA, false + ); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); + this.addAbility(ability); + } + + private ParasiticImpetus(final ParasiticImpetus card) { + super(card); + } + + @Override + public ParasiticImpetus copy() { + return new ParasiticImpetus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Parcelbeast.java b/Mage.Sets/src/mage/cards/p/Parcelbeast.java new file mode 100644 index 0000000000..6df3f1873e --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/Parcelbeast.java @@ -0,0 +1,93 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Parcelbeast extends CardImpl { + + public Parcelbeast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Mutate {G}{U} + this.addAbility(new MutateAbility(this, "{G}{U}")); + + // {1}, {T}: Look at the top card of your library. If it's a land card, you may put it onto the battlefield. If you don't put the card onto the battlefield, put it into your hand. + Ability ability = new SimpleActivatedAbility(new ParcelbeastEffect(), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private Parcelbeast(final Parcelbeast card) { + super(card); + } + + @Override + public Parcelbeast copy() { + return new Parcelbeast(this); + } +} + +class ParcelbeastEffect extends OneShotEffect { + + ParcelbeastEffect() { + super(Outcome.Benefit); + staticText = "look at the top card of your library. " + + "If it's a land card, you may put it onto the battlefield. " + + "If you don't put the card onto the battlefield, put it into your hand"; + } + + private ParcelbeastEffect(final ParcelbeastEffect effect) { + super(effect); + } + + @Override + public ParcelbeastEffect copy() { + return new ParcelbeastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.lookAtCards("", card, game); + if (card.isLand() && player.chooseUse( + outcome, "Put " + card.getName() + " onto the battlefield?", + "(otherwise put it into your hand", "To battlefield", + "To hand", source, game)) { + player.moveCards(card, Zone.BATTLEFIELD, source, game); + } else { + player.moveCards(card, Zone.HAND, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PartingThoughts.java b/Mage.Sets/src/mage/cards/p/PartingThoughts.java index b6e908c2ee..ddca0883aa 100644 --- a/Mage.Sets/src/mage/cards/p/PartingThoughts.java +++ b/Mage.Sets/src/mage/cards/p/PartingThoughts.java @@ -66,7 +66,7 @@ class PartingThoughtsEffect extends OneShotEffect { numberOfCounters += counter.getCount(); } if (numberOfCounters > 0) { - controller.drawCards(numberOfCounters, game); + controller.drawCards(numberOfCounters, source.getSourceId(), game); controller.loseLife(numberOfCounters, game, false); } } diff --git a/Mage.Sets/src/mage/cards/p/PatagiaTiger.java b/Mage.Sets/src/mage/cards/p/PatagiaTiger.java new file mode 100644 index 0000000000..3565fa9a00 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PatagiaTiger.java @@ -0,0 +1,49 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PatagiaTiger extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.HUMAN); + + public PatagiaTiger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Patagia Tiger enters the battlefield, target Human you control gets +2/+2 until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(2, 2)); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private PatagiaTiger(final PatagiaTiger card) { + super(card); + } + + @Override + public PatagiaTiger copy() { + return new PatagiaTiger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PathOfAncestry.java b/Mage.Sets/src/mage/cards/p/PathOfAncestry.java index 82a171c7a7..5750db49fb 100644 --- a/Mage.Sets/src/mage/cards/p/PathOfAncestry.java +++ b/Mage.Sets/src/mage/cards/p/PathOfAncestry.java @@ -56,7 +56,7 @@ public final class PathOfAncestry extends CardImpl { class PathOfAncestryTriggeredAbility extends TriggeredAbilityImpl { public PathOfAncestryTriggeredAbility(Effect effect) { - super(Zone.ALL, effect, true); + super(Zone.ALL, effect, false); } public PathOfAncestryTriggeredAbility(final PathOfAncestryTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/p/PatientRebuilding.java b/Mage.Sets/src/mage/cards/p/PatientRebuilding.java index a884bf9615..9f4b2ee7bc 100644 --- a/Mage.Sets/src/mage/cards/p/PatientRebuilding.java +++ b/Mage.Sets/src/mage/cards/p/PatientRebuilding.java @@ -1,7 +1,5 @@ package mage.cards.p; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -11,13 +9,14 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.Set; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class PatientRebuilding extends CardImpl { @@ -49,7 +48,7 @@ class PatientRebuildingEffect extends OneShotEffect { public PatientRebuildingEffect() { super(Outcome.DrawCard); - this.staticText = "target opponent puts the top three cards of their library into their graveyard, " + this.staticText = "target opponent mills three cards, " + "then you draw a card for each land card put into that graveyard this way"; } @@ -70,14 +69,14 @@ class PatientRebuildingEffect extends OneShotEffect { return false; } int numberOfLandCards = 0; - Set movedCards = player.moveCardsToGraveyardWithInfo(player.getLibrary().getTopCards(game, 3), source, game, Zone.LIBRARY); + Set movedCards = player.millCards(3, source, game).getCards(game); for (Card card : movedCards) { if (card.isLand()) { numberOfLandCards++; } } if (numberOfLandCards > 0) { - return controller.drawCards(numberOfLandCards, game) > 0; + return controller.drawCards(numberOfLandCards, source.getSourceId(), game) > 0; } return true; } diff --git a/Mage.Sets/src/mage/cards/p/PatrolHound.java b/Mage.Sets/src/mage/cards/p/PatrolHound.java index aa3c45f9ee..266ba9535a 100644 --- a/Mage.Sets/src/mage/cards/p/PatrolHound.java +++ b/Mage.Sets/src/mage/cards/p/PatrolHound.java @@ -22,7 +22,7 @@ public final class PatrolHound extends CardImpl { public PatrolHound(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/p/PatronOfTheOrochi.java b/Mage.Sets/src/mage/cards/p/PatronOfTheOrochi.java index d874b55b6c..96af53188b 100644 --- a/Mage.Sets/src/mage/cards/p/PatronOfTheOrochi.java +++ b/Mage.Sets/src/mage/cards/p/PatronOfTheOrochi.java @@ -33,7 +33,9 @@ public final class PatronOfTheOrochi extends CardImpl { this.power = new MageInt(7); this.toughness = new MageInt(7); - // Snake offering (You may cast this card any time you could cast an instant by sacrificing a Snake and paying the difference in mana costs between this and the sacrificed Snake. Mana cost includes color.) + // Snake offering (You may cast this card any time you could cast an instant + // by sacrificing a Snake and paying the difference in mana costs between this + // and the sacrificed Snake. Mana cost includes color.) this.addAbility(new OfferingAbility(SubType.SNAKE)); // {T}: Untap all Forests and all green creatures. Activate this ability only once each turn. diff --git a/Mage.Sets/src/mage/cards/p/PawnOfUlamog.java b/Mage.Sets/src/mage/cards/p/PawnOfUlamog.java index d9aa4601a3..222b1b8f45 100644 --- a/Mage.Sets/src/mage/cards/p/PawnOfUlamog.java +++ b/Mage.Sets/src/mage/cards/p/PawnOfUlamog.java @@ -1,22 +1,17 @@ - package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentToken; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.EldraziSpawnToken; /** @@ -24,16 +19,25 @@ import mage.game.permanent.token.EldraziSpawnToken; * @author North */ public final class PawnOfUlamog extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature you control"); + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(Predicates.not(TokenPredicate.instance)); + } + public PawnOfUlamog(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.VAMPIRE); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(2); this.toughness = new MageInt(2); - this.addAbility(new PawnOfUlamogTriggeredAbility()); + // Whenever Pawn of Ulamog or another nontoken creature you control dies, you may create a 0/1 colorless + // Eldrazi Spawn creature token. It has "Sacrifice this creature: Add {C}." + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility(new CreateTokenEffect(new EldraziSpawnToken()), true, filter)); } public PawnOfUlamog(final PawnOfUlamog card) { @@ -44,49 +48,4 @@ public final class PawnOfUlamog extends CardImpl { public PawnOfUlamog copy() { return new PawnOfUlamog(this); } -} - -class PawnOfUlamogTriggeredAbility extends TriggeredAbilityImpl { - - public PawnOfUlamogTriggeredAbility() { - super(Zone.BATTLEFIELD, new CreateTokenEffect(new EldraziSpawnToken()), true); - } - - public PawnOfUlamogTriggeredAbility(PawnOfUlamogTriggeredAbility ability) { - super(ability); - } - - @Override - public PawnOfUlamogTriggeredAbility copy() { - return new PawnOfUlamogTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - UUID targetId = event.getTargetId(); - MageObject card = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); - if (card instanceof Permanent && !(card instanceof PermanentToken)) { - Permanent permanent = (Permanent) card; - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.isDiesEvent() - && permanent.isControlledBy(this.controllerId) - && (targetId.equals(this.getSourceId()) - || (permanent.isCreature() - && !targetId.equals(this.getSourceId()) - && !(permanent instanceof PermanentToken)))) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever Pawn of Ulamog or another nontoken creature you control dies, you may create a 0/1 colorless Eldrazi Spawn creature token. It has \"Sacrifice this creature: Add {C}.\""; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PeaceTalks.java b/Mage.Sets/src/mage/cards/p/PeaceTalks.java index bff35048f3..79d01f6043 100644 --- a/Mage.Sets/src/mage/cards/p/PeaceTalks.java +++ b/Mage.Sets/src/mage/cards/p/PeaceTalks.java @@ -1,176 +1,176 @@ -package mage.cards.p; - -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousRuleModifyingEffect; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.RestrictionEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class PeaceTalks extends CardImpl { - - public PeaceTalks(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}"); - - // This turn and next turn, creatures can't attack, - // and players and permanents can't be the targets - // of spells or activated abilities. - this.getSpellAbility().addEffect(new PeaceTalksEffect()); - - } - - private PeaceTalks(final PeaceTalks card) { - super(card); - } - - @Override - public PeaceTalks copy() { - return new PeaceTalks(this); - } -} - -class PeaceTalksEffect extends OneShotEffect { - - public PeaceTalksEffect() { - super(Outcome.Neutral); - this.staticText = "This turn and next turn, creatures can't attack," - + "and players and permanents can't be the targets of spells " - + "or activated abilities"; - } - - public PeaceTalksEffect(final PeaceTalksEffect effect) { - super(effect); - } - - @Override - public PeaceTalksEffect copy() { - return new PeaceTalksEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - RestrictionEffect effect = new PeaceTalksCantAttackEffect(); - game.addEffect(effect, source); - ContinuousRuleModifyingEffect effect2 = new PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities(); - game.addEffect(effect2, source); - return true; - } -} - -class PeaceTalksCantAttackEffect extends RestrictionEffect { - - int startedTurnNum = 0; - - public PeaceTalksCantAttackEffect() { - super(Duration.Custom); - staticText = "Creatures can't attack this turn and next turn"; - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - startedTurnNum = game.getTurnNum(); - } - - public PeaceTalksCantAttackEffect(final PeaceTalksCantAttackEffect effect) { - super(effect); - this.startedTurnNum = effect.startedTurnNum; - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return permanent.isCreature(); - } - - @Override - public boolean canAttack(Game game, boolean canUseChooseDialogs) { - return false; - } - - @Override - public PeaceTalksCantAttackEffect copy() { - return new PeaceTalksCantAttackEffect(this); - } - - @Override - public boolean isInactive(Ability source, Game game) { - if (game.getTurnNum() > (startedTurnNum + 1)) { - this.discard(); - return true; - } - return false; - } -} - -class PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities extends ContinuousRuleModifyingEffectImpl { - - int startedTurnNum = 0; - - public PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities() { - super(Duration.Custom, Outcome.Neutral); - staticText = "players and permanents can't be the targets of spells or activated abilities"; - } - - public PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities(final PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities effect) { - super(effect); - this.startedTurnNum = effect.startedTurnNum; - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - startedTurnNum = game.getTurnNum(); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.CAST_SPELL - || event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - if (event.getTargetId().equals(playerId)) { - return false; - } - } - for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { - if (event.getTargetId().equals(permanent.getId())) { - return false; - } - } - return true; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities copy() { - return new PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities(this); - } - - @Override - public boolean isInactive(Ability source, Game game) { - if (game.getTurnNum() > (startedTurnNum + 1)) { - this.discard(); - return true; - } - return false; - } -} +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousRuleModifyingEffect; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class PeaceTalks extends CardImpl { + + public PeaceTalks(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}"); + + // This turn and next turn, creatures can't attack, + // and players and permanents can't be the targets + // of spells or activated abilities. + this.getSpellAbility().addEffect(new PeaceTalksEffect()); + + } + + private PeaceTalks(final PeaceTalks card) { + super(card); + } + + @Override + public PeaceTalks copy() { + return new PeaceTalks(this); + } +} + +class PeaceTalksEffect extends OneShotEffect { + + public PeaceTalksEffect() { + super(Outcome.Neutral); + this.staticText = "This turn and next turn, creatures can't attack," + + "and players and permanents can't be the targets of spells " + + "or activated abilities"; + } + + public PeaceTalksEffect(final PeaceTalksEffect effect) { + super(effect); + } + + @Override + public PeaceTalksEffect copy() { + return new PeaceTalksEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + RestrictionEffect effect = new PeaceTalksCantAttackEffect(); + game.addEffect(effect, source); + ContinuousRuleModifyingEffect effect2 = new PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities(); + game.addEffect(effect2, source); + return true; + } +} + +class PeaceTalksCantAttackEffect extends RestrictionEffect { + + int startedTurnNum = 0; + + public PeaceTalksCantAttackEffect() { + super(Duration.Custom); + staticText = "Creatures can't attack this turn and next turn"; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + startedTurnNum = game.getTurnNum(); + } + + public PeaceTalksCantAttackEffect(final PeaceTalksCantAttackEffect effect) { + super(effect); + this.startedTurnNum = effect.startedTurnNum; + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.isCreature(); + } + + @Override + public boolean canAttack(Game game, boolean canUseChooseDialogs) { + return false; + } + + @Override + public PeaceTalksCantAttackEffect copy() { + return new PeaceTalksCantAttackEffect(this); + } + + @Override + public boolean isInactive(Ability source, Game game) { + if (game.getTurnNum() > (startedTurnNum + 1)) { + this.discard(); + return true; + } + return false; + } +} + +class PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities extends ContinuousRuleModifyingEffectImpl { + + int startedTurnNum = 0; + + public PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities() { + super(Duration.Custom, Outcome.Neutral); + staticText = "players and permanents can't be the targets of spells or activated abilities"; + } + + public PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities(final PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities effect) { + super(effect); + this.startedTurnNum = effect.startedTurnNum; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + startedTurnNum = game.getTurnNum(); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL + || event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + if (event.getTargetId().equals(playerId)) { + return false; + } + } + for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { + if (event.getTargetId().equals(permanent.getId())) { + return false; + } + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities copy() { + return new PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities(this); + } + + @Override + public boolean isInactive(Ability source, Game game) { + if (game.getTurnNum() > (startedTurnNum + 1)) { + this.discard(); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PearlLakeAncient.java b/Mage.Sets/src/mage/cards/p/PearlLakeAncient.java index 194cb43923..09d36f2957 100644 --- a/Mage.Sets/src/mage/cards/p/PearlLakeAncient.java +++ b/Mage.Sets/src/mage/cards/p/PearlLakeAncient.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.ReturnToHandChosenControlledPermanentCost; import mage.abilities.effects.common.ReturnToHandSourceEffect; @@ -34,7 +34,7 @@ public final class PearlLakeAncient extends CardImpl { this.addAbility(FlashAbility.getInstance()); // Pearl Lake Ancient can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Prowess this.addAbility(new ProwessAbility()); diff --git a/Mage.Sets/src/mage/cards/p/PedanticLearning.java b/Mage.Sets/src/mage/cards/p/PedanticLearning.java index b72a54652c..369e68327b 100644 --- a/Mage.Sets/src/mage/cards/p/PedanticLearning.java +++ b/Mage.Sets/src/mage/cards/p/PedanticLearning.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.Set; import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.mana.ManaCostsImpl; @@ -61,10 +59,8 @@ class PedanticLearningTriggeredAbility extends TriggeredAbilityImpl { Card card = game.getCard(event.getTargetId()); if (card != null) { UUID cardOwnerId = card.getOwnerId(); - Set cardType = card.getCardType(); if (cardOwnerId != null && card.isOwnedBy(getControllerId()) - && cardType != null && card.isLand()) { return true; } diff --git a/Mage.Sets/src/mage/cards/p/PeelFromReality.java b/Mage.Sets/src/mage/cards/p/PeelFromReality.java index b38f818961..766f0e2d6b 100644 --- a/Mage.Sets/src/mage/cards/p/PeelFromReality.java +++ b/Mage.Sets/src/mage/cards/p/PeelFromReality.java @@ -1,44 +1,35 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class PeelFromReality extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public PeelFromReality(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); // Return target creature you control and target creature you don't control to their owners' hands. this.getSpellAbility().addEffect(new PeelFromRealityEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public PeelFromReality(final PeelFromReality card) { + private PeelFromReality(final PeelFromReality card) { super(card); } @@ -50,12 +41,12 @@ public final class PeelFromReality extends CardImpl { class PeelFromRealityEffect extends OneShotEffect { - public PeelFromRealityEffect() { + PeelFromRealityEffect() { super(Outcome.ReturnToHand); this.staticText = "Return target creature you control and target creature you don't control to their owners' hands"; } - public PeelFromRealityEffect(final PeelFromRealityEffect effect) { + private PeelFromRealityEffect(final PeelFromRealityEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java b/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java new file mode 100644 index 0000000000..8db533e2ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java @@ -0,0 +1,65 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PeerIntoTheAbyss extends CardImpl { + + public PeerIntoTheAbyss(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}{B}"); + + // Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time. + this.getSpellAbility().addEffect(new PeerIntoTheAbyssEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); + } + + private PeerIntoTheAbyss(final PeerIntoTheAbyss card) { + super(card); + } + + @Override + public PeerIntoTheAbyss copy() { + return new PeerIntoTheAbyss(this); + } +} + +class PeerIntoTheAbyssEffect extends OneShotEffect { + + PeerIntoTheAbyssEffect() { + super(Outcome.Benefit); + staticText = "Target player draws cards equal to half the number of cards in their library " + + "and loses half their life. Round up each time."; + } + + private PeerIntoTheAbyssEffect(final PeerIntoTheAbyssEffect effect) { + super(effect); + } + + @Override + public PeerIntoTheAbyssEffect copy() { + return new PeerIntoTheAbyssEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (targetPlayer == null) { + return false; + } + targetPlayer.drawCards((int) Math.ceil(targetPlayer.getLibrary().size() / 2.0), source.getSourceId(), game); + targetPlayer.loseLife((int) Math.ceil(targetPlayer.getLife() / 2.0), game, false); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PeerPressure.java b/Mage.Sets/src/mage/cards/p/PeerPressure.java index cde6725dbf..2a130b8330 100644 --- a/Mage.Sets/src/mage/cards/p/PeerPressure.java +++ b/Mage.Sets/src/mage/cards/p/PeerPressure.java @@ -85,7 +85,7 @@ class PeerPressureEffect extends OneShotEffect { if (playerWithMost != null && playerWithMost.equals(controller.getId())) { for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(SubType.byDescription(chosenType), chosenType), controller.getId(), source.getSourceId(), game)) { ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/p/PelakkaWurm.java b/Mage.Sets/src/mage/cards/p/PelakkaWurm.java index 0a690d34e8..3acc2b2a80 100644 --- a/Mage.Sets/src/mage/cards/p/PelakkaWurm.java +++ b/Mage.Sets/src/mage/cards/p/PelakkaWurm.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -28,7 +28,7 @@ public final class PelakkaWurm extends CardImpl { this.addAbility(TrampleAbility.getInstance()); this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(7), false)); - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } public PelakkaWurm(final PelakkaWurm card) { diff --git a/Mage.Sets/src/mage/cards/p/PeltCollector.java b/Mage.Sets/src/mage/cards/p/PeltCollector.java index 7ef8ae3b5f..bd267e7b5b 100644 --- a/Mage.Sets/src/mage/cards/p/PeltCollector.java +++ b/Mage.Sets/src/mage/cards/p/PeltCollector.java @@ -135,7 +135,7 @@ class PeltCollectorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent triggeringCreature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent triggeringCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); Permanent sourceCreature = game.getPermanent(source.getSourceId()); if (!PeltCollectorAbility.isPowerGreater(sourceCreature, triggeringCreature)) { return false; diff --git a/Mage.Sets/src/mage/cards/p/PendantOfProsperity.java b/Mage.Sets/src/mage/cards/p/PendantOfProsperity.java index bdd8d5f4a1..b6cffe30fa 100644 --- a/Mage.Sets/src/mage/cards/p/PendantOfProsperity.java +++ b/Mage.Sets/src/mage/cards/p/PendantOfProsperity.java @@ -5,25 +5,19 @@ import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; -import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetCardInHand; -import mage.target.common.TargetOpponent; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; import mage.abilities.effects.common.EntersBattlefieldUnderControlOfOpponentOfChoiceEffect; @@ -85,7 +79,7 @@ class PendantOfProsperityEffect extends OneShotEffect { if (player == null) { return false; } - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); if (!player.chooseUse(outcome, "Put a land into play from your hand?", source, game)) { return true; } diff --git a/Mage.Sets/src/mage/cards/p/PentadPrism.java b/Mage.Sets/src/mage/cards/p/PentadPrism.java index 236a1ddfb7..e0bf9dca2c 100644 --- a/Mage.Sets/src/mage/cards/p/PentadPrism.java +++ b/Mage.Sets/src/mage/cards/p/PentadPrism.java @@ -1,8 +1,8 @@ - package mage.cards.p; import java.util.UUID; import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.keyword.SunburstAbility; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; @@ -17,12 +17,13 @@ import mage.counters.CounterType; public final class PentadPrism extends CardImpl { public PentadPrism(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Sunburst this.addAbility(new SunburstAbility(this)); // Remove a charge counter from Pentad Prism: Add one mana of any color. - this.addAbility(new AnyColorManaAbility(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(1)))); + this.addAbility(new AnyColorManaAbility(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(1)), + new CountersSourceCount(CounterType.CHARGE), false)); } public PentadPrism(final PentadPrism card) { diff --git a/Mage.Sets/src/mage/cards/p/PenumbraBobcat.java b/Mage.Sets/src/mage/cards/p/PenumbraBobcat.java index cb35264113..0bf0813923 100644 --- a/Mage.Sets/src/mage/cards/p/PenumbraBobcat.java +++ b/Mage.Sets/src/mage/cards/p/PenumbraBobcat.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class PenumbraBobcat extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new PenumbraBobcatToken(), 1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new PenumbraBobcatToken(), 1), false)); } public PenumbraBobcat(final PenumbraBobcat card) { diff --git a/Mage.Sets/src/mage/cards/p/PenumbraKavu.java b/Mage.Sets/src/mage/cards/p/PenumbraKavu.java index 62f28bb944..39c884d8e7 100644 --- a/Mage.Sets/src/mage/cards/p/PenumbraKavu.java +++ b/Mage.Sets/src/mage/cards/p/PenumbraKavu.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class PenumbraKavu extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new PenumbraKavuToken(), 1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new PenumbraKavuToken(), 1), false)); } public PenumbraKavu(final PenumbraKavu card) { diff --git a/Mage.Sets/src/mage/cards/p/PenumbraSpider.java b/Mage.Sets/src/mage/cards/p/PenumbraSpider.java index 9761b8f2b4..34d1aa47ad 100644 --- a/Mage.Sets/src/mage/cards/p/PenumbraSpider.java +++ b/Mage.Sets/src/mage/cards/p/PenumbraSpider.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class PenumbraSpider extends CardImpl { // Reach this.addAbility(ReachAbility.getInstance()); // When Penumbra Spider dies, create a 2/4 black Spider creature token with reach. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new PenumbraSpiderToken()), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new PenumbraSpiderToken()), false)); } public PenumbraSpider(final PenumbraSpider card) { diff --git a/Mage.Sets/src/mage/cards/p/PenumbraWurm.java b/Mage.Sets/src/mage/cards/p/PenumbraWurm.java index ca9654f73d..ac7b100b62 100644 --- a/Mage.Sets/src/mage/cards/p/PenumbraWurm.java +++ b/Mage.Sets/src/mage/cards/p/PenumbraWurm.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; @@ -29,7 +29,7 @@ public final class PenumbraWurm extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Penumbra Wurm dies, create a 6/6 black Wurm creature token with trample. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new PenumbraWurmToken(), 1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new PenumbraWurmToken(), 1), false)); } public PenumbraWurm(final PenumbraWurm card) { diff --git a/Mage.Sets/src/mage/cards/p/PerilousMyr.java b/Mage.Sets/src/mage/cards/p/PerilousMyr.java index 1ccbe47fad..1843606d1f 100644 --- a/Mage.Sets/src/mage/cards/p/PerilousMyr.java +++ b/Mage.Sets/src/mage/cards/p/PerilousMyr.java @@ -4,7 +4,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class PerilousMyr extends CardImpl { this.toughness = new MageInt(1); // When Perilous Myr dies, it deals 2 damage to any target. - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it"), false); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2, "it"), false); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PerimeterSergeant.java b/Mage.Sets/src/mage/cards/p/PerimeterSergeant.java new file mode 100644 index 0000000000..1079023081 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PerimeterSergeant.java @@ -0,0 +1,44 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PerimeterSergeant extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.HUMAN, "Humans"); + + public PerimeterSergeant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever Perimeter Sergeant attacks, other Humans you control get +1/+0 until end of turn. + this.addAbility(new AttacksTriggeredAbility(new BoostControlledEffect( + 1, 0, Duration.EndOfTurn, filter, true + ), false)); + } + + private PerimeterSergeant(final PerimeterSergeant card) { + super(card); + } + + @Override + public PerimeterSergeant copy() { + return new PerimeterSergeant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PerishTheThought.java b/Mage.Sets/src/mage/cards/p/PerishTheThought.java index cd7770f577..00a691c08a 100644 --- a/Mage.Sets/src/mage/cards/p/PerishTheThought.java +++ b/Mage.Sets/src/mage/cards/p/PerishTheThought.java @@ -1,7 +1,7 @@ - package mage.cards.p; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -61,9 +61,10 @@ class PerishTheThoughtEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player targetOpponent = game.getPlayer(source.getFirstTarget()); - if (targetOpponent != null) { + MageObject sourceObject = source.getSourceObject(game); + if (targetOpponent != null && sourceObject != null) { if (!targetOpponent.getHand().isEmpty()) { - targetOpponent.revealCards("Perish the Thought", targetOpponent.getHand(), game); + targetOpponent.revealCards(sourceObject.getIdName(), targetOpponent.getHand(), game); Player you = game.getPlayer(source.getControllerId()); if (you != null) { TargetCard target = new TargetCard(Zone.HAND, filter); @@ -71,13 +72,13 @@ class PerishTheThoughtEffect extends OneShotEffect { if (you.choose(Outcome.Neutral, targetOpponent.getHand(), target, game)) { Card chosenCard = targetOpponent.getHand().get(target.getFirstTarget(), game); if (chosenCard != null) { - chosenCard.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); - targetOpponent.shuffleLibrary(source, game); + targetOpponent.shuffleCardsToLibrary(chosenCard, game, source); } } - return true; + } } + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/p/PermeatingMass.java b/Mage.Sets/src/mage/cards/p/PermeatingMass.java index 286739e23c..3a08fd59e4 100644 --- a/Mage.Sets/src/mage/cards/p/PermeatingMass.java +++ b/Mage.Sets/src/mage/cards/p/PermeatingMass.java @@ -59,7 +59,7 @@ class PermeatingMassEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability ability) { - Permanent copyTo = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, ability)); + Permanent copyTo = game.getPermanent(getTargetPointer().getFirst(game, ability)); if (copyTo != null) { Permanent copyFrom = ability.getSourcePermanentOrLKI(game); if (copyFrom != null) { diff --git a/Mage.Sets/src/mage/cards/p/Persecute.java b/Mage.Sets/src/mage/cards/p/Persecute.java index 606121cf23..b5cad0fddb 100644 --- a/Mage.Sets/src/mage/cards/p/Persecute.java +++ b/Mage.Sets/src/mage/cards/p/Persecute.java @@ -1,24 +1,23 @@ - package mage.cards.p; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.choices.ChoiceColor; import mage.constants.CardType; import mage.constants.Outcome; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Persecute extends CardImpl { @@ -29,7 +28,6 @@ public final class Persecute extends CardImpl { // Choose a color. Target player reveals their hand and discards all cards of that color. this.getSpellAbility().addEffect(new PersecuteEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - } public Persecute(final Persecute card) { @@ -44,12 +42,12 @@ public final class Persecute extends CardImpl { class PersecuteEffect extends OneShotEffect { - public PersecuteEffect() { + PersecuteEffect() { super(Outcome.Discard); this.staticText = "Choose a color. Target player reveals their hand and discards all cards of that color"; } - public PersecuteEffect(final PersecuteEffect effect) { + private PersecuteEffect(final PersecuteEffect effect) { super(effect); } @@ -64,17 +62,16 @@ class PersecuteEffect extends OneShotEffect { MageObject sourceObject = game.getObject(source.getSourceId()); Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); ChoiceColor choice = new ChoiceColor(); - if (controller != null && sourceObject != null && targetPlayer != null && controller.choose(outcome, choice, game)) { - Cards hand = targetPlayer.getHand(); - targetPlayer.revealCards(sourceObject.getIdName(), hand, game); - Set cards = hand.getCards(game); - for (Card card : cards) { - if (card != null && card.getColor(game).shares(choice.getColor())) { - targetPlayer.discard(card, source, game); - } - } - return true; + if (controller == null + || sourceObject == null + || targetPlayer == null + || !controller.choose(outcome, choice, game)) { + return false; } - return false; + FilterCard filterCard = new FilterCard(); + filterCard.add(new ColorPredicate(choice.getColor())); + targetPlayer.revealCards(source, targetPlayer.getHand(), game); + targetPlayer.discard(new CardsImpl(targetPlayer.getHand().getCards(filterCard, game)), source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/p/PersonalIncarnation.java b/Mage.Sets/src/mage/cards/p/PersonalIncarnation.java index d0c82220e6..c39a4cc0fc 100644 --- a/Mage.Sets/src/mage/cards/p/PersonalIncarnation.java +++ b/Mage.Sets/src/mage/cards/p/PersonalIncarnation.java @@ -4,7 +4,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; @@ -41,7 +41,7 @@ public final class PersonalIncarnation extends CardImpl { ability.setMayActivate(TargetController.OWNER); this.addAbility(ability); // When Personal Incarnation dies, its owner loses half their life, rounded up. - this.addAbility(new DiesTriggeredAbility(new PersonalIncarnationLoseHalfLifeEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new PersonalIncarnationLoseHalfLifeEffect())); } public PersonalIncarnation(final PersonalIncarnation card) { diff --git a/Mage.Sets/src/mage/cards/p/PestilentHaze.java b/Mage.Sets/src/mage/cards/p/PestilentHaze.java new file mode 100644 index 0000000000..424901bd53 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PestilentHaze.java @@ -0,0 +1,70 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PestilentHaze extends CardImpl { + + public PestilentHaze(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); + + // Choose one — + // • All creatures get -2/-2 until end of turn. + this.getSpellAbility().addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn)); + + // • Remove two loyalty counters from each planeswalker. + this.getSpellAbility().addMode(new Mode(new PestilentHazeEffect())); + } + + private PestilentHaze(final PestilentHaze card) { + super(card); + } + + @Override + public PestilentHaze copy() { + return new PestilentHaze(this); + } +} + +class PestilentHazeEffect extends OneShotEffect { + + PestilentHazeEffect() { + super(Outcome.Benefit); + staticText = "remove two loyalty counters from each planeswalker"; + } + + private PestilentHazeEffect(final PestilentHazeEffect effect) { + super(effect); + } + + @Override + public PestilentHazeEffect copy() { + return new PestilentHazeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_PERMANENT_PLANESWALKER, + source.getControllerId(), source.getSourceId(), game + ).stream() + .forEach(permanent -> permanent.removeCounters(CounterType.LOYALTY.createInstance(2), game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PetalsOfInsight.java b/Mage.Sets/src/mage/cards/p/PetalsOfInsight.java index e331b79599..ab83a8e016 100644 --- a/Mage.Sets/src/mage/cards/p/PetalsOfInsight.java +++ b/Mage.Sets/src/mage/cards/p/PetalsOfInsight.java @@ -71,7 +71,7 @@ class PetalsOfInsightEffect extends OneShotEffect { controller.moveCards(spellCard, Zone.HAND, source, game); } } else { - controller.drawCards(3, game); + controller.drawCards(3, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/p/PetraSphinx.java b/Mage.Sets/src/mage/cards/p/PetraSphinx.java index 253d4e77f0..c97a792299 100644 --- a/Mage.Sets/src/mage/cards/p/PetraSphinx.java +++ b/Mage.Sets/src/mage/cards/p/PetraSphinx.java @@ -78,7 +78,7 @@ class PetraSphinxEffect extends OneShotEffect { if (card != null) { Cards cards = new CardsImpl(card); player.revealCards(source, cards, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { player.moveCards(cards, Zone.HAND, source, game); } else { player.moveCards(cards, Zone.GRAVEYARD, source, game); diff --git a/Mage.Sets/src/mage/cards/p/PetrifiedWoodKin.java b/Mage.Sets/src/mage/cards/p/PetrifiedWoodKin.java index a42b819797..140cb43463 100644 --- a/Mage.Sets/src/mage/cards/p/PetrifiedWoodKin.java +++ b/Mage.Sets/src/mage/cards/p/PetrifiedWoodKin.java @@ -7,7 +7,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.OneShotEffect; import mage.constants.SubType; @@ -44,7 +44,7 @@ public final class PetrifiedWoodKin extends CardImpl { this.toughness = new MageInt(3); // Petrified Wood-Kin can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Bloodthirst X this.addAbility(new EntersBattlefieldAbility( diff --git a/Mage.Sets/src/mage/cards/p/PhageTheUntouchable.java b/Mage.Sets/src/mage/cards/p/PhageTheUntouchable.java index e1bc4cafc8..db9dec6f2b 100644 --- a/Mage.Sets/src/mage/cards/p/PhageTheUntouchable.java +++ b/Mage.Sets/src/mage/cards/p/PhageTheUntouchable.java @@ -7,7 +7,7 @@ import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.InvertCondition; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.LoseGameSourceControllerEffect; @@ -37,7 +37,7 @@ public final class PhageTheUntouchable extends CardImpl { // When Phage the Untouchable enters the battlefield, if you didn't cast it from your hand, you lose the game. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new LoseGameSourceControllerEffect(), false), - new InvertCondition(CastFromHandSourceCondition.instance), + new InvertCondition(CastFromHandSourcePermanentCondition.instance), "When {this} enters the battlefield, if you didn't cast it from your hand, you lose the game" ), new CastFromHandWatcher()); diff --git a/Mage.Sets/src/mage/cards/p/PhalanxFormation.java b/Mage.Sets/src/mage/cards/p/PhalanxFormation.java index 6375c4ef24..76e9bc4288 100644 --- a/Mage.Sets/src/mage/cards/p/PhalanxFormation.java +++ b/Mage.Sets/src/mage/cards/p/PhalanxFormation.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -12,23 +10,24 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PhalanxFormation extends CardImpl { public PhalanxFormation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); // Strive — Phalanx Formation costs {1}{W} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{1}{W}")); + // Any number of target creatures each gain double strike until end of turn. Effect effect = new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn); effect.setText("Any number of target creatures each gain double strike until end of turn"); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(0,Integer.MAX_VALUE)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE)); } public PhalanxFormation(final PhalanxFormation card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalMount.java b/Mage.Sets/src/mage/cards/p/PhantasmalMount.java index 549941c397..9c00008da8 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalMount.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalMount.java @@ -1,156 +1,156 @@ -package mage.cards.p; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.SacrificeTargetEffect; -import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.constants.SubType; -import mage.abilities.keyword.FlyingAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.ToughnessPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author jeffwadsworth - */ -public final class PhantasmalMount extends CardImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("with toughness 2 or less"); - - static { - filter.add(new ToughnessPredicate(ComparisonType.FEWER_THAN, 3)); - } - - public PhantasmalMount(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); - - this.subtype.add(SubType.ILLUSION); - this.subtype.add(SubType.HORSE); - this.power = new MageInt(1); - this.toughness = new MageInt(1); - - // Flying - this.addAbility(FlyingAbility.getInstance()); - - // {tap}: Target creature you control with toughness 2 or less gets +1/+1 and gains flying until end of turn. When Phantasmal Mount leaves the battlefield this turn, sacrifice that creature. When the creature leaves the battlefield this turn, sacrifice Phantasmal Mount. - Ability activatedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PhantasmalMountEffect(), new TapSourceCost()); - activatedAbility.addTarget(new TargetControlledCreaturePermanent(filter)); - this.addAbility(activatedAbility); - - } - - private PhantasmalMount(final PhantasmalMount card) { - super(card); - } - - @Override - public PhantasmalMount copy() { - return new PhantasmalMount(this); - } -} - -class PhantasmalMountEffect extends OneShotEffect { - - PhantasmalMountEffect() { - super(Outcome.Neutral); - staticText = "Target creature you control with toughness 2 or less gets +1/+1 " - + "and gains flying until end of turn. When {this} leaves the battlefield this turn, " - + "sacrifice that creature. When the creature leaves the battlefield this turn, sacrifice {this}"; - } - - PhantasmalMountEffect(PhantasmalMountEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanent(source.getFirstTarget()); - if (targetCreature != null) { - ContinuousEffect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); - game.addEffect(effect, source); - Effect sacrificeCreatureEffect = new SacrificeTargetEffect(); - Effect sacrificePhantasmalMountEffect = new SacrificeTargetEffect(); - ContinuousEffect gainAbility = new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); - gainAbility.setTargetPointer(new FixedTarget(source.getFirstTarget())); - game.addEffect(gainAbility, source); - sacrificeCreatureEffect.setTargetPointer(new FixedTarget(source.getFirstTarget())); - sacrificePhantasmalMountEffect.setTargetPointer(new FixedTarget(source.getSourceId())); - DelayedTriggeredAbility dTA = new PhantasmalMountDelayedTriggeredAbility( - sacrificeCreatureEffect, - source.getSourceId()); - DelayedTriggeredAbility dTA2 = new PhantasmalMountDelayedTriggeredAbility( - sacrificePhantasmalMountEffect, - source.getFirstTarget()); - game.addDelayedTriggeredAbility(dTA, source); - game.addDelayedTriggeredAbility(dTA2, source); - return true; - } - return false; - } - - @Override - public PhantasmalMountEffect copy() { - return new PhantasmalMountEffect(this); - } -} - -class PhantasmalMountDelayedTriggeredAbility extends DelayedTriggeredAbility { - - UUID creatureId; - - PhantasmalMountDelayedTriggeredAbility(Effect effect, UUID creatureId) { - super(effect, Duration.EndOfTurn, true); - this.creatureId = creatureId; - } - - PhantasmalMountDelayedTriggeredAbility(PhantasmalMountDelayedTriggeredAbility ability) { - super(ability); - this.creatureId = ability.creatureId; - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == EventType.ZONE_CHANGE - && event.getTargetId().equals(creatureId)) { - return true; - } - return false; - } - - @Override - public PhantasmalMountDelayedTriggeredAbility copy() { - return new PhantasmalMountDelayedTriggeredAbility(this); - } - - @Override - public String getRule() { - return "this left the battlefield"; - } -} +package mage.cards.p; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.ToughnessPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jeffwadsworth + */ +public final class PhantasmalMount extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("with toughness 2 or less"); + + static { + filter.add(new ToughnessPredicate(ComparisonType.FEWER_THAN, 3)); + } + + public PhantasmalMount(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.ILLUSION); + this.subtype.add(SubType.HORSE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {tap}: Target creature you control with toughness 2 or less gets +1/+1 and gains flying until end of turn. When Phantasmal Mount leaves the battlefield this turn, sacrifice that creature. When the creature leaves the battlefield this turn, sacrifice Phantasmal Mount. + Ability activatedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PhantasmalMountEffect(), new TapSourceCost()); + activatedAbility.addTarget(new TargetControlledCreaturePermanent(filter)); + this.addAbility(activatedAbility); + + } + + private PhantasmalMount(final PhantasmalMount card) { + super(card); + } + + @Override + public PhantasmalMount copy() { + return new PhantasmalMount(this); + } +} + +class PhantasmalMountEffect extends OneShotEffect { + + PhantasmalMountEffect() { + super(Outcome.Neutral); + staticText = "Target creature you control with toughness 2 or less gets +1/+1 " + + "and gains flying until end of turn. When {this} leaves the battlefield this turn, " + + "sacrifice that creature. When the creature leaves the battlefield this turn, sacrifice {this}"; + } + + PhantasmalMountEffect(PhantasmalMountEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent targetCreature = game.getPermanent(source.getFirstTarget()); + if (targetCreature != null) { + ContinuousEffect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + game.addEffect(effect, source); + Effect sacrificeCreatureEffect = new SacrificeTargetEffect(); + Effect sacrificePhantasmalMountEffect = new SacrificeTargetEffect(); + ContinuousEffect gainAbility = new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); + gainAbility.setTargetPointer(new FixedTarget(source.getFirstTarget())); + game.addEffect(gainAbility, source); + sacrificeCreatureEffect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + sacrificePhantasmalMountEffect.setTargetPointer(new FixedTarget(source.getSourceId())); + DelayedTriggeredAbility dTA = new PhantasmalMountDelayedTriggeredAbility( + sacrificeCreatureEffect, + source.getSourceId()); + DelayedTriggeredAbility dTA2 = new PhantasmalMountDelayedTriggeredAbility( + sacrificePhantasmalMountEffect, + source.getFirstTarget()); + game.addDelayedTriggeredAbility(dTA, source); + game.addDelayedTriggeredAbility(dTA2, source); + return true; + } + return false; + } + + @Override + public PhantasmalMountEffect copy() { + return new PhantasmalMountEffect(this); + } +} + +class PhantasmalMountDelayedTriggeredAbility extends DelayedTriggeredAbility { + + UUID creatureId; + + PhantasmalMountDelayedTriggeredAbility(Effect effect, UUID creatureId) { + super(effect, Duration.EndOfTurn, true); + this.creatureId = creatureId; + } + + PhantasmalMountDelayedTriggeredAbility(PhantasmalMountDelayedTriggeredAbility ability) { + super(ability); + this.creatureId = ability.creatureId; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == EventType.ZONE_CHANGE + && event.getTargetId().equals(creatureId)) { + return true; + } + return false; + } + + @Override + public PhantasmalMountDelayedTriggeredAbility copy() { + return new PhantasmalMountDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "this left the battlefield"; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalSphere.java b/Mage.Sets/src/mage/cards/p/PhantasmalSphere.java index fb57f5e4ae..689becf0c8 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalSphere.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalSphere.java @@ -1,121 +1,121 @@ -package mage.cards.p; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.LeavesBattlefieldTriggeredAbility; -import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateTokenTargetEffect; -import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.constants.SubType; -import mage.abilities.keyword.FlyingAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.token.TokenImpl; -import mage.players.Player; -import mage.target.common.TargetOpponent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author jeffwadsworth - */ -public final class PhantasmalSphere extends CardImpl { - - public PhantasmalSphere(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); - - this.subtype.add(SubType.ILLUSION); - this.power = new MageInt(0); - this.toughness = new MageInt(1); - - // Flying - this.addAbility(FlyingAbility.getInstance()); - - // At the beginning of your upkeep, put a +1/+1 counter on Phantasmal Sphere, then sacrifice Phantasmal Sphere unless you pay {1} for each +1/+1 counter on it. - Ability ability = new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), TargetController.YOU, false); - Effect effect = new SacrificeSourceUnlessPaysEffect(new CountersSourceCount(CounterType.P1P1)); - effect.setText("then sacrifice {this} unless you pay {1} for each +1/+1 counter on it."); - ability.addEffect(effect); - this.addAbility(ability); - - // When Phantasmal Sphere leaves the battlefield, target opponent puts an X/X blue Orb creature token with flying onto the battlefield, where X is the number of +1/+1 counters on Phantasmal Sphere. - Ability ability2 = new LeavesBattlefieldTriggeredAbility(new PhantasmalSphereEffect(), false); - ability2.addTarget(new TargetOpponent()); - this.addAbility(ability2); - - } - - private PhantasmalSphere(final PhantasmalSphere card) { - super(card); - } - - @Override - public PhantasmalSphere copy() { - return new PhantasmalSphere(this); - } -} - -class PhantasmalSphereEffect extends OneShotEffect { - - public PhantasmalSphereEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "target opponent puts an X/X blue Orb creature token " - + "with flying onto the battlefield, where X is the number " - + "of +1/+1 counters on {this}"; - } - - public PhantasmalSphereEffect(final PhantasmalSphereEffect effect) { - super(effect); - } - - @Override - public PhantasmalSphereEffect copy() { - return new PhantasmalSphereEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Player targetOpponent = game.getPlayer(source.getFirstTarget()); - if (controller != null - && targetOpponent != null) { - Effect effect = new CreateTokenTargetEffect(new PhantasmalSphereToken( - new CountersSourceCount(CounterType.P1P1).calculate( - game, source, null))); - effect.setTargetPointer(new FixedTarget(targetOpponent.getId())); - effect.apply(game, source); - } - return false; - } -} - -class PhantasmalSphereToken extends TokenImpl { - - public PhantasmalSphereToken(int xValue) { - super("Orb", "X/X blue Orb creature token with flying"); - cardType.add(CardType.CREATURE); - color.setBlue(true); - subtype.add(SubType.ORB); - power = new MageInt(xValue); - toughness = new MageInt(xValue); - addAbility(FlyingAbility.getInstance()); - } - - public PhantasmalSphereToken(final PhantasmalSphereToken token) { - super(token); - } - - public PhantasmalSphereToken copy() { - return new PhantasmalSphereToken(this); - } -} +package mage.cards.p; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.token.TokenImpl; +import mage.players.Player; +import mage.target.common.TargetOpponent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jeffwadsworth + */ +public final class PhantasmalSphere extends CardImpl { + + public PhantasmalSphere(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.ILLUSION); + this.power = new MageInt(0); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of your upkeep, put a +1/+1 counter on Phantasmal Sphere, then sacrifice Phantasmal Sphere unless you pay {1} for each +1/+1 counter on it. + Ability ability = new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), TargetController.YOU, false); + Effect effect = new SacrificeSourceUnlessPaysEffect(new CountersSourceCount(CounterType.P1P1)); + effect.setText("then sacrifice {this} unless you pay {1} for each +1/+1 counter on it."); + ability.addEffect(effect); + this.addAbility(ability); + + // When Phantasmal Sphere leaves the battlefield, target opponent puts an X/X blue Orb creature token with flying onto the battlefield, where X is the number of +1/+1 counters on Phantasmal Sphere. + Ability ability2 = new LeavesBattlefieldTriggeredAbility(new PhantasmalSphereEffect(), false); + ability2.addTarget(new TargetOpponent()); + this.addAbility(ability2); + + } + + private PhantasmalSphere(final PhantasmalSphere card) { + super(card); + } + + @Override + public PhantasmalSphere copy() { + return new PhantasmalSphere(this); + } +} + +class PhantasmalSphereEffect extends OneShotEffect { + + public PhantasmalSphereEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "target opponent puts an X/X blue Orb creature token " + + "with flying onto the battlefield, where X is the number " + + "of +1/+1 counters on {this}"; + } + + public PhantasmalSphereEffect(final PhantasmalSphereEffect effect) { + super(effect); + } + + @Override + public PhantasmalSphereEffect copy() { + return new PhantasmalSphereEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player targetOpponent = game.getPlayer(source.getFirstTarget()); + if (controller != null + && targetOpponent != null) { + Effect effect = new CreateTokenTargetEffect(new PhantasmalSphereToken( + new CountersSourceCount(CounterType.P1P1).calculate( + game, source, null))); + effect.setTargetPointer(new FixedTarget(targetOpponent.getId())); + effect.apply(game, source); + } + return false; + } +} + +class PhantasmalSphereToken extends TokenImpl { + + public PhantasmalSphereToken(int xValue) { + super("Orb", "X/X blue Orb creature token with flying"); + cardType.add(CardType.CREATURE); + color.setBlue(true); + subtype.add(SubType.ORB); + power = new MageInt(xValue); + toughness = new MageInt(xValue); + addAbility(FlyingAbility.getInstance()); + } + + public PhantasmalSphereToken(final PhantasmalSphereToken token) { + super(token); + } + + public PhantasmalSphereToken copy() { + return new PhantasmalSphereToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PhantomWhelp.java b/Mage.Sets/src/mage/cards/p/PhantomWhelp.java index 33d2e7e760..f397cf302a 100644 --- a/Mage.Sets/src/mage/cards/p/PhantomWhelp.java +++ b/Mage.Sets/src/mage/cards/p/PhantomWhelp.java @@ -22,7 +22,7 @@ public final class PhantomWhelp extends CardImpl { public PhantomWhelp(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); this.subtype.add(SubType.ILLUSION); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/p/PhaseDolphin.java b/Mage.Sets/src/mage/cards/p/PhaseDolphin.java new file mode 100644 index 0000000000..86173b7b77 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PhaseDolphin.java @@ -0,0 +1,53 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PhaseDolphin extends CardImpl { + + private static final FilterPermanent filter + = new FilterAttackingCreature("another target attacking creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public PhaseDolphin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.WHALE); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Whenever Phase Dolphin attacks, another target attacking creature can't be blocked this turn. + Ability ability = new AttacksTriggeredAbility(new CantBeBlockedTargetEffect() + .setText("another target attacking creature can't be blocked this turn"), false); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private PhaseDolphin(final PhaseDolphin card) { + super(card); + } + + @Override + public PhaseDolphin copy() { + return new PhaseDolphin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PhenaxGodOfDeception.java b/Mage.Sets/src/mage/cards/p/PhenaxGodOfDeception.java index ec36c25ab3..decaf844f2 100644 --- a/Mage.Sets/src/mage/cards/p/PhenaxGodOfDeception.java +++ b/Mage.Sets/src/mage/cards/p/PhenaxGodOfDeception.java @@ -45,14 +45,12 @@ public final class PhenaxGodOfDeception extends CardImpl { // Creatures you control have "{T}: Target player puts the top X cards of their library into their graveyard, where X is this creature's toughness." Ability ability = new SimpleActivatedAbility( new PutTopCardOfLibraryIntoGraveTargetEffect(SourcePermanentToughnessValue.getInstance()) - .setText("Target player puts the top X cards of their library into their graveyard, " + - "where X is this creature's toughness"), new TapSourceCost()); + .setText("Target player mills X cards, where X is this creature's toughness"), new TapSourceCost()); ability.addTarget(new TargetPlayer()); this.addAbility(new SimpleStaticAbility( new GainAbilityControlledEffect( ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES, false - ).setText("Creatures you control have \"{T}: Target player puts the top X cards of their library " + - "into their graveyard, where X is this creature's toughness.\"") + ).setText("Creatures you control have \"{T}: Target player mills X cards, where X is this creature's toughness.\"") )); } diff --git a/Mage.Sets/src/mage/cards/p/PheresBandBrawler.java b/Mage.Sets/src/mage/cards/p/PheresBandBrawler.java index 327f6450dd..b4a3693517 100644 --- a/Mage.Sets/src/mage/cards/p/PheresBandBrawler.java +++ b/Mage.Sets/src/mage/cards/p/PheresBandBrawler.java @@ -8,9 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -20,13 +18,6 @@ import java.util.UUID; */ public final class PheresBandBrawler extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public PheresBandBrawler(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); @@ -38,7 +29,7 @@ public final class PheresBandBrawler extends CardImpl { // When Pheres-Band Brawler enters the battlefield, it fights up to one target creature you don't control. Ability ability = new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect() .setText("it fights up to one target creature you don't control")); - ability.addTarget(new TargetPermanent(0, 1, filter, false)); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianAltar.java b/Mage.Sets/src/mage/cards/p/PhyrexianAltar.java index 1fd96e1d32..1ff13dc098 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianAltar.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianAltar.java @@ -3,6 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.common.PermanentsYouControlCount; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -21,7 +22,10 @@ public final class PhyrexianAltar extends CardImpl { // Sacrifice a creature: Add one mana of any color. this.addAbility(new AnyColorManaAbility( - new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT)))); + new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT)), + PermanentsYouControlCount.instance, + false + )); } public PhyrexianAltar(final PhyrexianAltar card) { diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java b/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java index a0880af364..2ca284b737 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java @@ -1,7 +1,6 @@ - package mage.cards.p; -import java.util.UUID; +import java.util.Optional; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -11,23 +10,20 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.ChooseACardNameEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; +import mage.util.CardUtil; +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class PhyrexianRevoker extends CardImpl { public PhyrexianRevoker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); this.subtype.add(SubType.HORROR); this.power = new MageInt(2); this.toughness = new MageInt(1); @@ -47,7 +43,6 @@ public final class PhyrexianRevoker extends CardImpl { public PhyrexianRevoker copy() { return new PhyrexianRevoker(this); } - } class PhyrexianRevokerEffect2 extends ContinuousRuleModifyingEffectImpl { @@ -75,7 +70,7 @@ class PhyrexianRevokerEffect2 extends ContinuousRuleModifyingEffectImpl { public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source.getSourceId()); if (mageObject != null) { - return "You can't activate abilities of sources with that name (" + mageObject.getLogName() + " in play)."; + return "You can't activate abilities of sources with that name (" + mageObject.getName() + " in play)."; } return null; } @@ -83,12 +78,16 @@ class PhyrexianRevokerEffect2 extends ContinuousRuleModifyingEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == EventType.ACTIVATE_ABILITY) { - MageObject object = game.getObject(event.getSourceId()); - if (object != null && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) { - return true; + MageObject object = game.getObject(event.getSourceId()); // Can happen for special ability???? + if (object != null) { + Optional optAbility = object.getAbilities().get(event.getTargetId()); + if (optAbility.isPresent() && AbilityType.SPECIAL_ACTION == optAbility.get().getAbilityType()) { + return false; + } + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(object, cardName, game); } } return false; } - } diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianTower.java b/Mage.Sets/src/mage/cards/p/PhyrexianTower.java index a677a89792..f8761cd1e7 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianTower.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianTower.java @@ -26,10 +26,10 @@ public final class PhyrexianTower extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); addSuperType(SuperType.LEGENDARY); - // {tap}: Add {C}. + // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); - // {tap}, Sacrifice a creature: Add {B}{B}. + // {T}, Sacrifice a creature: Add {B}{B}. Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlackMana(2), new TapSourceCost()); ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/p/Phytotitan.java b/Mage.Sets/src/mage/cards/p/Phytotitan.java index 30ca720f79..e8c6591370 100644 --- a/Mage.Sets/src/mage/cards/p/Phytotitan.java +++ b/Mage.Sets/src/mage/cards/p/Phytotitan.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -32,7 +32,7 @@ public final class Phytotitan extends CardImpl { this.toughness = new MageInt(2); // When Phytotitan dies, return it to the battlefield tapped under its owner's control at the beginning of their next upkeep. - this.addAbility(new DiesTriggeredAbility(new PhytotitanEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new PhytotitanEffect())); } public Phytotitan(final Phytotitan card) { diff --git a/Mage.Sets/src/mage/cards/p/PistusStrike.java b/Mage.Sets/src/mage/cards/p/PistusStrike.java index 04992db9a4..7ffe18afc1 100644 --- a/Mage.Sets/src/mage/cards/p/PistusStrike.java +++ b/Mage.Sets/src/mage/cards/p/PistusStrike.java @@ -66,7 +66,7 @@ class PoisonControllerTargetCreatureEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/p/PitchburnDevils.java b/Mage.Sets/src/mage/cards/p/PitchburnDevils.java index d715c2eb37..3f18bbe211 100644 --- a/Mage.Sets/src/mage/cards/p/PitchburnDevils.java +++ b/Mage.Sets/src/mage/cards/p/PitchburnDevils.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class PitchburnDevils extends CardImpl { this.toughness = new MageInt(3); // When Pitchburn Devils dies, it deals 3 damage to any target. - DiesTriggeredAbility ability = new DiesTriggeredAbility(new DamageTargetEffect(3, "it")); + DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(3, "it")); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PithingNeedle.java b/Mage.Sets/src/mage/cards/p/PithingNeedle.java index d8049e8ab0..2ec117dff4 100644 --- a/Mage.Sets/src/mage/cards/p/PithingNeedle.java +++ b/Mage.Sets/src/mage/cards/p/PithingNeedle.java @@ -1,7 +1,5 @@ package mage.cards.p; -import java.util.Optional; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; @@ -11,12 +9,18 @@ import mage.abilities.effects.common.ChooseACardNameEffect; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; /** - * * @author jeffwadsworth, nox */ public final class PithingNeedle extends CardImpl { @@ -70,14 +74,13 @@ class PithingNeedleEffect extends ContinuousRuleModifyingEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { MageObject object = game.getObject(event.getSourceId()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); Optional ability = game.getAbility(event.getTargetId(), event.getSourceId()); - if (ability.isPresent() + if (ability.isPresent() && object != null) { - if (game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) // controller in range + return game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) // controller in range && !(ability.get() instanceof ActivatedManaAbilityImpl) // not an activated mana ability - && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) { - return true; - } + && CardUtil.haveSameNames(object, cardName, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/p/Plagiarize.java b/Mage.Sets/src/mage/cards/p/Plagiarize.java index 8e4d4cc4a9..405be022a2 100644 --- a/Mage.Sets/src/mage/cards/p/Plagiarize.java +++ b/Mage.Sets/src/mage/cards/p/Plagiarize.java @@ -64,7 +64,7 @@ class PlagiarizeEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(1, game, event.getAppliedEffects()); + player.drawCards(1, event.getSourceId(), game, event.getAppliedEffects()); } return true; } diff --git a/Mage.Sets/src/mage/cards/p/PlagueDogs.java b/Mage.Sets/src/mage/cards/p/PlagueDogs.java index 9eda0693bc..f12376dd91 100644 --- a/Mage.Sets/src/mage/cards/p/PlagueDogs.java +++ b/Mage.Sets/src/mage/cards/p/PlagueDogs.java @@ -4,7 +4,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -26,12 +26,12 @@ public final class PlagueDogs extends CardImpl { public PlagueDogs(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}"); this.subtype.add(SubType.ZOMBIE); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); // When Plague Dogs dies, all creatures get -1/-1 until end of turn. - this.addAbility(new DiesTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn), false)); + this.addAbility(new DiesSourceTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn), false)); // {2}, Sacrifice Plague Dogs: Draw a card. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{2}")); ability.addCost(new SacrificeSourceCost()); diff --git a/Mage.Sets/src/mage/cards/p/PlagueSpitter.java b/Mage.Sets/src/mage/cards/p/PlagueSpitter.java index e312b05ab3..c80ac79d91 100644 --- a/Mage.Sets/src/mage/cards/p/PlagueSpitter.java +++ b/Mage.Sets/src/mage/cards/p/PlagueSpitter.java @@ -4,7 +4,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageEverythingEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -28,7 +28,7 @@ public final class PlagueSpitter extends CardImpl { // At the beginning of your upkeep, Plague Spitter deals 1 damage to each creature and each player. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DamageEverythingEffect(1), TargetController.YOU, false)); // When Plague Spitter dies, Plague Spitter deals 1 damage to each creature and each player. - this.addAbility(new DiesTriggeredAbility(new DamageEverythingEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DamageEverythingEffect(1), false)); } public PlagueSpitter(final PlagueSpitter card) { diff --git a/Mage.Sets/src/mage/cards/p/PlagueWight.java b/Mage.Sets/src/mage/cards/p/PlagueWight.java index 433b85b01d..aa6551c174 100644 --- a/Mage.Sets/src/mage/cards/p/PlagueWight.java +++ b/Mage.Sets/src/mage/cards/p/PlagueWight.java @@ -2,7 +2,7 @@ package mage.cards.p; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.cards.CardImpl; @@ -32,7 +32,7 @@ public final class PlagueWight extends CardImpl { this.toughness = new MageInt(1); // Whenever Plague Wight becomes blocked, each creature blocking it gets -1/-1 until end of turn. - this.addAbility(new BecomesBlockedTriggeredAbility(new PlagueWightEffect(), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new PlagueWightEffect(), false)); } private PlagueWight(final PlagueWight card) { diff --git a/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java b/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java index 1662e16197..585eac5656 100644 --- a/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java +++ b/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java @@ -67,14 +67,13 @@ class PlaneswalkersMischiefEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player opponent = game.getPlayer(source.getFirstTarget()); - if (opponent != null - && opponent.getHand().size() > 0) { + if (opponent != null && opponent.getHand().size() > 0) { Card revealedCard = opponent.getHand().getRandom(game); if (revealedCard == null) { return false; } Cards cards = new CardsImpl(revealedCard); - opponent.revealCards("Planeswalker's Mischief Reveal", cards, game); + opponent.revealCards(source, cards, game); if (revealedCard.isInstant() || revealedCard.isSorcery()) { opponent.moveCardToExileWithInfo(revealedCard, source.getSourceId(), "Planeswalker's Mischief", source.getSourceId(), game, Zone.HAND, true); @@ -123,8 +122,7 @@ class PlaneswalkersMischiefCastFromExileEffect extends AsThoughEffectImpl { Card card = game.getCard(objectId); if (player != null && card != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - return true; + return allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game); } } return false; @@ -146,7 +144,7 @@ class PlaneswalkersMischiefCondition implements Condition { if (!game.getExile().getExileZone(exileId).contains(cardId)) { return false; } - SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class, source.getSourceId()); + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); if (watcher != null) { List spells = watcher.getSpellsCastThisTurn(source.getControllerId()); if (spells != null) { diff --git a/Mage.Sets/src/mage/cards/p/PleaForPower.java b/Mage.Sets/src/mage/cards/p/PleaForPower.java index f59ea46431..95fa283b16 100644 --- a/Mage.Sets/src/mage/cards/p/PleaForPower.java +++ b/Mage.Sets/src/mage/cards/p/PleaForPower.java @@ -72,7 +72,7 @@ class PleaForPowerEffect extends OneShotEffect { if (timeCount > knowledgeCount) { new AddExtraTurnControllerEffect().apply(game, source); } else { - controller.drawCards(3, game); + controller.drawCards(3, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/p/PollutedDead.java b/Mage.Sets/src/mage/cards/p/PollutedDead.java index fc70b9d4e1..9bc48539cf 100644 --- a/Mage.Sets/src/mage/cards/p/PollutedDead.java +++ b/Mage.Sets/src/mage/cards/p/PollutedDead.java @@ -4,7 +4,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class PollutedDead extends CardImpl { this.toughness = new MageInt(3); // When Polluted Dead dies, destroy target land. - Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect()); + Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect()); Target target = new TargetLandPermanent(); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/p/PollywogSymbiote.java b/Mage.Sets/src/mage/cards/p/PollywogSymbiote.java new file mode 100644 index 0000000000..7f8c95ff9f --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PollywogSymbiote.java @@ -0,0 +1,62 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.FilterSpell; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterCreatureSpell; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PollywogSymbiote extends CardImpl { + + private static final FilterCard filter = new FilterCreatureCard(); + private static final FilterSpell filter2 = new FilterCreatureSpell(); + private static final Predicate predicate = new AbilityPredicate(MutateAbility.class); + + static { + filter.add(predicate); + filter2.add(predicate); + } + + public PollywogSymbiote(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.FROG); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Each creature spell you cast costs {1} less to cast if it has mutate. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1) + .setText("each creature spell you cast costs {1} less to cast if it has mutate"))); + + // Whenever you cast a creature spell, if it has mutate, draw a card, then discard a card. + this.addAbility(new SpellCastControllerTriggeredAbility( + new DrawDiscardControllerEffect(1, 1), filter2, false, + "Whenever you cast a creature spell, if it has mutate, draw a card, then discard a card." + )); + } + + private PollywogSymbiote(final PollywogSymbiote card) { + super(card); + } + + @Override + public PollywogSymbiote copy() { + return new PollywogSymbiote(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Polymorph.java b/Mage.Sets/src/mage/cards/p/Polymorph.java index 9e3a7446cf..addab1b2bf 100644 --- a/Mage.Sets/src/mage/cards/p/Polymorph.java +++ b/Mage.Sets/src/mage/cards/p/Polymorph.java @@ -64,7 +64,7 @@ class PolymorphEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/p/PolymorphousRush.java b/Mage.Sets/src/mage/cards/p/PolymorphousRush.java index 7906f47180..db694d8431 100644 --- a/Mage.Sets/src/mage/cards/p/PolymorphousRush.java +++ b/Mage.Sets/src/mage/cards/p/PolymorphousRush.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.OneShotEffect; @@ -19,16 +17,15 @@ import mage.target.Target; import mage.target.common.TargetCreaturePermanent; import mage.util.functions.EmptyApplyToPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PolymorphousRush extends CardImpl { - - public PolymorphousRush(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); // Strive - Polymorphous Rush costs {1}{U} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{1}{U}")); diff --git a/Mage.Sets/src/mage/cards/p/PontiffOfBlight.java b/Mage.Sets/src/mage/cards/p/PontiffOfBlight.java index d823b2a277..708a6687f2 100644 --- a/Mage.Sets/src/mage/cards/p/PontiffOfBlight.java +++ b/Mage.Sets/src/mage/cards/p/PontiffOfBlight.java @@ -1,8 +1,5 @@ - - package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; @@ -10,29 +7,28 @@ import mage.abilities.keyword.ExtortAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; +import java.util.UUID; + /** - * * @author LevelX2 */ - - public final class PontiffOfBlight extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Other creatures you control"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + static { filter.add(AnotherPredicate.instance); filter.add(TargetController.YOU.getControllerPredicate()); } - public PontiffOfBlight (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{B}"); + public PontiffOfBlight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.CLERIC); @@ -43,11 +39,13 @@ public final class PontiffOfBlight extends CardImpl { this.addAbility(new ExtortAbility()); // Other creatures you control have extort. (If a creature has multiple instances of extort, each triggers separately.) - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new ExtortAbility(), Duration.WhileOnBattlefield, filter))); - + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + new ExtortAbility(), Duration.WhileOnBattlefield, filter + ).setText("Other creatures you control have extort. " + + "(If a creature has multiple instances of extort, each triggers separately.)"))); } - public PontiffOfBlight (final PontiffOfBlight card) { + private PontiffOfBlight(final PontiffOfBlight card) { super(card); } @@ -55,5 +53,4 @@ public final class PontiffOfBlight extends CardImpl { public PontiffOfBlight copy() { return new PontiffOfBlight(this); } - } diff --git a/Mage.Sets/src/mage/cards/p/Porcuparrot.java b/Mage.Sets/src/mage/cards/p/Porcuparrot.java new file mode 100644 index 0000000000..1f8bf3b988 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/Porcuparrot.java @@ -0,0 +1,50 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.SourceMutatedCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Porcuparrot extends CardImpl { + + public Porcuparrot(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Mutate {2}{R} + this.addAbility(new MutateAbility(this, "{2}{R}")); + + // {T}: This creature deals X damage to any target, where X is the number of times this creature has mutated. + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect( + SourceMutatedCount.instance, "this creature" + ), new TapSourceCost()); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + } + + private Porcuparrot(final Porcuparrot card) { + super(card); + } + + @Override + public Porcuparrot copy() { + return new Porcuparrot(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Portcullis.java b/Mage.Sets/src/mage/cards/p/Portcullis.java index 1cddb7e13e..e8478e4bbc 100644 --- a/Mage.Sets/src/mage/cards/p/Portcullis.java +++ b/Mage.Sets/src/mage/cards/p/Portcullis.java @@ -1,6 +1,5 @@ package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.TriggeredAbility; @@ -13,11 +12,7 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SetTargetPointer; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; @@ -28,8 +23,9 @@ import mage.players.Player; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author spjspj */ public final class Portcullis extends CardImpl { @@ -43,7 +39,7 @@ public final class Portcullis extends CardImpl { // Return that card to the battlefield under its owner's control when Portcullis leaves the battlefield. String rule = "Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature."; String rule2 = " Return that card to the battlefield under its owner's control when {this} leaves the battlefield."; - TriggeredAbility ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new PortcullisExileEffect(), + TriggeredAbility ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new PortcullisExileEffect(), filter, false, SetTargetPointer.PERMANENT, rule); MoreThanXCreaturesOnBFCondition condition = new MoreThanXCreaturesOnBFCondition(2); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, condition, rule + rule2)); @@ -104,7 +100,7 @@ class PortcullisExileEffect extends OneShotEffect { UUID exileZoneId = CardUtil.getExileZoneId(game, creatureToExile.getId(), creatureToExile.getZoneChangeCounter(game)); controller.moveCardsToExile(creatureToExile, source, game, true, exileZoneId, portcullis.getName()); FixedTarget fixedTarget = new FixedTarget(portcullis, game); - Effect returnEffect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect returnEffect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); returnEffect.setTargetPointer(new FixedTarget(creatureToExile.getId(), game.getState().getZoneChangeCounter(creatureToExile.getId()))); DelayedTriggeredAbility delayedAbility = new PortcullisReturnToBattlefieldTriggeredAbility(fixedTarget, returnEffect); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/p/Portent.java b/Mage.Sets/src/mage/cards/p/Portent.java index 6a84576250..b2405c28a4 100644 --- a/Mage.Sets/src/mage/cards/p/Portent.java +++ b/Mage.Sets/src/mage/cards/p/Portent.java @@ -47,7 +47,7 @@ class PortentEffect extends OneShotEffect { public PortentEffect() { super(Outcome.DrawCard); - this.staticText = "look at the top three cards of target player's library, then put them back in any order. You may have that player shuffle their library."; + this.staticText = "look at the top three cards of target player's library, then put them back in any order. You may have that player shuffle their library"; } public PortentEffect(final PortentEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PossibilityStorm.java b/Mage.Sets/src/mage/cards/p/PossibilityStorm.java index 998707e955..134108fb51 100644 --- a/Mage.Sets/src/mage/cards/p/PossibilityStorm.java +++ b/Mage.Sets/src/mage/cards/p/PossibilityStorm.java @@ -1,8 +1,6 @@ - package mage.cards.p; -import java.util.EnumSet; -import java.util.Set; +import java.util.ArrayList; import java.util.UUID; import mage.MageObject; import mage.MageObjectReference; @@ -147,7 +145,7 @@ class PossibilityStormEffect extends OneShotEffect { return false; } - private boolean sharesType(Card card, Set cardTypes) { + private boolean sharesType(Card card, ArrayList cardTypes) { for (CardType type : card.getCardType()) { if (cardTypes.contains(type)) { return true; diff --git a/Mage.Sets/src/mage/cards/p/Pounce.java b/Mage.Sets/src/mage/cards/p/Pounce.java index 8ec34d321e..012981c8ba 100644 --- a/Mage.Sets/src/mage/cards/p/Pounce.java +++ b/Mage.Sets/src/mage/cards/p/Pounce.java @@ -1,37 +1,27 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.effects.common.FightTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; -import mage.target.Target; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class Pounce extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public Pounce(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Target creature you control fights target creature you don't control. this.getSpellAbility().addEffect(new FightTargetsEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - Target target = new TargetCreaturePermanent(filter); - this.getSpellAbility().addTarget(target); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } public Pounce(final Pounce card) { diff --git a/Mage.Sets/src/mage/cards/p/PouncingShoreshark.java b/Mage.Sets/src/mage/cards/p/PouncingShoreshark.java new file mode 100644 index 0000000000..07415e4f88 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PouncingShoreshark.java @@ -0,0 +1,50 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PouncingShoreshark extends CardImpl { + + public PouncingShoreshark(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.SHARK); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Mutate {3}{U} + this.addAbility(new MutateAbility(this, "{3}{U}")); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When this creature mutates, you may return target creature an opponent controls to its owner's hand. + Ability ability = new MutatesSourceTriggeredAbility(new ReturnToHandTargetEffect(), true); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private PouncingShoreshark(final PouncingShoreshark card) { + super(card); + } + + @Override + public PouncingShoreshark copy() { + return new PouncingShoreshark(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PowerArtifact.java b/Mage.Sets/src/mage/cards/p/PowerArtifact.java index b0063c5978..dbf2a960ab 100644 --- a/Mage.Sets/src/mage/cards/p/PowerArtifact.java +++ b/Mage.Sets/src/mage/cards/p/PowerArtifact.java @@ -1,11 +1,5 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.cards.p; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; @@ -15,13 +9,7 @@ import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -29,14 +17,15 @@ import mage.target.TargetPermanent; import mage.target.common.TargetArtifactPermanent; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author nick.myers */ public final class PowerArtifact extends CardImpl { public PowerArtifact(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}{U}"); this.subtype.add(SubType.AURA); // Enchant artifact @@ -51,7 +40,7 @@ public final class PowerArtifact extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PowerArtifactCostModificationEffect())); } - public PowerArtifact(final PowerArtifact card) { + private PowerArtifact(final PowerArtifact card) { super(card); } @@ -65,11 +54,12 @@ class PowerArtifactCostModificationEffect extends CostModificationEffectImpl { PowerArtifactCostModificationEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "The activation cost of target artifact is reduced by {2}. If this would reduce target artifact's activation cost below {1}, target artifact's activation cost becomes {1}. Power artifact has no effect on artifacts that have no activation cost or whose activation cost is {0}."; + staticText = "Enchanted artifact's activated abilities cost {2} less to activate. " + + "This effect can't reduce the amount of mana an ability costs to activate to less than one mana."; } - PowerArtifactCostModificationEffect(PowerArtifactCostModificationEffect effect) { + private PowerArtifactCostModificationEffect(PowerArtifactCostModificationEffect effect) { super(effect); } @@ -92,10 +82,12 @@ class PowerArtifactCostModificationEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - Permanent artifact = game.getPermanent(abilityToModify.getSourceId()); - if (artifact != null && artifact.getAttachments().contains(source.getSourceId())) { + Permanent artifact = game.getPermanentOrLKIBattlefield(abilityToModify.getSourceId()); + if (artifact != null + && artifact.getAttachments().contains(source.getSourceId())) { if (abilityToModify.getAbilityType() == AbilityType.ACTIVATED - || (abilityToModify.getAbilityType() == AbilityType.MANA && (abilityToModify instanceof ActivatedAbility))) { + || (abilityToModify.getAbilityType() == AbilityType.MANA + && (abilityToModify instanceof ActivatedAbility))) { return true; } } diff --git a/Mage.Sets/src/mage/cards/p/PowerLeak.java b/Mage.Sets/src/mage/cards/p/PowerLeak.java index 309ec4ec79..6d313dcf71 100644 --- a/Mage.Sets/src/mage/cards/p/PowerLeak.java +++ b/Mage.Sets/src/mage/cards/p/PowerLeak.java @@ -91,7 +91,7 @@ class PowerLeakEffect extends OneShotEffect { PreventDamageByTargetEffect effect = new PreventDamageByTargetEffect(Duration.OneUse, xValue, false); if (xValue != 0 && cost.isPaid()) { - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } player.damage(2, source.getSourceId(), game); diff --git a/Mage.Sets/src/mage/cards/p/PowerTaint.java b/Mage.Sets/src/mage/cards/p/PowerTaint.java index 29eed92097..94806da421 100644 --- a/Mage.Sets/src/mage/cards/p/PowerTaint.java +++ b/Mage.Sets/src/mage/cards/p/PowerTaint.java @@ -1,61 +1,61 @@ -package mage.cards.p; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetEnchantmentPermanent; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.common.AttachEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.DoUnlessTargetPlayerOrTargetsControllerPaysEffect; -import mage.abilities.effects.common.LoseLifeTargetEffect; -import mage.abilities.keyword.CyclingAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.TargetController; -import mage.constants.Zone; - -/** - * - * @author jeffwadsworth - */ -public final class PowerTaint extends CardImpl { - - public PowerTaint(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); - - this.subtype.add(SubType.AURA); - - // Enchant enchantment - TargetPermanent auraTarget = new TargetEnchantmentPermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // At the beginning of the upkeep of enchanted enchantment's controller, that player loses 2 life unless they pay {2}. - Effect effect = new DoUnlessTargetPlayerOrTargetsControllerPaysEffect(new LoseLifeTargetEffect(2), - new ManaCostsImpl("{2}"), "that player loses 2 life unless they pay {2}"); - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, - TargetController.CONTROLLER_ATTACHED_TO, false, true, - "At the beginning of the upkeep of enchanted enchantment's controller, ")); - - // Cycling {2} - this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); - - } - - public PowerTaint(final PowerTaint card) { - super(card); - } - - @Override - public PowerTaint copy() { - return new PowerTaint(this); - } -} +package mage.cards.p; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetEnchantmentPermanent; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DoUnlessTargetPlayerOrTargetsControllerPaysEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; + +/** + * + * @author jeffwadsworth + */ +public final class PowerTaint extends CardImpl { + + public PowerTaint(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant enchantment + TargetPermanent auraTarget = new TargetEnchantmentPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of the upkeep of enchanted enchantment's controller, that player loses 2 life unless they pay {2}. + Effect effect = new DoUnlessTargetPlayerOrTargetsControllerPaysEffect(new LoseLifeTargetEffect(2), + new ManaCostsImpl("{2}"), "that player loses 2 life unless they pay {2}"); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, + TargetController.CONTROLLER_ATTACHED_TO, false, true, + "At the beginning of the upkeep of enchanted enchantment's controller, ")); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + + } + + public PowerTaint(final PowerTaint card) { + super(card); + } + + @Override + public PowerTaint copy() { + return new PowerTaint(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Powerleech.java b/Mage.Sets/src/mage/cards/p/Powerleech.java index a1bdb8c836..eda07f710a 100644 --- a/Mage.Sets/src/mage/cards/p/Powerleech.java +++ b/Mage.Sets/src/mage/cards/p/Powerleech.java @@ -3,8 +3,6 @@ package mage.cards.p; import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +23,7 @@ public final class Powerleech extends CardImpl { public Powerleech(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}{G}"); - // Whenever an artifact an opponent controls becomes tapped or an opponent activates an artifact's ability without {tap} in its activation cost, you gain 1 life. + // Whenever an artifact an opponent controls becomes tapped or an opponent activates an artifact's ability without {T} in its activation cost, you gain 1 life. this.addAbility(new PowerleechTriggeredAbility()); } @@ -75,17 +73,8 @@ class PowerleechTriggeredAbility extends TriggeredAbilityImpl { if (stackAbility == null) { return false; } - boolean triggerable = true; - for (Cost cost : stackAbility.getCosts()) { - if (cost instanceof TapSourceCost) { - triggerable = false; - break; - } - } - if (!triggerable) { - return false; - } - return player.hasOpponent(permanent.getControllerId(), game); + return !stackAbility.hasTapCost() + && player.hasOpponent(permanent.getControllerId(), game); } if (event.getType() == GameEvent.EventType.TAPPED) { Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); diff --git a/Mage.Sets/src/mage/cards/p/PowerstoneShard.java b/Mage.Sets/src/mage/cards/p/PowerstoneShard.java index 7862217f16..722f1d88bd 100644 --- a/Mage.Sets/src/mage/cards/p/PowerstoneShard.java +++ b/Mage.Sets/src/mage/cards/p/PowerstoneShard.java @@ -1,9 +1,9 @@ - package mage.cards.p; -import java.util.UUID; import mage.Mana; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.hint.ValueHint; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -11,8 +11,9 @@ import mage.constants.CardType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.NamePredicate; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class PowerstoneShard extends CardImpl { @@ -28,7 +29,9 @@ public final class PowerstoneShard extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {T}: Add {C} for each artifact you control named Powerstone Shard. - this.addAbility(new DynamicManaAbility(Mana.ColorlessMana(1), new PermanentsOnBattlefieldCount(filter))); + DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + this.addAbility(new DynamicManaAbility(Mana.ColorlessMana(1), xValue) + .addHint(new ValueHint("Artifact you control named Powerstone Shard", xValue))); } public PowerstoneShard(final PowerstoneShard card) { diff --git a/Mage.Sets/src/mage/cards/p/PraetorsGrasp.java b/Mage.Sets/src/mage/cards/p/PraetorsGrasp.java index 9a9bf1cb69..8539ab821a 100644 --- a/Mage.Sets/src/mage/cards/p/PraetorsGrasp.java +++ b/Mage.Sets/src/mage/cards/p/PraetorsGrasp.java @@ -1,4 +1,3 @@ - package mage.cards.p; import mage.MageObject; @@ -26,7 +25,8 @@ public final class PraetorsGrasp extends CardImpl { public PraetorsGrasp(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); - // Search target opponent's library for a card and exile it face down. Then that player shuffles their library. You may look at and play that card for as long as it remains exiled. + // Search target opponent's library for a card and exile it face down. Then that player + //shuffles their library. You may look at and play that card for as long as it remains exiled. this.getSpellAbility().addEffect(new PraetorsGraspEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); } @@ -45,7 +45,9 @@ class PraetorsGraspEffect extends OneShotEffect { public PraetorsGraspEffect() { super(Outcome.PlayForFree); - staticText = "Search target opponent's library for a card and exile it face down. Then that player shuffles their library. You may look at and play that card for as long as it remains exiled"; + staticText = "Search target opponent's library for a card and exile it " + + "face down. Then that player shuffles their library. You may " + + "look at and play that card for as long as it remains exiled"; } public PraetorsGraspEffect(final PraetorsGraspEffect effect) { @@ -62,15 +64,21 @@ class PraetorsGraspEffect extends OneShotEffect { Player opponent = game.getPlayer(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); - if (controller != null && opponent != null && sourceObject != null) { + if (controller != null + && opponent != null + && sourceObject != null) { TargetCardInLibrary target = new TargetCardInLibrary(); if (controller.searchLibrary(target, source, game, opponent.getId())) { UUID targetId = target.getFirstTarget(); Card card = opponent.getLibrary().getCard(targetId, game); - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - if (card != null && exileId != null) { - game.informPlayers(controller.getLogName() + " moves the searched card face down to exile"); - card.moveToExile(exileId, sourceObject.getIdName(), source.getSourceId(), game); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), + source.getSourceObjectZoneChangeCounter()); + if (card != null + && exileId != null) { + game.informPlayers(controller.getLogName() + " moves the searched " + + "card face down to exile"); + card.moveToExile(exileId, sourceObject.getIdName(), + source.getSourceId(), game); card.setFaceDown(true, game); game.addEffect(new PraetorsGraspPlayEffect(card.getId()), source); game.addEffect(new PraetorsGraspRevealEffect(card.getId()), source); @@ -110,12 +118,16 @@ class PraetorsGraspPlayEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (objectId.equals(cardId) && affectedControllerId.equals(source.getControllerId())) { + if (objectId.equals(cardId) + && affectedControllerId.equals(source.getControllerId())) { Player controller = game.getPlayer(source.getControllerId()); - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - if (exileId != null && controller != null) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), + source.getSourceObjectZoneChangeCounter()); + if (exileId != null + && controller != null) { ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone != null && exileZone.contains(cardId)) { + if (exileZone != null + && exileZone.contains(cardId)) { return true; } } @@ -152,15 +164,21 @@ class PraetorsGraspRevealEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (objectId.equals(cardId) && affectedControllerId.equals(source.getControllerId())) { + if (objectId.equals(cardId) + && affectedControllerId.equals(source.getControllerId())) { MageObject sourceObject = source.getSourceObject(game); - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - if (exileId != null && sourceObject != null) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), + source.getSourceObjectZoneChangeCounter()); + if (exileId != null + && sourceObject != null) { ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone != null && exileZone.contains(cardId)) { + if (exileZone != null + && exileZone.contains(cardId)) { Player controller = game.getPlayer(source.getControllerId()); Card card = game.getCard(cardId); - if (controller != null && card != null && game.getState().getZone(cardId) == Zone.EXILED) { + if (controller != null + && card != null + && game.getState().getZone(cardId) == Zone.EXILED) { return true; } } else { diff --git a/Mage.Sets/src/mage/cards/p/Preacher.java b/Mage.Sets/src/mage/cards/p/Preacher.java index cda4db79a2..5b9769746e 100644 --- a/Mage.Sets/src/mage/cards/p/Preacher.java +++ b/Mage.Sets/src/mage/cards/p/Preacher.java @@ -74,22 +74,17 @@ class PreacherEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + Permanent targetPermanent = game.getPermanent(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); - if (controller != null - && sourcePermanent != null) { - Permanent targetPermanent = game.getPermanent(source.getFirstTarget()); - if (targetPermanent != null) { - SourceTappedCondition sourceTappedCondition = SourceTappedCondition.instance; - SourceHasRemainedInSameZoneCondition conditionSourceSameZone = new SourceHasRemainedInSameZoneCondition(sourcePermanent.getId()); - SourceHasRemainedInSameZoneCondition conditionTargetSameZone = new SourceHasRemainedInSameZoneCondition(targetPermanent.getId()); - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), - new CompoundCondition(sourceTappedCondition, new CompoundCondition(conditionSourceSameZone, conditionTargetSameZone)), - "Gain control of target creature of an opponent's choice that they control for as long as {this} remains tapped"); - effect.setTargetPointer(new FixedTarget(targetPermanent.getId())); - game.addEffect(effect, source); - return true; - } + if (controller != null && sourcePermanent != null && targetPermanent != null) { + SourceTappedCondition sourceTappedCondition = SourceTappedCondition.instance; + ConditionalContinuousEffect effect = new ConditionalContinuousEffect( + new GainControlTargetEffect(Duration.Custom), + sourceTappedCondition, + "Gain control of target creature of an opponent's choice that they control for as long as {this} remains tapped"); + effect.setTargetPointer(new FixedTarget(targetPermanent.getId())); + game.addEffect(effect, source); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/p/PrecognitionField.java b/Mage.Sets/src/mage/cards/p/PrecognitionField.java index 80a9b352d4..6a4bd15000 100644 --- a/Mage.Sets/src/mage/cards/p/PrecognitionField.java +++ b/Mage.Sets/src/mage/cards/p/PrecognitionField.java @@ -1,17 +1,20 @@ package mage.cards.p; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; import mage.game.Game; import mage.players.Player; @@ -22,6 +25,15 @@ import java.util.UUID; */ public final class PrecognitionField extends CardImpl { + private static final FilterCard filter = new FilterCard("cast instant or sorcery spells"); + + static { + filter.add(Predicates.or( + CardType.INSTANT.getPredicate(), + CardType.SORCERY.getPredicate() + )); + } + public PrecognitionField(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); @@ -29,7 +41,7 @@ public final class PrecognitionField extends CardImpl { this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); // You may cast the top card of your library if it's an instant or sorcery card. - this.addAbility(new SimpleStaticAbility(new PrecognitionFieldTopCardCastEffect())); + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); // {3}: Exile the top card of your library. this.addAbility(new SimpleActivatedAbility(new PrecognitionFieldExileEffect(), new GenericManaCost(3))); @@ -45,50 +57,6 @@ public final class PrecognitionField extends CardImpl { } } -class PrecognitionFieldTopCardCastEffect extends AsThoughEffectImpl { - - public PrecognitionFieldTopCardCastEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "You may cast the top card of your library if it's an instant or sorcery card."; - } - - public PrecognitionFieldTopCardCastEffect(final PrecognitionFieldTopCardCastEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public PrecognitionFieldTopCardCastEffect copy() { - return new PrecognitionFieldTopCardCastEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(objectId); - if (card != null) { - Player controller = game.getPlayer(affectedControllerId); - if (controller != null) { - Card topCard = controller.getLibrary().getFromTop(game); - MageObject precognitionField = game.getObject(source.getSourceId()); - if (precognitionField != null - && topCard != null) { - return topCard == card - && (topCard.isInstant() || topCard.isSorcery()) - && topCard.getSpellAbility() != null - && topCard.getSpellAbility().spellCanBeActivatedRegularlyNow(controller.getId(), game); - } - } - } - } - return false; - } -} - class PrecognitionFieldExileEffect extends OneShotEffect { public PrecognitionFieldExileEffect() { diff --git a/Mage.Sets/src/mage/cards/p/PrecognitivePerception.java b/Mage.Sets/src/mage/cards/p/PrecognitivePerception.java index 32f2d03551..d6c07ab8a8 100644 --- a/Mage.Sets/src/mage/cards/p/PrecognitivePerception.java +++ b/Mage.Sets/src/mage/cards/p/PrecognitivePerception.java @@ -62,7 +62,7 @@ class PrecognitivePerceptionEffect extends OneShotEffect { if (AddendumCondition.instance.apply(game, source)) { controller.scry(3, source, game); } - controller.drawCards(3, game); + controller.drawCards(3, source.getSourceId(), game); return true; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PredatorOoze.java b/Mage.Sets/src/mage/cards/p/PredatorOoze.java index ff472f3e56..4f5bd58b25 100644 --- a/Mage.Sets/src/mage/cards/p/PredatorOoze.java +++ b/Mage.Sets/src/mage/cards/p/PredatorOoze.java @@ -30,7 +30,7 @@ public final class PredatorOoze extends CardImpl { this.addAbility(IndestructibleAbility.getInstance()); // Whenever Predator Ooze attacks, put a +1/+1 counter on it. - this.addAbility(new AttacksTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false)); + this.addAbility(new AttacksTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()).setText("put a +1/+1 counter on it"), false)); // Whenever a creature dealt damage by Predator Ooze this turn dies, put a +1/+1 counter on Predator Ooze. this.addAbility(new DealtDamageAndDiedTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()))); diff --git a/Mage.Sets/src/mage/cards/p/PredatoryImpetus.java b/Mage.Sets/src/mage/cards/p/PredatoryImpetus.java new file mode 100644 index 0000000000..76dcd0113f --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PredatoryImpetus.java @@ -0,0 +1,104 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.effects.RequirementEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PredatoryImpetus extends CardImpl { + + public PredatoryImpetus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +3/+3, must be blocked if able, and is goaded. + this.addAbility(new GoadAttachedAbility( + new BoostEnchantedEffect(3, 3) + .setText("Enchanted creature gets +3/+3"), + new PredatoryImpetusEffect() + )); + } + + private PredatoryImpetus(final PredatoryImpetus card) { + super(card); + } + + @Override + public PredatoryImpetus copy() { + return new PredatoryImpetus(this); + } +} + +class PredatoryImpetusEffect extends RequirementEffect { + + PredatoryImpetusEffect() { + super(Duration.WhileOnBattlefield); + staticText = ", must be blocked if able"; + } + + private PredatoryImpetusEffect(final PredatoryImpetusEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + Permanent attachment = game.getPermanent(source.getSourceId()); + if (attachment == null || attachment.getAttachedTo() == null) { + return false; + } + Permanent attachedCreature = game.getPermanent(attachment.getAttachedTo()); + return attachedCreature != null && attachedCreature.isAttacking() + && permanent.canBlock(attachment.getAttachedTo(), game); + } + + @Override + public boolean mustAttack(Game game) { + return false; + } + + @Override + public boolean mustBlock(Game game) { + return false; + } + + @Override + public UUID mustBlockAttackerIfElseUnblocked(Ability source, Game game) { + Permanent attachment = game.getPermanent(source.getSourceId()); + return attachment == null ? null : attachment.getAttachedTo(); + } + + @Override + public int getMinNumberOfBlockers() { + return 1; + } + + @Override + public PredatoryImpetusEffect copy() { + return new PredatoryImpetusEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PredatoryWurm.java b/Mage.Sets/src/mage/cards/p/PredatoryWurm.java new file mode 100644 index 0000000000..9808b09b7f --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PredatoryWurm.java @@ -0,0 +1,53 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterPlaneswalkerPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PredatoryWurm extends CardImpl { + + private static final FilterPermanent filter = new FilterPlaneswalkerPermanent(SubType.GARRUK); + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + + public PredatoryWurm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.WURM); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), + condition, "{this} gets +2/+2 as long as you control a Garruk planeswalker" + ))); + } + + private PredatoryWurm(final PredatoryWurm card) { + super(card); + } + + @Override + public PredatoryWurm copy() { + return new PredatoryWurm(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Predict.java b/Mage.Sets/src/mage/cards/p/Predict.java index c2ae143a7a..d53aa652fd 100644 --- a/Mage.Sets/src/mage/cards/p/Predict.java +++ b/Mage.Sets/src/mage/cards/p/Predict.java @@ -8,7 +8,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; @@ -30,7 +29,7 @@ public final class Predict extends CardImpl { this.getSpellAbility().addTarget(new TargetPlayer()); } - public Predict(final Predict card) { + private Predict(final Predict card) { super(card); } @@ -42,13 +41,13 @@ public final class Predict extends CardImpl { class PredictEffect extends OneShotEffect { - public PredictEffect() { + PredictEffect() { super(Outcome.DrawCard); - this.staticText = ", then target player puts the top card of their library into their graveyard. " - + "If that card has the chosen name, you draw two cards. Otherwise, you draw a card."; + this.staticText = ", then target player mills a card. If a card with the chosen name was milled this way, " + + "you draw two cards. Otherwise, you draw a card."; } - public PredictEffect(final PredictEffect effect) { + private PredictEffect(final PredictEffect effect) { super(effect); } @@ -62,19 +61,17 @@ class PredictEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(source.getFirstTarget()); String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - if (controller != null && targetPlayer != null && cardName != null && !cardName.isEmpty()) { - int amount = 1; - Card card = targetPlayer.getLibrary().getFromTop(game); - if (card != null) { - controller.moveCards(card, Zone.GRAVEYARD, source, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { - amount = 2; - } - } - controller.drawCards(amount, game); - return true; + if (controller == null || targetPlayer == null || cardName == null || cardName.isEmpty()) { + return false; } - return false; + int toDraw = 1; + for (Card card : targetPlayer.millCards(1, source, game).getCards(game)) { + if (CardUtil.haveSameNames(card, cardName, game)) { + toDraw = 2; + break; + } + } + controller.drawCards(toDraw, source.getSourceId(), game); + return true; } - } diff --git a/Mage.Sets/src/mage/cards/p/Preordain.java b/Mage.Sets/src/mage/cards/p/Preordain.java index 4eae38cd40..39ce81a2c4 100644 --- a/Mage.Sets/src/mage/cards/p/Preordain.java +++ b/Mage.Sets/src/mage/cards/p/Preordain.java @@ -1,14 +1,14 @@ package mage.cards.p; -import java.util.UUID; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public final class Preordain extends CardImpl { @@ -18,11 +18,11 @@ public final class Preordain extends CardImpl { // Scry 2, then draw a card. (To scry 2, look at the top two cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.) this.getSpellAbility().addEffect( - new ScryEffect(2).setText("Scry 2, ") + new ScryEffect(2, false) ); this.getSpellAbility().addEffect( new DrawCardSourceControllerEffect(1) - .setText("then draw a card. (To scry 2, " + .setText(", then draw a card. (To scry 2, " + "look at the top two cards of your library, " + "then put any number of them on the " + "bottom of your library and the rest on " diff --git a/Mage.Sets/src/mage/cards/p/PrepareFight.java b/Mage.Sets/src/mage/cards/p/PrepareFight.java index fa7ddd4902..2901114f77 100644 --- a/Mage.Sets/src/mage/cards/p/PrepareFight.java +++ b/Mage.Sets/src/mage/cards/p/PrepareFight.java @@ -1,33 +1,28 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.*; +import mage.abilities.effects.common.FightTargetsEffect; +import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.AftermathAbility; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardSetInfo; import mage.cards.SplitCard; -import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; -import mage.target.Target; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SpellAbilityType; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author stravant */ public final class PrepareFight extends SplitCard { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public PrepareFight(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, new CardType[]{CardType.SORCERY}, "{1}{W}", "{3}{G}", SpellAbilityType.SPLIT_AFTERMATH); @@ -35,13 +30,13 @@ public final class PrepareFight extends SplitCard { // Untap target creature. It gets +2/+2 and gains lifelink until end of turn. getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); Effect effect = new UntapTargetEffect(); - effect.setText("Untap target creature."); + effect.setText("Untap target creature"); getLeftHalfCard().getSpellAbility().addEffect(effect); effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); effect.setText("It gets +2/+2"); getLeftHalfCard().getSpellAbility().addEffect(effect); effect = new GainAbilityTargetEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn); - effect.setText("and gains lifelink until end of turn."); + effect.setText("and gains lifelink until end of turn"); getLeftHalfCard().getSpellAbility().addEffect(effect); // to @@ -50,11 +45,10 @@ public final class PrepareFight extends SplitCard { getRightHalfCard().addAbility(new AftermathAbility().setRuleAtTheTop(true)); getRightHalfCard().getSpellAbility().addEffect(new FightTargetsEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - Target target = new TargetCreaturePermanent(filter); - getRightHalfCard().getSpellAbility().addTarget(target); + getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public PrepareFight(final PrepareFight card) { + private PrepareFight(final PrepareFight card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/p/PreyUpon.java b/Mage.Sets/src/mage/cards/p/PreyUpon.java index 85bf7926cf..ebe83d2e98 100644 --- a/Mage.Sets/src/mage/cards/p/PreyUpon.java +++ b/Mage.Sets/src/mage/cards/p/PreyUpon.java @@ -1,41 +1,30 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.effects.common.FightTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; -import mage.target.Target; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class PreyUpon extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public PreyUpon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); // Target creature you control fights target creature you don't control. this.getSpellAbility().addEffect(new FightTargetsEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - Target target = new TargetCreaturePermanent(filter); - this.getSpellAbility().addTarget(target); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public PreyUpon(final PreyUpon card) { + private PreyUpon(final PreyUpon card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java b/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java index 79839d09aa..2d9fb9c9b9 100644 --- a/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java +++ b/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java @@ -2,11 +2,9 @@ package mage.cards.p; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.FilterOpponent; import mage.filter.FilterPermanent; @@ -34,12 +32,12 @@ public final class PriceOfBetrayal extends CardImpl { )); } - private static final FilterPermanentOrPlayer filter2 = new FilterPermanentOrPlayer("artifact, creature, planeswalker, or opponent", filter, new FilterOpponent()); + private static final FilterPermanentOrPlayer filter2 = new FilterPermanentOrPlayer("artifact, creature, planeswalker or opponent", filter, new FilterOpponent()); public PriceOfBetrayal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}"); - // Remove up to five counters from target artifact, creature, planeswalker, or opponent. + // Remove up to five counters from target artifact, creature, planeswalker or opponent. this.getSpellAbility().addEffect(new PriceOfBetrayalEffect()); this.getSpellAbility().addTarget(new TargetPermanentOrPlayer(1, 1, filter2, false)); } @@ -57,8 +55,8 @@ public final class PriceOfBetrayal extends CardImpl { class PriceOfBetrayalEffect extends OneShotEffect { PriceOfBetrayalEffect() { - super(Outcome.Benefit); - staticText = "Remove up to five counters from target artifact, creature, planeswalker, or opponent."; + super(Outcome.AIDontUseIt); + staticText = "Remove up to five counters from target artifact, creature, planeswalker or opponent."; } private PriceOfBetrayalEffect(final PriceOfBetrayalEffect effect) { @@ -76,6 +74,8 @@ class PriceOfBetrayalEffect extends OneShotEffect { if (controller == null) { return false; } + + // from permanent Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { int toRemove = 5; @@ -83,7 +83,7 @@ class PriceOfBetrayalEffect extends OneShotEffect { String[] counterNames = permanent.getCounters(game).keySet().toArray(new String[0]); for (String counterName : counterNames) { if (controller.chooseUse(Outcome.Neutral, "Do you want to remove " + counterName + " counters?", source, game)) { - if (permanent.getCounters(game).get(counterName).getCount() == 1 || toRemove == 1) { + if (permanent.getCounters(game).get(counterName).getCount() == 1 || (toRemove - removed == 1)) { permanent.removeCounters(counterName, 1, game); removed++; } else { @@ -98,9 +98,10 @@ class PriceOfBetrayalEffect extends OneShotEffect { break; } } - game.addEffect(new BoostSourceEffect(removed, 0, Duration.EndOfTurn), source); return true; } + + // from player Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { int toRemove = 5; @@ -108,7 +109,7 @@ class PriceOfBetrayalEffect extends OneShotEffect { String[] counterNames = player.getCounters().keySet().toArray(new String[0]); for (String counterName : counterNames) { if (controller.chooseUse(Outcome.Neutral, "Do you want to remove " + counterName + " counters?", source, game)) { - if (player.getCounters().get(counterName).getCount() == 1 || toRemove == 1) { + if (player.getCounters().get(counterName).getCount() == 1 || (toRemove - removed == 1)) { player.removeCounters(counterName, 1, source, game); removed++; } else { @@ -123,7 +124,6 @@ class PriceOfBetrayalEffect extends OneShotEffect { break; } } - game.addEffect(new BoostSourceEffect(removed, 0, Duration.EndOfTurn), source); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/p/PriceOfFame.java b/Mage.Sets/src/mage/cards/p/PriceOfFame.java index 4a8dd77d97..be37a6e773 100644 --- a/Mage.Sets/src/mage/cards/p/PriceOfFame.java +++ b/Mage.Sets/src/mage/cards/p/PriceOfFame.java @@ -36,7 +36,7 @@ public final class PriceOfFame extends CardImpl { // This spell costs {2} less to cast if it targets a legendary creature. this.addAbility(new SimpleStaticAbility( - Zone.STACK, new SpellCostReductionSourceEffect(2, condition) + Zone.ALL, new SpellCostReductionSourceEffect(2, condition).setCanWorksOnStackOnly(true) ).setRuleAtTheTop(true)); // Destroy target creature. diff --git a/Mage.Sets/src/mage/cards/p/PriceOfGlory.java b/Mage.Sets/src/mage/cards/p/PriceOfGlory.java index 1fbc55a591..1e4c849cca 100644 --- a/Mage.Sets/src/mage/cards/p/PriceOfGlory.java +++ b/Mage.Sets/src/mage/cards/p/PriceOfGlory.java @@ -1,5 +1,6 @@ package mage.cards.p; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; @@ -15,8 +16,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; -import java.util.UUID; - /** * @author cbt33, Loki (Heartbeat of Spring) */ @@ -65,7 +64,7 @@ class PriceOfGloryAbility extends TriggeredAbilityImpl { if (permanent.isLand() && game.getState().getPlayersInRange(controllerId, game).contains(permanent.getControllerId()) && !permanent.isControlledBy(game.getActivePlayerId())) { // intervening if clause - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); return true; } return false; @@ -97,7 +96,7 @@ class PriceOfGloryEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Permanent land = game.getPermanentOrLKIBattlefield(this.targetPointer.getFirst(game, source)); + Permanent land = game.getPermanent(getTargetPointer().getFirst(game, source)); if (land != null && !land.isControlledBy(game.getActivePlayerId())) { // intervening if clause has to be checked again land.destroy(source.getSourceId(), game, false); } diff --git a/Mage.Sets/src/mage/cards/p/PricklyMarmoset.java b/Mage.Sets/src/mage/cards/p/PricklyMarmoset.java new file mode 100644 index 0000000000..07e349d7dc --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PricklyMarmoset.java @@ -0,0 +1,44 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PricklyMarmoset extends CardImpl { + + public PricklyMarmoset(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.MONKEY); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Whenever you cycle a card, Prickly Marmoset gets +2/+0 until end of turn. + this.addAbility(new CycleControllerTriggeredAbility( + new BoostSourceEffect(2, 0, Duration.EndOfTurn) + )); + } + + private PricklyMarmoset(final PricklyMarmoset card) { + super(card); + } + + @Override + public PricklyMarmoset copy() { + return new PricklyMarmoset(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Pridemalkin.java b/Mage.Sets/src/mage/cards/p/Pridemalkin.java new file mode 100644 index 0000000000..fab9b798b1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/Pridemalkin.java @@ -0,0 +1,61 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.CounterPredicate; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Pridemalkin extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); + + static { + filter.add(new CounterPredicate(CounterType.P1P1)); + } + + public Pridemalkin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Pridemalkin enters the battlefield, put a +1/+1 counter on target creature you control. + Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + + // Each creature you control with a +1/+1 counter on it has trample. + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter + ))); + } + + private Pridemalkin(final Pridemalkin card) { + super(card); + } + + @Override + public Pridemalkin copy() { + return new Pridemalkin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java b/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java index 1a157bbc6e..c3d267f29e 100644 --- a/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java +++ b/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java @@ -6,12 +6,15 @@ import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.HighestCMCOfPermanentValue; import mage.abilities.dynamicvalue.common.SacrificeCostConvertedMana; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetControlledPermanent; @@ -28,10 +31,14 @@ public final class PriestOfYawgmoth extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - // {T}, Sacrifice an artifact: Add an amount of {B} equal to the sacrificed artifact's converted mana cost. + // {T}, Sacrifice an artifact: Add an amount of {B} equal to the sacrificed artifact's converted mana cost. Ability ability = new DynamicManaAbility(Mana.BlackMana(1), new SacrificeCostConvertedMana("artifact"), - "add an amount of {B} equal to the sacrificed artifact's converted mana cost"); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledArtifactPermanent()))); + new TapSourceCost(), + "add an amount of {B} equal to the sacrificed artifact's converted mana cost", + false, + new HighestCMCOfPermanentValue(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, true) + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PrimalDruid.java b/Mage.Sets/src/mage/cards/p/PrimalDruid.java index 2a8de813ed..fcafcea74c 100644 --- a/Mage.Sets/src/mage/cards/p/PrimalDruid.java +++ b/Mage.Sets/src/mage/cards/p/PrimalDruid.java @@ -2,7 +2,7 @@ package mage.cards.p; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.cards.CardImpl; @@ -30,7 +30,7 @@ public final class PrimalDruid extends CardImpl { // When Primal Druid dies, you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. Effect effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true); effect.setText("you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library"); - this.addAbility(new DiesTriggeredAbility(effect, true)); + this.addAbility(new DiesSourceTriggeredAbility(effect, true)); } diff --git a/Mage.Sets/src/mage/cards/p/PrimalEmpathy.java b/Mage.Sets/src/mage/cards/p/PrimalEmpathy.java new file mode 100644 index 0000000000..a295e4793b --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PrimalEmpathy.java @@ -0,0 +1,96 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PrimalEmpathy extends CardImpl { + + public PrimalEmpathy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}{U}"); + + // At the beginning of your upkeep, draw a card if you control a creature with the greatest power among creatures on the battlefield. Otherwise, put a +1/+1 counter on a creature you control. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new PrimalEmpathyEffect(), TargetController.YOU, false + )); + } + + private PrimalEmpathy(final PrimalEmpathy card) { + super(card); + } + + @Override + public PrimalEmpathy copy() { + return new PrimalEmpathy(this); + } +} + +class PrimalEmpathyEffect extends OneShotEffect { + + PrimalEmpathyEffect() { + super(Outcome.Benefit); + staticText = "draw a card if you control a creature " + + "with the greatest power among creatures on the battlefield. " + + "Otherwise, put a +1/+1 counter on a creature you control"; + } + + private PrimalEmpathyEffect(final PrimalEmpathyEffect effect) { + super(effect); + } + + @Override + public PrimalEmpathyEffect copy() { + return new PrimalEmpathyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int highestPower = game + .getBattlefield() + .getActivePermanents(source.getControllerId(), game) + .stream() + .filter(Permanent::isCreature) + .map(Permanent::getPower) + .mapToInt(MageInt::getValue) + .max() + .orElse(0); + boolean flag = game.getBattlefield() + .getAllActivePermanents(source.getControllerId()) + .stream() + .filter(Permanent::isCreature) + .map(Permanent::getPower) + .mapToInt(MageInt::getValue) + .anyMatch(i -> i >= highestPower); + if (flag) { + return player.drawCards(1, source.getSourceId(), game) > 0; + } + Target target = new TargetControlledCreaturePermanent(); + target.setNotTarget(true); + if (!player.choose(outcome, target, source.getSourceId(), game)) { + return false; + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + return permanent != null && permanent.addCounters(CounterType.P1P1.createInstance(), source, game); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PrimalMight.java b/Mage.Sets/src/mage/cards/p/PrimalMight.java new file mode 100644 index 0000000000..b0d0e69504 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PrimalMight.java @@ -0,0 +1,46 @@ +package mage.cards.p; + +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.FightTargetsEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author mikalinn777 + */ + + +public final class PrimalMight extends CardImpl { + + public PrimalMight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{G}"); + + // Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don’t control. + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addEffect(new BoostTargetEffect(ManacostVariableValue.instance, ManacostVariableValue.instance, Duration.EndOfTurn)); + // + this.getSpellAbility().addEffect(new FightTargetsEffect() + .concatBy("Then") + .setText("it fights up to one target creature you don't control")); + this.getSpellAbility().addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); + + } + + public PrimalMight(final PrimalMight card) { + super(card); + } + + @Override + public PrimalMight copy() { + return new PrimalMight(this); + } + +} diff --git a/Mage.Sets/src/mage/cards/p/PrivateResearch.java b/Mage.Sets/src/mage/cards/p/PrivateResearch.java index bef10ca1c2..4cfa4af84f 100644 --- a/Mage.Sets/src/mage/cards/p/PrivateResearch.java +++ b/Mage.Sets/src/mage/cards/p/PrivateResearch.java @@ -1,60 +1,60 @@ -package mage.cards.p; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.DiesAttachedTriggeredAbility; -import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.counters.CounterType; - -/** - * - * @author jeffwadsworth - */ -public final class PrivateResearch extends CardImpl { - - private static final String rule = "draw a card for each page counter on {this}."; - - public PrivateResearch(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // At the beginning of your upkeep, you may put a page counter on Private Research. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.PAGE.createInstance(), true), TargetController.YOU, true)); - - // When enchanted creature dies, draw a card for each page counter on Private Research. - this.addAbility(new DiesAttachedTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.PAGE)).setText(rule), "enchanted creature")); - - } - - public PrivateResearch(final PrivateResearch card) { - super(card); - } - - @Override - public PrivateResearch copy() { - return new PrivateResearch(this); - } -} +package mage.cards.p; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DiesAttachedTriggeredAbility; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; + +/** + * + * @author jeffwadsworth + */ +public final class PrivateResearch extends CardImpl { + + private static final String rule = "draw a card for each page counter on {this}."; + + public PrivateResearch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of your upkeep, you may put a page counter on Private Research. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.PAGE.createInstance(), true), TargetController.YOU, true)); + + // When enchanted creature dies, draw a card for each page counter on Private Research. + this.addAbility(new DiesAttachedTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.PAGE)).setText(rule), "enchanted creature")); + + } + + public PrivateResearch(final PrivateResearch card) { + super(card); + } + + @Override + public PrivateResearch copy() { + return new PrivateResearch(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Probe.java b/Mage.Sets/src/mage/cards/p/Probe.java index 6c98d48e93..4a80a78d85 100644 --- a/Mage.Sets/src/mage/cards/p/Probe.java +++ b/Mage.Sets/src/mage/cards/p/Probe.java @@ -32,7 +32,7 @@ public final class Probe extends CardImpl { this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new DiscardTargetEffect(2), KickedCondition.instance, - "

if this spell was kicked, target player discards two cards")); + "

If this spell was kicked, target player discards two cards")); this.getSpellAbility().setTargetAdjuster(ProbeAdjuster.instance); } diff --git a/Mage.Sets/src/mage/cards/p/ProfaneCommand.java b/Mage.Sets/src/mage/cards/p/ProfaneCommand.java index 9e9b5172f9..53826990e3 100644 --- a/Mage.Sets/src/mage/cards/p/ProfaneCommand.java +++ b/Mage.Sets/src/mage/cards/p/ProfaneCommand.java @@ -1,4 +1,3 @@ - package mage.cards.p; import mage.abilities.Ability; @@ -48,7 +47,7 @@ public final class ProfaneCommand extends CardImpl { // * Return target creature card with converted mana cost X or less from your graveyard to the battlefield. Mode mode = new Mode(); - mode.addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + mode.addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); mode.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card with converted mana cost X or less from your graveyard"))); this.getSpellAbility().addMode(mode); diff --git a/Mage.Sets/src/mage/cards/p/ProfaneProcession.java b/Mage.Sets/src/mage/cards/p/ProfaneProcession.java index 67d0fa8434..8008c8eabb 100644 --- a/Mage.Sets/src/mage/cards/p/ProfaneProcession.java +++ b/Mage.Sets/src/mage/cards/p/ProfaneProcession.java @@ -76,7 +76,7 @@ class ProfaneProcessionEffect extends OneShotEffect { MageObject sourceObject = source.getSourceObject(game); if (controller != null && exileId != null && sourceObject != null) { new ExileTargetEffect(exileId, sourceObject.getIdName()).setTargetPointer(targetPointer).apply(game, source); - game.applyEffects(); + game.getState().processAction(game); ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone != null && exileZone.size() > 2) { new TransformSourceEffect(true).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/p/ProfanerOfTheDead.java b/Mage.Sets/src/mage/cards/p/ProfanerOfTheDead.java index f7336f9897..ea1c4eb187 100644 --- a/Mage.Sets/src/mage/cards/p/ProfanerOfTheDead.java +++ b/Mage.Sets/src/mage/cards/p/ProfanerOfTheDead.java @@ -62,7 +62,7 @@ class ProfanerOfTheDeadReturnEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent exploitedCreature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent exploitedCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && exploitedCreature != null) { FilterCreaturePermanent filter = new FilterCreaturePermanent(); filter.add(TargetController.OPPONENT.getControllerPredicate()); diff --git a/Mage.Sets/src/mage/cards/p/ProsperousPirates.java b/Mage.Sets/src/mage/cards/p/ProsperousPirates.java index 8d3de1a966..06183091c9 100644 --- a/Mage.Sets/src/mage/cards/p/ProsperousPirates.java +++ b/Mage.Sets/src/mage/cards/p/ProsperousPirates.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -11,8 +9,9 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.game.permanent.token.TreasureToken; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ProsperousPirates extends CardImpl { @@ -26,7 +25,7 @@ public final class ProsperousPirates extends CardImpl { this.toughness = new MageInt(4); // When Prosperous Pirates enters the battlefield, create two colorless Treasure artifact tokens with "{T}, Sacrifice this artifact: Add one mana of any color." - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new TreasureToken("XLN"), 2))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new TreasureToken(), 2))); } public ProsperousPirates(final ProsperousPirates card) { diff --git a/Mage.Sets/src/mage/cards/p/ProteanHulk.java b/Mage.Sets/src/mage/cards/p/ProteanHulk.java index 50a43b2da6..30a2df4205 100644 --- a/Mage.Sets/src/mage/cards/p/ProteanHulk.java +++ b/Mage.Sets/src/mage/cards/p/ProteanHulk.java @@ -4,7 +4,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.*; import mage.constants.CardType; @@ -34,7 +34,7 @@ public final class ProteanHulk extends CardImpl { this.toughness = new MageInt(6); // When Protean Hulk dies, search your library for any number of creature cards with total converted mana cost 6 or less and put them onto the battlefield. Then shuffle your library. - this.addAbility(new DiesTriggeredAbility(new ProteanHulkEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new ProteanHulkEffect())); } public ProteanHulk(final ProteanHulk card) { diff --git a/Mage.Sets/src/mage/cards/p/ProteanRaider.java b/Mage.Sets/src/mage/cards/p/ProteanRaider.java index e1aee66bd8..0e5ee02b6b 100644 --- a/Mage.Sets/src/mage/cards/p/ProteanRaider.java +++ b/Mage.Sets/src/mage/cards/p/ProteanRaider.java @@ -1,18 +1,20 @@ - package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.effects.common.CopyPermanentEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** * @author JayDi85 */ @@ -29,6 +31,8 @@ public final class ProteanRaider extends CardImpl { // Raid — If you attacked with a creature this turn, you may have Protean Raider enter the battlefield as a copy of any creature on the battlefield. Ability ability = new EntersBattlefieldAbility(new CopyPermanentEffect(), true, RaidCondition.instance, "Raid — If you attacked with a creature this turn, you may have {this} enter the battlefield as a copy of any creature on the battlefield.", ""); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java b/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java index d8ecd23dac..337bbe5b63 100644 --- a/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java +++ b/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java @@ -72,7 +72,7 @@ class ProteanThaumaturgeApplyToPermanent extends ApplyToPermanent { @Override public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { - permanent.addAbility(ProteanThaumaturge.createAbility(), game); + permanent.addAbility(ProteanThaumaturge.createAbility(), source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/p/ProudWildbonder.java b/Mage.Sets/src/mage/cards/p/ProudWildbonder.java new file mode 100644 index 0000000000..fe4893d74f --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProudWildbonder.java @@ -0,0 +1,61 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.DamageAsThoughNotBlockedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.DependencyType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProudWildbonder extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(new AbilityPredicate(TrampleAbility.class)); + } + + public ProudWildbonder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R/G}{R/G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Creatures you control with trample have "You may have this creature assign its combat damage as though it weren't blocked." + ContinuousEffect effect = new GainAbilityControlledEffect( + DamageAsThoughNotBlockedAbility.getInstance(), Duration.WhileOnBattlefield, filter + ); + effect.setText("Creatures you control with trample have " + + "\"You may have this creature assign its combat damage as though it weren't blocked.\""); + effect.setDependedToType(DependencyType.AddingAbility); + this.addAbility(new SimpleStaticAbility(effect)); + } + + private ProudWildbonder(final ProudWildbonder card) { + super(card); + } + + @Override + public ProudWildbonder copy() { + return new ProudWildbonder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Provoke.java b/Mage.Sets/src/mage/cards/p/Provoke.java index 37a648c142..351d4037b1 100644 --- a/Mage.Sets/src/mage/cards/p/Provoke.java +++ b/Mage.Sets/src/mage/cards/p/Provoke.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.UntapTargetEffect; @@ -10,37 +8,31 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Provoke extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public Provoke(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Untap target creature you don't control. That creature blocks this turn if able. this.getSpellAbility().addEffect(new UntapTargetEffect()); Effect effect = new BlocksIfAbleTargetEffect(Duration.EndOfTurn); effect.setText("That creature blocks this turn if able"); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } - public Provoke(final Provoke card) { + private Provoke(final Provoke card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java b/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java index e676d6beb9..c3b547c582 100644 --- a/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java +++ b/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CantBeCounteredControlledEffect; import mage.cards.CardImpl; @@ -35,7 +35,7 @@ public final class ProwlingSerpopard extends CardImpl { this.toughness = new MageInt(3); // Prowling Serpopard can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Creature spells you control can't be countered. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeCounteredControlledEffect(filterTarget, null, Duration.WhileOnBattlefield))); diff --git a/Mage.Sets/src/mage/cards/p/PryingQuestions.java b/Mage.Sets/src/mage/cards/p/PryingQuestions.java index fe21705c4c..b1f314c267 100644 --- a/Mage.Sets/src/mage/cards/p/PryingQuestions.java +++ b/Mage.Sets/src/mage/cards/p/PryingQuestions.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; @@ -16,20 +14,20 @@ import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PryingQuestions extends CardImpl { public PryingQuestions(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Target opponent loses 3 life and puts a card from their hand on top of their library. - this.getSpellAbility().addTarget(new TargetOpponent()); this.getSpellAbility().addEffect(new LoseLifeTargetEffect(3)); - this.getSpellAbility().addEffect(new PryingQuestionsEffect()); - + this.getSpellAbility().addEffect(new PryingQuestionsEffect().concatBy("and")); + this.getSpellAbility().addTarget(new TargetOpponent()); } public PryingQuestions(final PryingQuestions card) { @@ -46,7 +44,7 @@ class PryingQuestionsEffect extends OneShotEffect { public PryingQuestionsEffect() { super(Outcome.Detriment); - this.staticText = "and puts a card from their hand on top of their library"; + this.staticText = "puts a card from their hand on top of their library"; } public PryingQuestionsEffect(final PryingQuestionsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PsychicImpetus.java b/Mage.Sets/src/mage/cards/p/PsychicImpetus.java new file mode 100644 index 0000000000..bbbedc9098 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PsychicImpetus.java @@ -0,0 +1,55 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksAttachedTriggeredAbility; +import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PsychicImpetus extends CardImpl { + + public PsychicImpetus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 and is goaded. + this.addAbility(new GoadAttachedAbility(new BoostEnchantedEffect(2, 2))); + + // Whenever enchanted creature attacks, you scry 2. + this.addAbility(new AttacksAttachedTriggeredAbility( + new ScryEffect(2, false).setText("you scry 2"), AttachmentType.AURA, false + )); + } + + private PsychicImpetus(final PsychicImpetus card) { + super(card); + } + + @Override + public PsychicImpetus copy() { + return new PsychicImpetus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PsychicSpiral.java b/Mage.Sets/src/mage/cards/p/PsychicSpiral.java index f142d79042..2dc1abe54b 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicSpiral.java +++ b/Mage.Sets/src/mage/cards/p/PsychicSpiral.java @@ -1,15 +1,12 @@ - package mage.cards.p; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; @@ -21,8 +18,7 @@ import mage.target.TargetPlayer; public final class PsychicSpiral extends CardImpl { public PsychicSpiral(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}"); // Shuffle all cards from your graveyard into your library. Target player puts that many cards from the top of their library into their graveyard. this.getSpellAbility().addTarget(new TargetPlayer()); @@ -43,7 +39,7 @@ class PsychicSpiralEffect extends OneShotEffect { public PsychicSpiralEffect() { super(Outcome.GainLife); - staticText = "Shuffle all cards from your graveyard into your library. Target player puts that many cards from the top of their library into their graveyard"; + staticText = "Shuffle all cards from your graveyard into your library. Target player mills that many cards"; } public PsychicSpiralEffect(final PsychicSpiralEffect effect) { @@ -52,19 +48,13 @@ class PsychicSpiralEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - int cardsInGraveyard = player.getGraveyard().size(); - for (Card card: player.getGraveyard().getCards(game)) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - player.shuffleLibrary(source, game); - + Player controller = game.getPlayer(source.getControllerId()); + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (controller != null && targetPlayer != null) { + int cardsInGraveyard = controller.getGraveyard().size(); + controller.shuffleCardsToLibrary(controller.getGraveyard(), game, source); if (cardsInGraveyard > 0) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (targetPlayer != null) { - targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, cardsInGraveyard), Zone.GRAVEYARD, source, game); - } + targetPlayer.millCards(cardsInGraveyard, source, game); } return true; } @@ -75,4 +65,4 @@ class PsychicSpiralEffect extends OneShotEffect { public PsychicSpiralEffect copy() { return new PsychicSpiralEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/p/PsychicStrike.java b/Mage.Sets/src/mage/cards/p/PsychicStrike.java index f5c63b1c1f..344c345efe 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicStrike.java +++ b/Mage.Sets/src/mage/cards/p/PsychicStrike.java @@ -66,7 +66,7 @@ class PsychicStrikeEffect extends OneShotEffect { if (stackObject != null) { Player controller = game.getPlayer(stackObject.getControllerId()); if (controller != null) { - controller.moveCards(controller.getLibrary().getTopCards(game, 2), Zone.GRAVEYARD, source, game); + controller.millCards(2, source, game); } } return countered; diff --git a/Mage.Sets/src/mage/cards/p/PsychicTheft.java b/Mage.Sets/src/mage/cards/p/PsychicTheft.java index 94b14e02fb..565ecf2dbb 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicTheft.java +++ b/Mage.Sets/src/mage/cards/p/PsychicTheft.java @@ -80,7 +80,7 @@ class PsychicTheftEffect extends OneShotEffect { Card chosenCard = null; if (cardsHand > 0) { TargetCard target = new TargetCard(Zone.HAND, filter); - if (controller.choose(Outcome.Benefit, opponent.getHand(), target, game)) { + if (controller.choose(Outcome.Exile, opponent.getHand(), target, game)) { chosenCard = opponent.getHand().get(target.getFirstTarget(), game); } } @@ -156,7 +156,7 @@ class PsychicTheftCondition implements Condition { if (!game.getExile().getExileZone(exileId).contains(cardId)) { return false; } - SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class, source.getSourceId()); + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); if (watcher != null) { List spells = watcher.getSpellsCastThisTurn(source.getControllerId()); if (spells != null) { diff --git a/Mage.Sets/src/mage/cards/p/PsychicVortex.java b/Mage.Sets/src/mage/cards/p/PsychicVortex.java index 31cd032e07..87b86d7e5b 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicVortex.java +++ b/Mage.Sets/src/mage/cards/p/PsychicVortex.java @@ -55,14 +55,14 @@ public final class PsychicVortex extends CardImpl { class PsychicVortexCost extends CostImpl { PsychicVortexCost() { - this.text = "Draw a card."; + this.text = "Draw a card"; } @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { Player controller = game.getPlayer(controllerId); if (controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, sourceId, game); this.paid = true; return true; } diff --git a/Mage.Sets/src/mage/cards/p/PureReflection.java b/Mage.Sets/src/mage/cards/p/PureReflection.java index cad8231b33..645bc1d895 100644 --- a/Mage.Sets/src/mage/cards/p/PureReflection.java +++ b/Mage.Sets/src/mage/cards/p/PureReflection.java @@ -71,7 +71,7 @@ public final class PureReflection extends CardImpl { game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game).forEach((permanent) -> { permanent.destroy(source.getSourceId(), game,false); }); - game.applyEffects(); + game.getState().processAction(game); // Then that player creates an X/X white Reflection creature token, where X is the converted mana cost of that spell. ReflectionPureToken token = new ReflectionPureToken(spell.getConvertedManaCost()); diff --git a/Mage.Sets/src/mage/cards/p/PuresteelPaladin.java b/Mage.Sets/src/mage/cards/p/PuresteelPaladin.java index ae6c3fcf39..745e9c5a7c 100644 --- a/Mage.Sets/src/mage/cards/p/PuresteelPaladin.java +++ b/Mage.Sets/src/mage/cards/p/PuresteelPaladin.java @@ -1,8 +1,5 @@ - - package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -11,6 +8,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -18,6 +16,8 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; +import java.util.UUID; + /** * @author Loki */ @@ -29,7 +29,7 @@ public final class PuresteelPaladin extends CardImpl { } public PuresteelPaladin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.KNIGHT); @@ -38,11 +38,15 @@ public final class PuresteelPaladin extends CardImpl { // Whenever an Equipment enters the battlefield under your control, you may draw a card. this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), filter, true)); + // Metalcraft — Equipment you control have equip {0} as long as you control three or more artifacts this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new GainAbilityControlledEffect(new EquipAbility(Outcome.AddAbility, new GenericManaCost(0)), Duration.WhileOnBattlefield, filter), MetalcraftCondition.instance, - "Metalcraft — Equipment you control have equip {0} as long as you control three or more artifacts"))); + "Metalcraft — Equipment you control have equip {0} as long as you control three or more artifacts")) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); } public PuresteelPaladin(final PuresteelPaladin card) { diff --git a/Mage.Sets/src/mage/cards/p/Purgatory.java b/Mage.Sets/src/mage/cards/p/Purgatory.java index 7fdb5917e8..25818c66db 100644 --- a/Mage.Sets/src/mage/cards/p/Purgatory.java +++ b/Mage.Sets/src/mage/cards/p/Purgatory.java @@ -96,7 +96,7 @@ class PurgatoryTriggeredAbility extends TriggeredAbilityImpl { && permanent.isCreature() && permanent.isOwnedBy(controller.getId())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + this.getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/p/PurpleCrystalCrab.java b/Mage.Sets/src/mage/cards/p/PurpleCrystalCrab.java index 43dbbc267b..6a896a5d4c 100644 --- a/Mage.Sets/src/mage/cards/p/PurpleCrystalCrab.java +++ b/Mage.Sets/src/mage/cards/p/PurpleCrystalCrab.java @@ -2,7 +2,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.constants.SubType; import mage.cards.CardImpl; @@ -23,7 +23,7 @@ public final class PurpleCrystalCrab extends CardImpl { this.toughness = new MageInt(1); // When Purple-Crystal Crab dies, draw card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } public PurpleCrystalCrab(final PurpleCrystalCrab card) { diff --git a/Mage.Sets/src/mage/cards/p/PursuedWhale.java b/Mage.Sets/src/mage/cards/p/PursuedWhale.java new file mode 100644 index 0000000000..d26a8f02d8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PursuedWhale.java @@ -0,0 +1,76 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.permanent.token.PursuedWhaleToken; +import mage.game.permanent.token.Token; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PursuedWhale extends CardImpl { + + public PursuedWhale(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); + + this.subtype.add(SubType.WHALE); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able." + this.addAbility(new EntersBattlefieldTriggeredAbility(new PursuedWhaleTokenEffect())); + + // Spells your opponents cast that target Pursued Whale cost {3} more to cast. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(3, new FilterCard("Spells"), TargetController.OPPONENT)) + ); + } + + private PursuedWhale(final PursuedWhale card) { + super(card); + } + + @Override + public PursuedWhale copy() { + return new PursuedWhale(this); + } +} + +class PursuedWhaleTokenEffect extends OneShotEffect { + + private static final Token token = new PursuedWhaleToken(); + + PursuedWhaleTokenEffect() { + super(Outcome.Benefit); + staticText = "each opponent creates a 1/1 red Pirate creature token with " + + "\"This creature can't block\" and \"Creatures you control attack each combat if able.\""; + } + + private PursuedWhaleTokenEffect(final PursuedWhaleTokenEffect effect) { + super(effect); + } + + @Override + public PursuedWhaleTokenEffect copy() { + return new PursuedWhaleTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getOpponents(source.getControllerId())) { + token.putOntoBattlefield(1, game, source.getSourceId(), playerId); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PutAway.java b/Mage.Sets/src/mage/cards/p/PutAway.java index 0f4c6da129..333525b37e 100644 --- a/Mage.Sets/src/mage/cards/p/PutAway.java +++ b/Mage.Sets/src/mage/cards/p/PutAway.java @@ -1,21 +1,15 @@ package mage.cards.p; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import java.util.UUID; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.ShuffleIntoLibraryTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.players.Player; +import mage.filter.StaticFilters; import mage.target.TargetSpell; import mage.target.common.TargetCardInYourGraveyard; - -import java.util.UUID; +import mage.target.targetpointer.SecondTargetPointer; /** * @author jeffwadsworth @@ -25,11 +19,13 @@ public final class PutAway extends CardImpl { public PutAway(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{U}"); - // Counter target spell. You may shuffle up to one target card from your graveyard into your library. - this.getSpellAbility().addEffect(new PutAwayEffect()); + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addEffect(new ShuffleIntoLibraryTargetEffect(true) + .setTargetPointer(new SecondTargetPointer()) + .setText("you may shuffle up to one target card from your graveyard into your library")); this.getSpellAbility().addTarget(new TargetSpell()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 1, new FilterCard())); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 1, StaticFilters.FILTER_CARD_FROM_YOUR_GRAVEYARD)); } @@ -42,41 +38,3 @@ public final class PutAway extends CardImpl { return new PutAway(this); } } - -class PutAwayEffect extends OneShotEffect { - - boolean countered = false; - - public PutAwayEffect() { - super(Outcome.Neutral); - this.staticText = "Counter target spell. You may shuffle up to one target card from your graveyard into your library"; - } - - public PutAwayEffect(final PutAwayEffect effect) { - super(effect); - } - - @Override - public PutAwayEffect copy() { - return new PutAwayEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Spell spell = game.getStack().getSpell(source.getFirstTarget()); - if (spell != null && game.getStack().counter(spell.getId(), source.getSourceId(), game)) { - countered = true; - } - - Card card = game.getCard(source.getTargets().get(1).getFirstTarget()); - Player you = game.getPlayer(source.getControllerId()); - if (you != null && card != null) { - if (you.chooseUse(Outcome.Benefit, "Do you wish to shuffle up to one target card from your graveyard into your library?", source, game) - && game.getState().getZone(card.getId()).match(Zone.GRAVEYARD)) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); - you.shuffleLibrary(source, game); - } - } - return countered; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/Putrefy.java b/Mage.Sets/src/mage/cards/p/Putrefy.java index b0b3c0f39b..f467c3a875 100644 --- a/Mage.Sets/src/mage/cards/p/Putrefy.java +++ b/Mage.Sets/src/mage/cards/p/Putrefy.java @@ -27,7 +27,7 @@ public final class Putrefy extends CardImpl { public Putrefy (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{G}"); - + // Destroy target artifact or creature. It can't be regenerated. this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); } diff --git a/Mage.Sets/src/mage/cards/p/PyreHound.java b/Mage.Sets/src/mage/cards/p/PyreHound.java index 6e42ee1b4c..4a17383d6d 100644 --- a/Mage.Sets/src/mage/cards/p/PyreHound.java +++ b/Mage.Sets/src/mage/cards/p/PyreHound.java @@ -22,7 +22,7 @@ public final class PyreHound extends CardImpl { public PyreHound(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); this.subtype.add(SubType.ELEMENTAL); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/p/Pyroceratops.java b/Mage.Sets/src/mage/cards/p/Pyroceratops.java new file mode 100644 index 0000000000..3c2fcd89e0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/Pyroceratops.java @@ -0,0 +1,47 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Pyroceratops extends CardImpl { + + public Pyroceratops(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever you cast a noncreature spell, put a +1/+1 counter on Pyroceratops. + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + )); + } + + private Pyroceratops(final Pyroceratops card) { + super(card); + } + + @Override + public Pyroceratops copy() { + return new Pyroceratops(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PyromancersGauntlet.java b/Mage.Sets/src/mage/cards/p/PyromancersGauntlet.java index 6c6c1c9b4c..4e56c86f57 100644 --- a/Mage.Sets/src/mage/cards/p/PyromancersGauntlet.java +++ b/Mage.Sets/src/mage/cards/p/PyromancersGauntlet.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -25,7 +24,7 @@ import mage.util.CardUtil; public final class PyromancersGauntlet extends CardImpl { public PyromancersGauntlet(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // If a red instant or sorcery spell you control or a red planeswalker you control would deal damage to a permanent or player, it deals that much damage plus 2 to that permanent or player instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PyromancersGauntletReplacementEffect())); @@ -58,22 +57,21 @@ class PyromancersGauntletReplacementEffect extends ReplacementEffectImpl { || event.getType() == GameEvent.EventType.DAMAGE_CREATURE || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { MageObject object = game.getObject(event.getSourceId()); if (object instanceof Spell) { if (((Spell) object).isControlledBy(source.getControllerId()) && (object.isInstant() - || object.isSorcery())){ + || object.isSorcery())) { return true; } } - Permanent permanent = game.getBattlefield().getPermanent(event.getSourceId()); - if(permanent != null && permanent.isPlaneswalker()){ - return true; - } - return false; + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + return permanent != null + && permanent.isPlaneswalker() + && source.isControlledBy(permanent.getControllerId()); } @Override diff --git a/Mage.Sets/src/mage/cards/q/QarsiSadist.java b/Mage.Sets/src/mage/cards/q/QarsiSadist.java index bc18b2e299..7e05641809 100644 --- a/Mage.Sets/src/mage/cards/q/QarsiSadist.java +++ b/Mage.Sets/src/mage/cards/q/QarsiSadist.java @@ -1,11 +1,8 @@ - package mage.cards.q; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.ExploitCreatureTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.keyword.ExploitAbility; @@ -15,14 +12,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class QarsiSadist extends CardImpl { public QarsiSadist(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.CLERIC); this.power = new MageInt(1); @@ -30,12 +28,11 @@ public final class QarsiSadist extends CardImpl { // Exploit this.addAbility(new ExploitAbility()); + // When Qarsi Sadist exploits a creature, target opponent loses 2 life and you gain 2 life. Ability ability = new ExploitCreatureTriggeredAbility(new LoseLifeTargetEffect(2), false); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); ability.addTarget(new TargetOpponent()); - Effect effect = new GainLifeEffect(2); - effect.setText("and you gain 2 life"); - ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/q/QasaliAmbusher.java b/Mage.Sets/src/mage/cards/q/QasaliAmbusher.java index 9fa8d53023..32f19ddb9e 100644 --- a/Mage.Sets/src/mage/cards/q/QasaliAmbusher.java +++ b/Mage.Sets/src/mage/cards/q/QasaliAmbusher.java @@ -2,16 +2,22 @@ package mage.cards.q; import java.util.UUID; import mage.MageInt; -import mage.abilities.ActivatedAbilityImpl; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.Ability; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.decorator.ConditionalAsThoughEffect; import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashSourceEffect; import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledLandPermanent; -import mage.game.Game; -import mage.game.combat.CombatGroup; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.watchers.common.PlayerAttackedStepWatcher; /** * @@ -19,6 +25,18 @@ import mage.game.combat.CombatGroup; */ public final class QasaliAmbusher extends CardImpl { + private static final FilterControlledPermanent filterForest = new FilterControlledPermanent(); + private static final FilterControlledPermanent filterPlains = new FilterControlledPermanent(); + + static { + filterForest.add(SubType.FOREST.getPredicate()); + filterPlains.add(SubType.PLAINS.getPredicate()); + } + + private static final Condition condition = + new CompoundCondition("If a creature is attacking you and you control a Forest and a Plains", + AttackedThisStepCondition.instance, new PermanentsOnTheBattlefieldCondition(filterForest), new PermanentsOnTheBattlefieldCondition(filterPlains)); + public QasaliAmbusher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{W}"); this.subtype.add(SubType.CAT); @@ -30,10 +48,13 @@ public final class QasaliAmbusher extends CardImpl { // Reach this.addAbility(ReachAbility.getInstance()); - // If a creature is attacking you and you control a Forest and a Plains, + // If a creature is attacking you and you control a Forest and a Plains, // you may cast Qasali Ambusher without paying its mana cost and as though it had flash. - this.addAbility(new QasaliAmbusherAbility()); - + Ability ability = new AlternativeCostSourceAbility(null, condition); + ability.addEffect(new ConditionalAsThoughEffect(new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame), condition) + .setText("you may cast {this} without paying its mana cost and as though it had flash")); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); } public QasaliAmbusher(final QasaliAmbusher card) { @@ -44,57 +65,4 @@ public final class QasaliAmbusher extends CardImpl { public QasaliAmbusher copy() { return new QasaliAmbusher(this); } -} - -class QasaliAmbusherAbility extends ActivatedAbilityImpl { - - private static final FilterControlledLandPermanent filterPlains = new FilterControlledLandPermanent(); - private static final FilterControlledLandPermanent filterForest = new FilterControlledLandPermanent(); - - static { - filterPlains.add(SubType.PLAINS.getPredicate()); - filterForest.add(SubType.FOREST.getPredicate()); - } - - public QasaliAmbusherAbility() { - super(Zone.HAND, new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame), new ManaCostsImpl()); - this.timing = TimingRule.INSTANT; - this.usesStack = false; - } - - public QasaliAmbusherAbility(final QasaliAmbusherAbility ability) { - super(ability); - } - - @Override - public QasaliAmbusherAbility copy() { - return new QasaliAmbusherAbility(this); - } - - @Override - public ActivationStatus canActivate(UUID playerId, Game game) { - if (!game.getBattlefield().getActivePermanents(filterPlains, - this.getControllerId(), this.getSourceId(), game).isEmpty() - && !game.getBattlefield().getActivePermanents(filterForest, - this.getControllerId(), this.getSourceId(), game).isEmpty()) { - for (CombatGroup group : game.getCombat().getGroups()) { - if (isControlledBy(group.getDefenderId())) { - return super.canActivate(playerId, game); - } - } - } - return ActivationStatus.getFalse(); - } - - @Override - public String getRule(boolean all) { - return this.getRule(); - } - - @Override - public String getRule() { - return "If a creature is attacking you and you control a Forest and " - + "a Plains, you may cast {this} without paying its mana " - + "cost and as though it had flash."; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/q/QasaliSlingers.java b/Mage.Sets/src/mage/cards/q/QasaliSlingers.java index a68dd90737..457e83c16f 100644 --- a/Mage.Sets/src/mage/cards/q/QasaliSlingers.java +++ b/Mage.Sets/src/mage/cards/q/QasaliSlingers.java @@ -1,28 +1,27 @@ - package mage.cards.q; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author spjspj */ public final class QasaliSlingers extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent(SubType.CAT, "Cat"); + public QasaliSlingers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); @@ -35,10 +34,14 @@ public final class QasaliSlingers extends CardImpl { this.addAbility(ReachAbility.getInstance()); // Whenever Qasali Slingers or another Cat enters the battlefield under your control, you may destroy target artifact or enchantment. - this.addAbility(new QasaliSlingersTriggeredAbility()); + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new DestroyTargetEffect(), filter, true, true + ); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + this.addAbility(ability); } - public QasaliSlingers(final QasaliSlingers card) { + private QasaliSlingers(final QasaliSlingers card) { super(card); } @@ -47,44 +50,3 @@ public final class QasaliSlingers extends CardImpl { return new QasaliSlingers(this); } } - -class QasaliSlingersTriggeredAbility extends TriggeredAbilityImpl { - - public QasaliSlingersTriggeredAbility() { - super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true); - this.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); - } - - public QasaliSlingersTriggeredAbility(final QasaliSlingersTriggeredAbility ability) { - super(ability); - } - - @Override - public QasaliSlingersTriggeredAbility copy() { - return new QasaliSlingersTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null) { - if (permanent.getId().equals(this.getSourceId())) { - return true; - } - if (permanent.hasSubtype(SubType.CAT, game) && permanent.isControlledBy(this.getControllerId())) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} or another Cat enters the battlefield under your control, you may destroy target artifact or enchantment."; - } -} diff --git a/Mage.Sets/src/mage/cards/q/QuarryColossus.java b/Mage.Sets/src/mage/cards/q/QuarryColossus.java index 0b9c69c15a..7d16f1c30e 100644 --- a/Mage.Sets/src/mage/cards/q/QuarryColossus.java +++ b/Mage.Sets/src/mage/cards/q/QuarryColossus.java @@ -1,4 +1,3 @@ - package mage.cards.q; import java.util.UUID; @@ -30,7 +29,8 @@ public final class QuarryColossus extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(6); - // When Quarry Colossus enters the battlefield, put target creature into its owner's library just beneath the top X cards of that library, where X is the number of Plains you control. + // When Quarry Colossus enters the battlefield, put target creature into its owner's + // library just beneath the top X cards of that library, where X is the number of Plains you control. Ability ability = new EntersBattlefieldTriggeredAbility(new QuarryColossusReturnLibraryEffect(), false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); @@ -49,8 +49,9 @@ public final class QuarryColossus extends CardImpl { class QuarryColossusReturnLibraryEffect extends OneShotEffect { public QuarryColossusReturnLibraryEffect() { - super(Outcome.Benefit); - this.staticText = "put target creature into its owner's library just beneath the top X cards of that library, where X is the number of Plains you control"; + super(Outcome.Detriment); + this.staticText = "put target creature into its owner's library just beneath the " + + "top X cards of that library, where X is the number of Plains you control"; } public QuarryColossusReturnLibraryEffect(final QuarryColossusReturnLibraryEffect effect) { @@ -69,7 +70,8 @@ class QuarryColossusReturnLibraryEffect extends OneShotEffect { if (permanent != null && controller != null) { Player owner = game.getPlayer(permanent.getOwnerId()); if (owner != null) { - int plains = game.getBattlefield().countAll(new FilterPermanent(SubType.PLAINS, "Plains you control"), source.getControllerId(), game); + int plains = game.getBattlefield().countAll(new FilterPermanent( + SubType.PLAINS, "Plains you control"), source.getControllerId(), game); controller.putCardOnTopXOfLibrary(permanent, game, source, plains); return true; } diff --git a/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java b/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java new file mode 100644 index 0000000000..e83513d223 --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java @@ -0,0 +1,160 @@ +package mage.cards.q; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.DinosaurBeastToken; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class QuartzwoodCrasher extends CardImpl { + + public QuartzwoodCrasher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}{G}"); + + this.subtype.add(SubType.DINOSAUR); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever one or more creatures you control with trample deal combat damage to a player, create an X/X green Dinosaur Beast creature token with trample, where X is the amount of damage those creatures dealt to that player. + this.addAbility(new QuartzwoodCrasherTriggeredAbility()); + } + + private QuartzwoodCrasher(final QuartzwoodCrasher card) { + super(card); + } + + @Override + public QuartzwoodCrasher copy() { + return new QuartzwoodCrasher(this); + } +} + +class QuartzwoodCrasherTriggeredAbility extends TriggeredAbilityImpl { + + private final Set damagedPlayerIds = new HashSet<>(); + + QuartzwoodCrasherTriggeredAbility() { + super(Zone.BATTLEFIELD, new QuartzwoodCrasherEffect(), false); + this.addWatcher(new QuartzwoodCrasherWatcher()); + } + + private QuartzwoodCrasherTriggeredAbility(final QuartzwoodCrasherTriggeredAbility ability) { + super(ability); + } + + @Override + public QuartzwoodCrasherTriggeredAbility copy() { + return new QuartzwoodCrasherTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER + || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST) { + damagedPlayerIds.clear(); + return false; + } + if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER + && ((DamagedPlayerEvent) event).isCombatDamage()) { + Permanent creature = game.getPermanent(event.getSourceId()); + if (creature != null && creature.isControlledBy(controllerId) + && creature.hasAbility(TrampleAbility.getInstance(), game) + && !damagedPlayerIds.contains(event.getTargetId())) { + damagedPlayerIds.add(event.getTargetId()); + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever one or more creatures you control with trample deal combat damage to a player, " + + "create an X/X green Dinosaur Beast creature token with trample, " + + "where X is the amount of damage those creatures dealt to that player."; + } +} + +class QuartzwoodCrasherEffect extends OneShotEffect { + + QuartzwoodCrasherEffect() { + super(Outcome.Benefit); + } + + private QuartzwoodCrasherEffect(final QuartzwoodCrasherEffect effect) { + super(effect); + } + + @Override + public QuartzwoodCrasherEffect copy() { + return new QuartzwoodCrasherEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + QuartzwoodCrasherWatcher watcher = game.getState().getWatcher(QuartzwoodCrasherWatcher.class); + return watcher != null && new DinosaurBeastToken( + watcher.getDamage(targetPointer.getFirst(game, source), source.getControllerId()) + ).putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + } +} + +class QuartzwoodCrasherWatcher extends Watcher { + + private final Map> damageMap = new HashMap<>(); + + QuartzwoodCrasherWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST) { + damageMap.clear(); + return; + } + if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER + || !((DamagedPlayerEvent) event).isCombatDamage()) { + return; + } + Permanent creature = game.getPermanent(event.getSourceId()); + if (creature == null || !creature.hasAbility(TrampleAbility.getInstance(), game)) { + return; + } + damageMap + .computeIfAbsent(event.getTargetId(), x -> new HashMap<>()) + .compute(creature.getControllerId(), (uuid, i) -> i == null ? event.getAmount() : event.getAmount() + i); + } + + public int getDamage(UUID damagedPlayerId, UUID controllerId) { + if (!damageMap.containsKey(damagedPlayerId)) { + return 0; + } + return damageMap.get(damagedPlayerId).getOrDefault(controllerId, 0); + } +} diff --git a/Mage.Sets/src/mage/cards/q/QueenOfIce.java b/Mage.Sets/src/mage/cards/q/QueenOfIce.java index d1fe38e761..a83d83f648 100644 --- a/Mage.Sets/src/mage/cards/q/QueenOfIce.java +++ b/Mage.Sets/src/mage/cards/q/QueenOfIce.java @@ -29,7 +29,7 @@ public final class QueenOfIce extends AdventureCard { // Whenever Queen of Ice deals combat damage to a creature, tap that creature. It doesn't untap during its controller's next untap step. Ability ability = new DealsDamageToACreatureTriggeredAbility( - new TapTargetEffect().setText("tap that creature."), + new TapTargetEffect().setText("tap that creature"), true, false, true ); ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect() diff --git a/Mage.Sets/src/mage/cards/q/QuestingBeast.java b/Mage.Sets/src/mage/cards/q/QuestingBeast.java index 5ff8e7b120..4f8a5ff1fe 100644 --- a/Mage.Sets/src/mage/cards/q/QuestingBeast.java +++ b/Mage.Sets/src/mage/cards/q/QuestingBeast.java @@ -82,7 +82,7 @@ class QuestingBeastPreventionEffect extends ContinuousRuleModifyingEffectImpl { QuestingBeastPreventionEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Combat damage that would be dealt by creatures you control can't be prevented."; + staticText = "Combat damage that would be dealt by creatures you control can't be prevented"; } private QuestingBeastPreventionEffect(final QuestingBeastPreventionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/q/QuicksilverElemental.java b/Mage.Sets/src/mage/cards/q/QuicksilverElemental.java index 2a68085555..622ee8bbb1 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksilverElemental.java +++ b/Mage.Sets/src/mage/cards/q/QuicksilverElemental.java @@ -17,7 +17,6 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.ManaPoolItem; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; import java.util.UUID; @@ -35,18 +34,15 @@ public final class QuicksilverElemental extends CardImpl { this.toughness = new MageInt(4); // {U}: Quicksilver Elemental gains all activated abilities of target creature until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new QuicksilverElementalEffect(), new ManaCostsImpl("{U}")); + Ability ability = new SimpleActivatedAbility(new QuicksilverElementalEffect(), new ManaCostsImpl("{U}")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // You may spend blue mana as though it were mana of any color to pay the activation costs of Quicksilver Elemental's abilities. - QuickSilverElementalBlueManaEffect effect2 = new QuickSilverElementalBlueManaEffect(); - effect2.setTargetPointer(new FixedTarget(this.getId())); - Ability ability2 = new SimpleStaticAbility(Zone.BATTLEFIELD, effect2); - this.addAbility(ability2); + this.addAbility(new SimpleStaticAbility(new QuickSilverElementalBlueManaEffect())); } - public QuicksilverElemental(final QuicksilverElemental card) { + private QuicksilverElemental(final QuicksilverElemental card) { super(card); } @@ -63,7 +59,7 @@ class QuicksilverElementalEffect extends OneShotEffect { staticText = "{this} gains all activated abilities of target creature until end of turn"; } - QuicksilverElementalEffect(final QuicksilverElementalEffect effect) { + private QuicksilverElementalEffect(final QuicksilverElementalEffect effect) { super(effect); } @@ -89,44 +85,14 @@ class QuicksilverElementalEffect extends OneShotEffect { } } -//class QuicksilverElementalEffect extends ContinuousEffectImpl { -// -// public QuicksilverElementalEffect() { -// super(Duration.EndOfTurn, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); -// staticText = "{this} gains all activated abilities of target creature until end of turn"; -// } -// -// public QuicksilverElementalEffect(final QuicksilverElementalEffect effect) { -// super(effect); -// } -// -// @Override -// public QuicksilverElementalEffect copy() { -// return new QuicksilverElementalEffect(this); -// } -// -// @Override -// public boolean apply(Game game, Ability source) { -// Permanent permanent = game.getPermanent(source.getSourceId()); -// Permanent creature = game.getPermanent(source.getTargets().getFirstTarget()); -// -// if (permanent != null && creature != null) { -// for (ActivatedAbility ability : creature.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD)) { -// permanent.addAbility(ability, source.getSourceId(), game); -// } -// } -// return false; -// } -//} - class QuickSilverElementalBlueManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect { - public QuickSilverElementalBlueManaEffect() { + QuickSilverElementalBlueManaEffect() { super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit); staticText = "You may spend blue mana as though it were mana of any color to pay the activation costs of {this}'s abilities"; } - public QuickSilverElementalBlueManaEffect(final QuickSilverElementalBlueManaEffect effect) { + private QuickSilverElementalBlueManaEffect(final QuickSilverElementalBlueManaEffect effect) { super(effect); } @@ -143,11 +109,7 @@ class QuickSilverElementalBlueManaEffect extends AsThoughEffectImpl implements A @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { objectId = CardUtil.getMainCardId(game, objectId); // for split cards - if (objectId.equals(getTargetPointer().getFirst(game, source))) { - return affectedControllerId.equals(source.getControllerId()); - } - - return false; + return objectId.equals(source.getSourceId()) && affectedControllerId.equals(source.getControllerId()); } @Override diff --git a/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java b/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java index 5a4afebf54..f4043c0c5c 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java +++ b/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java @@ -4,10 +4,10 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; import mage.cards.CardImpl; @@ -26,7 +26,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetLandPermanent; import mage.target.targetadjustment.TargetAdjuster; -import mage.target.targetpointer.FixedTarget; /** * @@ -37,15 +36,21 @@ public final class QuicksilverFountain extends CardImpl { public QuicksilverFountain(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - // At the beginning of each player's upkeep, that player puts a flood counter on target non-Island land they control of their choice. That land is an Island for as long as it has a flood counter on it. - Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new QuicksilverFountainEffect(), TargetController.ANY, false, true); + // At the beginning of each player's upkeep, that player puts a flood + // counter on target non-Island land they control of their choice. + // That land is an Island for as long as it has a flood counter on it. + Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new QuicksilverFountainEffect(), TargetController.ANY, false, true); ability.addTarget(new TargetLandPermanent()); ability.setTargetAdjuster(QuicksilverFountainAdjuster.instance); this.addAbility(ability); - // At the beginning of each end step, if all lands on the battlefield are Islands, remove all flood counters from them. + // At the beginning of each end step, if all lands on the battlefield are + // Islands, remove all flood counters from them. + // Note: This applies only if Quicksilver Fountain is on the battlefield Condition condition = new AllLandsAreSubtypeCondition(SubType.ISLAND); - this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new QuicksilverFountainEffect2(), TargetController.ANY, condition, false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, + new QuicksilverFountainEffect2(), TargetController.ANY, condition, false)); } public QuicksilverFountain(final QuicksilverFountain card) { @@ -96,13 +101,15 @@ class QuicksilverFountainEffect extends OneShotEffect { Permanent landChosen = game.getPermanent(source.getFirstTarget()); landChosen.addCounters(CounterType.FLOOD.createInstance(), source, game); ContinuousEffect becomesBasicLandTargetEffect - = new BecomesBasicLandTargetEffect(Duration.OneUse, false, SubType.ISLAND); + = new BecomesBasicLandTargetEffect(Duration.Custom, false, SubType.ISLAND); ConditionalContinuousEffect effect = new ConditionalContinuousEffect(becomesBasicLandTargetEffect, - new LandHasFloodCounterCondition(this), staticText); - this.setTargetPointer(new FixedTarget(landChosen, game)); - effect.setTargetPointer(new FixedTarget(landChosen, game)); - game.addEffect(effect, source); + new LandHasFloodCounterCondition(), staticText); + // Bug #6885 Fixed when owner/controller leaves the game the effect still applies + SimpleStaticAbility gainAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + gainAbility.setSourceId(landChosen.getId()); + gainAbility.getTargets().add(source.getTargets().get(0)); + game.addEffect(effect, gainAbility); return true; } return false; @@ -128,7 +135,8 @@ class QuicksilverFountainEffect2 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { for (Permanent land : game.getBattlefield().getAllActivePermanents(CardType.LAND)) { - land.removeCounters(CounterType.FLOOD.createInstance(land.getCounters(game).getCount(CounterType.FLOOD)), game); + land.removeCounters(CounterType.FLOOD.createInstance( + land.getCounters(game).getCount(CounterType.FLOOD)), game); } return true; } @@ -163,15 +171,12 @@ class AllLandsAreSubtypeCondition implements Condition { class LandHasFloodCounterCondition implements Condition { - private final Effect effect; - - public LandHasFloodCounterCondition(Effect effect) { - this.effect = effect; + public LandHasFloodCounterCondition() { } @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(effect.getTargetPointer().getFirst(game, source)); + Permanent permanent = game.getPermanent(source.getFirstTarget()); return permanent != null && permanent.getCounters(game).getCount(CounterType.FLOOD) > 0; } diff --git a/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java b/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java index 87b0caca71..38c7600aa8 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java +++ b/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java @@ -1,4 +1,3 @@ - package mage.cards.q; import java.util.UUID; @@ -14,12 +13,13 @@ import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.TargetPermanent; import mage.target.common.TargetAnyTarget; +import mage.watchers.common.LostControlWatcher; /** * @@ -44,6 +44,7 @@ public final class QuicksmithRebel extends CardImpl { "target artifact you control gains \"{T}: This artifact deals 2 damage to any target\" for as long as you control {this}"); Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); ability.addTarget(new TargetPermanent(new FilterControlledArtifactPermanent())); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java b/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java index 2b69c073e1..d52dfb6b07 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java +++ b/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java @@ -1,4 +1,3 @@ - package mage.cards.q; import java.util.UUID; @@ -14,11 +13,12 @@ import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.TargetPermanent; +import mage.watchers.common.LostControlWatcher; /** * @@ -42,6 +42,7 @@ public final class QuicksmithSpy extends CardImpl { "target artifact you control gains \"{T}: Draw a card\" for as long as you control {this}"); Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); ability.addTarget(new TargetPermanent(new FilterControlledArtifactPermanent())); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/q/QuirionDryad.java b/Mage.Sets/src/mage/cards/q/QuirionDryad.java index 4e2afae86e..e15c701108 100644 --- a/Mage.Sets/src/mage/cards/q/QuirionDryad.java +++ b/Mage.Sets/src/mage/cards/q/QuirionDryad.java @@ -21,7 +21,7 @@ import mage.filter.predicate.mageobject.ColorPredicate; */ public final class QuirionDryad extends CardImpl { - private static final FilterSpell filter = new FilterSpell("white, blue, black, or red spell"); + private static final FilterSpell filter = new FilterSpell("a spell that's white, blue, black, or red"); static { filter.add(Predicates.or( diff --git a/Mage.Sets/src/mage/cards/r/RabidBite.java b/Mage.Sets/src/mage/cards/r/RabidBite.java index 1b9526dc8a..7344f543dd 100644 --- a/Mage.Sets/src/mage/cards/r/RabidBite.java +++ b/Mage.Sets/src/mage/cards/r/RabidBite.java @@ -4,8 +4,7 @@ import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -16,22 +15,16 @@ import java.util.UUID; */ public final class RabidBite extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public RabidBite(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Target creature you control deals damage equal to its power to target creature you don't control. this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public RabidBite(final RabidBite card) { + private RabidBite(final RabidBite card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/r/RabidElephant.java b/Mage.Sets/src/mage/cards/r/RabidElephant.java index 58f0770275..95a69e47ec 100644 --- a/Mage.Sets/src/mage/cards/r/RabidElephant.java +++ b/Mage.Sets/src/mage/cards/r/RabidElephant.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MultipliedValue; import mage.abilities.dynamicvalue.common.BlockedCreatureCount; @@ -32,7 +32,7 @@ public final class RabidElephant extends CardImpl { DynamicValue value = new MultipliedValue(new BlockedCreatureCount(), 2); Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true); effect.setText("it gets +2/+2 until end of turn for each creature blocking it"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public RabidElephant(final RabidElephant card) { diff --git a/Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java b/Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java new file mode 100644 index 0000000000..64237b8e0e --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java @@ -0,0 +1,75 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; +import mage.abilities.hint.common.MyTurnHint; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterLandCard; + +import java.util.UUID; + +/** + * @author arcox + */ +public final class RadhaHeartOfKeld extends CardImpl { + private static final FilterCard filter = new FilterLandCard("play land cards"); + + public RadhaHeartOfKeld(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // As long as it's your turn, Radha, Heart of Keld has first strike. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield), + MyTurnCondition.instance, "As long as it's your turn, {this} has first strike." + )).addHint(MyTurnHint.instance)); + + // You may look at the top card of your library any time, and you may play lands from the top of your library. + LookAtTopCardOfLibraryAnyTimeEffect lookEffect = new LookAtTopCardOfLibraryAnyTimeEffect(); + lookEffect.overrideRuleText("You may look at the top card of your library any time, "); + PlayTheTopCardEffect playEffect = new PlayTheTopCardEffect(filter); + playEffect.overrideRuleText("and you may play lands from the top of your library"); + + SimpleStaticAbility lookAndPlayAbility = new SimpleStaticAbility(lookEffect); + lookAndPlayAbility.addEffect(playEffect); + this.addAbility(lookAndPlayAbility); + + // 4RG: Radha gets +X/+X until end of turn, where X is the number of lands you control. + DynamicValue controlledLands = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS); + BoostSourceEffect bse = new BoostSourceEffect(controlledLands, controlledLands, Duration.EndOfTurn, true); + bse.overrideRuleText("Radha gets +X/+X until end of turn, where X is the number of lands you control"); + this.addAbility(new SimpleActivatedAbility(bse, new ManaCostsImpl("{4}{R}{G}"))); + } + + private RadhaHeartOfKeld(final RadhaHeartOfKeld card) { + super(card); + } + + @Override + public RadhaHeartOfKeld copy() { + return new RadhaHeartOfKeld(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RagDealer.java b/Mage.Sets/src/mage/cards/r/RagDealer.java index a10c1d4138..0f97076fed 100644 --- a/Mage.Sets/src/mage/cards/r/RagDealer.java +++ b/Mage.Sets/src/mage/cards/r/RagDealer.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -13,11 +11,12 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInASingleGraveyard; +import java.util.UUID; + /** - * * @author LevelX, North */ public final class RagDealer extends CardImpl { @@ -33,7 +32,7 @@ public final class RagDealer extends CardImpl { // {2}{B}, {T}: Exile up to three target cards from a single graveyard. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new ManaCostsImpl("{2}{B}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("cards from a single graveyard"))); + ability.addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RageForger.java b/Mage.Sets/src/mage/cards/r/RageForger.java index 3af9a97727..15d35f91ac 100644 --- a/Mage.Sets/src/mage/cards/r/RageForger.java +++ b/Mage.Sets/src/mage/cards/r/RageForger.java @@ -86,7 +86,7 @@ class RageForgerDamageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent attackingCreature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent attackingCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && attackingCreature != null) { game.damagePlayerOrPlaneswalker(source.getFirstTarget(), 1, attackingCreature.getId(), game, false, true); return true; diff --git a/Mage.Sets/src/mage/cards/r/RagingGorilla.java b/Mage.Sets/src/mage/cards/r/RagingGorilla.java index 8b11fd21f7..5c5df5d7f6 100644 --- a/Mage.Sets/src/mage/cards/r/RagingGorilla.java +++ b/Mage.Sets/src/mage/cards/r/RagingGorilla.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class RagingGorilla extends CardImpl { this.toughness = new MageInt(3); // Whenever Raging Gorilla blocks or becomes blocked, it gets +2/-2 until end of turn. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(2, -2, Duration.EndOfTurn), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(2, -2, Duration.EndOfTurn), false)); } public RagingGorilla(final RagingGorilla card) { diff --git a/Mage.Sets/src/mage/cards/r/RaidersWake.java b/Mage.Sets/src/mage/cards/r/RaidersWake.java index 3fafbd93f9..e99f74624d 100644 --- a/Mage.Sets/src/mage/cards/r/RaidersWake.java +++ b/Mage.Sets/src/mage/cards/r/RaidersWake.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.DiscardsACardOpponentTriggeredAbility; @@ -9,16 +7,19 @@ import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SetTargetPointer; import mage.constants.TargetController; import mage.target.common.TargetOpponent; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class RaidersWake extends CardImpl { @@ -29,11 +30,13 @@ public final class RaidersWake extends CardImpl { // Whenever an opponent discards a card, that player loses 2 life. this.addAbility(new DiscardsACardOpponentTriggeredAbility(new LoseLifeTargetEffect(2), false, SetTargetPointer.PLAYER)); - // Raid — At the beginning of your end step, if you attacked with a creature this turn, target opponent discards a card. + // Raid — At the beginning of your end step, if you attacked this turn, target opponent discards a card. Ability ability = new ConditionalInterveningIfTriggeredAbility( new BeginningOfEndStepTriggeredAbility(new DiscardTargetEffect(1), TargetController.YOU, false), RaidCondition.instance, - "Raid — At the beginning of your end step, if you attacked with a creature this turn, target opponent discards a card."); + "Raid — At the beginning of your end step, if you attacked this turn, target opponent discards a card."); ability.addTarget(new TargetOpponent()); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java b/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java index 37f5452921..e47929beac 100644 --- a/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java +++ b/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java @@ -15,10 +15,10 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; -import mage.game.stack.Spell; import mage.util.CardUtil; import java.util.UUID; +import mage.cards.Card; /** * @author LevelX2 @@ -105,10 +105,8 @@ class RakdosLordOfRiotsCostReductionEffect extends CostModificationEffectImpl { Ability spellAbility = abilityToModify; if (spellAbility != null) { int amount = OpponentsLostLifeCount.instance.calculate(game, source, this); - if (amount > 0) { - CardUtil.reduceCost(spellAbility, amount); - return true; - } + CardUtil.reduceCost(spellAbility, amount); + return true; } return false; } @@ -117,9 +115,9 @@ class RakdosLordOfRiotsCostReductionEffect extends CostModificationEffectImpl { public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify instanceof SpellAbility) { if (abilityToModify.isControlledBy(source.getControllerId())) { - Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); - if (spell != null) { - return spell.isCreature(); + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + if (spellCard != null) { + return spellCard.isCreature(); } } } diff --git a/Mage.Sets/src/mage/cards/r/RakdosRagemutt.java b/Mage.Sets/src/mage/cards/r/RakdosRagemutt.java index e4c1a606b2..90171b0a0c 100644 --- a/Mage.Sets/src/mage/cards/r/RakdosRagemutt.java +++ b/Mage.Sets/src/mage/cards/r/RakdosRagemutt.java @@ -19,7 +19,7 @@ public final class RakdosRagemutt extends CardImpl { public RakdosRagemutt(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{R}"); this.subtype.add(SubType.ELEMENTAL); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/r/RakingClaws.java b/Mage.Sets/src/mage/cards/r/RakingClaws.java new file mode 100644 index 0000000000..334c12d571 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RakingClaws.java @@ -0,0 +1,41 @@ +package mage.cards.r; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RakingClaws extends CardImpl { + + public RakingClaws(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Target creature gains double strike until end of turn. + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + DoubleStrikeAbility.getInstance(), Duration.EndOfTurn + )); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private RakingClaws(final RakingClaws card) { + super(card); + } + + @Override + public RakingClaws copy() { + return new RakingClaws(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RakishHeir.java b/Mage.Sets/src/mage/cards/r/RakishHeir.java index 37de821632..a722a6cae7 100644 --- a/Mage.Sets/src/mage/cards/r/RakishHeir.java +++ b/Mage.Sets/src/mage/cards/r/RakishHeir.java @@ -74,7 +74,7 @@ class RakishHeirTriggeredAbility extends TriggeredAbilityImpl { && permanent.hasSubtype(SubType.VAMPIRE, game) && permanent.isControlledBy(controllerId)) { this.getEffects().clear(); AddCountersTargetEffect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); this.addEffect(effect); return true; } diff --git a/Mage.Sets/src/mage/cards/r/RakshasasSecret.java b/Mage.Sets/src/mage/cards/r/RakshasasSecret.java index 0b62846b48..2f2ea93bda 100644 --- a/Mage.Sets/src/mage/cards/r/RakshasasSecret.java +++ b/Mage.Sets/src/mage/cards/r/RakshasasSecret.java @@ -1,8 +1,5 @@ - package mage.cards.r; -import java.util.UUID; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; @@ -10,25 +7,23 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author emerald000 */ public final class RakshasasSecret extends CardImpl { public RakshasasSecret(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Target opponent discards two cards. Put the top two cards of your library into your graveyard. this.getSpellAbility().addEffect(new DiscardTargetEffect(2)); this.getSpellAbility().addTarget(new TargetOpponent()); - Effect effect = new PutTopCardOfLibraryIntoGraveControllerEffect(2); - effect.setText("Put the top two cards of your library into your graveyard"); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new PutTopCardOfLibraryIntoGraveControllerEffect(2).setText("Mill two cards.")); } - public RakshasasSecret(final RakshasasSecret card) { + private RakshasasSecret(final RakshasasSecret card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/r/RamThrough.java b/Mage.Sets/src/mage/cards/r/RamThrough.java new file mode 100644 index 0000000000..18f8568f20 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RamThrough.java @@ -0,0 +1,94 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +import static mage.game.combat.CombatGroup.getLethalDamage; + +/** + * @author TheElk801 + */ +public final class RamThrough extends CardImpl { + + public RamThrough(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Target creature you control deals damage equal to its power to target creature you don't control. If the creature you control has trample, excess damage is dealt to that creature's controller instead. + this.getSpellAbility().addEffect(new RamThroughEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + } + + private RamThrough(final RamThrough card) { + super(card); + } + + @Override + public RamThrough copy() { + return new RamThrough(this); + } +} + +class RamThroughEffect extends OneShotEffect { + + RamThroughEffect() { + super(Outcome.Benefit); + staticText = "Target creature you control deals damage equal to its power to target creature you don't control. " + + "If the creature you control has trample, excess damage is dealt to that creature's controller instead."; + } + + private RamThroughEffect(final RamThroughEffect effect) { + super(effect); + } + + @Override + public RamThroughEffect copy() { + return new RamThroughEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (source.getTargets().size() != 2) { + throw new IllegalStateException("It must have two targets, but found " + source.getTargets().size()); + } + + Permanent myPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + Permanent anotherPermanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + + if (myPermanent == null || anotherPermanent == null) { + return false; + } + int power = myPermanent.getPower().getValue(); + if (power < 1) { + return false; + } + if (!myPermanent.getAbilities().containsKey(TrampleAbility.getInstance().getId())) { + return anotherPermanent.damage(power, myPermanent.getId(), game, false, true) > 0; + } + int lethal = getLethalDamage(anotherPermanent, game); + if (myPermanent.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { + lethal = Math.min(lethal, 1); + } + lethal = Math.min(lethal, power); + anotherPermanent.damage(lethal, myPermanent.getId(), game); + Player player = game.getPlayer(anotherPermanent.getControllerId()); + if (player != null && lethal < power) { + player.damage(power - lethal, myPermanent.getId(), game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RambunctiousMutt.java b/Mage.Sets/src/mage/cards/r/RambunctiousMutt.java new file mode 100644 index 0000000000..f751f3d529 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RambunctiousMutt.java @@ -0,0 +1,51 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RambunctiousMutt extends CardImpl { + + private static final FilterPermanent filter + = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment an opponent controls"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public RambunctiousMutt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); + + this.subtype.add(SubType.DOG); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When Rambunctious Mutt enters the battlefield, destroy target artifact or enchantment an opponent controls. + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private RambunctiousMutt(final RambunctiousMutt card) { + super(card); + } + + @Override + public RambunctiousMutt copy() { + return new RambunctiousMutt(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RangeTrooper.java b/Mage.Sets/src/mage/cards/r/RangeTrooper.java index d3606f0131..3900345377 100644 --- a/Mage.Sets/src/mage/cards/r/RangeTrooper.java +++ b/Mage.Sets/src/mage/cards/r/RangeTrooper.java @@ -1,6 +1,5 @@ package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -11,22 +10,23 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author NinthWorld */ public final class RangeTrooper extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Trooper creatures you control"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Trooper creatures"); static { filter.add(SubType.TROOPER.getPredicate()); @@ -34,14 +34,14 @@ public final class RangeTrooper extends CardImpl { public RangeTrooper(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); - + this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.TROOPER); this.power = new MageInt(2); this.toughness = new MageInt(2); // Trooper creatures you control have "When this creature enters that battlefield, you may exile target creature. Return that creature to the battlefield at the beginning of the next end step." - Ability ability = new EntersBattlefieldTriggeredAbility(new RangeTrooperEffect(), true, true); + Ability ability = new EntersBattlefieldTriggeredAbility(new RangeTrooperEffect(), true, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(ability, Duration.WhileOnBattlefield, filter, false))); @@ -61,7 +61,7 @@ class RangeTrooperEffect extends OneShotEffect { public RangeTrooperEffect() { super(Outcome.Detriment); - staticText = "When this creature enters the battlefield, you may exile target creature. Return that creature to the battlefield at the beginning of the next end step"; + staticText = "exile target creature. Return that creature to the battlefield at the beginning of the next end step"; } public RangeTrooperEffect(final RangeTrooperEffect effect) { @@ -74,7 +74,7 @@ class RangeTrooperEffect extends OneShotEffect { MageObject sourceObject = game.getObject(source.getSourceId()); if (permanent != null && sourceObject != null) { if (permanent.moveToExile(source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game)) { - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/r/Ransack.java b/Mage.Sets/src/mage/cards/r/Ransack.java index 4fe3889c39..c85c89cda3 100644 --- a/Mage.Sets/src/mage/cards/r/Ransack.java +++ b/Mage.Sets/src/mage/cards/r/Ransack.java @@ -1,87 +1,87 @@ -package mage.cards.r; - -import static java.lang.Integer.min; -import java.util.Set; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; -import mage.target.TargetPlayer; - -/** - * - * @author jeffwadsworth - */ -public final class Ransack extends CardImpl { - - public Ransack(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); - - // Look at the top five cards of target player's library. - //Put any number of them on the bottom of that library in any order - //and the rest on top of the library in any order. - this.getSpellAbility().addEffect(new RansackEffect()); - this.getSpellAbility().addTarget(new TargetPlayer()); - - } - - public Ransack(final Ransack card) { - super(card); - } - - @Override - public Ransack copy() { - return new Ransack(this); - } -} - -class RansackEffect extends OneShotEffect { - - public RansackEffect() { - super(Outcome.Detriment); - this.staticText = "Look at the top five cards of target player's library. " - + "Put any number of them on the bottom of that library in any order " - + "and the rest on top of the library in any order"; - } - - public RansackEffect(final RansackEffect effect) { - super(effect); - } - - @Override - public RansackEffect copy() { - return new RansackEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); - FilterCard filter = new FilterCard("cards to put on the bottom of your library"); - if (player != null) { - int number = min(player.getLibrary().size(), 5); - Set cards = player.getLibrary().getTopCards(game, number); - Cards cardsRemaining = new CardsImpl(); - cardsRemaining.addAll(cards); - TargetCard target = new TargetCard(0, number, Zone.LIBRARY, filter); - if (player.choose(Outcome.DrawCard, cardsRemaining, target, game)) { - Cards pickedCards = new CardsImpl(target.getTargets()); - cardsRemaining.removeAll(pickedCards); - player.putCardsOnBottomOfLibrary(pickedCards, game, source, true); - player.putCardsOnTopOfLibrary(cardsRemaining, game, source, true); - return true; - } - } - return false; - } -} +package mage.cards.r; + +import static java.lang.Integer.min; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.TargetPlayer; + +/** + * + * @author jeffwadsworth + */ +public final class Ransack extends CardImpl { + + public Ransack(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); + + // Look at the top five cards of target player's library. + //Put any number of them on the bottom of that library in any order + //and the rest on top of the library in any order. + this.getSpellAbility().addEffect(new RansackEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); + + } + + public Ransack(final Ransack card) { + super(card); + } + + @Override + public Ransack copy() { + return new Ransack(this); + } +} + +class RansackEffect extends OneShotEffect { + + public RansackEffect() { + super(Outcome.Detriment); + this.staticText = "Look at the top five cards of target player's library. " + + "Put any number of them on the bottom of that library in any order " + + "and the rest on top of the library in any order"; + } + + public RansackEffect(final RansackEffect effect) { + super(effect); + } + + @Override + public RansackEffect copy() { + return new RansackEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + FilterCard filter = new FilterCard("cards to put on the bottom of your library"); + if (player != null) { + int number = min(player.getLibrary().size(), 5); + Set cards = player.getLibrary().getTopCards(game, number); + Cards cardsRemaining = new CardsImpl(); + cardsRemaining.addAll(cards); + TargetCard target = new TargetCard(0, number, Zone.LIBRARY, filter); + if (player.choose(Outcome.DrawCard, cardsRemaining, target, game)) { + Cards pickedCards = new CardsImpl(target.getTargets()); + cardsRemaining.removeAll(pickedCards); + player.putCardsOnBottomOfLibrary(pickedCards, game, source, true); + player.putCardsOnTopOfLibrary(cardsRemaining, game, source, true); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RapidDecay.java b/Mage.Sets/src/mage/cards/r/RapidDecay.java index 459a1d0596..f83aeca414 100644 --- a/Mage.Sets/src/mage/cards/r/RapidDecay.java +++ b/Mage.Sets/src/mage/cards/r/RapidDecay.java @@ -1,18 +1,17 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInASingleGraveyard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class RapidDecay extends CardImpl { @@ -22,7 +21,7 @@ public final class RapidDecay extends CardImpl { // Exile up to three target cards from a single graveyard. this.getSpellAbility().addEffect(new ExileTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("cards from a single graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS)); // Cycling {2} this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); diff --git a/Mage.Sets/src/mage/cards/r/RapidHybridization.java b/Mage.Sets/src/mage/cards/r/RapidHybridization.java index 5c157eeb92..79ed435dee 100644 --- a/Mage.Sets/src/mage/cards/r/RapidHybridization.java +++ b/Mage.Sets/src/mage/cards/r/RapidHybridization.java @@ -57,7 +57,7 @@ class RapidHybridizationEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(targetPointer.getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { FrogLizardToken token = new FrogLizardToken(); token.putOntoBattlefield(1, game, source.getSourceId(), permanent.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/r/RareBGone.java b/Mage.Sets/src/mage/cards/r/RareBGone.java index bb97dfceb0..5440a478f8 100644 --- a/Mage.Sets/src/mage/cards/r/RareBGone.java +++ b/Mage.Sets/src/mage/cards/r/RareBGone.java @@ -2,23 +2,18 @@ package mage.cards.r; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicate; -import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import java.util.Objects; -import java.util.Set; import java.util.UUID; /** @@ -31,10 +26,9 @@ public final class RareBGone extends CardImpl { // Each player sacrifices all permanents that are rare or mythic rare, then each player reveals their hand and discards all cards that are rare or mythic rare. this.getSpellAbility().addEffect(new RareBGoneEffect()); - } - public RareBGone(final RareBGone card) { + private RareBGone(final RareBGone card) { super(card); } @@ -50,22 +44,17 @@ class RareBGoneEffect extends OneShotEffect { private static final FilterCard filterCard = new FilterCard(); static { - filterPermanent.add(Predicates.or( - new RarityPredicate(Rarity.RARE), - new RarityPredicate(Rarity.MYTHIC) - )); - filterCard.add(Predicates.or( - new RarityPredicate(Rarity.RARE), - new RarityPredicate(Rarity.MYTHIC) - )); + filterPermanent.add(RareBGonePredicate.instance); + filterCard.add(RareBGonePredicate.instance); } - public RareBGoneEffect() { + RareBGoneEffect() { super(Outcome.Benefit); - this.staticText = "Each player sacrifices all permanents that are rare or mythic rare, then each player reveals their hand and discards all cards that are rare or mythic rare"; + this.staticText = "Each player sacrifices all permanents that are rare or mythic rare, " + + "then each player reveals their hand and discards all cards that are rare or mythic rare"; } - public RareBGoneEffect(final RareBGoneEffect effect) { + private RareBGoneEffect(final RareBGoneEffect effect) { super(effect); } @@ -77,44 +66,29 @@ class RareBGoneEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterPermanent, playerId, game)) { - permanent.sacrifice(source.getSourceId(), game); - } - Cards hand = player.getHand(); - player.revealCards("Rare-B-Gone", hand, game); - Set cards = hand.getCards(game); - for (Card card : cards) { - if (card != null && filterCard.match(card, game)) { - player.discard(card, source, game); - } - } - } - } - return true; + if (controller == null) { + return false; } - return false; + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + } + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterPermanent, playerId, game)) { + permanent.sacrifice(source.getSourceId(), game); + } + Cards cards = player.getHand(); + player.revealCards(source, cards, game); + player.discard(new CardsImpl(cards.getCards(filterCard, game)), source, game); + } + return true; } } -class RarityPredicate implements Predicate { - - private final Rarity rarity; - - public RarityPredicate(Rarity rarity) { - this.rarity = rarity; - } +enum RareBGonePredicate implements Predicate { + instance; @Override public boolean apply(Card input, Game game) { - return Objects.equals(input.getRarity(), rarity); - } - - @Override - public String toString() { - return "Rarity(" + rarity + ')'; + return Objects.equals(input.getRarity(), Rarity.RARE) || Objects.equals(input.getRarity(), Rarity.MYTHIC); } } diff --git a/Mage.Sets/src/mage/cards/r/RasputinDreamweaver.java b/Mage.Sets/src/mage/cards/r/RasputinDreamweaver.java index e3fc6822f8..2533fb1aeb 100644 --- a/Mage.Sets/src/mage/cards/r/RasputinDreamweaver.java +++ b/Mage.Sets/src/mage/cards/r/RasputinDreamweaver.java @@ -14,6 +14,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.PreventDamageToSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.SimpleManaAbility; @@ -47,7 +48,9 @@ public final class RasputinDreamweaver extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.DREAM.createInstance(7)), "seven dream counters on it")); // Remove a dream counter from Rasputin: Add {C}. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1), new RemoveCountersSourceCost(CounterType.DREAM.createInstance()))); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1), + new RemoveCountersSourceCost(CounterType.DREAM.createInstance()), + new CountersSourceCount(CounterType.DREAM))); // Remove a dream counter from Rasputin: Prevent the next 1 damage that would be dealt to Rasputin this turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventDamageToSourceEffect(Duration.EndOfTurn, 1), new RemoveCountersSourceCost(CounterType.DREAM.createInstance()))); diff --git a/Mage.Sets/src/mage/cards/r/RatsFeast.java b/Mage.Sets/src/mage/cards/r/RatsFeast.java index 395650b520..38bc8555eb 100644 --- a/Mage.Sets/src/mage/cards/r/RatsFeast.java +++ b/Mage.Sets/src/mage/cards/r/RatsFeast.java @@ -22,7 +22,7 @@ public final class RatsFeast extends CardImpl { // Exile X target cards from a single graveyard. this.getSpellAbility().addEffect(new ExileTargetEffect( - "Exile X target cards from a single graveyard.", true + "Exile X target cards from a single graveyard", true )); this.getSpellAbility().setTargetAdjuster(RatsFeastAdjuster.instance); } @@ -44,6 +44,6 @@ enum RatsFeastAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { int xValue = ability.getManaCostsToPay().getX(); ability.getTargets().clear(); - ability.addTarget(new TargetCardInASingleGraveyard(xValue, xValue, StaticFilters.FILTER_CARD)); + ability.addTarget(new TargetCardInASingleGraveyard(xValue, xValue, StaticFilters.FILTER_CARD_CARDS)); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/RaugrinCrystal.java b/Mage.Sets/src/mage/cards/r/RaugrinCrystal.java new file mode 100644 index 0000000000..0aec12f3ed --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RaugrinCrystal.java @@ -0,0 +1,39 @@ +package mage.cards.r; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RaugrinCrystal extends CardImpl { + + public RaugrinCrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add {U}, {R}, or {W}. + this.addAbility(new BlueManaAbility()); + this.addAbility(new RedManaAbility()); + this.addAbility(new WhiteManaAbility()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private RaugrinCrystal(final RaugrinCrystal card) { + super(card); + } + + @Override + public RaugrinCrystal copy() { + return new RaugrinCrystal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RaugrinTriome.java b/Mage.Sets/src/mage/cards/r/RaugrinTriome.java new file mode 100644 index 0000000000..8953e63b1a --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RaugrinTriome.java @@ -0,0 +1,48 @@ +package mage.cards.r; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RaugrinTriome extends CardImpl { + + public RaugrinTriome(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.ISLAND); + this.subtype.add(SubType.MOUNTAIN); + this.subtype.add(SubType.PLAINS); + + // ({T}: Add {U}, {R}, or {W}.) + this.addAbility(new BlueManaAbility()); + this.addAbility(new RedManaAbility()); + this.addAbility(new WhiteManaAbility()); + + // Raugrin Triome enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Cycling {3} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"))); + } + + private RaugrinTriome(final RaugrinTriome card) { + super(card); + } + + @Override + public RaugrinTriome copy() { + return new RaugrinTriome(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RavagerWurm.java b/Mage.Sets/src/mage/cards/r/RavagerWurm.java index 962702de23..f6214f280d 100644 --- a/Mage.Sets/src/mage/cards/r/RavagerWurm.java +++ b/Mage.Sets/src/mage/cards/r/RavagerWurm.java @@ -12,9 +12,8 @@ import mage.cards.CardSetInfo; import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -28,13 +27,10 @@ import java.util.UUID; public final class RavagerWurm extends CardImpl { private static final FilterPermanent filter - = new FilterCreaturePermanent("creature you don't control"); - private static final FilterPermanent filter2 = new FilterPermanent("land with an activated ability that isn't a mana ability"); static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - filter2.add(RavagerWurmPredicate.instance); + filter.add(RavagerWurmPredicate.instance); } public RavagerWurm(UUID ownerId, CardSetInfo setInfo) { @@ -52,13 +48,13 @@ public final class RavagerWurm extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility( new FightTargetSourceEffect().setText("{this} fights target creature you don't control"), false ); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); ability.getModes().setMinModes(0); ability.getModes().setMaxModes(1); // • Destroy target land with an activated ability that isn't a mana ability. Mode mode = new Mode(new DestroyTargetEffect()); - mode.addTarget(new TargetPermanent(filter2)); + mode.addTarget(new TargetPermanent(filter)); ability.addMode(mode); this.addAbility(ability); } @@ -78,14 +74,11 @@ enum RavagerWurmPredicate implements Predicate { @Override public boolean apply(Permanent input, Game game) { - if (input == null || !input.isLand()) { - return false; - } - for (Ability ability : input.getAbilities()) { - if (ability.getAbilityType() == AbilityType.ACTIVATED) { - return true; - } - } - return false; + return input != null && input.isLand() + && input + .getAbilities(game) + .stream() + .map(Ability::getAbilityType) + .anyMatch(AbilityType.ACTIVATED::equals); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java b/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java new file mode 100644 index 0000000000..53bc169dc8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java @@ -0,0 +1,134 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageMultiEffect; +import mage.abilities.effects.common.DevourEffect; +import mage.abilities.keyword.DevourAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanentAmount; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class RavenousGigantotherium extends CardImpl { + + public RavenousGigantotherium(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Devour 3 + this.addAbility(new DevourAbility(DevourEffect.DevourFactor.Devour3)); + + // When Ravenous Gigantotherium enters the battlefield, it deals X damage divided as you choose among up to X target creatures, where X is its power. Each of those creatures deals damage equal to its power to Ravenous Gigantotherium. + this.addAbility(new RavenousGigantotheriumAbility()); + } + + private RavenousGigantotherium(final RavenousGigantotherium card) { + super(card); + } + + @Override + public RavenousGigantotherium copy() { + return new RavenousGigantotherium(this); + } +} + +class RavenousGigantotheriumAbility extends EntersBattlefieldTriggeredAbility { + + RavenousGigantotheriumAbility() { + super(null, false); + } + + private RavenousGigantotheriumAbility(final RavenousGigantotheriumAbility ability) { + super(ability); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!super.checkTrigger(event, game)) { + return false; + } + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent == null) { + return false; + } + int power = Math.max(permanent.getPower().getValue(), 0); + this.getEffects().clear(); + this.addEffect(new DamageMultiEffect(power)); + this.addEffect(new RavenousGigantotheriumEffect()); + this.getTargets().clear(); + if (power < 1) { + return true; + } + this.addTarget(new TargetCreaturePermanentAmount(power)); + return true; + } + + @Override + public String getRule() { + return "When {this} enters the battlefield, it deals X damage " + + "divided as you choose among up to X target creatures, where X is its power. " + + "Each of those creatures deals damage equal to its power to {this}."; + } + + @Override + public RavenousGigantotheriumAbility copy() { + return new RavenousGigantotheriumAbility(this); + } +} + +class RavenousGigantotheriumEffect extends OneShotEffect { + + RavenousGigantotheriumEffect() { + super(Outcome.Benefit); + } + + private RavenousGigantotheriumEffect(final RavenousGigantotheriumEffect effect) { + super(effect); + } + + @Override + public RavenousGigantotheriumEffect copy() { + return new RavenousGigantotheriumEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePerm = game.getPermanent(source.getSourceId()); + if (sourcePerm == null) { + return false; + } + List permanentList = source + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + for (Permanent permanent : permanentList) { + sourcePerm.damage(permanent.getPower().getValue(), permanent.getId(), game); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java b/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java index dcdac69a32..b030d577db 100644 --- a/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java +++ b/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java @@ -85,7 +85,7 @@ class RayamiFirstOfTheFallenEffect extends ContinuousEffectImpl { || ability instanceof DoubleStrikeAbility || ability instanceof DeathtouchAbility || ability instanceof HasteAbility - || ability instanceof HexproofAbility + || ability instanceof HexproofBaseAbility || ability instanceof IndestructibleAbility || ability instanceof LifelinkAbility || ability instanceof MenaceAbility diff --git a/Mage.Sets/src/mage/cards/r/RazorclawBear.java b/Mage.Sets/src/mage/cards/r/RazorclawBear.java index 74057a10b8..1fe1cd2af4 100644 --- a/Mage.Sets/src/mage/cards/r/RazorclawBear.java +++ b/Mage.Sets/src/mage/cards/r/RazorclawBear.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class RazorclawBear extends CardImpl { // Whenever Razorclaw Bear becomes blocked, it gets +2/+2 until end of turn. Effect effect = new BoostSourceEffect(2, 2, Duration.EndOfTurn); effect.setText("it gets +2/+2 until end of turn"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public RazorclawBear(final RazorclawBear card) { diff --git a/Mage.Sets/src/mage/cards/r/RazorfieldRhino.java b/Mage.Sets/src/mage/cards/r/RazorfieldRhino.java index 28b09384e2..dcb832c7ce 100644 --- a/Mage.Sets/src/mage/cards/r/RazorfieldRhino.java +++ b/Mage.Sets/src/mage/cards/r/RazorfieldRhino.java @@ -1,37 +1,37 @@ - - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; + +import java.util.UUID; /** - * * @author Loki */ public final class RazorfieldRhino extends CardImpl { - public RazorfieldRhino (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{6}"); + public RazorfieldRhino(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}"); this.subtype.add(SubType.RHINO); this.power = new MageInt(4); this.toughness = new MageInt(4); + + // Metalcraft — Razorfield Rhino gets +2/+2 as long as you control three or more artifacts. ContinuousEffect effect1 = new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect1, MetalcraftCondition.instance, "Metalcraft — Razorfield Rhino gets +2/+2 as long as you control three or more artifacts"))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect1, MetalcraftCondition.instance, "Metalcraft — Razorfield Rhino gets +2/+2 as long as you control three or more artifacts")) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance)); } - public RazorfieldRhino (final RazorfieldRhino card) { + public RazorfieldRhino(final RazorfieldRhino card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/r/ReadTheRunes.java b/Mage.Sets/src/mage/cards/r/ReadTheRunes.java index 407b3f7bc6..ed406a3715 100644 --- a/Mage.Sets/src/mage/cards/r/ReadTheRunes.java +++ b/Mage.Sets/src/mage/cards/r/ReadTheRunes.java @@ -59,7 +59,7 @@ class ReadTheRunesEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - int drawnCards = controller.drawCards(source.getManaCostsToPay().getX(), game); + int drawnCards = controller.drawCards(source.getManaCostsToPay().getX(), source.getSourceId(), game); Target target = new TargetControlledPermanent(0, drawnCards, new FilterControlledPermanent(), true); controller.chooseTarget(Outcome.Sacrifice, target, source, game); int sacrificedPermanents = 0; diff --git a/Mage.Sets/src/mage/cards/r/ReadTheTides.java b/Mage.Sets/src/mage/cards/r/ReadTheTides.java new file mode 100644 index 0000000000..1c02d51448 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReadTheTides.java @@ -0,0 +1,39 @@ +package mage.cards.r; + +import mage.abilities.Mode; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReadTheTides extends CardImpl { + + public ReadTheTides(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{U}"); + + // Choose one — + // • Draw three cards. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3)); + + // • Return up to two target creatures to their owners' hands. + Mode mode = new Mode(new ReturnToHandTargetEffect()); + mode.addTarget(new TargetCreaturePermanent(0, 2)); + this.getSpellAbility().addMode(mode); + } + + private ReadTheTides(final ReadTheTides card) { + super(card); + } + + @Override + public ReadTheTides copy() { + return new ReadTheTides(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RealityShift.java b/Mage.Sets/src/mage/cards/r/RealityShift.java index f50600a417..2aa4d4b22c 100644 --- a/Mage.Sets/src/mage/cards/r/RealityShift.java +++ b/Mage.Sets/src/mage/cards/r/RealityShift.java @@ -60,7 +60,7 @@ class RealityShiftEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent targetCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (targetCreature != null) { Effect effect = new ManifestTargetPlayerEffect(1, "Its controller"); effect.setTargetPointer(new FixedTarget(targetCreature.getControllerId())); diff --git a/Mage.Sets/src/mage/cards/r/RebornHero.java b/Mage.Sets/src/mage/cards/r/RebornHero.java index eafb801ffa..c0f824c082 100644 --- a/Mage.Sets/src/mage/cards/r/RebornHero.java +++ b/Mage.Sets/src/mage/cards/r/RebornHero.java @@ -4,7 +4,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CardsInControllerGraveCondition; import mage.abilities.costs.mana.ManaCostsImpl; @@ -41,7 +41,7 @@ public final class RebornHero extends CardImpl { Ability ability = new SimpleStaticAbility( Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new GainAbilitySourceEffect(new DiesTriggeredAbility(new DoIfCostPaid( + new GainAbilitySourceEffect(new DiesSourceTriggeredAbility(new DoIfCostPaid( new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl("{W}{W}") ))), new CardsInControllerGraveCondition(7), diff --git a/Mage.Sets/src/mage/cards/r/Rebound.java b/Mage.Sets/src/mage/cards/r/Rebound.java index 5afdee305d..8fe2a1e5b4 100644 --- a/Mage.Sets/src/mage/cards/r/Rebound.java +++ b/Mage.Sets/src/mage/cards/r/Rebound.java @@ -1,77 +1,77 @@ -package mage.cards.r; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.filter.FilterSpell; -import mage.filter.predicate.other.TargetsOnlyOnePlayerPredicate; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.players.Player; -import mage.target.TargetPlayer; -import mage.target.TargetSpell; - -/** - * - * @author jeffwadsworth - */ -public final class Rebound extends CardImpl { - - public Rebound(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); - - // Change the target of target spell that targets only a player. The new target must be a player. - this.getSpellAbility().addEffect(new ReboundEffect()); - FilterSpell filter = new FilterSpell("spell that targets only a player"); - filter.add(new TargetsOnlyOnePlayerPredicate()); - this.getSpellAbility().addTarget(new TargetSpell(filter)); - - } - - public Rebound(final Rebound card) { - super(card); - } - - @Override - public Rebound copy() { - return new Rebound(this); - } -} - -class ReboundEffect extends OneShotEffect { - - public ReboundEffect() { - super(Outcome.Neutral); - this.staticText = "Change the target of target spell that targets only a player. The new target must be a player"; - } - - public ReboundEffect(final ReboundEffect effect) { - super(effect); - } - - @Override - public ReboundEffect copy() { - return new ReboundEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Spell spell = game.getStack().getSpell(source.getFirstTarget()); - Player controller = game.getPlayer(source.getControllerId()); - if (spell != null - && controller != null) { - spell.getSpellAbility().getTargets().clear(); - TargetPlayer targetPlayer = new TargetPlayer(); - if (controller.choose(Outcome.Neutral, targetPlayer, source.getSourceId(), game)) { - spell.getSpellAbility().addTarget(targetPlayer); - game.informPlayers("The target of the spell was changed to " + targetPlayer.getTargetedName(game)); - return true; - } - } - return false; - } -} +package mage.cards.r; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.FilterSpell; +import mage.filter.predicate.other.TargetsOnlyOnePlayerPredicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.TargetSpell; + +/** + * + * @author jeffwadsworth + */ +public final class Rebound extends CardImpl { + + public Rebound(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Change the target of target spell that targets only a player. The new target must be a player. + this.getSpellAbility().addEffect(new ReboundEffect()); + FilterSpell filter = new FilterSpell("spell that targets only a player"); + filter.add(new TargetsOnlyOnePlayerPredicate()); + this.getSpellAbility().addTarget(new TargetSpell(filter)); + + } + + public Rebound(final Rebound card) { + super(card); + } + + @Override + public Rebound copy() { + return new Rebound(this); + } +} + +class ReboundEffect extends OneShotEffect { + + public ReboundEffect() { + super(Outcome.Neutral); + this.staticText = "Change the target of target spell that targets only a player. The new target must be a player"; + } + + public ReboundEffect(final ReboundEffect effect) { + super(effect); + } + + @Override + public ReboundEffect copy() { + return new ReboundEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = game.getStack().getSpell(source.getFirstTarget()); + Player controller = game.getPlayer(source.getControllerId()); + if (spell != null + && controller != null) { + spell.getSpellAbility().getTargets().clear(); + TargetPlayer targetPlayer = new TargetPlayer(); + if (controller.choose(Outcome.Neutral, targetPlayer, source.getSourceId(), game)) { + spell.getSpellAbility().addTarget(targetPlayer); + game.informPlayers("The target of the spell was changed to " + targetPlayer.getTargetedName(game)); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RecklessRage.java b/Mage.Sets/src/mage/cards/r/RecklessRage.java index d6a16ebcc6..452aeb7888 100644 --- a/Mage.Sets/src/mage/cards/r/RecklessRage.java +++ b/Mage.Sets/src/mage/cards/r/RecklessRage.java @@ -1,16 +1,16 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.constants.CardType; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.SecondTargetPointer; +import java.util.UUID; + /** * @author JayDi85 */ @@ -20,9 +20,7 @@ public final class RecklessRage extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); // Reckless Rage deals 4 damage to target creature you don't control and 2 damage to target creature you control. - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.getSpellAbility().addEffect(new DamageTargetEffect(4).setUseOnlyTargetPointer(true)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addEffect(new DamageTargetEffect(2).setUseOnlyTargetPointer(true) @@ -30,7 +28,7 @@ public final class RecklessRage extends CardImpl { .setTargetPointer(new SecondTargetPointer())); } - public RecklessRage(final RecklessRage card) { + private RecklessRage(final RecklessRage card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/r/ReconnaissanceMission.java b/Mage.Sets/src/mage/cards/r/ReconnaissanceMission.java new file mode 100644 index 0000000000..a435aa9e8d --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReconnaissanceMission.java @@ -0,0 +1,42 @@ +package mage.cards.r; + +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReconnaissanceMission extends CardImpl { + + public ReconnaissanceMission(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); + + // Whenever a creature you control deals combat damage to a player, you may draw a card. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_CREATURE, + true, SetTargetPointer.NONE, true + )); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private ReconnaissanceMission(final ReconnaissanceMission card) { + super(card); + } + + @Override + public ReconnaissanceMission copy() { + return new ReconnaissanceMission(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RecurringInsight.java b/Mage.Sets/src/mage/cards/r/RecurringInsight.java index 9fa6dd3ff9..adb2967212 100644 --- a/Mage.Sets/src/mage/cards/r/RecurringInsight.java +++ b/Mage.Sets/src/mage/cards/r/RecurringInsight.java @@ -57,7 +57,7 @@ class RecurringInsightEffect extends OneShotEffect { if (controller != null) { Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); if (opponent != null) { - controller.drawCards(opponent.getHand().size(), game); + controller.drawCards(opponent.getHand().size(), source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/RedManaBattery.java b/Mage.Sets/src/mage/cards/r/RedManaBattery.java index 1281c0d257..36a08f3c7e 100644 --- a/Mage.Sets/src/mage/cards/r/RedManaBattery.java +++ b/Mage.Sets/src/mage/cards/r/RedManaBattery.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -28,18 +27,20 @@ public final class RedManaBattery extends CardImpl { public RedManaBattery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {2}, {tap}: Put a charge counter on Red Mana Battery. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance(1)), new GenericManaCost(2)); + // {2}, {T}: Put a charge counter on Red Mana Battery. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); this.addAbility(ability); - // {tap}, Remove any number of charge counters from Red Mana Battery: Add {R}, then add an additional {R} for each charge counter removed this way. + // {T}, Remove any number of charge counters from Red Mana Battery: Add {R}, + // then add an additional {R} for each charge counter removed this way. ability = new DynamicManaAbility( Mana.RedMana(1), new IntPlusDynamicValue(1, RemovedCountersForCostValue.instance), new TapSourceCost(), "Add {R}, then add {R} for each charge counter removed this way", - true, new CountersSourceCount(CounterType.CHARGE)); + true, new IntPlusDynamicValue(1, new CountersSourceCount(CounterType.CHARGE))); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE.createInstance(), "Remove any number of charge counters from {this}")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/r/ReefPirates.java b/Mage.Sets/src/mage/cards/r/ReefPirates.java index 970faaf4ad..b74cbde8fe 100644 --- a/Mage.Sets/src/mage/cards/r/ReefPirates.java +++ b/Mage.Sets/src/mage/cards/r/ReefPirates.java @@ -26,7 +26,7 @@ public final class ReefPirates extends CardImpl { // Whenever Reef Pirates deals damage to an opponent, that player puts the top card of their library into their graveyard. Effect effect = new PutLibraryIntoGraveTargetEffect(1); - effect.setText("that player puts the top card of their library into their graveyard"); + effect.setText("that player mills a card"); this.addAbility(new DealsDamageToAPlayerTriggeredAbility(effect, false, true)); } diff --git a/Mage.Sets/src/mage/cards/r/ReefWorm.java b/Mage.Sets/src/mage/cards/r/ReefWorm.java index 07707cac53..b54fed6a46 100644 --- a/Mage.Sets/src/mage/cards/r/ReefWorm.java +++ b/Mage.Sets/src/mage/cards/r/ReefWorm.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class ReefWorm extends CardImpl { this.toughness = new MageInt(1); // When Reef Worm dies, create a 3/3 blue Fish creature token with "When this creature dies, create a 6/6 blue Whale creature token with "When this creature dies, create a 9/9 blue Kraken creature token."" - addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ReefWormFishToken()))); + addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ReefWormFishToken()))); } public ReefWorm(final ReefWorm card) { diff --git a/Mage.Sets/src/mage/cards/r/ReflectorMage.java b/Mage.Sets/src/mage/cards/r/ReflectorMage.java index faabe8b477..8de4580f25 100644 --- a/Mage.Sets/src/mage/cards/r/ReflectorMage.java +++ b/Mage.Sets/src/mage/cards/r/ReflectorMage.java @@ -119,7 +119,7 @@ class ExclusionRitualReplacementEffect extends ContinuousRuleModifyingEffectImpl if (spell != null && spell.isFaceDown(game)) { return false; // Face Down cast spell (Morph creature) has no name } - return CardUtil.haveSameNames(card.getName(), creatureName) && Objects.equals(ownerId, card.getOwnerId()); + return CardUtil.haveSameNames(card, creatureName, game) && Objects.equals(ownerId, card.getOwnerId()); } return false; } diff --git a/Mage.Sets/src/mage/cards/r/RegalBehemoth.java b/Mage.Sets/src/mage/cards/r/RegalBehemoth.java index fdf945fef1..706c0d3b8f 100644 --- a/Mage.Sets/src/mage/cards/r/RegalBehemoth.java +++ b/Mage.Sets/src/mage/cards/r/RegalBehemoth.java @@ -89,7 +89,7 @@ class RegalBehemothTriggeredManaAbility extends TriggeredManaAbility { ManaEvent mEvent = (ManaEvent) event; for (Effect effect : getEffects()) { effect.setValue("mana", mEvent.getMana()); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/RegalLeosaur.java b/Mage.Sets/src/mage/cards/r/RegalLeosaur.java new file mode 100644 index 0000000000..da3993b7e8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RegalLeosaur.java @@ -0,0 +1,45 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RegalLeosaur extends CardImpl { + + public RegalLeosaur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}"); + + this.subtype.add(SubType.DINOSAUR); + this.subtype.add(SubType.CAT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Mutate {1}{R/W}{R/W} + this.addAbility(new MutateAbility(this, "{1}{R/W}{R/W}")); + + // Whenever this creature mutates, other creatures you control get +2/+1 until end of turn. + this.addAbility(new MutatesSourceTriggeredAbility( + new BoostControlledEffect(2, 1, Duration.EndOfTurn, false) + )); + } + + private RegalLeosaur(final RegalLeosaur card) { + super(card); + } + + @Override + public RegalLeosaur copy() { + return new RegalLeosaur(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/Regeneration.java b/Mage.Sets/src/mage/cards/r/Regeneration.java index 5629806f40..c77c82772c 100644 --- a/Mage.Sets/src/mage/cards/r/Regeneration.java +++ b/Mage.Sets/src/mage/cards/r/Regeneration.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -14,23 +12,24 @@ import mage.constants.*; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Plopman */ public final class Regeneration extends CardImpl { public Regeneration(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); this.subtype.add(SubType.AURA); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); + // {G}: Regenerate enchanted creature. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateAttachedEffect(AttachmentType.AURA), new ManaCostsImpl("{G}"))); } diff --git a/Mage.Sets/src/mage/cards/r/Regression.java b/Mage.Sets/src/mage/cards/r/Regression.java index bd4257d50b..8369966c5e 100644 --- a/Mage.Sets/src/mage/cards/r/Regression.java +++ b/Mage.Sets/src/mage/cards/r/Regression.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/r/ReiverDemon.java b/Mage.Sets/src/mage/cards/r/ReiverDemon.java index 695f4151a0..71167330d3 100644 --- a/Mage.Sets/src/mage/cards/r/ReiverDemon.java +++ b/Mage.Sets/src/mage/cards/r/ReiverDemon.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.keyword.FlyingAbility; @@ -43,7 +43,7 @@ public final class ReiverDemon extends CardImpl { // When Reiver Demon enters the battlefield, if you cast it from your hand, destroy all nonartifact, nonblack creatures. They can't be regenerated. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter, true), false), - CastFromHandSourceCondition.instance, + CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, destroy all nonartifact, nonblack creatures. They can't be regenerated."), new CastFromHandWatcher()); } diff --git a/Mage.Sets/src/mage/cards/r/RekindledFlame.java b/Mage.Sets/src/mage/cards/r/RekindledFlame.java index b294ef6821..a5592bde47 100644 --- a/Mage.Sets/src/mage/cards/r/RekindledFlame.java +++ b/Mage.Sets/src/mage/cards/r/RekindledFlame.java @@ -1,30 +1,26 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.Condition; +import mage.abilities.condition.common.OpponentHasNoCardsInHandCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.hint.ConditionHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class RekindledFlame extends CardImpl { - static final String rule = "if an opponent has no cards in hand, you may return Rekindled Flame from your graveyard to your hand"; - public RekindledFlame(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}"); @@ -34,10 +30,10 @@ public final class RekindledFlame extends CardImpl { // At the beginning of your upkeep, if an opponent has no cards in hand, you may return Rekindled Flame from your graveyard to your hand. Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), TargetController.YOU, true - ), - new OpponentHasNoCardsInHandCondition(), rule); + new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), TargetController.YOU, true), + OpponentHasNoCardsInHandCondition.instance, + "If an opponent has no cards in hand, you may return Rekindled Flame from your graveyard to your hand."); + ability.addHint(new ConditionHint(OpponentHasNoCardsInHandCondition.instance, "Opponent has no cards in hand")); ability.setRuleVisible(true); this.addAbility(ability); @@ -51,21 +47,4 @@ public final class RekindledFlame extends CardImpl { public RekindledFlame copy() { return new RekindledFlame(this); } -} - -class OpponentHasNoCardsInHandCondition implements Condition { - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - for (UUID playerId : game.getOpponents(source.getControllerId())) { - Player opponent = game.getPlayer(playerId); - if (opponent != null && opponent.getHand().isEmpty()) { - return true; - } - } - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/RekindlingPhoenix.java b/Mage.Sets/src/mage/cards/r/RekindlingPhoenix.java index 3eac6457e9..6528f7ae01 100644 --- a/Mage.Sets/src/mage/cards/r/RekindlingPhoenix.java +++ b/Mage.Sets/src/mage/cards/r/RekindlingPhoenix.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -29,7 +29,7 @@ public final class RekindlingPhoenix extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Rekindling Phoenix dies, create a 0/1 red Elemental creature token with "At the beginning of your upkeep, sacrifice this creature and return target card named Rekindling Phoenix from your graveyard to the battlefield. It gains haste until end of turn." - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new RekindlingPhoenixToken()), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new RekindlingPhoenixToken()), false)); } public RekindlingPhoenix(final RekindlingPhoenix card) { diff --git a/Mage.Sets/src/mage/cards/r/ReleaseTheDogs.java b/Mage.Sets/src/mage/cards/r/ReleaseTheDogs.java new file mode 100644 index 0000000000..6e7e8dc8b9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReleaseTheDogs.java @@ -0,0 +1,31 @@ +package mage.cards.r; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.WhiteDogToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReleaseTheDogs extends CardImpl { + + public ReleaseTheDogs(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}"); + + // Create four 1/1 white Dog creature tokens. + this.getSpellAbility().addEffect(new CreateTokenEffect(new WhiteDogToken(), 4)); + } + + private ReleaseTheDogs(final ReleaseTheDogs card) { + super(card); + } + + @Override + public ReleaseTheDogs copy() { + return new ReleaseTheDogs(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java b/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java index c8a9ebdf8d..cf4d704047 100644 --- a/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java +++ b/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java @@ -3,22 +3,18 @@ package mage.cards.r; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.TargetController; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetNonlandPermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -67,58 +63,7 @@ class ReleaseToTheWindEffect extends OneShotEffect { if (controller != null) { Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (targetPermanent != null) { - if (controller.moveCards(targetPermanent, Zone.EXILED, source, game)) { - Card card = game.getCard(targetPermanent.getId()); - if (card != null) { - ContinuousEffect effect = new ReleaseToTheWindEffectCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); - game.addEffect(effect, source); - } - } - } - return true; - } - return false; - } -} - -class ReleaseToTheWindEffectCastFromExileEffect extends AsThoughEffectImpl { - - public ReleaseToTheWindEffectCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - staticText = "For as long as that card remains exiled, its owner may cast it without paying its mana cost"; - } - - public ReleaseToTheWindEffectCastFromExileEffect(final ReleaseToTheWindEffectCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public ReleaseToTheWindEffectCastFromExileEffect copy() { - return new ReleaseToTheWindEffectCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - UUID ownerId = game.getOwnerId(objectId); - if (objectId.equals(getTargetPointer().getFirst(game, source))) { - if (affectedControllerId.equals(ownerId)) { - Card card = game.getCard(objectId); - Player player = game.getPlayer(ownerId); - if (player != null && card != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - return true; - } - } - } else { - if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) { - // object has moved zone so effect can be discarted - this.discard(); + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, targetPermanent, TargetController.OWNER, Duration.Custom, true); } } return false; diff --git a/Mage.Sets/src/mage/cards/r/RelentlessDead.java b/Mage.Sets/src/mage/cards/r/RelentlessDead.java index 063fd464b5..d9567943ba 100644 --- a/Mage.Sets/src/mage/cards/r/RelentlessDead.java +++ b/Mage.Sets/src/mage/cards/r/RelentlessDead.java @@ -2,7 +2,7 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DoIfCostPaid; @@ -37,10 +37,10 @@ public final class RelentlessDead extends CardImpl { this.addAbility(new MenaceAbility()); // When Relentless Dead dies, you may pay {B}. If you do, return it to its owner's hand. - this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new ReturnToHandSourceEffect(), new ManaCostsImpl("{B}")))); + this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new ReturnToHandSourceEffect(), new ManaCostsImpl("{B}")))); // When Relentless Dead dies, you may pay {X}. If you do, return another target Zombie creature card with converted mana cost X from your graveyard to the battlefield. - this.addAbility(new DiesTriggeredAbility(new RelentlessDeadEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new RelentlessDeadEffect())); } public RelentlessDead(final RelentlessDead card) { diff --git a/Mage.Sets/src/mage/cards/r/ReliquaryMonk.java b/Mage.Sets/src/mage/cards/r/ReliquaryMonk.java index 2e3e3ff044..5b254a277b 100644 --- a/Mage.Sets/src/mage/cards/r/ReliquaryMonk.java +++ b/Mage.Sets/src/mage/cards/r/ReliquaryMonk.java @@ -3,7 +3,7 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -29,7 +29,7 @@ public final class ReliquaryMonk extends CardImpl { this.toughness = new MageInt(2); // When Reliquary Monk dies, destroy target artifact or enchantment. - Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), false); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/Reminisce.java b/Mage.Sets/src/mage/cards/r/Reminisce.java index c34ec68fb5..c825c764bd 100644 --- a/Mage.Sets/src/mage/cards/r/Reminisce.java +++ b/Mage.Sets/src/mage/cards/r/Reminisce.java @@ -1,15 +1,12 @@ - package mage.cards.r; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; @@ -21,7 +18,7 @@ import mage.target.TargetPlayer; public final class Reminisce extends CardImpl { public Reminisce(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); // Target player shuffles their graveyard into their library. this.getSpellAbility().addEffect(new ReminisceEffect()); @@ -39,29 +36,26 @@ public final class Reminisce extends CardImpl { } class ReminisceEffect extends OneShotEffect { - + ReminisceEffect() { super(Outcome.Neutral); this.staticText = "Target player shuffles their graveyard into their library"; } - + ReminisceEffect(final ReminisceEffect effect) { super(effect); } - + @Override public ReminisceEffect copy() { return new ReminisceEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(this.getTargetPointer().getFirst(game, source)); if (player != null) { - for (Card card: player.getGraveyard().getCards(game)) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - player.shuffleLibrary(source, game); + player.shuffleCardsToLibrary(player.getGraveyard(), game, source); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/r/RenegadeDoppelganger.java b/Mage.Sets/src/mage/cards/r/RenegadeDoppelganger.java index a6c2585ab4..c6cf0e58e1 100644 --- a/Mage.Sets/src/mage/cards/r/RenegadeDoppelganger.java +++ b/Mage.Sets/src/mage/cards/r/RenegadeDoppelganger.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -10,9 +9,9 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -28,7 +27,7 @@ import mage.util.functions.EmptyApplyToPermanent; public final class RenegadeDoppelganger extends CardImpl { public RenegadeDoppelganger(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.SHAPESHIFTER); this.power = new MageInt(0); @@ -74,7 +73,7 @@ class RenegadeDoppelgangerTriggeredAbility extends TriggeredAbilityImpl { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null && permanent.isCreature() && permanent.isControlledBy(this.getControllerId())) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); } return true; } @@ -107,10 +106,7 @@ class RenegadeDoppelgangerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); - Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source)); - if (targetCreature == null) { - targetCreature = (Permanent) game.getLastKnownInformation(getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD); - } + Permanent targetCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (targetCreature == null || permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java b/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java index e017510a8c..b6e30492ec 100644 --- a/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java +++ b/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java @@ -122,8 +122,7 @@ class RepeatedReverberationEffect extends OneShotEffect { if (controller == null || sourcePermanent == null) { return false; } - stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); + stackAbility.createCopyOnStack(game, source, source.getControllerId(), true, 2); game.informPlayers(sourcePermanent.getIdName() + ": " + controller.getLogName() + " copied loyalty ability twice"); return true; } diff --git a/Mage.Sets/src/mage/cards/r/RepeatingBarrage.java b/Mage.Sets/src/mage/cards/r/RepeatingBarrage.java index f574aae4f8..530ea6ec3d 100644 --- a/Mage.Sets/src/mage/cards/r/RepeatingBarrage.java +++ b/Mage.Sets/src/mage/cards/r/RepeatingBarrage.java @@ -1,13 +1,12 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.common.RaidCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityWord; @@ -16,8 +15,9 @@ import mage.constants.Zone; import mage.target.common.TargetAnyTarget; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class RepeatingBarrage extends CardImpl { @@ -29,12 +29,13 @@ public final class RepeatingBarrage extends CardImpl { this.getSpellAbility().addEffect(new DamageTargetEffect(3)); this.getSpellAbility().addTarget(new TargetAnyTarget()); - // Raid — {3}{R}{R}: Return Repeating Barrage from your graveyard to your hand. Activate this ability only if you attacked with a creature this turn. + // Raid — {3}{R}{R}: Return Repeating Barrage from your graveyard to your hand. Activate this ability only if you attacked this turn. Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), new ManaCostsImpl("{3}{R}{R}"), RaidCondition.instance); ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/r/Repopulate.java b/Mage.Sets/src/mage/cards/r/Repopulate.java index 7ceaa5452a..4e51a3804f 100644 --- a/Mage.Sets/src/mage/cards/r/Repopulate.java +++ b/Mage.Sets/src/mage/cards/r/Repopulate.java @@ -1,17 +1,15 @@ package mage.cards.r; -import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.CyclingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; @@ -58,12 +56,9 @@ class RepopulateEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { - Set cards = player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game); - for (Card card : cards) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - player.shuffleLibrary(source, game); - return true; + return player.shuffleCardsToLibrary( + new CardsImpl(player.getGraveyard() + .getCards(StaticFilters.FILTER_CARD_CREATURE, game)), game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/r/Reprocess.java b/Mage.Sets/src/mage/cards/r/Reprocess.java index e25958e0f1..bb16d7dcb1 100644 --- a/Mage.Sets/src/mage/cards/r/Reprocess.java +++ b/Mage.Sets/src/mage/cards/r/Reprocess.java @@ -78,7 +78,7 @@ class ReprocessEffect extends OneShotEffect { amount++; } } - player.drawCards(amount, game); + player.drawCards(amount, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/ReptilianReflection.java b/Mage.Sets/src/mage/cards/r/ReptilianReflection.java new file mode 100644 index 0000000000..8393e07454 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReptilianReflection.java @@ -0,0 +1,61 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.game.permanent.token.TokenImpl; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReptilianReflection extends CardImpl { + + public ReptilianReflection(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + // Whenever you cycle a card, Reptilian Reflection becomes a 5/4 Dinosaur creature with trample and haste in addition to its other types until end of turn. + this.addAbility(new CycleControllerTriggeredAbility(new BecomesCreatureSourceEffect( + new ReptilianReflectionToken(), "enchantment", Duration.EndOfTurn + ).setText("have {this} become a 5/4 Dinosaur creature with trample and haste " + + "in addition to its other types until end of turn"), true)); + } + + private ReptilianReflection(final ReptilianReflection card) { + super(card); + } + + @Override + public ReptilianReflection copy() { + return new ReptilianReflection(this); + } +} + +class ReptilianReflectionToken extends TokenImpl { + + ReptilianReflectionToken() { + super("", "5/4 Dinosaur creature with trample and haste"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.DINOSAUR); + power = new MageInt(5); + toughness = new MageInt(4); + this.addAbility(TrampleAbility.getInstance()); + this.addAbility(HasteAbility.getInstance()); + } + + private ReptilianReflectionToken(final ReptilianReflectionToken token) { + super(token); + } + + public ReptilianReflectionToken copy() { + return new ReptilianReflectionToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RequiemAngel.java b/Mage.Sets/src/mage/cards/r/RequiemAngel.java index 20cfb3f4db..0ccb69b287 100644 --- a/Mage.Sets/src/mage/cards/r/RequiemAngel.java +++ b/Mage.Sets/src/mage/cards/r/RequiemAngel.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -16,8 +14,9 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** - * * @author intimidatingant */ public final class RequiemAngel extends CardImpl { @@ -30,7 +29,7 @@ public final class RequiemAngel extends CardImpl { } public RequiemAngel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}"); this.subtype.add(SubType.ANGEL); this.power = new MageInt(5); @@ -39,7 +38,7 @@ public final class RequiemAngel extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever another non-Spirit creature you control dies, create a 1/1 white Spirit creature token with flying. - this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken("ISD"), 1), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken(), 1), false, filter)); } public RequiemAngel(final RequiemAngel card) { diff --git a/Mage.Sets/src/mage/cards/r/RescueFromTheUnderworld.java b/Mage.Sets/src/mage/cards/r/RescueFromTheUnderworld.java index b5041281c0..8997a22c4a 100644 --- a/Mage.Sets/src/mage/cards/r/RescueFromTheUnderworld.java +++ b/Mage.Sets/src/mage/cards/r/RescueFromTheUnderworld.java @@ -15,7 +15,6 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.events.GameEvent; @@ -28,6 +27,7 @@ import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; +import mage.filter.StaticFilters; /** * Once you announce you're casting Rescue from the Underworld, no player may @@ -57,7 +57,7 @@ public final class RescueFromTheUnderworld extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}"); // As an additional cost to cast Rescue from the Underworld, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent("a creature"), false))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, false))); // Choose target creature card in your graveyard. Return that card and the sacrificed card to the battlefield under your control at the beginning of your next upkeep. Exile Rescue from the Underworld. this.getSpellAbility().addEffect(new RescueFromTheUnderworldTextEffect()); diff --git a/Mage.Sets/src/mage/cards/r/ResearchDevelopment.java b/Mage.Sets/src/mage/cards/r/ResearchDevelopment.java index d21da05926..54065c4f6b 100644 --- a/Mage.Sets/src/mage/cards/r/ResearchDevelopment.java +++ b/Mage.Sets/src/mage/cards/r/ResearchDevelopment.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.Set; @@ -6,23 +5,18 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; -import mage.cards.Card; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.cards.SplitCard; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SpellAbilityType; import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; -import mage.game.permanent.token.ElementalToken; +import mage.game.permanent.token.ResearchDevelopmentToken; import mage.players.Player; import mage.target.TargetCard; /** - * * @author magenoxx */ public final class ResearchDevelopment extends SplitCard { @@ -49,10 +43,6 @@ public final class ResearchDevelopment extends SplitCard { class ResearchEffect extends OneShotEffect { - private static final String choiceText = "Choose a card you own from outside the game"; - - private static final FilterCard filter = new FilterCard("card"); - public ResearchEffect() { super(Outcome.Benefit); this.staticText = "Choose up to four cards you own from outside the game and shuffle them into your library"; @@ -69,53 +59,20 @@ class ResearchEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - StringBuilder textToAsk = new StringBuilder(choiceText); - textToAsk.append(" (0)"); - int count = 0; - while (player.chooseUse(Outcome.Benefit, textToAsk.toString(), source, game)) { - Cards cards = player.getSideboard(); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + if (controller.chooseUse(Outcome.Benefit, staticText + "?", source, game)) { + Cards cards = controller.getSideboard(); if (cards.isEmpty()) { - game.informPlayer(player, "You have no cards outside the game."); - break; + game.informPlayer(controller, "You have no cards outside the game."); + return true; } - - Set filtered = cards.getCards(filter, game); - if (filtered.isEmpty()) { - game.informPlayer(player, "You have no " + filter.getMessage() + " outside the game."); - break; - } - - Cards filteredCards = new CardsImpl(); - for (Card card : filtered) { - filteredCards.add(card.getId()); - } - - TargetCard target = new TargetCard(Zone.OUTSIDE, filter); - if (player.choose(Outcome.Benefit, filteredCards, target, game)) { - Card card = player.getSideboard().get(target.getFirstTarget(), game); - if (card != null) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); - count++; - textToAsk = new StringBuilder(choiceText); - textToAsk.append(" ("); - textToAsk.append(count); - textToAsk.append(')'); - } - } - - if (count == 4) { - break; + TargetCard target = new TargetCard(0, 4, Zone.OUTSIDE, new FilterCard("cards you own from outside the game")); + target.setNotTarget(true); + if (controller.choose(Outcome.Benefit, controller.getSideboard(), target, game)) { + controller.shuffleCardsToLibrary(new CardsImpl(target.getTargets()), game, source); } } - - game.informPlayers(player.getLogName() + " has chosen " + count + " card(s) to shuffle into their library."); - - if (count > 0) { - player.shuffleLibrary(source, game); - } - return true; } @@ -144,15 +101,15 @@ class DevelopmentEffect extends OneShotEffect { for (UUID opponentUuid : opponents) { Player opponent = game.getPlayer(opponentUuid); if (opponent != null && opponent.chooseUse(Outcome.Detriment, - "Allow " + player.getLogName() + " to draw a card instead? (" + Integer.toString(i + 1) + ')', source, game)) { + "Allow " + player.getLogName() + " to draw a card instead? (" + (i + 1) + ')', source, game)) { game.informPlayers(opponent.getLogName() + " had chosen to let " + player.getLogName() + " draw a card."); - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); putToken = false; break; } } if (putToken) { - new CreateTokenEffect(new ElementalToken("DIS", 1)).apply(game, source); + new CreateTokenEffect(new ResearchDevelopmentToken()).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java b/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java index 80f127f700..2376752bd1 100644 --- a/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java +++ b/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java @@ -25,7 +25,7 @@ public final class ResoluteWatchdog extends CardImpl { public ResoluteWatchdog(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/r/RestlessDreams.java b/Mage.Sets/src/mage/cards/r/RestlessDreams.java index a2b83827f3..2859a7f51d 100644 --- a/Mage.Sets/src/mage/cards/r/RestlessDreams.java +++ b/Mage.Sets/src/mage/cards/r/RestlessDreams.java @@ -1,6 +1,7 @@ package mage.cards.r; import mage.abilities.Ability; +import mage.abilities.costs.Cost; import mage.abilities.costs.common.DiscardXTargetCost; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; @@ -8,14 +9,13 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureCard; import mage.game.Game; +import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetadjustment.TargetAdjuster; import java.util.UUID; -import mage.abilities.costs.Cost; -import mage.filter.common.FilterCreatureCard; -import mage.target.Target; /** * @author fireshoes @@ -26,7 +26,8 @@ public final class RestlessDreams extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}"); // As an additional cost to cast Restless Dreams, discard X cards. - this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, true)); + this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, false)); + // Return X target creature cards from your graveyard to your hand. Effect effect = new ReturnFromGraveyardToHandTargetEffect(); effect.setText("Return X target creature cards from your graveyard to your hand"); @@ -57,8 +58,8 @@ enum RestlessDreamsAdjuster implements TargetAdjuster { xValue = ((DiscardXTargetCost) cost).getAmount(); } } - Target target = new TargetCardInYourGraveyard(xValue, - new FilterCreatureCard(new StringBuilder(xValue).append(xValue != 1 ? + Target target = new TargetCardInYourGraveyard(xValue, + new FilterCreatureCard(new StringBuilder(xValue).append(xValue != 1 ? " creature cards" : "creature card").append(" from your graveyard").toString())); ability.addTarget(target); } diff --git a/Mage.Sets/src/mage/cards/r/RestoreBalance.java b/Mage.Sets/src/mage/cards/r/RestoreBalance.java index dec27da79f..18a8f97963 100644 --- a/Mage.Sets/src/mage/cards/r/RestoreBalance.java +++ b/Mage.Sets/src/mage/cards/r/RestoreBalance.java @@ -1,25 +1,13 @@ - package mage.cards.r; -import mage.abilities.Ability; import mage.abilities.costs.mana.ColoredManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.BalanceEffect; import mage.abilities.keyword.SuspendAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; -import mage.constants.Outcome; -import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterControlledLandPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCardInHand; -import mage.target.common.TargetControlledPermanent; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; /** @@ -35,10 +23,10 @@ public final class RestoreBalance extends CardImpl { // Suspend 6-{W} this.addAbility(new SuspendAbility(6, new ColoredManaCost(ColoredManaSymbol.W), this)); // Each player chooses a number of lands they control equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players sacrifice creatures and discard cards the same way. - this.getSpellAbility().addEffect(new RestoreBalanceEffect()); + this.getSpellAbility().addEffect(new BalanceEffect()); } - public RestoreBalance(final RestoreBalance card) { + private RestoreBalance(final RestoreBalance card) { super(card); } @@ -47,137 +35,3 @@ public final class RestoreBalance extends CardImpl { return new RestoreBalance(this); } } - - -class RestoreBalanceEffect extends OneShotEffect { - - RestoreBalanceEffect() { - super(Outcome.Sacrifice); - staticText = "Each player chooses a number of lands they control equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players sacrifice creatures and discard cards the same way"; - } - - RestoreBalanceEffect(final RestoreBalanceEffect effect) { - super(effect); - } - - @Override - public RestoreBalanceEffect copy() { - return new RestoreBalanceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - //Lands - int minLand = Integer.MAX_VALUE; - Cards landsToSacrifice = new CardsImpl(); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - int count = game.getBattlefield().countAll(new FilterControlledLandPermanent(), player.getId(), game); - if (count < minLand) { - minLand = count; - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - TargetControlledPermanent target = new TargetControlledPermanent(minLand, minLand, new FilterControlledLandPermanent(), true); - if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), player.getId(), source.getSourceId(), game)) { - if (permanent != null && !target.getTargets().contains(permanent.getId())) { - landsToSacrifice.add(permanent); - } - } - } - } - } - - for (UUID cardId : landsToSacrifice) { - Permanent permanent = game.getPermanent(cardId); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - } - } - - //Creatures - int minCreature = Integer.MAX_VALUE; - Cards creaturesToSacrifice = new CardsImpl(); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - int count = game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), player.getId(), game); - if (count < minCreature) { - minCreature = count; - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - TargetControlledPermanent target = new TargetControlledPermanent(minCreature, minCreature, new FilterControlledCreaturePermanent(), true); - if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), player.getId(), source.getSourceId(), game)) { - if (permanent != null && !target.getTargets().contains(permanent.getId())) { - creaturesToSacrifice.add(permanent); - } - } - } - } - } - - for (UUID cardId : creaturesToSacrifice) { - Permanent permanent = game.getPermanent(cardId); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - } - } - - //Cards in hand - int minCard = Integer.MAX_VALUE; - Map cardsToDiscard = new HashMap<>(2); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - int count = player.getHand().size(); - if (count < minCard) { - minCard = count; - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Cards cards = new CardsImpl(); - TargetCardInHand target = new TargetCardInHand(minCard, new FilterCard()); - if (target.choose(Outcome.Discard, player.getId(), source.getSourceId(), game)) { - for (Card card : player.getHand().getCards(game)) { - if (card != null && !target.getTargets().contains(card.getId())) { - cards.add(card); - } - } - cardsToDiscard.put(playerId, cards); - } - } - } - - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null && cardsToDiscard.get(playerId) != null) { - for (UUID cardId : cardsToDiscard.get(playerId)) { - Card card = game.getCard(cardId); - player.discard(card, source, game); - - } - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/r/Retaliation.java b/Mage.Sets/src/mage/cards/r/Retaliation.java index 21e41b0057..87b2954d07 100644 --- a/Mage.Sets/src/mage/cards/r/Retaliation.java +++ b/Mage.Sets/src/mage/cards/r/Retaliation.java @@ -1,9 +1,9 @@ - package mage.cards.r; import java.util.UUID; import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.cards.CardImpl; @@ -20,12 +20,20 @@ import mage.filter.StaticFilters; public final class Retaliation extends CardImpl { public Retaliation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); - // Creatures you control have "Whenever this creature becomes blocked by a creature, this creature gets +1/+1 until end of turn." - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(new BecomesBlockedByCreatureTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false), - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES))); + // Creatures you control have "Whenever this creature becomes blocked by a creature, + // this creature gets +1/+1 until end of turn." + Effect effect = new GainAbilityControlledEffect( + new BecomesBlockedByCreatureTriggeredAbility( + new BoostSourceEffect( + 1, 1, + Duration.EndOfTurn), false), + Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES); + effect.setText("Creatures you control have \"Whenever this creature becomes blocked by a creature, " + + "this creature gets +1/+1 until end of turn."); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + effect)); } public Retaliation(final Retaliation card) { diff --git a/Mage.Sets/src/mage/cards/r/RetracedImage.java b/Mage.Sets/src/mage/cards/r/RetracedImage.java index 050e0a7138..6916cd6b23 100644 --- a/Mage.Sets/src/mage/cards/r/RetracedImage.java +++ b/Mage.Sets/src/mage/cards/r/RetracedImage.java @@ -1,81 +1,81 @@ -package mage.cards.r; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCardInHand; - -/** - * - * @author jeffwadsworth - */ -public final class RetracedImage extends CardImpl { - - public RetracedImage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}"); - - // Reveal a card in your hand, then put that card onto the battlefield if it has the same name as a permanent. - this.getSpellAbility().addEffect(new RetracedImageEffect()); - - } - - private RetracedImage(final RetracedImage card) { - super(card); - } - - @Override - public RetracedImage copy() { - return new RetracedImage(this); - } -} - -class RetracedImageEffect extends OneShotEffect { - - public RetracedImageEffect() { - super(Outcome.PutCardInPlay); - this.staticText = "Reveal a card in your hand, then put that card onto the battlefield if it has the same name as a permanent"; - } - - public RetracedImageEffect(final RetracedImageEffect effect) { - super(effect); - } - - @Override - public RetracedImageEffect copy() { - return new RetracedImageEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - TargetCardInHand target = new TargetCardInHand(); - if (target.canChoose(controller.getId(), game) - && controller.choose(outcome, target, source.getSourceId(), game)) { - Card chosenCard = game.getCard(target.getFirstTarget()); - if (chosenCard != null) { - Cards cards = new CardsImpl(); - cards.add(chosenCard); - controller.revealCards(source, cards, game); - for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { - if (permanent.getName().equals(chosenCard.getName())) { - return controller.moveCards(chosenCard, Zone.BATTLEFIELD, source, game); - } - } - } - } - } - return false; - } -} +package mage.cards.r; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +/** + * + * @author jeffwadsworth + */ +public final class RetracedImage extends CardImpl { + + public RetracedImage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}"); + + // Reveal a card in your hand, then put that card onto the battlefield if it has the same name as a permanent. + this.getSpellAbility().addEffect(new RetracedImageEffect()); + + } + + private RetracedImage(final RetracedImage card) { + super(card); + } + + @Override + public RetracedImage copy() { + return new RetracedImage(this); + } +} + +class RetracedImageEffect extends OneShotEffect { + + public RetracedImageEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "Reveal a card in your hand, then put that card onto the battlefield if it has the same name as a permanent"; + } + + public RetracedImageEffect(final RetracedImageEffect effect) { + super(effect); + } + + @Override + public RetracedImageEffect copy() { + return new RetracedImageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + TargetCardInHand target = new TargetCardInHand(); + if (target.canChoose(controller.getId(), game) + && controller.choose(outcome, target, source.getSourceId(), game)) { + Card chosenCard = game.getCard(target.getFirstTarget()); + if (chosenCard != null) { + Cards cards = new CardsImpl(); + cards.add(chosenCard); + controller.revealCards(source, cards, game); + for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { + if (permanent.getName().equals(chosenCard.getName())) { + return controller.moveCards(chosenCard, Zone.BATTLEFIELD, source, game); + } + } + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RetreatToHagra.java b/Mage.Sets/src/mage/cards/r/RetreatToHagra.java index 1b6856cecb..1237aa9877 100644 --- a/Mage.Sets/src/mage/cards/r/RetreatToHagra.java +++ b/Mage.Sets/src/mage/cards/r/RetreatToHagra.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -14,6 +13,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Outcome; import mage.target.common.TargetCreaturePermanent; /** @@ -23,19 +23,22 @@ import mage.target.common.TargetCreaturePermanent; public final class RetreatToHagra extends CardImpl { public RetreatToHagra(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); - // Landfall- Whenever a land enters the battlefield under your control, choose one - Target creature gets +1/+0 and gains deathtouch until end of turn; + // Landfall- Whenever a land enters the battlefield under your control, + // choose one - Target creature gets +1/+0 and gains deathtouch until end of turn; LandfallAbility ability = new LandfallAbility(new BoostTargetEffect(1, 0, Duration.EndOfTurn), false); - ability.addEffect(new GainAbilityTargetEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn)); + Effect effect = new GainAbilityTargetEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn); + effect.setOutcome(Outcome.Benefit); + ability.addEffect(effect); ability.addTarget(new TargetCreaturePermanent()); // or Each opponent loses 1 life and you gain 1 life. Mode mode = new Mode(); mode.addEffect(new LoseLifeOpponentsEffect(1)); - Effect effect = new GainLifeEffect(1); - effect.setText("and you gain 1 life"); - mode.addEffect(effect); + Effect gainLife = new GainLifeEffect(1); + gainLife.setText("and you gain 1 life"); + mode.addEffect(gainLife); ability.addMode(mode); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RetrofitterFoundry.java b/Mage.Sets/src/mage/cards/r/RetrofitterFoundry.java index e6f66162a0..0197093c6d 100644 --- a/Mage.Sets/src/mage/cards/r/RetrofitterFoundry.java +++ b/Mage.Sets/src/mage/cards/r/RetrofitterFoundry.java @@ -1,6 +1,5 @@ package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; @@ -14,13 +13,14 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.game.permanent.token.RetrofitterFoundryToken; +import mage.game.permanent.token.Construct4Token; import mage.game.permanent.token.ServoToken; import mage.game.permanent.token.ThopterColorlessToken; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class RetrofitterFoundry extends CardImpl { @@ -62,7 +62,7 @@ public final class RetrofitterFoundry extends CardImpl { // {T}, Sacrifice a Thopter: Create a 4/4 colorless Construct artifact creature token. ability = new SimpleActivatedAbility( - new CreateTokenEffect(new RetrofitterFoundryToken()), + new CreateTokenEffect(new Construct4Token()), new TapSourceCost() ); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter2))); diff --git a/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java b/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java index f6decd52d6..6a3f7ce162 100644 --- a/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java +++ b/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java @@ -88,7 +88,7 @@ class ReturnFromExtinctionTarget extends TargetCardInYourGraveyard { return false; } for (Card card : player.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) { - if (card.isAllCreatureTypes() || card.getAbilities(game).contains(ChangelingAbility.getInstance())) { + if (card.isAllCreatureTypes() || card.hasAbility(ChangelingAbility.getInstance(), game)) { if (!subTypes.isEmpty()) { return true; } else { diff --git a/Mage.Sets/src/mage/cards/r/ReturnedReveler.java b/Mage.Sets/src/mage/cards/r/ReturnedReveler.java index e0a10efc98..34a34083ae 100644 --- a/Mage.Sets/src/mage/cards/r/ReturnedReveler.java +++ b/Mage.Sets/src/mage/cards/r/ReturnedReveler.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class ReturnedReveler extends CardImpl { this.toughness = new MageInt(3); // When Returned Reveler dies, each player puts the top three cards of their library into their graveyard. - this.addAbility(new DiesTriggeredAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(3, TargetController.ANY))); + this.addAbility(new DiesSourceTriggeredAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(3, TargetController.ANY))); } public ReturnedReveler(final ReturnedReveler card) { diff --git a/Mage.Sets/src/mage/cards/r/Reveillark.java b/Mage.Sets/src/mage/cards/r/Reveillark.java index e1a5221631..dd4479bb48 100644 --- a/Mage.Sets/src/mage/cards/r/Reveillark.java +++ b/Mage.Sets/src/mage/cards/r/Reveillark.java @@ -38,9 +38,9 @@ public final class Reveillark extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Reveillark leaves the battlefield, return up to two target creature cards with power 2 or less from your graveyard to the battlefield. - Ability ability = new LeavesBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), false); + Ability ability = new LeavesBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), false); ability.addTarget(new TargetCardInYourGraveyard(0,2,filter)); - this.addAbility(ability); + this.addAbility(ability); // Evoke {5}{W} this.addAbility(new EvokeAbility(this, "{5}{W}")); } diff --git a/Mage.Sets/src/mage/cards/r/RevelInRiches.java b/Mage.Sets/src/mage/cards/r/RevelInRiches.java index b1011b4465..ca8bf841b2 100644 --- a/Mage.Sets/src/mage/cards/r/RevelInRiches.java +++ b/Mage.Sets/src/mage/cards/r/RevelInRiches.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; @@ -19,8 +17,9 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.game.permanent.token.TreasureToken; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class RevelInRiches extends CardImpl { @@ -37,7 +36,7 @@ public final class RevelInRiches extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{B}"); // Whenever a creature an opponent controls dies, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color." - this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new TreasureToken("XLN")), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new TreasureToken()), false, filter)); // At the beginning of your upkeep, if you control ten or more Treasures, you win the game. TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect(), TargetController.YOU, false); this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/r/RhysticStudy.java b/Mage.Sets/src/mage/cards/r/RhysticStudy.java index 21b51a968b..8c34fd706d 100644 --- a/Mage.Sets/src/mage/cards/r/RhysticStudy.java +++ b/Mage.Sets/src/mage/cards/r/RhysticStudy.java @@ -69,7 +69,7 @@ class RhysticStudyDrawEffect extends OneShotEffect { && cost.pay(source, game, source.getSourceId(), opponent.getId(), false, null)) { return true; } - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/Riddlekeeper.java b/Mage.Sets/src/mage/cards/r/Riddlekeeper.java index 2310fa20ee..d6d08f9799 100644 --- a/Mage.Sets/src/mage/cards/r/Riddlekeeper.java +++ b/Mage.Sets/src/mage/cards/r/Riddlekeeper.java @@ -28,7 +28,7 @@ public final class Riddlekeeper extends CardImpl { // Whenever a creature attacks you or a planeswalker you control, that creature's controller puts the top two cards of their library into their graveyard. Effect effect = new PutTopCardOfLibraryIntoGraveTargetEffect(2); - effect.setText("that creature's controller puts the top two cards of their library into their graveyard"); + effect.setText("that creature's controller mills two cards"); this.addAbility(new AttacksAllTriggeredAbility(effect, false, StaticFilters.FILTER_PERMANENT_CREATURE, SetTargetPointer.PLAYER, true, true)); } diff --git a/Mage.Sets/src/mage/cards/r/RielleTheEverwise.java b/Mage.Sets/src/mage/cards/r/RielleTheEverwise.java new file mode 100644 index 0000000000..58c90e1f7c --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RielleTheEverwise.java @@ -0,0 +1,125 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RielleTheEverwise extends CardImpl { + + private static final DynamicValue xValue + = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY); + + public RielleTheEverwise(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // Rielle, the Everwise gets +1/+0 for each instant and sorcery card in your graveyard. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + xValue, StaticValue.get(0), Duration.WhileOnBattlefield + ).setText("{this} gets +1/+0 for each instant and sorcery card in your graveyard"))); + + // Whenever you discard one or more cards for the first time each turn, draw that many cards. + this.addAbility(new RielleTheEverwiseTriggeredAbility()); + } + + private RielleTheEverwise(final RielleTheEverwise card) { + super(card); + } + + @Override + public RielleTheEverwise copy() { + return new RielleTheEverwise(this); + } +} + +class RielleTheEverwiseTriggeredAbility extends TriggeredAbilityImpl { + + RielleTheEverwiseTriggeredAbility() { + super(Zone.BATTLEFIELD, null); + this.addWatcher(new RielleTheEverwiseWatcher()); + } + + private RielleTheEverwiseTriggeredAbility(final RielleTheEverwiseTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DISCARDED_CARDS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + RielleTheEverwiseWatcher watcher = game.getState().getWatcher(RielleTheEverwiseWatcher.class); + if (watcher == null + || !watcher.checkDiscarded(event.getPlayerId()) + || !event.getPlayerId().equals(getControllerId()) + || event.getAmount() == 0) { + return false; + } + this.getEffects().clear(); + this.addEffect(new DrawCardSourceControllerEffect(event.getAmount())); + return true; + } + + @Override + public RielleTheEverwiseTriggeredAbility copy() { + return new RielleTheEverwiseTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever you discard one or more cards for the first time each turn, draw that many cards."; + } +} + +class RielleTheEverwiseWatcher extends Watcher { + + private final Map discardedThisTurn = new HashMap<>(); + + RielleTheEverwiseWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DISCARDED_CARDS + && event.getAmount() > 0) { + discardedThisTurn.merge(event.getPlayerId(), 1, Integer::sum); + } + } + + @Override + public void reset() { + super.reset(); + this.discardedThisTurn.clear(); + } + + boolean checkDiscarded(UUID playerId) { + return discardedThisTurn.getOrDefault(playerId, 0) < 2; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RienneAngelOfRebirth.java b/Mage.Sets/src/mage/cards/r/RienneAngelOfRebirth.java index b6c73306d9..345da504cf 100644 --- a/Mage.Sets/src/mage/cards/r/RienneAngelOfRebirth.java +++ b/Mage.Sets/src/mage/cards/r/RienneAngelOfRebirth.java @@ -133,7 +133,7 @@ class RienneAngelOfRebirthEffect extends OneShotEffect { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { Effect effect = new ReturnFromGraveyardToHandTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return that card to your hand at the beginning of the next end step"); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); return true; diff --git a/Mage.Sets/src/mage/cards/r/Riftsweeper.java b/Mage.Sets/src/mage/cards/r/Riftsweeper.java index 8fc19bea15..aa4f3fabd2 100644 --- a/Mage.Sets/src/mage/cards/r/Riftsweeper.java +++ b/Mage.Sets/src/mage/cards/r/Riftsweeper.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -12,11 +11,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.other.FaceDownPredicate; import mage.game.Game; +import mage.players.Player; import mage.target.common.TargetCardInExile; /** @@ -26,12 +25,13 @@ import mage.target.common.TargetCardInExile; public final class Riftsweeper extends CardImpl { private static final FilterCard filter = new FilterCard("face-up exiled card"); + static { filter.add(Predicates.not(FaceDownPredicate.instance)); } public Riftsweeper(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.SHAMAN); @@ -74,13 +74,12 @@ class RiftsweeperEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Card card = game.getCard(targetPointer.getFirst(game, source)); if (card != null) { - // remove exiting suspend counters + // remove existing suspend counters card.getCounters(game).clear(); - // move to exile - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - game.getPlayer(card.getOwnerId()).shuffleLibrary(source, game); - game.informPlayers("Riftsweeper: Choosen card was " + card.getName()); - return true; + Player owner = game.getPlayer(card.getOwnerId()); + if (owner != null) { + return owner.shuffleCardsToLibrary(card, game, source); + } } return false; } diff --git a/Mage.Sets/src/mage/cards/r/RiggingRunner.java b/Mage.Sets/src/mage/cards/r/RiggingRunner.java index 6214bfe666..5cd6688ab4 100644 --- a/Mage.Sets/src/mage/cards/r/RiggingRunner.java +++ b/Mage.Sets/src/mage/cards/r/RiggingRunner.java @@ -1,21 +1,22 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.common.RaidHint; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class RiggingRunner extends CardImpl { @@ -31,11 +32,13 @@ public final class RiggingRunner extends CardImpl { // First strike this.addAbility(FirstStrikeAbility.getInstance()); - // Raid — Rigging Runner enters the battlefield with a +1/+1 counter on it if you attacked with a creature this turn. + // Raid — Rigging Runner enters the battlefield with a +1/+1 counter on it if you attacked this turn. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1), false), - RaidCondition.instance, - "Raid — {this} enters the battlefield with a +1/+1 counter on it if you attacked with a creature this turn.", - "{this} enters the battlefield with a +1/+1 counter"), + RaidCondition.instance, + "Raid — {this} enters the battlefield with a +1/+1 counter on it if you attacked this turn.", + "{this} enters the battlefield with a +1/+1 counter") + .setAbilityWord(AbilityWord.RAID) + .addHint(RaidHint.instance), new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java b/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java new file mode 100644 index 0000000000..10266f3289 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java @@ -0,0 +1,92 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.permanent.token.GreenCatToken; +import mage.game.permanent.token.WhiteDogToken; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author htrajan + */ +public final class RinAndSeriInseparable extends CardImpl { + + private static final FilterSpell dogSpellFilter = new FilterSpell("a Dog spell"); + private static final FilterSpell catSpellFilter = new FilterSpell("a Cat spell"); + + private static final FilterPermanent dogPermanentFilter = new FilterControlledCreaturePermanent("Dogs you control"); + private static final FilterPermanent catPermanentFilter = new FilterControlledCreaturePermanent("Cats you control"); + + static { + dogSpellFilter.add(SubType.DOG.getPredicate()); + catSpellFilter.add(SubType.CAT.getPredicate()); + + dogPermanentFilter.add(SubType.DOG.getPredicate()); + catPermanentFilter.add(SubType.CAT.getPredicate()); + } + + public RinAndSeriInseparable(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DOG); + this.subtype.add(SubType.CAT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever you cast a Dog spell, create a 1/1 green Cat creature token. + this.addAbility(new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new GreenCatToken()), dogSpellFilter, false + )); + + // Whenever you cast a Cat spell, create a 1/1 white Dog creature token. + this.addAbility(new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new WhiteDogToken()), catSpellFilter, false + )); + + // {R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control. + DynamicValue dogCount = new PermanentsOnBattlefieldCount(dogPermanentFilter); + Effect damageEffect = new DamageTargetEffect(dogCount); + damageEffect.setText("{source} deals damage to any target equal to the number of Dogs you control"); + DynamicValue catCount = new PermanentsOnBattlefieldCount(catPermanentFilter); + Effect lifeGainEffect = new GainLifeEffect(catCount); + lifeGainEffect.setText("You gain life equal to the number of Cats you control"); + Ability ability = new SimpleActivatedAbility(damageEffect, new ManaCostsImpl("{R}{G}{W}")); + ability.addEffect(lifeGainEffect); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetAnyTarget()); + ability.addHint(new ValueHint("Dogs you control", dogCount)); + ability.addHint(new ValueHint("Cats you control", catCount)); + this.addAbility(ability); + } + + private RinAndSeriInseparable(final RinAndSeriInseparable card) { + super(card); + } + + @Override + public RinAndSeriInseparable copy() { + return new RinAndSeriInseparable(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RiptideCrab.java b/Mage.Sets/src/mage/cards/r/RiptideCrab.java index c0ce13fec5..2bfcc369a5 100644 --- a/Mage.Sets/src/mage/cards/r/RiptideCrab.java +++ b/Mage.Sets/src/mage/cards/r/RiptideCrab.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class RiptideCrab extends CardImpl { // Vigilance this.addAbility(VigilanceAbility.getInstance()); // When Riptide Crab dies, draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } public RiptideCrab(final RiptideCrab card) { diff --git a/Mage.Sets/src/mage/cards/r/RiseAgain.java b/Mage.Sets/src/mage/cards/r/RiseAgain.java new file mode 100644 index 0000000000..a87bc57396 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RiseAgain.java @@ -0,0 +1,33 @@ +package mage.cards.r; + +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RiseAgain extends CardImpl { + + public RiseAgain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); + + // Return target creature card from your graveyard to the battlefield. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + } + + private RiseAgain(final RiseAgain card) { + super(card); + } + + @Override + public RiseAgain copy() { + return new RiseAgain(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RiseFall.java b/Mage.Sets/src/mage/cards/r/RiseFall.java index b3042b9902..f345dce851 100644 --- a/Mage.Sets/src/mage/cards/r/RiseFall.java +++ b/Mage.Sets/src/mage/cards/r/RiseFall.java @@ -1,19 +1,15 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.cards.SplitCard; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SpellAbilityType; import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.permanent.Permanent; @@ -22,19 +18,22 @@ import mage.target.TargetPlayer; import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class RiseFall extends SplitCard { + private static final FilterCard filter = new FilterCreatureCard("creature card from a graveyard"); + public RiseFall(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}{B}", "{B}{R}", SpellAbilityType.SPLIT); // Rise // Return target creature card from a graveyard and target creature on the battlefield to their owners' hands. getLeftHalfCard().getSpellAbility().addEffect(new RiseEffect()); - getLeftHalfCard().getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard"))); + getLeftHalfCard().getSpellAbility().addTarget(new TargetCardInGraveyard(filter)); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Fall @@ -43,7 +42,7 @@ public final class RiseFall extends SplitCard { getRightHalfCard().getSpellAbility().addTarget(new TargetPlayer()); } - public RiseFall(final RiseFall card) { + private RiseFall(final RiseFall card) { super(card); } @@ -55,12 +54,12 @@ public final class RiseFall extends SplitCard { class RiseEffect extends OneShotEffect { - public RiseEffect() { + RiseEffect() { super(Outcome.ReturnToHand); this.staticText = "Return target creature card from a graveyard and target creature on the battlefield to their owners' hands"; } - public RiseEffect(final RiseEffect effect) { + private RiseEffect(final RiseEffect effect) { super(effect); } @@ -72,31 +71,31 @@ class RiseEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Cards cardsToHand = new CardsImpl(); - Card cardInGraveyard = game.getCard(getTargetPointer().getFirst(game, source)); - if (cardInGraveyard != null) { - cardsToHand.add(cardInGraveyard); - } - Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (permanent != null) { - cardsToHand.add(permanent); - } - controller.moveCards(cardsToHand, Zone.HAND, source, game); - return true; + if (controller == null) { + return false; } - return false; + Cards cardsToHand = new CardsImpl(); + Card cardInGraveyard = game.getCard(getTargetPointer().getFirst(game, source)); + if (cardInGraveyard != null) { + cardsToHand.add(cardInGraveyard); + } + Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + if (permanent != null) { + cardsToHand.add(permanent); + } + controller.moveCards(cardsToHand, Zone.HAND, source, game); + return true; } } class FallEffect extends OneShotEffect { - public FallEffect() { + FallEffect() { super(Outcome.Discard); this.staticText = "Target player reveals two cards at random from their hand, then discards each nonland card revealed this way"; } - public FallEffect(final FallEffect effect) { + private FallEffect(final FallEffect effect) { super(effect); } @@ -109,34 +108,32 @@ class FallEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null) { - Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (targetPlayer != null) { - if (!targetPlayer.getHand().isEmpty()) { - Card card = targetPlayer.getHand().getRandom(game); - if (card == null) { - return false; - } - Cards cards = new CardsImpl(card); - if (targetPlayer.getHand().size() > 1) { - do { - card = targetPlayer.getHand().getRandom(game); - if (card == null) { - return false; - } - } while (cards.contains(card.getId())); - cards.add(card); - } - targetPlayer.revealCards(sourceObject.getIdName(), cards, game); - for (Card cardToDiscard : cards.getCards(game)) { - if (!cardToDiscard.isLand()) { - targetPlayer.discard(cardToDiscard, source, game); - } - } - } - } + if (controller == null) { + return false; + } + Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (targetPlayer == null) { return true; } - return false; + if (targetPlayer.getHand().isEmpty()) { + return true; + } + Card card = targetPlayer.getHand().getRandom(game); + if (card == null) { + return false; + } + Cards cards = new CardsImpl(card); + if (targetPlayer.getHand().size() > 1) { + do { + card = targetPlayer.getHand().getRandom(game); + if (card == null) { + return false; + } + } while (cards.contains(card.getId())); + cards.add(card); + } + targetPlayer.revealCards(sourceObject.getIdName(), cards, game); + targetPlayer.discard(new CardsImpl(cards.getCards(StaticFilters.FILTER_CARD_NON_LAND, game)), source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/r/RisenReef.java b/Mage.Sets/src/mage/cards/r/RisenReef.java index 71c38de59a..8b58944b89 100644 --- a/Mage.Sets/src/mage/cards/r/RisenReef.java +++ b/Mage.Sets/src/mage/cards/r/RisenReef.java @@ -2,7 +2,7 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -22,8 +22,7 @@ import java.util.UUID; */ public final class RisenReef extends CardImpl { - private static final FilterPermanent filter - = new FilterPermanent(SubType.ELEMENTAL, "{this} or another Elemental"); + private static final FilterPermanent filter = new FilterPermanent(SubType.ELEMENTAL, "Elemental"); public RisenReef(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}"); @@ -33,7 +32,9 @@ public final class RisenReef extends CardImpl { this.toughness = new MageInt(1); // Whenever Risen Reef or another Elemental enters the battlefield under your control, look at the top card of your library. If it's a land card, you may put it onto the battlefield tapped. If you don't put the card onto the battlefield, put it into your hand. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new RisenReefEffect(), filter)); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new RisenReefEffect(), filter, false, true + )); } private RisenReef(final RisenReef card) { @@ -85,4 +86,4 @@ class RisenReefEffect extends OneShotEffect { } return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/r/RishadanPort.java b/Mage.Sets/src/mage/cards/r/RishadanPort.java index 45fe3d81e0..3cb6c5a151 100644 --- a/Mage.Sets/src/mage/cards/r/RishadanPort.java +++ b/Mage.Sets/src/mage/cards/r/RishadanPort.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -14,18 +12,19 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.target.common.TargetLandPermanent; +import java.util.UUID; + /** - * * @author jonubuu */ public final class RishadanPort extends CardImpl { public RishadanPort(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},null); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, null); - // {tap}: Add {C}. + // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); - // {1}, {tap}: Tap target land. + // {1}, {T}: Tap target land. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetLandPermanent()); diff --git a/Mage.Sets/src/mage/cards/r/RishkarPeemaRenegade.java b/Mage.Sets/src/mage/cards/r/RishkarPeemaRenegade.java index 591437ed8f..963bb2ccd0 100644 --- a/Mage.Sets/src/mage/cards/r/RishkarPeemaRenegade.java +++ b/Mage.Sets/src/mage/cards/r/RishkarPeemaRenegade.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -13,22 +11,22 @@ import mage.abilities.mana.GreenManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.CounterAnyPredicate; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class RishkarPeemaRenegade extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Each creature you control with a counter on it"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); static { filter.add(CounterAnyPredicate.instance); @@ -52,14 +50,13 @@ public final class RishkarPeemaRenegade extends CardImpl { // Each creature you control with a counter on it has "T: Add G." this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, new GainAbilityControlledEffect( - new GreenManaAbility(), - Duration.WhileOnBattlefield, - filter))); + new GreenManaAbility(), Duration.WhileOnBattlefield, filter + ).setText("Each creature you control with a counter on it has \"{T}: Add {G}.\"") + )); } - public RishkarPeemaRenegade(final RishkarPeemaRenegade card) { + private RishkarPeemaRenegade(final RishkarPeemaRenegade card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/r/RiskFactor.java b/Mage.Sets/src/mage/cards/r/RiskFactor.java index c9dd1373c0..9133a2df21 100644 --- a/Mage.Sets/src/mage/cards/r/RiskFactor.java +++ b/Mage.Sets/src/mage/cards/r/RiskFactor.java @@ -67,7 +67,7 @@ class RiskFactorEffect extends OneShotEffect { if (opponent.chooseUse(outcome, "Do you choose to take the damage?", source, game)) { opponent.damage(4, source.getSourceId(), game); } else { - controller.drawCards(3, game); + controller.drawCards(3, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/RiskyMove.java b/Mage.Sets/src/mage/cards/r/RiskyMove.java index b72f91ad8d..1fc5ba2af7 100644 --- a/Mage.Sets/src/mage/cards/r/RiskyMove.java +++ b/Mage.Sets/src/mage/cards/r/RiskyMove.java @@ -165,7 +165,7 @@ class RiskyMoveFlipCoinEffect extends OneShotEffect { if (!controller.flipCoin(source, game, true)) { if (permanent != null && chosenOpponent != null) { ContinuousEffect effect = new RiskyMoveCreatureGainControlEffect(Duration.Custom, chosenOpponent.getId()); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); game.informPlayers(chosenOpponent.getLogName() + " has gained control of " + permanent.getLogName()); return true; diff --git a/Mage.Sets/src/mage/cards/r/RiteOfConsumption.java b/Mage.Sets/src/mage/cards/r/RiteOfConsumption.java index e6230fb966..6c5b805efc 100644 --- a/Mage.Sets/src/mage/cards/r/RiteOfConsumption.java +++ b/Mage.Sets/src/mage/cards/r/RiteOfConsumption.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -27,7 +27,7 @@ public final class RiteOfConsumption extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // As an additional cost to cast Rite of Consumption, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent("a creature"), false))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, false))); // Rite of Consumption deals damage equal to the sacrificed creature's power to target player. You gain life equal to the damage dealt this way. this.getSpellAbility().addEffect(new RiteOfConsumptionEffect()); this.getSpellAbility().addTarget(new TargetPlayerOrPlaneswalker()); diff --git a/Mage.Sets/src/mage/cards/r/RiteOfTheSerpent.java b/Mage.Sets/src/mage/cards/r/RiteOfTheSerpent.java index 02b31e7760..1a7bb465bc 100644 --- a/Mage.Sets/src/mage/cards/r/RiteOfTheSerpent.java +++ b/Mage.Sets/src/mage/cards/r/RiteOfTheSerpent.java @@ -60,7 +60,7 @@ class RiteOfTheSerpentEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent targetCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (targetCreature != null) { if (targetCreature.getCounters(game).containsKey(CounterType.P1P1)) { new CreateTokenEffect(new SnakeToken("KTK")).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/r/RitesOfSpring.java b/Mage.Sets/src/mage/cards/r/RitesOfSpring.java index 76dfca48a1..3991d3acd3 100644 --- a/Mage.Sets/src/mage/cards/r/RitesOfSpring.java +++ b/Mage.Sets/src/mage/cards/r/RitesOfSpring.java @@ -1,25 +1,23 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetCardInHand; +import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetDiscard; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class RitesOfSpring extends CardImpl { @@ -31,7 +29,7 @@ public final class RitesOfSpring extends CardImpl { getSpellAbility().addEffect(new RitesOfSpringEffect()); } - public RitesOfSpring(final RitesOfSpring card) { + private RitesOfSpring(final RitesOfSpring card) { super(card); } @@ -43,12 +41,13 @@ public final class RitesOfSpring extends CardImpl { class RitesOfSpringEffect extends OneShotEffect { - public RitesOfSpringEffect() { + RitesOfSpringEffect() { super(Outcome.DrawCard); - this.staticText = "Discard any number of cards. Search your library for up to that many basic land cards, reveal those cards, and put them into your hand. Then shuffle your library."; + this.staticText = "Discard any number of cards. Search your library for up to that many basic land cards, " + + "reveal those cards, and put them into your hand. Then shuffle your library."; } - public RitesOfSpringEffect(final RitesOfSpringEffect effect) { + private RitesOfSpringEffect(final RitesOfSpringEffect effect) { super(effect); } @@ -60,23 +59,15 @@ class RitesOfSpringEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Target target = new TargetCardInHand(0, Integer.MAX_VALUE, new FilterCard("cards to discard")); - while (controller.canRespond() && !target.isChosen()) { - target.choose(Outcome.BoostCreature, controller.getId(), source.getSourceId(), game); - } - int numDiscarded = 0; - for (UUID targetId : target.getTargets()) { - Card card = controller.getHand().get(targetId, game); - if (controller.discard(card, source, game)) { - numDiscarded++; - } - } - game.applyEffects(); - return new SearchLibraryPutInHandEffect( - new TargetCardInLibrary(0, numDiscarded, StaticFilters.FILTER_CARD_BASIC_LAND), true, true) - .apply(game, source); + if (controller == null) { + return false; } - return false; + TargetCard target = new TargetDiscard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD, controller.getId()); + controller.choose(Outcome.AIDontUseIt, controller.getHand(), target, game); + int numDiscarded = controller.discard(new CardsImpl(target.getTargets()), source, game).size(); + new SearchLibraryPutInHandEffect(new TargetCardInLibrary( + 0, numDiscarded, StaticFilters.FILTER_CARD_BASIC_LAND + ), true, true).apply(game, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/r/RixMaadiReveler.java b/Mage.Sets/src/mage/cards/r/RixMaadiReveler.java index 82136a1ee7..a8196431f4 100644 --- a/Mage.Sets/src/mage/cards/r/RixMaadiReveler.java +++ b/Mage.Sets/src/mage/cards/r/RixMaadiReveler.java @@ -73,10 +73,10 @@ class RixMaadiRevelerEffect extends OneShotEffect { } if (SpectacleCondition.instance.apply(game, source)) { player.discard(player.getHand().size(), false, source, game); - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); } else { player.discard(1, false, source, game); - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java b/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java index 6c77689b4a..29cd3f704a 100644 --- a/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java +++ b/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java @@ -2,7 +2,7 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.effects.common.counter.ProliferateEffect; @@ -56,7 +56,7 @@ public final class RoaleskApexHybrid extends CardImpl { this.addAbility(ability); // When Roalsk dies, proliferate, then proliferate again. (Choose any number of permanents and/or players, then give each another counter of each kind already there. Then do it again.) - ability = new DiesTriggeredAbility(new ProliferateEffect(false)); + ability = new DiesSourceTriggeredAbility(new ProliferateEffect(false)); ability.addEffect(new ProliferateEffect(" again", true).concatBy(", then")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RoamingGhostlight.java b/Mage.Sets/src/mage/cards/r/RoamingGhostlight.java new file mode 100644 index 0000000000..75f7bf1158 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RoamingGhostlight.java @@ -0,0 +1,54 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RoamingGhostlight extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("non-Spirit creature"); + + static { + filter.add(Predicates.not(SubType.SPIRIT.getPredicate())); + } + + public RoamingGhostlight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Roaming Ghostlight enters the battlefield, return up to one target non-Spirit creature to its owner's hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect()); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + this.addAbility(ability); + } + + private RoamingGhostlight(final RoamingGhostlight card) { + super(card); + } + + @Override + public RoamingGhostlight copy() { + return new RoamingGhostlight(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RoarOfJukai.java b/Mage.Sets/src/mage/cards/r/RoarOfJukai.java index 6d777a3cae..84af25a1d9 100644 --- a/Mage.Sets/src/mage/cards/r/RoarOfJukai.java +++ b/Mage.Sets/src/mage/cards/r/RoarOfJukai.java @@ -87,7 +87,7 @@ class RoarOfJukaiEffect extends OneShotEffect { if (new PermanentsOnTheBattlefieldCondition(filter).apply(game, source)) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filterBlocked, source.getControllerId(), source.getSourceId(), game)) { ContinuousEffect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/r/RocEgg.java b/Mage.Sets/src/mage/cards/r/RocEgg.java index 126df90a82..e5e5797ee9 100644 --- a/Mage.Sets/src/mage/cards/r/RocEgg.java +++ b/Mage.Sets/src/mage/cards/r/RocEgg.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class RocEgg extends CardImpl { this.toughness = new MageInt(3); this.addAbility(DefenderAbility.getInstance()); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(rocEggToken, 1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(rocEggToken, 1), false)); } public RocEgg(final RocEgg card) { diff --git a/Mage.Sets/src/mage/cards/r/RockBasilisk.java b/Mage.Sets/src/mage/cards/r/RockBasilisk.java index 6fef87b86b..43581603bc 100644 --- a/Mage.Sets/src/mage/cards/r/RockBasilisk.java +++ b/Mage.Sets/src/mage/cards/r/RockBasilisk.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -38,7 +38,7 @@ public final class RockBasilisk extends CardImpl { Effect effect = new CreateDelayedTriggeredAbilityEffect( new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false)); } public RockBasilisk(final RockBasilisk card) { diff --git a/Mage.Sets/src/mage/cards/r/RogueSkycaptain.java b/Mage.Sets/src/mage/cards/r/RogueSkycaptain.java index a5440d2d57..e56b0bfd03 100644 --- a/Mage.Sets/src/mage/cards/r/RogueSkycaptain.java +++ b/Mage.Sets/src/mage/cards/r/RogueSkycaptain.java @@ -1,102 +1,102 @@ -package mage.cards.r; - -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.costs.Cost; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.RemoveAllCountersSourceEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetOpponent; -import mage.util.ManaUtil; - -import java.util.Set; -import java.util.UUID; - -/** - * @author Ketsuban - */ -public class RogueSkycaptain extends CardImpl { - - public RogueSkycaptain(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); - this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.ROGUE); - this.subtype.add(SubType.MERCENARY); - this.power = new MageInt(3); - this.toughness = new MageInt(4); - - // Flying - this.addAbility(FlyingAbility.getInstance()); - - // At the beginning of your upkeep, put a wage counter on Rogue Skycaptain. You - // may pay 2 for each wage counter on it. If you don't, remove all wage counters - // from Rogue Skycaptain and an opponent gains control of it. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new RogueSkycaptainEffect(), TargetController.YOU, false)); - } - - @Override - public Card copy() { - return null; - } - -} - -class RogueSkycaptainEffect extends OneShotEffect { - - public RogueSkycaptainEffect() { - super(Outcome.GainControl); - staticText = "put a wage counter on {this}. You may pay {2} for each wage counter on it. If you don't, remove all wage counters from {this} and an opponent gains control of it"; - } - - public RogueSkycaptainEffect(final RogueSkycaptainEffect effect) { - super(effect); - } - - @Override - public Effect copy() { - return new RogueSkycaptainEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null && permanent != null) { - new AddCountersSourceEffect(CounterType.WAGE.createInstance(), true).apply(game, source); - Cost cost = ManaUtil.createManaCost(2 * permanent.getCounters(game).getCount(CounterType.WAGE), false); - if (!cost.pay(source, game, controller.getId(), controller.getId(), false)) { - new RemoveAllCountersSourceEffect(CounterType.WAGE).apply(game, source); - Player opponent; - Set opponents = game.getOpponents(controller.getId()); - if (opponents.size() == 1) { - opponent = game.getPlayer(opponents.iterator().next()); - } else { - Target target = new TargetOpponent(true); - target.setNotTarget(true); - target.choose(Outcome.GainControl, source.getControllerId(), source.getSourceId(), game); - opponent = game.getPlayer(target.getFirstTarget()); - } - if (opponent != null) { - permanent.changeControllerId(opponent.getId(), game); - } - } - return true; - } - return false; - } +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.RemoveAllCountersSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetOpponent; +import mage.util.ManaUtil; + +import java.util.Set; +import java.util.UUID; + +/** + * @author Ketsuban + */ +public class RogueSkycaptain extends CardImpl { + + public RogueSkycaptain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.subtype.add(SubType.MERCENARY); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of your upkeep, put a wage counter on Rogue Skycaptain. You + // may pay 2 for each wage counter on it. If you don't, remove all wage counters + // from Rogue Skycaptain and an opponent gains control of it. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new RogueSkycaptainEffect(), TargetController.YOU, false)); + } + + @Override + public Card copy() { + return null; + } + +} + +class RogueSkycaptainEffect extends OneShotEffect { + + public RogueSkycaptainEffect() { + super(Outcome.GainControl); + staticText = "put a wage counter on {this}. You may pay {2} for each wage counter on it. If you don't, remove all wage counters from {this} and an opponent gains control of it"; + } + + public RogueSkycaptainEffect(final RogueSkycaptainEffect effect) { + super(effect); + } + + @Override + public Effect copy() { + return new RogueSkycaptainEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null && permanent != null) { + new AddCountersSourceEffect(CounterType.WAGE.createInstance(), true).apply(game, source); + Cost cost = ManaUtil.createManaCost(2 * permanent.getCounters(game).getCount(CounterType.WAGE), false); + if (!cost.pay(source, game, controller.getId(), controller.getId(), false)) { + new RemoveAllCountersSourceEffect(CounterType.WAGE).apply(game, source); + Player opponent; + Set opponents = game.getOpponents(controller.getId()); + if (opponents.size() == 1) { + opponent = game.getPlayer(opponents.iterator().next()); + } else { + Target target = new TargetOpponent(true); + target.setNotTarget(true); + target.choose(Outcome.GainControl, source.getControllerId(), source.getSourceId(), game); + opponent = game.getPlayer(target.getFirstTarget()); + } + if (opponent != null) { + permanent.changeControllerId(opponent.getId(), game); + } + } + return true; + } + return false; + } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/RoilElemental.java b/Mage.Sets/src/mage/cards/r/RoilElemental.java index 4a2b3c44c5..a4e01d142e 100644 --- a/Mage.Sets/src/mage/cards/r/RoilElemental.java +++ b/Mage.Sets/src/mage/cards/r/RoilElemental.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -13,6 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.LostControlWatcher; /** * @@ -36,6 +36,7 @@ public final class RoilElemental extends CardImpl { ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), new SourceOnBattlefieldControlUnchangedCondition(), rule); Ability ability = new LandfallAbility(Zone.BATTLEFIELD, effect, true); ability.addTarget(new TargetCreaturePermanent()); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RookieMistake.java b/Mage.Sets/src/mage/cards/r/RookieMistake.java new file mode 100644 index 0000000000..8f9f16512e --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RookieMistake.java @@ -0,0 +1,89 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RookieMistake extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(new AnotherTargetPredicate(2)); + } + + public RookieMistake(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // Until end of turn, target creature gets +0/+2 and another target creature gets -2/-0. + this.getSpellAbility().addEffect(new RookieMistakeEffect()); + TargetPermanent target = new TargetCreaturePermanent(); + target.setTargetTag(1); + this.getSpellAbility().addTarget(target.withChooseHint("+0/+2")); + target = new TargetPermanent(filter); + target.setTargetTag(2); + this.getSpellAbility().addTarget(target.withChooseHint("-2/-0")); + } + + private RookieMistake(final RookieMistake card) { + super(card); + } + + @Override + public RookieMistake copy() { + return new RookieMistake(this); + } +} + +class RookieMistakeEffect extends OneShotEffect { + + RookieMistakeEffect() { + super(Outcome.BoostCreature); + this.staticText = "until end of turn, target creature gets +0/+2 and another target creature gets -2/-0"; + } + + private RookieMistakeEffect(final RookieMistakeEffect effect) { + super(effect); + } + + @Override + public RookieMistakeEffect copy() { + return new RookieMistakeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + ContinuousEffect effect = new BoostTargetEffect(0, 2, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + } + permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + if (permanent != null) { + ContinuousEffect effect = new BoostTargetEffect(-2, 0, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RoonOfTheHiddenRealm.java b/Mage.Sets/src/mage/cards/r/RoonOfTheHiddenRealm.java index 22072f622d..12bfbc0b3c 100644 --- a/Mage.Sets/src/mage/cards/r/RoonOfTheHiddenRealm.java +++ b/Mage.Sets/src/mage/cards/r/RoonOfTheHiddenRealm.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -16,11 +14,7 @@ import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; @@ -29,8 +23,9 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class RoonOfTheHiddenRealm extends CardImpl { @@ -42,7 +37,7 @@ public final class RoonOfTheHiddenRealm extends CardImpl { } public RoonOfTheHiddenRealm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{W}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}{U}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.RHINO); this.subtype.add(SubType.SOLDIER); @@ -98,7 +93,7 @@ class RoonOfTheHiddenRealmEffect extends OneShotEffect { if (permanent != null) { int zcc = permanent.getZoneChangeCounter(game); if (controller.moveCards(permanent, Zone.EXILED, source, game)) { - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setTargetPointer(new FixedTarget(permanent.getId(), zcc + 1)); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); diff --git a/Mage.Sets/src/mage/cards/r/RootingKavu.java b/Mage.Sets/src/mage/cards/r/RootingKavu.java index 7e0fc263db..8e34a33494 100644 --- a/Mage.Sets/src/mage/cards/r/RootingKavu.java +++ b/Mage.Sets/src/mage/cards/r/RootingKavu.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.common.ExileSourceFromGraveCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DoIfCostPaid; @@ -31,7 +31,7 @@ public final class RootingKavu extends CardImpl { this.toughness = new MageInt(3); // When Rooting Kavu dies, you may exile it. If you do, shuffle all creature cards from your graveyard into your library. - this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new RootingKavuEffect(), new ExileSourceFromGraveCost()))); + this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new RootingKavuEffect(), new ExileSourceFromGraveCost()))); } public RootingKavu(final RootingKavu card) { diff --git a/Mage.Sets/src/mage/cards/r/RootingMoloch.java b/Mage.Sets/src/mage/cards/r/RootingMoloch.java new file mode 100644 index 0000000000..8fb72f250e --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RootingMoloch.java @@ -0,0 +1,136 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RootingMoloch extends CardImpl { + + private static final FilterCard filter = new FilterCard("card with a cycling ability from your graveyard"); + + static { + filter.add(new AbilityPredicate(CyclingAbility.class)); + } + + public RootingMoloch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.LIZARD); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // When Rooting Moloch enters the battlefield, exile target card with a cycling ability from your graveyard. Until the end of your next turn, you may play that card. + Ability ability = new EntersBattlefieldTriggeredAbility(new RootingMolochEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private RootingMoloch(final RootingMoloch card) { + super(card); + } + + @Override + public RootingMoloch copy() { + return new RootingMoloch(this); + } +} + +class RootingMolochEffect extends OneShotEffect { + + RootingMolochEffect() { + super(Outcome.Benefit); + staticText = "exile target card with a cycling ability from your graveyard. " + + "Until the end of your next turn, you may play that card."; + } + + private RootingMolochEffect(final RootingMolochEffect effect) { + super(effect); + } + + @Override + public RootingMolochEffect copy() { + return new RootingMolochEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (controller == null || card == null) { + return false; + } + controller.moveCards(card, Zone.EXILED, source, game); + ContinuousEffect effect = new RootingMolochMayPlayEffect(); + effect.setTargetPointer(new FixedTarget(card, game)); + game.addEffect(effect, source); + return true; + } +} + +class RootingMolochMayPlayEffect extends AsThoughEffectImpl { + + private int castOnTurn = 0; + + RootingMolochMayPlayEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); + this.staticText = "Until the end of your next turn, you may play that card."; + } + + private RootingMolochMayPlayEffect(final RootingMolochMayPlayEffect effect) { + super(effect); + castOnTurn = effect.castOnTurn; + } + + @Override + public RootingMolochMayPlayEffect copy() { + return new RootingMolochMayPlayEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + castOnTurn = game.getTurnNum(); + } + + @Override + public boolean isInactive(Ability source, Game game) { + if (castOnTurn != game.getTurnNum() && game.getPhase().getStep().getType() == PhaseStep.END_TURN) { + return game.isActivePlayer(source.getControllerId()); + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return source.isControlledBy(affectedControllerId) + && getTargetPointer().getTargets(game, source).contains(sourceId); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java b/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java index 9186613c39..f4229c6515 100644 --- a/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java +++ b/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java @@ -5,16 +5,18 @@ import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.Cost; +import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.mana.BasicManaEffect; -import mage.abilities.mana.BasicManaAbility; +import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.abilities.mana.conditional.ManaCondition; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; +import mage.constants.Zone; import mage.game.Game; import java.util.UUID; @@ -34,10 +36,9 @@ public final class RosheenMeanderer extends CardImpl { // {T}: Add {C}{C}{C}{C}. Spend this mana only on costs that contain {X}. this.addAbility(new RosheenMeandererManaAbility()); - } - public RosheenMeanderer(final RosheenMeanderer card) { + private RosheenMeanderer(final RosheenMeanderer card) { super(card); } @@ -47,14 +48,14 @@ public final class RosheenMeanderer extends CardImpl { } } -class RosheenMeandererManaAbility extends BasicManaAbility { +class RosheenMeandererManaAbility extends ActivatedManaAbilityImpl { RosheenMeandererManaAbility() { - super(new BasicManaEffect(new RosheenMeandererConditionalMana())); + super(Zone.BATTLEFIELD, new BasicManaEffect(new RosheenMeandererConditionalMana()), new TapSourceCost()); this.netMana.add(Mana.ColorlessMana(4)); } - RosheenMeandererManaAbility(RosheenMeandererManaAbility ability) { + private RosheenMeandererManaAbility(RosheenMeandererManaAbility ability) { super(ability); } @@ -66,7 +67,7 @@ class RosheenMeandererManaAbility extends BasicManaAbility { class RosheenMeandererConditionalMana extends ConditionalMana { - public RosheenMeandererConditionalMana() { + RosheenMeandererConditionalMana() { super(Mana.ColorlessMana(4)); staticText = "Spend this mana only on costs that contain {X}"; addCondition(new RosheenMeandererManaCondition()); diff --git a/Mage.Sets/src/mage/cards/r/RotcrownGhoul.java b/Mage.Sets/src/mage/cards/r/RotcrownGhoul.java index bb979190f9..5591fd73a1 100644 --- a/Mage.Sets/src/mage/cards/r/RotcrownGhoul.java +++ b/Mage.Sets/src/mage/cards/r/RotcrownGhoul.java @@ -4,7 +4,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class RotcrownGhoul extends CardImpl { this.toughness = new MageInt(3); // When Rotcrown Ghoul dies, target player puts the top five cards of their library into their graveyard. - Ability ability = new DiesTriggeredAbility(new PutLibraryIntoGraveTargetEffect(5)); + Ability ability = new DiesSourceTriggeredAbility(new PutLibraryIntoGraveTargetEffect(5)); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RottenheartGhoul.java b/Mage.Sets/src/mage/cards/r/RottenheartGhoul.java index a3ae6504fa..8a71bd7a30 100644 --- a/Mage.Sets/src/mage/cards/r/RottenheartGhoul.java +++ b/Mage.Sets/src/mage/cards/r/RottenheartGhoul.java @@ -4,7 +4,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class RottenheartGhoul extends CardImpl { this.toughness = new MageInt(4); // When Rottenheart Ghoul dies, target player discards a card. - Ability ability = new DiesTriggeredAbility(new DiscardTargetEffect(1)); + Ability ability = new DiesSourceTriggeredAbility(new DiscardTargetEffect(1)); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RouseTheMob.java b/Mage.Sets/src/mage/cards/r/RouseTheMob.java index 9f80c7b050..ad2a2099e4 100644 --- a/Mage.Sets/src/mage/cards/r/RouseTheMob.java +++ b/Mage.Sets/src/mage/cards/r/RouseTheMob.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -13,20 +11,21 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class RouseTheMob extends CardImpl { public RouseTheMob(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); // Strive — Rouse the Mob costs {2}{R} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{R}")); + // Any number of target creatures each get +2/+0 and gain trample until end of turn. - Effect effect = new BoostTargetEffect(2,0, Duration.EndOfTurn); + Effect effect = new BoostTargetEffect(2, 0, Duration.EndOfTurn); effect.setText("Any number of target creatures each get +2/+0"); this.getSpellAbility().addEffect(effect); effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); diff --git a/Mage.Sets/src/mage/cards/r/RousingRead.java b/Mage.Sets/src/mage/cards/r/RousingRead.java new file mode 100644 index 0000000000..80646faf20 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RousingRead.java @@ -0,0 +1,58 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RousingRead extends CardImpl { + + public RousingRead(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // When Rousing Read enters the battlefield, draw two cards, then discard a card. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new DrawDiscardControllerEffect(2, 1) + )); + + // Enchanted creature gets +1/+1 and has flying. + ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield)); + ability.addEffect(new GainAbilityAttachedEffect( + FlyingAbility.getInstance(), AttachmentType.AURA + ).setText("and has flying")); + this.addAbility(ability); + } + + private RousingRead(final RousingRead card) { + super(card); + } + + @Override + public RousingRead copy() { + return new RousingRead(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RovingKeep.java b/Mage.Sets/src/mage/cards/r/RovingKeep.java index 05f9af99ad..8006368aa0 100644 --- a/Mage.Sets/src/mage/cards/r/RovingKeep.java +++ b/Mage.Sets/src/mage/cards/r/RovingKeep.java @@ -39,7 +39,7 @@ public final class RovingKeep extends CardImpl { ); ability.addEffect(new GainAbilitySourceEffect( TrampleAbility.getInstance(), Duration.EndOfTurn - ).setText("and gains trample until end of turn.")); + ).setText("and gains trample until end of turn")); ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn) .setText("It can attack this turn as though it didn't have defender")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/r/RowdyCrew.java b/Mage.Sets/src/mage/cards/r/RowdyCrew.java index f4171399c0..7d34ad922f 100644 --- a/Mage.Sets/src/mage/cards/r/RowdyCrew.java +++ b/Mage.Sets/src/mage/cards/r/RowdyCrew.java @@ -71,7 +71,7 @@ class RowdyCrewEffect extends OneShotEffect { Permanent creature = game.getPermanent(source.getSourceId()); Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); Cards cards = new CardsImpl(); int cardsInHand = player.getHand().size(); switch (cardsInHand) { diff --git a/Mage.Sets/src/mage/cards/r/RubyLeech.java b/Mage.Sets/src/mage/cards/r/RubyLeech.java index 13638d55da..ba11fe901b 100644 --- a/Mage.Sets/src/mage/cards/r/RubyLeech.java +++ b/Mage.Sets/src/mage/cards/r/RubyLeech.java @@ -1,23 +1,23 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author LoneFox */ public final class RubyLeech extends CardImpl { @@ -29,7 +29,7 @@ public final class RubyLeech extends CardImpl { } public RubyLeech(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.LEECH); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -38,7 +38,7 @@ public final class RubyLeech extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // Red spells you cast cost {R} more to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{R}")))); + new SpellsCostIncreasingAllEffect(new ManaCostsImpl("{R}"), filter, TargetController.YOU))); } public RubyLeech(final RubyLeech card) { diff --git a/Mage.Sets/src/mage/cards/r/RuinGhost.java b/Mage.Sets/src/mage/cards/r/RuinGhost.java index a39b2da139..3783fc527a 100644 --- a/Mage.Sets/src/mage/cards/r/RuinGhost.java +++ b/Mage.Sets/src/mage/cards/r/RuinGhost.java @@ -32,7 +32,7 @@ public final class RuinGhost extends CardImpl { // {W}, {T}: Exile target land you control, then return it to the battlefield under your control. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetForSourceEffect(), new ManaCostsImpl("{W")); ability.addCost(new TapSourceCost()); - ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(true)); + ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(false)); ability.addTarget(new TargetPermanent(new FilterControlledLandPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RuinRaider.java b/Mage.Sets/src/mage/cards/r/RuinRaider.java index 60ccfd7269..b91f50cd77 100644 --- a/Mage.Sets/src/mage/cards/r/RuinRaider.java +++ b/Mage.Sets/src/mage/cards/r/RuinRaider.java @@ -1,29 +1,21 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.abilities.hint.common.RaidHint; +import mage.cards.*; +import mage.constants.*; import mage.game.Game; import mage.players.Player; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class RuinRaider extends CardImpl { @@ -36,13 +28,15 @@ public final class RuinRaider extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - // Raid — At the beginning of your end step, if you attacked with a creature this turn, reveal the top card of your library and put that card into your hand. You lose life equal to the card's converted mana cost. + // Raid — At the beginning of your end step, if you attacked this turn, reveal the top card of your library and put that card into your hand. You lose life equal to the card's converted mana cost. Ability ability = new ConditionalInterveningIfTriggeredAbility( new BeginningOfEndStepTriggeredAbility(new RuinRaiderEffect(), TargetController.YOU, false), RaidCondition.instance, - "Raid — At the beginning of your end step, if you attacked with a creature this turn, " - + "reveal the top card of your library and put that card into your hand. " - + "You lose life equal to the card's converted mana cost."); + "Raid — At the beginning of your end step, if you attacked this turn, " + + "reveal the top card of your library and put that card into your hand. " + + "You lose life equal to the card's converted mana cost."); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/r/RuinRat.java b/Mage.Sets/src/mage/cards/r/RuinRat.java index 513b137219..86ce487b07 100644 --- a/Mage.Sets/src/mage/cards/r/RuinRat.java +++ b/Mage.Sets/src/mage/cards/r/RuinRat.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.cards.CardImpl; @@ -30,7 +30,7 @@ public final class RuinRat extends CardImpl { this.addAbility(DeathtouchAbility.getInstance()); // When Ruin Rat dies, exile target card from an opponent's graveyard. - DiesTriggeredAbility ability = new DiesTriggeredAbility(new ExileTargetEffect()); + DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(new ExileTargetEffect()); ability.addTarget(new TargetCardInOpponentsGraveyard(new FilterCard("card from an opponent's graveyard"))); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/r/RuinationRioter.java b/Mage.Sets/src/mage/cards/r/RuinationRioter.java index dace98732c..00af5f37cf 100644 --- a/Mage.Sets/src/mage/cards/r/RuinationRioter.java +++ b/Mage.Sets/src/mage/cards/r/RuinationRioter.java @@ -2,7 +2,7 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.DamageTargetEffect; @@ -31,7 +31,7 @@ public final class RuinationRioter extends CardImpl { this.toughness = new MageInt(2); // When Ruination Rioter dies, you may have it deal damage to any target equal to the number of land cards in your graveyard. - Ability ability = new DiesTriggeredAbility( + Ability ability = new DiesSourceTriggeredAbility( new DamageTargetEffect(xValue).setText("you may have it deal damage to any target " + "equal to the number of land cards in your graveyard."), true ); diff --git a/Mage.Sets/src/mage/cards/r/RuinousUltimatum.java b/Mage.Sets/src/mage/cards/r/RuinousUltimatum.java new file mode 100644 index 0000000000..29aa5e730f --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RuinousUltimatum.java @@ -0,0 +1,40 @@ +package mage.cards.r; + +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RuinousUltimatum extends CardImpl { + + private static final FilterPermanent filter + = new FilterNonlandPermanent("nonland permanents your opponents control"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public RuinousUltimatum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}{R}{W}{W}{W}{B}{B}"); + + // Destroy all nonland permanents your opponents control + this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + } + + private RuinousUltimatum(final RuinousUltimatum card) { + super(card); + } + + @Override + public RuinousUltimatum copy() { + return new RuinousUltimatum(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RukhEgg.java b/Mage.Sets/src/mage/cards/r/RukhEgg.java index 6cb6036d6e..7889f9206f 100644 --- a/Mage.Sets/src/mage/cards/r/RukhEgg.java +++ b/Mage.Sets/src/mage/cards/r/RukhEgg.java @@ -4,7 +4,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -31,7 +31,7 @@ public final class RukhEgg extends CardImpl { // When Rukh Egg dies, create a 4/4 red Bird creature token with flying at the beginning of the next end step. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new CreateTokenEffect(new RukhEggBirdToken()))); effect.setText("create a 4/4 red Bird creature token with flying at the beginning of the next end step"); - Ability ability = new DiesTriggeredAbility(effect); + Ability ability = new DiesSourceTriggeredAbility(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RumblingRockslide.java b/Mage.Sets/src/mage/cards/r/RumblingRockslide.java new file mode 100644 index 0000000000..ca4a6d08d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RumblingRockslide.java @@ -0,0 +1,39 @@ +package mage.cards.r; + +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RumblingRockslide extends CardImpl { + + private static final DynamicValue xValue + = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_LAND_SHORT_TEXT); + + public RumblingRockslide(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // Rumbling Rockslide deals damage to target creature equal to the number of lands you control. + this.getSpellAbility().addEffect(new DamageTargetEffect(xValue) + .setText("{this} deals damage to target creature equal to the number of lands you control")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private RumblingRockslide(final RumblingRockslide card) { + super(card); + } + + @Override + public RumblingRockslide copy() { + return new RumblingRockslide(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/Rumination.java b/Mage.Sets/src/mage/cards/r/Rumination.java index ea20a2779a..1951201ed1 100644 --- a/Mage.Sets/src/mage/cards/r/Rumination.java +++ b/Mage.Sets/src/mage/cards/r/Rumination.java @@ -56,7 +56,7 @@ public final class Rumination extends CardImpl { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); putOnLibrary(player, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/r/RunAfoul.java b/Mage.Sets/src/mage/cards/r/RunAfoul.java new file mode 100644 index 0000000000..db304c47c4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RunAfoul.java @@ -0,0 +1,41 @@ +package mage.cards.r; + +import java.util.UUID; + +import mage.abilities.effects.common.SacrificeEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.TargetOpponent; + +/** + * @author arcox + */ +public final class RunAfoul extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public RunAfoul(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + + // Target opponent sacrifices a creature with flying. + this.getSpellAbility().addEffect(new SacrificeEffect(filter, 1, "Target opponent")); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + public RunAfoul(final RunAfoul card) { + super(card); + } + + @Override + public RunAfoul copy() { + return new RunAfoul(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RunedHalo.java b/Mage.Sets/src/mage/cards/r/RunedHalo.java index 3800320f37..50861691d1 100644 --- a/Mage.Sets/src/mage/cards/r/RunedHalo.java +++ b/Mage.Sets/src/mage/cards/r/RunedHalo.java @@ -57,7 +57,7 @@ class RunedHaloSetProtectionEffect extends OneShotEffect { public RunedHaloSetProtectionEffect() { super(Outcome.Protect); - staticText = "

You have protection from the chosen name (You can't be targeted, dealt damage, or enchanted by anything with that name.)"; + staticText = "

You have protection from the chosen card name (You can't be targeted, dealt damage, or enchanted by anything with that name.)"; } public RunedHaloSetProtectionEffect(final RunedHaloSetProtectionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RunedServitor.java b/Mage.Sets/src/mage/cards/r/RunedServitor.java index 80651aab25..0f8e979d4a 100644 --- a/Mage.Sets/src/mage/cards/r/RunedServitor.java +++ b/Mage.Sets/src/mage/cards/r/RunedServitor.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -21,7 +21,7 @@ public final class RunedServitor extends CardImpl { this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(2); this.toughness = new MageInt(2); - this.addAbility(new DiesTriggeredAbility(new DrawCardAllEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardAllEffect(1), false)); } public RunedServitor(final RunedServitor card) { diff --git a/Mage.Sets/src/mage/cards/r/Runewing.java b/Mage.Sets/src/mage/cards/r/Runewing.java index 4532e36b6d..f5a329d2bc 100644 --- a/Mage.Sets/src/mage/cards/r/Runewing.java +++ b/Mage.Sets/src/mage/cards/r/Runewing.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.FlyingAbility; @@ -37,7 +37,7 @@ public final class Runewing extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Runewing dies, draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); } public Runewing(final Runewing card) { diff --git a/Mage.Sets/src/mage/cards/r/RunicArmasaur.java b/Mage.Sets/src/mage/cards/r/RunicArmasaur.java index 4c589622b4..3ec878d959 100644 --- a/Mage.Sets/src/mage/cards/r/RunicArmasaur.java +++ b/Mage.Sets/src/mage/cards/r/RunicArmasaur.java @@ -1,82 +1,82 @@ -package mage.cards.r; - -import java.util.UUID; -import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.AbilityType; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.stack.StackAbility; - -/** - * - * @author LevelX2 - */ -public final class RunicArmasaur extends CardImpl { - - public RunicArmasaur(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); - - this.subtype.add(SubType.DINOSAUR); - this.power = new MageInt(2); - this.toughness = new MageInt(5); - - // Whenever an opponent activates an ability of a creature or a land that is not a mana ability, you may draw a card. - this.addAbility(new RunicArmasaurTriggeredAbility()); - } - - public RunicArmasaur(final RunicArmasaur card) { - super(card); - } - - @Override - public RunicArmasaur copy() { - return new RunicArmasaur(this); - } -} - -class RunicArmasaurTriggeredAbility extends TriggeredAbilityImpl { - - RunicArmasaurTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true); - } - - RunicArmasaurTriggeredAbility(final RunicArmasaurTriggeredAbility ability) { - super(ability); - } - - @Override - public RunicArmasaurTriggeredAbility copy() { - return new RunicArmasaurTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.ACTIVATED_ABILITY; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); - if (stackAbility != null - && stackAbility.getAbilityType() == AbilityType.ACTIVATED - && game.getOpponents(this.getControllerId()).contains(stackAbility.getControllerId())) { - MageObject abilitySourceObject = stackAbility.getSourceObject(game); - return abilitySourceObject != null && (abilitySourceObject.isLand() || abilitySourceObject.isCreature()); - } - return false; - } - - @Override - public String getRule() { - return "Whenever an opponent activates an ability of a creature or a land that is not a mana ability, you may draw a card."; - } -} +package mage.cards.r; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AbilityType; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.stack.StackAbility; + +/** + * + * @author LevelX2 + */ +public final class RunicArmasaur extends CardImpl { + + public RunicArmasaur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); + + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + + // Whenever an opponent activates an ability of a creature or a land that is not a mana ability, you may draw a card. + this.addAbility(new RunicArmasaurTriggeredAbility()); + } + + public RunicArmasaur(final RunicArmasaur card) { + super(card); + } + + @Override + public RunicArmasaur copy() { + return new RunicArmasaur(this); + } +} + +class RunicArmasaurTriggeredAbility extends TriggeredAbilityImpl { + + RunicArmasaurTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true); + } + + RunicArmasaurTriggeredAbility(final RunicArmasaurTriggeredAbility ability) { + super(ability); + } + + @Override + public RunicArmasaurTriggeredAbility copy() { + return new RunicArmasaurTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.ACTIVATED_ABILITY; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility != null + && stackAbility.getAbilityType() == AbilityType.ACTIVATED + && game.getOpponents(this.getControllerId()).contains(stackAbility.getControllerId())) { + MageObject abilitySourceObject = stackAbility.getSourceObject(game); + return abilitySourceObject != null && (abilitySourceObject.isLand() || abilitySourceObject.isCreature()); + } + return false; + } + + @Override + public String getRule() { + return "Whenever an opponent activates an ability of a creature or a land that is not a mana ability, you may draw a card."; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RushingTideZubera.java b/Mage.Sets/src/mage/cards/r/RushingTideZubera.java index f80c8015a4..fb8920811b 100644 --- a/Mage.Sets/src/mage/cards/r/RushingTideZubera.java +++ b/Mage.Sets/src/mage/cards/r/RushingTideZubera.java @@ -4,7 +4,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -31,7 +31,7 @@ public final class RushingTideZubera extends CardImpl { this.toughness = new MageInt(3); // When Rushing-Tide Zubera dies, if 4 or more damage was dealt to it this turn, draw three cards. - Ability ability = new ConditionalInterveningIfTriggeredAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(3)), new RushingTideZuberaCondition(), + Ability ability = new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(3)), new RushingTideZuberaCondition(), "When {this} dies, if 4 or more damage was dealt to it this turn, draw three cards."); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RustedRelic.java b/Mage.Sets/src/mage/cards/r/RustedRelic.java index 95014e11eb..b845edb68c 100644 --- a/Mage.Sets/src/mage/cards/r/RustedRelic.java +++ b/Mage.Sets/src/mage/cards/r/RustedRelic.java @@ -1,38 +1,38 @@ - - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; import mage.game.permanent.token.TokenImpl; -import mage.game.permanent.token.Token; + +import java.util.UUID; /** - * * @author Loki */ public final class RustedRelic extends CardImpl { - public RustedRelic (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + public RustedRelic(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + // Metalcraft — Rusted Relic is a 5/5 Golem artifact creature as long as you control three or more artifacts. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new ConditionalContinuousEffect( - new BecomesCreatureSourceEffect(new RustedRelicToken(), "artifact", Duration.WhileOnBattlefield), - MetalcraftCondition.instance, - "Metalcraft — {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts"))); + new ConditionalContinuousEffect( + new BecomesCreatureSourceEffect(new RustedRelicToken(), "artifact", Duration.WhileOnBattlefield), + MetalcraftCondition.instance, + "Metalcraft — {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts")) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); } - public RustedRelic (final RustedRelic card) { + public RustedRelic(final RustedRelic card) { super(card); } @@ -51,6 +51,7 @@ class RustedRelicToken extends TokenImpl { power = new MageInt(5); toughness = new MageInt(5); } + public RustedRelicToken(final RustedRelicToken token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/r/RyuseiTheFallingStar.java b/Mage.Sets/src/mage/cards/r/RyuseiTheFallingStar.java index 08a62c7fd6..58b9630ef6 100644 --- a/Mage.Sets/src/mage/cards/r/RyuseiTheFallingStar.java +++ b/Mage.Sets/src/mage/cards/r/RyuseiTheFallingStar.java @@ -4,7 +4,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -36,7 +36,7 @@ public final class RyuseiTheFallingStar extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(5); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new DiesTriggeredAbility(new DamageAllEffect(5, filter))); + this.addAbility(new DiesSourceTriggeredAbility(new DamageAllEffect(5, filter))); } public RyuseiTheFallingStar(final RyuseiTheFallingStar card) { diff --git a/Mage.Sets/src/mage/cards/s/SabaccGame.java b/Mage.Sets/src/mage/cards/s/SabaccGame.java index 7a60b3d2b1..733d46d314 100644 --- a/Mage.Sets/src/mage/cards/s/SabaccGame.java +++ b/Mage.Sets/src/mage/cards/s/SabaccGame.java @@ -76,7 +76,7 @@ class SabaccGameEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Permanent targetPermanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent targetPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (targetPermanent != null) { Player opponent = game.getPlayer(targetPermanent.getControllerId()); if (opponent != null) { diff --git a/Mage.Sets/src/mage/cards/s/SabertoothMauler.java b/Mage.Sets/src/mage/cards/s/SabertoothMauler.java new file mode 100644 index 0000000000..4ee840e721 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SabertoothMauler.java @@ -0,0 +1,51 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.common.MorbidCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SabertoothMauler extends CardImpl { + + public SabertoothMauler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // At the beginning of your end step, if a creature died this turn, put a +1/+1 counter on Sabertooth Mauler and untap it. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance() + ), TargetController.YOU, false + ), MorbidCondition.instance, "At the beginning of your end step, " + + "if a creature died this turn, put a +1/+1 counter on {this} and untap it." + ); + ability.addEffect(new UntapSourceEffect()); + this.addAbility(ability); + } + + private SabertoothMauler(final SabertoothMauler card) { + super(card); + } + + @Override + public SabertoothMauler copy() { + return new SabertoothMauler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SacellumGodspeaker.java b/Mage.Sets/src/mage/cards/s/SacellumGodspeaker.java index dae6282025..bed2265a6f 100644 --- a/Mage.Sets/src/mage/cards/s/SacellumGodspeaker.java +++ b/Mage.Sets/src/mage/cards/s/SacellumGodspeaker.java @@ -33,7 +33,9 @@ public final class SacellumGodspeaker extends CardImpl { this.toughness = new MageInt(2); // {T}: Reveal any number of creature cards with power 5 or greater from your hand. Add {G} for each card revealed this way. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new SacellumGodspeakerEffect(), new TapSourceCost())); + SimpleManaAbility ability = new SimpleManaAbility(Zone.BATTLEFIELD, new SacellumGodspeakerEffect(), new TapSourceCost()); + ability.setUndoPossible(false); + this.addAbility(ability); } public SacellumGodspeaker(final SacellumGodspeaker card) { diff --git a/Mage.Sets/src/mage/cards/s/SacredPrey.java b/Mage.Sets/src/mage/cards/s/SacredPrey.java index fe1ff1407a..ce4c023b4d 100644 --- a/Mage.Sets/src/mage/cards/s/SacredPrey.java +++ b/Mage.Sets/src/mage/cards/s/SacredPrey.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class SacredPrey extends CardImpl { this.toughness = new MageInt(1); // Whenever Sacred Prey becomes blocked, you gain 1 life. - this.addAbility(new BecomesBlockedTriggeredAbility(new GainLifeEffect(1), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new GainLifeEffect(1), false)); } public SacredPrey(final SacredPrey card) { diff --git a/Mage.Sets/src/mage/cards/s/SacredRites.java b/Mage.Sets/src/mage/cards/s/SacredRites.java index a1302c4ba3..3104c737bd 100644 --- a/Mage.Sets/src/mage/cards/s/SacredRites.java +++ b/Mage.Sets/src/mage/cards/s/SacredRites.java @@ -1,24 +1,23 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetCardInHand; +import mage.target.common.TargetDiscard; + +import java.util.UUID; /** - * * @author cbt33 */ public final class SacredRites extends CardImpl { @@ -30,7 +29,7 @@ public final class SacredRites extends CardImpl { this.getSpellAbility().addEffect(new SacredRitesEffect()); } - public SacredRites(final SacredRites card) { + private SacredRites(final SacredRites card) { super(card); } @@ -43,11 +42,11 @@ public final class SacredRites extends CardImpl { class SacredRitesEffect extends OneShotEffect { SacredRitesEffect() { - super(Outcome.Benefit); + super(Outcome.AIDontUseIt); this.staticText = "Discard any number of cards. Creatures you control get +0/+1 until end of turn for each card discarded this way."; } - SacredRitesEffect(final SacredRitesEffect effect) { + private SacredRitesEffect(final SacredRitesEffect effect) { super(effect); } @@ -59,21 +58,15 @@ class SacredRitesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Target target = new TargetCardInHand(0, Integer.MAX_VALUE, new FilterCard("cards to discard")); - while (controller.canRespond() && !target.isChosen()) { - target.choose(Outcome.BoostCreature, controller.getId(), source.getSourceId(), game); - } - int numDiscarded = 0; - for (UUID targetId : target.getTargets()) { - Card card = controller.getHand().get(targetId, game); - if (controller.discard(card, source, game)) { - numDiscarded++; - } - } - game.addEffect(new BoostControlledEffect(0, numDiscarded, Duration.EndOfTurn), source); - return true; + if (controller == null) { + return false; } - return false; + Target target = new TargetDiscard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD, controller.getId()); + target.choose(outcome, controller.getId(), source.getSourceId(), game); + int numDiscarded = controller.discard(new CardsImpl(target.getTargets()), source, game).size(); + if (numDiscarded > 0) { + game.addEffect(new BoostControlledEffect(0, numDiscarded, Duration.EndOfTurn), source); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SadisticAugermage.java b/Mage.Sets/src/mage/cards/s/SadisticAugermage.java index f6b2d9d83f..dfbd3d36f5 100644 --- a/Mage.Sets/src/mage/cards/s/SadisticAugermage.java +++ b/Mage.Sets/src/mage/cards/s/SadisticAugermage.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -32,7 +32,7 @@ public final class SadisticAugermage extends CardImpl { this.toughness = new MageInt(1); // When Sadistic Augermage dies, each player puts a card from their hand on top of their library. - this.addAbility(new DiesTriggeredAbility(new WidespreadPanicEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new WidespreadPanicEffect())); } public SadisticAugermage(final SadisticAugermage card) { diff --git a/Mage.Sets/src/mage/cards/s/SageOfTheFalls.java b/Mage.Sets/src/mage/cards/s/SageOfTheFalls.java index 29ad92531e..b40b05b3e3 100644 --- a/Mage.Sets/src/mage/cards/s/SageOfTheFalls.java +++ b/Mage.Sets/src/mage/cards/s/SageOfTheFalls.java @@ -1,7 +1,7 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -18,8 +18,7 @@ import java.util.UUID; */ public final class SageOfTheFalls extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("{this} or another non-Human creature"); + private static final FilterPermanent filter = new FilterCreaturePermanent("non-Human creature"); static { filter.add(Predicates.not(SubType.HUMAN.getPredicate())); @@ -34,9 +33,9 @@ public final class SageOfTheFalls extends CardImpl { this.toughness = new MageInt(5); // Whenever Sage of the Falls or another non-Human creature enters the battlefield under you control, you may draw a card. If you do, discard a card. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility( - new DrawDiscardControllerEffect(1, 1, true), filter - )); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(new DrawDiscardControllerEffect( + 1, 1, true + ), filter, false, true)); } private SageOfTheFalls(final SageOfTheFalls card) { diff --git a/Mage.Sets/src/mage/cards/s/SakashimaTheImpostor.java b/Mage.Sets/src/mage/cards/s/SakashimaTheImpostor.java index 8fed936baa..a0e23f4e3d 100644 --- a/Mage.Sets/src/mage/cards/s/SakashimaTheImpostor.java +++ b/Mage.Sets/src/mage/cards/s/SakashimaTheImpostor.java @@ -64,7 +64,7 @@ class SakashimaTheImpostorApplier extends ApplyToPermanent { permanent.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnToHandSourceEffect(true)), false), new ManaCostsImpl("{2}{U}{U}") - ), game); + ), source.getSourceId(), game); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java b/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java index 3329be2819..95f4f69c7e 100644 --- a/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java +++ b/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java @@ -23,7 +23,7 @@ public final class SaltRoadAmbushers extends CardImpl { public SaltRoadAmbushers(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.WARRIOR); this.power = new MageInt(3); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/s/SaltcrustedSteppe.java b/Mage.Sets/src/mage/cards/s/SaltcrustedSteppe.java index 9b20de817d..e21e5415ca 100644 --- a/Mage.Sets/src/mage/cards/s/SaltcrustedSteppe.java +++ b/Mage.Sets/src/mage/cards/s/SaltcrustedSteppe.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -7,9 +6,10 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; -import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ import mage.counters.CounterType; public final class SaltcrustedSteppe extends CardImpl { public SaltcrustedSteppe(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {tap}: Add {C}. this.addAbility(new ColorlessManaAbility()); @@ -36,7 +36,8 @@ public final class SaltcrustedSteppe extends CardImpl { this.addAbility(ability); // {1}, Remove X storage counters from Saltcrusted Steppe: Add X mana in any combination of {G} and/or {W}. ability = new SimpleManaAbility(Zone.BATTLEFIELD, - new AddManaInAnyCombinationEffect(RemovedCountersForCostValue.instance, ColoredManaSymbol.G, ColoredManaSymbol.W), + new AddManaInAnyCombinationEffect(RemovedCountersForCostValue.instance, + new CountersSourceCount(CounterType.STORAGE), ColoredManaSymbol.G, ColoredManaSymbol.W), new GenericManaCost(1)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SalvageDrone.java b/Mage.Sets/src/mage/cards/s/SalvageDrone.java index 66e0942638..e8944e482f 100644 --- a/Mage.Sets/src/mage/cards/s/SalvageDrone.java +++ b/Mage.Sets/src/mage/cards/s/SalvageDrone.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.abilities.keyword.DevoidAbility; import mage.abilities.keyword.IngestAbility; @@ -30,7 +30,7 @@ public final class SalvageDrone extends CardImpl { // Ingest (Whenever this creature deals combat damage to a player, that player exiles the top card of their library.) this.addAbility(new IngestAbility()); // When Salvage Drone dies, you may draw a card. If you do, discard a card. - this.addAbility(new DiesTriggeredAbility(new DrawDiscardControllerEffect(1, 1, true), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawDiscardControllerEffect(1, 1, true), false)); } diff --git a/Mage.Sets/src/mage/cards/s/SamiteBlessing.java b/Mage.Sets/src/mage/cards/s/SamiteBlessing.java index 1168cf6606..616b24c3b1 100644 --- a/Mage.Sets/src/mage/cards/s/SamiteBlessing.java +++ b/Mage.Sets/src/mage/cards/s/SamiteBlessing.java @@ -1,58 +1,58 @@ -package mage.cards.s; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToTargetEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Zone; - -/** - * - * @author jeffwadsworth - */ -public final class SamiteBlessing extends CardImpl { - - private static final String rule = "Enchanted creature has \"{T}: The next time a source of your choice would deal damage to target creature this turn, prevent that damage.\""; - - public SamiteBlessing(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // Enchanted creature has "{tap}: The next time a source of your choice would deal damage to target creature this turn, prevent that damage." - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventNextDamageFromChosenSourceToTargetEffect(Duration.EndOfTurn), new TapSourceCost()); - ability2.addTarget(new TargetCreaturePermanent()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability2, AttachmentType.AURA, Duration.WhileOnBattlefield, rule))); - - } - - public SamiteBlessing(final SamiteBlessing card) { - super(card); - } - - @Override - public SamiteBlessing copy() { - return new SamiteBlessing(this); - } -} +package mage.cards.s; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; + +/** + * + * @author jeffwadsworth + */ +public final class SamiteBlessing extends CardImpl { + + private static final String rule = "Enchanted creature has \"{T}: The next time a source of your choice would deal damage to target creature this turn, prevent that damage.\""; + + public SamiteBlessing(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has "{tap}: The next time a source of your choice would deal damage to target creature this turn, prevent that damage." + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventNextDamageFromChosenSourceToTargetEffect(Duration.EndOfTurn), new TapSourceCost()); + ability2.addTarget(new TargetCreaturePermanent()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability2, AttachmentType.AURA, Duration.WhileOnBattlefield, rule))); + + } + + public SamiteBlessing(final SamiteBlessing card) { + super(card); + } + + @Override + public SamiteBlessing copy() { + return new SamiteBlessing(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SamiteCenserBearer.java b/Mage.Sets/src/mage/cards/s/SamiteCenserBearer.java index ebc34483b8..411236965c 100644 --- a/Mage.Sets/src/mage/cards/s/SamiteCenserBearer.java +++ b/Mage.Sets/src/mage/cards/s/SamiteCenserBearer.java @@ -72,7 +72,7 @@ class SamiteCenserBearerEffect extends OneShotEffect { PreventDamageToTargetEffect effect = new PreventDamageToTargetEffect(Duration.EndOfTurn, 1); List permanents = game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, player.getId(), game); for (Permanent permanent : permanents) { - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/s/SanctuaryBlade.java b/Mage.Sets/src/mage/cards/s/SanctuaryBlade.java new file mode 100644 index 0000000000..287ca9060c --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanctuaryBlade.java @@ -0,0 +1,55 @@ +package mage.cards.s; + +import mage.abilities.common.AttachedToCreatureSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.ChooseColorEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.keyword.ProtectionChosenColorAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class SanctuaryBlade extends CardImpl { + + public SanctuaryBlade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + this.subtype.add(SubType.EQUIPMENT); + + // As Sanctuary Blade becomes attached to a creature, choose a color. + this.addAbility(new AttachedToCreatureSourceTriggeredAbility(new ChooseColorEffect(Outcome.Benefit), false)); + + // Equipped creature gets +2/+0 and has protection from the last chosen color. + Effect boostEffect = new BoostEquippedEffect(2, 0); + boostEffect.concatBy("."); + SimpleStaticAbility ability = new SimpleStaticAbility(Zone.BATTLEFIELD, boostEffect); + ProtectionChosenColorAttachedEffect protectionEfect = new ProtectionChosenColorAttachedEffect(false); + protectionEfect.setText("and has protection from the last chosen color."); + ability.addEffect(protectionEfect); + this.addAbility(ability); + + // Equip {3} + this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(3))); + } + + private SanctuaryBlade(final SanctuaryBlade card) { + super(card); + } + + @Override + public SanctuaryBlade copy() { + return new SanctuaryBlade(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SanctuaryLockdown.java b/Mage.Sets/src/mage/cards/s/SanctuaryLockdown.java new file mode 100644 index 0000000000..25c84a9b9c --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanctuaryLockdown.java @@ -0,0 +1,61 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SanctuaryLockdown extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent(SubType.HUMAN, "Humans"); + private static final FilterControlledPermanent filter2 + = new FilterControlledPermanent(SubType.HUMAN, "untapped Humans you control"); + + static { + filter2.add(Predicates.not(TappedPredicate.instance)); + } + + public SanctuaryLockdown(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // Humans you control get +1/+1. + this.addAbility(new SimpleStaticAbility( + new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter) + )); + + // {2}, Tap two untapped Humans you control: Tap target creature an opponent controls. + Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new GenericManaCost(2)); + ability.addCost(new TapTargetCost(new TargetControlledPermanent(2, filter2))); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private SanctuaryLockdown(final SanctuaryLockdown card) { + super(card); + } + + @Override + public SanctuaryLockdown copy() { + return new SanctuaryLockdown(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SanctuarySmasher.java b/Mage.Sets/src/mage/cards/s/SanctuarySmasher.java new file mode 100644 index 0000000000..744e77b9ba --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanctuarySmasher.java @@ -0,0 +1,54 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SanctuarySmasher extends CardImpl { + + public SanctuarySmasher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); + + this.subtype.add(SubType.RHINO); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(6); + this.toughness = new MageInt(4); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Cycling {2}{R} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}{R}"))); + + // When you cycle Sanctuary Smasher, put a first strike counter on target creature you control. + Ability ability = new CycleTriggeredAbility( + new AddCountersTargetEffect(CounterType.FIRST_STRIKE.createInstance()) + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private SanctuarySmasher(final SanctuarySmasher card) { + super(card); + } + + @Override + public SanctuarySmasher copy() { + return new SanctuarySmasher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfAll.java b/Mage.Sets/src/mage/cards/s/SanctumOfAll.java new file mode 100644 index 0000000000..97386229b2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanctumOfAll.java @@ -0,0 +1,109 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.search.SearchLibraryGraveyardPutOntoBattlefieldEffect; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class SanctumOfAll extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + private static final FilterCard filterCard = new FilterCard("a Shrine card"); + + static final PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter); + + static { + filter.add(SubType.SHRINE.getPredicate()); + filterCard.add(SubType.SHRINE.getPredicate()); + } + + public SanctumOfAll(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{U}{B}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + + // At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SearchLibraryGraveyardPutOntoBattlefieldEffect(filterCard), TargetController.YOU, true)); + + // If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time. + this.addAbility(new SimpleStaticAbility(new SanctumOfAllTriggerEffect()).addHint(new ValueHint("Shrines you control", count))); + } + + private SanctumOfAll(final SanctumOfAll card) { + super(card); + } + + @Override + public SanctumOfAll copy() { + return new SanctumOfAll(this); + } +} + +class SanctumOfAllTriggerEffect extends ReplacementEffectImpl { + + SanctumOfAllTriggerEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time"; + } + + private SanctumOfAllTriggerEffect(SanctumOfAllTriggerEffect effect) { + super(effect); + } + + @Override + public SanctumOfAllTriggerEffect copy() { + return new SanctumOfAllTriggerEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(event.getAmount() + 1); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.NUMBER_OF_TRIGGERS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + // Only triggers of the controller of Sanctum of All + if (source.isControlledBy(event.getPlayerId())) { + // Only trigger while you control six or more Shrines + int numShrines = SanctumOfAll.count.calculate(game, source, this); + if (numShrines >= 6) { + // Only for triggers of other Shrines + Permanent permanent = game.getPermanent(event.getSourceId()); + return permanent != null + && !permanent.getId().equals(source.getSourceId()) + && permanent.hasSubtype(SubType.SHRINE, game); + } + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java b/Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java new file mode 100644 index 0000000000..a132be5050 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; + +/** + * @author jmharmon + */ + +public final class SanctumOfCalmWaters extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filter); + + static { + filter.add(SubType.SHRINE.getPredicate()); + } + + public SanctumOfCalmWaters(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + + // At the beginning of your precombat main phase, you may draw X cards, where X is the number of Shrines you control. If you do, discard a card. + Ability ability = new BeginningOfPreCombatMainTriggeredAbility(new DrawCardSourceControllerEffect(xValue) + .setText("you may draw X cards, where X is the number of Shrines you control"), + TargetController.YOU, true) + .addHint(new ValueHint("Shrines you control", xValue)); + ability.addEffect(new DiscardControllerEffect(1).setText("If you do, discard a card")); + this.addAbility(ability); + } + + public SanctumOfCalmWaters(final SanctumOfCalmWaters card) { + super(card); + } + + @Override + public SanctumOfCalmWaters copy() { + return new SanctumOfCalmWaters(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfFruitfulHarvest.java b/Mage.Sets/src/mage/cards/s/SanctumOfFruitfulHarvest.java new file mode 100644 index 0000000000..62c8b50374 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanctumOfFruitfulHarvest.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.Mana; +import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.mana.DynamicManaEffect; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class SanctumOfFruitfulHarvest extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filter); + + static { + filter.add(SubType.SHRINE.getPredicate()); + } + + public SanctumOfFruitfulHarvest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + + // At the beginning of your precombat main phase, add X mana of any one color, where X is the number of Shrines you control. + this.addAbility(new BeginningOfPreCombatMainTriggeredAbility( + new DynamicManaEffect( + Mana.AnyMana(1), + xValue, + "add X mana of any one color, where X is the number of Shrines you control", + true), + TargetController.YOU, false) + .addHint(new ValueHint("Shrines you control", xValue))); + } + + private SanctumOfFruitfulHarvest(final SanctumOfFruitfulHarvest card) { + super(card); + } + + @Override + public SanctumOfFruitfulHarvest copy() { + return new SanctumOfFruitfulHarvest(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java b/Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java new file mode 100644 index 0000000000..d6ef9e0495 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java @@ -0,0 +1,64 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author jmharmon + */ + +public final class SanctumOfShatteredHeights extends CardImpl { + + private static final FilterCard filter = new FilterCard("a land card or Shrine card"); + private static final FilterPermanent filterShrinesOnly = new FilterControlledPermanent("Shrine you control"); + private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filterShrinesOnly); + + static { + filter.add(Predicates.or(CardType.LAND.getPredicate(), SubType.SHRINE.getPredicate())); + filterShrinesOnly.add(SubType.SHRINE.getPredicate()); + } + + public SanctumOfShatteredHeights(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + + // {1}, Discard a land card or Shrine card: Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(xValue) + .setText("Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control"), + new ManaCostsImpl<>("{1}")) + .addHint(new ValueHint("Shrines you control", xValue)); + ability.addCost(new DiscardTargetCost(new TargetCardInHand(filter))); + ability.addTarget(new TargetCreatureOrPlaneswalker()); + this.addAbility(ability); + } + + public SanctumOfShatteredHeights(final SanctumOfShatteredHeights card) { + super(card); + } + + @Override + public SanctumOfShatteredHeights copy() { + return new SanctumOfShatteredHeights(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfStoneFangs.java b/Mage.Sets/src/mage/cards/s/SanctumOfStoneFangs.java new file mode 100644 index 0000000000..2067849ca4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanctumOfStoneFangs.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class SanctumOfStoneFangs extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(""); + private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filter, null); + + static { + filter.add(SubType.SHRINE.getPredicate()); + } + + public SanctumOfStoneFangs(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + + // At the beginning of your precombat main phase, each opponent loses X life and you gain X life, where X is the number of Shrines you control. + Ability ability = new BeginningOfPreCombatMainTriggeredAbility( + new LoseLifeOpponentsEffect(xValue).setText("each opponent loses X life"), + TargetController.YOU, false) + .addHint(new ValueHint("Shrines you control", xValue)); + ability.addEffect(new GainLifeEffect(xValue).setText("and you gain X life, where X is the number of Shrines you control")); + this.addAbility(ability); + } + + private SanctumOfStoneFangs(final SanctumOfStoneFangs card) { + super(card); + } + + @Override + public SanctumOfStoneFangs copy() { + return new SanctumOfStoneFangs(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfTranquilLight.java b/Mage.Sets/src/mage/cards/s/SanctumOfTranquilLight.java new file mode 100644 index 0000000000..50ea1e8b8d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanctumOfTranquilLight.java @@ -0,0 +1,76 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.cost.CostModificationSourceEffect; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class SanctumOfTranquilLight extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("Shrine you control"); + private static final PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter); + + static { + filter.add(SubType.SHRINE.getPredicate()); + } + + public SanctumOfTranquilLight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + + // {5}{W}: Tap target creature. This ability costs {1} less to activate for each Shrine you control. + Ability ability = new SimpleActivatedAbility(new SanctumOfTranquilLightActivatedAbility()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new CostModificationSourceEffect(Duration.EndOfGame, Outcome.Benefit, SanctumOfTranquilLightActivatedAbility.class, count, true).setText("")) + .addHint(new ValueHint("Shrines you control", count))); + } + + private SanctumOfTranquilLight(final SanctumOfTranquilLight card) { + super(card); + } + + @Override + public SanctumOfTranquilLight copy() { + return new SanctumOfTranquilLight(this); + } +} + +class SanctumOfTranquilLightActivatedAbility extends SimpleActivatedAbility { + + SanctumOfTranquilLightActivatedAbility() { + super(new TapTargetEffect().setText("target creature. This ability costs {1} less to activate for each Shrine you control"), new ManaCostsImpl<>("{5}{W}")); + } + + private SanctumOfTranquilLightActivatedAbility(SanctumOfTranquilLightActivatedAbility ability) { + super(ability); + } + + @Override + public SanctumOfTranquilLightActivatedAbility copy() { + return new SanctumOfTranquilLightActivatedAbility(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SandsteppeOutcast.java b/Mage.Sets/src/mage/cards/s/SandsteppeOutcast.java index fcc419c66a..72b0150e3e 100644 --- a/Mage.Sets/src/mage/cards/s/SandsteppeOutcast.java +++ b/Mage.Sets/src/mage/cards/s/SandsteppeOutcast.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; @@ -15,14 +13,15 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** - * * @author emerald000 */ public final class SandsteppeOutcast extends CardImpl { public SandsteppeOutcast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WARRIOR); this.power = new MageInt(2); @@ -31,12 +30,12 @@ public final class SandsteppeOutcast extends CardImpl { // When Sandsteppe Outcast enters the battlefield, choose one - // * Put a +1/+1 counter on Sandsteppe Outcast. Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())); - + // * Create a 1/1 white Spirit creature token with flying. Mode mode = new Mode(); - mode.addEffect(new CreateTokenEffect(new SpiritWhiteToken("FRF"))); + mode.addEffect(new CreateTokenEffect(new SpiritWhiteToken())); ability.addMode(mode); - this.addAbility(ability); + this.addAbility(ability); } public SandsteppeOutcast(final SandsteppeOutcast card) { diff --git a/Mage.Sets/src/mage/cards/s/SandsteppeScavenger.java b/Mage.Sets/src/mage/cards/s/SandsteppeScavenger.java index 770fa83768..d23385995d 100644 --- a/Mage.Sets/src/mage/cards/s/SandsteppeScavenger.java +++ b/Mage.Sets/src/mage/cards/s/SandsteppeScavenger.java @@ -18,7 +18,7 @@ public final class SandsteppeScavenger extends CardImpl { public SandsteppeScavenger(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.subtype.add(SubType.SCOUT); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/s/SandstoneOracle.java b/Mage.Sets/src/mage/cards/s/SandstoneOracle.java index 3762ff4d0a..775c31c40c 100644 --- a/Mage.Sets/src/mage/cards/s/SandstoneOracle.java +++ b/Mage.Sets/src/mage/cards/s/SandstoneOracle.java @@ -74,7 +74,7 @@ class SandstoneOracleEffect extends OneShotEffect { game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " has chosen " + opponent.getLogName()); int cardsDiff = opponent.getHand().size() - controller.getHand().size(); if (cardsDiff > 0) { - controller.drawCards(cardsDiff, game); + controller.drawCards(cardsDiff, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/s/SandwurmConvergence.java b/Mage.Sets/src/mage/cards/s/SandwurmConvergence.java index 63d0b0374a..ba04fcf1ca 100644 --- a/Mage.Sets/src/mage/cards/s/SandwurmConvergence.java +++ b/Mage.Sets/src/mage/cards/s/SandwurmConvergence.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; @@ -16,10 +14,11 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.game.permanent.token.WurmToken3; +import mage.game.permanent.token.Wurm55Token; + +import java.util.UUID; /** - * * @author fireshoes */ public final class SandwurmConvergence extends CardImpl { @@ -39,7 +38,7 @@ public final class SandwurmConvergence extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); // At the beginning of your end step, create a 5/5 green Wurm creature token. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new WurmToken3()), TargetController.YOU, false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new Wurm55Token()), TargetController.YOU, false)); } public SandwurmConvergence(final SandwurmConvergence card) { diff --git a/Mage.Sets/src/mage/cards/s/SanguineBond.java b/Mage.Sets/src/mage/cards/s/SanguineBond.java index 9a25f2c7ae..fa12709d1e 100644 --- a/Mage.Sets/src/mage/cards/s/SanguineBond.java +++ b/Mage.Sets/src/mage/cards/s/SanguineBond.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; @@ -13,15 +11,15 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author North */ public final class SanguineBond extends CardImpl { public SanguineBond(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); // Whenever you gain life, target opponent loses that much life. SanguineBondTriggeredAbility ability = new SanguineBondTriggeredAbility(); diff --git a/Mage.Sets/src/mage/cards/s/SanguineIndulgence.java b/Mage.Sets/src/mage/cards/s/SanguineIndulgence.java new file mode 100644 index 0000000000..1b17df6ac3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanguineIndulgence.java @@ -0,0 +1,53 @@ +package mage.cards.s; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SanguineIndulgence extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2); + private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn"); + + public SanguineIndulgence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); + + // This spell costs {3} less to cast if you've gained 3 or more life this turn. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionSourceEffect(3, condition) + ).addHint(hint).setRuleAtTheTop(true), + new PlayerGainedLifeWatcher()); + + // Return up to two target creature cards from your graveyard to your hand. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD + )); + } + + private SanguineIndulgence(final SanguineIndulgence card) { + super(card); + } + + @Override + public SanguineIndulgence copy() { + return new SanguineIndulgence(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SanityGrinding.java b/Mage.Sets/src/mage/cards/s/SanityGrinding.java index a63afd36f9..ae141fcb70 100644 --- a/Mage.Sets/src/mage/cards/s/SanityGrinding.java +++ b/Mage.Sets/src/mage/cards/s/SanityGrinding.java @@ -1,33 +1,26 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author jeffwadsworth - * */ public final class SanityGrinding extends CardImpl { public SanityGrinding(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{U}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}{U}{U}"); // Chroma - Reveal the top ten cards of your library. For each blue mana symbol in the mana costs of the revealed cards, target opponent puts the top card of their library into their graveyard. Then put the cards you revealed this way on the bottom of your library in any order. this.getSpellAbility().addEffect(new SanityGrindingEffect()); @@ -49,7 +42,9 @@ class SanityGrindingEffect extends OneShotEffect { public SanityGrindingEffect() { super(Outcome.Neutral); - staticText = "Chroma — Reveal the top ten cards of your library. For each blue mana symbol in the mana costs of the revealed cards, target opponent puts the top card of their library into their graveyard. Then put the cards you revealed this way on the bottom of your library in any order"; + staticText = "Chroma — Reveal the top ten cards of your library. " + + "For each blue mana symbol in the mana costs of the revealed cards, target opponent mills a card. " + + "Then put the cards you revealed this way on the bottom of your library in any order"; } public SanityGrindingEffect(final SanityGrindingEffect effect) { @@ -68,8 +63,7 @@ class SanityGrindingEffect extends OneShotEffect { controller.revealCards(sourceObject.getIdName(), revealed, game); Player targetOpponent = game.getPlayer(source.getFirstTarget()); if (targetOpponent != null) { - targetOpponent.moveCards(targetOpponent.getLibrary().getTopCards(game, new ChromaSanityGrindingCount(revealed).calculate(game, source, this)), - Zone.GRAVEYARD, source, game); + targetOpponent.millCards(new ChromaSanityGrindingCount(revealed).calculate(game, source, this), source, game); } return controller.putCardsOnBottomOfLibrary(revealed, game, source, true); } diff --git a/Mage.Sets/src/mage/cards/s/SapphireLeech.java b/Mage.Sets/src/mage/cards/s/SapphireLeech.java index 8b7e1386d7..31d8772e27 100644 --- a/Mage.Sets/src/mage/cards/s/SapphireLeech.java +++ b/Mage.Sets/src/mage/cards/s/SapphireLeech.java @@ -1,23 +1,23 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author LoneFox */ public final class SapphireLeech extends CardImpl { @@ -29,7 +29,7 @@ public final class SapphireLeech extends CardImpl { } public SapphireLeech(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.LEECH); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -38,7 +38,7 @@ public final class SapphireLeech extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Blue spells you cast cost {U} more to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{U}")))); + new SpellsCostIncreasingAllEffect(new ManaCostsImpl("{U}"), filter, TargetController.YOU))); } public SapphireLeech(final SapphireLeech card) { diff --git a/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java b/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java index df66369b2e..336099b071 100644 --- a/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java +++ b/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java @@ -1,27 +1,24 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CantBeBlockedByAllSourceEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; +import java.util.Objects; +import java.util.UUID; + /** - * * @author L_J */ public final class SaprazzanBreaker extends CardImpl { @@ -36,7 +33,7 @@ public final class SaprazzanBreaker extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SaprazzanBreakerEffect(), new ManaCostsImpl("{U}"))); } - public SaprazzanBreaker(final SaprazzanBreaker card) { + private SaprazzanBreaker(final SaprazzanBreaker card) { super(card); } @@ -48,12 +45,12 @@ public final class SaprazzanBreaker extends CardImpl { class SaprazzanBreakerEffect extends OneShotEffect { - public SaprazzanBreakerEffect() { + SaprazzanBreakerEffect() { super(Outcome.Benefit); - this.staticText = "Put the top card of your library into your graveyard. If that card is a land card, {this} can't be blocked this turn"; + this.staticText = "Mill a card. If a land card was milled this way, {this} can't be blocked this turn"; } - public SaprazzanBreakerEffect(final SaprazzanBreakerEffect effect) { + private SaprazzanBreakerEffect(final SaprazzanBreakerEffect effect) { super(effect); } @@ -65,16 +62,12 @@ class SaprazzanBreakerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Card card = player.getLibrary().getFromTop(game); - if (card != null) { - player.moveCards(card, Zone.GRAVEYARD, source, game); - if (card.isLand()) { - game.addEffect(new CantBeBlockedByAllSourceEffect(StaticFilters.FILTER_PERMANENT_CREATURES, Duration.EndOfTurn), source); - } - } - return true; + if (player == null) { + return false; } - return false; + if (player.millCards(1, source, game).getCards(game).stream().filter(Objects::nonNull).anyMatch(MageObject::isLand)) { + game.addEffect(new CantBeBlockedByAllSourceEffect(StaticFilters.FILTER_PERMANENT_CREATURES, Duration.EndOfTurn), source); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SaprazzanHeir.java b/Mage.Sets/src/mage/cards/s/SaprazzanHeir.java index 3fea5cefbb..111f4df99c 100644 --- a/Mage.Sets/src/mage/cards/s/SaprazzanHeir.java +++ b/Mage.Sets/src/mage/cards/s/SaprazzanHeir.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class SaprazzanHeir extends CardImpl { this.toughness = new MageInt(1); // Whenever Saprazzan Heir becomes blocked, you may draw three cards. - this.addAbility(new BecomesBlockedTriggeredAbility(new DrawCardSourceControllerEffect(3), true)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(3), true)); } public SaprazzanHeir(final SaprazzanHeir card) { diff --git a/Mage.Sets/src/mage/cards/s/SaprazzanRaider.java b/Mage.Sets/src/mage/cards/s/SaprazzanRaider.java index 50ab00e49d..cb0d63682f 100644 --- a/Mage.Sets/src/mage/cards/s/SaprazzanRaider.java +++ b/Mage.Sets/src/mage/cards/s/SaprazzanRaider.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class SaprazzanRaider extends CardImpl { this.toughness = new MageInt(2); // When Saprazzan Raider becomes blocked, return it to its owner's hand. - this.addAbility(new BecomesBlockedTriggeredAbility(new ReturnToHandSourceEffect(), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new ReturnToHandSourceEffect(), false)); } public SaprazzanRaider(final SaprazzanRaider card) { diff --git a/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java b/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java index 7fd3df301e..3a604196c9 100644 --- a/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java +++ b/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java @@ -13,14 +13,13 @@ import mage.game.permanent.token.SaprolingToken; import java.util.UUID; /** - * * @author noahg */ public final class SaprolingInfestation extends CardImpl { public SaprolingInfestation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); - + // Whenever a player kicks a spell, you put a 1/1 green Saproling creature token onto the battlefield. this.addAbility(new SaprolingInfestationTriggeredAbility()); @@ -38,7 +37,7 @@ public final class SaprolingInfestation extends CardImpl { class SaprolingInfestationTriggeredAbility extends TriggeredAbilityImpl { SaprolingInfestationTriggeredAbility() { - super(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken("INV")), false); + super(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken()), false); } SaprolingInfestationTriggeredAbility(final SaprolingInfestationTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java b/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java index db16f3ff52..d79fd6fea9 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java @@ -10,11 +10,11 @@ import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.mana.AddConditionalManaOfAnyColorEffect; import mage.abilities.mana.conditional.ConditionalSpellManaBuilder; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterSpell; import mage.game.permanent.token.DragonToken2; @@ -46,6 +46,7 @@ public final class SarkhanFireblood extends CardImpl { // +1: Add two mana of any combination of colors. Spend this mana only to cast Dragon spells. this.addAbility(new LoyaltyAbility( new AddConditionalManaOfAnyColorEffect( + StaticValue.get(2), StaticValue.get(2), new ConditionalSpellManaBuilder(filter), false diff --git a/Mage.Sets/src/mage/cards/s/SarkhanUnbroken.java b/Mage.Sets/src/mage/cards/s/SarkhanUnbroken.java index 7cb65b50ac..8690b7fb60 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanUnbroken.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanUnbroken.java @@ -82,7 +82,7 @@ class SarkhanUnbrokenAbility1 extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); game.fireUpdatePlayersEvent(); diff --git a/Mage.Sets/src/mage/cards/s/SarkhanVol.java b/Mage.Sets/src/mage/cards/s/SarkhanVol.java index 92322a1616..f9bfb4fc45 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanVol.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanVol.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effects; @@ -15,15 +13,16 @@ import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.StaticFilters; import mage.game.permanent.token.DragonToken; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public final class SarkhanVol extends CardImpl { @@ -31,7 +30,7 @@ public final class SarkhanVol extends CardImpl { private static DragonToken dragonToken = new DragonToken(); public SarkhanVol(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{2}{R}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{R}{G}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SARKHAN); @@ -44,11 +43,9 @@ public final class SarkhanVol extends CardImpl { this.addAbility(new LoyaltyAbility(effects1, 1)); // -2: Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. - Effects effects2 = new Effects(); - effects2.add(new GainControlTargetEffect(Duration.EndOfTurn)); - effects2.add(new UntapTargetEffect()); - effects2.add(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); - LoyaltyAbility ability = new LoyaltyAbility(effects2, -2); + LoyaltyAbility ability = new LoyaltyAbility(new GainControlTargetEffect(Duration.EndOfTurn), -2); + ability.addEffect(new UntapTargetEffect().setText("Untap that creature")); + ability.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); @@ -56,7 +53,7 @@ public final class SarkhanVol extends CardImpl { this.addAbility(new LoyaltyAbility(new CreateTokenEffect(dragonToken, 5), -6)); } - public SarkhanVol(final SarkhanVol card) { + private SarkhanVol(final SarkhanVol card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java b/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java index 0cd6b7ace1..e802e0f501 100644 --- a/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java +++ b/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java @@ -1,5 +1,6 @@ package mage.cards.s; +import java.util.ArrayList; import mage.MageInt; import mage.Mana; import mage.abilities.Ability; @@ -11,8 +12,6 @@ import mage.abilities.effects.common.FlipSourceEffect; import mage.abilities.effects.common.ManaEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.choices.Choice; -import mage.choices.ChoiceColor; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledLandPermanent; @@ -26,9 +25,10 @@ import mage.game.permanent.Permanent; import mage.game.permanent.token.TokenImpl; import mage.players.Player; -import java.util.ArrayList; import java.util.List; import java.util.UUID; +import mage.choices.Choice; +import mage.choices.ChoiceColor; /** * @author LevelX2 @@ -133,9 +133,55 @@ class SasayasEssenceManaEffect extends ManaEffect { @Override public List getNetMana(Game game, Ability source) { - return new ArrayList<>(); + List netMana = new ArrayList<>(); + Player controller = game.getPlayer(source.getControllerId()); + Mana producedMana = (Mana) this.getValue("mana"); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller != null && producedMana != null && permanent != null) { + FilterPermanent filter = new FilterLandPermanent(); + filter.add(Predicates.not(new PermanentIdPredicate(permanent.getId()))); + filter.add(new NamePredicate(permanent.getName())); + int count = game.getBattlefield().countAll(filter, controller.getId(), game); + if (count > 0) { + if (producedMana.getBlack() > 0) { + netMana.add(Mana.BlackMana(count)); + } + if (producedMana.getRed() > 0) { + netMana.add(Mana.RedMana(count)); + } + if (producedMana.getBlue() > 0) { + netMana.add(Mana.BlueMana(count)); + } + if (producedMana.getGreen() > 0) { + netMana.add(Mana.GreenMana(count)); + } + if (producedMana.getWhite() > 0) { + netMana.add(Mana.WhiteMana(count)); + } + if (producedMana.getColorless() > 0) { + netMana.add(Mana.ColorlessMana(count)); + } + } + } + return netMana; + } + /** + * RULINGS 6/1/2005 If Sasaya’s Essence’s controller has four Forests and + * taps one of them for Green, the Essence will add GreenGreenGreen to that + * player’s mana pool for a total of GreenGreenGreenGreen. + * + * 6/1/2005 If Sasaya’s Essence’s controller has four Mossfire Valley and + * taps one of them for RedGreen, the Essence will add three mana (one for + * each other Mossfire Valley) of any combination of Red and/or Green to + * that player’s mana pool. + * + * 6/1/2005 If Sasaya’s Essence’s controller has two Brushlands and taps one + * of them for White, Sasaya’s Essence adds another White to that player’s + * mana pool. It won’t produce Green or Colorless unless the land was tapped + * for Green or Colorless instead. + */ @Override public Mana produceMana(Game game, Ability source) { Mana newMana = new Mana(); diff --git a/Mage.Sets/src/mage/cards/s/SaskiaTheUnyielding.java b/Mage.Sets/src/mage/cards/s/SaskiaTheUnyielding.java index 6105a5f1b1..892576b7a0 100644 --- a/Mage.Sets/src/mage/cards/s/SaskiaTheUnyielding.java +++ b/Mage.Sets/src/mage/cards/s/SaskiaTheUnyielding.java @@ -57,7 +57,7 @@ public final class SaskiaTheUnyielding extends CardImpl { class SaskiaTheUnyieldingEffect extends OneShotEffect { public SaskiaTheUnyieldingEffect() { - super(Outcome.Benefit); + super(Outcome.Detriment); this.staticText = "it deals that much damage to the chosen player"; } diff --git a/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java b/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java index 19e16e50ca..762b17575d 100644 --- a/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java +++ b/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java @@ -1,6 +1,6 @@ - package mage.cards.s; +import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -19,16 +19,11 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.TargetAdjuster; import mage.target.targetpointer.FixedTarget; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - /** * @author LevelX2 */ @@ -57,8 +52,6 @@ public final class SatyrFiredancer extends CardImpl { class SatyrFiredancerTriggeredAbility extends TriggeredAbilityImpl { - private List handledStackObjects = new ArrayList<>(); - public SatyrFiredancerTriggeredAbility() { super(Zone.BATTLEFIELD, new SatyrFiredancerDamageEffect(), false); targetAdjuster = SatyrFiredancerAdjuster.instance; @@ -73,11 +66,6 @@ class SatyrFiredancerTriggeredAbility extends TriggeredAbilityImpl { return new SatyrFiredancerTriggeredAbility(this); } - @Override - public void reset(Game game) { - handledStackObjects.clear(); - } - @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == EventType.DAMAGED_PLAYER; @@ -85,27 +73,26 @@ class SatyrFiredancerTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (isControlledBy(game.getControllerId(event.getSourceId()))) { - MageObject damageSource = game.getObject(event.getSourceId()); - if (damageSource != null) { - if (game.getOpponents(getControllerId()).contains(event.getTargetId())) { - MageObject object = game.getObject(event.getSourceId()); - if (object != null && (object.isInstant() || object.isSorcery())) { - if (!(damageSource instanceof StackObject) || !handledStackObjects.contains(damageSource.getId())) { - if (damageSource instanceof StackObject) { - handledStackObjects.add(damageSource.getId()); - } - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); // used by adjust targets - effect.setValue("damage", event.getAmount()); - } - return true; - } - } - } - } + if (!isControlledBy(game.getControllerId(event.getSourceId()))) { + return false; } - return false; + MageObject damageSource = game.getObject(event.getSourceId()); + if (damageSource == null) { + return false; + } + UUID damageTargetId = event.getTargetId(); + if (!game.getOpponents(getControllerId()).contains(damageTargetId)) { + return false; + } + MageObject sourceObject = game.getObject(event.getSourceId()); + if (sourceObject == null || !(sourceObject.isInstant() || sourceObject.isSorcery())) { + return false; + } + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(damageTargetId)); // used by adjust targets + effect.setValue("damage", event.getAmount()); + } + return true; } @Override @@ -157,4 +144,4 @@ enum SatyrFiredancerAdjuster implements TargetAdjuster { ability.getTargets().add(new TargetCreaturePermanent(filter)); } } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/SavageFirecat.java b/Mage.Sets/src/mage/cards/s/SavageFirecat.java index c564472541..0187df8c7e 100644 --- a/Mage.Sets/src/mage/cards/s/SavageFirecat.java +++ b/Mage.Sets/src/mage/cards/s/SavageFirecat.java @@ -74,6 +74,9 @@ class SavageFirecatTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { + if (game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA + return false; + } return game.getCard(event.getSourceId()).isLand() && event.getPlayerId().equals(this.controllerId); } diff --git a/Mage.Sets/src/mage/cards/s/SavagePunch.java b/Mage.Sets/src/mage/cards/s/SavagePunch.java index a8d8588613..d20283338f 100644 --- a/Mage.Sets/src/mage/cards/s/SavagePunch.java +++ b/Mage.Sets/src/mage/cards/s/SavagePunch.java @@ -11,8 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -24,12 +23,6 @@ import java.util.UUID; */ public final class SavagePunch extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public SavagePunch(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); @@ -46,11 +39,11 @@ public final class SavagePunch extends CardImpl { effect.setText("
Target creature you control fights target creature you don't control"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - Target target = new TargetCreaturePermanent(filter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL); this.getSpellAbility().addTarget(target); } - public SavagePunch(final SavagePunch card) { + private SavagePunch(final SavagePunch card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/s/SavageSmash.java b/Mage.Sets/src/mage/cards/s/SavageSmash.java index 6ee5657ac0..79533c4e61 100644 --- a/Mage.Sets/src/mage/cards/s/SavageSmash.java +++ b/Mage.Sets/src/mage/cards/s/SavageSmash.java @@ -6,8 +6,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -18,13 +17,6 @@ import java.util.UUID; */ public final class SavageSmash extends CardImpl { - private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public SavageSmash(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}{G}"); @@ -34,7 +26,7 @@ public final class SavageSmash extends CardImpl { new FightTargetsEffect().setText("It fights target creature you don't control") ); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } private SavageSmash(final SavageSmash card) { diff --git a/Mage.Sets/src/mage/cards/s/SavageStomp.java b/Mage.Sets/src/mage/cards/s/SavageStomp.java index 77c4d86017..d2a8f044d1 100644 --- a/Mage.Sets/src/mage/cards/s/SavageStomp.java +++ b/Mage.Sets/src/mage/cards/s/SavageStomp.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.abilities.common.SimpleStaticAbility; @@ -12,13 +11,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -29,23 +26,17 @@ import java.util.UUID; */ public final class SavageStomp extends CardImpl { - private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent("creature you don't control"); - private static final FilterPermanent filter2 + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.DINOSAUR, "a Dinosaur you control"); - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - - private static final Condition condition = new SourceTargetsPermanentCondition(filter2); + private static final Condition condition = new SourceTargetsPermanentCondition(filter); public SavageStomp(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); // Savage Stomp costs {2} less to cast if it targets a Dinosaur you control. this.addAbility(new SimpleStaticAbility( - Zone.STACK, new SpellCostReductionSourceEffect(2, condition) + Zone.ALL, new SpellCostReductionSourceEffect(2, condition).setCanWorksOnStackOnly(true) ).setRuleAtTheTop(true)); // Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control. @@ -55,11 +46,10 @@ public final class SavageStomp extends CardImpl { effect.setText("Then that creature fights target creature you don't control"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - Target target = new TargetCreaturePermanent(filter); - this.getSpellAbility().addTarget(target); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public SavageStomp(final SavageStomp card) { + private SavageStomp(final SavageStomp card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/s/SavageSummoning.java b/Mage.Sets/src/mage/cards/s/SavageSummoning.java index 8c4d07ebba..15228dd844 100644 --- a/Mage.Sets/src/mage/cards/s/SavageSummoning.java +++ b/Mage.Sets/src/mage/cards/s/SavageSummoning.java @@ -3,7 +3,7 @@ package mage.cards.s; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.ReplacementEffectImpl; @@ -34,7 +34,7 @@ public final class SavageSummoning extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); // Savage Summoning can't be countered. - Ability ability = new CantBeCounteredAbility(); + Ability ability = new CantBeCounteredSourceAbility(); ability.setRuleAtTheTop(true); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SavageSwipe.java b/Mage.Sets/src/mage/cards/s/SavageSwipe.java index c1aee533be..65caea0855 100644 --- a/Mage.Sets/src/mage/cards/s/SavageSwipe.java +++ b/Mage.Sets/src/mage/cards/s/SavageSwipe.java @@ -10,9 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -26,19 +24,13 @@ import java.util.UUID; */ public final class SavageSwipe extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public SavageSwipe(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); // Target creature you control gets +2/+2 until end of turn if its power is 2. Then it fights target creature you don't control. this.getSpellAbility().addEffect(new SavageSwipeEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } private SavageSwipe(final SavageSwipe card) { @@ -78,8 +70,8 @@ class SavageSwipeEffect extends OneShotEffect { ContinuousEffect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); - game.applyEffects(); + game.getState().processAction(game); } return new FightTargetsEffect().apply(game, source); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/SavaiCrystal.java b/Mage.Sets/src/mage/cards/s/SavaiCrystal.java new file mode 100644 index 0000000000..7f4205fb6e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SavaiCrystal.java @@ -0,0 +1,39 @@ +package mage.cards.s; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SavaiCrystal extends CardImpl { + + public SavaiCrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add {R}, {W}, or {B}. + this.addAbility(new RedManaAbility()); + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlackManaAbility()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private SavaiCrystal(final SavaiCrystal card) { + super(card); + } + + @Override + public SavaiCrystal copy() { + return new SavaiCrystal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SavaiSabertooth.java b/Mage.Sets/src/mage/cards/s/SavaiSabertooth.java new file mode 100644 index 0000000000..65cec7e29d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SavaiSabertooth.java @@ -0,0 +1,32 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SavaiSabertooth extends CardImpl { + + public SavaiSabertooth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + } + + private SavaiSabertooth(final SavaiSabertooth card) { + super(card); + } + + @Override + public SavaiSabertooth copy() { + return new SavaiSabertooth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SavaiThundermane.java b/Mage.Sets/src/mage/cards/s/SavaiThundermane.java new file mode 100644 index 0000000000..544f27681c --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SavaiThundermane.java @@ -0,0 +1,51 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SavaiThundermane extends CardImpl { + + public SavaiThundermane(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.CAT); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever you cycle a card, you may pay {2}. When you do, Savai Thundermane deals 2 damage to target creature and you gain 2 life. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(2), false, + "{this} deals 2 damage to target creature and you gain 2 life" + ); + ability.addEffect(new GainLifeEffect(2)); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(new CycleControllerTriggeredAbility( + new DoWhenCostPaid(ability, new GenericManaCost(2), "Pay {2}?") + )); + } + + private SavaiThundermane(final SavaiThundermane card) { + super(card); + } + + @Override + public SavaiThundermane copy() { + return new SavaiThundermane(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SavaiTriome.java b/Mage.Sets/src/mage/cards/s/SavaiTriome.java new file mode 100644 index 0000000000..bf636c4809 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SavaiTriome.java @@ -0,0 +1,48 @@ +package mage.cards.s; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SavaiTriome extends CardImpl { + + public SavaiTriome(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.MOUNTAIN); + this.subtype.add(SubType.PLAINS); + this.subtype.add(SubType.SWAMP); + + // ({T}: Add {R}, {W}, or {B}.) + this.addAbility(new RedManaAbility()); + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlackManaAbility()); + + // Savai Triome enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Cycling {3} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"))); + } + + private SavaiTriome(final SavaiTriome card) { + super(card); + } + + @Override + public SavaiTriome copy() { + return new SavaiTriome(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SawtoothLoon.java b/Mage.Sets/src/mage/cards/s/SawtoothLoon.java index 36288ecc17..d6cccfa11f 100644 --- a/Mage.Sets/src/mage/cards/s/SawtoothLoon.java +++ b/Mage.Sets/src/mage/cards/s/SawtoothLoon.java @@ -84,7 +84,7 @@ class SawtoothLoonEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(2, game); + controller.drawCards(2, source.getSourceId(), game); TargetCardInHand target = new TargetCardInHand(2, 2, new FilterCard()); controller.chooseTarget(Outcome.Detriment, target, source, game); Cards cardsToLibrary = new CardsImpl(target.getTargets()); diff --git a/Mage.Sets/src/mage/cards/s/SawtoothOgre.java b/Mage.Sets/src/mage/cards/s/SawtoothOgre.java new file mode 100644 index 0000000000..960fd59c7f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SawtoothOgre.java @@ -0,0 +1,43 @@ +package mage.cards.s; + +import java.util.UUID; + +import mage.MageInt; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author arcox + */ +public final class SawtoothOgre extends CardImpl { + + public SawtoothOgre(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + this.subtype.add(SubType.OGRE); + + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever Sawtooth Ogre blocks or becomes blocked by a creature, Sawtooth Ogre deals 1 damage to that creature at end of combat. + Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility( + new DamageTargetEffect(1)), true) + .setText("{this} deals 1 damage to that creature at end of combat"); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false)); + } + + public SawtoothOgre(final SawtoothOgre card) { + super(card); + } + + @Override + public SawtoothOgre copy() { + return new SawtoothOgre(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SawtuskDemolisher.java b/Mage.Sets/src/mage/cards/s/SawtuskDemolisher.java new file mode 100644 index 0000000000..8e90904631 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SawtuskDemolisher.java @@ -0,0 +1,98 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.keyword.MutateAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.BeastToken; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SawtuskDemolisher extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("noncreature permanent"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public SawtuskDemolisher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Mutate {3}{G} + this.addAbility(new MutateAbility(this, "{3}{G}")); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever this creature mutates, destroy target noncreature permanent. Its controller creates a 3/3 green Beast creature token. + Ability ability = new MutatesSourceTriggeredAbility(new SawtuskDemolisherEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private SawtuskDemolisher(final SawtuskDemolisher card) { + super(card); + } + + @Override + public SawtuskDemolisher copy() { + return new SawtuskDemolisher(this); + } +} + +class SawtuskDemolisherEffect extends OneShotEffect { + + SawtuskDemolisherEffect() { + super(Outcome.Benefit); + staticText = "destroy target noncreature permanent. Its controller creates a 3/3 green Beast creature token."; + } + + private SawtuskDemolisherEffect(final SawtuskDemolisherEffect effect) { + super(effect); + } + + @Override + public SawtuskDemolisherEffect copy() { + return new SawtuskDemolisherEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + Player player = game.getPlayer(permanent.getControllerId()); + permanent.destroy(source.getSourceId(), game, false); + if (player == null) { + return false; + } + Effect effect = new CreateTokenTargetEffect(new BeastToken()); + effect.setTargetPointer(new FixedTarget(player.getId(), game)); + return effect.apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScamperingScorcher.java b/Mage.Sets/src/mage/cards/s/ScamperingScorcher.java index fbbbc088bf..9561b437f8 100644 --- a/Mage.Sets/src/mage/cards/s/ScamperingScorcher.java +++ b/Mage.Sets/src/mage/cards/s/ScamperingScorcher.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.permanent.token.YoungPyromancerElementalToken; +import mage.game.permanent.token.RedElementalToken; import java.util.UUID; @@ -34,7 +34,7 @@ public final class ScamperingScorcher extends CardImpl { // When Scampering Scorcher enters the battlefield, create two 1/1 red Elemental creature tokens. Elementals you control gain haste until end of turn. Ability ability = new EntersBattlefieldTriggeredAbility( - new CreateTokenEffect(new YoungPyromancerElementalToken(), 2) + new CreateTokenEffect(new RedElementalToken(), 2) ); ability.addEffect(new GainAbilityControlledEffect( HasteAbility.getInstance(), Duration.EndOfTurn, filter diff --git a/Mage.Sets/src/mage/cards/s/ScarabFeast.java b/Mage.Sets/src/mage/cards/s/ScarabFeast.java index 8e8b1dfa6b..6a3e02693c 100644 --- a/Mage.Sets/src/mage/cards/s/ScarabFeast.java +++ b/Mage.Sets/src/mage/cards/s/ScarabFeast.java @@ -1,25 +1,26 @@ package mage.cards.s; -import java.util.UUID; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInASingleGraveyard; +import java.util.UUID; + /** * @author Stravant */ public final class ScarabFeast extends CardImpl { public ScarabFeast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // Exile up to three target cards from a single graveyard. getSpellAbility().addEffect(new ExileTargetEffect()); - getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("cards from a single graveyard"))); + getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS)); // Cycling {B} addAbility(new CyclingAbility(new ManaCostsImpl("{B}"))); diff --git a/Mage.Sets/src/mage/cards/s/ScarwoodBandits.java b/Mage.Sets/src/mage/cards/s/ScarwoodBandits.java index fa59c4dcc5..31934619f8 100644 --- a/Mage.Sets/src/mage/cards/s/ScarwoodBandits.java +++ b/Mage.Sets/src/mage/cards/s/ScarwoodBandits.java @@ -26,6 +26,7 @@ import mage.target.common.TargetArtifactPermanent; import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; /** * @author L_J @@ -47,7 +48,7 @@ public final class ScarwoodBandits extends CardImpl { new DoUnlessAnyOpponentPaysEffect( new ConditionalContinuousEffect( new GainControlTargetEffect(Duration.Custom, true), - SourceOnBattlefieldCondition.instance, + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), "gain control of target artifact for as long as {this} remains on the battlefield"), new GenericManaCost(2)), new ManaCostsImpl("{2}{G}")); diff --git a/Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java b/Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java new file mode 100644 index 0000000000..d518ad24f6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java @@ -0,0 +1,154 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ScholarOfTheLostTrove extends CardImpl { + + private static final FilterCard filter + = new FilterCard("instant, sorcery, or artifact card from your graveyard"); + + static { + filter.add(Predicates.or( + CardType.INSTANT.getPredicate(), + CardType.SORCERY.getPredicate(), + CardType.ARTIFACT.getPredicate() + )); + } + + public ScholarOfTheLostTrove(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); + + this.subtype.add(SubType.SPHINX); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Scholar of the Lost Trove enters the battlefield, you may cast target instant, sorcery, or artifact card from your graveyard without paying its mana cost. If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead. + Ability ability = new EntersBattlefieldTriggeredAbility(new ScholarOfTheLostTroveEffect(), true); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private ScholarOfTheLostTrove(final ScholarOfTheLostTrove card) { + super(card); + } + + @Override + public ScholarOfTheLostTrove copy() { + return new ScholarOfTheLostTrove(this); + } +} + +class ScholarOfTheLostTroveEffect extends OneShotEffect { + + ScholarOfTheLostTroveEffect() { + super(Outcome.PlayForFree); + this.staticText = "you may cast target instant, sorcery, or artifact card from your graveyard without paying its mana cost. " + + "If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead"; + } + + private ScholarOfTheLostTroveEffect(final ScholarOfTheLostTroveEffect effect) { + super(effect); + } + + @Override + public ScholarOfTheLostTroveEffect copy() { + return new ScholarOfTheLostTroveEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); + if (card == null) { + return true; + } + if (!controller.chooseUse(Outcome.PlayForFree, "Cast " + card.getLogName() + '?', source, game)) { + return true; + } + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), + game, true, new MageObjectReference(source.getSourceObject(game), game)); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + if (!cardWasCast || !card.isInstantOrSorcery()) { + return true; + } + ContinuousEffect effect = new ScholarOfTheLostTroveReplacementEffect(card.getId()); + effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId()))); + game.addEffect(effect, source); + return true; + } +} + +class ScholarOfTheLostTroveReplacementEffect extends ReplacementEffectImpl { + + private final UUID cardId; + + ScholarOfTheLostTroveReplacementEffect(UUID cardId) { + super(Duration.EndOfTurn, Outcome.Exile); + this.cardId = cardId; + staticText = "If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead"; + } + + private ScholarOfTheLostTroveReplacementEffect(final ScholarOfTheLostTroveReplacementEffect effect) { + super(effect); + this.cardId = effect.cardId; + } + + @Override + public ScholarOfTheLostTroveReplacementEffect copy() { + return new ScholarOfTheLostTroveReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(this.cardId); + if (controller == null || card == null) { + return false; + } + controller.moveCards(card, Zone.EXILED, source, game); + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.getToZone() == Zone.GRAVEYARD + && zEvent.getTargetId().equals(this.cardId); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScionOfVituGhazi.java b/Mage.Sets/src/mage/cards/s/ScionOfVituGhazi.java index 35d37c2cee..0d52f4ecf3 100644 --- a/Mage.Sets/src/mage/cards/s/ScionOfVituGhazi.java +++ b/Mage.Sets/src/mage/cards/s/ScionOfVituGhazi.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.PopulateEffect; @@ -32,7 +32,7 @@ public final class ScionOfVituGhazi extends CardImpl { //When Scion of Vitu-Ghazi enters the battlefield, if you cast it from your hand, create a 1/1 white Bird creature token with flying, then populate. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BirdToken()), false); ability.addEffect(new PopulateEffect("then")); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourceCondition.instance, + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, create a 1/1 white Bird creature token with flying, then populate."), new CastFromHandWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/ScorchingLava.java b/Mage.Sets/src/mage/cards/s/ScorchingLava.java index 6cf9e7255b..6a16e3c70f 100644 --- a/Mage.Sets/src/mage/cards/s/ScorchingLava.java +++ b/Mage.Sets/src/mage/cards/s/ScorchingLava.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -34,14 +33,17 @@ public final class ScorchingLava extends CardImpl { // Kicker {R} this.addAbility(new KickerAbility("{R}")); - // Scorching Lava deals 2 damage to any target. If Scorching Lava was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead. + // Scorching Lava deals 2 damage to any target. If Scorching Lava was kicked, + // that creature can't be regenerated this turn and if it would die this turn, exile it instead. this.getSpellAbility().addEffect(new DamageTargetEffect(2)); this.getSpellAbility().addEffect(new ConditionalContinuousRuleModifyingEffect( - new CantRegenerateTargetEffect(Duration.EndOfTurn, "that creature"), new LockedInCondition(KickedCondition.instance))); + new CantRegenerateTargetEffect(Duration.EndOfTurn, "If Scorching Lava was kicked, " + + "\n" + "that creature "), + new LockedInCondition(KickedCondition.instance))); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new ExileTargetIfDiesEffect(), - new LockedInCondition(KickedCondition.instance) - ).setText("and if it would die this turn, exile it instead")); + new LockedInCondition(KickedCondition.instance), + "and if it would die this turn, exile it instead.")); this.getSpellAbility().addTarget(new TargetAnyTarget()); } @@ -59,7 +61,6 @@ class ScorchingLavaEffect extends OneShotEffect { public ScorchingLavaEffect() { super(Outcome.Exile); - this.staticText = "and if it would die this turn, exile it instead"; } public ScorchingLavaEffect(final ScorchingLavaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/ScourAllPossibilities.java b/Mage.Sets/src/mage/cards/s/ScourAllPossibilities.java index e3d67b370c..6ce1b3d0f2 100644 --- a/Mage.Sets/src/mage/cards/s/ScourAllPossibilities.java +++ b/Mage.Sets/src/mage/cards/s/ScourAllPossibilities.java @@ -20,8 +20,8 @@ public final class ScourAllPossibilities extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}"); // Scry 2, then draw a card. - this.getSpellAbility().addEffect(new ScryEffect(2).setText("scry 2,")); - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("then")); + this.getSpellAbility().addEffect(new ScryEffect(2, false)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy(", then")); // Flashback {4}{U} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{4}{U}"), TimingRule.SORCERY)); diff --git a/Mage.Sets/src/mage/cards/s/ScourTheLaboratory.java b/Mage.Sets/src/mage/cards/s/ScourTheLaboratory.java index 4b52a814ce..ca30d92477 100644 --- a/Mage.Sets/src/mage/cards/s/ScourTheLaboratory.java +++ b/Mage.Sets/src/mage/cards/s/ScourTheLaboratory.java @@ -1,7 +1,6 @@ package mage.cards.s; import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.DeliriumCondition; @@ -23,7 +22,7 @@ public final class ScourTheLaboratory extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}{U}"); // Delirium — Scour the Laboratory costs {2} less to cast if there are four or more card types among cards in your graveyard. - Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(2, DeliriumCondition.instance)); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, DeliriumCondition.instance)); ability.setRuleAtTheTop(true); ability.setAbilityWord(AbilityWord.DELIRIUM); ability.addHint(DeliriumHint.instance); diff --git a/Mage.Sets/src/mage/cards/s/ScourgeOfValkas.java b/Mage.Sets/src/mage/cards/s/ScourgeOfValkas.java index c2332f8c6a..27c638bd37 100644 --- a/Mage.Sets/src/mage/cards/s/ScourgeOfValkas.java +++ b/Mage.Sets/src/mage/cards/s/ScourgeOfValkas.java @@ -2,7 +2,7 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; @@ -10,9 +10,12 @@ import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -25,13 +28,7 @@ import java.util.UUID; */ public final class ScourgeOfValkas extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("{this} or another Dragon"); - - static { - filter.add(SubType.DRAGON.getPredicate()); - } - - private static final String rule = "Whenever {this} or another Dragon enters the battlefield under your control, it deals X damage to any target, where X is the number of Dragons you control."; + private static final FilterPermanent filter = new FilterPermanent(SubType.DRAGON, "Dragon"); public ScourgeOfValkas(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}{R}"); @@ -45,16 +42,17 @@ public final class ScourgeOfValkas extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Scourge of Valkas or another Dragon enters the battlefield under your control, it deals X damage to any target, where X is the number of Dragons you control. - Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new ScourgeOfValkasDamageEffect(), filter, false, rule); + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new ScourgeOfValkasDamageEffect(), filter, false, true + ); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); // {R}: Scourge of Valkas gets +1/+0 until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}"))); - + this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}"))); } - public ScourgeOfValkas(final ScourgeOfValkas card) { + private ScourgeOfValkas(final ScourgeOfValkas card) { super(card); } @@ -66,12 +64,14 @@ public final class ScourgeOfValkas extends CardImpl { class ScourgeOfValkasDamageEffect extends OneShotEffect { - public ScourgeOfValkasDamageEffect() { + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.DRAGON, ""); + + ScourgeOfValkasDamageEffect() { super(Outcome.Damage); this.staticText = "it deals X damage to any target, where X is the number of Dragons you control"; } - public ScourgeOfValkasDamageEffect(final ScourgeOfValkasDamageEffect effect) { + private ScourgeOfValkasDamageEffect(final ScourgeOfValkasDamageEffect effect) { super(effect); } @@ -84,24 +84,22 @@ class ScourgeOfValkasDamageEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent enteringDragon = (Permanent) getValue("permanentEnteringBattlefield"); - if (controller != null && enteringDragon != null) { - FilterPermanent filter = new FilterPermanent(); - filter.add(SubType.DRAGON.getPredicate()); - int dragons = game.getBattlefield().countAll(filter, source.getControllerId(), game); - if (dragons > 0) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - permanent.damage(dragons, enteringDragon.getId(), game, false, true); - } else { - Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (player != null - && player.isInGame()) { - player.damage(dragons, enteringDragon.getId(), game); - } - } - } + if (controller == null || enteringDragon == null) { + return false; + } + int dragons = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + if (dragons < 1) { return true; } - return false; + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent != null) { + permanent.damage(dragons, enteringDragon.getId(), game, false, true); + } else { + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (player != null) { + player.damage(dragons, enteringDragon.getId(), game); + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/s/Scragnoth.java b/Mage.Sets/src/mage/cards/s/Scragnoth.java index c6497821d7..d1a5604e18 100644 --- a/Mage.Sets/src/mage/cards/s/Scragnoth.java +++ b/Mage.Sets/src/mage/cards/s/Scragnoth.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class Scragnoth extends CardImpl { this.toughness = new MageInt(4); // Scragnoth can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Protection from blue this.addAbility(ProtectionAbility.from(ObjectColor.BLUE)); } diff --git a/Mage.Sets/src/mage/cards/s/Scrambleverse.java b/Mage.Sets/src/mage/cards/s/Scrambleverse.java index e90639cf5c..3f43dd7fae 100644 --- a/Mage.Sets/src/mage/cards/s/Scrambleverse.java +++ b/Mage.Sets/src/mage/cards/s/Scrambleverse.java @@ -58,7 +58,7 @@ class ScrambleverseEffect extends OneShotEffect { int count = players.size(); for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterNonlandPermanent(), source.getControllerId(), source.getSourceId(), game)) { ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, true, players.get(RandomUtil.nextInt(count))); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); permanent.untap(game); } diff --git a/Mage.Sets/src/mage/cards/s/ScrapyardMongrel.java b/Mage.Sets/src/mage/cards/s/ScrapyardMongrel.java index dc4727c640..412f2bf00f 100644 --- a/Mage.Sets/src/mage/cards/s/ScrapyardMongrel.java +++ b/Mage.Sets/src/mage/cards/s/ScrapyardMongrel.java @@ -26,7 +26,7 @@ public final class ScrapyardMongrel extends CardImpl { public ScrapyardMongrel(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.color.setRed(true); this.power = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/s/ScreamingShield.java b/Mage.Sets/src/mage/cards/s/ScreamingShield.java index a31cb67e01..0e37473c59 100644 --- a/Mage.Sets/src/mage/cards/s/ScreamingShield.java +++ b/Mage.Sets/src/mage/cards/s/ScreamingShield.java @@ -37,7 +37,7 @@ public final class ScreamingShield extends CardImpl { Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(0, 3)); ability.addEffect(new GainAbilityAttachedEffect( toAdd, AttachmentType.EQUIPMENT - ).setText("and has \"{2}, {T}: Target player puts the top three cards of their library into their graveyard.\"")); + ).setText("and has \"{2}, {T}: Target player mills three cards.\"")); this.addAbility(ability); // Equip {3} diff --git a/Mage.Sets/src/mage/cards/s/ScreechingBuzzard.java b/Mage.Sets/src/mage/cards/s/ScreechingBuzzard.java index 0c4e229616..a3424b3e16 100644 --- a/Mage.Sets/src/mage/cards/s/ScreechingBuzzard.java +++ b/Mage.Sets/src/mage/cards/s/ScreechingBuzzard.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class ScreechingBuzzard extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Screeching Buzzard dies, each opponent discards a card. - this.addAbility(new DiesTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT), false)); } public ScreechingBuzzard(final ScreechingBuzzard card) { diff --git a/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java b/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java index da04611f60..7d4e8dfe3e 100644 --- a/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java +++ b/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java @@ -1,40 +1,44 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author ayratn */ public final class ScreechingSilcaw extends CardImpl { - private static final String rule = "Metalcraft — Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard."; - public ScreechingSilcaw(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.BIRD); - this.power = new MageInt(1); this.toughness = new MageInt(2); + // Flying this.addAbility(FlyingAbility.getInstance()); //"Metalcraft — Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard. TriggeredAbility conditional = new ConditionalInterveningIfTriggeredAbility( - new DealsCombatDamageToAPlayerTriggeredAbility(new PutLibraryIntoGraveTargetEffect(4), false, true), - MetalcraftCondition.instance, rule); + new DealsCombatDamageToAPlayerTriggeredAbility( + new PutLibraryIntoGraveTargetEffect(4), false, true + ), MetalcraftCondition.instance, "Metalcraft — Whenever {this} " + + "deals combat damage to a player, if you control three or more artifacts, that player mills four cards." + ); + conditional.setAbilityWord(AbilityWord.METALCRAFT); + conditional.addHint(MetalcraftHint.instance); this.addAbility(conditional); } diff --git a/Mage.Sets/src/mage/cards/s/ScreechingSliver.java b/Mage.Sets/src/mage/cards/s/ScreechingSliver.java index 908ef9eda5..c91946e029 100644 --- a/Mage.Sets/src/mage/cards/s/ScreechingSliver.java +++ b/Mage.Sets/src/mage/cards/s/ScreechingSliver.java @@ -37,7 +37,7 @@ public final class ScreechingSliver extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, - allSliversFilter, "All Slivers have \"{T}: Target player puts the top card of their library into their graveyard.\""))); + allSliversFilter, "All Slivers have \"{T}: Target player mills a card.\""))); } public ScreechingSliver(final ScreechingSliver card) { diff --git a/Mage.Sets/src/mage/cards/s/ScryingGlass.java b/Mage.Sets/src/mage/cards/s/ScryingGlass.java index bb535f3a52..f0fd0cc4e9 100644 --- a/Mage.Sets/src/mage/cards/s/ScryingGlass.java +++ b/Mage.Sets/src/mage/cards/s/ScryingGlass.java @@ -71,7 +71,7 @@ class ScryingGlassEffect extends OneShotEffect { targetOpponent.revealCards(source, targetOpponent.getHand(), game); if (targetOpponent.getHand().count(filter, game) == amount) { game.informPlayers(controller.getName() + " has chosen the exact number and color of the revealed cards from " + targetOpponent.getName() + "'s hand. They draw a card."); - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); return true; } else { game.informPlayers(controller.getName() + " has chosen incorrectly and will not draw a card."); diff --git a/Mage.Sets/src/mage/cards/s/ScuttlingDoomEngine.java b/Mage.Sets/src/mage/cards/s/ScuttlingDoomEngine.java index acb9f784d4..52469546ce 100644 --- a/Mage.Sets/src/mage/cards/s/ScuttlingDoomEngine.java +++ b/Mage.Sets/src/mage/cards/s/ScuttlingDoomEngine.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleEvasionAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; @@ -41,7 +41,7 @@ public final class ScuttlingDoomEngine extends CardImpl { // Scuttling Doom Engine can't be blocked by creatures with power 2 or less. this.addAbility(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield))); // When Scuttling Doom Engine dies, it deals 6 damage to target opponnent - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(6, "it"), false); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(6, "it"), false); ability.addTarget(new TargetOpponentOrPlaneswalker()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/ScuttlingSliver.java b/Mage.Sets/src/mage/cards/s/ScuttlingSliver.java index 8f01aa9999..9532d22d0f 100644 --- a/Mage.Sets/src/mage/cards/s/ScuttlingSliver.java +++ b/Mage.Sets/src/mage/cards/s/ScuttlingSliver.java @@ -32,8 +32,9 @@ public final class ScuttlingSliver extends CardImpl { this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( new SimpleActivatedAbility( new UntapSourceEffect().setText("untap this creature"), new GenericManaCost(2) - ), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS - ))); + ), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS) + .withForceQuotes() + )); } private ScuttlingSliver(final ScuttlingSliver card) { diff --git a/Mage.Sets/src/mage/cards/s/SeaDasherOctopus.java b/Mage.Sets/src/mage/cards/s/SeaDasherOctopus.java new file mode 100644 index 0000000000..a79023fef8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SeaDasherOctopus.java @@ -0,0 +1,47 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SeaDasherOctopus extends CardImpl { + + public SeaDasherOctopus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + + this.subtype.add(SubType.OCTOPUS); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Mutate {1}{U} + this.addAbility(new MutateAbility(this, "{1}{U}")); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Whenever this creature deals combat damage to a player, draw a card. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new DrawCardSourceControllerEffect(1), false + )); + } + + private SeaDasherOctopus(final SeaDasherOctopus card) { + super(card); + } + + @Override + public SeaDasherOctopus copy() { + return new SeaDasherOctopus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeafarersQuay.java b/Mage.Sets/src/mage/cards/s/SeafarersQuay.java index cefff3230b..3b488ef772 100644 --- a/Mage.Sets/src/mage/cards/s/SeafarersQuay.java +++ b/Mage.Sets/src/mage/cards/s/SeafarersQuay.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -15,8 +13,9 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author L_J */ public final class SeafarersQuay extends CardImpl { @@ -32,7 +31,10 @@ public final class SeafarersQuay extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Blue legendary creatures you control have "bands with other legendary creatures." - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(new BandsWithOtherAbility(SuperType.LEGENDARY), Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect( + new BandsWithOtherAbility(SuperType.LEGENDARY), Duration.WhileOnBattlefield, filter) + .withForceQuotes() + )); } public SeafarersQuay(final SeafarersQuay card) { diff --git a/Mage.Sets/src/mage/cards/s/Seance.java b/Mage.Sets/src/mage/cards/s/Seance.java index 251b1b8e80..3d3ad5e70f 100644 --- a/Mage.Sets/src/mage/cards/s/Seance.java +++ b/Mage.Sets/src/mage/cards/s/Seance.java @@ -71,7 +71,7 @@ class SeanceEffect extends OneShotEffect { if (controller != null && card != null) { controller.moveCards(card, Zone.EXILED, source, game); // Also if the move to exile is replaced, the copy takes place CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, false); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setAdditionalSubType(SubType.SPIRIT); effect.apply(game, source); ExileTargetEffect exileEffect = new ExileTargetEffect(); diff --git a/Mage.Sets/src/mage/cards/s/SearchTheCity.java b/Mage.Sets/src/mage/cards/s/SearchTheCity.java index 57ad86e633..0522121de1 100644 --- a/Mage.Sets/src/mage/cards/s/SearchTheCity.java +++ b/Mage.Sets/src/mage/cards/s/SearchTheCity.java @@ -157,7 +157,7 @@ class SearchTheCityExiledCardToHandEffect extends OneShotEffect { ExileZone searchTheCityExileZone = game.getExile().getExileZone(source.getSourceId()); if (cardName != null && searchTheCityExileZone != null) { for (Card card : searchTheCityExileZone.getCards(game)) { - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { if (card.moveToZone(Zone.HAND, source.getSourceId(), game, true)) { game.informPlayers("Search the City: put " + card.getName() + " into owner's hand"); } diff --git a/Mage.Sets/src/mage/cards/s/SeasonedHallowblade.java b/Mage.Sets/src/mage/cards/s/SeasonedHallowblade.java new file mode 100644 index 0000000000..5d20277ce9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SeasonedHallowblade.java @@ -0,0 +1,47 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.TapSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SeasonedHallowblade extends CardImpl { + + public SeasonedHallowblade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn. + Ability ability = new SimpleActivatedAbility(new TapSourceEffect(), new DiscardCardCost()); + ability.addEffect(new GainAbilitySourceEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains indestructible until end of turn")); + this.addAbility(ability); + } + + private SeasonedHallowblade(final SeasonedHallowblade card) { + super(card); + } + + @Override + public SeasonedHallowblade copy() { + return new SeasonedHallowblade(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeasonedPyromancer.java b/Mage.Sets/src/mage/cards/s/SeasonedPyromancer.java index c40f20bda2..f1e705b89d 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonedPyromancer.java +++ b/Mage.Sets/src/mage/cards/s/SeasonedPyromancer.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.token.YoungPyromancerElementalToken; +import mage.game.permanent.token.RedElementalToken; import mage.players.Player; import java.util.UUID; @@ -40,7 +40,7 @@ public final class SeasonedPyromancer extends CardImpl { // {3}{R}{R}, Exile Seasoned Pyromancer from your graveyard: Create two 1/1 red Elemental creature tokens. Ability ability = new SimpleActivatedAbility( Zone.GRAVEYARD, - new CreateTokenEffect(new YoungPyromancerElementalToken(), 2), + new CreateTokenEffect(new RedElementalToken(), 2), new ManaCostsImpl("{3}{R}{R}") ); ability.addCost(new ExileSourceFromGraveCost()); @@ -84,11 +84,11 @@ class SeasonedPyromancerEffect extends OneShotEffect { int nonlands = player .discard(2, false, source, game) .count(StaticFilters.FILTER_CARD_NON_LAND, game); - player.drawCards(2, game); + player.drawCards(2, source.getSourceId(), game); if (nonlands == 0) { return true; } - new YoungPyromancerElementalToken().putOntoBattlefield( + new RedElementalToken().putOntoBattlefield( nonlands, game, source.getSourceId(), source.getControllerId() ); return true; diff --git a/Mage.Sets/src/mage/cards/s/SecureTheScene.java b/Mage.Sets/src/mage/cards/s/SecureTheScene.java new file mode 100644 index 0000000000..5680511915 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SecureTheScene.java @@ -0,0 +1,68 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SoldierToken; +import mage.game.permanent.token.Token; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SecureTheScene extends CardImpl { + + public SecureTheScene(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}"); + + // Exile target nonland permanent. Its controller creates a 1/1 white Soldier creature token. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addEffect(new SecureTheSceneEffect()); + this.getSpellAbility().addTarget(new TargetNonlandPermanent()); + } + + private SecureTheScene(final SecureTheScene card) { + super(card); + } + + @Override + public SecureTheScene copy() { + return new SecureTheScene(this); + } +} + +class SecureTheSceneEffect extends OneShotEffect { + + private static final Token token = new SoldierToken(); + + SecureTheSceneEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Its controller creates a 1/1 white Soldier creature token"; + } + + private SecureTheSceneEffect(final SecureTheSceneEffect effect) { + super(effect); + } + + @Override + public SecureTheSceneEffect copy() { + return new SecureTheSceneEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (permanent == null) { + return false; + } + return token.putOntoBattlefield(1, game, source.getSourceId(), permanent.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeeBeyond.java b/Mage.Sets/src/mage/cards/s/SeeBeyond.java index 74728b840c..aedd6c6073 100644 --- a/Mage.Sets/src/mage/cards/s/SeeBeyond.java +++ b/Mage.Sets/src/mage/cards/s/SeeBeyond.java @@ -53,7 +53,7 @@ class SeeBeyondEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if(controller != null) { - controller.drawCards(2, game); + controller.drawCards(2, source.getSourceId(), game); if (!controller.getHand().isEmpty()) { TargetCard target = new TargetCard(Zone.HAND, new FilterCard("card to shuffle into your library")); controller.choose(Outcome.Detriment, controller.getHand(), target, game); diff --git a/Mage.Sets/src/mage/cards/s/SeeTheTruth.java b/Mage.Sets/src/mage/cards/s/SeeTheTruth.java new file mode 100644 index 0000000000..d348423e46 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SeeTheTruth.java @@ -0,0 +1,82 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; +import mage.filter.FilterCard; +import mage.game.stack.Spell; + +/** + * @author TheElk801 + */ +public final class SeeTheTruth extends CardImpl { + + public SeeTheTruth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}"); + + // Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead. + this.getSpellAbility().addEffect(new SeeTheTruthEffect()); + } + + private SeeTheTruth(final SeeTheTruth card) { + super(card); + } + + @Override + public SeeTheTruth copy() { + return new SeeTheTruth(this); + } +} + +class SeeTheTruthEffect extends OneShotEffect { + + SeeTheTruthEffect() { + super(Outcome.Benefit); + staticText = "Look at the top three cards of your library. " + + "Put one of those cards into your hand and the rest on the bottom of your library in any order. " + + "If this spell was cast from anywhere other than your hand, " + + "put each of those cards into your hand instead."; + } + + private SeeTheTruthEffect(final SeeTheTruthEffect effect) { + super(effect); + } + + @Override + public SeeTheTruthEffect copy() { + return new SeeTheTruthEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Spell sourceSpell = game.getStack().getSpell(source.getId()); // Use id to get the correct spell in case of copied spells + if (player == null || sourceSpell == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 3)); + if (!cards.isEmpty()) { + if (sourceSpell.isCopy() || Zone.HAND.equals(sourceSpell.getFromZone())) { // A copied spell was NOT cast at all + TargetCardInLibrary target = new TargetCardInLibrary(new FilterCard("card to put into your hand")); + player.chooseTarget(outcome, cards, target, source, game); + cards.removeIf(target.getFirstTarget()::equals); + player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game); + player.putCardsOnBottomOfLibrary(cards, game, source, true); + } else { + player.moveCards(cards, Zone.HAND, source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeedGuardian.java b/Mage.Sets/src/mage/cards/s/SeedGuardian.java index ade4624f24..c268d4e176 100644 --- a/Mage.Sets/src/mage/cards/s/SeedGuardian.java +++ b/Mage.Sets/src/mage/cards/s/SeedGuardian.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.ReachAbility; @@ -32,7 +32,7 @@ public final class SeedGuardian extends CardImpl { // Reach this.addAbility(ReachAbility.getInstance()); // When Seed Guardian dies, create an X/X green Elemental creature token, where X is the number of creature cards in your graveyard. - this.addAbility(new DiesTriggeredAbility(new SeedGuardianEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new SeedGuardianEffect(), false)); } public SeedGuardian(final SeedGuardian card) { diff --git a/Mage.Sets/src/mage/cards/s/SeedguideAsh.java b/Mage.Sets/src/mage/cards/s/SeedguideAsh.java index 660b6695dc..cdb0582109 100644 --- a/Mage.Sets/src/mage/cards/s/SeedguideAsh.java +++ b/Mage.Sets/src/mage/cards/s/SeedguideAsh.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -33,7 +33,7 @@ public final class SeedguideAsh extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); // When Seedguide Ash dies, you may search your library for up to three Forest cards and put them onto the battlefield tapped. If you do, shuffle your library. - this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 3, filter), true, false, Outcome.PutLandInPlay), true)); + this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 3, filter), true, false, Outcome.PutLandInPlay), true)); } public SeedguideAsh(final SeedguideAsh card) { diff --git a/Mage.Sets/src/mage/cards/s/SeizeTheSoul.java b/Mage.Sets/src/mage/cards/s/SeizeTheSoul.java index cc8ceac639..bffe087601 100644 --- a/Mage.Sets/src/mage/cards/s/SeizeTheSoul.java +++ b/Mage.Sets/src/mage/cards/s/SeizeTheSoul.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.common.CreateTokenEffect; @@ -13,11 +11,12 @@ import mage.constants.CardType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.permanent.token.SpiritToken; +import mage.game.permanent.token.SpiritWhiteToken; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SeizeTheSoul extends CardImpl { @@ -34,18 +33,18 @@ public final class SeizeTheSoul extends CardImpl { // Destroy target nonwhite, nonblack creature. Put a 1/1 white Spirit creature token with flying onto the battlefield. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritToken())); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken())); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); // Haunt // When the creature Seize the Soul haunts dies, destroy target nonwhite, nonblack creature. Put a 1/1 white Spirit creature token with flying onto the battlefield. Ability ability = new HauntAbility(this, new DestroyTargetEffect()); - ability.addEffect(new CreateTokenEffect(new SpiritToken())); - ability.addTarget(new TargetCreaturePermanent()); + ability.addEffect(new CreateTokenEffect(new SpiritWhiteToken())); + ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); } - public SeizeTheSoul(final SeizeTheSoul card) { + private SeizeTheSoul(final SeizeTheSoul card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/s/SelectiveAdaptation.java b/Mage.Sets/src/mage/cards/s/SelectiveAdaptation.java new file mode 100644 index 0000000000..1ccd946e15 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SelectiveAdaptation.java @@ -0,0 +1,132 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.*; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SelectiveAdaptation extends CardImpl { + + public SelectiveAdaptation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}"); + + // Reveal the top seven cards of your library. Choose from among them a card with flying, a card with first strike, and so on for double strike, deathtouch, haste, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance. Put one of the chosen cards onto the battlefield, the other chosen cards into your hand, and the rest into your graveyard. + this.getSpellAbility().addEffect(new SelectiveAdaptationEffect()); + } + + private SelectiveAdaptation(final SelectiveAdaptation card) { + super(card); + } + + @Override + public SelectiveAdaptation copy() { + return new SelectiveAdaptation(this); + } +} + +class SelectiveAdaptationEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterPermanentCard("a card to put onto the battlefield"); + + private enum AbilitySelector { + FLYING(FlyingAbility.class, "flying"), + FIRST_STRIKE(FirstStrikeAbility.class, "first strike"), + DOUBLE_STRIKE(DoubleStrikeAbility.class, "double strike"), + DEATHTOUCH(DeathtouchAbility.class, "deathtouch"), + HASTE(HasteAbility.class, "haste"), + HEXPROOF(HexproofBaseAbility.class, "hexproof"), + INDESTRUCTIBLE(IndestructibleAbility.class, "indestructible"), + LIFELINK(LifelinkAbility.class, "lifelink"), + MENACE(MenaceAbility.class, "menace"), + REACH(ReachAbility.class, "reach"), + TRAMPLE(TrampleAbility.class, "trample"), + VIGILANCE(VigilanceAbility.class, "vigilance"); + + private final Class abilityClass; + private final String abilityName; + private final FilterCard filter; + + private AbilitySelector(Class abilityClass, String abilityName) { + this.abilityClass = abilityClass; + this.abilityName = abilityName; + this.filter = new FilterCard("card with " + abilityName); + filter.add(new AbilityPredicate(abilityClass)); + } + + private TargetCardInLibrary makeTarget() { + return new TargetCardInLibrary(0, 1, filter); + } + } + + SelectiveAdaptationEffect() { + super(Outcome.Benefit); + staticText = "Reveal the top seven cards of your library. Choose from among them a card with flying, " + + "a card with first strike, and so on for double strike, deathtouch, haste, hexproof, indestructible, " + + "lifelink, menace, reach, trample, and vigilance. Put one of the chosen cards onto the battlefield, " + + "the other chosen cards into your hand, and the rest into your graveyard."; + } + + private SelectiveAdaptationEffect(final SelectiveAdaptationEffect effect) { + super(effect); + } + + @Override + public SelectiveAdaptationEffect copy() { + return new SelectiveAdaptationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards top7 = new CardsImpl(player.getLibrary().getTopCards(game, 7)); + Cards toGrave = top7.copy(); + player.revealCards(source, toGrave, game); + Cards toHand = new CardsImpl(); + if (toGrave.isEmpty()) { + return false; + } + for (AbilitySelector abilitySelector : AbilitySelector.values()) { + if (toGrave.count(abilitySelector.filter, game) < 1) { + continue; + } + TargetCard target = abilitySelector.makeTarget(); + player.choose(Outcome.DrawCard, top7, target, game); + toHand.add(target.getFirstTarget()); + toGrave.remove(target.getFirstTarget()); + } + if (toGrave.isEmpty()) { + return false; + } + if (toHand.count(filter, game) > 0) { + TargetCard target = new TargetCardInLibrary(filter); + player.choose(Outcome.PutCreatureInPlay, toHand, target, game); + Card toBattlefield = game.getCard(target.getFirstTarget()); + if (toBattlefield != null + && player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game) + && game.getPermanent(toBattlefield.getId()) != null) { + toHand.remove(toBattlefield); + } + } + player.moveCards(toHand, Zone.HAND, source, game); + player.moveCards(toGrave, Zone.GRAVEYARD, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SelflessSavior.java b/Mage.Sets/src/mage/cards/s/SelflessSavior.java new file mode 100644 index 0000000000..fe9c603a64 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SelflessSavior.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SelflessSavior extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another target creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public SelflessSavior(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.DOG); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Sacrifice Selfless Savior: Another target creature gains indestructible until end of turn. + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ), new SacrificeSourceCost()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private SelflessSavior(final SelflessSavior card) { + super(card); + } + + @Override + public SelflessSavior copy() { + return new SelflessSavior(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SellSwordBrute.java b/Mage.Sets/src/mage/cards/s/SellSwordBrute.java index 3048cc10ae..bdf0bae886 100644 --- a/Mage.Sets/src/mage/cards/s/SellSwordBrute.java +++ b/Mage.Sets/src/mage/cards/s/SellSwordBrute.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class SellSwordBrute extends CardImpl { this.toughness = new MageInt(2); // When Sell-Sword Brute dies, it deals 2 damage to you. - this.addAbility(new DiesTriggeredAbility(new DamageControllerEffect(2, "it"), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DamageControllerEffect(2, "it"), false)); } public SellSwordBrute(final SellSwordBrute card) { diff --git a/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java b/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java index 3516ba3642..5bd3a0df20 100644 --- a/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java +++ b/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -7,9 +6,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ManaEffect; import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; @@ -18,7 +15,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicate; import mage.filter.predicate.permanent.AnotherPredicate; @@ -54,7 +50,7 @@ public final class SelvalaHeartOfTheWilds extends CardImpl { // {G}, {T}: Add X mana in any combination of colors, where X is the greatest power among creatures you control. ManaEffect manaEffect = new AddManaInAnyCombinationEffect( - GreatestPowerAmongControlledCreaturesValue.instance, rule2, + GreatestPowerAmongControlledCreaturesValue.instance, GreatestPowerAmongControlledCreaturesValue.instance, rule2, ColoredManaSymbol.B, ColoredManaSymbol.U, ColoredManaSymbol.R, ColoredManaSymbol.W, ColoredManaSymbol.G); Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, manaEffect, new ManaCostsImpl("{G}")); ability.addCost(new TapSourceCost()); @@ -96,13 +92,13 @@ class SelvalaHeartOfTheWildsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(targetPointer.getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { if (filter2.match(permanent, game)) { Player permanentController = game.getPlayer(permanent.getControllerId()); if (permanentController != null && permanentController.chooseUse(Outcome.DrawCard, "Would you like to draw a card?", source, game)) { - permanentController.drawCards(1, game); + permanentController.drawCards(1, source.getSourceId(), game); } } return true; @@ -133,4 +129,4 @@ class GreatestPowerPredicate implements Predicate { public String toString() { return "Greatest Power"; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/SemblanceAnvil.java b/Mage.Sets/src/mage/cards/s/SemblanceAnvil.java index dba8d34db8..7cfdf8817b 100644 --- a/Mage.Sets/src/mage/cards/s/SemblanceAnvil.java +++ b/Mage.Sets/src/mage/cards/s/SemblanceAnvil.java @@ -1,5 +1,7 @@ package mage.cards.s; +import java.util.List; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -18,9 +20,6 @@ import mage.players.Player; import mage.target.TargetCard; import mage.util.CardUtil; -import java.util.List; -import java.util.UUID; - /** * @author nantuko */ @@ -107,15 +106,16 @@ class SemblanceAnvilCostReductionEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - Card sourceCard = game.getCard(abilityToModify.getSourceId()); - if (sourceCard != null && sourceCard.isOwnedBy(source.getControllerId())) { + if (abilityToModify instanceof SpellAbility + && abilityToModify.isControlledBy(source.getControllerId())) { + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + if (spellCard != null) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { List imprinted = permanent.getImprinted(); if (imprinted != null && !imprinted.isEmpty()) { Card imprintedCard = game.getCard(imprinted.get(0)); - return imprintedCard != null && imprintedCard.shareTypes(sourceCard); + return imprintedCard != null && imprintedCard.shareTypes(spellCard); } } } diff --git a/Mage.Sets/src/mage/cards/s/SenatorLottDod.java b/Mage.Sets/src/mage/cards/s/SenatorLottDod.java index 2c6f13cddc..1301edcc49 100644 --- a/Mage.Sets/src/mage/cards/s/SenatorLottDod.java +++ b/Mage.Sets/src/mage/cards/s/SenatorLottDod.java @@ -1,10 +1,7 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.CostModificationEffectImpl; @@ -13,12 +10,14 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.stack.Spell; import mage.players.Player; -import mage.target.Target; import mage.util.CardUtil; +import java.util.Set; +import java.util.UUID; + /** - * * @author Styxo */ public final class SenatorLottDod extends CardImpl { @@ -32,11 +31,10 @@ public final class SenatorLottDod extends CardImpl { this.toughness = new MageInt(2); // Spells your opponents cast that target you cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SenatorLottDodSpellsTargetingYouCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SenatorLottDodSpellsTargetingYouCostModificationEffect())); // Spell your opponents cast that target a creature you control cost {1} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SenatorLottDodSpellsTargetingCreatureCostReductionEffect())); - + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SenatorLottDodSpellsTargetingCreatureCostModificationEffect())); } public SenatorLottDod(final SenatorLottDod card) { @@ -49,14 +47,14 @@ public final class SenatorLottDod extends CardImpl { } } -class SenatorLottDodSpellsTargetingCreatureCostReductionEffect extends CostModificationEffectImpl { +class SenatorLottDodSpellsTargetingCreatureCostModificationEffect extends CostModificationEffectImpl { - public SenatorLottDodSpellsTargetingCreatureCostReductionEffect() { + public SenatorLottDodSpellsTargetingCreatureCostModificationEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - this.staticText = "Spell your opponents cast that target a creature you control cost {1} more to cast."; + this.staticText = "Spell your opponents cast that target a creature you control cost {1} more to cast"; } - protected SenatorLottDodSpellsTargetingCreatureCostReductionEffect(SenatorLottDodSpellsTargetingCreatureCostReductionEffect effect) { + protected SenatorLottDodSpellsTargetingCreatureCostModificationEffect(SenatorLottDodSpellsTargetingCreatureCostModificationEffect effect) { super(effect); } @@ -68,38 +66,57 @@ class SenatorLottDodSpellsTargetingCreatureCostReductionEffect extends CostModif @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - Permanent permanent = game.getPermanent(targetUUID); - if (permanent != null && permanent.isControlledBy(source.getControllerId())) { - return true; - } - } - } - } - } + if (!(abilityToModify instanceof SpellAbility)) { + return false; } + + if (!game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { + return false; + } + + Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); + Set allTargets; + if (spell != null) { + // real cast + allTargets = CardUtil.getAllSelectedTargets(abilityToModify, game); + } else { + // playable + allTargets = CardUtil.getAllPossibleTargets(abilityToModify, game); + + // can target without cost increase + if (allTargets.stream().anyMatch(target -> !isTargetCompatible(target, source, game))) { + return false; + } + ; + } + + return allTargets.stream().anyMatch(target -> isTargetCompatible(target, source, game)); + } + + private boolean isTargetCompatible(UUID target, Ability source, Game game) { + // target a creature you control + Permanent targetPermanent = game.getPermanent(target); + if (targetPermanent != null && targetPermanent.isCreature() && targetPermanent.isControlledBy(source.getControllerId())) { + return true; + } + return false; } @Override - public SenatorLottDodSpellsTargetingCreatureCostReductionEffect copy() { - return new SenatorLottDodSpellsTargetingCreatureCostReductionEffect(this); + public SenatorLottDodSpellsTargetingCreatureCostModificationEffect copy() { + return new SenatorLottDodSpellsTargetingCreatureCostModificationEffect(this); } } -class SenatorLottDodSpellsTargetingYouCostReductionEffect extends CostModificationEffectImpl { +class SenatorLottDodSpellsTargetingYouCostModificationEffect extends CostModificationEffectImpl { - public SenatorLottDodSpellsTargetingYouCostReductionEffect() { + public SenatorLottDodSpellsTargetingYouCostModificationEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - this.staticText = "Spells your opponents cast that target you cost {2} more to cast."; + this.staticText = "Spells your opponents cast that target you cost {2} more to cast"; } - protected SenatorLottDodSpellsTargetingYouCostReductionEffect(SenatorLottDodSpellsTargetingYouCostReductionEffect effect) { + protected SenatorLottDodSpellsTargetingYouCostModificationEffect(SenatorLottDodSpellsTargetingYouCostModificationEffect effect) { super(effect); } @@ -111,26 +128,45 @@ class SenatorLottDodSpellsTargetingYouCostReductionEffect extends CostModificati @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - Player player = game.getPlayer(targetUUID); - if (player != null && player.getId().equals(source.getControllerId())) { - return true; - } - } - } - } - } + if (!(abilityToModify instanceof SpellAbility)) { + return false; } + + if (!game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { + return false; + } + + Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); + Set allTargets; + if (spell != null) { + // real cast + allTargets = CardUtil.getAllSelectedTargets(abilityToModify, game); + } else { + // playable + allTargets = CardUtil.getAllPossibleTargets(abilityToModify, game); + + // can target without cost increase + if (allTargets.stream().anyMatch(target -> !isTargetCompatible(target, source, game))) { + return false; + } + ; + } + + return allTargets.stream().anyMatch(target -> isTargetCompatible(target, source, game)); + } + + private boolean isTargetCompatible(UUID target, Ability source, Game game) { + // target you + Player targetPlayer = game.getPlayer(target); + if (targetPlayer != null && targetPlayer.getId().equals(source.getControllerId())) { + return true; + } + return false; } @Override - public SenatorLottDodSpellsTargetingYouCostReductionEffect copy() { - return new SenatorLottDodSpellsTargetingYouCostReductionEffect(this); + public SenatorLottDodSpellsTargetingYouCostModificationEffect copy() { + return new SenatorLottDodSpellsTargetingYouCostModificationEffect(this); } } diff --git a/Mage.Sets/src/mage/cards/s/SengirAutocrat.java b/Mage.Sets/src/mage/cards/s/SengirAutocrat.java index 7420097d0a..89b3deabc1 100644 --- a/Mage.Sets/src/mage/cards/s/SengirAutocrat.java +++ b/Mage.Sets/src/mage/cards/s/SengirAutocrat.java @@ -1,9 +1,6 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -12,17 +9,18 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.FilterPermanent; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.SerfToken; +import java.util.UUID; + /** - * * @author Quercitron */ public final class SengirAutocrat extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Serf tokens"); + private static final FilterPermanent filter = new FilterPermanent("Serf tokens"); static { filter.add(SubType.SERF.getPredicate()); @@ -37,11 +35,10 @@ public final class SengirAutocrat extends CardImpl { this.toughness = new MageInt(2); // When Sengir Autocrat enters the battlefield, create three 0/1 black Serf creature tokens. - Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SerfToken(), 3)); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SerfToken(), 3))); + // When Sengir Autocrat leaves the battlefield, exile all Serf tokens. - ability = new LeavesBattlefieldTriggeredAbility(new ExileAllEffect(filter), false); - this.addAbility(ability); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new ExileAllEffect(filter), false)); } public SengirAutocrat(final SengirAutocrat card) { diff --git a/Mage.Sets/src/mage/cards/s/SenseisDiviningTop.java b/Mage.Sets/src/mage/cards/s/SenseisDiviningTop.java index 33db73fc7b..a7a405735b 100644 --- a/Mage.Sets/src/mage/cards/s/SenseisDiviningTop.java +++ b/Mage.Sets/src/mage/cards/s/SenseisDiviningTop.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -16,6 +15,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; /** * @author LevelX @@ -24,7 +24,10 @@ public final class SenseisDiviningTop extends CardImpl { public SenseisDiviningTop(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + // {1}: Look at the top three cards of your library, then put them back in any order. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new LookLibraryControllerEffect(3, false, true), new ManaCostsImpl("{1}"))); + // {T}: Draw a card, then put Sensei's Divining Top on top of its owner's library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new TapSourceCost()); ability.addEffect(new SenseisDiviningTopEffect()); this.addAbility(ability); @@ -45,7 +48,7 @@ class SenseisDiviningTopEffect extends OneShotEffect { public SenseisDiviningTopEffect() { super(Outcome.ReturnToHand); - staticText = ", then put Sensei's Divining Top on top of its owner's library"; + staticText = ", then put {this} on top of its owner's library"; } public SenseisDiviningTopEffect(final SenseisDiviningTopEffect effect) { @@ -60,8 +63,9 @@ class SenseisDiviningTopEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - return permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + Player owner = game.getPlayer(game.getOwnerId(source.getSourceId())); + if (permanent != null && owner != null) { + return owner.putCardsOnTopOfLibrary(permanent, game, source, true); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/Sentinel.java b/Mage.Sets/src/mage/cards/s/Sentinel.java index bdbfd2e58c..323593fc76 100644 --- a/Mage.Sets/src/mage/cards/s/Sentinel.java +++ b/Mage.Sets/src/mage/cards/s/Sentinel.java @@ -78,7 +78,7 @@ class SentinelEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent targetPermanent = game.getPermanentOrLKIBattlefield(targetPointer.getFirst(game, source)); + Permanent targetPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && targetPermanent != null) { int newToughness = CardUtil.addWithOverflowCheck(targetPermanent.getPower().getValue(), 1); game.addEffect(new SetToughnessSourceEffect(StaticValue.get(newToughness), Duration.Custom, SubLayer.SetPT_7b), source); diff --git a/Mage.Sets/src/mage/cards/s/SentinelOfThePearlTrident.java b/Mage.Sets/src/mage/cards/s/SentinelOfThePearlTrident.java index 97a6036248..0cd7f0b6b1 100644 --- a/Mage.Sets/src/mage/cards/s/SentinelOfThePearlTrident.java +++ b/Mage.Sets/src/mage/cards/s/SentinelOfThePearlTrident.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -24,8 +22,9 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SentinelOfThePearlTrident extends CardImpl { @@ -87,7 +86,7 @@ class SentinelOfThePearlTridentEffect extends OneShotEffect { int zcc = permanent.getZoneChangeCounter(game); controller.moveCards(permanent, Zone.EXILED, source, game); //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setTargetPointer(new FixedTarget(permanent.getId(), zcc + 1)); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/s/SentinelTower.java b/Mage.Sets/src/mage/cards/s/SentinelTower.java index be47a83810..398c5b4204 100644 --- a/Mage.Sets/src/mage/cards/s/SentinelTower.java +++ b/Mage.Sets/src/mage/cards/s/SentinelTower.java @@ -2,10 +2,13 @@ package mage.cards.s; import mage.MageObject; import mage.MageObjectReference; +import mage.abilities.Ability; import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -44,13 +47,18 @@ public final class SentinelTower extends CardImpl { class SentinelTowerTriggeredAbility extends SpellCastAllTriggeredAbility { + private String damageInfo; + SentinelTowerTriggeredAbility() { super(new DamageTargetEffect(0), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false); this.addTarget(new TargetAnyTarget()); + this.addHint(new ValueHint("There were cast instant and sorcery this turn", SentinelTowerSpellsCastValue.instance)); + this.damageInfo = null; } SentinelTowerTriggeredAbility(final SentinelTowerTriggeredAbility effect) { super(effect); + this.damageInfo = effect.damageInfo; } @Override @@ -78,6 +86,7 @@ class SentinelTowerTriggeredAbility extends SpellCastAllTriggeredAbility { break; } } + damageInfo = " (" + damageToDeal + " damage)"; for (Effect effect : this.getEffects()) { if (effect instanceof DamageTargetEffect) { ((DamageTargetEffect) effect).setAmount(StaticValue.get(damageToDeal)); @@ -92,7 +101,8 @@ class SentinelTowerTriggeredAbility extends SpellCastAllTriggeredAbility { public String getRule() { return "Whenever an instant or sorcery spell is cast during your turn, " + "{this} deals damage to any target equal to 1 " - + "plus the number of instant and sorcery spells cast before that spell this turn."; + + "plus the number of instant and sorcery spells cast before that spell this turn." + + (damageInfo != null ? damageInfo : ""); } } @@ -124,3 +134,36 @@ class SentinelTowerWatcher extends Watcher { return spellsThisTurn; } } + +enum SentinelTowerSpellsCastValue implements DynamicValue { + + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + SentinelTowerWatcher watcher = game.getState().getWatcher(SentinelTowerWatcher.class); + if (watcher == null) { + return 0; + } + List spellsCast = watcher.getSpellsThisTurn(); + if (spellsCast == null) { + return 0; + } + return spellsCast.size(); + } + + @Override + public SentinelTowerSpellsCastValue copy() { + return instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "There was an instant or sorcery spell in this turn"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/Seraph.java b/Mage.Sets/src/mage/cards/s/Seraph.java index b656237d5d..eadf1a19b6 100644 --- a/Mage.Sets/src/mage/cards/s/Seraph.java +++ b/Mage.Sets/src/mage/cards/s/Seraph.java @@ -1,140 +1,140 @@ -package mage.cards.s; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.DealtDamageAndDiedTriggeredAbility; -import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; -import mage.abilities.effects.common.SacrificeTargetEffect; -import mage.constants.SubType; -import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author jeffwadsworth - */ -public final class Seraph extends CardImpl { - - public Seraph(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{W}"); - - this.subtype.add(SubType.ANGEL); - this.power = new MageInt(4); - this.toughness = new MageInt(4); - - // Flying - this.addAbility(FlyingAbility.getInstance()); - - // Whenever a creature dealt damage by Seraph this turn dies, put that card onto the battlefield under your control at the beginning of the next end step. Sacrifice the creature when you lose control of Seraph. - Effect effect = new CreateDelayedTriggeredAbilityEffect( - new AtTheBeginOfNextEndStepDelayedTriggeredAbility( - new SeraphEffect())); - effect.setText("put that card onto the battlefield under your control at the beginning of the next end step. Sacrifice the creature when you lose control of {this}"); - this.addAbility(new DealtDamageAndDiedTriggeredAbility(effect)); - - } - - private Seraph(final Seraph card) { - super(card); - } - - @Override - public Seraph copy() { - return new Seraph(this); - } -} - -class SeraphEffect extends OneShotEffect { - - SeraphEffect() { - super(Outcome.Neutral); - staticText = "put that card onto the battlefield under your control. Sacrifice it when you lose control of {this}"; - } - - SeraphEffect(SeraphEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Card creatureCard = game.getCard(targetPointer.getFirst(game, source)); - if (controller != null - && creatureCard != null - && game.getState().getZone(creatureCard.getId()) == Zone.GRAVEYARD) { // must be still in the graveyard - controller.moveCards(creatureCard, Zone.BATTLEFIELD, source, game, false, false, false, null); - OneShotEffect effect = new SacrificeTargetEffect(); - effect.setText("Sacrifice this if Seraph leaves the battlefield or its current controller loses control of it."); - effect.setTargetPointer(new FixedTarget(creatureCard.getId())); - SeraphDelayedTriggeredAbility dTA = new SeraphDelayedTriggeredAbility(effect, source.getSourceId()); - game.addDelayedTriggeredAbility(dTA, source); - return true; - } - return false; - } - - @Override - public SeraphEffect copy() { - return new SeraphEffect(this); - } -} - -class SeraphDelayedTriggeredAbility extends DelayedTriggeredAbility { - - UUID seraph; - - SeraphDelayedTriggeredAbility(Effect effect, UUID seraph) { - super(effect, Duration.EndOfGame, true); - this.seraph = seraph; - } - - SeraphDelayedTriggeredAbility(SeraphDelayedTriggeredAbility ability) { - super(ability); - this.seraph = ability.seraph; - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.LOST_CONTROL - || event.getType() == EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == EventType.LOST_CONTROL - && event.getSourceId().equals(seraph)) { - return true; - } - if (event.getType() == EventType.ZONE_CHANGE - && event.getTargetId().equals(seraph)) { - return true; - } - return false; - } - - @Override - public SeraphDelayedTriggeredAbility copy() { - return new SeraphDelayedTriggeredAbility(this); - } - - @Override - public String getRule() { - return "Control of Seraph was lost, sacrifice this."; - } -} +package mage.cards.s; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.DealtDamageAndDiedTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jeffwadsworth + */ +public final class Seraph extends CardImpl { + + public Seraph(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{W}"); + + this.subtype.add(SubType.ANGEL); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever a creature dealt damage by Seraph this turn dies, put that card onto the battlefield under your control at the beginning of the next end step. Sacrifice the creature when you lose control of Seraph. + Effect effect = new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new SeraphEffect())); + effect.setText("put that card onto the battlefield under your control at the beginning of the next end step. Sacrifice the creature when you lose control of {this}"); + this.addAbility(new DealtDamageAndDiedTriggeredAbility(effect)); + + } + + private Seraph(final Seraph card) { + super(card); + } + + @Override + public Seraph copy() { + return new Seraph(this); + } +} + +class SeraphEffect extends OneShotEffect { + + SeraphEffect() { + super(Outcome.Neutral); + staticText = "put that card onto the battlefield under your control. Sacrifice it when you lose control of {this}"; + } + + SeraphEffect(SeraphEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card creatureCard = game.getCard(targetPointer.getFirst(game, source)); + if (controller != null + && creatureCard != null + && game.getState().getZone(creatureCard.getId()) == Zone.GRAVEYARD) { // must be still in the graveyard + controller.moveCards(creatureCard, Zone.BATTLEFIELD, source, game, false, false, false, null); + OneShotEffect effect = new SacrificeTargetEffect(); + effect.setText("Sacrifice this if Seraph leaves the battlefield or its current controller loses control of it."); + effect.setTargetPointer(new FixedTarget(creatureCard.getId())); + SeraphDelayedTriggeredAbility dTA = new SeraphDelayedTriggeredAbility(effect, source.getSourceId()); + game.addDelayedTriggeredAbility(dTA, source); + return true; + } + return false; + } + + @Override + public SeraphEffect copy() { + return new SeraphEffect(this); + } +} + +class SeraphDelayedTriggeredAbility extends DelayedTriggeredAbility { + + UUID seraph; + + SeraphDelayedTriggeredAbility(Effect effect, UUID seraph) { + super(effect, Duration.EndOfGame, true); + this.seraph = seraph; + } + + SeraphDelayedTriggeredAbility(SeraphDelayedTriggeredAbility ability) { + super(ability); + this.seraph = ability.seraph; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.LOST_CONTROL + || event.getType() == EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == EventType.LOST_CONTROL + && event.getSourceId().equals(seraph)) { + return true; + } + if (event.getType() == EventType.ZONE_CHANGE + && event.getTargetId().equals(seraph)) { + return true; + } + return false; + } + + @Override + public SeraphDelayedTriggeredAbility copy() { + return new SeraphDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Control of Seraph was lost, sacrifice this."; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SereneRemembrance.java b/Mage.Sets/src/mage/cards/s/SereneRemembrance.java index b805b2f3b5..6f4cd445f3 100644 --- a/Mage.Sets/src/mage/cards/s/SereneRemembrance.java +++ b/Mage.Sets/src/mage/cards/s/SereneRemembrance.java @@ -7,13 +7,14 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInASingleGraveyard; import java.util.UUID; +import mage.cards.Cards; +import mage.cards.CardsImpl; /** * @author LevelX2 @@ -23,10 +24,9 @@ public final class SereneRemembrance extends CardImpl { public SereneRemembrance(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); - // Shuffle Serene Remembrance and up to three target cards from a single graveyard into their owners' libraries. this.getSpellAbility().addEffect(new SereneRemembranceEffect()); - this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("up to three target cards from a single graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS)); } @@ -44,7 +44,7 @@ class SereneRemembranceEffect extends OneShotEffect { public SereneRemembranceEffect() { super(Outcome.Benefit); - this.staticText = "Shuffle Serene Remembrance and up to three target cards from a single graveyard into their owners' libraries"; + this.staticText = "Shuffle {this} and up to three target cards from a single graveyard into their owners' libraries"; } public SereneRemembranceEffect(final SereneRemembranceEffect effect) { @@ -58,36 +58,21 @@ class SereneRemembranceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - boolean result = false; - - // 3 cards to graveyard - Player graveyardPlayer = null; - for (UUID cardInGraveyard : targetPointer.getTargets(game, source)) { - Card card = game.getCard(cardInGraveyard); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Card card = game.getCard(source.getSourceId()); if (card != null) { - for (Player player : game.getPlayers().values()) { - if (player.getGraveyard().contains(card.getId())) { - graveyardPlayer = player; - player.getGraveyard().remove(card); - result |= card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } + controller.shuffleCardsToLibrary(card, game, source); + } + Cards cards = new CardsImpl(getTargetPointer().getTargets(game, source)); + if (!cards.isEmpty()) { + Player owner = game.getPlayer(cards.getCards(game).iterator().next().getOwnerId()); + if (owner != null) { + owner.shuffleCardsToLibrary(cards, game, source); } } + return true; } - - // source card to graveyard - Card card = game.getCard(source.getSourceId()); - if (card != null) { - result |= card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); - Player player = game.getPlayer(card.getOwnerId()); - if (player != null) { - player.shuffleLibrary(source, game); - } - if (graveyardPlayer != null && !graveyardPlayer.equals(player)) { - graveyardPlayer.shuffleLibrary(source, game); - } - } - - return result; + return false; } } diff --git a/Mage.Sets/src/mage/cards/s/SerraBestiary.java b/Mage.Sets/src/mage/cards/s/SerraBestiary.java index 4e3892ace4..a571390814 100644 --- a/Mage.Sets/src/mage/cards/s/SerraBestiary.java +++ b/Mage.Sets/src/mage/cards/s/SerraBestiary.java @@ -7,7 +7,6 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.AttachEffect; @@ -104,16 +103,12 @@ class SerraBestiaryRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl } MageObject object = game.getObject(event.getSourceId()); Optional ability = game.getAbility(event.getTargetId(), event.getSourceId()); - if (ability.isPresent() + return ability.isPresent() && object != null && object.isCreature() && object.getId().equals(enchantedCreature.getId()) - && game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId())) { - if (ability.get().getCosts().stream().anyMatch((cost) -> (cost instanceof TapSourceCost))) { - return true; - } - } - return false; + && game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) + && ability.get().hasTapCost(); } @Override diff --git a/Mage.Sets/src/mage/cards/s/SerrasHymn.java b/Mage.Sets/src/mage/cards/s/SerrasHymn.java index 5d7b9be30b..efe88bbf76 100644 --- a/Mage.Sets/src/mage/cards/s/SerrasHymn.java +++ b/Mage.Sets/src/mage/cards/s/SerrasHymn.java @@ -1,55 +1,55 @@ -package mage.cards.s; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.common.PreventDamageToTargetMultiAmountEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.target.common.TargetAnyTargetAmount; - -/** - * - * @author jeffwadsworth - */ -public final class SerrasHymn extends CardImpl { - - private static final String rule = "Prevent the next X damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose, where X is the number of verse counters on {this}."; - - public SerrasHymn(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); - - // At the beginning of your upkeep, you may put a verse counter on Serra's Hymn. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.VERSE.createInstance(), true), TargetController.YOU, true)); - - // Sacrifice Serra's Hymn: Prevent the next X damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose, where X is the number of verse counters on Serra's Hymn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new PreventDamageToTargetMultiAmountEffect( - Duration.EndOfTurn, - 1, false, true, // the integer 1 is ignored due to the dynamic number being set - new CountersSourceCount(CounterType.VERSE)).setText(rule), - new SacrificeSourceCost()); - ability.addTarget(new TargetAnyTargetAmount(new CountersSourceCount(CounterType.VERSE))); - this.addAbility(ability); - - } - - public SerrasHymn(final SerrasHymn card) { - super(card); - } - - @Override - public SerrasHymn copy() { - return new SerrasHymn(this); - } -} +package mage.cards.s; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.PreventDamageToTargetMultiAmountEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.common.TargetAnyTargetAmount; + +/** + * + * @author jeffwadsworth + */ +public final class SerrasHymn extends CardImpl { + + private static final String rule = "Prevent the next X damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose, where X is the number of verse counters on {this}."; + + public SerrasHymn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + // At the beginning of your upkeep, you may put a verse counter on Serra's Hymn. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.VERSE.createInstance(), true), TargetController.YOU, true)); + + // Sacrifice Serra's Hymn: Prevent the next X damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose, where X is the number of verse counters on Serra's Hymn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new PreventDamageToTargetMultiAmountEffect( + Duration.EndOfTurn, + 1, false, true, // the integer 1 is ignored due to the dynamic number being set + new CountersSourceCount(CounterType.VERSE)).setText(rule), + new SacrificeSourceCost()); + ability.addTarget(new TargetAnyTargetAmount(new CountersSourceCount(CounterType.VERSE))); + this.addAbility(ability); + + } + + public SerrasHymn(final SerrasHymn card) { + super(card); + } + + @Override + public SerrasHymn copy() { + return new SerrasHymn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SerratedScorpion.java b/Mage.Sets/src/mage/cards/s/SerratedScorpion.java new file mode 100644 index 0000000000..f95e3077ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SerratedScorpion.java @@ -0,0 +1,44 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SerratedScorpion extends CardImpl { + + public SerratedScorpion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.SCORPION); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // When Serrated Scorpion dies, it deals 2 damage to each opponent and you gain 2 life. + Ability ability = new DiesSourceTriggeredAbility(new DamagePlayersEffect( + 2, TargetController.OPPONENT, "it" + )); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); + this.addAbility(ability); + } + + private SerratedScorpion(final SerratedScorpion card) { + super(card); + } + + @Override + public SerratedScorpion copy() { + return new SerratedScorpion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SerumPowder.java b/Mage.Sets/src/mage/cards/s/SerumPowder.java index b30d9caf37..0f9ac2704e 100644 --- a/Mage.Sets/src/mage/cards/s/SerumPowder.java +++ b/Mage.Sets/src/mage/cards/s/SerumPowder.java @@ -69,7 +69,7 @@ class SerumPowderReplaceEffect extends ReplacementEffectImpl { for (Card card: cards.getCards(game)) { card.moveToExile(null, null, source.getSourceId(), game); } - controller.drawCards(cardsHand, game); + controller.drawCards(cardsHand, source.getSourceId(), game); } game.informPlayers(sourceCard.getLogName() +": " + controller.getLogName() + " exiles hand and draws " + cardsHand + " card(s)"); return true; diff --git a/Mage.Sets/src/mage/cards/s/SerumRaker.java b/Mage.Sets/src/mage/cards/s/SerumRaker.java index 2ccbb51593..9055a92de9 100644 --- a/Mage.Sets/src/mage/cards/s/SerumRaker.java +++ b/Mage.Sets/src/mage/cards/s/SerumRaker.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class SerumRaker extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new DiesTriggeredAbility(new DiscardEachPlayerEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new DiscardEachPlayerEffect())); } public SerumRaker (final SerumRaker card) { diff --git a/Mage.Sets/src/mage/cards/s/SerumTank.java b/Mage.Sets/src/mage/cards/s/SerumTank.java index 59b4f5bbbe..52875eee5c 100644 --- a/Mage.Sets/src/mage/cards/s/SerumTank.java +++ b/Mage.Sets/src/mage/cards/s/SerumTank.java @@ -1,48 +1,44 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author CountAndromalius */ public final class SerumTank extends CardImpl { public SerumTank(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // Whenever {this} or another artifact comes into play, put a charge counter on {this}. - Effect effect = new AddCountersSourceEffect(CounterType.CHARGE.createInstance()); - effect.setText("put a charge counter on {this}"); - this.addAbility(new SerumTankTriggeredAbility(effect)); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), StaticFilters.FILTER_PERMANENT_ARTIFACT + )); // {3}, {tap}, Remove a charge counter from {this}: Draw a card. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{3}")); - ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(1))); + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), new GenericManaCost(3) + ); ability.addCost(new TapSourceCost()); + ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance())); this.addAbility(ability); } - public SerumTank(final SerumTank card) { + private SerumTank(final SerumTank card) { super(card); } @@ -51,42 +47,3 @@ public final class SerumTank extends CardImpl { return new SerumTank(this); } } - -class SerumTankTriggeredAbility extends TriggeredAbilityImpl { - - SerumTankTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, false); - } - - SerumTankTriggeredAbility(final SerumTankTriggeredAbility ability) { - super(ability); - } - - @Override - public SerumTankTriggeredAbility copy() { - return new SerumTankTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - UUID targetId = event.getTargetId(); - Permanent permanent = game.getPermanent(targetId); - if (permanent.isArtifact()) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(permanent, game)); - } - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} or another artifact enters the battlefield, put a charge counter on {this}."; - } -} diff --git a/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java b/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java index 32aaf50a85..f6454d2975 100644 --- a/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java +++ b/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -40,7 +40,7 @@ public final class ServantOfTheScale extends CardImpl { "with a +1/+1 counter on it")); // When Servant of the Scale dies, put X +1/+1 counters on target creature you control, where X is the number of +1/+1 counter on Servant of the Scale. - Ability ability = new DiesTriggeredAbility(new ServantOfTheScaleEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new ServantOfTheScaleEffect(), false); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SetessanTactics.java b/Mage.Sets/src/mage/cards/s/SetessanTactics.java index 51e2b69656..87a6601845 100644 --- a/Mage.Sets/src/mage/cards/s/SetessanTactics.java +++ b/Mage.Sets/src/mage/cards/s/SetessanTactics.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.abilityword.StriveAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -19,8 +17,9 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SetessanTactics extends CardImpl { @@ -36,6 +35,7 @@ public final class SetessanTactics extends CardImpl { // Strive - Setessan Tactics costs G more to cast for each target beyond the first. this.addAbility(new StriveAbility("{G}")); + // Until end of turn, any number of target creatures each get +1/+1 and gain "T: This creature fights another target creature." this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE)); Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); diff --git a/Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java b/Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java new file mode 100644 index 0000000000..596757a7dd --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java @@ -0,0 +1,76 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.MinotaurToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SethronHurloonGeneral extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent(SubType.MINOTAUR, "nontoken Minotaur"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent(SubType.MINOTAUR, ""); + private static final FilterPermanent filter3 = new FilterPermanent(SubType.MINOTAUR, ""); + + static { + filter.add(Predicates.not(TokenPredicate.instance)); + } + + public SethronHurloonGeneral(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.MINOTAUR); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever Sethron, Hurloon General or another nontoken Minotaur enters the battlefield under your control, create a 2/3 red Minotaur creature token. + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new CreateTokenEffect(new MinotaurToken()), filter, false, true + )); + + // {2}{B/R}: Minotaurs you control get +1/+0 and gain menace and haste until end of turn. + Ability ability = new SimpleActivatedAbility(new BoostControlledEffect( + 1, 0, Duration.EndOfTurn, filter2 + ).setText("Minotaurs you control get +1/+0"), new ManaCostsImpl("{2}{B/R}")); + ability.addEffect(new GainAbilityControlledEffect( + new MenaceAbility(), Duration.EndOfTurn, filter3 + ).setText("and gain menace")); + ability.addEffect(new GainAbilityControlledEffect( + HasteAbility.getInstance(), Duration.EndOfTurn, filter3 + ).setText("and haste until end of turn")); + this.addAbility(ability); + } + + private SethronHurloonGeneral(final SethronHurloonGeneral card) { + super(card); + } + + @Override + public SethronHurloonGeneral copy() { + return new SethronHurloonGeneral(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SetonKrosanProtector.java b/Mage.Sets/src/mage/cards/s/SetonKrosanProtector.java index 298a22bcd7..8edf79240a 100644 --- a/Mage.Sets/src/mage/cards/s/SetonKrosanProtector.java +++ b/Mage.Sets/src/mage/cards/s/SetonKrosanProtector.java @@ -1,10 +1,14 @@ package mage.cards.s; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import mage.MageInt; import mage.Mana; +import mage.abilities.Ability; import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -12,9 +16,11 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -23,13 +29,6 @@ import mage.target.common.TargetControlledCreaturePermanent; */ public final class SetonKrosanProtector extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Druid you control"); - - static { - filter.add(Predicates.not(TappedPredicate.instance)); - filter.add(SubType.DRUID.getPredicate()); - } - public SetonKrosanProtector(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}{G}{G}"); addSuperType(SuperType.LEGENDARY); @@ -40,7 +39,12 @@ public final class SetonKrosanProtector extends CardImpl { this.toughness = new MageInt(2); // Tap an untapped Druid you control: Add {G}. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana(1), + FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Druid you control"); + filter.add(Predicates.not(TappedPredicate.instance)); + filter.add(SubType.DRUID.getPredicate()); + this.addAbility(new SimpleManaAbility( + Zone.BATTLEFIELD, + new SetonKrosanProtectorManaEffect(filter), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); } @@ -53,3 +57,39 @@ public final class SetonKrosanProtector extends CardImpl { return new SetonKrosanProtector(this); } } + +class SetonKrosanProtectorManaEffect extends BasicManaEffect { + + private final FilterPermanent filter; + + public SetonKrosanProtectorManaEffect(FilterPermanent filter) { + super(Mana.GreenMana(1)); + this.filter = filter; + } + + public SetonKrosanProtectorManaEffect(final SetonKrosanProtectorManaEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + } + + @Override + public SetonKrosanProtectorManaEffect copy() { + return new SetonKrosanProtectorManaEffect(this); + } + + @Override + public List getNetMana(Game game, Ability source) { + if (game != null && game.inCheckPlayableState()) { + // Because the ability can be used multiple times, multiply with untapped druids + int count = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + List netMana = new ArrayList<>(); + if (count > 0) { + netMana.add(Mana.GreenMana(count)); + } + return netMana; + + } + return super.getNetMana(game, source); + } + +} diff --git a/Mage.Sets/src/mage/cards/s/SettleBeyondReality.java b/Mage.Sets/src/mage/cards/s/SettleBeyondReality.java index 7abb90d369..b8814372f3 100644 --- a/Mage.Sets/src/mage/cards/s/SettleBeyondReality.java +++ b/Mage.Sets/src/mage/cards/s/SettleBeyondReality.java @@ -7,9 +7,7 @@ import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetE import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -20,12 +18,6 @@ import java.util.UUID; */ public final class SettleBeyondReality extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public SettleBeyondReality(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}"); @@ -35,11 +27,11 @@ public final class SettleBeyondReality extends CardImpl { // • Exile target creature you don't control. this.getSpellAbility().addEffect(new ExileTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); // • Exile target creature you control, then return it to the battlefield under its owner's control. Mode mode = new Mode(new ExileTargetForSourceEffect()); - mode.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); + mode.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); mode.addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addMode(mode); } diff --git a/Mage.Sets/src/mage/cards/s/SeverSoul.java b/Mage.Sets/src/mage/cards/s/SeverSoul.java index 96bf920f72..2df31e8d7a 100644 --- a/Mage.Sets/src/mage/cards/s/SeverSoul.java +++ b/Mage.Sets/src/mage/cards/s/SeverSoul.java @@ -64,7 +64,7 @@ class GainLifeEqualToToughnessEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/s/SeveredStrands.java b/Mage.Sets/src/mage/cards/s/SeveredStrands.java index 523aedf125..9ce635c5e9 100644 --- a/Mage.Sets/src/mage/cards/s/SeveredStrands.java +++ b/Mage.Sets/src/mage/cards/s/SeveredStrands.java @@ -8,7 +8,7 @@ import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetOpponentsCreaturePermanent; @@ -22,7 +22,7 @@ public final class SeveredStrands extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // As an additional cost to cast this spell, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent("a creature"), true))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, true))); // You gain life equal to the sacrificed creature's toughness. Destroy target creature an opponent controls. this.getSpellAbility().addEffect(new GainLifeEffect( diff --git a/Mage.Sets/src/mage/cards/s/SewerNemesis.java b/Mage.Sets/src/mage/cards/s/SewerNemesis.java index 7bfc1fa2fc..612a44b550 100644 --- a/Mage.Sets/src/mage/cards/s/SewerNemesis.java +++ b/Mage.Sets/src/mage/cards/s/SewerNemesis.java @@ -114,7 +114,7 @@ class SewerNemesisTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever the chosen player casts a spell, that player puts the top card of their library into their graveyard."; + return "Whenever the chosen player casts a spell, that player mills a card."; } @Override diff --git a/Mage.Sets/src/mage/cards/s/ShaakHerd.java b/Mage.Sets/src/mage/cards/s/ShaakHerd.java index 96f3933057..16852ebec0 100644 --- a/Mage.Sets/src/mage/cards/s/ShaakHerd.java +++ b/Mage.Sets/src/mage/cards/s/ShaakHerd.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -33,7 +33,7 @@ public final class ShaakHerd extends CardImpl { this.toughness = new MageInt(2); // When Shaak Herd dies, you may return another target creature card from your graveyard to your hand. - Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), true); + Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), true); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/ShabrazTheSkyshark.java b/Mage.Sets/src/mage/cards/s/ShabrazTheSkyshark.java new file mode 100644 index 0000000000..1fb6afe8ce --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShabrazTheSkyshark.java @@ -0,0 +1,70 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DrawCardControllerTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShabrazTheSkyshark extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.HUMAN, "Human"); + + public ShabrazTheSkyshark(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHARK); + this.subtype.add(SubType.BIRD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Partner with Brallin, Skyshark Rider + this.addAbility(new PartnerWithAbility("Brallin, Skyshark Rider")); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever you draw a card, put a +1/+1 counter on Shabraz, the Skyshark and you gain 1 life. + Ability ability = new DrawCardControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false + ); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); + + // {W/U}: Target Human gains flying until end of turn. + ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl("{W/U}")); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private ShabrazTheSkyshark(final ShabrazTheSkyshark card) { + super(card); + } + + @Override + public ShabrazTheSkyshark copy() { + return new ShabrazTheSkyshark(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Shacklegeist.java b/Mage.Sets/src/mage/cards/s/Shacklegeist.java new file mode 100644 index 0000000000..d5a633beca --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Shacklegeist.java @@ -0,0 +1,65 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CanBlockOnlyFlyingAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Shacklegeist extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledPermanent("untapped Spirits you control"); + + static { + filter.add(Predicates.not(TappedPredicate.instance)); + filter.add(SubType.SPIRIT.getPredicate()); + } + + public Shacklegeist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Shacklegeist can block only creatures with flying. + this.addAbility(new CanBlockOnlyFlyingAbility()); + + // Tap two untapped Spirits you control: Tap target creature you don't control. + Ability ability = new SimpleActivatedAbility( + new TapTargetEffect(), new TapTargetCost(new TargetControlledPermanent(2, filter)) + ); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.addAbility(ability); + } + + private Shacklegeist(final Shacklegeist card) { + super(card); + } + + @Override + public Shacklegeist copy() { + return new Shacklegeist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShadowSlice.java b/Mage.Sets/src/mage/cards/s/ShadowSlice.java index a3ea77c054..7a25b54a7a 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowSlice.java +++ b/Mage.Sets/src/mage/cards/s/ShadowSlice.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.effects.common.CipherEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; @@ -9,15 +7,15 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ShadowSlice extends CardImpl { - public ShadowSlice (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{B}"); - + public ShadowSlice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); // Target Opponent loses 3 life. this.getSpellAbility().addEffect(new LoseLifeTargetEffect(3)); @@ -33,7 +31,7 @@ public final class ShadowSlice extends CardImpl { } @Override - public ShadowSlice copy() { + public ShadowSlice copy() { return new ShadowSlice(this); } } diff --git a/Mage.Sets/src/mage/cards/s/ShahOfNaarIsle.java b/Mage.Sets/src/mage/cards/s/ShahOfNaarIsle.java index be4bed717a..9e9d404ff6 100644 --- a/Mage.Sets/src/mage/cards/s/ShahOfNaarIsle.java +++ b/Mage.Sets/src/mage/cards/s/ShahOfNaarIsle.java @@ -106,7 +106,7 @@ class ShahOfNaarIsleEffect extends OneShotEffect { Player opponent = game.getPlayer(playerId); if (opponent != null) { int number = opponent.getAmount(0, 3, "Draw how many cards?", game); - opponent.drawCards(number, game); + opponent.drawCards(number, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/s/ShamanOfThePack.java b/Mage.Sets/src/mage/cards/s/ShamanOfThePack.java index 167160264f..929b0d6e90 100644 --- a/Mage.Sets/src/mage/cards/s/ShamanOfThePack.java +++ b/Mage.Sets/src/mage/cards/s/ShamanOfThePack.java @@ -1,13 +1,12 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -16,8 +15,9 @@ import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class ShamanOfThePack extends CardImpl { @@ -30,7 +30,7 @@ public final class ShamanOfThePack extends CardImpl { } public ShamanOfThePack(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(3); @@ -41,6 +41,7 @@ public final class ShamanOfThePack extends CardImpl { effect.setText("target opponent loses life equal to the number of Elves you control"); Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); ability.addTarget(new TargetOpponent()); + ability.addHint(new ValueHint("Elves you control", new PermanentsOnBattlefieldCount(filter))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/Shambleshark.java b/Mage.Sets/src/mage/cards/s/Shambleshark.java index 5faf34c397..c9d8c07414 100644 --- a/Mage.Sets/src/mage/cards/s/Shambleshark.java +++ b/Mage.Sets/src/mage/cards/s/Shambleshark.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.keyword.EvolveAbility; import mage.abilities.keyword.FlashAbility; @@ -10,15 +8,16 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Shambleshark extends CardImpl { public Shambleshark(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}{U}"); - this.subtype.add(SubType.FISH); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}"); + this.subtype.add(SubType.SHARK); this.subtype.add(SubType.CRAB); this.power = new MageInt(2); @@ -30,7 +29,7 @@ public final class Shambleshark extends CardImpl { this.addAbility(new EvolveAbility()); } - public Shambleshark(final Shambleshark card) { + private Shambleshark(final Shambleshark card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java b/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java index ed512bf60f..83fdf692a4 100644 --- a/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java +++ b/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -35,7 +35,7 @@ public final class ShamblingGoblin extends CardImpl { this.toughness = new MageInt(1); // When Shambling Goblin dies, target creature an opponent controls gets -1/-1 until end of turn. - Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-1,-1, Duration.EndOfTurn)); + Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-1,-1, Duration.EndOfTurn)); ability.addTarget(new TargetCreaturePermanent(filterOpponentCreature)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java b/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java index 84d5681765..c6f445e9ec 100644 --- a/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java +++ b/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class ShamblingSwarm extends CardImpl { this.toughness = new MageInt(3); // When Shambling Swarm dies, distribute three -1/-1 counters among one, two, or three target creatures. For each -1/-1 counter you put on a creature this way, remove a -1/-1 counter from that creature at the beginning of the next end step. - Ability ability = new DiesTriggeredAbility(new DistributeCountersEffect(CounterType.M1M1, 3, true, "one, two, or three target creatures"), false); + Ability ability = new DiesSourceTriggeredAbility(new DistributeCountersEffect(CounterType.M1M1, 3, true, "one, two, or three target creatures"), false); ability.addTarget(new TargetCreaturePermanentAmount(3)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/ShapeStealer.java b/Mage.Sets/src/mage/cards/s/ShapeStealer.java index d2bc6f976b..f9d7ba254c 100644 --- a/Mage.Sets/src/mage/cards/s/ShapeStealer.java +++ b/Mage.Sets/src/mage/cards/s/ShapeStealer.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; @@ -37,7 +37,7 @@ public final class ShapeStealer extends CardImpl { // each one in succession. The first trigger put on the stack will be the last to resolve, // so that will set Shape Stealer's final power and toughness. // Whenever Shape Stealer blocks or becomes blocked by a creature, change Shape Stealer's base power and toughness to that creature's power and toughness until end of turn. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new ShapeStealerEffect(), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new ShapeStealerEffect(), false)); } public ShapeStealer(final ShapeStealer card) { @@ -70,7 +70,7 @@ class ShapeStealerEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { ContinuousEffect effect = new SetPowerToughnessSourceEffect(permanent.getPower().getValue(), permanent.getToughness().getValue(), Duration.EndOfTurn, SubLayer.SetPT_7b); game.addEffect(effect, source); diff --git a/Mage.Sets/src/mage/cards/s/SharedTrauma.java b/Mage.Sets/src/mage/cards/s/SharedTrauma.java index 915bbbeeb1..2f82c35a9b 100644 --- a/Mage.Sets/src/mage/cards/s/SharedTrauma.java +++ b/Mage.Sets/src/mage/cards/s/SharedTrauma.java @@ -43,7 +43,7 @@ class SharedTraumaEffect extends OneShotEffect { public SharedTraumaEffect() { super(Outcome.Detriment); - this.staticText = "Join forces — Starting with you, each player may pay any amount of mana. Each player puts the top X cards of their library into their graveyard, where X is the total amount of mana paid this way"; + this.staticText = "Join forces — Starting with you, each player may pay any amount of mana. Each player mills X cards, where X is the total amount of mana paid this way"; } public SharedTraumaEffect(final SharedTraumaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SharkTyphoon.java b/Mage.Sets/src/mage/cards/s/SharkTyphoon.java new file mode 100644 index 0000000000..8753458c49 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SharkTyphoon.java @@ -0,0 +1,119 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.common.ZoneChangeTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.SharkToken; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SharkTyphoon extends CardImpl { + + public SharkTyphoon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{5}{U}"); + + // Whenever you cast a noncreature spell, create an X/X blue Shark creature token with flying, where X is that spell's converted mana cost. + this.addAbility(new SpellCastControllerTriggeredAbility( + new SharkTyphoonCastEffect(), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false, true + )); + + // Cycling {X}{1}{U} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{X}{1}{U}"))); + + // When you cycle Shark Typhoon, create an X/X blue Shark creature token with flying. + this.addAbility(new SharkTyphoonTriggeredAbility()); + } + + private SharkTyphoon(final SharkTyphoon card) { + super(card); + } + + @Override + public SharkTyphoon copy() { + return new SharkTyphoon(this); + } +} + +class SharkTyphoonCastEffect extends OneShotEffect { + + SharkTyphoonCastEffect() { + super(Outcome.Benefit); + staticText = "create an X/X blue Shark creature token with flying, where X is that spell's converted mana cost"; + } + + private SharkTyphoonCastEffect(final SharkTyphoonCastEffect effect) { + super(effect); + } + + @Override + public SharkTyphoonCastEffect copy() { + return new SharkTyphoonCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = game.getSpell(getTargetPointer().getFirst(game, source)); + int xValue = 0; + if (spell != null) { + xValue = spell.getConvertedManaCost(); + } + return new SharkToken(xValue).putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + } +} + +class SharkTyphoonTriggeredAbility extends ZoneChangeTriggeredAbility { + + SharkTyphoonTriggeredAbility() { + super(Zone.ALL, null, "", false); + } + + private SharkTyphoonTriggeredAbility(SharkTyphoonTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getSourceId().equals(this.getSourceId())) { + return false; + } + StackObject object = game.getStack().getStackObject(event.getSourceId()); + if (object == null || !(object.getStackAbility() instanceof CyclingAbility)) { + return false; + } + this.getEffects().clear(); + this.addEffect(new CreateTokenEffect(new SharkToken(object.getStackAbility().getManaCostsToPay().getX()))); + return true; + } + + @Override + public SharkTyphoonTriggeredAbility copy() { + return new SharkTyphoonTriggeredAbility(this); + } + + @Override + public String getRule() { + return "When you cycle {this}, create an X/X blue Shark creature token with flying."; + } +} diff --git a/Mage.Sets/src/mage/cards/s/Sharktocrab.java b/Mage.Sets/src/mage/cards/s/Sharktocrab.java index 64c91d73a1..d094f4f2dc 100644 --- a/Mage.Sets/src/mage/cards/s/Sharktocrab.java +++ b/Mage.Sets/src/mage/cards/s/Sharktocrab.java @@ -22,7 +22,7 @@ public final class Sharktocrab extends CardImpl { public Sharktocrab(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); - this.subtype.add(SubType.FISH); + this.subtype.add(SubType.SHARK); this.subtype.add(SubType.OCTOPUS); this.subtype.add(SubType.CRAB); this.power = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/s/ShatterAssumptions.java b/Mage.Sets/src/mage/cards/s/ShatterAssumptions.java index bea5cab22d..09dc73af6f 100644 --- a/Mage.Sets/src/mage/cards/s/ShatterAssumptions.java +++ b/Mage.Sets/src/mage/cards/s/ShatterAssumptions.java @@ -3,7 +3,9 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterCard; @@ -83,17 +85,14 @@ class ShatterAssumptionsEffect extends OneShotEffect { if (player == null) { return false; } - Cards cards = new CardsImpl(player.getHand()); - player.revealCards(source, cards, game); + player.revealCards(source, player.getHand(), game); FilterCard f; if (colorless) { f = filter; } else { f = filter2; } - for (Card card : cards.getCards(f, source.getSourceId(), source.getControllerId(), game)) { - player.discard(card, source, game); - } + player.discard(new CardsImpl(player.getHand().getCards(f, game)), source, game); return true; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/ShatterTheSky.java b/Mage.Sets/src/mage/cards/s/ShatterTheSky.java index b150c4c041..df61edd001 100644 --- a/Mage.Sets/src/mage/cards/s/ShatterTheSky.java +++ b/Mage.Sets/src/mage/cards/s/ShatterTheSky.java @@ -75,7 +75,7 @@ class ShatterTheSkyEffect extends OneShotEffect { .distinct() .map(game::getPlayer) .filter(Objects::nonNull) - .forEach(player -> player.drawCards(1, game)); + .forEach(player -> player.drawCards(1, source.getSourceId(), game)); effect.apply(game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/s/ShelteringPrayers.java b/Mage.Sets/src/mage/cards/s/ShelteringPrayers.java index 7526f97c07..a74ed04795 100644 --- a/Mage.Sets/src/mage/cards/s/ShelteringPrayers.java +++ b/Mage.Sets/src/mage/cards/s/ShelteringPrayers.java @@ -1,98 +1,98 @@ -package mage.cards.s; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.keyword.ShroudAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import static mage.constants.Layer.AbilityAddingRemovingEffects_6; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * - * @author jeffwadsworth - */ -public final class ShelteringPrayers extends CardImpl { - - public ShelteringPrayers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); - - // Basic lands each player controls have shroud as long as that player controls three or fewer lands. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ShelteringPrayersEffect())); - - } - - public ShelteringPrayers(final ShelteringPrayers card) { - super(card); - } - - @Override - public ShelteringPrayers copy() { - return new ShelteringPrayers(this); - } -} - -class ShelteringPrayersEffect extends ContinuousEffectImpl { - - public ShelteringPrayersEffect() { - super(Duration.WhileOnBattlefield, Outcome.AddAbility); - staticText = "Basic lands each player controls have shroud as long as that player controls three or fewer lands."; - dependencyTypes.add(DependencyType.AddingAbility); - - } - - public ShelteringPrayersEffect(final ShelteringPrayersEffect effect) { - super(effect); - } - - @Override - public ShelteringPrayersEffect copy() { - return new ShelteringPrayersEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null - && game.getBattlefield().getAllActivePermanents(new FilterLandPermanent(), playerId, game).size() < 4) { - for (Permanent land : game.getBattlefield().getAllActivePermanents(new FilterLandPermanent(), playerId, game)) { - if (land != null - && land.isBasic()) { - switch (layer) { - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - land.getAbilities().add(ShroudAbility.getInstance()); - } - break; - } - } - } - } - } - return true; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return Layer.AbilityAddingRemovingEffects_6 == layer; - } - -} +package mage.cards.s; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.ShroudAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.DependencyType; +import mage.constants.Duration; +import mage.constants.Layer; +import static mage.constants.Layer.AbilityAddingRemovingEffects_6; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.filter.common.FilterLandPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author jeffwadsworth + */ +public final class ShelteringPrayers extends CardImpl { + + public ShelteringPrayers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + // Basic lands each player controls have shroud as long as that player controls three or fewer lands. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ShelteringPrayersEffect())); + + } + + public ShelteringPrayers(final ShelteringPrayers card) { + super(card); + } + + @Override + public ShelteringPrayers copy() { + return new ShelteringPrayers(this); + } +} + +class ShelteringPrayersEffect extends ContinuousEffectImpl { + + public ShelteringPrayersEffect() { + super(Duration.WhileOnBattlefield, Outcome.AddAbility); + staticText = "Basic lands each player controls have shroud as long as that player controls three or fewer lands."; + dependencyTypes.add(DependencyType.AddingAbility); + + } + + public ShelteringPrayersEffect(final ShelteringPrayersEffect effect) { + super(effect); + } + + @Override + public ShelteringPrayersEffect copy() { + return new ShelteringPrayersEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null + && game.getBattlefield().getAllActivePermanents(new FilterLandPermanent(), playerId, game).size() < 4) { + for (Permanent land : game.getBattlefield().getAllActivePermanents(new FilterLandPermanent(), playerId, game)) { + if (land != null + && land.isBasic()) { + switch (layer) { + case AbilityAddingRemovingEffects_6: + if (sublayer == SubLayer.NA) { + land.getAbilities().add(ShroudAbility.getInstance()); + } + break; + } + } + } + } + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return Layer.AbilityAddingRemovingEffects_6 == layer; + } + +} diff --git a/Mage.Sets/src/mage/cards/s/ShieldedByFaith.java b/Mage.Sets/src/mage/cards/s/ShieldedByFaith.java index 7d60dd5190..ef0d17a9ea 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldedByFaith.java +++ b/Mage.Sets/src/mage/cards/s/ShieldedByFaith.java @@ -39,13 +39,15 @@ public final class ShieldedByFaith extends CardImpl { this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // Enchanted creature has indestructible. - Effect effect = new GainAbilityAttachedEffect(IndestructibleAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield); + Effect effect = new GainAbilityAttachedEffect(IndestructibleAbility.getInstance(), + AttachmentType.AURA, Duration.WhileOnBattlefield); effect.setText("Enchanted creature has indestructible"); + effect.setOutcome(Outcome.Benefit); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); // Whenever a creature enters the battlefield, you may attach Shielded by Faith to that creature. this.addAbility(new EntersBattlefieldAllTriggeredAbility( - Zone.BATTLEFIELD, new AttachEffect(Outcome.Neutral, "attach {source} to that creature"), + Zone.BATTLEFIELD, new AttachEffect(Outcome.Benefit, "attach {source} to that creature"), new FilterCreaturePermanent("a creature"), true, SetTargetPointer.PERMANENT, null, false)); } diff --git a/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java b/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java index 5afe8b026c..edc7d925e2 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java +++ b/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java @@ -78,7 +78,7 @@ class ShieldsOfVelisVelGainEffect extends ContinuousEffectImpl { for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { Permanent permanent = it.next().getPermanent(game); if (permanent != null) { - permanent.addAbility(ChangelingAbility.getInstance(), source.getSourceId(), game, false); + permanent.addAbility(ChangelingAbility.getInstance(), source.getSourceId(), game); } else { it.remove(); } diff --git a/Mage.Sets/src/mage/cards/s/ShiftingCeratops.java b/Mage.Sets/src/mage/cards/s/ShiftingCeratops.java index 6a501a6451..efc57339c0 100644 --- a/Mage.Sets/src/mage/cards/s/ShiftingCeratops.java +++ b/Mage.Sets/src/mage/cards/s/ShiftingCeratops.java @@ -3,7 +3,7 @@ package mage.cards.s; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; @@ -40,7 +40,7 @@ public final class ShiftingCeratops extends CardImpl { this.toughness = new MageInt(4); // This spell can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Protection from blue this.addAbility(ProtectionAbility.from(ObjectColor.BLUE)); diff --git a/Mage.Sets/src/mage/cards/s/ShiftingShadow.java b/Mage.Sets/src/mage/cards/s/ShiftingShadow.java index e181f37a9d..5f9f1e72ba 100644 --- a/Mage.Sets/src/mage/cards/s/ShiftingShadow.java +++ b/Mage.Sets/src/mage/cards/s/ShiftingShadow.java @@ -71,12 +71,12 @@ public final class ShiftingShadow extends CardImpl { class ShiftingShadowEffect extends OneShotEffect { - private UUID auraId; + private final UUID auraId; public ShiftingShadowEffect(UUID auraId) { super(Outcome.PutCreatureInPlay); this.staticText = "destroy this creature. Reveal cards from the top of your library until you reveal a creature card. " - + "Put that card onto the battlefield and attach Shifting Shadow to it, then put all other cards revealed this way on the bottom of your library in a random order"; + + "Put that card onto the battlefield and attach {this} to it, then put all other cards revealed this way on the bottom of your library in a random order"; this.auraId = auraId; } @@ -107,6 +107,11 @@ class ShiftingShadowEffect extends OneShotEffect { } if (aura != null) { enchanted.destroy(source.getSourceId(), game, false); + // Because this effect has two steps, we have to call the processAction method here, so that triggered effects of the target going to graveyard go to the stack + // If we don't do it here, gained triggered effects to the target will be removed from the following moveCards method and the applyEffcts done there. + // Example: {@link org.mage.test.commander.duel.MairsilThePretenderTest#MairsilThePretenderTest Test} + game.getState().processAction(game); + Cards revealed = new CardsImpl(); Cards otherCards = new CardsImpl(); for (Card card : controller.getLibrary().getCards(game)) { diff --git a/Mage.Sets/src/mage/cards/s/ShinyImpetus.java b/Mage.Sets/src/mage/cards/s/ShinyImpetus.java new file mode 100644 index 0000000000..f02de1301c --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShinyImpetus.java @@ -0,0 +1,58 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksAttachedTriggeredAbility; +import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.permanent.token.TreasureToken; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShinyImpetus extends CardImpl { + + public ShinyImpetus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 and is goaded. + this.addAbility(new GoadAttachedAbility(new BoostEnchantedEffect(2, 2))); + + // Whenever enchanted creature attacks, you create a Treasure token. + this.addAbility(new AttacksAttachedTriggeredAbility( + new CreateTokenEffect(new TreasureToken()) + .setText("you create a Treasure token"), + AttachmentType.AURA, false + )); + } + + private ShinyImpetus(final ShinyImpetus card) { + super(card); + } + + @Override + public ShinyImpetus copy() { + return new ShinyImpetus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShipwreckDowser.java b/Mage.Sets/src/mage/cards/s/ShipwreckDowser.java new file mode 100644 index 0000000000..b68abcb097 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShipwreckDowser.java @@ -0,0 +1,51 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShipwreckDowser extends CardImpl { + + private static final FilterCard filter + = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"); + + public ShipwreckDowser(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.MERFOLK); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Prowess + this.addAbility(new ProwessAbility()); + + // When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private ShipwreckDowser(final ShipwreckDowser card) { + super(card); + } + + @Override + public ShipwreckDowser copy() { + return new ShipwreckDowser(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShipwreckLooter.java b/Mage.Sets/src/mage/cards/s/ShipwreckLooter.java index 03ae7cef4b..202318de1e 100644 --- a/Mage.Sets/src/mage/cards/s/ShipwreckLooter.java +++ b/Mage.Sets/src/mage/cards/s/ShipwreckLooter.java @@ -1,21 +1,22 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawDiscardControllerEffect; -import mage.constants.SubType; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; +import mage.constants.SubType; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ShipwreckLooter extends CardImpl { @@ -28,11 +29,13 @@ public final class ShipwreckLooter extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - // Raid - When Shipwreck Looter enters the battlefield,if you attacked with a creature this turn, you may draw a card. If you do, discard a card. + // Raid - When Shipwreck Looter enters the battlefield,if you attacked this turn, you may draw a card. If you do, discard a card. Ability ability = new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new DrawDiscardControllerEffect(1, 1, true)), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, you may draw a card. If you do, discard a card."); + "Raid — When {this} enters the battlefield, if you attacked this turn, you may draw a card. If you do, discard a card."); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java b/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java index 5af0b2937e..de64a813aa 100644 --- a/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java +++ b/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java @@ -121,7 +121,7 @@ class ShireiShizosCaretakerEffect extends OneShotEffect { Effect effect = new ShireiShizosCaretakerReturnEffect(shireiId); effect.setText("return that card to the battlefield if {this} is still on the battlefield"); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); - delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(card, game)); game.addDelayedTriggeredAbility(delayedAbility, source); return true; } diff --git a/Mage.Sets/src/mage/cards/s/ShivanPhoenix.java b/Mage.Sets/src/mage/cards/s/ShivanPhoenix.java index 33d1dca830..80425ea158 100644 --- a/Mage.Sets/src/mage/cards/s/ShivanPhoenix.java +++ b/Mage.Sets/src/mage/cards/s/ShivanPhoenix.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class ShivanPhoenix extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Shivan Phoenix dies, return Shivan Phoenix to its owner's hand. - this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect())); } public ShivanPhoenix(final ShivanPhoenix card) { diff --git a/Mage.Sets/src/mage/cards/s/ShoalSerpent.java b/Mage.Sets/src/mage/cards/s/ShoalSerpent.java index 8769d56081..73a060cff2 100644 --- a/Mage.Sets/src/mage/cards/s/ShoalSerpent.java +++ b/Mage.Sets/src/mage/cards/s/ShoalSerpent.java @@ -67,7 +67,7 @@ class ShoalSerpentEffect extends ContinuousEffectImpl { switch (layer) { case AbilityAddingRemovingEffects_6: if (sublayer == SubLayer.NA) { - permanent.getAbilities().removeIf(entry -> entry.getId().equals(DefenderAbility.getInstance().getId())); + permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game); } break; } diff --git a/Mage.Sets/src/mage/cards/s/Shocker.java b/Mage.Sets/src/mage/cards/s/Shocker.java index 87ae290807..176afefbf0 100644 --- a/Mage.Sets/src/mage/cards/s/Shocker.java +++ b/Mage.Sets/src/mage/cards/s/Shocker.java @@ -1,28 +1,26 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsDamageToAPlayerTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author markedagain */ public final class Shocker extends CardImpl { public Shocker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.INSECT); this.power = new MageInt(1); this.toughness = new MageInt(1); @@ -40,14 +38,15 @@ public final class Shocker extends CardImpl { return new Shocker(this); } } + class ShockerEffect extends OneShotEffect { - public ShockerEffect() { + ShockerEffect() { super(Outcome.Discard); this.staticText = " that player discards all the cards in their hand, then draws that many cards"; } - public ShockerEffect(final ShockerEffect effect) { + private ShockerEffect(final ShockerEffect effect) { super(effect); } @@ -59,14 +58,11 @@ class ShockerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (targetPlayer != null) { - int count = targetPlayer.getHand().size(); - for (Card card : targetPlayer.getHand().getCards(game)) { - targetPlayer.discard(card, source, game); - } - targetPlayer.drawCards(count, game); - return false; - } + if (targetPlayer == null) { + return false; + } + int count = targetPlayer.discard(targetPlayer.getHand(), source, game).size(); + targetPlayer.drawCards(count, source.getSourceId(), game); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/Showstopper.java b/Mage.Sets/src/mage/cards/s/Showstopper.java index ff46adbe76..8fbc3a3f4b 100644 --- a/Mage.Sets/src/mage/cards/s/Showstopper.java +++ b/Mage.Sets/src/mage/cards/s/Showstopper.java @@ -1,9 +1,8 @@ - package mage.cards.s; import java.util.UUID; import mage.abilities.TriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -34,14 +33,12 @@ public final class Showstopper extends CardImpl { public Showstopper (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{R}"); - - // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls." - TriggeredAbility ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it"), false); + TriggeredAbility ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2, "it"), false); Target target = new TargetCreaturePermanent(filter2); ability.addTarget(target); Effect effect = new GainAbilityControlledEffect(ability, Duration.EndOfTurn, filter); - effect.setText("Until end of turn, creatures you control gain \"When this creature dies, it deals 2 damage to target creature an opponent controls.\" "); + effect.setText("Until end of turn, creatures you control gain \"When this creature dies, it deals 2 damage to target creature an opponent controls.\""); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/s/ShredMemory.java b/Mage.Sets/src/mage/cards/s/ShredMemory.java index f84304230c..d3005cd630 100644 --- a/Mage.Sets/src/mage/cards/s/ShredMemory.java +++ b/Mage.Sets/src/mage/cards/s/ShredMemory.java @@ -1,27 +1,27 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.keyword.TransmuteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInASingleGraveyard; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ShredMemory extends CardImpl { public ShredMemory(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); // Exile up to four target cards from a single graveyard. this.getSpellAbility().addEffect(new ExileTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 4, new FilterCard("cards"))); + this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 4, StaticFilters.FILTER_CARD_CARDS)); + // Transmute {1}{B}{B} this.addAbility(new TransmuteAbility("{1}{B}{B}")); } diff --git a/Mage.Sets/src/mage/cards/s/ShreddedSails.java b/Mage.Sets/src/mage/cards/s/ShreddedSails.java new file mode 100644 index 0000000000..eb20e6fe23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShreddedSails.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.abilities.Mode; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetArtifactPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShreddedSails extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public ShreddedSails(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Choose one — + // • Destroy target artifact. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetArtifactPermanent()); + + // • Shredded Sails deals 4 damage to target creature with flying. + Mode mode = new Mode(new DamageTargetEffect(4)); + mode.addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addMode(mode); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private ShreddedSails(final ShreddedSails card) { + super(card); + } + + @Override + public ShreddedSails copy() { + return new ShreddedSails(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Shriekgeist.java b/Mage.Sets/src/mage/cards/s/Shriekgeist.java index fb41104d80..9c0b1c8c0d 100644 --- a/Mage.Sets/src/mage/cards/s/Shriekgeist.java +++ b/Mage.Sets/src/mage/cards/s/Shriekgeist.java @@ -80,6 +80,6 @@ class ShriekgeistTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever {this} deals combat damage to a player, that player puts the top two cards of their library into their graveyard."; + return "Whenever {this} deals combat damage to a player, that player mills two cards."; } } diff --git a/Mage.Sets/src/mage/cards/s/ShrivelingRot.java b/Mage.Sets/src/mage/cards/s/ShrivelingRot.java index dfe775bf0e..278fa9bab3 100644 --- a/Mage.Sets/src/mage/cards/s/ShrivelingRot.java +++ b/Mage.Sets/src/mage/cards/s/ShrivelingRot.java @@ -145,7 +145,7 @@ class ShrivelingRotEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { if (permanent.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(permanent.getId()) && game.getState().getZone(permanent.getId()) != Zone.GRAVEYARD) { diff --git a/Mage.Sets/src/mage/cards/s/SibilantSpirit.java b/Mage.Sets/src/mage/cards/s/SibilantSpirit.java index 80c491f681..6b68ad9b4b 100644 --- a/Mage.Sets/src/mage/cards/s/SibilantSpirit.java +++ b/Mage.Sets/src/mage/cards/s/SibilantSpirit.java @@ -66,7 +66,7 @@ class SibilantSpiritEffect extends OneShotEffect { Player defender = game.getPlayer(defenderId); if (defender != null) { if (defender.chooseUse(outcome, "Draw a card?", source, game)) { - defender.drawCards(1, game); + defender.drawCards(1, source.getSourceId(), game); } } return false; diff --git a/Mage.Sets/src/mage/cards/s/SickeningDreams.java b/Mage.Sets/src/mage/cards/s/SickeningDreams.java index 3a74c9e7ee..ac2d709f11 100644 --- a/Mage.Sets/src/mage/cards/s/SickeningDreams.java +++ b/Mage.Sets/src/mage/cards/s/SickeningDreams.java @@ -47,7 +47,7 @@ class SickeningDreamsAdditionalCost extends VariableCostImpl { SickeningDreamsAdditionalCost() { super("cards to discard"); - this.text = "as an additional cost to cast this spell, discard X cards"; + this.text = "discard X cards"; } SickeningDreamsAdditionalCost(final SickeningDreamsAdditionalCost cost) { diff --git a/Mage.Sets/src/mage/cards/s/SidarKondoOfJamuraa.java b/Mage.Sets/src/mage/cards/s/SidarKondoOfJamuraa.java index f3db71d472..6bbd1ab410 100644 --- a/Mage.Sets/src/mage/cards/s/SidarKondoOfJamuraa.java +++ b/Mage.Sets/src/mage/cards/s/SidarKondoOfJamuraa.java @@ -80,8 +80,8 @@ class SidarKondoOfJamuraaCantBlockCreaturesSourceEffect extends RestrictionEffec @Override public boolean applies(Permanent permanent, Ability source, Game game) { - if (permanent.hasAbility(FlyingAbility.getInstance().getId(), game) - || permanent.hasAbility(ReachAbility.getInstance().getId(), game)) { + if (permanent.hasAbility(FlyingAbility.getInstance(), game) + || permanent.hasAbility(ReachAbility.getInstance(), game)) { return false; } return game.getOpponents(source.getControllerId()).contains(permanent.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/s/SidisiBroodTyrant.java b/Mage.Sets/src/mage/cards/s/SidisiBroodTyrant.java index 933cbb20ce..b4bca16a13 100644 --- a/Mage.Sets/src/mage/cards/s/SidisiBroodTyrant.java +++ b/Mage.Sets/src/mage/cards/s/SidisiBroodTyrant.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.Set; import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; @@ -73,13 +71,9 @@ class SidisiBroodTyrantTriggeredAbility extends TriggeredAbilityImpl { if (zEvent != null && Zone.LIBRARY == zEvent.getFromZone() && Zone.GRAVEYARD == zEvent.getToZone() && zEvent.getCards() != null) { for (Card card : zEvent.getCards()) { if (card != null) { - UUID cardOwnerId = card.getOwnerId(); - Set cardType = card.getCardType(); - if (cardOwnerId != null && card.isOwnedBy(getControllerId()) - && cardType != null && card.isCreature()) { return true; } diff --git a/Mage.Sets/src/mage/cards/s/SiegeStriker.java b/Mage.Sets/src/mage/cards/s/SiegeStriker.java new file mode 100644 index 0000000000..33bfd4cc6f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SiegeStriker.java @@ -0,0 +1,99 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author mikalinn777 + */ +public final class SiegeStriker extends CardImpl { + + public SiegeStriker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Double Strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // Whenever Siege Striker attacks, you may tap any number of untapped creatures you control. Siege Striker gets +1/+1 until end of turn for each creature tapped this way. + this.addAbility(new AttacksTriggeredAbility( + new SiegeStrikerEffect(), true + )); + + } + + public SiegeStriker(final SiegeStriker card) { + super(card); + } + + @Override + public SiegeStriker copy() { + return new SiegeStriker(this); + } +} + +class SiegeStrikerEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("untapped creatures you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(Predicates.not(TappedPredicate.instance)); + } + + public SiegeStrikerEffect() { + super(Outcome.GainLife); + staticText = "you may tap any number of untapped creatures you control. " + + "{this} gets +1/+1 until end of turn for each creature tapped this way"; + } + + public SiegeStrikerEffect(SiegeStrikerEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + int tappedAmount = 0; + TargetCreaturePermanent target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true); + if (target.canChoose(source.getControllerId(), game) + && target.choose(Outcome.Tap, source.getControllerId(), source.getSourceId(), game)) { + for (UUID creatureId : target.getTargets()) { + Permanent creature = game.getPermanent(creatureId); + if (creature != null) { + creature.tap(game); + tappedAmount++; + } + } + } + if (tappedAmount > 0) { + game.addEffect(new BoostSourceEffect(tappedAmount, tappedAmount, Duration.EndOfTurn), source); + return true; + } + return false; + } + + @Override + public SiegeStrikerEffect copy() { + return new SiegeStrikerEffect(this); + } + +} diff --git a/Mage.Sets/src/mage/cards/s/SigiledContender.java b/Mage.Sets/src/mage/cards/s/SigiledContender.java new file mode 100644 index 0000000000..9e89d6bf83 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SigiledContender.java @@ -0,0 +1,50 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SigiledContender extends CardImpl { + + private static final Condition condition = new SourceHasCounterCondition(CounterType.P1P1); + + public SigiledContender(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Sigiled Contender has lifelink as long as it has a +1/+1 counter on it. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect( + LifelinkAbility.getInstance(), Duration.WhileOnBattlefield + ), condition, "{this} has lifelink as long as it has a +1/+1 counter on it" + ))); + } + + private SigiledContender(final SigiledContender card) { + super(card); + } + + @Override + public SigiledContender copy() { + return new SigiledContender(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SignalTheClans.java b/Mage.Sets/src/mage/cards/s/SignalTheClans.java index 0a9d34bad7..5775bbc25c 100644 --- a/Mage.Sets/src/mage/cards/s/SignalTheClans.java +++ b/Mage.Sets/src/mage/cards/s/SignalTheClans.java @@ -56,39 +56,36 @@ class SignalTheClansEffect extends SearchEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } //Search your library for three creature cards - if (player.searchLibrary(target, source, game)) { + if (controller.searchLibrary(target, source, game)) { + boolean shuffleDone = false; if (!target.getTargets().isEmpty()) { - Cards cards = new CardsImpl(); - for (UUID cardId : target.getTargets()) { - Card card = player.getLibrary().remove(cardId, game); - if (card != null) { - cards.add(card); - } - } + Cards cards = new CardsImpl(target.getTargets()); //Reveal them - player.revealCards("Reveal", cards, game); + controller.revealCards(source, cards, game); Card cardsArray[] = cards.getCards(game).toArray(new Card[0]); //If you reveal three cards with different names if (Stream.of(cardsArray).map(MageObject::getName).collect(Collectors.toSet()).size() == 3) { //Choose one of them at random and put that card into your hand Card randomCard = cards.getRandom(game); - randomCard.moveToZone(Zone.HAND, source.getSourceId(), game, true); + controller.moveCards(randomCard, Zone.HAND, source, game); cards.remove(randomCard); } - //Shuffle the rest into your library - for (Card card : cards.getCards(game)) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + // Shuffle the rest into your library + if (!cards.isEmpty()) { + controller.shuffleCardsToLibrary(cards, game, source); + shuffleDone = true; } } - player.shuffleLibrary(source, game); + if (!shuffleDone) { + controller.shuffleLibrary(source, game); + } return true; } - player.shuffleLibrary(source, game); return false; } diff --git a/Mage.Sets/src/mage/cards/s/SilenceTheBelievers.java b/Mage.Sets/src/mage/cards/s/SilenceTheBelievers.java index cd4bb3d042..e38ebb5400 100644 --- a/Mage.Sets/src/mage/cards/s/SilenceTheBelievers.java +++ b/Mage.Sets/src/mage/cards/s/SilenceTheBelievers.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.abilities.Ability; @@ -7,8 +6,8 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -20,19 +19,17 @@ import java.util.List; import java.util.UUID; - /** - * * @author LevelX2 */ public final class SilenceTheBelievers extends CardImpl { public SilenceTheBelievers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{B}"); // Strive - Silence the Believers costs 2B more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{B}")); + // Exile any number of target creatures and all Auras attached to them. this.getSpellAbility().addEffect(new SilenceTheBelieversExileEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE)); @@ -69,11 +66,11 @@ class SilenceTheBelieversExileEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - for (UUID targetId: this.getTargetPointer().getTargets(game, source)) { + for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { Permanent creature = game.getPermanent(targetId); if (creature != null) { List attachments = new ArrayList<>(creature.getAttachments()); - for (UUID attachmentId: attachments) { + for (UUID attachmentId : attachments) { Permanent attachment = game.getPermanent(attachmentId); if (attachment != null && attachment.hasSubtype(SubType.AURA, game)) { controller.moveCardToExileWithInfo(attachment, null, null, source.getSourceId(), game, Zone.BATTLEFIELD, true); diff --git a/Mage.Sets/src/mage/cards/s/SilentChantZubera.java b/Mage.Sets/src/mage/cards/s/SilentChantZubera.java index b2d0ec1e0b..b3d36bc842 100644 --- a/Mage.Sets/src/mage/cards/s/SilentChantZubera.java +++ b/Mage.Sets/src/mage/cards/s/SilentChantZubera.java @@ -5,7 +5,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.GainLifeEffect; @@ -29,7 +29,7 @@ public final class SilentChantZubera extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - Ability ability = new DiesTriggeredAbility(new GainLifeEffect(new SilentChantZuberaDynamicValue())); + Ability ability = new DiesSourceTriggeredAbility(new GainLifeEffect(new SilentChantZuberaDynamicValue())); this.addAbility(ability, new ZuberasDiedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/SilkenfistFighter.java b/Mage.Sets/src/mage/cards/s/SilkenfistFighter.java index 4d8c1d64cb..bd7c4a5227 100644 --- a/Mage.Sets/src/mage/cards/s/SilkenfistFighter.java +++ b/Mage.Sets/src/mage/cards/s/SilkenfistFighter.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.UntapSourceEffect; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class SilkenfistFighter extends CardImpl { // Whenever Silkenfist Fighter becomes blocked, untap it. Effect effect = new UntapSourceEffect(); effect.setText("untap it"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public SilkenfistFighter(final SilkenfistFighter card) { diff --git a/Mage.Sets/src/mage/cards/s/SilkenfistOrder.java b/Mage.Sets/src/mage/cards/s/SilkenfistOrder.java index 1d6b9ff506..5f76079d5f 100644 --- a/Mage.Sets/src/mage/cards/s/SilkenfistOrder.java +++ b/Mage.Sets/src/mage/cards/s/SilkenfistOrder.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.UntapSourceEffect; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class SilkenfistOrder extends CardImpl { // Whenever Silkenfist Order becomes blocked, untap it. Effect effect = new UntapSourceEffect(); effect.setText("untap it"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public SilkenfistOrder(final SilkenfistOrder card) { diff --git a/Mage.Sets/src/mage/cards/s/SilvarDevourerOfTheFree.java b/Mage.Sets/src/mage/cards/s/SilvarDevourerOfTheFree.java new file mode 100644 index 0000000000..ad6a54a3b2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SilvarDevourerOfTheFree.java @@ -0,0 +1,67 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledPermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SilvarDevourerOfTheFree extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledPermanent(SubType.HUMAN, "a Human"); + + public SilvarDevourerOfTheFree(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Partner with Trynn, Champion of Freedom + this.addAbility(new PartnerWithAbility("Trynn, Champion of Freedom")); + + // Menace + this.addAbility(new MenaceAbility()); + + // Sacrifice a Human: Put a +1/+1 counter on Silvar, Devourer of the Free. It gains indestructible until end of turn. + Ability ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + new SacrificeTargetCost(new TargetControlledPermanent(filter)) + ); + ability.addEffect(new GainAbilitySourceEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains indestructible until end of turn")); + this.addAbility(ability); + } + + private SilvarDevourerOfTheFree(final SilvarDevourerOfTheFree card) { + super(card); + } + + @Override + public SilvarDevourerOfTheFree copy() { + return new SilvarDevourerOfTheFree(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SilverWyvern.java b/Mage.Sets/src/mage/cards/s/SilverWyvern.java index 42e7c731b1..1a01717a61 100644 --- a/Mage.Sets/src/mage/cards/s/SilverWyvern.java +++ b/Mage.Sets/src/mage/cards/s/SilverWyvern.java @@ -1,54 +1,62 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; +import mage.MageItem; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ChooseNewTargetsTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterStackObject; -import mage.filter.predicate.Predicate; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; import mage.game.stack.StackObject; import mage.target.Target; import mage.target.TargetStackObject; -import mage.target.Targets; + +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Stream; /** - * * @author LevelX2 */ public final class SilverWyvern extends CardImpl { - + + private static final FilterStackObject filter = new FilterStackObject(); + + static { + filter.add(SilverWyvernPredicate.instance); + } + public SilverWyvern(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.subtype.add(SubType.DRAKE); + this.power = new MageInt(4); this.toughness = new MageInt(3); // Flying this.addAbility(FlyingAbility.getInstance()); // {U}: Change the target of target spell or ability that targets only Silver Wyvern. The new target must be a creature. - Effect effect = new ChooseNewTargetsTargetEffect(true, true); - effect.setText("Change the target of target spell or ability that targets only {this}. The new target must be a creature"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{U}")); - FilterStackObject filter = new FilterStackObject(); - filter.add(new TargetsOnlySourcePredicate(getId())); + Ability ability = new SimpleActivatedAbility( + new ChooseNewTargetsTargetEffect(true, true) + .setText("Change the target of target spell or ability that targets only {this}. " + + "The new target must be a creature"), + new ManaCostsImpl("{U}") + ); ability.addTarget(new TargetStackObject(filter)); this.addAbility(ability); - + } - public SilverWyvern(final SilverWyvern card) { + private SilverWyvern(final SilverWyvern card) { super(card); } @@ -58,35 +66,24 @@ public final class SilverWyvern extends CardImpl { } } -class TargetsOnlySourcePredicate implements Predicate { - - private final UUID sourceId; - - public TargetsOnlySourcePredicate(UUID sourceId) { - this.sourceId = sourceId; - } +enum SilverWyvernPredicate implements ObjectSourcePlayerPredicate> { + instance; @Override - public boolean apply(MageObject input, Game game) { - StackObject stackObject = game.getStack().getStackObject(input.getId()); - if (stackObject != null) { - Targets spellTargets = stackObject.getStackAbility().getTargets(); - int numberOfTargets = 0; - for (Target target : spellTargets) { - if (target.getFirstTarget() == null || !target.getFirstTarget().toString().equals(sourceId.toString())) { // UUID != UUID does not work - it's always false - return false; - } - numberOfTargets += target.getTargets().size(); - } - if (numberOfTargets == 1) { - return true; - } - } - return false; + public boolean apply(ObjectSourcePlayer input, Game game) { + return makeStream(input, game).anyMatch(input.getSourceId()::equals) + && makeStream(input, game).allMatch(input.getSourceId()::equals); } - @Override - public String toString() { - return "target spell or ability that targets only source"; + private static final Stream makeStream(ObjectSourcePlayer input, Game game) { + return input.getObject() + .getStackAbility() + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .map(game::getPermanent) + .filter(Objects::nonNull) + .map(MageItem::getId); } } diff --git a/Mage.Sets/src/mage/cards/s/SilverbackShaman.java b/Mage.Sets/src/mage/cards/s/SilverbackShaman.java index d33b3e2867..794d0a67e7 100644 --- a/Mage.Sets/src/mage/cards/s/SilverbackShaman.java +++ b/Mage.Sets/src/mage/cards/s/SilverbackShaman.java @@ -1,7 +1,7 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class SilverbackShaman extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Silverback Shaman dies, draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1))); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1))); } private SilverbackShaman(final SilverbackShaman card) { diff --git a/Mage.Sets/src/mage/cards/s/SilversmoteGhoul.java b/Mage.Sets/src/mage/cards/s/SilversmoteGhoul.java new file mode 100644 index 0000000000..85d5dd0288 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SilversmoteGhoul.java @@ -0,0 +1,64 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SilversmoteGhoul extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2); + private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn"); + + public SilversmoteGhoul(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // At the beginning of your end step, if you gained 3 or more life this turn, return Silversmote Ghoul from your graveyard to the battlefield tapped. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(true), + TargetController.YOU, null, false + ), condition, "At the beginning of your end step, " + + "if you gained 3 or more life this turn, return {this} from your graveyard to the battlefield tapped." + ).addHint(hint), new PlayerGainedLifeWatcher()); + + // {1}{B}, Sacrifice Silversmote Ghoul: Draw a card. + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}{B}") + ); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private SilversmoteGhoul(final SilversmoteGhoul card) { + super(card); + } + + @Override + public SilversmoteGhoul copy() { + return new SilversmoteGhoul(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Sindbad.java b/Mage.Sets/src/mage/cards/s/Sindbad.java index 771411d55c..acdee2cf53 100644 --- a/Mage.Sets/src/mage/cards/s/Sindbad.java +++ b/Mage.Sets/src/mage/cards/s/Sindbad.java @@ -69,7 +69,7 @@ class SindbadEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Card card = controller.getLibrary().getFromTop(game); - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); controller.revealCards("Sindbad", new CardsImpl(card), game); if (!filter.match(card, game)) { controller.discard(card, source, game); diff --git a/Mage.Sets/src/mage/cards/s/SingleCombat.java b/Mage.Sets/src/mage/cards/s/SingleCombat.java index 309fe92b2f..eec4681d94 100644 --- a/Mage.Sets/src/mage/cards/s/SingleCombat.java +++ b/Mage.Sets/src/mage/cards/s/SingleCombat.java @@ -60,7 +60,7 @@ class SingleCombatEffect extends OneShotEffect { SingleCombatEffect() { super(Outcome.Benefit); - staticText = "Each player chooses a creature or planeswalker they control, then sacrifices the rest."; + staticText = "Each player chooses a creature or planeswalker they control, then sacrifices the rest"; } private SingleCombatEffect(final SingleCombatEffect effect) { @@ -97,7 +97,7 @@ class SingleCombatRestrictionEffect extends ContinuousRuleModifyingEffectImpl { SingleCombatRestrictionEffect() { super(Duration.UntilEndOfYourNextTurn, Outcome.Neutral); - staticText = "Players can't cast creature or planeswalker spells until the end of your next turn."; + staticText = "Players can't cast creature or planeswalker spells until the end of your next turn"; } private SingleCombatRestrictionEffect(final SingleCombatRestrictionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SinsOfThePast.java b/Mage.Sets/src/mage/cards/s/SinsOfThePast.java index 21aa8edb35..35177017ea 100644 --- a/Mage.Sets/src/mage/cards/s/SinsOfThePast.java +++ b/Mage.Sets/src/mage/cards/s/SinsOfThePast.java @@ -20,6 +20,7 @@ import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; /** * @author emerald000 @@ -65,8 +66,8 @@ class SinsOfThePastEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); if (card != null) { - ContinuousEffect effect = new SinsOfThePastCastFromGraveyardEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.GRAVEYARD, TargetController.YOU, Duration.EndOfTurn, true);; + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); effect = new SinsOfThePastReplacementEffect(card.getId()); game.addEffect(effect, source); @@ -76,39 +77,6 @@ class SinsOfThePastEffect extends OneShotEffect { } } -class SinsOfThePastCastFromGraveyardEffect extends AsThoughEffectImpl { - - SinsOfThePastCastFromGraveyardEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.PlayForFree); - } - - SinsOfThePastCastFromGraveyardEffect(final SinsOfThePastCastFromGraveyardEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public SinsOfThePastCastFromGraveyardEffect copy() { - return new SinsOfThePastCastFromGraveyardEffect(this); - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) { - Player player = game.getPlayer(affectedControllerId); - if(player != null) { - player.setCastSourceIdWithAlternateMana(sourceId, null, null); - return true; - } - } - return false; - } -} - class SinsOfThePastReplacementEffect extends ReplacementEffectImpl { private final UUID cardId; diff --git a/Mage.Sets/src/mage/cards/s/SireOfInsanity.java b/Mage.Sets/src/mage/cards/s/SireOfInsanity.java index 7a9f1af074..90ece9fc95 100644 --- a/Mage.Sets/src/mage/cards/s/SireOfInsanity.java +++ b/Mage.Sets/src/mage/cards/s/SireOfInsanity.java @@ -1,43 +1,39 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.game.Game; -import mage.game.events.GameEvent; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ - - public final class SireOfInsanity extends CardImpl { - public SireOfInsanity (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{R}"); + public SireOfInsanity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{R}"); this.subtype.add(SubType.DEMON); - this.power = new MageInt(6); this.toughness = new MageInt(4); // At the beginning of each end step, each player discards their hand. - this.addAbility(new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of each end step", true, new SireOfInsanityEffect())); - + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new SireOfInsanityEffect(), TargetController.ANY, false + )); } - public SireOfInsanity (final SireOfInsanity card) { + private SireOfInsanity(final SireOfInsanity card) { super(card); } @@ -54,7 +50,7 @@ class SireOfInsanityEffect extends OneShotEffect { staticText = "each player discards their hand"; } - SireOfInsanityEffect(final SireOfInsanityEffect effect) { + private SireOfInsanityEffect(final SireOfInsanityEffect effect) { super(effect); } @@ -67,9 +63,7 @@ class SireOfInsanityEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - for (Card c : player.getHand().getCards(game)) { - player.discard(c, source, game); - } + player.discard(player.getHand(), source, game); } } return true; diff --git a/Mage.Sets/src/mage/cards/s/SirenOfTheSilentSong.java b/Mage.Sets/src/mage/cards/s/SirenOfTheSilentSong.java index 9bb077784a..9bde3310b1 100644 --- a/Mage.Sets/src/mage/cards/s/SirenOfTheSilentSong.java +++ b/Mage.Sets/src/mage/cards/s/SirenOfTheSilentSong.java @@ -34,7 +34,7 @@ public final class SirenOfTheSilentSong extends CardImpl { // Inspired — Whenever Siren of the Silent Song becomes untapped, each opponent discards a card, then puts the top card of their library into their graveyard. Ability ability = new InspiredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT)); Effect effect = new PutTopCardOfLibraryIntoGraveEachPlayerEffect(1, TargetController.OPPONENT); - effect.setText(", then puts the top card of their library into their graveyard"); + effect.setText(", then mills a card"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SirenReaver.java b/Mage.Sets/src/mage/cards/s/SirenReaver.java index 517e092365..713816d357 100644 --- a/Mage.Sets/src/mage/cards/s/SirenReaver.java +++ b/Mage.Sets/src/mage/cards/s/SirenReaver.java @@ -1,12 +1,11 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.common.RaidHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -16,8 +15,9 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SirenReaver extends CardImpl { @@ -30,15 +30,15 @@ public final class SirenReaver extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - // Raid — Siren Reaver costs {1} less to cast if you attacked with a creature this turn. - Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, RaidCondition.instance)); - ability.setAbilityWord(AbilityWord.RAID); + // Raid — Siren Reaver costs {1} less to cast if you attacked this turn. + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, RaidCondition.instance)); ability.setRuleAtTheTop(true); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); // Flying this.addAbility(FlyingAbility.getInstance()); - } public SirenReaver(final SirenReaver card) { diff --git a/Mage.Sets/src/mage/cards/s/SirensRuse.java b/Mage.Sets/src/mage/cards/s/SirensRuse.java index 62baaece6b..fe0a4fdcf6 100644 --- a/Mage.Sets/src/mage/cards/s/SirensRuse.java +++ b/Mage.Sets/src/mage/cards/s/SirensRuse.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.abilities.Ability; @@ -16,7 +15,6 @@ import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; /** - * * @author TheElk801 */ public final class SirensRuse extends CardImpl { @@ -64,9 +62,9 @@ class SirensRuseEffect extends ExileTargetForSourceEffect { isPirate = true; } if (super.apply(game, source)) { - new ReturnToBattlefieldUnderYourControlTargetEffect(true).apply(game, source); + new ReturnToBattlefieldUnderYourControlTargetEffect(false).apply(game, source); if (isPirate && player != null) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/SithMindseer.java b/Mage.Sets/src/mage/cards/s/SithMindseer.java index 74a76a635f..07fe6121a4 100644 --- a/Mage.Sets/src/mage/cards/s/SithMindseer.java +++ b/Mage.Sets/src/mage/cards/s/SithMindseer.java @@ -6,6 +6,8 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.HateCondition; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; @@ -13,6 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.Zone; import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.LifeLossOtherFromCombatWatcher; @@ -29,11 +32,16 @@ public final class SithMindseer extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - // Hate — When Sith Mindseer enters the battlefield, if an opponent loses life from a source other than combat damage, gain control of target creature for as long as Sith Mindseer remains on the battlefield. + // Hate — When Sith Mindseer enters the battlefield, if an opponent loses life from a source other than combat damage, + // gain control of target creature for as long as Sith Mindseer remains on the battlefield. Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new GainControlTargetEffect(Duration.WhileOnBattlefield)), + new EntersBattlefieldTriggeredAbility(new ConditionalContinuousEffect( + new GainControlTargetEffect(Duration.Custom, true), + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), + "gain control of target creature for as long as {this} remains on the battlefield")), HateCondition.instance, - "Hate — When Sith Mindseer enters the battlefield, if an opponent loses life from a source other than combat damage, gain control of target creature for as long as Sith Mindseer remains on the battlefield."); + "Hate — When {this} enters the battlefield, if an opponent loses life from a source other than combat damage," + + " gain control of target creature for as long as {this} remains on the battlefield."); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability, new LifeLossOtherFromCombatWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/SkeletalScrying.java b/Mage.Sets/src/mage/cards/s/SkeletalScrying.java index 7e65477a11..76a790f5f5 100644 --- a/Mage.Sets/src/mage/cards/s/SkeletalScrying.java +++ b/Mage.Sets/src/mage/cards/s/SkeletalScrying.java @@ -105,7 +105,7 @@ class SkeletalScryingEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if ( controller != null ) { - controller.drawCards(amount.calculate(game, source, this), game); + controller.drawCards(amount.calculate(game, source, this), source.getSourceId(), game); controller.loseLife(amount.calculate(game, source, this), game, false); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SkeletonScavengers.java b/Mage.Sets/src/mage/cards/s/SkeletonScavengers.java index 4adc1ec205..95f31ef9eb 100644 --- a/Mage.Sets/src/mage/cards/s/SkeletonScavengers.java +++ b/Mage.Sets/src/mage/cards/s/SkeletonScavengers.java @@ -1,130 +1,130 @@ -package mage.cards.s; - -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.AsEntersBattlefieldAbility; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.RegenerateSourceEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.util.ManaUtil; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class SkeletonScavengers extends CardImpl { - - public SkeletonScavengers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); - - this.subtype.add(SubType.SKELETON); - this.power = new MageInt(0); - this.toughness = new MageInt(0); - - // Skeleton Scavengers enters the battlefield with a +1/+1 counter on it. - this.addAbility(new AsEntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()))); - - // Pay {1} for each +1/+1 counter on Skeleton Scavengers: Regenerate Skeleton Scavengers. When it regenerates this way, put a +1/+1 counter on it. - this.addAbility(new SimpleActivatedAbility(new SkeletonScavengersEffect(), new DynamicValueGenericManaCost(new CountersSourceCount(CounterType.P1P1)))); - - } - - public SkeletonScavengers(final SkeletonScavengers card) { - super(card); - } - - @Override - public SkeletonScavengers copy() { - return new SkeletonScavengers(this); - } -} - -class DynamicValueGenericManaCost extends CostImpl { - - DynamicValue amount; - - public DynamicValueGenericManaCost(DynamicValue amount) { - this.amount = amount; - setText(); - } - - public DynamicValueGenericManaCost(DynamicValueGenericManaCost cost) { - super(cost); - this.amount = cost.amount; - } - - @Override - public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - Player controller = game.getPlayer(controllerId); - if (controller == null) { - return false; - } - Cost cost = ManaUtil.createManaCost(amount, game, ability, null); - return cost.canPay(ability, sourceId, controllerId, game); - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { - Player controller = game.getPlayer(controllerId); - if (controller == null) { - return false; - } - - Cost cost = ManaUtil.createManaCost(amount, game, ability, null); - paid = cost.pay(ability, game, sourceId, controllerId, noMana); - - return paid; - } - - @Override - public DynamicValueGenericManaCost copy() { - return new DynamicValueGenericManaCost(this); - } - - private void setText() { - text = ("Pay {1} for each +1/+1 counter on {this}"); - } -} - -class SkeletonScavengersEffect extends OneShotEffect { - - SkeletonScavengersEffect() { - super(Outcome.Benefit); - this.staticText = "Regenerate {this}. When it regenerates this way, put a +1/+1 counter on it"; - } - - SkeletonScavengersEffect(final SkeletonScavengersEffect effect) { - super(effect); - } - - @Override - public SkeletonScavengersEffect copy() { - return new SkeletonScavengersEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent skeletonScavengers = game.getPermanent(source.getSourceId()); - if (skeletonScavengers != null) { - if (new RegenerateSourceEffect().apply(game, source)) { - return new AddCountersSourceEffect(CounterType.P1P1.createInstance()).apply(game, source); - } - } - return false; - } -} +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.ManaUtil; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class SkeletonScavengers extends CardImpl { + + public SkeletonScavengers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.SKELETON); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Skeleton Scavengers enters the battlefield with a +1/+1 counter on it. + this.addAbility(new AsEntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()))); + + // Pay {1} for each +1/+1 counter on Skeleton Scavengers: Regenerate Skeleton Scavengers. When it regenerates this way, put a +1/+1 counter on it. + this.addAbility(new SimpleActivatedAbility(new SkeletonScavengersEffect(), new DynamicValueGenericManaCost(new CountersSourceCount(CounterType.P1P1)))); + + } + + public SkeletonScavengers(final SkeletonScavengers card) { + super(card); + } + + @Override + public SkeletonScavengers copy() { + return new SkeletonScavengers(this); + } +} + +class DynamicValueGenericManaCost extends CostImpl { + + DynamicValue amount; + + public DynamicValueGenericManaCost(DynamicValue amount) { + this.amount = amount; + setText(); + } + + public DynamicValueGenericManaCost(DynamicValueGenericManaCost cost) { + super(cost); + this.amount = cost.amount; + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + if (controller == null) { + return false; + } + Cost cost = ManaUtil.createManaCost(amount, game, ability, null); + return cost.canPay(ability, sourceId, controllerId, game); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Player controller = game.getPlayer(controllerId); + if (controller == null) { + return false; + } + + Cost cost = ManaUtil.createManaCost(amount, game, ability, null); + paid = cost.pay(ability, game, sourceId, controllerId, noMana); + + return paid; + } + + @Override + public DynamicValueGenericManaCost copy() { + return new DynamicValueGenericManaCost(this); + } + + private void setText() { + text = ("Pay {1} for each +1/+1 counter on {this}"); + } +} + +class SkeletonScavengersEffect extends OneShotEffect { + + SkeletonScavengersEffect() { + super(Outcome.Benefit); + this.staticText = "Regenerate {this}. When it regenerates this way, put a +1/+1 counter on it"; + } + + SkeletonScavengersEffect(final SkeletonScavengersEffect effect) { + super(effect); + } + + @Override + public SkeletonScavengersEffect copy() { + return new SkeletonScavengersEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent skeletonScavengers = game.getPermanent(source.getSourceId()); + if (skeletonScavengers != null) { + if (new RegenerateSourceEffect().apply(game, source)) { + return new AddCountersSourceEffect(CounterType.P1P1.createInstance()).apply(game, source); + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SkillBorrower.java b/Mage.Sets/src/mage/cards/s/SkillBorrower.java index 60f9d87363..0fefe34ec9 100644 --- a/Mage.Sets/src/mage/cards/s/SkillBorrower.java +++ b/Mage.Sets/src/mage/cards/s/SkillBorrower.java @@ -1,26 +1,19 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.ActivatedAbility; import mage.abilities.StaticAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.GainActivatedAbilitiesOfTopCardEffect; import mage.abilities.effects.common.continuous.PlayWithTheTopCardRevealedEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; import mage.constants.SubType; import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; + +import java.util.UUID; /** * @@ -54,8 +47,14 @@ public final class SkillBorrower extends CardImpl { class SkillBorrowerAbility extends StaticAbility { + private static final FilterCard filter = new FilterCard("an artifact or creature card"); + + static { + filter.add(Predicates.or(CardType.CREATURE.getPredicate(), CardType.ARTIFACT.getPredicate())); + } + public SkillBorrowerAbility() { - super(Zone.BATTLEFIELD, new SkillBorrowerEffect()); + super(Zone.BATTLEFIELD, new GainActivatedAbilitiesOfTopCardEffect(filter)); } public SkillBorrowerAbility(SkillBorrowerAbility ability) { @@ -66,47 +65,5 @@ class SkillBorrowerAbility extends StaticAbility { public SkillBorrowerAbility copy() { return new SkillBorrowerAbility(this); } - - @Override - public String getRule() { - return "As long as the top card of your library is an artifact or creature card, {this} has all activated abilities of that card"; - } } -class SkillBorrowerEffect extends ContinuousEffectImpl { - - public SkillBorrowerEffect() { - super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - staticText = "As long as the top card of your library is an artifact or creature card, {this} has all activated abilities of that card"; - } - - public SkillBorrowerEffect(final SkillBorrowerEffect effect) { - super(effect); - } - - @Override - public SkillBorrowerEffect copy() { - return new SkillBorrowerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Card card = player.getLibrary().getFromTop(game); - if (card != null && (card.isCreature() || card.isArtifact())) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - for (Ability ability : card.getAbilities(game)) { - if (ability instanceof ActivatedAbility) { - permanent.addAbility(ability, source.getSourceId(), game); - } - } - return true; - } - } - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/SkirgeFamiliar.java b/Mage.Sets/src/mage/cards/s/SkirgeFamiliar.java index 63d3bce82b..8c7974c18f 100644 --- a/Mage.Sets/src/mage/cards/s/SkirgeFamiliar.java +++ b/Mage.Sets/src/mage/cards/s/SkirgeFamiliar.java @@ -5,6 +5,8 @@ import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.dynamicvalue.IntPlusDynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; import mage.abilities.keyword.FlyingAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -29,7 +31,13 @@ public final class SkirgeFamiliar extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Discard a card: Add {B}. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlackMana(1), new DiscardCardCost(false))); + // public SimpleManaAbility(Zone zone, Mana mana, Cost cost, DynamicValue netAmount) { + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlackMana(1), + new DiscardCardCost(false), + // not perfect but for hand cards correct, activated abilities on Battlefield will miss one possible available mana + // to solve this we have to do possible mana calculation per pell/ability to use. + new IntPlusDynamicValue(-1, CardsInControllerHandCount.instance) + )); } public SkirgeFamiliar(final SkirgeFamiliar card) { diff --git a/Mage.Sets/src/mage/cards/s/SkirkProspector.java b/Mage.Sets/src/mage/cards/s/SkirkProspector.java index b359eb40ae..0618b62902 100644 --- a/Mage.Sets/src/mage/cards/s/SkirkProspector.java +++ b/Mage.Sets/src/mage/cards/s/SkirkProspector.java @@ -5,6 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -34,7 +35,9 @@ public final class SkirkProspector extends CardImpl { this.toughness = new MageInt(1); // Sacrifice a Goblin: Add {R}. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana(1), new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1,filter,true)))); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana(1), + new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1,filter,true)), + new PermanentsOnBattlefieldCount(filter))); } public SkirkProspector(final SkirkProspector card) { diff --git a/Mage.Sets/src/mage/cards/s/SkullProphet.java b/Mage.Sets/src/mage/cards/s/SkullProphet.java new file mode 100644 index 0000000000..18b96cce47 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkullProphet.java @@ -0,0 +1,47 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SkullProphet extends CardImpl { + + public SkullProphet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // {T}: Add {B} or {G}. + this.addAbility(new BlackManaAbility()); + this.addAbility(new GreenManaAbility()); + + // {T}: Put the top two cards of your library into your graveyard. + this.addAbility(new SimpleActivatedAbility( + new PutTopCardOfLibraryIntoGraveControllerEffect(2), new TapSourceCost() + )); + } + + private SkullProphet(final SkullProphet card) { + super(card); + } + + @Override + public SkullProphet copy() { + return new SkullProphet(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SkullRend.java b/Mage.Sets/src/mage/cards/s/SkullRend.java index 9b521c8794..e2a3f3ec39 100644 --- a/Mage.Sets/src/mage/cards/s/SkullRend.java +++ b/Mage.Sets/src/mage/cards/s/SkullRend.java @@ -2,7 +2,6 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -10,24 +9,24 @@ import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; +import java.util.List; import java.util.Objects; import java.util.UUID; +import java.util.stream.Collectors; /** - * @author LevelX2 + * @author TheElk801 */ public final class SkullRend extends CardImpl { public SkullRend(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{R}"); - // Skull Rend deals 2 damage to each opponent. Those players each discard two cards at random. this.getSpellAbility().addEffect(new SkullRendEffect()); - } - public SkullRend(final SkullRend card) { + private SkullRend(final SkullRend card) { super(card); } @@ -38,39 +37,29 @@ public final class SkullRend extends CardImpl { private static class SkullRendEffect extends OneShotEffect { - public SkullRendEffect() { + private SkullRendEffect() { super(Outcome.Damage); staticText = "{this} deals 2 damage to each opponent. Those players each discard two cards at random"; } - public SkullRendEffect(final SkullRendEffect effect) { + private SkullRendEffect(final SkullRendEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - if (!Objects.equals(playerId, source.getControllerId())) { - Player opponent = game.getPlayer(playerId); - if (opponent != null) { - // damage - opponent.damage(2, source.getSourceId(), game); - // discard 2 cards at random - int amount = Math.min(2, opponent.getHand().size()); - for (int i = 0; i < amount; i++) { - Card card = opponent.getHand().getRandom(game); - if (card != null) { - opponent.discard(card, source, game); - } - } - } - } - } - return true; + List opponents = game + .getOpponents(source.getControllerId()) + .stream().map(game::getPlayer) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + for (Player opponent : opponents) { + opponent.damage(2, source.getSourceId(), game); } - return false; + for (Player opponent : opponents) { + opponent.discard(2, true, source, game); + } + return true; } @Override @@ -78,4 +67,4 @@ public final class SkullRend extends CardImpl { return new SkullRendEffect(this); } } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/Skullclamp.java b/Mage.Sets/src/mage/cards/s/Skullclamp.java index 5c348e7009..59e53ea608 100644 --- a/Mage.Sets/src/mage/cards/s/Skullclamp.java +++ b/Mage.Sets/src/mage/cards/s/Skullclamp.java @@ -28,7 +28,7 @@ public final class Skullclamp extends CardImpl { // Equipped creature gets +1/-1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1, -1))); // Whenever equipped creature dies, draw two cards. - this.addAbility(new DiesAttachedTriggeredAbility(new DrawCardSourceControllerEffect(2), "equipped")); + this.addAbility(new DiesAttachedTriggeredAbility(new DrawCardSourceControllerEffect(2), "equipped creature")); // Equip {1} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(1))); } diff --git a/Mage.Sets/src/mage/cards/s/SkullknockerOgre.java b/Mage.Sets/src/mage/cards/s/SkullknockerOgre.java index c4d453f514..f75f81793c 100644 --- a/Mage.Sets/src/mage/cards/s/SkullknockerOgre.java +++ b/Mage.Sets/src/mage/cards/s/SkullknockerOgre.java @@ -65,7 +65,7 @@ class SkullknockerOgreEffect extends OneShotEffect { return false; } if (player.discard(1, true, source, game).size() > 0) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/Skulltap.java b/Mage.Sets/src/mage/cards/s/Skulltap.java index 6039544777..23182e6325 100644 --- a/Mage.Sets/src/mage/cards/s/Skulltap.java +++ b/Mage.Sets/src/mage/cards/s/Skulltap.java @@ -7,7 +7,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -20,7 +20,7 @@ public final class Skulltap extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}"); // As an additional cost to cast Skulltap, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent("a creature"), true))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, true))); // Draw two cards. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); } diff --git a/Mage.Sets/src/mage/cards/s/SkySwallower.java b/Mage.Sets/src/mage/cards/s/SkySwallower.java index 0a6bafd472..68252b5138 100644 --- a/Mage.Sets/src/mage/cards/s/SkySwallower.java +++ b/Mage.Sets/src/mage/cards/s/SkySwallower.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; @@ -17,15 +15,16 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author ciaccona007 */ public final class SkySwallower extends CardImpl { public SkySwallower(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); - + this.subtype.add(SubType.LEVIATHAN); this.power = new MageInt(8); this.toughness = new MageInt(8); @@ -68,7 +67,7 @@ class GainControlAllPermanentsEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (targetPlayer != null && targetPlayer.isInGame()) { + if (targetPlayer != null) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { if (permanent != null && !permanent.getId().equals(source.getSourceId())) { permanent.changeControllerId(targetPlayer.getId(), game); diff --git a/Mage.Sets/src/mage/cards/s/Skybind.java b/Mage.Sets/src/mage/cards/s/Skybind.java index 8caa7cfb1b..083cd8eaa9 100644 --- a/Mage.Sets/src/mage/cards/s/Skybind.java +++ b/Mage.Sets/src/mage/cards/s/Skybind.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.abilityword.ConstellationAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -19,8 +17,9 @@ import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Skybind extends CardImpl { @@ -68,7 +67,7 @@ class SkybindEffect extends OneShotEffect { if (permanent != null && sourcePermanent != null) { if (permanent.moveToExile(source.getSourceId(), sourcePermanent.getName(), source.getSourceId(), game)) { //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/s/SkycatSovereign.java b/Mage.Sets/src/mage/cards/s/SkycatSovereign.java new file mode 100644 index 0000000000..9991c2032b --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkycatSovereign.java @@ -0,0 +1,67 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.permanent.token.CatBirdToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SkycatSovereign extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("other creature you control with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + filter.add(AnotherPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + + public SkycatSovereign(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.CAT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Skycat Sovereign gets +1/+1 for each other creature you control with flying. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect(xValue, xValue, Duration.WhileOnBattlefield))); + + // {2}{W}{U}: Create a 1/1 white Cat Bird creature token with flying. + this.addAbility(new SimpleActivatedAbility( + new CreateTokenEffect(new CatBirdToken()), new ManaCostsImpl("{2}{W}{U}") + )); + } + + private SkycatSovereign(final SkycatSovereign card) { + super(card); + } + + @Override + public SkycatSovereign copy() { + return new SkycatSovereign(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Skylasher.java b/Mage.Sets/src/mage/cards/s/Skylasher.java index af737ceda6..0d0f8c8df8 100644 --- a/Mage.Sets/src/mage/cards/s/Skylasher.java +++ b/Mage.Sets/src/mage/cards/s/Skylasher.java @@ -5,7 +5,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.ProtectionAbility; import mage.abilities.keyword.ReachAbility; @@ -30,7 +30,7 @@ public final class Skylasher extends CardImpl { // Flash this.addAbility(FlashAbility.getInstance()); // Skylasher can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Reach, protection from blue this.addAbility(ReachAbility.getInstance()); this.addAbility(ProtectionAbility.from(ObjectColor.BLUE)); diff --git a/Mage.Sets/src/mage/cards/s/SkymarchBloodletter.java b/Mage.Sets/src/mage/cards/s/SkymarchBloodletter.java index e7207e3f96..904f841ac9 100644 --- a/Mage.Sets/src/mage/cards/s/SkymarchBloodletter.java +++ b/Mage.Sets/src/mage/cards/s/SkymarchBloodletter.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -15,8 +13,9 @@ import mage.constants.SubType; import mage.target.Target; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SkymarchBloodletter extends CardImpl { @@ -34,7 +33,7 @@ public final class SkymarchBloodletter extends CardImpl { // When Skymarch Bloodletters enters the battlefield, target opponent loses 1 life and you gain 1 life. Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(1), false); - ability.addEffect(new GainLifeEffect(1).setText("and you gain 1 life")); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); Target target = new TargetOpponent(); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java b/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java index c7119a8183..1ab4b0e839 100644 --- a/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java +++ b/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java @@ -1,37 +1,38 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.SendOptionUsedEventEffect; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SkyriderPatrol extends CardImpl { + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent("another creature you control"); + + static { + filter.add(AnotherPredicate.instance); + } + public SkyriderPatrol(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); @@ -44,20 +45,19 @@ public final class SkyriderPatrol extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // At the beginning of combat on your turn, you may pay {G}{U}. When you do, put a +1/+1 counter on another target creature you control, and that creature gains flying until end of turn. - this.addAbility(new BeginningOfCombatTriggeredAbility( - new DoIfCostPaid( - new SkyriderPatrolCreateReflexiveTriggerEffect(), - new ManaCostsImpl("{G}{U}"), - "Pay {G}{U} to put a +1/+1 counter on another" - + " creature you control and give it flying?" - ).setText("you may pay {G}{U}. When you do, " - + "put a +1/+1 counter on another target creature you control, " - + "and that creature gains flying until end of turn."), - TargetController.YOU, false - )); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false, + "put a +1/+1 counter on another target creature you control, " + + "and that creature gains flying until end of turn" + ); + ability.addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn)); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(new BeginningOfCombatTriggeredAbility(new DoWhenCostPaid( + ability, new ManaCostsImpl("{G}{U}"), "Pay {G}{U}?" + ), TargetController.YOU, false)); } - public SkyriderPatrol(final SkyriderPatrol card) { + private SkyriderPatrol(final SkyriderPatrol card) { super(card); } @@ -66,71 +66,3 @@ public final class SkyriderPatrol extends CardImpl { return new SkyriderPatrol(this); } } - -class SkyriderPatrolCreateReflexiveTriggerEffect extends OneShotEffect { - - public SkyriderPatrolCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - public SkyriderPatrolCreateReflexiveTriggerEffect(final SkyriderPatrolCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public SkyriderPatrolCreateReflexiveTriggerEffect copy() { - return new SkyriderPatrolCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new SkyriderPatrolReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class SkyriderPatrolReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - private static final FilterControlledCreaturePermanent filter - = new FilterControlledCreaturePermanent("another creature you control"); - - static { - filter.add(AnotherPredicate.instance); - } - - public SkyriderPatrolReflexiveTriggeredAbility() { - super(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), Duration.OneUse, true); - this.addEffect(new GainAbilityTargetEffect( - FlyingAbility.getInstance(), - Duration.EndOfTurn - )); - this.addTarget(new TargetPermanent(filter)); - } - - public SkyriderPatrolReflexiveTriggeredAbility(final SkyriderPatrolReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public SkyriderPatrolReflexiveTriggeredAbility copy() { - return new SkyriderPatrolReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you pay {G}{U}, put a +1/+1 counter " - + "on another target creature you control, " - + "and that creature gains flying until end of turn."; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SkywaySniper.java b/Mage.Sets/src/mage/cards/s/SkywaySniper.java new file mode 100644 index 0000000000..362fae650a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkywaySniper.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SkywaySniper extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public SkywaySniper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.ARCHER); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // {2}{G}: Skyway Sniper deals 1 damage to target creature with flying. + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(1), new ManaCostsImpl("{2}{G}")); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private SkywaySniper(final SkywaySniper card) { + super(card); + } + + @Override + public SkywaySniper copy() { + return new SkywaySniper(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SlashingTiger.java b/Mage.Sets/src/mage/cards/s/SlashingTiger.java index 6f40799b4b..a0a963a28d 100644 --- a/Mage.Sets/src/mage/cards/s/SlashingTiger.java +++ b/Mage.Sets/src/mage/cards/s/SlashingTiger.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class SlashingTiger extends CardImpl { this.toughness = new MageInt(3); // Whenever Slashing Tiger becomes blocked, it gets +2/+2 until end of turn. - this.addAbility(new BecomesBlockedTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false)); } public SlashingTiger(final SlashingTiger card) { diff --git a/Mage.Sets/src/mage/cards/s/SlaughterTheStrong.java b/Mage.Sets/src/mage/cards/s/SlaughterTheStrong.java index b54b294cd1..c3832f3ceb 100644 --- a/Mage.Sets/src/mage/cards/s/SlaughterTheStrong.java +++ b/Mage.Sets/src/mage/cards/s/SlaughterTheStrong.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.abilities.Ability; @@ -70,7 +69,7 @@ class SlaughterTheStrongEffect extends OneShotEffect { if (player != null) { boolean selectionDone = false; Set selectedCreatures = new HashSet<>(); - while (selectionDone == false && player.isInGame()) { + while (player.canRespond() && selectionDone == false) { int powerSum = 0; for (UUID creatureId : selectedCreatures) { Permanent creature = game.getPermanent(creatureId); diff --git a/Mage.Sets/src/mage/cards/s/SlaughterhouseBouncer.java b/Mage.Sets/src/mage/cards/s/SlaughterhouseBouncer.java index b68d8d3a68..5a687fed34 100644 --- a/Mage.Sets/src/mage/cards/s/SlaughterhouseBouncer.java +++ b/Mage.Sets/src/mage/cards/s/SlaughterhouseBouncer.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.common.HellbentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -32,7 +32,7 @@ public final class SlaughterhouseBouncer extends CardImpl { // Hellbent - When Slaughterhouse Bouncer dies, if you have no cards in hand, target creature gets -3/-3 until end of turn. Ability ability = new ConditionalInterveningIfTriggeredAbility( - new DiesTriggeredAbility(new BoostTargetEffect(-3, -3, Duration.EndOfTurn)), + new DiesSourceTriggeredAbility(new BoostTargetEffect(-3, -3, Duration.EndOfTurn)), HellbentCondition.instance, "When {this} dies, if you have no cards in hand, target creature gets -3/-3 until end of turn." ); diff --git a/Mage.Sets/src/mage/cards/s/SleeperDart.java b/Mage.Sets/src/mage/cards/s/SleeperDart.java new file mode 100644 index 0000000000..0a0c3d2c5d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SleeperDart.java @@ -0,0 +1,45 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SleeperDart extends CardImpl { + + public SleeperDart(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + // When Sleeper Dart enters the battlefield, draw a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); + + // {T}, Sacrifice Sleeper Dart: Target creature doesn't untap during its controller's next untap step. + Ability ability = new SimpleActivatedAbility( + new DontUntapInControllersNextUntapStepTargetEffect(), new TapSourceCost() + ); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private SleeperDart(final SleeperDart card) { + super(card); + } + + @Override + public SleeperDart copy() { + return new SleeperDart(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SliceAndDice.java b/Mage.Sets/src/mage/cards/s/SliceAndDice.java index 4a45fd36a4..77865c847b 100644 --- a/Mage.Sets/src/mage/cards/s/SliceAndDice.java +++ b/Mage.Sets/src/mage/cards/s/SliceAndDice.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.common.CycleTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DamageAllEffect; @@ -9,25 +7,25 @@ import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SliceAndDice extends CardImpl { public SliceAndDice(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{R}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}"); // Slice and Dice deals 4 damage to each creature. this.getSpellAbility().addEffect(new DamageAllEffect(4, new FilterCreaturePermanent())); // Cycling {2}{R} this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}{R}"))); // When you cycle Slice and Dice, you may have it deal 1 damage to each creature. - this.addAbility(new CycleTriggeredAbility(new DamageAllEffect(1, new FilterCreaturePermanent()), true)); - + this.addAbility(new CycleTriggeredAbility(new DamageAllEffect(1, StaticFilters.FILTER_PERMANENT_CREATURE).setText("have it deal 1 damage to each creature"), true)); } public SliceAndDice(final SliceAndDice card) { diff --git a/Mage.Sets/src/mage/cards/s/SlingbowTrap.java b/Mage.Sets/src/mage/cards/s/SlingbowTrap.java index 87f3856419..8fcc5cd8a9 100644 --- a/Mage.Sets/src/mage/cards/s/SlingbowTrap.java +++ b/Mage.Sets/src/mage/cards/s/SlingbowTrap.java @@ -61,7 +61,7 @@ enum SlingbowTrapCondition implements Condition { for (UUID attackingCreatureId : game.getCombat().getAttackers()) { Permanent attackingCreature = game.getPermanent(attackingCreatureId); if (attackingCreature != null) { - if (attackingCreature.getColor(game).isBlack() && attackingCreature.hasAbility(FlyingAbility.getInstance().getId(), game)) { + if (attackingCreature.getColor(game).isBlack() && attackingCreature.hasAbility(FlyingAbility.getInstance(), game)) { return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SlinkingGiant.java b/Mage.Sets/src/mage/cards/s/SlinkingGiant.java index 3d83f4a134..07a3d03ff2 100644 --- a/Mage.Sets/src/mage/cards/s/SlinkingGiant.java +++ b/Mage.Sets/src/mage/cards/s/SlinkingGiant.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.WitherAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class SlinkingGiant extends CardImpl { this.addAbility(WitherAbility.getInstance()); // Whenever Slinking Giant blocks or becomes blocked, it gets -3/-0 until end of turn. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(-3, 0, Duration.EndOfTurn), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(-3, 0, Duration.EndOfTurn), false)); } public SlinkingGiant(final SlinkingGiant card) { diff --git a/Mage.Sets/src/mage/cards/s/SlipperyBogbonder.java b/Mage.Sets/src/mage/cards/s/SlipperyBogbonder.java new file mode 100644 index 0000000000..ccaf584530 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SlipperyBogbonder.java @@ -0,0 +1,142 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class SlipperyBogbonder extends CardImpl { + + public SlipperyBogbonder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Hexproof + this.addAbility(HexproofAbility.getInstance()); + + // When Slippery Bogbonder enters the battlefield, put a hexproof counter on target creature. Then move any number of counters from among creatures you control onto that creature. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.HEXPROOF.createInstance()) + ); + ability.addEffect(new SlipperyBogbonderEffect()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private SlipperyBogbonder(final SlipperyBogbonder card) { + super(card); + } + + @Override + public SlipperyBogbonder copy() { + return new SlipperyBogbonder(this); + } +} + +class SlipperyBogbonderEffect extends OneShotEffect { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("creatures to move counters from"); + + static { + filter.add(CounterAnyPredicate.instance); + } + + SlipperyBogbonderEffect() { + super(Outcome.Benefit); + staticText = "Then move any number of counters from among creatures you control onto that creature."; + } + + private SlipperyBogbonderEffect(final SlipperyBogbonderEffect effect) { + super(effect); + } + + @Override + public SlipperyBogbonderEffect copy() { + return new SlipperyBogbonderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent creature = game.getPermanent(source.getFirstTarget()); + FilterPermanent filterPermanent = filter.copy(); + filterPermanent.add(Predicates.not(new PermanentIdPredicate(source.getFirstTarget()))); + if (player == null || creature == null || game.getBattlefield().count( + filterPermanent, source.getSourceId(), source.getControllerId(), game + ) < 1) { + return false; + } + TargetPermanent target = new TargetPermanent(0, Integer.MAX_VALUE, filterPermanent, true); + player.choose(outcome, target, source.getSourceId(), game); + List permanents = target + .getTargets() + .stream() + .filter(uuid -> !creature.getId().equals(uuid)) + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (permanents.isEmpty()) { + return false; + } + Map> counterMap = new HashMap<>(); + for (Permanent permanent : permanents) { + permanent.getCounters(game) + .entrySet() + .stream() + .forEach(entry -> { + int num = player.getAmount( + 0, entry.getValue().getCount(), + "Choose how many " + entry.getKey() + + " counters to remove from " + permanent.getLogName(), game + ); + counterMap.computeIfAbsent( + permanent.getId(), x -> new HashMap<>() + ).put(entry.getKey(), num); + }); + } + for (Permanent permanent : permanents) { + counterMap + .get(permanent.getId()) + .entrySet() + .stream() + .filter(entry -> entry.getValue() > 0) + .map(entry -> CounterType.findByName(entry.getKey()).createInstance(entry.getValue())) + .filter(counter -> creature.addCounters(counter, source, game)) + .forEach(counter -> permanent.removeCounters(counter, game)); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SlithStrider.java b/Mage.Sets/src/mage/cards/s/SlithStrider.java index b1b9474e73..73845b1475 100644 --- a/Mage.Sets/src/mage/cards/s/SlithStrider.java +++ b/Mage.Sets/src/mage/cards/s/SlithStrider.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -25,7 +25,7 @@ public final class SlithStrider extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new BecomesBlockedTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), false)); } diff --git a/Mage.Sets/src/mage/cards/s/Slithermuse.java b/Mage.Sets/src/mage/cards/s/Slithermuse.java index b067606a0e..0f80ab9aaa 100644 --- a/Mage.Sets/src/mage/cards/s/Slithermuse.java +++ b/Mage.Sets/src/mage/cards/s/Slithermuse.java @@ -83,7 +83,7 @@ class SlithermuseEffect extends OneShotEffect { game.informPlayers(permanent.getName() + ": " + player.getLogName() + " has chosen " + chosenPlayer.getLogName()); int diff = chosenPlayer.getHand().size() - player.getHand().size(); if (diff > 0) { - player.drawCards(diff, game); + player.drawCards(diff, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/Slitherwisp.java b/Mage.Sets/src/mage/cards/s/Slitherwisp.java new file mode 100644 index 0000000000..5a7c972162 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Slitherwisp.java @@ -0,0 +1,60 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Slitherwisp extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("another spell that has flash"); + + static { + filter.add(new AbilityPredicate(FlashAbility.class)); + filter.add(AnotherPredicate.instance); + } + + public Slitherwisp(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{B}{B}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Whenever you cast another spell with flash, you draw a card and each opponent loses 1 life. + Ability ability = new SpellCastControllerTriggeredAbility( + new DrawCardSourceControllerEffect(1) + .setText("you draw a card"), + filter, false + ); + ability.addEffect(new LoseLifeOpponentsEffect(1).concatBy("and")); + this.addAbility(ability); + } + + private Slitherwisp(final Slitherwisp card) { + super(card); + } + + @Override + public Slitherwisp copy() { + return new Slitherwisp(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SmashToSmithereens.java b/Mage.Sets/src/mage/cards/s/SmashToSmithereens.java index f3bbcbcb02..c2315cbb1d 100644 --- a/Mage.Sets/src/mage/cards/s/SmashToSmithereens.java +++ b/Mage.Sets/src/mage/cards/s/SmashToSmithereens.java @@ -56,7 +56,7 @@ class SmashToSmithereensEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetArtifact = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + Permanent targetArtifact = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (targetArtifact != null) { Player controllerOfArtifact = game.getPlayer(targetArtifact.getControllerId()); targetArtifact.destroy(source.getSourceId(), game, false); diff --git a/Mage.Sets/src/mage/cards/s/SmolderingEfreet.java b/Mage.Sets/src/mage/cards/s/SmolderingEfreet.java index 56fed80827..642e602031 100644 --- a/Mage.Sets/src/mage/cards/s/SmolderingEfreet.java +++ b/Mage.Sets/src/mage/cards/s/SmolderingEfreet.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DamageControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class SmolderingEfreet extends CardImpl { this.toughness = new MageInt(2); // When Smoldering Efreet dies, it deals 2 damage to you. - this.addAbility(new DiesTriggeredAbility(new DamageControllerEffect(2, "it"), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DamageControllerEffect(2, "it"), false)); } public SmolderingEfreet(final SmolderingEfreet card) { diff --git a/Mage.Sets/src/mage/cards/s/SnapdaxApexOfTheHunt.java b/Mage.Sets/src/mage/cards/s/SnapdaxApexOfTheHunt.java new file mode 100644 index 0000000000..cdb4251418 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SnapdaxApexOfTheHunt.java @@ -0,0 +1,67 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SnapdaxApexOfTheHunt extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker an opponent controls"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public SnapdaxApexOfTheHunt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DINOSAUR); + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Mutate {2}{B/R}{W}{W} + this.addAbility(new MutateAbility(this, "{2}{B/R}{W}{W}")); + + // Double Strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // Whenever this creature mutates, it deals 4 damage to target creature or planeswalker an opponent controls and you gain 4 life. + Ability ability = new MutatesSourceTriggeredAbility( + new DamageTargetEffect(4, "it") + ); + ability.addEffect(new GainLifeEffect(4).concatBy("and")); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private SnapdaxApexOfTheHunt(final SnapdaxApexOfTheHunt card) { + super(card); + } + + @Override + public SnapdaxApexOfTheHunt copy() { + return new SnapdaxApexOfTheHunt(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SnapsailGlider.java b/Mage.Sets/src/mage/cards/s/SnapsailGlider.java index 7cc55dd5da..524b1fb2c8 100644 --- a/Mage.Sets/src/mage/cards/s/SnapsailGlider.java +++ b/Mage.Sets/src/mage/cards/s/SnapsailGlider.java @@ -1,40 +1,40 @@ - - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; + +import java.util.UUID; /** - * * @author Loki, nantuko */ public final class SnapsailGlider extends CardImpl { protected static String rule = "Metalcraft — Snapsail Glider has flying as long as you control three or more artifacts"; - public SnapsailGlider (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); + public SnapsailGlider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(2); this.toughness = new MageInt(2); + + // Metalcraft — Snapsail Glider has flying as long as you control three or more artifacts. ContinuousEffect effect = new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect, MetalcraftCondition.instance, rule))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect, MetalcraftCondition.instance, rule)) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance)); } - public SnapsailGlider (final SnapsailGlider card) { + public SnapsailGlider(final SnapsailGlider card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/s/SnareTactician.java b/Mage.Sets/src/mage/cards/s/SnareTactician.java new file mode 100644 index 0000000000..4524441a71 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SnareTactician.java @@ -0,0 +1,42 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SnareTactician extends CardImpl { + + public SnareTactician(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever you cycle a card, tap target creature an opponent controls. + Ability ability = new CycleControllerTriggeredAbility(new TapTargetEffect()); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private SnareTactician(final SnareTactician card) { + super(card); + } + + @Override + public SnareTactician copy() { + return new SnareTactician(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java b/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java index bc72bae2dd..8cce1e89f7 100644 --- a/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java +++ b/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -55,11 +54,13 @@ class SnickeringSquirrelEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); - - if (controller != null) { + Player dieRoller = game.getPlayer(event.getPlayerId()); + if (controller != null && dieRoller != null) { Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null && permanent.canTap() && !permanent.isTapped()) { - if (controller.chooseUse(Outcome.AIDontUseIt, "Do you want to tap this to increase the result of a die any player rolled by 1?", null, "Yes", "No", source, game)) { + if (permanent != null && !permanent.isTapped()) { + if (controller.chooseUse(Outcome.AIDontUseIt, "Do you want to tap this to increase the result of a die (" + + event.getAmount() + ") " + + dieRoller.getName() + " rolled by 1?", null, "Yes", "No", source, game)) { permanent.tap(game); // ignore planar dies (dice roll amount of planar dies is equal to 0) if (event.getAmount() > 0) { diff --git a/Mage.Sets/src/mage/cards/s/SnortingGahr.java b/Mage.Sets/src/mage/cards/s/SnortingGahr.java index 6a972e4a19..d830c18f83 100644 --- a/Mage.Sets/src/mage/cards/s/SnortingGahr.java +++ b/Mage.Sets/src/mage/cards/s/SnortingGahr.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class SnortingGahr extends CardImpl { // Whenever Snorting Gahr becomes blocked, it gets +2/+2 until end of turn. Effect effect = new BoostSourceEffect(2, 2, Duration.EndOfTurn); effect.setText("it gets +2/+2 until end of turn"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public SnortingGahr(final SnortingGahr card) { diff --git a/Mage.Sets/src/mage/cards/s/SnowDevil.java b/Mage.Sets/src/mage/cards/s/SnowDevil.java index de915a6c22..b5693ae570 100644 --- a/Mage.Sets/src/mage/cards/s/SnowDevil.java +++ b/Mage.Sets/src/mage/cards/s/SnowDevil.java @@ -1,97 +1,97 @@ -package mage.cards.s; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.abilities.keyword.FirstStrikeAbility; -import mage.abilities.keyword.FlyingAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; -import mage.game.Game; -import mage.game.combat.CombatGroup; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * - * @author jeffwadsworth - */ -public final class SnowDevil extends CardImpl { - - public SnowDevil(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // Enchanted creature has flying. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield))); - - // Enchanted creature has first strike as long as it's blocking and you control a snow land. - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - new ConditionalContinuousEffect( - new GainAbilityAttachedEffect( - FirstStrikeAbility.getInstance(), - AttachmentType.AURA, - Duration.WhileOnBattlefield), - SnowDevilCondition.instance, - "Enchanted creature has first strike as long as it's blocking and you control a snow land" - ))); - } - - private SnowDevil(final SnowDevil card) { - super(card); - } - - @Override - public SnowDevil copy() { - return new SnowDevil(this); - } -} - -enum SnowDevilCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - FilterLandPermanent filter = new FilterLandPermanent(); - filter.add(SuperType.SNOW.getPredicate()); - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null) { - Player controller = game.getPlayer(enchantment.getControllerId()); - Permanent creature = game.getPermanent(enchantment.getAttachedTo()); - if (creature != null - && controller != null) { - CombatGroup combatGroup = game.getCombat().findGroupOfBlocker(creature.getId()); - if (combatGroup != null - && !game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game).isEmpty()) { - return true; - } - } - } - return false; - } -} +package mage.cards.s; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.common.FilterLandPermanent; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author jeffwadsworth + */ +public final class SnowDevil extends CardImpl { + + public SnowDevil(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has flying. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield))); + + // Enchanted creature has first strike as long as it's blocking and you control a snow land. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new ConditionalContinuousEffect( + new GainAbilityAttachedEffect( + FirstStrikeAbility.getInstance(), + AttachmentType.AURA, + Duration.WhileOnBattlefield), + SnowDevilCondition.instance, + "Enchanted creature has first strike as long as it's blocking and you control a snow land" + ))); + } + + private SnowDevil(final SnowDevil card) { + super(card); + } + + @Override + public SnowDevil copy() { + return new SnowDevil(this); + } +} + +enum SnowDevilCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + FilterLandPermanent filter = new FilterLandPermanent(); + filter.add(SuperType.SNOW.getPredicate()); + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment != null) { + Player controller = game.getPlayer(enchantment.getControllerId()); + Permanent creature = game.getPermanent(enchantment.getAttachedTo()); + if (creature != null + && controller != null) { + CombatGroup combatGroup = game.getCombat().findGroupOfBlocker(creature.getId()); + if (combatGroup != null + && !game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game).isEmpty()) { + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SnowHound.java b/Mage.Sets/src/mage/cards/s/SnowHound.java index e671b45802..1b9e1212b5 100644 --- a/Mage.Sets/src/mage/cards/s/SnowHound.java +++ b/Mage.Sets/src/mage/cards/s/SnowHound.java @@ -35,7 +35,7 @@ public final class SnowHound extends CardImpl { public SnowHound(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/s/SoaringSeacliff.java b/Mage.Sets/src/mage/cards/s/SoaringSeacliff.java index 557065395f..32cd8f810f 100644 --- a/Mage.Sets/src/mage/cards/s/SoaringSeacliff.java +++ b/Mage.Sets/src/mage/cards/s/SoaringSeacliff.java @@ -23,10 +23,10 @@ public final class SoaringSeacliff extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.LAND},""); this.addAbility(new EntersBattlefieldTappedAbility()); - this.addAbility(new BlueManaAbility()); EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); + this.addAbility(new BlueManaAbility()); } public SoaringSeacliff(final SoaringSeacliff card) { diff --git a/Mage.Sets/src/mage/cards/s/SolRing.java b/Mage.Sets/src/mage/cards/s/SolRing.java index 9b836f10fa..55431f5ba1 100644 --- a/Mage.Sets/src/mage/cards/s/SolRing.java +++ b/Mage.Sets/src/mage/cards/s/SolRing.java @@ -18,6 +18,8 @@ public final class SolRing extends CardImpl { public SolRing(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + + // Tap: Add {C}{C} this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(2), new TapSourceCost())); } diff --git a/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java b/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java index 3800b3fac0..26aa39dd92 100644 --- a/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java +++ b/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java @@ -7,6 +7,8 @@ import mage.Mana; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.HighestCMCOfPermanentValue; import mage.abilities.dynamicvalue.common.SacrificeCostConvertedMana; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; @@ -38,8 +40,9 @@ public final class SoldeviAdnate extends CardImpl { this.toughness = new MageInt(2); // {T}, Sacrifice a black or artifact creature: Add an amount of {B} equal to the sacrificed creature's converted mana cost. - Ability ability = new DynamicManaAbility(Mana.BlackMana(1), new SacrificeCostConvertedMana("creature"), - "add an amount of {B} equal to the sacrificed creature's converted mana cost"); + Ability ability = new DynamicManaAbility(Mana.BlackMana(1), new SacrificeCostConvertedMana("creature"), new TapSourceCost(), + "add an amount of {B} equal to the sacrificed creature's converted mana cost" , false, + new HighestCMCOfPermanentValue(filter, true)); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SoldeviGolem.java b/Mage.Sets/src/mage/cards/s/SoldeviGolem.java index 5e9205c53e..11fe9dd024 100644 --- a/Mage.Sets/src/mage/cards/s/SoldeviGolem.java +++ b/Mage.Sets/src/mage/cards/s/SoldeviGolem.java @@ -43,7 +43,7 @@ public final class SoldeviGolem extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepSourceEffect())); // At the beginning of your upkeep, you may untap target tapped creature an opponent controls. If you do, untap Soldevi Golem. - Ability ability = new BeginningOfUpkeepTriggeredAbility(new UntapTargetEffect().setText("untap target tapped creature an opponent controls."), TargetController.YOU, true); + Ability ability = new BeginningOfUpkeepTriggeredAbility(new UntapTargetEffect().setText("untap target tapped creature an opponent controls"), TargetController.YOU, true); ability.addEffect(new UntapSourceEffect().setText("If you do, untap {this}")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SoldeviSentry.java b/Mage.Sets/src/mage/cards/s/SoldeviSentry.java index 14d2fdb476..e0de16347c 100644 --- a/Mage.Sets/src/mage/cards/s/SoldeviSentry.java +++ b/Mage.Sets/src/mage/cards/s/SoldeviSentry.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -17,14 +15,15 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author Ketsuban */ public final class SoldeviSentry extends CardImpl { public SoldeviSentry(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[] { CardType.ARTIFACT, CardType.CREATURE }, "{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}"); this.subtype.add(SubType.SOLDIER); this.power = new MageInt(1); this.toughness = new MageInt(1); @@ -53,10 +52,10 @@ class SoldeviSentryEffect extends RegenerateSourceEffect { //20110204 - 701.11 Player opponent = game.getPlayer(source.getFirstTarget()); Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null && permanent.regenerate(this.getId(), game)) { + if (permanent != null && permanent.regenerate(source, game)) { if (opponent != null) { if (opponent.chooseUse(Outcome.DrawCard, "Draw a card?", source, game)) { - opponent.drawCards(1, game); + opponent.drawCards(1, source.getSourceId(), game); } } this.used = true; diff --git a/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java b/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java index e80c292346..2783e4d2e3 100644 --- a/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java +++ b/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java @@ -2,7 +2,7 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; @@ -26,7 +26,7 @@ public final class SolemnSimulacrum extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true), true)); - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); } public SolemnSimulacrum(final SolemnSimulacrum card) { diff --git a/Mage.Sets/src/mage/cards/s/Solfatara.java b/Mage.Sets/src/mage/cards/s/Solfatara.java index e7ad74be02..e7354209e5 100644 --- a/Mage.Sets/src/mage/cards/s/Solfatara.java +++ b/Mage.Sets/src/mage/cards/s/Solfatara.java @@ -49,7 +49,7 @@ class SolfataraEffect extends ContinuousRuleModifyingEffectImpl { public SolfataraEffect() { super(Duration.EndOfTurn, Outcome.Detriment); - staticText = "Target player can't play land cards this turn."; + staticText = "Target player can't play land cards this turn"; } public SolfataraEffect(final SolfataraEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SolidFooting.java b/Mage.Sets/src/mage/cards/s/SolidFooting.java new file mode 100644 index 0000000000..348bd4ae26 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SolidFooting.java @@ -0,0 +1,103 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SolidFooting extends CardImpl { + + public SolidFooting(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.subtype.add(SubType.AURA); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(1, 1))); + + // As long as enchanted creature has vigilance, it assigns combat damage equal to its toughness rather than its power. + this.addAbility(new SimpleStaticAbility(new GauntletsOfLightEffect())); + } + + private SolidFooting(final SolidFooting card) { + super(card); + } + + @Override + public SolidFooting copy() { + return new SolidFooting(this); + } +} + +class GauntletsOfLightEffect extends ContinuousEffectImpl { + + GauntletsOfLightEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "As long as enchanted creature has vigilance, " + + "it assigns combat damage equal to its toughness rather than its power"; + } + + private GauntletsOfLightEffect(final GauntletsOfLightEffect effect) { + super(effect); + } + + @Override + public GauntletsOfLightEffect copy() { + return new GauntletsOfLightEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null || permanent.getAttachedTo() == null) { + return false; + } + Permanent attachedTo = game.getPermanent(permanent.getAttachedTo()); + if (attachedTo == null || !attachedTo.getAbilities().containsKey(VigilanceAbility.getInstance().getId())) { + return false; + } + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + filter.add(new PermanentIdPredicate(permanent.getAttachedTo())); + game.getCombat().setUseToughnessForDamage(true); + game.getCombat().addUseToughnessForDamageFilter(filter); + return true; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.RulesEffects; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SolidarityOfHeroes.java b/Mage.Sets/src/mage/cards/s/SolidarityOfHeroes.java index f7538e6766..67cd8b93ce 100644 --- a/Mage.Sets/src/mage/cards/s/SolidarityOfHeroes.java +++ b/Mage.Sets/src/mage/cards/s/SolidarityOfHeroes.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.abilityword.StriveAbility; import mage.abilities.effects.OneShotEffect; @@ -15,22 +13,22 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SolidarityOfHeroes extends CardImpl { public SolidarityOfHeroes(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Strive - Solidarity of Heroes costs {1}{G} more to cast for each target beyond the first. this.addAbility(new StriveAbility("{1}{G}")); + // Choose any number of target creatures. Double the number of +1/+1 counters on each of them. this.getSpellAbility().addEffect(new SolidarityOfHeroesEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE)); - } public SolidarityOfHeroes(final SolidarityOfHeroes card) { @@ -63,7 +61,7 @@ class SolidarityOfHeroesEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - for (UUID targetId: getTargetPointer().getTargets(game, source)) { + for (UUID targetId : getTargetPointer().getTargets(game, source)) { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { int existingCounters = permanent.getCounters(game).getCount(CounterType.P1P1); diff --git a/Mage.Sets/src/mage/cards/s/SomberwaldStag.java b/Mage.Sets/src/mage/cards/s/SomberwaldStag.java index 5ed035681a..4bf30c5e7c 100644 --- a/Mage.Sets/src/mage/cards/s/SomberwaldStag.java +++ b/Mage.Sets/src/mage/cards/s/SomberwaldStag.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -11,37 +9,31 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SomberwaldStag extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public SomberwaldStag(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); this.subtype.add(SubType.ELK); this.power = new MageInt(4); this.toughness = new MageInt(3); // When Somberwald Stag enters the battlefield, you may have it fight target creature you don't control. Effect effect = new FightTargetSourceEffect(); - effect.setText("you may have it fight target creature you don't control"); + effect.setText("have it fight target creature you don't control"); Ability ability = new EntersBattlefieldTriggeredAbility(effect, true); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); } - public SomberwaldStag(final SomberwaldStag card) { + private SomberwaldStag(final SomberwaldStag card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/s/Somnophore.java b/Mage.Sets/src/mage/cards/s/Somnophore.java index 33a3b13a17..208db0abfb 100644 --- a/Mage.Sets/src/mage/cards/s/Somnophore.java +++ b/Mage.Sets/src/mage/cards/s/Somnophore.java @@ -6,6 +6,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.condition.common.SourceOnBattlefieldCondition; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; import mage.abilities.effects.ContinuousRuleModifyingEffect; import mage.abilities.effects.Effect; @@ -44,7 +45,7 @@ public final class Somnophore extends CardImpl { // Whenever Somnophore deals damage to a player, tap target creature that player controls. That creature doesn't untap during its controller's untap step for as long as Somnophore remains on the battlefield. ContinuousRuleModifyingEffect skipUntapEffect = new DontUntapInControllersUntapStepTargetEffect(Duration.WhileOnBattlefield); skipUntapEffect.setText("That creature doesn't untap during its controller's untap step for as long as {this} remains on the battlefield"); - ConditionalContinuousRuleModifyingEffect effect = new ConditionalContinuousRuleModifyingEffect(skipUntapEffect, SourceOnBattlefieldCondition.instance); + ConditionalContinuousRuleModifyingEffect effect = new ConditionalContinuousRuleModifyingEffect(skipUntapEffect, new SourceRemainsInZoneCondition(Zone.BATTLEFIELD)); Ability ability = new SomnophoreTriggeredAbility(new TapTargetEffect()); ability.addTarget(new TargetCreaturePermanent()); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/s/SongOfBlood.java b/Mage.Sets/src/mage/cards/s/SongOfBlood.java index d786f476dd..9838614994 100644 --- a/Mage.Sets/src/mage/cards/s/SongOfBlood.java +++ b/Mage.Sets/src/mage/cards/s/SongOfBlood.java @@ -1,22 +1,17 @@ package mage.cards.s; -import java.util.Set; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -24,8 +19,10 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import java.util.Objects; +import java.util.UUID; + /** - * * @author spjspj */ public final class SongOfBlood extends CardImpl { @@ -38,7 +35,7 @@ public final class SongOfBlood extends CardImpl { this.getSpellAbility().addEffect(new SongOfBloodEffect()); } - public SongOfBlood(final SongOfBlood card) { + private SongOfBlood(final SongOfBlood card) { super(card); } @@ -50,13 +47,14 @@ public final class SongOfBlood extends CardImpl { class SongOfBloodEffect extends OneShotEffect { - public SongOfBloodEffect() { + SongOfBloodEffect() { super(Outcome.LoseLife); - this.staticText = "Put the top four cards of your library into your graveyard."; + this.staticText = "Mill four cards. Whenever a creature attacks this turn, " + + "it gets +1/+0 until end of turn for each creature card put into your graveyard this way."; } - public SongOfBloodEffect(final SongOfBloodEffect effect) { + private SongOfBloodEffect(final SongOfBloodEffect effect) { super(effect); } @@ -68,36 +66,35 @@ class SongOfBloodEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Cards cardsToGraveyard = new CardsImpl(); - cardsToGraveyard.addAll(controller.getLibrary().getTopCards(game, 4)); - if (!cardsToGraveyard.isEmpty()) { - Set movedCards = controller.moveCardsToGraveyardWithInfo(cardsToGraveyard.getCards(game), source, game, Zone.LIBRARY); - Cards cardsMoved = new CardsImpl(); - cardsMoved.addAll(movedCards); - int creatures = cardsMoved.count(StaticFilters.FILTER_CARD_CREATURE, game); - if (creatures > 0) { - // Setup a delayed trigger to give +X/+0 to any creature attacking this turn.. - DelayedTriggeredAbility delayedAbility = new SongOfBloodTriggeredAbility(creatures); - game.addDelayedTriggeredAbility(delayedAbility, source); - } - } - return true; + if (controller == null) { + return false; } - return false; + int creatures = controller + .millCards(4, source, game) + .getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> game.getState().getZone(card.getId()) == Zone.GRAVEYARD) + .filter(MageObject::isCreature) + .mapToInt(x -> 1) + .sum(); + // Setup a delayed trigger to give +X/+0 to any creature attacking this turn.. + DelayedTriggeredAbility delayedAbility = new SongOfBloodTriggeredAbility(creatures); + game.addDelayedTriggeredAbility(delayedAbility, source); + return true; } } class SongOfBloodTriggeredAbility extends DelayedTriggeredAbility { - int booster; + private final int booster; - public SongOfBloodTriggeredAbility(int booster) { + SongOfBloodTriggeredAbility(int booster) { super(new BoostTargetEffect(booster, 0, Duration.EndOfTurn), Duration.EndOfTurn, false); this.booster = booster; } - public SongOfBloodTriggeredAbility(SongOfBloodTriggeredAbility ability) { + private SongOfBloodTriggeredAbility(SongOfBloodTriggeredAbility ability) { super(ability); this.booster = ability.booster; } @@ -126,6 +123,6 @@ class SongOfBloodTriggeredAbility extends DelayedTriggeredAbility { @Override public String getRule() { - return "Whenever a creature attacks this turn, it gets +1/+0 (+" + booster + "/0) until end of turn for each creature card put into your graveyard this way."; + return "Whenever a creature attacks this turn, it gets +" + booster + "/0 until end of turn."; } } diff --git a/Mage.Sets/src/mage/cards/s/SongOfCreation.java b/Mage.Sets/src/mage/cards/s/SongOfCreation.java new file mode 100644 index 0000000000..6b50c97e95 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SongOfCreation.java @@ -0,0 +1,49 @@ +package mage.cards.s; + +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.PlayAdditionalLandsControllerEffect; +import mage.abilities.effects.common.discard.DiscardHandControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SongOfCreation extends CardImpl { + + public SongOfCreation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}{U}{R}"); + + // You may play an additional land on each of your turns. + this.addAbility(new SimpleStaticAbility( + new PlayAdditionalLandsControllerEffect(1, Duration.WhileOnBattlefield) + )); + + // Whenever you cast a spell, draw two cards. + this.addAbility(new SpellCastControllerTriggeredAbility( + new DrawCardSourceControllerEffect(2), false + )); + + // At the beginning of your end step, discard your hand. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new DiscardHandControllerEffect(), TargetController.YOU, false + )); + } + + private SongOfCreation(final SongOfCreation card) { + super(card); + } + + @Override + public SongOfCreation copy() { + return new SongOfCreation(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java b/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java index ce6b7187a3..8c9b34ca57 100644 --- a/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java +++ b/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -11,11 +10,12 @@ import mage.abilities.mana.GreenManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -28,13 +28,13 @@ import mage.target.TargetPermanent; public final class SongOfTheDryads extends CardImpl { public SongOfTheDryads(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); this.subtype.add(SubType.AURA); // Enchant permanent TargetPermanent auraTarget = new TargetPermanent(); this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.Benefit)); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); @@ -58,6 +58,7 @@ class BecomesColorlessForestLandEffect extends ContinuousEffectImpl { public BecomesColorlessForestLandEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); this.staticText = "Enchanted permanent is a colorless Forest land"; + dependencyTypes.add(DependencyType.BecomeForest); } public BecomesColorlessForestLandEffect(final BecomesColorlessForestLandEffect effect) { @@ -89,10 +90,10 @@ class BecomesColorlessForestLandEffect extends ContinuousEffectImpl { permanent.getColor(game).setRed(false); break; case AbilityAddingRemovingEffects_6: - permanent.removeAllAbilities(source.getSourceId(), game); permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); break; case TypeChangingEffects_4: + permanent.removeAllAbilities(source.getSourceId(), game); permanent.getCardType().clear(); permanent.addCardType(CardType.LAND); permanent.getSubtype(game).clear(); @@ -107,6 +108,8 @@ class BecomesColorlessForestLandEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4; + return layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.ColorChangingEffects_5 + || layer == Layer.TypeChangingEffects_4; } } diff --git a/Mage.Sets/src/mage/cards/s/SonorousHowlbonder.java b/Mage.Sets/src/mage/cards/s/SonorousHowlbonder.java new file mode 100644 index 0000000000..c0c7bd752e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SonorousHowlbonder.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByOneAllEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.DependencyType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SonorousHowlbonder extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creature you control with menace"); + + static { + filter.add(new AbilityPredicate(MenaceAbility.class)); + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public SonorousHowlbonder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B/R}{B/R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Menace + this.addAbility(new MenaceAbility()); + + // Each creature you control with menace can't be blocked except by three or more creatures. + ContinuousEffect effect = new CantBeBlockedByOneAllEffect(3, filter); + effect.setDependedToType(DependencyType.AddingAbility); + this.addAbility(new SimpleStaticAbility(effect)); + } + + private SonorousHowlbonder(final SonorousHowlbonder card) { + super(card); + } + + @Override + public SonorousHowlbonder copy() { + return new SonorousHowlbonder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java b/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java index 1e1735f9a2..3ec72f473f 100644 --- a/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java +++ b/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.Optional; @@ -11,18 +10,14 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.ChooseACardNameEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; import mage.target.common.TargetOpponent; +import mage.util.CardUtil; /** - * * @author TheElk801 */ public final class SorcerousSpyglass extends CardImpl { @@ -73,7 +68,9 @@ class SorcerousSpyglassEntersEffect extends ChooseACardNameEffect { if (opponent != null) { MageObject sourceObject = game.getObject(source.getSourceId()); player.lookAtCards(sourceObject != null ? sourceObject.getIdName() : null, opponent.getHand(), game); - player.chooseUse(Outcome.Benefit, "Press ok to name a card", "You won't be able to resize the window once you do", "Ok", " ", source, game); + player.chooseUse(Outcome.Benefit, "Press Ok to name a card", + "You won't be able to resize the window showing opponents hand once you do", + "Ok", "", source, game); } } } @@ -110,13 +107,12 @@ class SorcerousSpyglassActivationEffect extends ContinuousRuleModifyingEffectImp @Override public boolean applies(GameEvent event, Ability source, Game game) { MageObject object = game.getObject(event.getSourceId()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); Optional ability = game.getAbility(event.getTargetId(), event.getSourceId()); if (ability.isPresent() && object != null) { - if (game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) // controller in range + return game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) // controller in range && ability.get().getAbilityType() != AbilityType.MANA - && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) { - return true; - } + && CardUtil.haveSameNames(object, cardName, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java b/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java index 038ddf9ead..8c720fcb43 100644 --- a/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java +++ b/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java @@ -1,12 +1,15 @@ package mage.cards.s; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.*; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.LifelinkAbility; @@ -21,7 +24,6 @@ import mage.filter.FilterCard; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreatureCard; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetControlledCreaturePermanent; @@ -58,11 +60,16 @@ public final class SorinImperiousBloodlord extends CardImpl { this.addAbility(ability); // +1: You may sacrifice a Vampire. When you do, Sorin, Imperious Bloodlord deals 3 damage to any target and you gain 3 life. - this.addAbility(new LoyaltyAbility(new DoIfCostPaid( - new SorinImperiousBloodlordCreateReflexiveTriggerEffect(), - new SacrificeTargetCost(new TargetControlledPermanent(filter)) - ).setText("You may sacrifice a Vampire. " + - "When you do, {this} deals 3 damage to any target and you gain 3 life." + ReflexiveTriggeredAbility triggeredAbility = new ReflexiveTriggeredAbility( + new DamageTargetEffect(3), false, + "{this} deals 3 damage to any target and you gain 3 life" + ); + triggeredAbility.addEffect(new GainLifeEffect(3)); + triggeredAbility.addTarget(new TargetAnyTarget()); + this.addAbility(new LoyaltyAbility(new DoWhenCostPaid( + triggeredAbility, + new SacrificeTargetCost(new TargetControlledPermanent(filter)), + "Sacrifice a Vampire?" ), 1)); // −3: You may put a Vampire creature card from your hand onto the battlefield. @@ -110,58 +117,3 @@ class SorinImperiousBloodlordEffect extends OneShotEffect { return true; } } - -class SorinImperiousBloodlordCreateReflexiveTriggerEffect extends OneShotEffect { - - SorinImperiousBloodlordCreateReflexiveTriggerEffect() { - super(Benefit); - } - - private SorinImperiousBloodlordCreateReflexiveTriggerEffect(final SorinImperiousBloodlordCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public SorinImperiousBloodlordCreateReflexiveTriggerEffect copy() { - return new SorinImperiousBloodlordCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new SorinImperiousBloodlordReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class SorinImperiousBloodlordReflexiveTriggeredAbility extends DelayedTriggeredAbility { - SorinImperiousBloodlordReflexiveTriggeredAbility() { - super(new DamageTargetEffect(3), Duration.OneUse, true); - this.addEffect(new GainLifeEffect(3)); - this.addTarget(new TargetAnyTarget()); - } - - private SorinImperiousBloodlordReflexiveTriggeredAbility(final SorinImperiousBloodlordReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public SorinImperiousBloodlordReflexiveTriggeredAbility copy() { - return new SorinImperiousBloodlordReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "{this} deals 3 damage to any target and you gain 3 life"; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java b/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java index 7a89e3f455..d65929f660 100644 --- a/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java +++ b/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java @@ -101,7 +101,7 @@ class SorinLordOfInnistradEffect extends OneShotEffect { } } } - game.applyEffects(); + game.getState().processAction(game); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { return controller.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game); diff --git a/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java b/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java index 3d94bbcb41..8d6261aaa6 100644 --- a/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java +++ b/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java @@ -47,7 +47,7 @@ public final class SorinVengefulBloodlord extends CardImpl { LifelinkAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_OR_PLANESWALKER_A ), MyTurnCondition.instance, "As long as it's your turn, " + - "creatures and planeswalkers you control have lifelink." + "creatures and planeswalkers you control have lifelink" )).addHint(MyTurnHint.instance)); // +2: Sorin, Vengeful Bloodlord deals 1 damage to target player or planeswalker. @@ -57,7 +57,7 @@ public final class SorinVengefulBloodlord extends CardImpl { // -X: Return target creature card with converted mana cost X from your graveyard to the battlefield. That creature is a vampire in addition to its other types. ability = new LoyaltyAbility(new ReturnFromGraveyardToBattlefieldTargetEffect().setText( - "Return target creature card with converted mana cost X from your graveyard to the battlefield." + "Return target creature card with converted mana cost X from your graveyard to the battlefield" )); ability.addEffect(new SorinVengefulBloodlordEffect()); ability.setTargetAdjuster(SorinVengefulBloodlordAdjuster.instance); @@ -95,7 +95,7 @@ enum SorinVengefulBloodlordAdjuster implements TargetAdjuster { class SorinVengefulBloodlordEffect extends ContinuousEffectImpl { SorinVengefulBloodlordEffect() { super(Duration.Custom, Outcome.Neutral); - staticText = "That creature is a vampire in addition to its other types."; + staticText = "That creature is a vampire in addition to its other types"; } private SorinVengefulBloodlordEffect(final SorinVengefulBloodlordEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SorrowsPath.java b/Mage.Sets/src/mage/cards/s/SorrowsPath.java index ee972bc336..f352c2bc23 100644 --- a/Mage.Sets/src/mage/cards/s/SorrowsPath.java +++ b/Mage.Sets/src/mage/cards/s/SorrowsPath.java @@ -1,10 +1,6 @@ - package mage.cards.s; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.BecomesTappedSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -16,25 +12,28 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterOpponentsCreaturePermanent; import mage.filter.predicate.permanent.BlockingPredicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanentSameController; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + /** - * * @author L_J */ public final class SorrowsPath extends CardImpl { - + private static final FilterOpponentsCreaturePermanent filter = new FilterOpponentsCreaturePermanent("blocking creatures an opponent controls"); - + static { filter.add(BlockingPredicate.instance); } @@ -43,17 +42,19 @@ public final class SorrowsPath extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {T}: Choose two target blocking creatures an opponent controls. If each of those creatures could block all creatures that the other is blocking, remove both of them from combat. Each one then blocks all creatures the other was blocking. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SorrowsPathSwitchBlockersEffect(), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new SorrowsPathSwitchBlockersEffect(), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanentSameController(2, 2, filter, false)); this.addAbility(ability); - + // Whenever Sorrow's Path becomes tapped, it deals 2 damage to you and each creature you control. Ability ability2 = new BecomesTappedSourceTriggeredAbility(new DamageControllerEffect(2)); - ability2.addEffect(new DamageAllEffect(2, new FilterControlledCreaturePermanent()).setText("and each creature you control")); + ability2.addEffect(new DamageAllEffect( + 2, StaticFilters.FILTER_CONTROLLED_CREATURE + ).setText("and each creature you control")); this.addAbility(ability2); } - public SorrowsPath(final SorrowsPath card) { + private SorrowsPath(final SorrowsPath card) { super(card); } @@ -66,12 +67,14 @@ public final class SorrowsPath extends CardImpl { class SorrowsPathSwitchBlockersEffect extends OneShotEffect { - public SorrowsPathSwitchBlockersEffect() { + SorrowsPathSwitchBlockersEffect() { super(Outcome.Detriment); - this.staticText = "Choose two target blocking creatures an opponent controls. If each of those creatures could block all creatures that the other is blocking, remove both of them from combat. Each one then blocks all creatures the other was blocking"; + this.staticText = "Choose two target blocking creatures an opponent controls. " + + "If each of those creatures could block all creatures that the other is blocking, " + + "remove both of them from combat. Each one then blocks all creatures the other was blocking"; } - public SorrowsPathSwitchBlockersEffect(final SorrowsPathSwitchBlockersEffect effect) { + private SorrowsPathSwitchBlockersEffect(final SorrowsPathSwitchBlockersEffect effect) { super(effect); } @@ -97,14 +100,14 @@ class SorrowsPathSwitchBlockersEffect extends OneShotEffect { Set attackers1 = new HashSet<>(); Set attackers2 = new HashSet<>(); boolean blockPossible = false; - + if (chosenGroup1 != null && chosenGroup2 != null) { blockPossible = getRestrictions(chosenGroup1, blocker2, attackers1, sourcePermanent, controller, game) && getRestrictions(chosenGroup2, blocker1, attackers2, sourcePermanent, controller, game); } if (!blockPossible) { return true; } - + // 10/1/2009: When the first ability resolves, if all the creatures that one of the targeted creatures was blocking have left combat, then the other targeted creature // is considered to be able to block all creatures the first creature is blocking. If the ability has its full effect, the second creature will be removed from combat // but not returned to combat; it doesn't block anything. @@ -112,13 +115,29 @@ class SorrowsPathSwitchBlockersEffect extends OneShotEffect { game.getCombat().removeFromCombat(blocker2.getId(), game, false); blocker1.setRemovedFromCombat(attackers2.isEmpty()); blocker2.setRemovedFromCombat(attackers1.isEmpty()); - + // 10/1/2009: Abilities that trigger whenever one of the targeted creatures blocks will trigger when the first ability resolves, because those creatures will change from // not blocking (since they're removed from combat) to blocking. It doesn't matter if those abilities triggered when those creatures blocked the first time. Abilities // that trigger whenever one of the attacking creatures becomes blocked will not trigger again, because they never stopped being blocked creatures. Abilities that // trigger whenever a creature blocks one of the attacking creatures will trigger again, though; those kinds of abilities trigger once for each creature that blocks. reassignBlocker(blocker1, attackers2, game); reassignBlocker(blocker2, attackers1, game); + Set morSet = new HashSet<>(); + attackers1 + .stream() + .map(permanent -> new MageObjectReference(permanent, game)) + .forEach(morSet::add); + attackers2 + .stream() + .map(permanent -> new MageObjectReference(permanent, game)) + .forEach(morSet::add); + String key = UUID.randomUUID().toString(); + game.getState().setValue("becameBlocked_" + key, morSet); + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.BATCH_BLOCK_NONCOMBAT, + source.getSourceId(), source.getSourceId(), + source.getControllerId(), key, 0) + ); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SoulCollector.java b/Mage.Sets/src/mage/cards/s/SoulCollector.java index ea7e94ddbd..4c303fd371 100644 --- a/Mage.Sets/src/mage/cards/s/SoulCollector.java +++ b/Mage.Sets/src/mage/cards/s/SoulCollector.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.DealtDamageAndDiedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -13,22 +11,25 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author markedagain */ public final class SoulCollector extends CardImpl { public SoulCollector(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.VAMPIRE); this.power = new MageInt(3); this.toughness = new MageInt(4); // Flying this.addAbility(FlyingAbility.getInstance()); + // Whenever a creature dealt damage by Soul Collector this turn dies, return that card to the battlefield under your control. - this.addAbility(new DealtDamageAndDiedTriggeredAbility(new ReturnToBattlefieldUnderYourControlTargetEffect(false))); + this.addAbility(new DealtDamageAndDiedTriggeredAbility(new ReturnToBattlefieldUnderYourControlTargetEffect())); + // Morph {B}{B}{B} this.addAbility(new MorphAbility(this, new ManaCostsImpl("{B}{B}{B}"))); } diff --git a/Mage.Sets/src/mage/cards/s/SoulOfRavnica.java b/Mage.Sets/src/mage/cards/s/SoulOfRavnica.java index 431b8a329c..e0ae6486e0 100644 --- a/Mage.Sets/src/mage/cards/s/SoulOfRavnica.java +++ b/Mage.Sets/src/mage/cards/s/SoulOfRavnica.java @@ -2,25 +2,18 @@ package mage.cards.s; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.ExileSourceFromGraveCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardForEachColorAmongControlledPermanentsEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; /** @@ -38,11 +31,12 @@ public final class SoulOfRavnica extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // {5}{U}{U}: Draw a card for each color among permanents you control. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SoulOfRavnicaEffect(), new ManaCostsImpl("{5}{U}{U}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardForEachColorAmongControlledPermanentsEffect(), new ManaCostsImpl("{5}{U}{U}"))); // {5}{U}{U}, Exile Soul of Ravnica from your graveyard: Draw a card for each color among permanents you control. - Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new SoulOfRavnicaEffect(), new ManaCostsImpl("{5}{U}{U}")); + Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new DrawCardForEachColorAmongControlledPermanentsEffect(), new ManaCostsImpl("{5}{U}{U}")); ability.addCost(new ExileSourceFromGraveCost()); this.addAbility(ability); } @@ -57,47 +51,3 @@ public final class SoulOfRavnica extends CardImpl { } } -class SoulOfRavnicaEffect extends OneShotEffect { - - public SoulOfRavnicaEffect() { - super(Outcome.DrawCard); - this.staticText = "Draw a card for each color among permanents you control"; - } - - public SoulOfRavnicaEffect(final SoulOfRavnicaEffect effect) { - super(effect); - } - - @Override - public SoulOfRavnicaEffect copy() { - return new SoulOfRavnicaEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Set colors = new HashSet<>(); - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) { - if (permanent.getColor(game).isBlack()) { - colors.add(ObjectColor.BLACK); - } - if (permanent.getColor(game).isBlue()) { - colors.add(ObjectColor.BLUE); - } - if (permanent.getColor(game).isRed()) { - colors.add(ObjectColor.RED); - } - if (permanent.getColor(game).isGreen()) { - colors.add(ObjectColor.GREEN); - } - if (permanent.getColor(game).isWhite()) { - colors.add(ObjectColor.WHITE); - } - } - controller.drawCards(colors.size(), game); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SoulRansom.java b/Mage.Sets/src/mage/cards/s/SoulRansom.java index bab5f1d07b..d227a097d6 100644 --- a/Mage.Sets/src/mage/cards/s/SoulRansom.java +++ b/Mage.Sets/src/mage/cards/s/SoulRansom.java @@ -94,7 +94,7 @@ class SoulRansomEffect extends OneShotEffect { if (controller == null) { return false; } - controller.drawCards(2, game); + controller.drawCards(2, source.getSourceId(), game); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SoulReap.java b/Mage.Sets/src/mage/cards/s/SoulReap.java index ba1fb190a2..4520aec737 100644 --- a/Mage.Sets/src/mage/cards/s/SoulReap.java +++ b/Mage.Sets/src/mage/cards/s/SoulReap.java @@ -127,7 +127,7 @@ class SoulReapEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent creature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (creature != null) { Player controller = game.getPlayer(creature.getControllerId()); if (controller != null) { diff --git a/Mage.Sets/src/mage/cards/s/SoulSculptor.java b/Mage.Sets/src/mage/cards/s/SoulSculptor.java index f82ccd16f1..710fe7cc5c 100644 --- a/Mage.Sets/src/mage/cards/s/SoulSculptor.java +++ b/Mage.Sets/src/mage/cards/s/SoulSculptor.java @@ -1,149 +1,149 @@ -package mage.cards.s; - -import java.util.UUID; -import mage.MageInt; -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.Condition; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import static mage.constants.Layer.TypeChangingEffects_4; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; -import mage.target.common.TargetCreaturePermanent; - -/** - * - * @author jeffwadsworth - */ -public final class SoulSculptor extends CardImpl { - - final String rule = "Target creature becomes an enchantment and loses all abilities until a player casts a creature spell."; - - public SoulSculptor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); - - this.subtype.add(SubType.HUMAN); - this.power = new MageInt(1); - this.toughness = new MageInt(1); - - // {1}{W}, {tap}: Target creature becomes an enchantment and loses all abilities until a player casts a creature spell. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new SoulSculptorEffect(), SoulSculptorCondition.instance, rule), new ManaCostsImpl("{1}{W}")); - ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent()); - this.addAbility(ability); - - } - - public SoulSculptor(final SoulSculptor card) { - super(card); - } - - @Override - public SoulSculptor copy() { - return new SoulSculptor(this); - } -} - -class SoulSculptorEffect extends ContinuousEffectImpl { - - public SoulSculptorEffect() { - super(Duration.Custom, Outcome.LoseAbility); - staticText = "target becomes an Enchantment and loses all abilites"; - dependencyTypes.add(DependencyType.EnchantmentAddingRemoving); - dependencyTypes.add(DependencyType.AddingAbility); - - } - - public SoulSculptorEffect(final SoulSculptorEffect effect) { - super(effect); - } - - @Override - public SoulSculptorEffect copy() { - return new SoulSculptorEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - Permanent targetPermanent = game.getPermanent(source.getFirstTarget()); - if (targetPermanent != null) { - affectedObjectList.add(new MageObjectReference(targetPermanent, game)); - } - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent permanent = affectedObjectList.get(0).getPermanent(game); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getCardType().clear(); - permanent.getSubtype(game).clear(); - if (!permanent.getCardType().contains(CardType.ENCHANTMENT)) { - permanent.getCardType().add(CardType.ENCHANTMENT); - } - } - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - permanent.getAbilities().clear(); - } - break; - } - return true; - } - this.discard(); - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return Layer.TypeChangingEffects_4 == layer - || Layer.AbilityAddingRemovingEffects_6 == layer; - } - -} - -enum SoulSculptorCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - if (!game.getStack().isEmpty()) { - StackObject stackObject = game.getStack().getFirst(); - if (stackObject != null) { - return !stackObject.getCardType().contains(CardType.CREATURE); - } - } - return true; - } - - @Override - public String toString() { - return "creature spell cast"; - } - -} +package mage.cards.s; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.DependencyType; +import mage.constants.Duration; +import mage.constants.Layer; +import static mage.constants.Layer.TypeChangingEffects_4; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author jeffwadsworth + */ +public final class SoulSculptor extends CardImpl { + + final String rule = "Target creature becomes an enchantment and loses all abilities until a player casts a creature spell."; + + public SoulSculptor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {1}{W}, {tap}: Target creature becomes an enchantment and loses all abilities until a player casts a creature spell. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new SoulSculptorEffect(), SoulSculptorCondition.instance, rule), new ManaCostsImpl("{1}{W}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + } + + public SoulSculptor(final SoulSculptor card) { + super(card); + } + + @Override + public SoulSculptor copy() { + return new SoulSculptor(this); + } +} + +class SoulSculptorEffect extends ContinuousEffectImpl { + + public SoulSculptorEffect() { + super(Duration.Custom, Outcome.LoseAbility); + staticText = "target becomes an Enchantment and loses all abilites"; + dependencyTypes.add(DependencyType.EnchantmentAddingRemoving); + dependencyTypes.add(DependencyType.AddingAbility); + + } + + public SoulSculptorEffect(final SoulSculptorEffect effect) { + super(effect); + } + + @Override + public SoulSculptorEffect copy() { + return new SoulSculptorEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + Permanent targetPermanent = game.getPermanent(source.getFirstTarget()); + if (targetPermanent != null) { + affectedObjectList.add(new MageObjectReference(targetPermanent, game)); + } + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = affectedObjectList.get(0).getPermanent(game); + if (permanent != null) { + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == SubLayer.NA) { + permanent.getCardType().clear(); + permanent.getSubtype(game).clear(); + if (!permanent.getCardType().contains(CardType.ENCHANTMENT)) { + permanent.getCardType().add(CardType.ENCHANTMENT); + } + } + break; + case AbilityAddingRemovingEffects_6: + if (sublayer == SubLayer.NA) { + permanent.getAbilities().clear(); + } + break; + } + return true; + } + this.discard(); + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return Layer.TypeChangingEffects_4 == layer + || Layer.AbilityAddingRemovingEffects_6 == layer; + } + +} + +enum SoulSculptorCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + if (!game.getStack().isEmpty()) { + StackObject stackObject = game.getStack().getFirst(); + if (stackObject != null) { + return !stackObject.getCardType().contains(CardType.CREATURE); + } + } + return true; + } + + @Override + public String toString() { + return "creature spell cast"; + } + +} diff --git a/Mage.Sets/src/mage/cards/s/SoulSear.java b/Mage.Sets/src/mage/cards/s/SoulSear.java new file mode 100644 index 0000000000..af843a5000 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SoulSear.java @@ -0,0 +1,38 @@ +package mage.cards.s; + +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.LoseAbilityTargetEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SoulSear extends CardImpl { + + public SoulSear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn. + this.getSpellAbility().addEffect(new DamageTargetEffect(5)); + this.getSpellAbility().addEffect(new LoseAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setText("That permanent loses indestructible until end of turn")); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private SoulSear(final SoulSear card) { + super(card); + } + + @Override + public SoulSear copy() { + return new SoulSear(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SoulSeparator.java b/Mage.Sets/src/mage/cards/s/SoulSeparator.java index d45530e37f..9691ba8772 100644 --- a/Mage.Sets/src/mage/cards/s/SoulSeparator.java +++ b/Mage.Sets/src/mage/cards/s/SoulSeparator.java @@ -33,10 +33,16 @@ public final class SoulSeparator extends CardImpl { // {5}, {T}, Sacrifice Soul Separator: Exile target creature card from your graveyard. // Create a token that's a copy of that card except it's 1/1, it's a Spirit in addition to its other types, and it has flying. - // Create a black Zombie creature token with power equal to that card's power and toughness equal that card's toughness. + // Put a black Zombie creature token onto the battlefield with power equal to that card's power and toughness equal that card's toughness. + + // 20200601 - 701.6c + // Previously, an effect that created tokens instructed a player to “put [those tokens] onto the battlefield.” + // Cards that were printed with that text have received errata in the Oracle card reference so they now + // “create” those tokens. + CreateTokenCopyTargetEffect copyEffect = new CreateTokenCopyTargetEffect(null, null, false, 1, false, false, null, 1, 1, true); copyEffect.setAdditionalSubType(SubType.SPIRIT); - copyEffect.setText("Create a token that's a copy of that card except it's 1/1, it's a Spirit in addition to its other types, and it has flying."); + copyEffect.setText("Create a token that's a copy of that card except it's 1/1, it's a Spirit in addition to its other types, and it has flying"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, copyEffect, new ManaCostsImpl("{5}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); diff --git a/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java b/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java index d26f0a5fdd..95f3198e03 100644 --- a/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java +++ b/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java @@ -1,48 +1,51 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.common.AbilityResolutionCountHint; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SubType; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.AbilityResolvedWatcher; + +import java.util.UUID; /** - * - * @author LevelX2 + * @author TheElk801 */ public final class SoulbrightFlamekin extends CardImpl { public SoulbrightFlamekin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.ELEMENTAL); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(2); this.toughness = new MageInt(1); // {2}: Target creature gains trample until end of turn. If this is the third time this ability has resolved this turn, you may add {R}{R}{R}{R}{R}{R}{R}{R}. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{2}")); - ability.addEffect(new SoulbrightFlamekinEffect()); + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl("{2}")); + ability.addEffect(new IfAbilityHasResolvedXTimesEffect(Outcome.PutManaInPool, 3, new SoulbrightFlamekinEffect())); ability.addTarget(new TargetCreaturePermanent()); - this.addAbility(ability); + ability.addHint(AbilityResolutionCountHint.instance); + this.addAbility(ability, new AbilityResolvedWatcher()); } - public SoulbrightFlamekin(final SoulbrightFlamekin card) { + private SoulbrightFlamekin(final SoulbrightFlamekin card) { super(card); } @@ -54,19 +57,13 @@ public final class SoulbrightFlamekin extends CardImpl { class SoulbrightFlamekinEffect extends OneShotEffect { - static class ActivationInfo { - public int zoneChangeCounter; - public int turn; - public int activations; - } - - public SoulbrightFlamekinEffect() { + SoulbrightFlamekinEffect() { super(Outcome.Damage); - this.staticText = "If this is the third time this ability has resolved this turn, you may add {R}{R}{R}{R}{R}{R}{R}{R}"; + this.staticText = "you may add {R}{R}{R}{R}{R}{R}{R}{R}"; } - public SoulbrightFlamekinEffect(final SoulbrightFlamekinEffect effect) { - super(effect); + private SoulbrightFlamekinEffect(final SoulbrightFlamekinEffect effect) { + super(effect); } @Override @@ -77,27 +74,8 @@ class SoulbrightFlamekinEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - ActivationInfo info; - Object object = game.getState().getValue(source.getSourceId() + "ActivationInfo"); - if (object instanceof ActivationInfo) { - info = (ActivationInfo) object; - if (info.turn != game.getTurnNum() || sourcePermanent.getZoneChangeCounter(game) != info.zoneChangeCounter) { - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - info.activations = 0; - } - } else { - info = new ActivationInfo(); - info.turn = game.getTurnNum(); - info.zoneChangeCounter = sourcePermanent.getZoneChangeCounter(game); - game.getState().setValue(source.getSourceId() + "ActivationInfo", info); - } - info.activations++; - if (info.activations == 3) { - controller.getManaPool().addMana(Mana.RedMana(8), game, source); - } + if (controller != null && controller.chooseUse(Outcome.PutManaInPool, "Add {R}{R}{R}{R}{R}{R}{R}{R}?", source, game)) { + controller.getManaPool().addMana(Mana.RedMana(8), game, source); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/s/SoulcageFiend.java b/Mage.Sets/src/mage/cards/s/SoulcageFiend.java index 669c4cacf3..71c179f86d 100644 --- a/Mage.Sets/src/mage/cards/s/SoulcageFiend.java +++ b/Mage.Sets/src/mage/cards/s/SoulcageFiend.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeAllPlayersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class SoulcageFiend extends CardImpl { this.toughness = new MageInt(2); // When Soulcage Fiend dies, each player loses 3 life. - this.addAbility(new DiesTriggeredAbility(new LoseLifeAllPlayersEffect(3))); + this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeAllPlayersEffect(3))); } public SoulcageFiend(final SoulcageFiend card) { diff --git a/Mage.Sets/src/mage/cards/s/Soulflayer.java b/Mage.Sets/src/mage/cards/s/Soulflayer.java index bd31006349..5e5b152ab1 100644 --- a/Mage.Sets/src/mage/cards/s/Soulflayer.java +++ b/Mage.Sets/src/mage/cards/s/Soulflayer.java @@ -1,8 +1,5 @@ package mage.cards.s; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; @@ -19,6 +16,10 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** * @author LevelX2 */ @@ -106,7 +107,7 @@ class SoulflayerEffect extends ContinuousEffectImpl implements SourceEffect { if (cardAbility instanceof HasteAbility) { abilitiesToAdd.add(HasteAbility.getInstance()); } - if (cardAbility instanceof HexproofAbility) { + if (cardAbility instanceof HexproofBaseAbility) { abilitiesToAdd.add(HexproofAbility.getInstance()); } if (cardAbility instanceof IndestructibleAbility) { diff --git a/Mage.Sets/src/mage/cards/s/Soulherder.java b/Mage.Sets/src/mage/cards/s/Soulherder.java index 00549d649d..aabf3f9418 100644 --- a/Mage.Sets/src/mage/cards/s/Soulherder.java +++ b/Mage.Sets/src/mage/cards/s/Soulherder.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; @@ -23,6 +22,8 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** * @author TheElk801 */ @@ -49,7 +50,7 @@ public final class Soulherder extends CardImpl { Ability ability = new BeginningOfEndStepTriggeredAbility( new ExileTargetForSourceEffect(), TargetController.YOU, true ); - ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, true).concatBy("then")); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false).concatBy("then")); ability.addTarget(new TargetControlledCreaturePermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SoulsMajesty.java b/Mage.Sets/src/mage/cards/s/SoulsMajesty.java index fef36bc2c1..19d4172741 100644 --- a/Mage.Sets/src/mage/cards/s/SoulsMajesty.java +++ b/Mage.Sets/src/mage/cards/s/SoulsMajesty.java @@ -52,7 +52,7 @@ public final class SoulsMajesty extends CardImpl { Permanent target = game.getPermanent(source.getFirstTarget()); Player player = game.getPlayer(source.getControllerId()); if (player != null && target != null) { - player.drawCards(target.getPower().getValue(), game); + player.drawCards(target.getPower().getValue(), source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/s/Soulshriek.java b/Mage.Sets/src/mage/cards/s/Soulshriek.java index 413ef752ce..648ac0b149 100644 --- a/Mage.Sets/src/mage/cards/s/Soulshriek.java +++ b/Mage.Sets/src/mage/cards/s/Soulshriek.java @@ -1,82 +1,81 @@ -package mage.cards.s; - -import java.util.UUID; - -import mage.abilities.Ability; -import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.SacrificeTargetEffect; -import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.targetpointer.FixedTarget; - -/* - * - * @author Ketsuban - */ -public class Soulshriek extends CardImpl { - - protected static final FilterCard filterCard = new FilterCard("creature cards"); - - static { - filterCard.add(CardType.CREATURE.getPredicate()); - } - - public Soulshriek(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}"); - - // Target creature you control gets +X/+0 until end of turn, where X is the number of creature cards in your graveyard. Sacrifice that creature at the beginning of the next end step. - this.getSpellAbility().addEffect(new SoulshriekEffect()); - this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - } - - @Override - public Card copy() { - return null; - } -} - -class SoulshriekEffect extends OneShotEffect { - - public SoulshriekEffect() { - super(Outcome.DestroyPermanent); - this.staticText = "Target creature you control gets +X/+0 until end of turn, where X is the number of creature cards in your graveyard. Sacrifice that creature at the beginning of the next end step"; - } - - public SoulshriekEffect(final SoulshriekEffect effect) { - super(effect); - } - - @Override - public SoulshriekEffect copy() { - return new SoulshriekEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - ContinuousEffect boost = new BoostTargetEffect(new CardsInControllerGraveyardCount(Soulshriek.filterCard), StaticValue.get(0), Duration.EndOfTurn); - boost.setTargetPointer(new FixedTarget(permanent, game)); - game.addEffect(boost, source); - Effect sacrifice = new SacrificeTargetEffect("Sacrifice that creature at the beginning of the next end step", source.getControllerId()); - sacrifice.setTargetPointer(new FixedTarget(permanent, game)); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrifice), source); - return true; - } - return false; - } -} +package mage.cards.s; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/* + * + * @author Ketsuban + */ +public class Soulshriek extends CardImpl { + + public Soulshriek(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Target creature you control gets +X/+0 until end of turn, where X is the number + // of creature cards in your graveyard. Sacrifice that creature at the beginning of the next end step. + CardsInControllerGraveyardCount count = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD); + Effect effect = new BoostTargetEffect(count, StaticValue.get(0), Duration.EndOfTurn, true); + effect.setText("Target creature you control gets +X/+0 until end of turn, where X is the number of creature cards in your graveyard."); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new SoulshriekEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + } + + public Soulshriek(final Soulshriek card) { + super(card); + } + + @Override + public Soulshriek copy() { + return new Soulshriek(this); + } +} + +class SoulshriekEffect extends OneShotEffect { + + public SoulshriekEffect() { + super(Outcome.Detriment); + this.staticText = "Sacrifice that creature at the beginning of the next end step"; + } + + public SoulshriekEffect(final SoulshriekEffect effect) { + super(effect); + } + + @Override + public SoulshriekEffect copy() { + return new SoulshriekEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + Effect sacrifice = new SacrificeTargetEffect("Sacrifice that creature " + + "at the beginning of the next end step", source.getControllerId()); + sacrifice.setTargetPointer(new FixedTarget(permanent, game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrifice), source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/Soulstinger.java b/Mage.Sets/src/mage/cards/s/Soulstinger.java index 955da0d7cd..56204981e3 100644 --- a/Mage.Sets/src/mage/cards/s/Soulstinger.java +++ b/Mage.Sets/src/mage/cards/s/Soulstinger.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -43,7 +43,7 @@ public final class Soulstinger extends CardImpl { new CountersSourceCount(CounterType.M1M1), Outcome.Detriment); effect.setText("you may put a -1/-1 counter on target creature for each -1/-1 counter on {this}"); - ability = new DiesTriggeredAbility(effect, true); + ability = new DiesSourceTriggeredAbility(effect, true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SouvenirSnatcher.java b/Mage.Sets/src/mage/cards/s/SouvenirSnatcher.java new file mode 100644 index 0000000000..b9f6347ec9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SouvenirSnatcher.java @@ -0,0 +1,59 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SouvenirSnatcher extends CardImpl { + + private static final FilterPermanent filter = new FilterArtifactPermanent("noncreature artifact"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public SouvenirSnatcher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.BIRD); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Mutate {5}{U} + this.addAbility(new MutateAbility(this, "{5}{U}")); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever this creature mutates, gain control of target noncreature artifact. + Ability ability = new MutatesSourceTriggeredAbility(new GainControlTargetEffect(Duration.Custom)); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private SouvenirSnatcher(final SouvenirSnatcher card) { + super(card); + } + + @Override + public SouvenirSnatcher copy() { + return new SouvenirSnatcher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SowerOfTemptation.java b/Mage.Sets/src/mage/cards/s/SowerOfTemptation.java index 3607841e7b..9800a91148 100644 --- a/Mage.Sets/src/mage/cards/s/SowerOfTemptation.java +++ b/Mage.Sets/src/mage/cards/s/SowerOfTemptation.java @@ -6,6 +6,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.SourceOnBattlefieldCondition; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; @@ -16,6 +17,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.target.common.TargetCreaturePermanent; @@ -78,7 +80,7 @@ class SowerOfTemptationGainControlEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { ConditionalContinuousEffect effect = new ConditionalContinuousEffect( new GainControlTargetEffect(Duration.Custom), - SourceOnBattlefieldCondition.instance, + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), "gain control of target creature for as long as {this} remains on the battlefield"); game.addEffect(effect, source); return false; diff --git a/Mage.Sets/src/mage/cards/s/SparkhunterMasticore.java b/Mage.Sets/src/mage/cards/s/SparkhunterMasticore.java new file mode 100644 index 0000000000..46afd30c0e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SparkhunterMasticore.java @@ -0,0 +1,65 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterObject; +import mage.target.common.TargetPlaneswalkerPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SparkhunterMasticore extends CardImpl { + + private static final FilterObject filter = new FilterObject("planeswalkers"); + + static { + filter.add(CardType.PLANESWALKER.getPredicate()); + } + + public SparkhunterMasticore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.MASTICORE); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // As an additional cost to cast this spell, discard a card. + this.getSpellAbility().addCost(new DiscardCardCost()); + + // Protection from planeswalkers + this.addAbility(new ProtectionAbility(filter)); + + // {1}: Sparkhunter Masticore deals 1 damage to target planeswalker. + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(1), new GenericManaCost(1)); + ability.addTarget(new TargetPlaneswalkerPermanent()); + this.addAbility(ability); + + // {3}: Sparkhunter Masticore gains indestructible until end of turn. + this.addAbility(new SimpleActivatedAbility(new GainAbilitySourceEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ), new GenericManaCost(3))); + } + + private SparkhunterMasticore(final SparkhunterMasticore card) { + super(card); + } + + @Override + public SparkhunterMasticore copy() { + return new SparkhunterMasticore(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SparktongueDragon.java b/Mage.Sets/src/mage/cards/s/SparktongueDragon.java index ae8dae4410..5396b970d1 100644 --- a/Mage.Sets/src/mage/cards/s/SparktongueDragon.java +++ b/Mage.Sets/src/mage/cards/s/SparktongueDragon.java @@ -1,28 +1,21 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.SendOptionUsedEventEffect; -import mage.constants.SubType; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; +import mage.constants.SubType; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SparktongueDragon extends CardImpl { @@ -38,16 +31,18 @@ public final class SparktongueDragon extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Sparktongue Dragon enters the battlefield, you may pay {2}{R}. When you do, it deals 3 damage to any target. - this.addAbility(new EntersBattlefieldTriggeredAbility( - new DoIfCostPaid( - new SparktongueDragonCreateReflexiveTriggerEffect(), - new ManaCostsImpl("{2}{R}"), - "Pay {2}{R} to deal 3 damage?" - ).setText("you may pay {2}{R}. When you do, it deals 3 damage to any target") - )); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(3), false, + "it deals 3 damage to any target" + ); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DoWhenCostPaid( + ability, new ManaCostsImpl("{2}{R}"), + "Pay {2}{R} to deal 3 damage?" + ))); } - public SparktongueDragon(final SparktongueDragon card) { + private SparktongueDragon(final SparktongueDragon card) { super(card); } @@ -56,59 +51,3 @@ public final class SparktongueDragon extends CardImpl { return new SparktongueDragon(this); } } - -class SparktongueDragonCreateReflexiveTriggerEffect extends OneShotEffect { - - public SparktongueDragonCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - this.staticText = "When you do, it deals 3 damage to any target"; - } - - public SparktongueDragonCreateReflexiveTriggerEffect(final SparktongueDragonCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public SparktongueDragonCreateReflexiveTriggerEffect copy() { - return new SparktongueDragonCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new SparktongueDragonReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class SparktongueDragonReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - public SparktongueDragonReflexiveTriggeredAbility() { - super(new DamageTargetEffect(3), Duration.OneUse, true); - this.addTarget(new TargetAnyTarget()); - } - - public SparktongueDragonReflexiveTriggeredAbility(final SparktongueDragonReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public SparktongueDragonReflexiveTriggeredAbility copy() { - return new SparktongueDragonReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you pay {2}{R}, {this} deals 3 damage to any target"; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SparringConstruct.java b/Mage.Sets/src/mage/cards/s/SparringConstruct.java index fd9626d107..18022974cd 100644 --- a/Mage.Sets/src/mage/cards/s/SparringConstruct.java +++ b/Mage.Sets/src/mage/cards/s/SparringConstruct.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.constants.SubType; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class SparringConstruct extends CardImpl { this.toughness = new MageInt(1); // When Sparring Construct dies, put a +1/+1 counter on target creature you control. - Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false); + Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SparringGolem.java b/Mage.Sets/src/mage/cards/s/SparringGolem.java index 8bf7eb23cb..191eb03673 100644 --- a/Mage.Sets/src/mage/cards/s/SparringGolem.java +++ b/Mage.Sets/src/mage/cards/s/SparringGolem.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.dynamicvalue.common.BlockedCreatureCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -29,7 +29,7 @@ public final class SparringGolem extends CardImpl { BlockedCreatureCount value = new BlockedCreatureCount(); Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true); effect.setText("it gets +1/+1 until end of turn for each creature blocking it"); - this.addAbility(new BecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); } public SparringGolem(final SparringGolem card) { diff --git a/Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java b/Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java new file mode 100644 index 0000000000..b269c73a90 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java @@ -0,0 +1,79 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.token.AngelToken; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpeakerOfTheHeavens extends CardImpl { + + public SpeakerOfTheHeavens(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // {T}: Create a 4/4 white Angel creature token with flying. Activate this ability only if you have at least 7 more life than your starting life total and only any time you could cast a sorcery. + this.addAbility(new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new CreateTokenEffect(new AngelToken()), + new TapSourceCost(), SpeakerOfTheHeavensCondition.instance + )); + } + + private SpeakerOfTheHeavens(final SpeakerOfTheHeavens card) { + super(card); + } + + @Override + public SpeakerOfTheHeavens copy() { + return new SpeakerOfTheHeavens(this); + } +} + +enum SpeakerOfTheHeavensCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + if (!MainPhaseStackEmptyCondition.instance.apply(game, source) + || !game.isActivePlayer(source.getControllerId())) { + return false; + } + Player player = game.getPlayer(source.getControllerId()); + if (player == null || player.getLife() < game.getLife() + 7) { + return false; + } + return true; + } + + @Override + public String toString() { + return "if you have at least 7 more life than your starting life total and only any time you could cast a sorcery"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SpeciesSpecialist.java b/Mage.Sets/src/mage/cards/s/SpeciesSpecialist.java new file mode 100644 index 0000000000..3d75b308d8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpeciesSpecialist.java @@ -0,0 +1,55 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.ChooseCreatureTypeEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ChosenSubtypePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpeciesSpecialist extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("a creature of the chosen type"); + + static { + filter.add(ChosenSubtypePredicate.instance); + } + + public SpeciesSpecialist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // As Species Specialist enters the battlefield, choose a creature type. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.DrawCard))); + + // Whenever a creature of the chosen type dies, you may draw a card. + this.addAbility(new DiesCreatureTriggeredAbility( + new DrawCardSourceControllerEffect(1), true, filter + )); + } + + private SpeciesSpecialist(final SpeciesSpecialist card) { + super(card); + } + + @Override + public SpeciesSpecialist copy() { + return new SpeciesSpecialist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpectralReserves.java b/Mage.Sets/src/mage/cards/s/SpectralReserves.java index feb8019187..22caf3fa9c 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralReserves.java +++ b/Mage.Sets/src/mage/cards/s/SpectralReserves.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -10,17 +8,18 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SpectralReserves extends CardImpl { public SpectralReserves(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}"); // Create two 1/1 white Spirit creature tokens with flying. You gain 2 life. - Effect effect = new CreateTokenEffect(new SpiritWhiteToken("EMN"), 2); + Effect effect = new CreateTokenEffect(new SpiritWhiteToken(), 2); effect.setText("Create two 1/1 white Spirit creature tokens with flying"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new GainLifeEffect(2)); diff --git a/Mage.Sets/src/mage/cards/s/SpectralSliver.java b/Mage.Sets/src/mage/cards/s/SpectralSliver.java index a6e474e2ca..d18c9d7d0f 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralSliver.java +++ b/Mage.Sets/src/mage/cards/s/SpectralSliver.java @@ -32,7 +32,7 @@ public final class SpectralSliver extends CardImpl { this.toughness = new MageInt(2); // All Sliver creatures have "{2}: This creature gets +1/+1 until end of turn." - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{2}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn).setText("this creature gets +1/+1 until end of turn"), new ManaCostsImpl("{2}")); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); } diff --git a/Mage.Sets/src/mage/cards/s/SpellboundDragon.java b/Mage.Sets/src/mage/cards/s/SpellboundDragon.java index e7f03140a9..5083039fee 100644 --- a/Mage.Sets/src/mage/cards/s/SpellboundDragon.java +++ b/Mage.Sets/src/mage/cards/s/SpellboundDragon.java @@ -71,7 +71,7 @@ class SpellboundDragonEffect extends OneShotEffect { Player you = game.getPlayer(source.getControllerId()); Permanent dragon = game.getPermanent(source.getSourceId()); if(you != null) { - you.drawCards(1, game); + you.drawCards(1, source.getSourceId(), game); TargetDiscard target = new TargetDiscard(you.getId()); you.choose(Outcome.Discard, target, source.getSourceId(), game); Card card = you.getHand().get(target.getFirstTarget(), game); diff --git a/Mage.Sets/src/mage/cards/s/SpelleaterWolverine.java b/Mage.Sets/src/mage/cards/s/SpelleaterWolverine.java new file mode 100644 index 0000000000..452b913676 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpelleaterWolverine.java @@ -0,0 +1,50 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.CardsInControllerGraveCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpelleaterWolverine extends CardImpl { + + private static final Condition condition = new CardsInControllerGraveCondition( + 3, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + ); + + public SpelleaterWolverine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.WOLVERINE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Spelleater Wolverine has double strike as long as there are three or more instant and/or sorcery cards in your graveyard. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance()), + condition, "{this} has double strike as long as there are " + + "three or more instant and/or sorcery cards in your graveyard" + ))); + } + + private SpelleaterWolverine(final SpelleaterWolverine card) { + super(card); + } + + @Override + public SpelleaterWolverine copy() { + return new SpelleaterWolverine(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Spelljack.java b/Mage.Sets/src/mage/cards/s/Spelljack.java index f966eb2091..7ea471aee7 100644 --- a/Mage.Sets/src/mage/cards/s/Spelljack.java +++ b/Mage.Sets/src/mage/cards/s/Spelljack.java @@ -6,6 +6,7 @@ import mage.abilities.Ability; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -13,6 +14,7 @@ import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; import mage.constants.ZoneDetail; import mage.game.Game; @@ -71,7 +73,7 @@ class SpelljackEffect extends OneShotEffect { if (stackObject != null && game.getStack().counter(targetId, source.getSourceId(), game, Zone.EXILED, false, ZoneDetail.NONE)) { Card card = ((Spell) stackObject).getCard(); if (card != null) { - ContinuousEffect effect = new SpelljackCastFromExileEffect(); + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.Custom, true); effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId()))); game.addEffect(effect, source); } @@ -81,50 +83,3 @@ class SpelljackEffect extends OneShotEffect { return false; } } - -class SpelljackCastFromExileEffect extends AsThoughEffectImpl { - - SpelljackCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - staticText = "You may cast that card without paying its mana cost as long as it remains exiled"; - } - - SpelljackCastFromExileEffect(final SpelljackCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public SpelljackCastFromExileEffect copy() { - return new SpelljackCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - if (getTargetPointer().getFirst(game, source) == null) { - this.discard(); - return false; - } - if (sourceId.equals(getTargetPointer().getFirst(game, source))) { - Card card = game.getCard(sourceId); - if (card != null) { - if (game.getState().getZone(sourceId) == Zone.EXILED) { - Player player = game.getPlayer(affectedControllerId); - if(player != null) { - player.setCastSourceIdWithAlternateMana(sourceId, null, null); - } - return true; - } else { - this.discard(); - } - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SpellpyrePhoenix.java b/Mage.Sets/src/mage/cards/s/SpellpyrePhoenix.java new file mode 100644 index 0000000000..91ab06ca19 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpellpyrePhoenix.java @@ -0,0 +1,136 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetCardInYourGraveyard; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpellpyrePhoenix extends CardImpl { + + private static final FilterCard filter + = new FilterInstantOrSorceryCard("instant or sorcery card with a cycling ability from your graveyard"); + + static { + filter.add(new AbilityPredicate(CyclingAbility.class)); + } + + public SpellpyrePhoenix(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + + this.subtype.add(SubType.PHOENIX); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Spellpyre Phoenix enters the battlefield, you may return target instant or sorcery card with a cycling ability from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility( + new ReturnFromGraveyardToHandTargetEffect(), true + ); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + // At the beginning of each end step, if you cycled two or more cards this turn, return Spellpyre Phoenix from your graveyard to your hand. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), + TargetController.ANY, SpellpyrePhoenixCondition.instance, false + ).addHint(SpellpyrePhoenixHint.instance), new SpellpyrePhoenixWatcher()); + } + + private SpellpyrePhoenix(final SpellpyrePhoenix card) { + super(card); + } + + @Override + public SpellpyrePhoenix copy() { + return new SpellpyrePhoenix(this); + } +} + +enum SpellpyrePhoenixCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + SpellpyrePhoenixWatcher watcher = game.getState().getWatcher(SpellpyrePhoenixWatcher.class); + return watcher != null && watcher.checkCycleCount(source.getControllerId()); + } + + @Override + public String toString() { + return "if you cycled two or more cards this turn"; + } +} + +enum SpellpyrePhoenixHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + SpellpyrePhoenixWatcher watcher = game.getState().getWatcher(SpellpyrePhoenixWatcher.class); + int count = 0; + if (watcher != null) { + count = watcher.getCycleCount(ability.getControllerId()); + } + return "Cards cycled this turn: " + count; + } + + @Override + public SpellpyrePhoenixHint copy() { + return instance; + } +} + +class SpellpyrePhoenixWatcher extends Watcher { + + private final Map cycleMap = new HashMap(); + + SpellpyrePhoenixWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.CYCLED_CARD) { + cycleMap.merge(event.getPlayerId(), 1, Integer::sum); + } + } + + @Override + public void reset() { + super.reset(); + cycleMap.clear(); + } + + boolean checkCycleCount(UUID playerId) { + return cycleMap.getOrDefault(playerId, 0) >= 2; + } + + int getCycleCount(UUID playerId) { + return cycleMap.getOrDefault(playerId, 0); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java b/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java index 25c5b0f25f..5eeed09130 100644 --- a/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java +++ b/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; @@ -23,13 +22,14 @@ import mage.target.common.TargetCardInASingleGraveyard; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author emerald000 */ public final class SpellweaverHelix extends CardImpl { - private static final FilterCard filter = new FilterCard("sorcery cards from a single graveyard"); + private static final FilterCard filter = new FilterCard("sorcery cards"); static { filter.add(CardType.SORCERY.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/s/SpellweaverVolute.java b/Mage.Sets/src/mage/cards/s/SpellweaverVolute.java index 971f423f3c..7f6b4fd2d0 100644 --- a/Mage.Sets/src/mage/cards/s/SpellweaverVolute.java +++ b/Mage.Sets/src/mage/cards/s/SpellweaverVolute.java @@ -90,7 +90,7 @@ class SpellweaverVoluteEffect extends OneShotEffect { && controller.chooseUse(Outcome.Copy, "Create a copy of " + enchantedCard.getName() + '?', source, game)) { Card copiedCard = game.copyCard(enchantedCard, source, source.getControllerId()); if (copiedCard != null) { - ownerEnchanted.getGraveyard().add(copiedCard); + controller.getGraveyard().add(copiedCard); game.getState().setZone(copiedCard.getId(), Zone.GRAVEYARD); if (controller.chooseUse(Outcome.PlayForFree, "Cast the copied card without paying mana cost?", source, game)) { if (copiedCard.getSpellAbility() != null) { diff --git a/Mage.Sets/src/mage/cards/s/SpellwildOuphe.java b/Mage.Sets/src/mage/cards/s/SpellwildOuphe.java index ac7dbe287c..047ca52579 100644 --- a/Mage.Sets/src/mage/cards/s/SpellwildOuphe.java +++ b/Mage.Sets/src/mage/cards/s/SpellwildOuphe.java @@ -1,22 +1,19 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.target.Target; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; + +import java.util.UUID; /** - * * @author LevelX2 & L_J */ public final class SpellwildOuphe extends CardImpl { @@ -27,8 +24,10 @@ public final class SpellwildOuphe extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(3); - // Spells that target Elderwood Scion cost {2} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellwildOupheCostReductionEffect())); + // Spells that target Spellwild Ouphe cost {2} less to cast. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(-2, new FilterCard("Spells"), TargetController.ANY)) + ); } public SpellwildOuphe(final SpellwildOuphe card) { @@ -39,48 +38,4 @@ public final class SpellwildOuphe extends CardImpl { public SpellwildOuphe copy() { return new SpellwildOuphe(this); } -} - -class SpellwildOupheCostReductionEffect extends CostModificationEffectImpl { - - private static final String effectText = "Spells that target {this} cost {2} less to cast"; - - SpellwildOupheCostReductionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = effectText; - } - - SpellwildOupheCostReductionEffect(SpellwildOupheCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, 2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - if (targetUUID.equals(source.getSourceId())) { - return true; - } - } - } - } - } - return false; - } - - @Override - public SpellwildOupheCostReductionEffect copy() { - return new SpellwildOupheCostReductionEffect(this); - } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SphereOfResistance.java b/Mage.Sets/src/mage/cards/s/SphereOfResistance.java index 0e5c8303fe..c1326f4da8 100644 --- a/Mage.Sets/src/mage/cards/s/SphereOfResistance.java +++ b/Mage.Sets/src/mage/cards/s/SphereOfResistance.java @@ -1,25 +1,28 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.TargetController; import mage.constants.Zone; +import mage.filter.FilterCard; + +import java.util.UUID; /** - * * @author Plopman */ public final class SphereOfResistance extends CardImpl { public SphereOfResistance(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Spells cost {1} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(1))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostIncreasingAllEffect(1, new FilterCard("Spells"), TargetController.ANY)) + ); } public SphereOfResistance(final SphereOfResistance card) { diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfNewPrahv.java b/Mage.Sets/src/mage/cards/s/SphinxOfNewPrahv.java index 690a8c7f3b..102695ce81 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxOfNewPrahv.java +++ b/Mage.Sets/src/mage/cards/s/SphinxOfNewPrahv.java @@ -1,20 +1,17 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.players.Player; -import mage.target.Target; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; import java.util.UUID; @@ -37,7 +34,9 @@ public final class SphinxOfNewPrahv extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Spells your opponents cast that target Sphinx of New Prahv cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(new SphinxOfNewPrahvCostIncreaseEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT)) + ); } private SphinxOfNewPrahv(final SphinxOfNewPrahv card) { @@ -49,49 +48,3 @@ public final class SphinxOfNewPrahv extends CardImpl { return new SphinxOfNewPrahv(this); } } - -class SphinxOfNewPrahvCostIncreaseEffect extends CostModificationEffectImpl { - - SphinxOfNewPrahvCostIncreaseEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Spells your opponents cast that target {this} cost {2} more to cast"; - } - - private SphinxOfNewPrahvCostIncreaseEffect(SphinxOfNewPrahvCostIncreaseEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null - || !(abilityToModify instanceof SpellAbility) - || !controller.hasOpponent(abilityToModify.getControllerId(), game)) { - return false; - } - for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { - Mode mode = abilityToModify.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetUUID : target.getTargets()) { - if (targetUUID.equals(source.getSourceId())) { - return true; - } - } - } - } - return false; - } - - @Override - public SphinxOfNewPrahvCostIncreaseEffect copy() { - return new SphinxOfNewPrahvCostIncreaseEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java b/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java index 038b392885..678629eaee 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java +++ b/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CantBeCounteredControlledEffect; import mage.abilities.keyword.FlyingAbility; @@ -37,7 +37,7 @@ public final class SphinxOfTheFinalWord extends CardImpl { this.toughness = new MageInt(5); // Sphinx of the Final Word can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Flying this.addAbility(FlyingAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfUthuun.java b/Mage.Sets/src/mage/cards/s/SphinxOfUthuun.java index d72f27be81..2cf17cfe44 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxOfUthuun.java +++ b/Mage.Sets/src/mage/cards/s/SphinxOfUthuun.java @@ -23,7 +23,9 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; +import mage.target.Target; import mage.target.TargetCard; +import mage.target.common.TargetOpponent; /** * @@ -81,6 +83,13 @@ class SphinxOfUthuunEffect extends OneShotEffect { Set opponents = game.getOpponents(source.getControllerId()); if (!opponents.isEmpty()) { Player opponent = game.getPlayer(opponents.iterator().next()); + if (opponents.size() > 1) { + Target targetOpponent = new TargetOpponent(true); + if (controller.chooseTarget(Outcome.Neutral, targetOpponent, source, game)) { + opponent = game.getPlayer(targetOpponent.getFirstTarget()); + game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " to separate the revealed cards"); + } + } TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile")); target.setRequired(false); diff --git a/Mage.Sets/src/mage/cards/s/SphinxsTutelage.java b/Mage.Sets/src/mage/cards/s/SphinxsTutelage.java index 1b5868ca4e..372f47facf 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxsTutelage.java +++ b/Mage.Sets/src/mage/cards/s/SphinxsTutelage.java @@ -1,7 +1,7 @@ package mage.cards.s; -import java.util.UUID; +import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.DrawCardControllerTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -11,8 +11,6 @@ import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -20,8 +18,11 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * * @author LevelX2 */ public final class SphinxsTutelage extends CardImpl { @@ -52,7 +53,7 @@ class SphinxsTutelageEffect extends OneShotEffect { public SphinxsTutelageEffect() { super(Outcome.Benefit); - this.staticText = "target opponent puts the top two cards of their library into their graveyard. If they're both nonland cards that share a color, repeat this process"; + this.staticText = "target opponent mills two cards. If two nonland cards that share a color were milled this way, repeat this process."; } public SphinxsTutelageEffect(final SphinxsTutelageEffect effect) { @@ -67,10 +68,11 @@ class SphinxsTutelageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - boolean colorShared; + if (targetPlayer != null) { int possibleIterations = targetPlayer.getLibrary().size() / 2; int iteration = 0; + boolean colorShared; do { iteration++; if (iteration > possibleIterations + 20) { @@ -82,20 +84,35 @@ class SphinxsTutelageEffect extends OneShotEffect { return true; } colorShared = false; - Cards cards = new CardsImpl(targetPlayer.getLibrary().getTopCards(game, 2)); - Card card1 = null; - for (Card card : cards.getCards(game)) { - if (card.isLand()) { + List cards = targetPlayer + .millCards(2, source, game) + .getCards(game) + .stream() + .filter(card -> !card.isLand()) + .collect(Collectors.toList()); + if (cards.size() < 2) { + break; + } + for (int i = 0; i < cards.size(); i++) { + if (colorShared) { break; } - if (card1 == null) { - card1 = card; - } else { - colorShared = card1.getColor(game).shares(card.getColor(game)); + ObjectColor color1 = cards.get(i).getColor(game); + if (color1.isColorless()) { + continue; + } + for (int j = 0; j < cards.size(); j++) { + if (i >= j) { + continue; + } + ObjectColor color2 = cards.get(j).getColor(game); + if (color1.shares(color2)) { + colorShared = true; + break; + } } } - targetPlayer.moveCards(cards, Zone.GRAVEYARD, source, game); - } while (colorShared && targetPlayer.isInGame()); + } while (colorShared); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/s/SpinalCentipede.java b/Mage.Sets/src/mage/cards/s/SpinalCentipede.java index 9d5073b48b..7e8c8347a9 100644 --- a/Mage.Sets/src/mage/cards/s/SpinalCentipede.java +++ b/Mage.Sets/src/mage/cards/s/SpinalCentipede.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.constants.SubType; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class SpinalCentipede extends CardImpl { this.toughness = new MageInt(2); // When Spinal Centipede dies, put a +1/+1 counter on target creature you control. - Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect( + Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect( CounterType.P1P1.createInstance() ), false); ability.addTarget(new TargetControlledCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/s/SpinalEmbrace.java b/Mage.Sets/src/mage/cards/s/SpinalEmbrace.java index ba31d7abda..3814470b51 100644 --- a/Mage.Sets/src/mage/cards/s/SpinalEmbrace.java +++ b/Mage.Sets/src/mage/cards/s/SpinalEmbrace.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; @@ -17,27 +15,21 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.TurnPhase; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SpinalEmbrace extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public SpinalEmbrace(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}{B}"); @@ -45,7 +37,7 @@ public final class SpinalEmbrace extends CardImpl { this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(TurnPhase.COMBAT)); // Untap target creature you don't control and gain control of it. It gains haste until end of turn. At the beginning of the next end step, sacrifice it. If you do, you gain life equal to its toughness. - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.getSpellAbility().addEffect(new UntapTargetEffect()); Effect effect = new GainControlTargetEffect(Duration.EndOfTurn); effect.setText("and gain control of it"); @@ -54,7 +46,7 @@ public final class SpinalEmbrace extends CardImpl { this.getSpellAbility().addEffect(new SpinalEmbraceAddDelayedEffect()); } - public SpinalEmbrace(final SpinalEmbrace card) { + private SpinalEmbrace(final SpinalEmbrace card) { super(card); } @@ -66,12 +58,12 @@ public final class SpinalEmbrace extends CardImpl { class SpinalEmbraceAddDelayedEffect extends OneShotEffect { - public SpinalEmbraceAddDelayedEffect() { + SpinalEmbraceAddDelayedEffect() { super(Outcome.Sacrifice); staticText = "At the beginning of the next end step, sacrifice it. If you do, you gain life equal to its toughness"; } - public SpinalEmbraceAddDelayedEffect(final SpinalEmbraceAddDelayedEffect effect) { + private SpinalEmbraceAddDelayedEffect(final SpinalEmbraceAddDelayedEffect effect) { super(effect); } @@ -92,12 +84,12 @@ class SpinalEmbraceAddDelayedEffect extends OneShotEffect { class SpinalEmbraceSacrificeEffect extends OneShotEffect { - public SpinalEmbraceSacrificeEffect() { + SpinalEmbraceSacrificeEffect() { super(Outcome.Benefit); this.staticText = "sacrifice it. If you do, you gain life equal to its toughness"; } - public SpinalEmbraceSacrificeEffect(final SpinalEmbraceSacrificeEffect effect) { + private SpinalEmbraceSacrificeEffect(final SpinalEmbraceSacrificeEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/s/SpinedMegalodon.java b/Mage.Sets/src/mage/cards/s/SpinedMegalodon.java new file mode 100644 index 0000000000..b740d87914 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpinedMegalodon.java @@ -0,0 +1,41 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpinedMegalodon extends CardImpl { + + public SpinedMegalodon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); + + this.subtype.add(SubType.SHARK); + this.power = new MageInt(5); + this.toughness = new MageInt(7); + + // Hexproof + this.addAbility(HexproofAbility.getInstance()); + + // Whenever Spined Megalodon attacks, scry 1. + this.addAbility(new AttacksTriggeredAbility(new ScryEffect(1), false)); + } + + private SpinedMegalodon(final SpinedMegalodon card) { + super(card); + } + + @Override + public SpinedMegalodon copy() { + return new SpinedMegalodon(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpinyStarfish.java b/Mage.Sets/src/mage/cards/s/SpinyStarfish.java index 8c577809ac..760f56bcf2 100644 --- a/Mage.Sets/src/mage/cards/s/SpinyStarfish.java +++ b/Mage.Sets/src/mage/cards/s/SpinyStarfish.java @@ -1,141 +1,141 @@ -package mage.cards.s; - -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfEndStepTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.Condition; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.RegenerateSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.token.StarfishToken; -import mage.watchers.Watcher; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -/** - * @author spike - */ -public final class SpinyStarfish extends CardImpl { - - public SpinyStarfish(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); - this.subtype.add(SubType.STARFISH); - this.power = new MageInt(0); - this.toughness = new MageInt(1); - - // {U}: Regenerate Spiny Starfish. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{U}"))); - - // At the beginning of each end step, if Spiny Starfish regenerated this turn, create a 0/1 blue Starfish creature token for each time it regenerated this turn. - this.addAbility( - new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility( - new CreateTokenEffect( - new StarfishToken(), - new SpinyStarfishDynamicValue()), - TargetController.ANY, - false), - SpinyStarfishCondition.instance, - "At the beginning of each end step, if {this} regenerated this turn, create a 0/1 blue Starfish creature token for each time it regenerated this turn."), - new SpinyStarfishWatcher()); - } - - public SpinyStarfish(final SpinyStarfish card) { - super(card); - } - - @Override - public SpinyStarfish copy() { - return new SpinyStarfish(this); - } -} - -enum SpinyStarfishCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - SpinyStarfishWatcher watcher = game.getState().getWatcher(SpinyStarfishWatcher.class); - return watcher != null && watcher.regeneratedCount(source.getSourceId()) != 0; - } - - @Override - public String toString() { - return "if Spiny Starfish regenerated this turn"; - } - -} - -class SpinyStarfishWatcher extends Watcher { - - // Probably dumb to record all regeneration events, could just record this, - // but not sure how to know what source this watcher is attached to. - private final Map regeneratedCount = new HashMap<>(); - - public SpinyStarfishWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.REGENERATED) { - UUID regeneratedId = event.getTargetId(); - Integer count = regeneratedCount.get(regeneratedId); - if (count == null) { - count = 0; - } - regeneratedCount.put(regeneratedId, ++count); - } - } - - @Override - public void reset() { - super.reset(); - regeneratedCount.clear(); - } - - public int regeneratedCount(UUID sourceId) { - return regeneratedCount.getOrDefault(sourceId, 0); - } - -} - -class SpinyStarfishDynamicValue implements DynamicValue { - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - SpinyStarfishWatcher watcher = game.getState().getWatcher( - SpinyStarfishWatcher.class); - if (watcher != null) { - return watcher.regeneratedCount(sourceAbility.getSourceId()); - } - return 0; - } - - @Override - public SpinyStarfishDynamicValue copy() { - return new SpinyStarfishDynamicValue(); - } - - @Override - public String toString() { - return "1"; - } - - @Override - public String getMessage() { - return "time {this} regenerated this turn"; - } -} +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.StarfishToken; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author spike + */ +public final class SpinyStarfish extends CardImpl { + + public SpinyStarfish(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + this.subtype.add(SubType.STARFISH); + this.power = new MageInt(0); + this.toughness = new MageInt(1); + + // {U}: Regenerate Spiny Starfish. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{U}"))); + + // At the beginning of each end step, if Spiny Starfish regenerated this turn, create a 0/1 blue Starfish creature token for each time it regenerated this turn. + this.addAbility( + new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new CreateTokenEffect( + new StarfishToken(), + new SpinyStarfishDynamicValue()), + TargetController.ANY, + false), + SpinyStarfishCondition.instance, + "At the beginning of each end step, if {this} regenerated this turn, create a 0/1 blue Starfish creature token for each time it regenerated this turn."), + new SpinyStarfishWatcher()); + } + + public SpinyStarfish(final SpinyStarfish card) { + super(card); + } + + @Override + public SpinyStarfish copy() { + return new SpinyStarfish(this); + } +} + +enum SpinyStarfishCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + SpinyStarfishWatcher watcher = game.getState().getWatcher(SpinyStarfishWatcher.class); + return watcher != null && watcher.regeneratedCount(source.getSourceId()) != 0; + } + + @Override + public String toString() { + return "if Spiny Starfish regenerated this turn"; + } + +} + +class SpinyStarfishWatcher extends Watcher { + + // Probably dumb to record all regeneration events, could just record this, + // but not sure how to know what source this watcher is attached to. + private final Map regeneratedCount = new HashMap<>(); + + public SpinyStarfishWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.REGENERATED) { + UUID regeneratedId = event.getTargetId(); + Integer count = regeneratedCount.get(regeneratedId); + if (count == null) { + count = 0; + } + regeneratedCount.put(regeneratedId, ++count); + } + } + + @Override + public void reset() { + super.reset(); + regeneratedCount.clear(); + } + + public int regeneratedCount(UUID sourceId) { + return regeneratedCount.getOrDefault(sourceId, 0); + } + +} + +class SpinyStarfishDynamicValue implements DynamicValue { + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + SpinyStarfishWatcher watcher = game.getState().getWatcher( + SpinyStarfishWatcher.class); + if (watcher != null) { + return watcher.regeneratedCount(sourceAbility.getSourceId()); + } + return 0; + } + + @Override + public SpinyStarfishDynamicValue copy() { + return new SpinyStarfishDynamicValue(); + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return "time {this} regenerated this turn"; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiralingDuelist.java b/Mage.Sets/src/mage/cards/s/SpiralingDuelist.java index 52af0e6edc..6ee7422305 100644 --- a/Mage.Sets/src/mage/cards/s/SpiralingDuelist.java +++ b/Mage.Sets/src/mage/cards/s/SpiralingDuelist.java @@ -1,23 +1,20 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.DoubleStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; + +import java.util.UUID; /** - * * @author North */ public final class SpiralingDuelist extends CardImpl { @@ -25,15 +22,17 @@ public final class SpiralingDuelist extends CardImpl { private static final String effectText = "Metalcraft — Spiraling Duelist has double strike as long as you control three or more artifacts."; public SpiralingDuelist(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.BERSERKER); - this.power = new MageInt(3); this.toughness = new MageInt(1); + // Metalcraft — Spiraling Duelist has double strike as long as you control three or more artifacts. ContinuousEffect effect = new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect, MetalcraftCondition.instance, effectText))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect, MetalcraftCondition.instance, effectText)) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance)); } public SpiralingDuelist(final SpiralingDuelist card) { diff --git a/Mage.Sets/src/mage/cards/s/SpireSerpent.java b/Mage.Sets/src/mage/cards/s/SpireSerpent.java index a8338bd651..ae207c0988 100644 --- a/Mage.Sets/src/mage/cards/s/SpireSerpent.java +++ b/Mage.Sets/src/mage/cards/s/SpireSerpent.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -11,16 +9,15 @@ import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; + +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class SpireSerpent extends CardImpl { @@ -28,19 +25,24 @@ public final class SpireSerpent extends CardImpl { private static final String abilityText1 = "Metalcraft — As long as you control three or more artifacts, {this} gets +2/+2"; public SpireSerpent(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); this.subtype.add(SubType.SERPENT); this.color.setBlue(true); this.power = new MageInt(3); this.toughness = new MageInt(5); + // Defender this.addAbility(DefenderAbility.getInstance()); + + // Metalcraft — As long as you control three or more artifacts, Spire Serpent gets +2/+2 and can attack as though it didn’t have defender. ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), MetalcraftCondition.instance, abilityText1); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect1); Effect effect = new ConditionalAsThoughEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.WhileOnBattlefield), MetalcraftCondition.instance); effect.setText("and can attack as though it didn't have defender"); ability.addEffect(effect); + ability.setAbilityWord(AbilityWord.METALCRAFT); + ability.addHint(MetalcraftHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SpiritBonds.java b/Mage.Sets/src/mage/cards/s/SpiritBonds.java index 2827034a87..2bdb334f1f 100644 --- a/Mage.Sets/src/mage/cards/s/SpiritBonds.java +++ b/Mage.Sets/src/mage/cards/s/SpiritBonds.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -25,8 +23,9 @@ import mage.game.permanent.token.SpiritWhiteToken; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SpiritBonds extends CardImpl { @@ -42,16 +41,16 @@ public final class SpiritBonds extends CardImpl { } public SpiritBonds(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // Whenever a nontoken creature enters the battlefield under your control, you may pay {W}. If you do, but a 1/1 white Spirit creature token with flying into play. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DoIfCostPaid(new CreateTokenEffect(new SpiritWhiteToken("M15")), new ManaCostsImpl("{W}")), filterNontoken, false)); - + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DoIfCostPaid(new CreateTokenEffect(new SpiritWhiteToken()), new ManaCostsImpl("{W}")), filterNontoken, false)); + // {1}{W}, Sacrifice a Spirit: Target non-Spirit creature you control gains indestructible until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{1}{W}")); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1,1,filterSpirit, true))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, filterSpirit, true))); ability.addTarget(new TargetControlledCreaturePermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java b/Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java new file mode 100644 index 0000000000..bd49a41871 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java @@ -0,0 +1,41 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpiritOfMalevolence extends CardImpl { + + public SpiritOfMalevolence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Spirit of Malevolence dies, each opponent loses 1 life and you gain 1 life. + Ability ability = new DiesSourceTriggeredAbility(new LoseLifeOpponentsEffect(1)); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); + } + + private SpiritOfMalevolence(final SpiritOfMalevolence card) { + super(card); + } + + @Override + public SpiritOfMalevolence copy() { + return new SpiritOfMalevolence(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiritualFocus.java b/Mage.Sets/src/mage/cards/s/SpiritualFocus.java index 4b1de900be..4eb184fec1 100644 --- a/Mage.Sets/src/mage/cards/s/SpiritualFocus.java +++ b/Mage.Sets/src/mage/cards/s/SpiritualFocus.java @@ -106,7 +106,7 @@ class SpiritualFocusDrawCardEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); if (player != null && permanent != null) { if (player.chooseUse(outcome, "Draw a card (" + permanent.getLogName() + ')', source, game)) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/SpitefulPrankster.java b/Mage.Sets/src/mage/cards/s/SpitefulPrankster.java new file mode 100644 index 0000000000..b0e11ae8e3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpitefulPrankster.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.common.MyTurnHint; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetPlayerOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpitefulPrankster extends CardImpl { + + public SpitefulPrankster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.DEVIL); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // As long as it's your turn, Spiteful Prankster has first strike. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield), + MyTurnCondition.instance, "As long as it's your turn, {this} has first strike." + )).addHint(MyTurnHint.instance)); + + // Whenever another creature dies, Spiteful Prankster deals 1 damage to target player or planeswalker. + Ability ability = new DiesCreatureTriggeredAbility( + new DamageTargetEffect(1), false, true + ); + ability.addTarget(new TargetPlayerOrPlaneswalker()); + this.addAbility(ability); + } + + private SpitefulPrankster(final SpitefulPrankster card) { + super(card); + } + + @Override + public SpitefulPrankster copy() { + return new SpitefulPrankster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpittingSlug.java b/Mage.Sets/src/mage/cards/s/SpittingSlug.java index 6907a53aab..b315f3d59f 100644 --- a/Mage.Sets/src/mage/cards/s/SpittingSlug.java +++ b/Mage.Sets/src/mage/cards/s/SpittingSlug.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -39,7 +39,7 @@ public final class SpittingSlug extends CardImpl { this.toughness = new MageInt(4); // Whenever Spitting Slug blocks or becomes blocked, you may pay {1}{G}. If you do, Spitting Slug gains first strike until end of turn. Otherwise, each creature blocking or blocked by Spitting Slug gains first strike until end of turn. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility( + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility( new DoIfCostPaid(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), new SpittingSlugEffect(), new ManaCostsImpl("{1}{G}")).setText("you may pay {1}{G}. If you do, {this} gains first strike until end of turn. Otherwise, each creature blocking or blocked by {this} gains first strike until end of turn"), @@ -79,7 +79,7 @@ class SpittingSlugEffect extends OneShotEffect { filter.add(Predicates.or(new BlockedByIdPredicate(sourcePermanent.getId()), new BlockingAttackerIdPredicate(sourcePermanent.getId()))); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { ContinuousEffect effect = new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/s/SplendorMare.java b/Mage.Sets/src/mage/cards/s/SplendorMare.java new file mode 100644 index 0000000000..dbc20eabe0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SplendorMare.java @@ -0,0 +1,52 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SplendorMare extends CardImpl { + + public SplendorMare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.ELK); + this.subtype.add(SubType.UNICORN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Cycling {1}{W} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}{W}"))); + + // When you cycle Splendor Mare, put a lifelink counter on target creature you control. + Ability ability = new CycleTriggeredAbility(new AddCountersTargetEffect(CounterType.LIFELINK.createInstance())); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private SplendorMare(final SplendorMare card) { + super(card); + } + + @Override + public SplendorMare copy() { + return new SplendorMare(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java b/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java index 153bc570f7..3466f128b5 100644 --- a/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java +++ b/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java @@ -68,7 +68,7 @@ class SpoilsOfTheVaultEffect extends OneShotEffect { for (Card card : controller.getLibrary().getCards(game)) { if (card != null) { cardsToReveal.add(card); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { controller.moveCards(card, Zone.HAND, source, game); break; } else { diff --git a/Mage.Sets/src/mage/cards/s/SpontaneousFlight.java b/Mage.Sets/src/mage/cards/s/SpontaneousFlight.java new file mode 100644 index 0000000000..7f792371a0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpontaneousFlight.java @@ -0,0 +1,36 @@ +package mage.cards.s; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpontaneousFlight extends CardImpl { + + public SpontaneousFlight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // Target creature gets +2/+2 until end of turn. Put a flying counter on it. + this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2)); + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.FLYING.createInstance()) + .setText("Put a flying counter on it")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private SpontaneousFlight(final SpontaneousFlight card) { + super(card); + } + + @Override + public SpontaneousFlight copy() { + return new SpontaneousFlight(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SporewebWeaver.java b/Mage.Sets/src/mage/cards/s/SporewebWeaver.java new file mode 100644 index 0000000000..77aeed621e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SporewebWeaver.java @@ -0,0 +1,50 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealtDamageToSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.HexproofFromBlueAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.SaprolingToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SporewebWeaver extends CardImpl { + + public SporewebWeaver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.SPIDER); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Hexproof from blue + this.addAbility(HexproofFromBlueAbility.getInstance()); + + // Whenever Sporeweb Weaver is dealt damage, you gain 1 life and create a 1/1 green Saproling creature token. + Ability ability = new DealtDamageToSourceTriggeredAbility(new GainLifeEffect(1), false); + ability.addEffect(new CreateTokenEffect(new SaprolingToken()).concatBy("and")); + this.addAbility(ability); + } + + private SporewebWeaver(final SporewebWeaver card) { + super(card); + } + + @Override + public SporewebWeaver copy() { + return new SporewebWeaver(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpreadingPlague.java b/Mage.Sets/src/mage/cards/s/SpreadingPlague.java index fea0ae4948..f2faa691fe 100644 --- a/Mage.Sets/src/mage/cards/s/SpreadingPlague.java +++ b/Mage.Sets/src/mage/cards/s/SpreadingPlague.java @@ -63,7 +63,7 @@ class SpreadingPlagueEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent creature = game.getPermanentOrLKIBattlefield(targetPointer.getFirst(game, source)); + Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (creature != null) { ObjectColor color = creature.getColor(game); for (Permanent permanent : game.getBattlefield().getActivePermanents(FILTER, source.getControllerId(), game)) { diff --git a/Mage.Sets/src/mage/cards/s/SpringjawTrap.java b/Mage.Sets/src/mage/cards/s/SpringjawTrap.java new file mode 100644 index 0000000000..b9edc56c0d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpringjawTrap.java @@ -0,0 +1,46 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpringjawTrap extends CardImpl { + + public SpringjawTrap(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // {4}, {T}, Sacrifice Springjaw Trap: It deals 3 damage to any target. + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(3, "it"), new GenericManaCost(4) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + } + + private SpringjawTrap(final SpringjawTrap card) { + super(card); + } + + @Override + public SpringjawTrap copy() { + return new SpringjawTrap(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpriteDragon.java b/Mage.Sets/src/mage/cards/s/SpriteDragon.java new file mode 100644 index 0000000000..cdc45e891d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpriteDragon.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpriteDragon extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a noncreature spell"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public SpriteDragon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{R}"); + + this.subtype.add(SubType.FAERIE); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever you cast a noncreature spell, put a +1/+1 counter on Sprite Dragon. + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter, false + )); + } + + private SpriteDragon(final SpriteDragon card) { + super(card); + } + + @Override + public SpriteDragon copy() { + return new SpriteDragon(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SproutingThrinax.java b/Mage.Sets/src/mage/cards/s/SproutingThrinax.java index fd5fe9af5d..a5b5f5d231 100644 --- a/Mage.Sets/src/mage/cards/s/SproutingThrinax.java +++ b/Mage.Sets/src/mage/cards/s/SproutingThrinax.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -28,7 +28,7 @@ public final class SproutingThrinax extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(saprolingToken, 3), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(saprolingToken, 3), false)); } public SproutingThrinax(final SproutingThrinax card) { diff --git a/Mage.Sets/src/mage/cards/s/SquanderedResources.java b/Mage.Sets/src/mage/cards/s/SquanderedResources.java index 803fca497e..689301ff7b 100644 --- a/Mage.Sets/src/mage/cards/s/SquanderedResources.java +++ b/Mage.Sets/src/mage/cards/s/SquanderedResources.java @@ -1,20 +1,27 @@ package mage.cards.s; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; import mage.Mana; -import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.common.ManaEffect; -import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.abilities.mana.AnyColorLandsProduceManaAbility; +import mage.abilities.mana.ManaOptions; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.choices.ChoiceColor; import mage.constants.CardType; -import mage.constants.ColoredManaSymbol; +import mage.constants.ManaType; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; @@ -22,10 +29,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetControlledPermanent; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - /** * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ @@ -64,24 +67,49 @@ class SquanderedResourcesEffect extends ManaEffect { @Override public List getNetMana(Game game, Ability source) { List netManas = new ArrayList<>(); - Mana types = getManaTypes(game, source); - if (types.getBlack() > 0) { - netManas.add(new Mana(ColoredManaSymbol.B)); + if (game != null && game.inCheckPlayableState()) { + // add color combinations of available mana + ManaOptions allPossibleMana = new ManaOptions(); + for (Permanent land : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_LAND, source.getControllerId(), game)) { + ManaOptions currentPossibleMana = new ManaOptions(); + Set manaTypes = AnyColorLandsProduceManaAbility.getManaTypesFromPermanent(land, game); + if (manaTypes.size() == 5 && !manaTypes.contains(ManaType.COLORLESS) || manaTypes.size() == 6) { + currentPossibleMana.add(Mana.AnyMana(1)); + if (manaTypes.contains(ManaType.COLORLESS)) { + currentPossibleMana.add(new Mana(ManaType.COLORLESS)); + } + } else { + for (ManaType manaType : manaTypes) { + currentPossibleMana.add(new Mana(manaType)); + } + } + allPossibleMana.addMana(currentPossibleMana); + } + allPossibleMana.removeDuplicated(); + return allPossibleMana.stream().collect(Collectors.toList()); } - if (types.getRed() > 0) { - netManas.add(new Mana(ColoredManaSymbol.R)); - } - if (types.getBlue() > 0) { - netManas.add(new Mana(ColoredManaSymbol.U)); - } - if (types.getGreen() > 0) { - netManas.add(new Mana(ColoredManaSymbol.G)); - } - if (types.getWhite() > 0) { - netManas.add(new Mana(ColoredManaSymbol.W)); - } - if (types.getGeneric() > 0) { - netManas.add(Mana.ColorlessMana(1)); + Set manaTypes = getManaTypesFromSacrificedPermanent(game, source); + if (manaTypes.size() == 5 && !manaTypes.contains(ManaType.COLORLESS) && !manaTypes.contains(ManaType.GENERIC)) { + netManas.add(Mana.AnyMana(1)); + } else { + if (manaTypes.contains(ManaType.BLACK)) { + netManas.add(Mana.BlackMana(1)); + } + if (manaTypes.contains(ManaType.RED)) { + netManas.add(Mana.RedMana(1)); + } + if (manaTypes.contains(ManaType.BLUE)) { + netManas.add(Mana.BlueMana(1)); + } + if (manaTypes.contains(ManaType.GREEN)) { + netManas.add(Mana.GreenMana(1)); + } + if (manaTypes.contains(ManaType.WHITE)) { + netManas.add(Mana.WhiteMana(1)); + } + if (manaTypes.contains(ManaType.COLORLESS)) { + netManas.add(Mana.ColorlessMana(1)); + } } return netManas; } @@ -92,34 +120,26 @@ class SquanderedResourcesEffect extends ManaEffect { if (game == null) { return mana; } - Mana types = getManaTypes(game, source); + Set manaTypes = getManaTypesFromSacrificedPermanent(game, source); Choice choice = new ChoiceColor(true); choice.getChoices().clear(); choice.setMessage("Pick a mana color"); - if (types.getBlack() > 0) { + if (manaTypes.contains(ManaType.BLACK)) { choice.getChoices().add("Black"); } - if (types.getRed() > 0) { + if (manaTypes.contains(ManaType.RED)) { choice.getChoices().add("Red"); } - if (types.getBlue() > 0) { + if (manaTypes.contains(ManaType.BLUE)) { choice.getChoices().add("Blue"); } - if (types.getGreen() > 0) { + if (manaTypes.contains(ManaType.GREEN)) { choice.getChoices().add("Green"); } - if (types.getWhite() > 0) { + if (manaTypes.contains(ManaType.WHITE)) { choice.getChoices().add("White"); } - if (types.getColorless() > 0) { - choice.getChoices().add("Colorless"); - } - if (types.getAny() > 0) { - choice.getChoices().add("Black"); - choice.getChoices().add("Red"); - choice.getChoices().add("Blue"); - choice.getChoices().add("Green"); - choice.getChoices().add("White"); + if (manaTypes.contains(ManaType.COLORLESS)) { choice.getChoices().add("Colorless"); } if (!choice.getChoices().isEmpty()) { @@ -158,20 +178,14 @@ class SquanderedResourcesEffect extends ManaEffect { return mana; } - private Mana getManaTypes(Game game, Ability source) { - Mana types = new Mana(); + private Set getManaTypesFromSacrificedPermanent(Game game, Ability source) { + Set types = new HashSet<>(); for (Cost cost : source.getCosts()) { if (cost instanceof SacrificeTargetCost && !((SacrificeTargetCost) cost).getPermanents().isEmpty()) { Permanent land = ((SacrificeTargetCost) cost).getPermanents().get(0); if (land != null) { - Abilities manaAbilities = land.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD); - for (ActivatedManaAbilityImpl ability : manaAbilities) { - if (!ability.equals(source) && ability.definesMana(game)) { - for (Mana netMana : ability.getNetMana(game)) { - types.add(netMana); - } - } - } + types.addAll(AnyColorLandsProduceManaAbility.getManaTypesFromPermanent(land, game)); + break; } } } diff --git a/Mage.Sets/src/mage/cards/s/SquealingDevil.java b/Mage.Sets/src/mage/cards/s/SquealingDevil.java index 4a212aaf03..a2976211e5 100644 --- a/Mage.Sets/src/mage/cards/s/SquealingDevil.java +++ b/Mage.Sets/src/mage/cards/s/SquealingDevil.java @@ -87,7 +87,7 @@ class SquealingDevilEffect extends OneShotEffect { Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null && permanent.isCreature()) { ContinuousEffect effect = new BoostTargetEffect(costX, 0, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SqueesRevenge.java b/Mage.Sets/src/mage/cards/s/SqueesRevenge.java index f30ec04673..036d5f293f 100644 --- a/Mage.Sets/src/mage/cards/s/SqueesRevenge.java +++ b/Mage.Sets/src/mage/cards/s/SqueesRevenge.java @@ -60,7 +60,7 @@ class SqueesRevengeEffect extends OneShotEffect { return true; } } - player.drawCards(2 * number, game); + player.drawCards(2 * number, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/s/Squeeze.java b/Mage.Sets/src/mage/cards/s/Squeeze.java index 3dabaa83dc..2605970a04 100644 --- a/Mage.Sets/src/mage/cards/s/Squeeze.java +++ b/Mage.Sets/src/mage/cards/s/Squeeze.java @@ -1,35 +1,34 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class Squeeze extends CardImpl { - + private static final FilterCard filter = new FilterCard("Sorcery spells"); - + static { filter.add(CardType.SORCERY.getPredicate()); } public Squeeze(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); // Sorcery spells cost {3} more to cast. - Effect effect = new SpellsCostIncreasementAllEffect(filter, 3); - effect.setText("Sorcery spells cost {3} more to cast"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostIncreasingAllEffect(3, filter, TargetController.ANY)) + ); } public Squeeze(final Squeeze card) { diff --git a/Mage.Sets/src/mage/cards/s/StandOrFall.java b/Mage.Sets/src/mage/cards/s/StandOrFall.java index 7286fbc6f1..2c976bee06 100644 --- a/Mage.Sets/src/mage/cards/s/StandOrFall.java +++ b/Mage.Sets/src/mage/cards/s/StandOrFall.java @@ -52,7 +52,7 @@ class StandOrFallEffect extends OneShotEffect { public StandOrFallEffect() { super(Outcome.Detriment); - this.staticText = "separate all creatures defending player controls into two piles. Only creatures in the pile of that player's choice can block this turn"; + this.staticText = "separate all creatures that player controls into two piles and that player chooses one. Only creatures in the chosen piles can block this turn"; } public StandOrFallEffect(final StandOrFallEffect effect) { @@ -117,7 +117,7 @@ class StandOrFallEffect extends OneShotEffect { if (permanent != null) { RestrictionEffect effect = new CantBlockTargetEffect(Duration.EndOfTurn); effect.setText(""); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/s/Standstill.java b/Mage.Sets/src/mage/cards/s/Standstill.java index 2460a184d4..13109fa7af 100644 --- a/Mage.Sets/src/mage/cards/s/Standstill.java +++ b/Mage.Sets/src/mage/cards/s/Standstill.java @@ -97,7 +97,7 @@ class StandstillEffect extends OneShotEffect { for (UUID uuid : game.getOpponents(this.getTargetPointer().getFirst(game, source))) { Player player = game.getPlayer(uuid); if (player != null) { - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/s/StarCompass.java b/Mage.Sets/src/mage/cards/s/StarCompass.java index bc8d0b475a..8e22b80c78 100644 --- a/Mage.Sets/src/mage/cards/s/StarCompass.java +++ b/Mage.Sets/src/mage/cards/s/StarCompass.java @@ -1,43 +1,34 @@ package mage.cards.s; -import mage.Mana; -import mage.abilities.Abilities; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.common.ManaEffect; -import mage.abilities.mana.ActivatedManaAbilityImpl; -import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.choices.Choice; -import mage.choices.ChoiceColor; import mage.constants.CardType; -import mage.constants.ColoredManaSymbol; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -import java.util.ArrayList; -import java.util.List; import java.util.UUID; +import mage.abilities.mana.AnyColorLandsProduceManaAbility; +import mage.constants.TargetController; /** * @author anonymous */ public final class StarCompass extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledLandPermanent(); + static { + filter.add(SuperType.BASIC.getPredicate()); + } + public StarCompass(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Star Compass enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); - // {tap}: Add one mana of any color that a basic land you control could produce. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new StarCompassManaEffect(), new TapSourceCost())); + // {T}: Add one mana of any color that a basic land you control could produce. + this.addAbility(new AnyColorLandsProduceManaAbility(TargetController.YOU, true, filter)); } public StarCompass(final StarCompass card) { @@ -48,144 +39,4 @@ public final class StarCompass extends CardImpl { public StarCompass copy() { return new StarCompass(this); } -} - -class StarCompassManaEffect extends ManaEffect { - - private static final FilterControlledPermanent filter = new FilterControlledLandPermanent(); - - static { - filter.add(SuperType.BASIC.getPredicate()); - } - - public StarCompassManaEffect() { - super(); - staticText = "Add one mana of any type that a basic land you control could produce"; - } - - public StarCompassManaEffect(final StarCompassManaEffect effect) { - super(effect); - } - - @Override - public List getNetMana(Game game, Ability source) { - List netManas = new ArrayList<>(); - Mana types = getManaTypes(game, source); - if (types.getBlack() > 0) { - netManas.add(new Mana(ColoredManaSymbol.B)); - } - if (types.getRed() > 0) { - netManas.add(new Mana(ColoredManaSymbol.R)); - } - if (types.getBlue() > 0) { - netManas.add(new Mana(ColoredManaSymbol.U)); - } - if (types.getGreen() > 0) { - netManas.add(new Mana(ColoredManaSymbol.G)); - } - if (types.getWhite() > 0) { - netManas.add(new Mana(ColoredManaSymbol.W)); - } - if (types.getGeneric() > 0) { - netManas.add(Mana.ColorlessMana(1)); - } - return netManas; - } - - @Override - public Mana produceMana(Game game, Ability source) { - Mana mana = new Mana(); - if (game == null) { - return mana; - } - Mana types = getManaTypes(game, source); - Choice choice = new ChoiceColor(true); - choice.getChoices().clear(); - choice.setMessage("Pick a mana color"); - if (types.getBlack() > 0) { - choice.getChoices().add("Black"); - } - if (types.getRed() > 0) { - choice.getChoices().add("Red"); - } - if (types.getBlue() > 0) { - choice.getChoices().add("Blue"); - } - if (types.getGreen() > 0) { - choice.getChoices().add("Green"); - } - if (types.getWhite() > 0) { - choice.getChoices().add("White"); - } - if (types.getColorless() > 0) { - choice.getChoices().add("Colorless"); - } - if (types.getAny() > 0) { - choice.getChoices().add("Black"); - choice.getChoices().add("Red"); - choice.getChoices().add("Blue"); - choice.getChoices().add("Green"); - choice.getChoices().add("White"); - choice.getChoices().add("Colorless"); - } - if (!choice.getChoices().isEmpty()) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return null; - } - if (choice.getChoices().size() == 1) { - choice.setChoice(choice.getChoices().iterator().next()); - } else { - if (!player.choose(outcome, choice, game)) { - return mana; - } - } - if (choice.getChoice() != null) { - switch (choice.getChoice()) { - case "Black": - mana.setBlack(1); - break; - case "Blue": - mana.setBlue(1); - break; - case "Red": - mana.setRed(1); - break; - case "Green": - mana.setGreen(1); - break; - case "White": - mana.setWhite(1); - break; - case "Colorless": - mana.setColorless(1); - break; - } - } - } - return mana; - } - - private Mana getManaTypes(Game game, Ability source) { - Mana types = new Mana(); - if (game != null) { - List lands = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game); - for (Permanent land : lands) { - Abilities manaAbilities = land.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD); - for (ActivatedManaAbilityImpl ability : manaAbilities) { - if (!ability.equals(source) && ability.definesMana(game)) { - for (Mana netMana : ability.getNetMana(game)) { - types.add(netMana); - } - } - } - } - } - return types; - } - - @Override - public StarCompassManaEffect copy() { - return new StarCompassManaEffect(this); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java b/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java index 81a49dc528..9e0df8ed14 100644 --- a/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java +++ b/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java @@ -12,7 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterEnchantmentPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.AnotherPredicate; @@ -47,15 +47,15 @@ public final class StarfieldOfNyx extends CardImpl { public StarfieldOfNyx(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}"); - // At the beginning of your upkeep, you may return target enchantment card + // At the beginning of your upkeep, you may return target enchantment card // from your graveyard to the battlefield. Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), TargetController.YOU, true); ability.addTarget(new TargetCardInGraveyard(filterGraveyardEnchantment)); this.addAbility(ability); - // As long as you control five or more enchantments, each other non-Aura enchantment - // you control is a creature in addition to its other types and has base power and + // As long as you control five or more enchantments, each other non-Aura enchantment + // you control is a creature in addition to its other types and has base power and // base toughness each equal to its converted mana cost. ConditionalContinuousEffect effect = new ConditionalContinuousEffect( new StarfieldOfNyxEffect(), new PermanentsOnTheBattlefieldCondition( @@ -74,13 +74,13 @@ public final class StarfieldOfNyx extends CardImpl { static class StarfieldOfNyxEffect extends ContinuousEffectImpl { - private static final FilterPermanent filter - = new FilterPermanent("Each other non-Aura enchantment you control"); - private static final FilterEnchantmentPermanent filter2 - = new FilterEnchantmentPermanent(); + private static final FilterControlledPermanent filter + = new FilterControlledPermanent("Each other non-Aura enchantment you control"); static { - filter2.add(TargetController.YOU.getControllerPredicate()); + filter.add(CardType.ENCHANTMENT.getPredicate()); + filter.add(Predicates.not(SubType.AURA.getPredicate())); + filter.add(AnotherPredicate.instance); } public StarfieldOfNyxEffect() { @@ -91,8 +91,14 @@ public final class StarfieldOfNyx extends CardImpl { this.dependendToTypes.add(DependencyType.EnchantmentAddingRemoving); // Enchanted Evening this.dependendToTypes.add(DependencyType.AuraAddingRemoving); // Cloudform + this.dependendToTypes.add(DependencyType.BecomeForest); // Song of the Dryads + this.dependendToTypes.add(DependencyType.BecomeMountain); + this.dependendToTypes.add(DependencyType.BecomePlains); + this.dependendToTypes.add(DependencyType.BecomeSwamp); + this.dependendToTypes.add(DependencyType.BecomeIsland); this.dependencyTypes.add(DependencyType.BecomeCreature); // Conspiracy + } public StarfieldOfNyxEffect(final StarfieldOfNyxEffect effect) { @@ -106,9 +112,6 @@ public final class StarfieldOfNyx extends CardImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - filter.add(CardType.ENCHANTMENT.getPredicate()); - filter.add(Predicates.not(SubType.AURA.getPredicate())); - filter.add(AnotherPredicate.instance); for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { switch (layer) { diff --git a/Mage.Sets/src/mage/cards/s/StartlingDevelopment.java b/Mage.Sets/src/mage/cards/s/StartlingDevelopment.java new file mode 100644 index 0000000000..020f29e7d5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StartlingDevelopment.java @@ -0,0 +1,45 @@ +package mage.cards.s; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StartlingDevelopment extends CardImpl { + + public StartlingDevelopment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Until end of turn, target creature becomes a blue Serpent with base power and toughness 4/4. + this.getSpellAbility().addEffect(new BecomesCreatureTargetEffect( + new CreatureToken( + 4, 4, "blue Serpent with base power and toughness 4/4" + ).withColor("U").withSubType(SubType.SERPENT), + false, false, Duration.EndOfTurn + ).setRemoveSubtypes(true)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Cycling {1} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}"))); + } + + private StartlingDevelopment(final StartlingDevelopment card) { + super(card); + } + + @Override + public StartlingDevelopment copy() { + return new StartlingDevelopment(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StaunchShieldmate.java b/Mage.Sets/src/mage/cards/s/StaunchShieldmate.java new file mode 100644 index 0000000000..68f108e0ed --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StaunchShieldmate.java @@ -0,0 +1,33 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StaunchShieldmate extends CardImpl { + + public StaunchShieldmate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.DWARF); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + } + + private StaunchShieldmate(final StaunchShieldmate card) { + super(card); + } + + @Override + public StaunchShieldmate copy() { + return new StaunchShieldmate(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SteadfastSentry.java b/Mage.Sets/src/mage/cards/s/SteadfastSentry.java index e118f863b5..8d5ecc1093 100644 --- a/Mage.Sets/src/mage/cards/s/SteadfastSentry.java +++ b/Mage.Sets/src/mage/cards/s/SteadfastSentry.java @@ -2,7 +2,7 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; @@ -31,7 +31,7 @@ public final class SteadfastSentry extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // When Steadfast Sentry dies, put a +1/+1 counter on target creature you control. - Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SteelPlumeMarshal.java b/Mage.Sets/src/mage/cards/s/SteelPlumeMarshal.java new file mode 100644 index 0000000000..0bb3a3dcc3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SteelPlumeMarshal.java @@ -0,0 +1,58 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.permanent.AttackingPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SteelPlumeMarshal extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("attacking creatures you control with flying"); + + static { + filter.add(AttackingPredicate.instance); + filter.add(new AbilityPredicate(FlyingAbility.class)); + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public SteelPlumeMarshal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Steel-Plume Marshal attacks, other attacking creatures you control with flying get +2/+2 until end of turn. + this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect( + 2, 2, Duration.EndOfTurn, filter, true + ), false)); + } + + private SteelPlumeMarshal(final SteelPlumeMarshal card) { + super(card); + } + + @Override + public SteelPlumeMarshal copy() { + return new SteelPlumeMarshal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SteelclawLance.java b/Mage.Sets/src/mage/cards/s/SteelclawLance.java index ace1b7684f..3034642fcd 100644 --- a/Mage.Sets/src/mage/cards/s/SteelclawLance.java +++ b/Mage.Sets/src/mage/cards/s/SteelclawLance.java @@ -4,7 +4,6 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.keyword.EquipAbility; -import mage.abilities.keyword.EquipFilterAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -13,6 +12,7 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; import java.util.UUID; +import mage.target.common.TargetControlledCreaturePermanent; /** * @author TheElk801 @@ -31,7 +31,7 @@ public final class SteelclawLance extends CardImpl { this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(2, 2))); // Equip Knight {1} - this.addAbility(new EquipFilterAbility(filter, new GenericManaCost(1))); + this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(1), new TargetControlledCreaturePermanent(filter))); // Equip {3} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(3))); diff --git a/Mage.Sets/src/mage/cards/s/StenchOfEvil.java b/Mage.Sets/src/mage/cards/s/StenchOfEvil.java index b20a807469..6562b791a0 100644 --- a/Mage.Sets/src/mage/cards/s/StenchOfEvil.java +++ b/Mage.Sets/src/mage/cards/s/StenchOfEvil.java @@ -1,80 +1,80 @@ -package mage.cards.s; - -import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.filter.common.FilterLandPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class StenchOfEvil extends CardImpl { - - public StenchOfEvil(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); - - // Destroy all Plains. For each land destroyed this way, Stench of Evil deals 1 damage to that land's controller unless they pay {2}. - this.getSpellAbility().addEffect(new StenchOfEvilEffect()); - - } - - private StenchOfEvil(final StenchOfEvil card) { - super(card); - } - - @Override - public StenchOfEvil copy() { - return new StenchOfEvil(this); - } -} - -class StenchOfEvilEffect extends OneShotEffect { - - private static final FilterLandPermanent filter = new FilterLandPermanent(); - - static { - filter.add(SubType.PLAINS.getPredicate()); - } - - public StenchOfEvilEffect() { - super(Outcome.Benefit); - this.staticText = "Destroy all Plains. For each land destroyed this way, {this} deals 1 damage to that land's controller unless they pay {2}"; - } - - public StenchOfEvilEffect(final StenchOfEvilEffect effect) { - super(effect); - } - - @Override - public StenchOfEvilEffect copy() { - return new StenchOfEvilEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (Permanent land : game.getBattlefield().getAllActivePermanents(filter, game)) { - UUID landControllerId = land.getControllerId(); - if (land.destroy(source.getSourceId(), game, false)) { - Cost cost = new ManaCostsImpl("{2}"); - Player landController = game.getPlayer(landControllerId); - if (landController != null - && cost.canPay(source, source.getSourceId(), landControllerId, game) - && !cost.pay(source, game, source.getSourceId(), landControllerId, false)) { - landController.damage(1, source.getSourceId(), game); - } - } - } - return true; - } -} +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.common.FilterLandPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class StenchOfEvil extends CardImpl { + + public StenchOfEvil(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); + + // Destroy all Plains. For each land destroyed this way, Stench of Evil deals 1 damage to that land's controller unless they pay {2}. + this.getSpellAbility().addEffect(new StenchOfEvilEffect()); + + } + + private StenchOfEvil(final StenchOfEvil card) { + super(card); + } + + @Override + public StenchOfEvil copy() { + return new StenchOfEvil(this); + } +} + +class StenchOfEvilEffect extends OneShotEffect { + + private static final FilterLandPermanent filter = new FilterLandPermanent(); + + static { + filter.add(SubType.PLAINS.getPredicate()); + } + + public StenchOfEvilEffect() { + super(Outcome.Benefit); + this.staticText = "Destroy all Plains. For each land destroyed this way, {this} deals 1 damage to that land's controller unless they pay {2}"; + } + + public StenchOfEvilEffect(final StenchOfEvilEffect effect) { + super(effect); + } + + @Override + public StenchOfEvilEffect copy() { + return new StenchOfEvilEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent land : game.getBattlefield().getAllActivePermanents(filter, game)) { + UUID landControllerId = land.getControllerId(); + if (land.destroy(source.getSourceId(), game, false)) { + Cost cost = new ManaCostsImpl("{2}"); + Player landController = game.getPlayer(landControllerId); + if (landController != null + && cost.canPay(source, source.getSourceId(), landControllerId, game) + && !cost.pay(source, game, source.getSourceId(), landControllerId, false)) { + landController.damage(1, source.getSourceId(), game); + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/StensiaMasquerade.java b/Mage.Sets/src/mage/cards/s/StensiaMasquerade.java index ee86d460f3..994a4ce5df 100644 --- a/Mage.Sets/src/mage/cards/s/StensiaMasquerade.java +++ b/Mage.Sets/src/mage/cards/s/StensiaMasquerade.java @@ -91,7 +91,7 @@ class StensiaMasqueradeTriggeredAbility extends TriggeredAbilityImpl { && permanent.hasSubtype(SubType.VAMPIRE, game) && permanent.isControlledBy(controllerId)) { this.getEffects().clear(); AddCountersTargetEffect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); this.addEffect(effect); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SternMentor.java b/Mage.Sets/src/mage/cards/s/SternMentor.java index f920d8448e..997d6be148 100644 --- a/Mage.Sets/src/mage/cards/s/SternMentor.java +++ b/Mage.Sets/src/mage/cards/s/SternMentor.java @@ -22,7 +22,8 @@ import mage.target.TargetPlayer; */ public final class SternMentor extends CardImpl { - private static final String ruleText = "As long as {this} is paired with another creature, each of those creatures has \"{T}: Target player puts the top two cards of their library into their graveyard.\""; + private static final String ruleText = "As long as {this} is paired with another creature, " + + "each of those creatures has \"{T}: Target player mills two cards.\""; public SternMentor(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); diff --git a/Mage.Sets/src/mage/cards/s/StitcherGeralf.java b/Mage.Sets/src/mage/cards/s/StitcherGeralf.java index 9c84a668d3..bbfc44d0d0 100644 --- a/Mage.Sets/src/mage/cards/s/StitcherGeralf.java +++ b/Mage.Sets/src/mage/cards/s/StitcherGeralf.java @@ -56,7 +56,7 @@ class StitcherGeralfEffect extends OneShotEffect { public StitcherGeralfEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "Each player puts the top three cards of their library into their graveyard. Exile up to two creature cards put into graveyards this way. Create an X/X blue Zombie creature token, where X is the total power of the cards exiled this way"; + this.staticText = "Each player mills three cards. Exile up to two creature cards put into graveyards this way. Create an X/X blue Zombie creature token, where X is the total power of the cards exiled this way"; } public StitcherGeralfEffect(final StitcherGeralfEffect effect) { @@ -76,10 +76,10 @@ class StitcherGeralfEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - cards.addAll(player.getLibrary().getTopCards(game, 3)); + cards.addAll(player.millCards(3,source,game)); } } - controller.moveCards(cards, Zone.GRAVEYARD, source, game); + cards.removeIf(uuid -> game.getState().getZone(uuid)!=Zone.GRAVEYARD); TargetCard target = new TargetCard(0, 2, Zone.GRAVEYARD, new FilterCreatureCard("creature cards to exile")); controller.chooseTarget(outcome, cards, target, source, game); int power = 0; diff --git a/Mage.Sets/src/mage/cards/s/StoicRebuttal.java b/Mage.Sets/src/mage/cards/s/StoicRebuttal.java index a934014164..629cba15b2 100644 --- a/Mage.Sets/src/mage/cards/s/StoicRebuttal.java +++ b/Mage.Sets/src/mage/cards/s/StoicRebuttal.java @@ -1,13 +1,11 @@ - - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityWord; @@ -15,20 +13,23 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.target.TargetSpell; +import java.util.UUID; + /** * @author ayrat */ public final class StoicRebuttal extends CardImpl { public StoicRebuttal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}"); // Metalcraft — Stoic Rebuttal costs {1} less to cast if you control three or more artifacts. - Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, MetalcraftCondition.instance)); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, MetalcraftCondition.instance)); ability.setRuleAtTheTop(true); ability.setAbilityWord(AbilityWord.METALCRAFT); + ability.addHint(MetalcraftHint.instance); this.addAbility(ability); - + // Counter target spell. this.getSpellAbility().addTarget(new TargetSpell()); this.getSpellAbility().addEffect(new CounterTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/s/StokeTheFlames.java b/Mage.Sets/src/mage/cards/s/StokeTheFlames.java index 39cd33c84c..bffdfd33b8 100644 --- a/Mage.Sets/src/mage/cards/s/StokeTheFlames.java +++ b/Mage.Sets/src/mage/cards/s/StokeTheFlames.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.ConvokeAbility; import mage.cards.CardImpl; @@ -9,19 +7,19 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author Quercitron */ public final class StokeTheFlames extends CardImpl { public StokeTheFlames(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}{R}"); // Convoke this.addAbility(new ConvokeAbility()); - + // Stoke the Flames deals 4 damage to any target. this.getSpellAbility().addEffect(new DamageTargetEffect(4)); this.getSpellAbility().addTarget(new TargetAnyTarget()); diff --git a/Mage.Sets/src/mage/cards/s/StolenByTheFae.java b/Mage.Sets/src/mage/cards/s/StolenByTheFae.java index 08fd09a589..9c6668ab56 100644 --- a/Mage.Sets/src/mage/cards/s/StolenByTheFae.java +++ b/Mage.Sets/src/mage/cards/s/StolenByTheFae.java @@ -27,9 +27,9 @@ public final class StolenByTheFae extends CardImpl { // Return target creature with converted mana cost X to its owner's hand. You create X 1/1 blue Faerie creature tokens with flying. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect() - .setText("Return target creature with converted mana cost X to its owner's hand.")); + .setText("Return target creature with converted mana cost X to its owner's hand")); this.getSpellAbility().addEffect(new CreateTokenEffect(new FaerieToken(), ManacostVariableValue.instance) - .setText("You create X 1/1 blue Faerie creature tokens with flying.")); + .setText("You create X 1/1 blue Faerie creature tokens with flying")); this.getSpellAbility().setTargetAdjuster(StolenByTheFaeAdjuster.instance); } diff --git a/Mage.Sets/src/mage/cards/s/StolenGoods.java b/Mage.Sets/src/mage/cards/s/StolenGoods.java index c6588a5328..0cecd5c087 100644 --- a/Mage.Sets/src/mage/cards/s/StolenGoods.java +++ b/Mage.Sets/src/mage/cards/s/StolenGoods.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -74,7 +73,7 @@ class StolenGoodsEffect extends OneShotEffect { if (card != null) { ContinuousEffect effect = new StolenGoodsCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); } return true; @@ -105,17 +104,11 @@ class StolenGoodsCastFromExileEffect extends AsThoughEffectImpl { } @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId != null && sourceId.equals(getTargetPointer().getFirst(game, source)) + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (objectId != null && objectId.equals(getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(sourceId); - if (card != null && game.getState().getZone(sourceId) == Zone.EXILED) { - Player player = game.getPlayer(affectedControllerId); - if (player != null) { - player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts()); - return true; - } - } + allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/s/StoneHavenPilgrim.java b/Mage.Sets/src/mage/cards/s/StoneHavenPilgrim.java new file mode 100644 index 0000000000..f1f2685ffb --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StoneHavenPilgrim.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StoneHavenPilgrim extends CardImpl { + + private static final Condition condition + = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT); + + public StoneHavenPilgrim(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.KOR); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Stone Haven Pilgrim attacks, if you control an artifact or enchantment, Stone Haven Pilgrim gets +1/+1 and gains lifelink until end of turn. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new AttacksTriggeredAbility( + new BoostSourceEffect(1, 1, Duration.EndOfTurn), false + ), condition, "Whenever {this} attacks, if you control an artifact or enchantment, " + + "{this} gets +1/+1 and gains lifelink until end of turn." + ); + ability.addEffect(new GainAbilitySourceEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn)); + this.addAbility(ability); + } + + private StoneHavenPilgrim(final StoneHavenPilgrim card) { + super(card); + } + + @Override + public StoneHavenPilgrim copy() { + return new StoneHavenPilgrim(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StonebrowKrosanHero.java b/Mage.Sets/src/mage/cards/s/StonebrowKrosanHero.java index 468f5855e0..c5ffaa0327 100644 --- a/Mage.Sets/src/mage/cards/s/StonebrowKrosanHero.java +++ b/Mage.Sets/src/mage/cards/s/StonebrowKrosanHero.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; import mage.abilities.effects.Effect; @@ -10,19 +9,21 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; +import java.util.UUID; + /** * * @author emerald000 */ public final class StonebrowKrosanHero extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature you control with trample"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(" creature you control with trample"); static { filter.add(new AbilityPredicate(TrampleAbility.class)); } diff --git a/Mage.Sets/src/mage/cards/s/StonybrookBanneret.java b/Mage.Sets/src/mage/cards/s/StonybrookBanneret.java index 70b28e219c..3c6a09327f 100644 --- a/Mage.Sets/src/mage/cards/s/StonybrookBanneret.java +++ b/Mage.Sets/src/mage/cards/s/StonybrookBanneret.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; @@ -14,8 +12,9 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; +import java.util.UUID; + /** - * * @author North */ public final class StonybrookBanneret extends CardImpl { @@ -29,7 +28,7 @@ public final class StonybrookBanneret extends CardImpl { } public StonybrookBanneret(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.MERFOLK); this.subtype.add(SubType.WIZARD); @@ -38,6 +37,7 @@ public final class StonybrookBanneret extends CardImpl { // Islandwalk this.addAbility(new IslandwalkAbility()); + // Merfolk spells and Wizard spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); } diff --git a/Mage.Sets/src/mage/cards/s/StormCaller.java b/Mage.Sets/src/mage/cards/s/StormCaller.java new file mode 100644 index 0000000000..0fbd4f64b1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StormCaller.java @@ -0,0 +1,41 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StormCaller extends CardImpl { + + public StormCaller(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.OGRE); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Storm Caller enters the battlefield, it deals 2 damage to each opponent. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new DamagePlayersEffect(2, TargetController.OPPONENT, "it") + )); + } + + private StormCaller(final StormCaller card) { + super(card); + } + + @Override + public StormCaller copy() { + return new StormCaller(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StormFleetAerialist.java b/Mage.Sets/src/mage/cards/s/StormFleetAerialist.java index 5b872311a2..2ce64f21fe 100644 --- a/Mage.Sets/src/mage/cards/s/StormFleetAerialist.java +++ b/Mage.Sets/src/mage/cards/s/StormFleetAerialist.java @@ -1,21 +1,22 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.common.RaidHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class StormFleetAerialist extends CardImpl { @@ -31,11 +32,13 @@ public final class StormFleetAerialist extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Raid - Storm Fleet Aerialist enters the battlefield with a +1/+1 counter on it if you attacked with a creature this turn. + // Raid - Storm Fleet Aerialist enters the battlefield with a +1/+1 counter on it if you attacked this turn. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1), false), - RaidCondition.instance, - "Raid — {this} enters the battlefield with a +1/+1 counter on it if you attacked with a creature this turn.", - "{this} enters the battlefield with a +1/+1 counter"), + RaidCondition.instance, + "Raid — {this} enters the battlefield with a +1/+1 counter on it if you attacked this turn.", + "{this} enters the battlefield with a +1/+1 counter") + .setAbilityWord(AbilityWord.RAID) + .addHint(RaidHint.instance), new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/StormFleetArsonist.java b/Mage.Sets/src/mage/cards/s/StormFleetArsonist.java index cee0f71bf4..cad88cdba2 100644 --- a/Mage.Sets/src/mage/cards/s/StormFleetArsonist.java +++ b/Mage.Sets/src/mage/cards/s/StormFleetArsonist.java @@ -1,23 +1,24 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.SacrificeEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.target.common.TargetOpponent; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class StormFleetArsonist extends CardImpl { @@ -30,12 +31,14 @@ public final class StormFleetArsonist extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // Raid - When Storm Fleet Arsonist enters the battlefield, if you attacked with a creature this turn, target opponent sacrifices a permanent. + // Raid - When Storm Fleet Arsonist enters the battlefield, if you attacked this turn, target opponent sacrifices a permanent. Ability ability = new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new SacrificeEffect(new FilterPermanent(), 1, "Target opponent")), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, target opponent sacrifices a permanent."); + "Raid — When {this} enters the battlefield, if you attacked this turn, target opponent sacrifices a permanent."); ability.addTarget(new TargetOpponent()); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/StormFleetPyromancer.java b/Mage.Sets/src/mage/cards/s/StormFleetPyromancer.java index ee36874684..444490c854 100644 --- a/Mage.Sets/src/mage/cards/s/StormFleetPyromancer.java +++ b/Mage.Sets/src/mage/cards/s/StormFleetPyromancer.java @@ -1,22 +1,23 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetAnyTarget; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class StormFleetPyromancer extends CardImpl { @@ -30,12 +31,14 @@ public final class StormFleetPyromancer extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - // Raid - When Storm Fleet Pyromancer enters the battlefield, if you attacked with a creature this turn, Storm Fleet Pyromancer deals 2 damage to any target. + // Raid - When Storm Fleet Pyromancer enters the battlefield, if you attacked this turn, Storm Fleet Pyromancer deals 2 damage to any target. Ability ability = new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(2)), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, {this} deals 2 damage to any target."); + "Raid — When {this} enters the battlefield, if you attacked this turn, {this} deals 2 damage to any target."); ability.addTarget(new TargetAnyTarget()); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/StormFleetSpy.java b/Mage.Sets/src/mage/cards/s/StormFleetSpy.java index c3b029d426..f69fca61a7 100644 --- a/Mage.Sets/src/mage/cards/s/StormFleetSpy.java +++ b/Mage.Sets/src/mage/cards/s/StormFleetSpy.java @@ -1,21 +1,22 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class StormFleetSpy extends CardImpl { @@ -28,11 +29,13 @@ public final class StormFleetSpy extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // Raid — When Storm Fleet Spy enters the battlefield, if you attacked with a creature this turn, draw a card. + // Raid — When Storm Fleet Spy enters the battlefield, if you attacked this turn, draw a card. Ability ability = new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, draw a card."); + "Raid — When {this} enters the battlefield, if you attacked this turn, draw a card."); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/StormriderRig.java b/Mage.Sets/src/mage/cards/s/StormriderRig.java index 0e2e449de6..409469791b 100644 --- a/Mage.Sets/src/mage/cards/s/StormriderRig.java +++ b/Mage.Sets/src/mage/cards/s/StormriderRig.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.SetTargetPointer; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -33,7 +33,7 @@ public final class StormriderRig extends CardImpl { // Whenever a creature enters the battlefield under your control, you may attach Stormrider Rig to it. this.addAbility(new EntersBattlefieldAllTriggeredAbility( Zone.BATTLEFIELD, new AttachEffect(Outcome.Detriment, "attach {source} to it"), - new FilterControlledCreaturePermanent("a creature"), true, SetTargetPointer.PERMANENT, null, true)); + StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, true, SetTargetPointer.PERMANENT, null, true)); // Equip {2} this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(2))); diff --git a/Mage.Sets/src/mage/cards/s/StormscapeFamiliar.java b/Mage.Sets/src/mage/cards/s/StormscapeFamiliar.java index 31f5d31778..d09f512d5f 100644 --- a/Mage.Sets/src/mage/cards/s/StormscapeFamiliar.java +++ b/Mage.Sets/src/mage/cards/s/StormscapeFamiliar.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; @@ -16,8 +14,9 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author North */ public final class StormscapeFamiliar extends CardImpl { @@ -31,7 +30,7 @@ public final class StormscapeFamiliar extends CardImpl { } public StormscapeFamiliar(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.BIRD); this.power = new MageInt(1); @@ -39,6 +38,7 @@ public final class StormscapeFamiliar extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // White spells and black spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); } diff --git a/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java b/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java index f28520f181..50b685b935 100644 --- a/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java +++ b/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.abilityword.LieutenantAbility; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; @@ -32,7 +32,7 @@ public final class StormsurgeKraken extends CardImpl { this.addAbility(HexproofAbility.getInstance()); // Lieutenant - As long as you control your commander, Stormsurge Kraken gets +2/+2 and has "Whenever Stormsurge Kraken becomes blocked, you may draw two cards." - ContinuousEffect effect = new GainAbilitySourceEffect(new BecomesBlockedTriggeredAbility(new DrawCardSourceControllerEffect(2), true), Duration.WhileOnBattlefield); + ContinuousEffect effect = new GainAbilitySourceEffect(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(2), true), Duration.WhileOnBattlefield); effect.setText("and has \"Whenever Stormsurge Kraken becomes blocked, you may draw two cards.\""); this.addAbility(new LieutenantAbility(effect)); } diff --git a/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java b/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java index 40e76151ee..0c46713eaf 100644 --- a/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java +++ b/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java @@ -91,7 +91,7 @@ public final class StormtideLeviathan extends CardImpl { // land abilities are intrinsic, so add them here, not in layer 6 if (!land.hasSubtype(SubType.ISLAND, game)) { land.getSubtype(game).add(SubType.ISLAND); - if (!land.getAbilities(game).contains(new BlueManaAbility())) { + if (!land.getAbilities(game).containsClass(BlueManaAbility.class)) { land.addAbility(new BlueManaAbility(), source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/cards/s/StormwildCapridor.java b/Mage.Sets/src/mage/cards/s/StormwildCapridor.java new file mode 100644 index 0000000000..ac782e8c43 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StormwildCapridor.java @@ -0,0 +1,98 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.PreventionEffectData; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.DamageCreatureEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StormwildCapridor extends CardImpl { + + public StormwildCapridor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.GOAT); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // If noncombat damage would be dealt to Stormwild Capridor, prevent that damage. + // Put a +1/+1 counter on Stormwild Capridor for each 1 damage prevented this way. + this.addAbility(new SimpleStaticAbility(new StormwildCapridorEffect())); + } + + private StormwildCapridor(final StormwildCapridor card) { + super(card); + } + + @Override + public StormwildCapridor copy() { + return new StormwildCapridor(this); + } +} + +class StormwildCapridorEffect extends PreventionEffectImpl { + + StormwildCapridorEffect() { + super(Duration.WhileOnBattlefield); + staticText = "If noncombat damage would be dealt to {this}, prevent that damage. " + + "Put a +1/+1 counter on {this} for each 1 damage prevented this way"; + } + + private StormwildCapridorEffect(final StormwildCapridorEffect effect) { + super(effect); + } + + @Override + public StormwildCapridorEffect copy() { + return new StormwildCapridorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + PreventionEffectData preventionEffectData = preventDamageAction(event, source, game); + if (preventionEffectData.getPreventedDamage() > 0) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(preventionEffectData.getPreventedDamage()), source, game); + } + return true; + } + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!event.getTargetId().equals(source.getSourceId()) + || !super.applies(event, source, game)) { + return false; + } + DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + return !damageEvent.isCombatDamage(); + } + +} diff --git a/Mage.Sets/src/mage/cards/s/StormwingEntity.java b/Mage.Sets/src/mage/cards/s/StormwingEntity.java new file mode 100644 index 0000000000..45e90a6e14 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StormwingEntity.java @@ -0,0 +1,108 @@ +package mage.cards.s; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.watchers.Watcher; + +/** + * @author TheElk801 + */ +public final class StormwingEntity extends CardImpl { + + private static final ConditionHint hint = new ConditionHint( + StormwingEntityCondition.instance, "You cast an instant or sorcery spell this turn" + ); + + public StormwingEntity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // This spell costs {2}{U} less to cast if you've cast an instant or sorcery spell this turn. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect( + new ManaCostsImpl("{2}{U}"), StormwingEntityCondition.instance + )).setRuleAtTheTop(true).addHint(hint), new StormwingEntityWatcher()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Prowess + this.addAbility(new ProwessAbility()); + + // When Stormwing Entity enters the battlefield, scry 2. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(2))); + } + + private StormwingEntity(final StormwingEntity card) { + super(card); + } + + @Override + public StormwingEntity copy() { + return new StormwingEntity(this); + } +} + +enum StormwingEntityCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + StormwingEntityWatcher watcher = game.getState().getWatcher(StormwingEntityWatcher.class); + return watcher != null && watcher.checkPlayer(source.getControllerId()); + } +} + +class StormwingEntityWatcher extends Watcher { + + private final Set playerMap = new HashSet<>(); + + StormwingEntityWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null || !spell.isInstantOrSorcery()) { + return; + } + playerMap.add(event.getPlayerId()); + } + + @Override + public void reset() { + playerMap.clear(); + super.reset(); + } + + boolean checkPlayer(UUID playerId) { + return playerMap.contains(playerId); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Stratadon.java b/Mage.Sets/src/mage/cards/s/Stratadon.java index 3394c00fb7..ce3a704473 100644 --- a/Mage.Sets/src/mage/cards/s/Stratadon.java +++ b/Mage.Sets/src/mage/cards/s/Stratadon.java @@ -32,7 +32,7 @@ public final class Stratadon extends CardImpl { this.toughness = new MageInt(5); // Domain - Stratadon costs {1} less to cast for each basic land type among lands you control. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new StratadonCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new StratadonCostReductionEffect())); // Trample this.addAbility(TrampleAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java b/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java index 24ac6e0dc4..3771b67568 100644 --- a/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java +++ b/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java @@ -1,11 +1,13 @@ - package mage.cards.s; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; @@ -16,9 +18,6 @@ import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetCardInGraveyard; -import java.util.List; -import java.util.UUID; - /** * * @author LevelX2 @@ -26,7 +25,7 @@ import java.util.UUID; public final class StreamOfConsciousness extends CardImpl { public StreamOfConsciousness(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); this.subtype.add(SubType.ARCANE); // Target player shuffles up to four target cards from their graveyard into their library. @@ -64,24 +63,11 @@ class StreamOfConsciousnessEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); if (player != null) { - List targets = source.getTargets().get(1).getTargets(); - boolean shuffle = false; - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - if (player.getGraveyard().contains(card.getId())) { - player.getGraveyard().remove(card); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - shuffle = true; - } - } - } - if (shuffle) { - player.shuffleLibrary(source, game); - } - return true; + Cards targets = new CardsImpl(source.getTargets().get(1).getTargets()); + targets.retainAll(player.getGraveyard()); + return player.shuffleCardsToLibrary(targets, game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/StrengthOfTheTajuru.java b/Mage.Sets/src/mage/cards/s/StrengthOfTheTajuru.java index 73a007d69f..49e269ffd8 100644 --- a/Mage.Sets/src/mage/cards/s/StrengthOfTheTajuru.java +++ b/Mage.Sets/src/mage/cards/s/StrengthOfTheTajuru.java @@ -58,7 +58,7 @@ class StrengthOfTheTajuruAddCountersTargetEffect extends OneShotEffect { public StrengthOfTheTajuruAddCountersTargetEffect() { super(Outcome.BoostCreature); - staticText = "Choose target creature, then choose another target creature for each time Strength of the Tajuru was kicked. Put X +1/+1 counters on each of them"; + staticText = "Choose target creature, then choose another target creature for each time this spell was kicked. Put X +1/+1 counters on each of them"; } public StrengthOfTheTajuruAddCountersTargetEffect(final StrengthOfTheTajuruAddCountersTargetEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/StromgaldSpy.java b/Mage.Sets/src/mage/cards/s/StromgaldSpy.java index 94ead75870..8bb0be3207 100644 --- a/Mage.Sets/src/mage/cards/s/StromgaldSpy.java +++ b/Mage.Sets/src/mage/cards/s/StromgaldSpy.java @@ -5,6 +5,8 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; import mage.constants.SubType; @@ -15,6 +17,7 @@ import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -32,8 +35,14 @@ public final class StromgaldSpy extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(4); - // Whenever Stromgald Spy attacks and isn't blocked, you may have defending player play with their hand revealed for as long as Stromgald Spy remains on the battlefield. If you do, Stromgald Spy assigns no combat damage this turn. - Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(new StromgaldSpyEffect(), true, true); + // Whenever Stromgald Spy attacks and isn't blocked, you may have defending player play with their hand revealed + // for as long as Stromgald Spy remains on the battlefield. If you do, Stromgald Spy assigns no combat damage this turn. + Ability ability = new AttacksAndIsNotBlockedTriggeredAbility( + new ConditionalContinuousEffect( + new StromgaldSpyEffect(), + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), + "you may have defending player play with their hand revealed for as long as {this} remains on the battlefield"), + true, true); ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true)); this.addAbility(ability); } @@ -51,8 +60,7 @@ public final class StromgaldSpy extends CardImpl { class StromgaldSpyEffect extends ContinuousEffectImpl { public StromgaldSpyEffect() { - super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Detriment); - this.staticText = "you may have defending player play with their hand revealed for as long as Stromgald Spy remains on the battlefield"; + super(Duration.Custom, Layer.PlayerEffects, SubLayer.NA, Outcome.Detriment); } public StromgaldSpyEffect(final StromgaldSpyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/StruggleSurvive.java b/Mage.Sets/src/mage/cards/s/StruggleSurvive.java index f9a9b6428a..ca7b072012 100644 --- a/Mage.Sets/src/mage/cards/s/StruggleSurvive.java +++ b/Mage.Sets/src/mage/cards/s/StruggleSurvive.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -8,14 +7,11 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.AftermathAbility; -import mage.cards.Card; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SpellAbilityType; -import mage.constants.Zone; import mage.filter.common.FilterControlledLandPermanent; import mage.game.Game; import mage.players.Player; @@ -69,16 +65,11 @@ class SurviveEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player sourcePlayer = game.getPlayer(source.getControllerId()); - if(sourcePlayer != null) { + if (sourcePlayer != null) { for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - for (Card card : player.getGraveyard().getCards(game)) { - if(card != null) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - } - player.shuffleLibrary(source, game); + player.shuffleCardsToLibrary(player.getGraveyard(), game, source); } } } diff --git a/Mage.Sets/src/mage/cards/s/StunningReversal.java b/Mage.Sets/src/mage/cards/s/StunningReversal.java index ed6eb68610..9f4cd5e2f6 100644 --- a/Mage.Sets/src/mage/cards/s/StunningReversal.java +++ b/Mage.Sets/src/mage/cards/s/StunningReversal.java @@ -65,7 +65,7 @@ class StunningReversalEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getPlayerId()); if (player != null) { - player.drawCards(7, game); + player.drawCards(7, source.getSourceId(), game); player.setLife(1, game, source); this.discard(); } diff --git a/Mage.Sets/src/mage/cards/s/SuChi.java b/Mage.Sets/src/mage/cards/s/SuChi.java index b8c58ef91c..d59bc7e049 100644 --- a/Mage.Sets/src/mage/cards/s/SuChi.java +++ b/Mage.Sets/src/mage/cards/s/SuChi.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.Mana; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.mana.BasicManaEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class SuChi extends CardImpl { this.toughness = new MageInt(4); // When Su-Chi dies, add {C}{C}{C}{C}. - this.addAbility(new DiesTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(4)), false)); + this.addAbility(new DiesSourceTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(4)), false)); } public SuChi(final SuChi card) { diff --git a/Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java b/Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java new file mode 100644 index 0000000000..15a359a4fd --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java @@ -0,0 +1,119 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardHandCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SubiraTulzidiCaravanner extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("another creature with power 2 or less"); + + static { + filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(AnotherPredicate.instance); + } + + public SubiraTulzidiCaravanner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // {1}: Another target creature with power 2 or less can't be blocked this turn. + Ability ability = new SimpleActivatedAbility( + new CantBeBlockedTargetEffect(Duration.EndOfTurn) + .setText("another target creature with power 2 or less can't be blocked this turn"), + new GenericManaCost(1) + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {1}{R}, {T}, Discard your hand: Until end of turn, whenever a creature you control with power 2 or less deals combat damage to a player, draw a card. + ability = new SimpleActivatedAbility(new CreateDelayedTriggeredAbilityEffect( + new SubiraTulzidiCaravannerAbility() + ), new ManaCostsImpl("{1}{R}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardHandCost()); + this.addAbility(ability); + } + + private SubiraTulzidiCaravanner(final SubiraTulzidiCaravanner card) { + super(card); + } + + @Override + public SubiraTulzidiCaravanner copy() { + return new SubiraTulzidiCaravanner(this); + } +} + +class SubiraTulzidiCaravannerAbility extends DelayedTriggeredAbility { + + SubiraTulzidiCaravannerAbility() { + super(new DrawCardSourceControllerEffect(1), Duration.EndOfTurn, false, false); + } + + private SubiraTulzidiCaravannerAbility(final SubiraTulzidiCaravannerAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event; + if (!dEvent.isCombatDamage()) { + return false; + } + Permanent permanent = game.getPermanent(event.getSourceId()); + return permanent != null + && permanent.isControlledBy(getControllerId()) + && permanent.isCreature() + && permanent.getPower().getValue() <= 2; + } + + @Override + public SubiraTulzidiCaravannerAbility copy() { + return new SubiraTulzidiCaravannerAbility(this); + } + + @Override + public String getRule() { + return "Until end of turn, whenever a creature you control with power 2 or less deals combat damage to a player, draw a card."; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SublimeEpiphany.java b/Mage.Sets/src/mage/cards/s/SublimeEpiphany.java new file mode 100644 index 0000000000..39c8cbbc7f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SublimeEpiphany.java @@ -0,0 +1,64 @@ +package mage.cards.s; + +import mage.abilities.Mode; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.TargetPlayer; +import mage.target.TargetSpell; +import mage.target.common.TargetActivatedOrTriggeredAbility; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SublimeEpiphany extends CardImpl { + + public SublimeEpiphany(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}{U}"); + + // Choose one or more — + this.getSpellAbility().getModes().setMinModes(1); + this.getSpellAbility().getModes().setMaxModes(5); + + // • Counter target spell + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); + + // • Counter target activated or triggered ability. + Mode mode = new Mode(new CounterTargetEffect()); + mode.addTarget(new TargetActivatedOrTriggeredAbility()); + this.getSpellAbility().addMode(mode); + + // • Return target nonland permanent to its owner's hand. + mode = new Mode(new ReturnToHandTargetEffect()); + mode.addTarget(new TargetNonlandPermanent()); + this.getSpellAbility().addMode(mode); + + // • Create a token that's a copy of target creature you control. + mode = new Mode(new CreateTokenCopyTargetEffect()); + mode.addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addMode(mode); + + // • Target player draws a card. + mode = new Mode(new DrawCardTargetEffect(1)); + mode.addTarget(new TargetPlayer()); + this.getSpellAbility().addMode(mode); + } + + private SublimeEpiphany(final SublimeEpiphany card) { + super(card); + } + + @Override + public SublimeEpiphany copy() { + return new SublimeEpiphany(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SuddenDisappearance.java b/Mage.Sets/src/mage/cards/s/SuddenDisappearance.java index eef0c4e5e9..13e8b37088 100644 --- a/Mage.Sets/src/mage/cards/s/SuddenDisappearance.java +++ b/Mage.Sets/src/mage/cards/s/SuddenDisappearance.java @@ -1,20 +1,12 @@ - package mage.cards.s; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.common.FilterNonlandPermanent; @@ -23,8 +15,11 @@ import mage.players.Player; import mage.target.TargetPlayer; import mage.target.targetpointer.FixedTargets; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class SuddenDisappearance extends CardImpl { @@ -73,7 +68,7 @@ class SuddenDisappearanceEffect extends OneShotEffect { for (Card card : permsSet) { targets.add(card.getId()); } - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, true); effect.setText("Return the exiled cards to the battlefield under their owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTargets(targets, game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/s/SuddenSpinnerets.java b/Mage.Sets/src/mage/cards/s/SuddenSpinnerets.java new file mode 100644 index 0000000000..5b744e6762 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuddenSpinnerets.java @@ -0,0 +1,38 @@ +package mage.cards.s; + +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SuddenSpinnerets extends CardImpl { + + public SuddenSpinnerets(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + + // Target creature gets +1/+3 until end of turn. Put a reach counter on it. Untap it. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 3)); + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.REACH.createInstance()) + .setText("Put a reach counter on it")); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap it")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private SuddenSpinnerets(final SuddenSpinnerets card) { + super(card); + } + + @Override + public SuddenSpinnerets copy() { + return new SuddenSpinnerets(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SuffocatingFumes.java b/Mage.Sets/src/mage/cards/s/SuffocatingFumes.java new file mode 100644 index 0000000000..b3821fba0d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuffocatingFumes.java @@ -0,0 +1,43 @@ +package mage.cards.s; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SuffocatingFumes extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterOpponentsCreaturePermanent("creatures your opponents control"); + + public SuffocatingFumes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); + + // Creatures your opponents control get -1/-1 until end of turn. + this.getSpellAbility().addEffect(new BoostAllEffect( + -1, -1, Duration.EndOfTurn, filter, false + )); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private SuffocatingFumes(final SuffocatingFumes card) { + super(card); + } + + @Override + public SuffocatingFumes copy() { + return new SuffocatingFumes(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SultaiEmissary.java b/Mage.Sets/src/mage/cards/s/SultaiEmissary.java index cb615a363b..29c91953fd 100644 --- a/Mage.Sets/src/mage/cards/s/SultaiEmissary.java +++ b/Mage.Sets/src/mage/cards/s/SultaiEmissary.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.keyword.ManifestEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class SultaiEmissary extends CardImpl { this.toughness = new MageInt(1); // When Sultai Emissary dies, manifest the top card of your library. - this.addAbility(new DiesTriggeredAbility(new ManifestEffect(1))); + this.addAbility(new DiesSourceTriggeredAbility(new ManifestEffect(1))); } public SultaiEmissary(final SultaiEmissary card) { diff --git a/Mage.Sets/src/mage/cards/s/SummonersEgg.java b/Mage.Sets/src/mage/cards/s/SummonersEgg.java index 055eb81237..d4d249ec2c 100644 --- a/Mage.Sets/src/mage/cards/s/SummonersEgg.java +++ b/Mage.Sets/src/mage/cards/s/SummonersEgg.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -36,7 +36,7 @@ public final class SummonersEgg extends CardImpl { // Imprint - When Summoner's Egg enters the battlefield, you may exile a card from your hand face down. this.addAbility(new EntersBattlefieldTriggeredAbility(new SummonersEggImprintEffect(), true, "Imprint — ")); // When Summoner's Egg dies, turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control. - this.addAbility(new DiesTriggeredAbility(new SummonersEggPutOntoBattlefieldEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new SummonersEggPutOntoBattlefieldEffect())); } public SummonersEgg(final SummonersEgg card) { diff --git a/Mage.Sets/src/mage/cards/s/SunTitan.java b/Mage.Sets/src/mage/cards/s/SunTitan.java index dc1625aebf..7a98c2b9e6 100644 --- a/Mage.Sets/src/mage/cards/s/SunTitan.java +++ b/Mage.Sets/src/mage/cards/s/SunTitan.java @@ -39,7 +39,7 @@ public final class SunTitan extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Whenever Sun Titan enters the battlefield or attacks, you may return target permanent card with converted mana cost 3 or less from your graveyard to the battlefield. - Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), true); + Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), true); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/Suncleanser.java b/Mage.Sets/src/mage/cards/s/Suncleanser.java index a1ec53e415..8e31d29e69 100644 --- a/Mage.Sets/src/mage/cards/s/Suncleanser.java +++ b/Mage.Sets/src/mage/cards/s/Suncleanser.java @@ -67,9 +67,9 @@ class SuncleanserRemoveCountersEffect extends OneShotEffect { public SuncleanserRemoveCountersEffect(boolean player) { super(Outcome.Benefit); if (player) { - staticText = "Target opponent loses all counters."; + staticText = "Target opponent loses all counters"; } else { - staticText = "Remove all counters from target creature."; + staticText = "Remove all counters from target creature"; } } diff --git a/Mage.Sets/src/mage/cards/s/SunderingStroke.java b/Mage.Sets/src/mage/cards/s/SunderingStroke.java index fb3feb97f9..da329bc299 100644 --- a/Mage.Sets/src/mage/cards/s/SunderingStroke.java +++ b/Mage.Sets/src/mage/cards/s/SunderingStroke.java @@ -32,7 +32,7 @@ public final class SunderingStroke extends CardImpl { )); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(7, 3)); this.getSpellAbility().addHint(new StaticHint( - "(You have to choose how 7 damage is divided even if you spend seven red mana)" + "(You have to choose how 7 damage is divided even if you spend seven red mana.)" )); } diff --git a/Mage.Sets/src/mage/cards/s/SunscapeFamiliar.java b/Mage.Sets/src/mage/cards/s/SunscapeFamiliar.java index 420701d6d9..dfacbc0626 100644 --- a/Mage.Sets/src/mage/cards/s/SunscapeFamiliar.java +++ b/Mage.Sets/src/mage/cards/s/SunscapeFamiliar.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; @@ -16,8 +14,9 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author North */ public final class SunscapeFamiliar extends CardImpl { @@ -31,7 +30,7 @@ public final class SunscapeFamiliar extends CardImpl { } public SunscapeFamiliar(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add(SubType.WALL); this.power = new MageInt(0); @@ -39,6 +38,7 @@ public final class SunscapeFamiliar extends CardImpl { // Defender this.addAbility(DefenderAbility.getInstance()); + // Green spells and blue spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); } diff --git a/Mage.Sets/src/mage/cards/s/SuperDuperDeathRay.java b/Mage.Sets/src/mage/cards/s/SuperDuperDeathRay.java new file mode 100644 index 0000000000..80994ec375 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuperDuperDeathRay.java @@ -0,0 +1,85 @@ +package mage.cards.s; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +import static mage.game.combat.CombatGroup.getLethalDamage; + +/** + * @author TheElk801 + */ +public final class SuperDuperDeathRay extends CardImpl { + + public SuperDuperDeathRay(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Trample + this.addAbility(new SimpleStaticAbility(new InfoEffect( + "Trample (This spell can deal excess damage to its target's controller.)" + ))); + + // Super-Duper Death Ray deals 4 damage to target creature. + this.getSpellAbility().addEffect(new SuperDuperDeathRayEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private SuperDuperDeathRay(final SuperDuperDeathRay card) { + super(card); + } + + @Override + public SuperDuperDeathRay copy() { + return new SuperDuperDeathRay(this); + } +} + +class SuperDuperDeathRayEffect extends OneShotEffect { + + SuperDuperDeathRayEffect() { + super(Outcome.Benefit); + staticText = "{this} deals 4 damage to target creature."; + } + + private SuperDuperDeathRayEffect(final SuperDuperDeathRayEffect effect) { + super(effect); + } + + @Override + public SuperDuperDeathRayEffect copy() { + return new SuperDuperDeathRayEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + MageObject sourceObject = source.getSourceObject(game); + if (permanent == null || sourceObject == null) { + return false; + } + int lethal = getLethalDamage(permanent, game); + if (sourceObject.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { + lethal = Math.min(lethal, 1); + } + lethal = Math.min(lethal, 4); + permanent.damage(lethal, source.getSourceId(), game); + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null && lethal < 4) { + player.damage(4 - lethal, source.getSourceId(), game); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SupernaturalStamina.java b/Mage.Sets/src/mage/cards/s/SupernaturalStamina.java index 3f67103eff..b4667f2ab7 100644 --- a/Mage.Sets/src/mage/cards/s/SupernaturalStamina.java +++ b/Mage.Sets/src/mage/cards/s/SupernaturalStamina.java @@ -1,7 +1,7 @@ package mage.cards.s; import java.util.UUID; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -26,7 +26,7 @@ public final class SupernaturalStamina extends CardImpl { .setText("Until end of turn, target creature gets +2/+0") ); getSpellAbility().addEffect(new GainAbilityTargetEffect( - new DiesTriggeredAbility( + new DiesSourceTriggeredAbility( new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false), Duration.EndOfTurn, diff --git a/Mage.Sets/src/mage/cards/s/SupplyRunners.java b/Mage.Sets/src/mage/cards/s/SupplyRunners.java new file mode 100644 index 0000000000..72ed35e23b --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SupplyRunners.java @@ -0,0 +1,50 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SupplyRunners extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("other creature you control"); + + static { + filter.add(AnotherPredicate.instance); + } + + public SupplyRunners(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); + + this.subtype.add(SubType.DOG); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Supply Runners enters the battlefield, put a +1/+1 counter on each other creature you control. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter) + )); + } + + private SupplyRunners(final SupplyRunners card) { + super(card); + } + + @Override + public SupplyRunners copy() { + return new SupplyRunners(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SupremeVerdict.java b/Mage.Sets/src/mage/cards/s/SupremeVerdict.java index e7249c4f7b..7dd6932c06 100644 --- a/Mage.Sets/src/mage/cards/s/SupremeVerdict.java +++ b/Mage.Sets/src/mage/cards/s/SupremeVerdict.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.common.DestroyAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -21,7 +21,7 @@ public final class SupremeVerdict extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}{W}{U}"); // Supreme Verdict can't be countered. - Ability ability = new CantBeCounteredAbility(); + Ability ability = new CantBeCounteredSourceAbility(); ability.setRuleAtTheTop(true); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java b/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java index d413aa2df3..dbecb75abc 100644 --- a/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java +++ b/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java @@ -56,8 +56,10 @@ public final class SurgicalExtraction extends CardImpl { class SurgicalExtractionEffect extends OneShotEffect { public SurgicalExtractionEffect() { - super(Outcome.Exile); - this.staticText = "Choose target card in a graveyard other than a basic land card. Search its owner's graveyard, hand, and library for any number of cards with the same name as that card and exile them. Then that player shuffles their library"; + super(Outcome.Detriment); + this.staticText = "Choose target card in a graveyard other than a basic land card. " + + "Search its owner's graveyard, hand, and library for any number of cards " + + "with the same name as that card and exile them. Then that player shuffles their library"; } public SurgicalExtractionEffect(final SurgicalExtractionEffect effect) { @@ -80,21 +82,24 @@ class SurgicalExtractionEffect extends OneShotEffect { if (chosenCard != null && controller != null) { Player owner = game.getPlayer(chosenCard.getOwnerId()); if (owner != null) { - String nameToSearch = chosenCard.isSplitCard() ? ((SplitCard) chosenCard).getLeftHalfCard().getName() : chosenCard.getName(); + String nameToSearch = chosenCard.isSplitCard() + ? ((SplitCard) chosenCard).getLeftHalfCard().getName() : chosenCard.getName(); FilterCard filterNamedCard = new FilterCard("card named " + nameToSearch); filterNamedCard.add(new NamePredicate(nameToSearch)); // cards in Graveyard int cardsCount = owner.getGraveyard().count(filterNamedCard, game); if (cardsCount > 0) { - filterNamedCard.setMessage("card named " + nameToSearch + " in the graveyard of " + owner.getName()); + filterNamedCard.setMessage("card named " + nameToSearch + + " in the graveyard of " + owner.getName()); TargetCardInGraveyard target = new TargetCardInGraveyard(0, cardsCount, filterNamedCard); if (controller.chooseTarget(Outcome.Exile, owner.getGraveyard(), target, source, game)) { List targets = target.getTargets(); for (UUID targetId : targets) { Card targetCard = owner.getGraveyard().get(targetId, game); if (targetCard != null) { - controller.moveCardToExileWithInfo(targetCard, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true); + controller.moveCardToExileWithInfo(targetCard, null, + "", source.getSourceId(), game, Zone.GRAVEYARD, true); } } } diff --git a/Mage.Sets/src/mage/cards/s/SurlyBadgersaur.java b/Mage.Sets/src/mage/cards/s/SurlyBadgersaur.java new file mode 100644 index 0000000000..c21c5e6df6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SurlyBadgersaur.java @@ -0,0 +1,72 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DiscardCardControllerTriggeredAbility; +import mage.abilities.effects.common.FightTargetSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterNonlandCard; +import mage.filter.predicate.Predicates; +import mage.game.permanent.token.TreasureToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SurlyBadgersaur extends CardImpl { + + private static final FilterCard filter = new FilterNonlandCard("a noncreature, nonland card"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public SurlyBadgersaur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.BADGER); + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever you discard a creature card, put a +1/+1 counter on Surly Badgersaur. + this.addAbility(new DiscardCardControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + false, StaticFilters.FILTER_CARD_CREATURE_A + )); + + // Whenever you discard a land card, create a treasure token. + this.addAbility(new DiscardCardControllerTriggeredAbility( + new CreateTokenEffect(new TreasureToken()), + false, StaticFilters.FILTER_CARD_LAND_A + )); + + // Whenever you discard a noncreature, nonland card, Surly Badgersaur fights up to one target creature you don't control. + Ability ability = new DiscardCardControllerTriggeredAbility( + new FightTargetSourceEffect().setText( + "{this} fights up to one target creature you don't control" + ), false, filter + ); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); + this.addAbility(ability); + } + + private SurlyBadgersaur(final SurlyBadgersaur card) { + super(card); + } + + @Override + public SurlyBadgersaur copy() { + return new SurlyBadgersaur(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java b/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java index bb5cfb8624..8b38d589d5 100644 --- a/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java +++ b/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CantBeCounteredControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -44,7 +44,7 @@ public final class SurrakDragonclaw extends CardImpl { this.addAbility(FlashAbility.getInstance()); // Surrak Dragonclaw can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Creature spells you control can't be countered. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeCounteredControlledEffect(filterTarget, null, Duration.WhileOnBattlefield))); diff --git a/Mage.Sets/src/mage/cards/s/SurveillingSprite.java b/Mage.Sets/src/mage/cards/s/SurveillingSprite.java index 988c83be6f..b27d88f50e 100644 --- a/Mage.Sets/src/mage/cards/s/SurveillingSprite.java +++ b/Mage.Sets/src/mage/cards/s/SurveillingSprite.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class SurveillingSprite extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Surveilling Sprite dies, you may draw a card. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); } public SurveillingSprite(final SurveillingSprite card) { diff --git a/Mage.Sets/src/mage/cards/s/SurvivalCache.java b/Mage.Sets/src/mage/cards/s/SurvivalCache.java index d780249ce8..1a80b98e86 100644 --- a/Mage.Sets/src/mage/cards/s/SurvivalCache.java +++ b/Mage.Sets/src/mage/cards/s/SurvivalCache.java @@ -62,7 +62,7 @@ class SurvivalCacheEffect extends OneShotEffect { } } if (haveMoreLife) - sourcePlayer.drawCards(1, game); + sourcePlayer.drawCards(1, source.getSourceId(), game); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/SurvivorOfTheUnseen.java b/Mage.Sets/src/mage/cards/s/SurvivorOfTheUnseen.java index 70ec11cf45..277de3506c 100644 --- a/Mage.Sets/src/mage/cards/s/SurvivorOfTheUnseen.java +++ b/Mage.Sets/src/mage/cards/s/SurvivorOfTheUnseen.java @@ -71,7 +71,7 @@ class SurvivorOfTheUnseenEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(2, game); + player.drawCards(2, source.getSourceId(), game); putOnLibrary(player, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SurvivorsBond.java b/Mage.Sets/src/mage/cards/s/SurvivorsBond.java new file mode 100644 index 0000000000..260a87e42a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SurvivorsBond.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.abilities.Mode; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SurvivorsBond extends CardImpl { + + private static final FilterCard filter + = new FilterCreatureCard("Human creature card from your graveyard"); + private static final FilterCard filter2 + = new FilterCreatureCard("non-Human creature card from your graveyard"); + + static { + filter.add(SubType.HUMAN.getPredicate()); + filter2.add(Predicates.not(SubType.HUMAN.getPredicate())); + } + + public SurvivorsBond(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); + + // Choose one or both — + this.getSpellAbility().getModes().setMinModes(1); + this.getSpellAbility().getModes().setMaxModes(2); + + // • Return target Human creature card from your graveyard to your hand. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(filter)); + + // • Return target non-Human creature card from your graveyard to your hand. + Mode mode = new Mode(new ReturnFromGraveyardToHandTargetEffect()); + mode.addTarget(new TargetCardInYourGraveyard(filter2)); + this.getSpellAbility().addMode(mode); + } + + private SurvivorsBond(final SurvivorsBond card) { + super(card); + } + + @Override + public SurvivorsBond copy() { + return new SurvivorsBond(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SwaggeringCorsair.java b/Mage.Sets/src/mage/cards/s/SwaggeringCorsair.java index 8d99797da8..eadcb1db16 100644 --- a/Mage.Sets/src/mage/cards/s/SwaggeringCorsair.java +++ b/Mage.Sets/src/mage/cards/s/SwaggeringCorsair.java @@ -1,19 +1,20 @@ - package mage.cards.s; -import java.util.UUID; - import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** * @author JayDi85 */ @@ -27,12 +28,14 @@ public final class SwaggeringCorsair extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // Raid — Swaggering Corsair enters the battlefield with a +1/+1 counter on it if you attacked with a creature this turn. + // Raid — Swaggering Corsair enters the battlefield with a +1/+1 counter on it if you attacked this turn. this.addAbility(new EntersBattlefieldAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), - RaidCondition.instance, - "Raid — {this} enters the battlefield with a +1/+1 counter on it if you attacked with a creature this turn.", "" - ), new PlayerAttackedWatcher()); + new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), + RaidCondition.instance, + "Raid — {this} enters the battlefield with a +1/+1 counter on it if you attacked this turn.", "") + .setAbilityWord(AbilityWord.RAID) + .addHint(RaidHint.instance), + new PlayerAttackedWatcher()); } public SwaggeringCorsair(final SwaggeringCorsair card) { diff --git a/Mage.Sets/src/mage/cards/s/SwallowWhole.java b/Mage.Sets/src/mage/cards/s/SwallowWhole.java new file mode 100644 index 0000000000..c5774ef8f1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SwallowWhole.java @@ -0,0 +1,92 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SwallowWhole extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledCreaturePermanent("an untapped creature you control"); + private static final FilterPermanent filter2 + = new FilterCreaturePermanent("tapped creature"); + + static { + filter.add(Predicates.not(TappedPredicate.instance)); + filter2.add(TappedPredicate.instance); + } + + public SwallowWhole(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}"); + + // As an additional cost to cast this spell, tap an untapped creature you control. + this.getSpellAbility().addCost(new TapTargetCost(new TargetControlledPermanent(filter))); + + // Exile target tapped creature. Put a +1/+1 counter on the creature tapped to cast this spell. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter2)); + this.getSpellAbility().addEffect(new SwallowWholeEffect()); + } + + private SwallowWhole(final SwallowWhole card) { + super(card); + } + + @Override + public SwallowWhole copy() { + return new SwallowWhole(this); + } +} + +class SwallowWholeEffect extends OneShotEffect { + + SwallowWholeEffect() { + super(Outcome.Benefit); + staticText = "Put a +1/+1 counter on the creature tapped to pay this spell's additional cost."; + } + + private SwallowWholeEffect(final SwallowWholeEffect effect) { + super(effect); + } + + @Override + public SwallowWholeEffect copy() { + return new SwallowWholeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getCosts() + .stream() + .filter(TapTargetCost.class::isInstance) + .map(TapTargetCost.class::cast) + .map(TapTargetCost::getTarget) + .map(Target::getFirstTarget) + .map(game::getPermanent) + .findFirst() + .orElse(null); + return permanent != null && permanent.addCounters(CounterType.P1P1.createInstance(), source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SwansOfBrynArgoll.java b/Mage.Sets/src/mage/cards/s/SwansOfBrynArgoll.java index c97e5c4338..093cee48e0 100644 --- a/Mage.Sets/src/mage/cards/s/SwansOfBrynArgoll.java +++ b/Mage.Sets/src/mage/cards/s/SwansOfBrynArgoll.java @@ -80,21 +80,21 @@ class SwansOfBrynArgollEffect extends PreventionEffectImpl { if (spell != null) { Player controllerOfSpell = game.getPlayer(spell.getControllerId()); if(controllerOfSpell != null) { - controllerOfSpell.drawCards(preventionEffectData.getPreventedDamage(), game); + controllerOfSpell.drawCards(preventionEffectData.getPreventedDamage(), source.getSourceId(), game); passed = true; } } if (permanent != null) { Player controllerOfPermanent = game.getPlayer(permanent.getControllerId()); if(controllerOfPermanent != null) { - controllerOfPermanent.drawCards(preventionEffectData.getPreventedDamage(), game); + controllerOfPermanent.drawCards(preventionEffectData.getPreventedDamage(), source.getSourceId(), game); passed = true; } } if (emblem != null) { Player controllerOfEmblem = game.getPlayer(emblem.getControllerId()); if(controllerOfEmblem != null) { - controllerOfEmblem.drawCards(preventionEffectData.getPreventedDamage(), game); + controllerOfEmblem.drawCards(preventionEffectData.getPreventedDamage(), source.getSourceId(), game); } passed = true; } @@ -104,7 +104,7 @@ class SwansOfBrynArgollEffect extends PreventionEffectImpl { if (cardSource != null) { Player owner = game.getPlayer(cardSource.getOwnerId()); if (owner != null) { - owner.drawCards(preventionEffectData.getPreventedDamage(), game); + owner.drawCards(preventionEffectData.getPreventedDamage(), source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/s/SwayOfTheStars.java b/Mage.Sets/src/mage/cards/s/SwayOfTheStars.java index 5fd870424f..d5f0563a63 100644 --- a/Mage.Sets/src/mage/cards/s/SwayOfTheStars.java +++ b/Mage.Sets/src/mage/cards/s/SwayOfTheStars.java @@ -1,8 +1,6 @@ - package mage.cards.s; import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -10,6 +8,8 @@ import mage.abilities.effects.common.DrawCardAllEffect; import mage.abilities.effects.common.SetPlayerLifeAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -50,7 +50,7 @@ class SwayOfTheStarsEffect extends OneShotEffect { public SwayOfTheStarsEffect() { super(Outcome.Neutral); - staticText = "Each player shuffles their hand, graveyard, and permanents they own into their library, then draws seven cards. Each player's life total becomes 7"; + staticText = "Each player shuffles their hand, graveyard, and permanents they own into their library"; } public SwayOfTheStarsEffect(final SwayOfTheStarsEffect effect) { @@ -68,10 +68,11 @@ class SwayOfTheStarsEffect extends OneShotEffect { player.moveCards(player.getGraveyard(), Zone.LIBRARY, source, game); FilterPermanent filter = new FilterPermanent(); filter.add(new OwnerIdPredicate(playerId)); + Cards toLib = new CardsImpl(); for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, controller.getId(), source.getSourceId(), game)) { - permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + toLib.add(permanent); } - player.shuffleLibrary(source, game); + player.shuffleCardsToLibrary(toLib, game, source); } } } diff --git a/Mage.Sets/src/mage/cards/s/SwiftKick.java b/Mage.Sets/src/mage/cards/s/SwiftKick.java index e01dff0969..d1f50d8462 100644 --- a/Mage.Sets/src/mage/cards/s/SwiftKick.java +++ b/Mage.Sets/src/mage/cards/s/SwiftKick.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.FightTargetsEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -9,24 +7,17 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; -import mage.target.Target; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SwiftKick extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public SwiftKick(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}"); @@ -38,12 +29,10 @@ public final class SwiftKick extends CardImpl { effect = new FightTargetsEffect(); effect.setText("It fights target creature you don't control"); this.getSpellAbility().addEffect(effect); - Target target = new TargetCreaturePermanent(filter); - this.getSpellAbility().addTarget(target); - + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public SwiftKick(final SwiftKick card) { + private SwiftKick(final SwiftKick card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/s/SwiftResponse.java b/Mage.Sets/src/mage/cards/s/SwiftResponse.java new file mode 100644 index 0000000000..1deee7a4ba --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SwiftResponse.java @@ -0,0 +1,41 @@ +package mage.cards.s; + +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SwiftResponse extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("tapped permanent"); + + static { + filter.add(TappedPredicate.instance); + } + + public SwiftResponse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Destroy target tapped creature. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + } + + private SwiftResponse(final SwiftResponse card) { + super(card); + } + + @Override + public SwiftResponse copy() { + return new SwiftResponse(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SwiftSilence.java b/Mage.Sets/src/mage/cards/s/SwiftSilence.java index d0619b05d6..3ca62a310b 100644 --- a/Mage.Sets/src/mage/cards/s/SwiftSilence.java +++ b/Mage.Sets/src/mage/cards/s/SwiftSilence.java @@ -66,7 +66,7 @@ class SwiftSilenceEffect extends OneShotEffect { } Player controller = game.getPlayer(source.getControllerId()); if (toDraw > 0 && controller != null){ - controller.drawCards(toDraw, game); + controller.drawCards(toDraw, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/SwordOfBodyAndMind.java b/Mage.Sets/src/mage/cards/s/SwordOfBodyAndMind.java index e412f9e57a..ead24ed438 100644 --- a/Mage.Sets/src/mage/cards/s/SwordOfBodyAndMind.java +++ b/Mage.Sets/src/mage/cards/s/SwordOfBodyAndMind.java @@ -100,6 +100,6 @@ class SwordOfBodyAndMindAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever equipped creature deals combat damage to a player, you create a 2/2 green Wolf creature token and that player puts the top ten cards of their library into their graveyard."; + return "Whenever equipped creature deals combat damage to a player, you create a 2/2 green Wolf creature token and that player mills ten cards."; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SwordOfFireAndIce.java b/Mage.Sets/src/mage/cards/s/SwordOfFireAndIce.java index 002c2d1109..2fa4c805c4 100644 --- a/Mage.Sets/src/mage/cards/s/SwordOfFireAndIce.java +++ b/Mage.Sets/src/mage/cards/s/SwordOfFireAndIce.java @@ -1,5 +1,3 @@ - - package mage.cards.s; import java.util.UUID; @@ -33,16 +31,18 @@ import mage.target.common.TargetAnyTarget; public final class SwordOfFireAndIce extends CardImpl { public SwordOfFireAndIce(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +2/+2 and has protection from red and from blue. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 2))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ProtectionAbility.from(ObjectColor.RED, ObjectColor.BLUE), AttachmentType.EQUIPMENT))); - // Whenever equipped creature deals combat damage to a player, Sword of Fire and Ice deals 2 damage to any target and you draw a card. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect( + ProtectionAbility.from(ObjectColor.RED, ObjectColor.BLUE), AttachmentType.EQUIPMENT))); + // Whenever equipped creature deals combat damage to a player, Sword of Fire + // and Ice deals 2 damage to any target and you draw a card. this.addAbility(new SwordOfFireAndIceAbility()); - // Equip - this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2))); + // Equip {2} + this.addAbility(new EquipAbility(Outcome.Benefit, new GenericManaCost(2))); } public SwordOfFireAndIce(final SwordOfFireAndIce card) { @@ -82,11 +82,14 @@ class SwordOfFireAndIceAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; Permanent p = game.getPermanent(event.getSourceId()); - return damageEvent.isCombatDamage() && p != null && p.getAttachments().contains(this.getSourceId()); + return damageEvent.isCombatDamage() + && p != null + && p.getAttachments().contains(this.getSourceId()); } @Override public String getRule() { - return "Whenever equipped creature deals combat damage to a player, {this} deals 2 damage to any target and you draw a card."; + return "Whenever equipped creature deals combat damage to a player, " + + "{this} deals 2 damage to any target and you draw a card."; } } diff --git a/Mage.Sets/src/mage/cards/s/SwornDefender.java b/Mage.Sets/src/mage/cards/s/SwornDefender.java index 71ff72a02b..2032339f9e 100644 --- a/Mage.Sets/src/mage/cards/s/SwornDefender.java +++ b/Mage.Sets/src/mage/cards/s/SwornDefender.java @@ -72,7 +72,7 @@ class SwornDefenderEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent targetPermanent = game.getPermanentOrLKIBattlefield(targetPointer.getFirst(game, source)); + Permanent targetPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (controller != null && targetPermanent != null) { int newPower = CardUtil.subtractWithOverflowCheck(targetPermanent.getToughness().getValue(), 1); int newToughness = CardUtil.addWithOverflowCheck(targetPermanent.getPower().getValue(), 1); diff --git a/Mage.Sets/src/mage/cards/s/SylvanHierophant.java b/Mage.Sets/src/mage/cards/s/SylvanHierophant.java index 80555a1adc..ea72e04e61 100644 --- a/Mage.Sets/src/mage/cards/s/SylvanHierophant.java +++ b/Mage.Sets/src/mage/cards/s/SylvanHierophant.java @@ -2,7 +2,7 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; @@ -40,7 +40,7 @@ public final class SylvanHierophant extends CardImpl { // When Sylvan Hierophant dies, exile Sylvan Hierophant, then return another target creature card from your graveyard to your hand. Effect effect = new ReturnFromGraveyardToHandTargetEffect(); - Ability ability = new DiesTriggeredAbility(new ExileSourceEffect(), false); + Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect(), false); ability.addEffect(effect); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SylvanLibrary.java b/Mage.Sets/src/mage/cards/s/SylvanLibrary.java index f18b270789..223e5306cc 100644 --- a/Mage.Sets/src/mage/cards/s/SylvanLibrary.java +++ b/Mage.Sets/src/mage/cards/s/SylvanLibrary.java @@ -71,7 +71,7 @@ class SylvanLibraryEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - controller.drawCards(2, game); + controller.drawCards(2, source.getSourceId(), game); SylvanLibraryCardsDrawnThisTurnWatcher watcher = game.getState().getWatcher(SylvanLibraryCardsDrawnThisTurnWatcher.class); if (watcher != null) { Cards cards = new CardsImpl(); @@ -95,7 +95,7 @@ class SylvanLibraryEffect extends OneShotEffect { for (UUID cardId : target.getTargets()) { Card card = cards.get(cardId, game); if (card != null) { - if (controller.canPayLifeCost() + if (controller.canPayLifeCost(source) && controller.getLife() >= 4 && controller.chooseUse(outcome, "Pay 4 life for " + card.getLogName() + "? (Otherwise it's put on top of your library)", source, game)) { controller.loseLife(4, game, false); diff --git a/Mage.Sets/src/mage/cards/s/SymbioticBeast.java b/Mage.Sets/src/mage/cards/s/SymbioticBeast.java index df9de72dce..4f2d1b0cc0 100644 --- a/Mage.Sets/src/mage/cards/s/SymbioticBeast.java +++ b/Mage.Sets/src/mage/cards/s/SymbioticBeast.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class SymbioticBeast extends CardImpl { this.toughness = new MageInt(4); // When Symbiotic Beast dies, create four 1/1 green Insect creature tokens. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new InsectToken(), 4))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new InsectToken(), 4))); } public SymbioticBeast(final SymbioticBeast card) { diff --git a/Mage.Sets/src/mage/cards/s/SymbioticElf.java b/Mage.Sets/src/mage/cards/s/SymbioticElf.java index b2f3b8d408..a40b193767 100644 --- a/Mage.Sets/src/mage/cards/s/SymbioticElf.java +++ b/Mage.Sets/src/mage/cards/s/SymbioticElf.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class SymbioticElf extends CardImpl { this.toughness = new MageInt(2); // When Symbiotic Elf dies, create two 1/1 green Insect creature tokens. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new InsectToken(), 2))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new InsectToken(), 2))); } public SymbioticElf(final SymbioticElf card) { diff --git a/Mage.Sets/src/mage/cards/s/SymbioticWurm.java b/Mage.Sets/src/mage/cards/s/SymbioticWurm.java index 0cca8ae6d8..45df2721da 100644 --- a/Mage.Sets/src/mage/cards/s/SymbioticWurm.java +++ b/Mage.Sets/src/mage/cards/s/SymbioticWurm.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class SymbioticWurm extends CardImpl { this.toughness = new MageInt(7); // When Symbiotic Wurm dies, create seven 1/1 green Insect creature tokens. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new InsectToken(), 7))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new InsectToken(), 7))); } diff --git a/Mage.Sets/src/mage/cards/s/SyphonMind.java b/Mage.Sets/src/mage/cards/s/SyphonMind.java index 820017992a..ba5bc4e75a 100644 --- a/Mage.Sets/src/mage/cards/s/SyphonMind.java +++ b/Mage.Sets/src/mage/cards/s/SyphonMind.java @@ -76,7 +76,7 @@ class SyphonMindEffect extends OneShotEffect { } } } - you.drawCards(amount, game); + you.drawCards(amount, source.getSourceId(), game); } return result; } diff --git a/Mage.Sets/src/mage/cards/s/SyrCarahTheBold.java b/Mage.Sets/src/mage/cards/s/SyrCarahTheBold.java index 1a7a682d00..688ab1d024 100644 --- a/Mage.Sets/src/mage/cards/s/SyrCarahTheBold.java +++ b/Mage.Sets/src/mage/cards/s/SyrCarahTheBold.java @@ -126,7 +126,7 @@ class SyrCarahTheBoldExileEffect extends OneShotEffect { String exileName = sourcePermanent.getIdName() + " "; controller.moveCardsToExile(card, source, game, true, source.getSourceId(), exileName); ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SyrElenoraTheDiscerning.java b/Mage.Sets/src/mage/cards/s/SyrElenoraTheDiscerning.java index 0be6760519..558ccfb909 100644 --- a/Mage.Sets/src/mage/cards/s/SyrElenoraTheDiscerning.java +++ b/Mage.Sets/src/mage/cards/s/SyrElenoraTheDiscerning.java @@ -1,23 +1,17 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.SpellAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.SetPowerSourceEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.Game; -import mage.target.Target; -import mage.util.CardUtil; +import mage.filter.FilterCard; -import java.util.Collection; import java.util.UUID; /** @@ -43,7 +37,9 @@ public final class SyrElenoraTheDiscerning extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); // Spells your opponents cast that target Syr Elenora cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(new SyrElenoraTheDiscerningCostIncreaseEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT)) + ); } private SyrElenoraTheDiscerning(final SyrElenoraTheDiscerning card) { @@ -55,46 +51,3 @@ public final class SyrElenoraTheDiscerning extends CardImpl { return new SyrElenoraTheDiscerning(this); } } - -class SyrElenoraTheDiscerningCostIncreaseEffect extends CostModificationEffectImpl { - - SyrElenoraTheDiscerningCostIncreaseEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Spells your opponents cast that target {this} cost {2} more to cast"; - } - - private SyrElenoraTheDiscerningCostIncreaseEffect(SyrElenoraTheDiscerningCostIncreaseEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, -2); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (!(abilityToModify instanceof SpellAbility) - || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - return false; - } - return abilityToModify - .getModes() - .getSelectedModes() - .stream() - .map(uuid -> abilityToModify.getModes().get(uuid)) - .map(Mode::getTargets) - .flatMap(Collection::stream) - .map(Target::getTargets) - .flatMap(Collection::stream) - .anyMatch(uuid -> uuid.equals(source.getSourceId())); - } - - @Override - public SyrElenoraTheDiscerningCostIncreaseEffect copy() { - return new SyrElenoraTheDiscerningCostIncreaseEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/s/SyrGwynHeroOfAshvale.java b/Mage.Sets/src/mage/cards/s/SyrGwynHeroOfAshvale.java index 6034bc8149..14c92435fb 100644 --- a/Mage.Sets/src/mage/cards/s/SyrGwynHeroOfAshvale.java +++ b/Mage.Sets/src/mage/cards/s/SyrGwynHeroOfAshvale.java @@ -8,7 +8,6 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.abilities.keyword.EquipFilterAbility; import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; @@ -23,6 +22,9 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.EquippedPredicate; import java.util.UUID; +import mage.abilities.keyword.EquipAbility; +import mage.constants.Outcome; +import mage.target.common.TargetControlledCreaturePermanent; /** * @author TheElk801 @@ -64,7 +66,8 @@ public final class SyrGwynHeroOfAshvale extends CardImpl { // Equipment you control have equip Knight {0}. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - new EquipFilterAbility(filter3, new GenericManaCost(0)), Duration.WhileOnBattlefield, filter2 + new EquipAbility(Outcome.AddAbility, new GenericManaCost(0), new TargetControlledCreaturePermanent(filter3)), + Duration.WhileOnBattlefield, filter2 ))); } diff --git a/Mage.Sets/src/mage/cards/s/SzadekLordOfSecrets.java b/Mage.Sets/src/mage/cards/s/SzadekLordOfSecrets.java index 893008fa81..7feea17314 100644 --- a/Mage.Sets/src/mage/cards/s/SzadekLordOfSecrets.java +++ b/Mage.Sets/src/mage/cards/s/SzadekLordOfSecrets.java @@ -54,7 +54,7 @@ class SzadekLordOfSecretsEffect extends ReplacementEffectImpl { SzadekLordOfSecretsEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If {this} would deal combat damage to a player, instead put that many +1/+1 counters on {this} and that player puts that many cards from the top of their library into their graveyard"; + staticText = "If {this} would deal combat damage to a player, instead put that many +1/+1 counters on {this} and that player mills that many cards"; } SzadekLordOfSecretsEffect(final SzadekLordOfSecretsEffect effect) { @@ -71,7 +71,7 @@ class SzadekLordOfSecretsEffect extends ReplacementEffectImpl { if (permanent != null) { permanent.addCounters(CounterType.P1P1.createInstance(damageEvent.getAmount()), source, game); if (damagedPlayer != null) { - damagedPlayer.moveCards(damagedPlayer.getLibrary().getTopCards(game, damageEvent.getAmount()), Zone.GRAVEYARD, source, game); + damagedPlayer.millCards(damageEvent.getAmount(), source, game); } } } diff --git a/Mage.Sets/src/mage/cards/t/TahngarthFirstMate.java b/Mage.Sets/src/mage/cards/t/TahngarthFirstMate.java index 693bf559f0..a8c700033e 100644 --- a/Mage.Sets/src/mage/cards/t/TahngarthFirstMate.java +++ b/Mage.Sets/src/mage/cards/t/TahngarthFirstMate.java @@ -133,7 +133,7 @@ class TahngarthFirstMateEffect extends OneShotEffect { ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfCombat, player.getId()); effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); - game.applyEffects(); + game.getState().processAction(game); return game.getCombat().addAttackerToCombat(permanent.getId(), target.getFirstTarget(), game); } } diff --git a/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java b/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java index 4aeaa23049..236ce4ff01 100644 --- a/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java +++ b/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java @@ -150,7 +150,7 @@ class TaigamOjutaiMasterGainReboundEffect extends ContinuousEffectImpl { } private void addReboundAbility(Card card, Ability source, Game game) { - boolean found = card.getAbilities(game).stream().anyMatch(ability -> ability instanceof ReboundAbility); + boolean found = card.getAbilities(game).containsClass(ReboundAbility.class); if (!found) { Ability ability = new ReboundAbility(); game.getState().addOtherAbility(card, ability); diff --git a/Mage.Sets/src/mage/cards/t/TailSlash.java b/Mage.Sets/src/mage/cards/t/TailSlash.java index 69d5dbbe3c..bc5f961942 100644 --- a/Mage.Sets/src/mage/cards/t/TailSlash.java +++ b/Mage.Sets/src/mage/cards/t/TailSlash.java @@ -4,8 +4,7 @@ import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -16,22 +15,16 @@ import java.util.UUID; */ public final class TailSlash extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public TailSlash(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); // Target creature you control deals damage equal to its power to target creature you don't control. this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public TailSlash(final TailSlash card) { + private TailSlash(final TailSlash card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/t/TallAsABeanstalk.java b/Mage.Sets/src/mage/cards/t/TallAsABeanstalk.java index fbb5509206..249a67404d 100644 --- a/Mage.Sets/src/mage/cards/t/TallAsABeanstalk.java +++ b/Mage.Sets/src/mage/cards/t/TallAsABeanstalk.java @@ -35,14 +35,14 @@ public final class TallAsABeanstalk extends CardImpl { // Enchanted creature gets +3/+3, has reach, and is a Giant in addition to its other types. ability = new SimpleStaticAbility( - new BoostEnchantedEffect(3, 3).setText("enchanted creature gets +3/+3,") + new BoostEnchantedEffect(3, 3).setText("enchanted creature gets +3/+3") ); ability.addEffect(new GainAbilityAttachedEffect( ReachAbility.getInstance(), AttachmentType.AURA - ).setText("has reach,")); + ).setText(", has reach")); ability.addEffect(new AddCardSubtypeAttachedEffect( SubType.GIANT, Duration.WhileOnBattlefield, AttachmentType.AURA - ).setText("and is a Giant in addition to its other types")); + ).setText(", and is a Giant in addition to its other types")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TalrandSkySummoner.java b/Mage.Sets/src/mage/cards/t/TalrandSkySummoner.java index 4616b64a74..98470b8cbe 100644 --- a/Mage.Sets/src/mage/cards/t/TalrandSkySummoner.java +++ b/Mage.Sets/src/mage/cards/t/TalrandSkySummoner.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -10,26 +8,18 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.game.permanent.token.DrakeToken; +import java.util.UUID; + /** - * * @author North */ public final class TalrandSkySummoner extends CardImpl { - private static final FilterSpell filter = new FilterSpell("instant or sorcery card"); - - static { - filter.add(Predicates.or( - CardType.INSTANT.getPredicate(), - CardType.SORCERY.getPredicate())); - } - public TalrandSkySummoner(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.MERFOLK); this.subtype.add(SubType.WIZARD); @@ -38,10 +28,10 @@ public final class TalrandSkySummoner extends CardImpl { this.toughness = new MageInt(2); // Whenever you cast an instant or sorcery spell, create a 2/2 blue Drake creature token with flying. - this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new DrakeToken()), filter, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new DrakeToken()), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false)); } - public TalrandSkySummoner(final TalrandSkySummoner card) { + private TalrandSkySummoner(final TalrandSkySummoner card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/t/TalruumChampion.java b/Mage.Sets/src/mage/cards/t/TalruumChampion.java index abb97d8c05..96734c2cc1 100644 --- a/Mage.Sets/src/mage/cards/t/TalruumChampion.java +++ b/Mage.Sets/src/mage/cards/t/TalruumChampion.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.LoseAbilityTargetEffect; import mage.abilities.keyword.FirstStrikeAbility; @@ -31,7 +31,7 @@ public final class TalruumChampion extends CardImpl { // Whenever Talruum Champion blocks or becomes blocked by a creature, that creature loses first strike until end of turn. Effect effect = new LoseAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); effect.setText("that creature loses first strike until end of turn"); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false)); } public TalruumChampion(final TalruumChampion card) { diff --git a/Mage.Sets/src/mage/cards/t/TamiyosEpiphany.java b/Mage.Sets/src/mage/cards/t/TamiyosEpiphany.java index bd626fa4f0..e924bb5021 100644 --- a/Mage.Sets/src/mage/cards/t/TamiyosEpiphany.java +++ b/Mage.Sets/src/mage/cards/t/TamiyosEpiphany.java @@ -17,8 +17,8 @@ public final class TamiyosEpiphany extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); // Scry 4, then draw two cards. - this.getSpellAbility().addEffect(new ScryEffect(4).setText("scry 4,")); - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText("then draw two cards")); + this.getSpellAbility().addEffect(new ScryEffect(4, false)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).concatBy(", then")); } private TamiyosEpiphany(final TamiyosEpiphany card) { diff --git a/Mage.Sets/src/mage/cards/t/TangleAsp.java b/Mage.Sets/src/mage/cards/t/TangleAsp.java index ac1a74de78..a51b71dd67 100644 --- a/Mage.Sets/src/mage/cards/t/TangleAsp.java +++ b/Mage.Sets/src/mage/cards/t/TangleAsp.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -30,7 +30,7 @@ public final class TangleAsp extends CardImpl { // Whenever Tangle Asp blocks or becomes blocked by a creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false)); } public TangleAsp(final TangleAsp card) { diff --git a/Mage.Sets/src/mage/cards/t/Tarpan.java b/Mage.Sets/src/mage/cards/t/Tarpan.java index 5529e42b62..c3a83c1d78 100644 --- a/Mage.Sets/src/mage/cards/t/Tarpan.java +++ b/Mage.Sets/src/mage/cards/t/Tarpan.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class Tarpan extends CardImpl { this.toughness = new MageInt(1); // When Tarpan dies, you gain 1 life. - this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(1), false)); + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(1), false)); } public Tarpan(final Tarpan card) { diff --git a/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java b/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java index 814b97225b..af9604a194 100644 --- a/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java +++ b/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java @@ -1,4 +1,3 @@ - package mage.cards.t; import mage.MageInt; @@ -37,6 +36,7 @@ public final class TasigurTheGoldenFang extends CardImpl { // Delve this.addAbility(new DelveAbility()); + // {2}{G/U}{G/U}: Put the top two cards of your library into your graveyard, then return a nonland card of an opponent's choice from your graveyard to your hand. Ability ability = new SimpleActivatedAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(2), new ManaCostsImpl("{2}{G/U}{G/U}")); ability.addEffect(new TasigurTheGoldenFangEffect()); diff --git a/Mage.Sets/src/mage/cards/t/TatsumasaTheDragonsFang.java b/Mage.Sets/src/mage/cards/t/TatsumasaTheDragonsFang.java index 492d4ed477..35005063fe 100644 --- a/Mage.Sets/src/mage/cards/t/TatsumasaTheDragonsFang.java +++ b/Mage.Sets/src/mage/cards/t/TatsumasaTheDragonsFang.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -25,8 +23,9 @@ import mage.game.permanent.Permanent; import mage.game.permanent.token.TatsumaDragonToken; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TatsumasaTheDragonsFang extends CardImpl { @@ -82,7 +81,7 @@ class TatsumaTheDragonsFangEffect extends OneShotEffect { Permanent tokenPermanent = game.getPermanent(tokenId); if (tokenPermanent != null) { FixedTarget fixedTarget = new FixedTarget(tokenPermanent, game); - Effect returnEffect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect returnEffect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); returnEffect.setTargetPointer(new FixedTarget(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()))); DelayedTriggeredAbility delayedAbility = new TatsumaTheDragonsFangTriggeredAbility(fixedTarget, returnEffect); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/t/TatteredMummy.java b/Mage.Sets/src/mage/cards/t/TatteredMummy.java index 92d4daecc1..7dd0951593 100644 --- a/Mage.Sets/src/mage/cards/t/TatteredMummy.java +++ b/Mage.Sets/src/mage/cards/t/TatteredMummy.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class TatteredMummy extends CardImpl { this.toughness = new MageInt(2); // When Tattered Mummy dies, each opponent loses 2 life. - this.addAbility(new DiesTriggeredAbility(new LoseLifeOpponentsEffect(2))); + this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeOpponentsEffect(2))); } public TatteredMummy(final TatteredMummy card) { diff --git a/Mage.Sets/src/mage/cards/t/TayamLuminousEnigma.java b/Mage.Sets/src/mage/cards/t/TayamLuminousEnigma.java new file mode 100644 index 0000000000..e424e5b6f3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TayamLuminousEnigma.java @@ -0,0 +1,237 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.RemoveCounterCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.*; +import mage.counters.Counter; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.permanent.CounterPredicate; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import static com.google.common.collect.Iterables.getOnlyElement; + +/** + * + * @author htrajan + */ +public final class TayamLuminousEnigma extends CardImpl { + + public TayamLuminousEnigma(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Each other creature you control enters the battlefield with an additional vigilance counter on it. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new TayamLuminousEnigmaReplacementEffect())); + + // {3}, Remove three counters from among creatures you control: Put the top three cards of your library into your graveyard, then return a permanent card with converted mana cost 3 or less from your graveyard to the battlefield. + PutTopCardOfLibraryIntoGraveControllerEffect millEffect = new PutTopCardOfLibraryIntoGraveControllerEffect(3); + millEffect.concatBy("."); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, millEffect, new GenericManaCost(3)); + ability.addCost(new TayamLuminousEnigmaCost()); + TayamLuminousEnigmaEffect effect = new TayamLuminousEnigmaEffect(); + ability.addEffect(effect); + this.addAbility(ability); + } + + private TayamLuminousEnigma(final TayamLuminousEnigma card) { + super(card); + } + + @Override + public TayamLuminousEnigma copy() { + return new TayamLuminousEnigma(this); + } +} + +class TayamLuminousEnigmaCost extends RemoveCounterCost { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a creature with a counter among creatures you control"); + + static { + filter.add(new CounterPredicate(null)); + } + + public TayamLuminousEnigmaCost() { + super(new TargetPermanent(1, 1, filter, true), null, 3); + } + + public TayamLuminousEnigmaCost(TayamLuminousEnigmaCost cost) { + super(cost); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + paid = false; + int countersRemoved = 0; + Player controller = game.getPlayer(controllerId); + for (int i = 0; i < countersToRemove; i++) { + if (target.choose(Outcome.UnboostCreature, controllerId, sourceId, game)) { + UUID targetId = getOnlyElement(target.getTargets()); + Permanent permanent = game.getPermanent(targetId); + if (permanent != null) { + if (!permanent.getCounters(game).isEmpty()) { + String counterName = null; + if (permanent.getCounters(game).size() > 1) { + Choice choice = new ChoiceImpl(true); + Set choices = new HashSet<>(); + for (Counter counter : permanent.getCounters(game).values()) { + if (permanent.getCounters(game).getCount(counter.getName()) > 0) { + choices.add(counter.getName()); + } + } + choice.setChoices(choices); + choice.setMessage("Choose a counter to remove from " + permanent.getLogName()); + if (!controller.choose(Outcome.UnboostCreature, choice, game)) { + return false; + } + counterName = choice.getChoice(); + } else { + for (Counter counter : permanent.getCounters(game).values()) { + if (counter.getCount() > 0) { + counterName = counter.getName(); + } + } + } + if (counterName != null) { + permanent.removeCounters(counterName, 1, game); + target.clearChosen(); + if (!game.isSimulation()) { + game.informPlayers(new StringBuilder(controller.getLogName()) + .append(" removes a ") + .append(counterName).append(" counter from ") + .append(permanent.getName()).toString()); + } + countersRemoved++; + if (countersRemoved == countersToRemove) { + paid = true; + break; + } + } + } + } + } else { + break; + } + } + return paid; + } + + @Override + public TayamLuminousEnigmaCost copy() { + return new TayamLuminousEnigmaCost(this); + } + + @Override + public String getText() { + return "Remove three counters from among creatures you control"; + } +} + +class TayamLuminousEnigmaEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterPermanentCard("permanent card in your graveyard with converted mana cost 3 or less"); + + static { + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + } + + TayamLuminousEnigmaEffect() { + super(Outcome.Benefit); + staticText = ", then return a permanent card with converted mana cost 3 or less from your graveyard to the battlefield"; + } + + private TayamLuminousEnigmaEffect(TayamLuminousEnigmaEffect effect) { + super(effect); + } + + @Override + public TayamLuminousEnigmaEffect copy() { + return new TayamLuminousEnigmaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || player.getGraveyard().count(filter, game) == 0) { + return false; + } + TargetCard target = new TargetCardInYourGraveyard(filter); + target.setNotTarget(true); + if (!player.choose(outcome, player.getGraveyard(), target, game)) { + return false; + } + return player.moveCards(game.getCard(target.getFirstTarget()), Zone.BATTLEFIELD, source, game); + } +} + +class TayamLuminousEnigmaReplacementEffect extends ReplacementEffectImpl { + + TayamLuminousEnigmaReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.BoostCreature); + staticText = "Each other creature you control enters the battlefield with an additional vigilance counter on it."; + } + + private TayamLuminousEnigmaReplacementEffect(final TayamLuminousEnigmaReplacementEffect effect) { + super(effect); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + return creature != null + && creature.isCreature() + && !source.getSourceId().equals(creature.getId()) + && creature.isControlledBy(source.getControllerId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + if (creature != null) { + creature.addCounters(CounterType.VIGILANCE.createInstance(), source, game, event.getAppliedEffects()); + } + return false; + } + + @Override + public TayamLuminousEnigmaReplacementEffect copy() { + return new TayamLuminousEnigmaReplacementEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TectonicGiant.java b/Mage.Sets/src/mage/cards/t/TectonicGiant.java index 3f72f5440f..b9e8d5a21f 100644 --- a/Mage.Sets/src/mage/cards/t/TectonicGiant.java +++ b/Mage.Sets/src/mage/cards/t/TectonicGiant.java @@ -1,6 +1,8 @@ package mage.cards.t; +import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.TriggeredAbilityImpl; @@ -10,19 +12,16 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamagePlayersEffect; import mage.cards.*; import mage.constants.*; +import static mage.constants.Outcome.Benefit; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.stack.StackObject; +import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInExile; import mage.target.targetpointer.FixedTarget; -import java.util.UUID; - -import static mage.constants.Outcome.Benefit; - /** * @author TheElk801 */ @@ -75,12 +74,14 @@ class TectonicGiantTriggeredAbility extends TriggeredAbilityImpl { case DECLARED_ATTACKERS: return game.getCombat().getAttackers().contains(this.getSourceId()); case TARGETED: - StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); - Player player = game.getPlayer(getControllerId()); - return sourceObject != null - && player != null - && player.hasOpponent(sourceObject.getControllerId(), game) - && event.getTargetId().equals(getSourceId()); + if (event.getTargetId().equals(getSourceId())) { + MageObject mageObject = game.getObject(event.getSourceId()); + Player player = game.getPlayer(getControllerId()); + return mageObject != null + && mageObject instanceof Spell + && player != null + && player.hasOpponent(((Spell) mageObject).getControllerId(), game); + } } return false; } @@ -100,8 +101,8 @@ class TectonicGiantEffect extends OneShotEffect { TectonicGiantEffect() { super(Benefit); - staticText = "exile the top two cards of your library. Choose one of them. " + - "Until the end of your next turn, you may play that card"; + staticText = "exile the top two cards of your library. Choose one of them. " + + "Until the end of your next turn, you may play that card"; } private TectonicGiantEffect(final TectonicGiantEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java b/Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java new file mode 100644 index 0000000000..3ba2279cb2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java @@ -0,0 +1,115 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.PhaseOutTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.turn.TurnMod; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TeferiMasterOfTime extends CardImpl { + + public TeferiMasterOfTime(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.TEFERI); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + + // You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant. + this.addAbility(new SimpleStaticAbility(new TeferiMasterOfTimeActivationEffect())); + + // +1: Draw a card, then discard a card. + this.addAbility(new LoyaltyAbility(new DrawDiscardControllerEffect(1, 1), 1)); + + // −3: Target creature you don't control phases out. + Ability ability = new LoyaltyAbility(new PhaseOutTargetEffect(), -3); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.addAbility(ability); + + // −10: Take two extra turns after this one. + this.addAbility(new LoyaltyAbility(new TeferiMasterOfTimeTurnEffect(), -10)); + } + + private TeferiMasterOfTime(final TeferiMasterOfTime card) { + super(card); + } + + @Override + public TeferiMasterOfTime copy() { + return new TeferiMasterOfTime(this); + } +} + +class TeferiMasterOfTimeActivationEffect extends AsThoughEffectImpl { + + TeferiMasterOfTimeActivationEffect() { + super(AsThoughEffectType.ACTIVATE_AS_INSTANT, Duration.EndOfGame, Outcome.Benefit); + staticText = "You may activate loyalty abilities of {this} " + + "on any player's turn any time you could cast an instant"; + } + + private TeferiMasterOfTimeActivationEffect(final TeferiMasterOfTimeActivationEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public TeferiMasterOfTimeActivationEffect copy() { + return new TeferiMasterOfTimeActivationEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + return affectedAbility.isControlledBy(source.getControllerId()) + && affectedAbility.getSourceId().equals(source.getSourceId()) + && affectedAbility instanceof LoyaltyAbility; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + return false; + } +} + +class TeferiMasterOfTimeTurnEffect extends OneShotEffect { + + TeferiMasterOfTimeTurnEffect() { + super(Outcome.ExtraTurn); + staticText = "take two extra turns after this one"; + } + + private TeferiMasterOfTimeTurnEffect(final TeferiMasterOfTimeTurnEffect effect) { + super(effect); + } + + @Override + public TeferiMasterOfTimeTurnEffect copy() { + return new TeferiMasterOfTimeTurnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), false)); + game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), false)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java b/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java new file mode 100644 index 0000000000..ec3ab59fc1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java @@ -0,0 +1,140 @@ +package mage.cards.t; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TeferiTimelessVoyager extends CardImpl { + + public TeferiTimelessVoyager(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.TEFERI); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // +1: Draw a card. + this.addAbility(new LoyaltyAbility(new DrawCardSourceControllerEffect(1), 1)); + + // −3: Put target creature on top of its owner's library. + Ability ability = new LoyaltyAbility(new PutOnLibraryTargetEffect(true), -3); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // −8: Each creature target opponent controls phases out. Until the end of your next turn, they can't phase in. + ability = new LoyaltyAbility(new TeferiTimelessVoyagerEffect(), -8); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private TeferiTimelessVoyager(final TeferiTimelessVoyager card) { + super(card); + } + + @Override + public TeferiTimelessVoyager copy() { + return new TeferiTimelessVoyager(this); + } +} + +class TeferiTimelessVoyagerEffect extends OneShotEffect { + + TeferiTimelessVoyagerEffect() { + super(Outcome.Benefit); + staticText = "each creature target opponent controls phases out. " + + "Until the end of your next turn, they can't phase in"; + } + + private TeferiTimelessVoyagerEffect(final TeferiTimelessVoyagerEffect effect) { + super(effect); + } + + @Override + public TeferiTimelessVoyagerEffect copy() { + return new TeferiTimelessVoyagerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent : game + .getBattlefield() + .getAllActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, source.getFirstTarget(), game + )) { + MageObjectReference mor = new MageObjectReference(permanent, game); + permanent.phaseOut(game); + game.addEffect(new TeferiTimelessVoyagerPhaseEffect(mor), source); + } + return true; + } +} + +class TeferiTimelessVoyagerPhaseEffect extends ContinuousRuleModifyingEffectImpl { + + private int castOnTurn = 0; + private final MageObjectReference mor; + + TeferiTimelessVoyagerPhaseEffect(MageObjectReference mor) { + super(Duration.Custom, Outcome.Neutral); + this.mor = mor; + } + + private TeferiTimelessVoyagerPhaseEffect(final TeferiTimelessVoyagerPhaseEffect effect) { + super(effect); + this.castOnTurn = effect.castOnTurn; + this.mor = effect.mor; + } + + @Override + public TeferiTimelessVoyagerPhaseEffect copy() { + return new TeferiTimelessVoyagerPhaseEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + castOnTurn = game.getTurnNum(); + } + + @Override + public boolean isInactive(Ability source, Game game) { + if (castOnTurn != game.getTurnNum() && game.getPhase().getStep().getType() == PhaseStep.END_TURN) { + return game.isActivePlayer(source.getControllerId()); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PHASE_IN; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return this.mor.refersTo(event.getTargetId(), game); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TeferisAgelessInsight.java b/Mage.Sets/src/mage/cards/t/TeferisAgelessInsight.java new file mode 100644 index 0000000000..4f1d57dc94 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeferisAgelessInsight.java @@ -0,0 +1,86 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.watchers.common.CardsDrawnDuringDrawStepWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TeferisAgelessInsight extends CardImpl { + + public TeferisAgelessInsight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); + this.addSuperType(SuperType.LEGENDARY); + + // If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead. + this.addAbility(new SimpleStaticAbility(new TeferisAgelessInsightEffect()), new CardsDrawnDuringDrawStepWatcher()); + } + + private TeferisAgelessInsight(final TeferisAgelessInsight card) { + super(card); + } + + @Override + public TeferisAgelessInsight copy() { + return new TeferisAgelessInsight(this); + } +} + +class TeferisAgelessInsightEffect extends ReplacementEffectImpl { + + TeferisAgelessInsightEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + staticText = "If you draw a card except the first one you draw in each of your draw steps, draw two cards instead"; + } + + private TeferisAgelessInsightEffect(final TeferisAgelessInsightEffect effect) { + super(effect); + } + + @Override + public TeferisAgelessInsightEffect copy() { + return new TeferisAgelessInsightEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + controller.drawCards(2, event.getSourceId(), game, event.getAppliedEffects()); + } + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DRAW_CARD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!event.getPlayerId().equals(source.getControllerId())) { + return false; + } + if (!game.isActivePlayer(event.getPlayerId()) + || game.getPhase().getStep().getType() != PhaseStep.DRAW) { + return true; + } + CardsDrawnDuringDrawStepWatcher watcher = game.getState().getWatcher(CardsDrawnDuringDrawStepWatcher.class); + return watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TeferisProtege.java b/Mage.Sets/src/mage/cards/t/TeferisProtege.java new file mode 100644 index 0000000000..cf392d9350 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeferisProtege.java @@ -0,0 +1,45 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TeferisProtege extends CardImpl { + + public TeferisProtege(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // {1}{U}, {T}: Draw a card, then discard a card. + Ability ability = new SimpleActivatedAbility( + new DrawDiscardControllerEffect(1, 1), new ManaCostsImpl("{1}{U}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private TeferisProtege(final TeferisProtege card) { + super(card); + } + + @Override + public TeferisProtege copy() { + return new TeferisProtege(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TeferisPuzzleBox.java b/Mage.Sets/src/mage/cards/t/TeferisPuzzleBox.java index 67190e2740..4ead6cbd57 100644 --- a/Mage.Sets/src/mage/cards/t/TeferisPuzzleBox.java +++ b/Mage.Sets/src/mage/cards/t/TeferisPuzzleBox.java @@ -55,7 +55,7 @@ class TeferisPuzzleBoxEffect extends OneShotEffect { if (player != null) { int count = player.getHand().size(); player.putCardsOnBottomOfLibrary(player.getHand(), game, source, true); - player.drawCards(count, game); + player.drawCards(count, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/t/TeferisTutelage.java b/Mage.Sets/src/mage/cards/t/TeferisTutelage.java new file mode 100644 index 0000000000..1b31489e79 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeferisTutelage.java @@ -0,0 +1,40 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.DrawCardControllerTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TeferisTutelage extends CardImpl { + + public TeferisTutelage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + // When Teferi's Tutelage enters the battlefield, draw a card, then discard a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawDiscardControllerEffect(1, 1))); + + // Whenever you draw a card, target opponent mills two cards. + Ability ability = new DrawCardControllerTriggeredAbility(new PutLibraryIntoGraveTargetEffect(2), false); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private TeferisTutelage(final TeferisTutelage card) { + super(card); + } + + @Override + public TeferisTutelage copy() { + return new TeferisTutelage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TeferisWavecaster.java b/Mage.Sets/src/mage/cards/t/TeferisWavecaster.java new file mode 100644 index 0000000000..54725309f8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeferisWavecaster.java @@ -0,0 +1,50 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TeferisWavecaster extends CardImpl { + + private static final FilterCard filter = new FilterCard("Teferi, Timeless Voyager"); + + static { + filter.add(new NamePredicate("Teferi, Timeless Voyager")); + } + + public TeferisWavecaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.MERFOLK); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Teferi's Wavecaster enters the battlefield, you may search your library and/or graveyard for a card named Teferi, Timeless Voyager, reveal it, and put it into your hand. If you search your library this way, shuffle it. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter), true)); + } + + private TeferisWavecaster(final TeferisWavecaster card) { + super(card); + } + + @Override + public TeferisWavecaster copy() { + return new TeferisWavecaster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TeleminPerformance.java b/Mage.Sets/src/mage/cards/t/TeleminPerformance.java index b1b7aca67a..b01048d173 100644 --- a/Mage.Sets/src/mage/cards/t/TeleminPerformance.java +++ b/Mage.Sets/src/mage/cards/t/TeleminPerformance.java @@ -77,7 +77,7 @@ class TeleminPerformanceEffect extends OneShotEffect { opponent.revealCards(source, reveal, game); opponent.moveCards(nonCreatures, Zone.GRAVEYARD, source, game); if (creature != null) { - game.applyEffects(); + game.getState().processAction(game); controller.moveCards(creature, Zone.BATTLEFIELD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/t/TemperedSliver.java b/Mage.Sets/src/mage/cards/t/TemperedSliver.java index 8fa09c623d..6de875e1a7 100644 --- a/Mage.Sets/src/mage/cards/t/TemperedSliver.java +++ b/Mage.Sets/src/mage/cards/t/TemperedSliver.java @@ -30,7 +30,7 @@ public final class TemperedSliver extends CardImpl { // Sliver creatures you control have "Whenever this creature deals combat damage to a player, put a +1/+1 counter on it." this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( new DealsCombatDamageToAPlayerTriggeredAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), false + new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), false,"Whenever this creature deals combat damage to a player, put a +1/+1 counter on it.",false ), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS ))); } diff --git a/Mage.Sets/src/mage/cards/t/TemperedVeteran.java b/Mage.Sets/src/mage/cards/t/TemperedVeteran.java new file mode 100644 index 0000000000..d9470f439d --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TemperedVeteran.java @@ -0,0 +1,67 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.CounterPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TemperedVeteran extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature with a +1/+1 counter on it"); + + static { + filter.add(new CounterPredicate(CounterType.P1P1)); + } + + public TemperedVeteran(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {W}, {T}: Put a +1/+1 counter on target creature with a +1/+1 counter on it. + Ability ability = new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{W}") + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {4}{W}{W}, {T}: Put a +1/+1 counter on target creature. + ability = new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{4}{W}{W}") + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private TemperedVeteran(final TemperedVeteran card) { + super(card); + } + + @Override + public TemperedVeteran copy() { + return new TemperedVeteran(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TemporalAperture.java b/Mage.Sets/src/mage/cards/t/TemporalAperture.java index 1bd65e5d27..9f6e7ae786 100644 --- a/Mage.Sets/src/mage/cards/t/TemporalAperture.java +++ b/Mage.Sets/src/mage/cards/t/TemporalAperture.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -121,11 +120,8 @@ class TemporalApertureTopCardCastEffect extends AsThoughEffectImpl { if (controller != null && game.getState().getZone(objectId) == Zone.LIBRARY) { if (controller.getLibrary().getFromTop(game).equals(card)) { - if (objectCard == card - && objectCard.getSpellAbility() != null - && objectCard.getSpellAbility().spellCanBeActivatedRegularlyNow(controller.getId(), game) - || objectCard.isLand()) { - controller.setCastSourceIdWithAlternateMana(objectId, null, null); + if (objectCard == card && objectCard.getSpellAbility() != null) { // only if castable + allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/t/TemporalCascade.java b/Mage.Sets/src/mage/cards/t/TemporalCascade.java index dc8e42bed9..d4d1fb3971 100644 --- a/Mage.Sets/src/mage/cards/t/TemporalCascade.java +++ b/Mage.Sets/src/mage/cards/t/TemporalCascade.java @@ -63,7 +63,7 @@ class TemporalCascadeDrawEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(7, game); + player.drawCards(7, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/t/TemporaryTruce.java b/Mage.Sets/src/mage/cards/t/TemporaryTruce.java index c3c6ba017b..b842241d4b 100644 --- a/Mage.Sets/src/mage/cards/t/TemporaryTruce.java +++ b/Mage.Sets/src/mage/cards/t/TemporaryTruce.java @@ -58,7 +58,7 @@ class TemporaryTruceEffect extends OneShotEffect { Player player = game.getPlayer(playerId); if (player != null) { int cardsToDraw = player.getAmount(0, 2, "Draw how many cards?", game); - player.drawCards(cardsToDraw, game); + player.drawCards(cardsToDraw, source.getSourceId(), game); player.gainLife((2 - cardsToDraw) * 2, game, source); } } diff --git a/Mage.Sets/src/mage/cards/t/TemptWithReflections.java b/Mage.Sets/src/mage/cards/t/TemptWithReflections.java index 8df6b4f317..3df0617fdc 100644 --- a/Mage.Sets/src/mage/cards/t/TemptWithReflections.java +++ b/Mage.Sets/src/mage/cards/t/TemptWithReflections.java @@ -59,7 +59,7 @@ class TemptWithReflectionsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Effect effect = new CreateTokenCopyTargetEffect(); effect.setTargetPointer(getTargetPointer()); diff --git a/Mage.Sets/src/mage/cards/t/TemurCharm.java b/Mage.Sets/src/mage/cards/t/TemurCharm.java index 5f5744beab..2e10b9e76b 100644 --- a/Mage.Sets/src/mage/cards/t/TemurCharm.java +++ b/Mage.Sets/src/mage/cards/t/TemurCharm.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.Mode; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.Effect; @@ -14,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Duration; -import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; import mage.target.Target; @@ -22,17 +20,16 @@ import mage.target.TargetSpell; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TemurCharm extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); private static final FilterCreaturePermanent filterCantBlock = new FilterCreaturePermanent("Creatures with power 3 or less"); static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); filterCantBlock.add(new PowerPredicate(ComparisonType.FEWER_THAN, 4)); } @@ -44,10 +41,10 @@ public final class TemurCharm extends CardImpl { Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); this.getSpellAbility().addEffect(effect); effect = new FightTargetsEffect(); - effect.setText("That creature fights target creature you don't control"); + effect.setText("It fights target creature you don't control"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - Target target = new TargetCreaturePermanent(filter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL); this.getSpellAbility().addTarget(target); // Counter target spell unless its controller pays {3}. @@ -60,7 +57,6 @@ public final class TemurCharm extends CardImpl { mode = new Mode(); mode.addEffect(new CantBlockAllEffect(filterCantBlock, Duration.EndOfTurn)); this.getSpellAbility().addMode(mode); - } private TemurCharm(final TemurCharm card) { diff --git a/Mage.Sets/src/mage/cards/t/TemurWarShaman.java b/Mage.Sets/src/mage/cards/t/TemurWarShaman.java index 5e3e4371da..948dfefb60 100644 --- a/Mage.Sets/src/mage/cards/t/TemurWarShaman.java +++ b/Mage.Sets/src/mage/cards/t/TemurWarShaman.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -11,30 +9,24 @@ import mage.abilities.effects.keyword.ManifestEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.TargetController; +import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TemurWarShaman extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public TemurWarShaman(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(4); @@ -45,11 +37,11 @@ public final class TemurWarShaman extends CardImpl { // Whenever a permanent you control is turned face up, if it is a creature, you may have it fight target creature you don't control. Ability ability = new TemurWarShamanTriggeredAbility(); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); } - public TemurWarShaman(final TemurWarShaman card) { + private TemurWarShaman(final TemurWarShaman card) { super(card); } @@ -61,11 +53,11 @@ public final class TemurWarShaman extends CardImpl { class TemurWarShamanTriggeredAbility extends TurnedFaceUpAllTriggeredAbility { - public TemurWarShamanTriggeredAbility() { + TemurWarShamanTriggeredAbility() { super(Zone.BATTLEFIELD, new TemurWarShamanFightEffect(), new FilterControlledCreaturePermanent(), true, true); } - public TemurWarShamanTriggeredAbility(final TemurWarShamanTriggeredAbility ability) { + private TemurWarShamanTriggeredAbility(final TemurWarShamanTriggeredAbility ability) { super(ability); } @@ -86,7 +78,7 @@ class TemurWarShamanFightEffect extends OneShotEffect { super(Outcome.Damage); } - TemurWarShamanFightEffect(final TemurWarShamanFightEffect effect) { + private TemurWarShamanFightEffect(final TemurWarShamanFightEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/t/TenaciousDead.java b/Mage.Sets/src/mage/cards/t/TenaciousDead.java index a9f9a1e0e2..54c980379d 100644 --- a/Mage.Sets/src/mage/cards/t/TenaciousDead.java +++ b/Mage.Sets/src/mage/cards/t/TenaciousDead.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DoIfCostPaid; @@ -29,7 +29,7 @@ public final class TenaciousDead extends CardImpl { // When Tenacious Dead dies, you may pay {1}{B}. If you do, return it to the battlefield tapped under its owner's control. Effect effect = new DoIfCostPaid(new ReturnToBattlefieldUnderOwnerControlSourceEffect(true), new ManaCostsImpl("{1}{B}")); - this.addAbility(new DiesTriggeredAbility(effect, false)); + this.addAbility(new DiesSourceTriggeredAbility(effect, false)); } diff --git a/Mage.Sets/src/mage/cards/t/TentativeConnection.java b/Mage.Sets/src/mage/cards/t/TentativeConnection.java new file mode 100644 index 0000000000..0683d1d77e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TentativeConnection.java @@ -0,0 +1,63 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TentativeConnection extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("you control a creature with menace"); + + static { + filter.add(new AbilityPredicate(MenaceAbility.class)); + } + + public TentativeConnection(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // This spell costs {3} less to cast if you control a creature with menace. + Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(3, condition)); + ability.setRuleAtTheTop(true); + ability.addHint(new ConditionHint(condition, "You control a creature with menace")); + this.addAbility(ability); + + // Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn).setText("It gains haste until end of turn.")); + } + + private TentativeConnection(final TentativeConnection card) { + super(card); + } + + @Override + public TentativeConnection copy() { + return new TentativeConnection(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java b/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java index 3f1dd36652..b4e5220460 100644 --- a/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java +++ b/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java @@ -1,6 +1,5 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -13,6 +12,8 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; +import java.util.UUID; + /** * @author TheElk801 */ @@ -33,7 +34,7 @@ public final class TenthDistrictLegionnaire extends CardImpl { Ability ability = new HeroicAbility(new AddCountersSourceEffect( CounterType.P1P1.createInstance() ), false, false); - ability.addEffect(new ScryEffect(1).setText(", then scry 1")); + ability.addEffect(new ScryEffect(1).concatBy(", then")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TerashisGrasp.java b/Mage.Sets/src/mage/cards/t/TerashisGrasp.java index 5b9caf0c70..0ec0dd5987 100644 --- a/Mage.Sets/src/mage/cards/t/TerashisGrasp.java +++ b/Mage.Sets/src/mage/cards/t/TerashisGrasp.java @@ -56,7 +56,7 @@ public final class TerashisGrasp extends CardImpl { @Override public boolean apply(Game game, Ability source) { - Permanent targetPermanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent targetPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (targetPermanent != null) { int cost = targetPermanent.getConvertedManaCost(); Player player = game.getPlayer(source.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/t/Terastodon.java b/Mage.Sets/src/mage/cards/t/Terastodon.java index 4a73c47b75..3adf3d54bb 100644 --- a/Mage.Sets/src/mage/cards/t/Terastodon.java +++ b/Mage.Sets/src/mage/cards/t/Terastodon.java @@ -88,7 +88,7 @@ class TerastodonEffect extends OneShotEffect { } } } - game.applyEffects(); + game.getState().processAction(game); ElephantToken elephantToken = new ElephantToken(); for (Entry entry : destroyedPermanents.entrySet()) { elephantToken.putOntoBattlefield(entry.getValue(), game, source.getSourceId(), entry.getKey()); diff --git a/Mage.Sets/src/mage/cards/t/Terminus.java b/Mage.Sets/src/mage/cards/t/Terminus.java index 3fd08eba5d..d66aff26f2 100644 --- a/Mage.Sets/src/mage/cards/t/Terminus.java +++ b/Mage.Sets/src/mage/cards/t/Terminus.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; @@ -9,12 +7,15 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.MiracleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.other.OwnerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; /** * @@ -23,13 +24,11 @@ import mage.game.permanent.Permanent; public final class Terminus extends CardImpl { public Terminus(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{W}{W}"); - - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}{W}"); // Put all creatures on the bottom of their owners' libraries. this.getSpellAbility().addEffect(new TerminusEffect()); - + // Miracle {W} this.addAbility(new MiracleAbility(this, new ManaCostsImpl("{W}"))); } @@ -61,10 +60,18 @@ class TerminusEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List permanents = game.getBattlefield().getActivePermanents( - StaticFilters.FILTER_PERMANENT_CREATURES, source.getControllerId(), source.getSourceId(), game); - for (Permanent permanent : permanents) { - permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + filter.add(new OwnerIdPredicate(player.getId())); + Cards toLib = new CardsImpl(); + for (Permanent permanent : game.getBattlefield() + .getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + toLib.add(permanent); + } + player.putCardsOnBottomOfLibrary(toLib, game, source, true); + } } return true; } diff --git a/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java b/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java new file mode 100644 index 0000000000..a27527b2a2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java @@ -0,0 +1,152 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.target.common.TargetAnyTarget; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TerrorOfThePeaks extends CardImpl { + + public TerrorOfThePeaks(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast. + this.addAbility(new SimpleStaticAbility(new TerrorOfThePeaksCostIncreaseEffect())); + + // Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any target. + this.addAbility(new TerrorOfThePeaksTriggeredAbility()); + } + + private TerrorOfThePeaks(final TerrorOfThePeaks card) { + super(card); + } + + @Override + public TerrorOfThePeaks copy() { + return new TerrorOfThePeaks(this); + } +} + +class TerrorOfThePeaksCostIncreaseEffect extends CostModificationEffectImpl { + + TerrorOfThePeaksCostIncreaseEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); + this.staticText = "Spells your opponents cast that target {this} cost an additional 3 life to cast"; + } + + private TerrorOfThePeaksCostIncreaseEffect(TerrorOfThePeaksCostIncreaseEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + abilityToModify.addCost(new PayLifeCost(3)); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + if (!(abilityToModify instanceof SpellAbility)) { + return false; + } + + if (!game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { + return false; + } + + Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); + Set allTargets; + if (spell != null) { + // real cast + allTargets = CardUtil.getAllSelectedTargets(abilityToModify, game); + } else { + // playable + allTargets = CardUtil.getAllPossibleTargets(abilityToModify, game); + + // can target without cost increase + if (allTargets.stream().anyMatch(target -> !isTargetCompatible(target, source, game))) { + return false; + } + ; + } + + return allTargets.stream().anyMatch(target -> isTargetCompatible(target, source, game)); + } + + private boolean isTargetCompatible(UUID target, Ability source, Game game) { + // target {this} + return Objects.equals(source.getSourceId(), target); + } + + @Override + public TerrorOfThePeaksCostIncreaseEffect copy() { + return new TerrorOfThePeaksCostIncreaseEffect(this); + } +} + +class TerrorOfThePeaksTriggeredAbility extends EntersBattlefieldAllTriggeredAbility { + + TerrorOfThePeaksTriggeredAbility() { + super(null, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE); + } + + private TerrorOfThePeaksTriggeredAbility(final TerrorOfThePeaksTriggeredAbility ability) { + super(ability); + } + + @Override + public TerrorOfThePeaksTriggeredAbility copy() { + return new TerrorOfThePeaksTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!super.checkTrigger(event, game)) { + return false; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null) { + return false; + } + this.getEffects().clear(); + this.getTargets().clear(); + this.addEffect(new DamageTargetEffect(permanent.getPower().getValue())); + this.addTarget(new TargetAnyTarget().withChooseHint("gets " + permanent.getPower().getValue() + " damage")); + return true; + } + + @Override + public String getRule() { + return "Whenever another creature enters the battlefield under your control, " + + "{this} deals damage equal to that creature's power to any target."; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java b/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java index 0e99542953..a0980c9bec 100644 --- a/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java +++ b/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java @@ -111,7 +111,7 @@ class TestamentOfFaithBecomesCreatureSourceEffect extends ContinuousEffectImpl i if (sublayer == SubLayer.NA) { if (!token.getAbilities().isEmpty()) { for (Ability ability: token.getAbilities()) { - permanent.addAbility(ability, source.getSourceId(), game, false); + permanent.addAbility(ability, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java b/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java index 16a82f9d07..520f0b35a5 100644 --- a/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java +++ b/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; @@ -12,11 +10,7 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; @@ -25,14 +19,16 @@ import mage.game.permanent.token.SpiritWhiteToken; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author emerald000 */ public final class TeysaOrzhovScion extends CardImpl { - + private static final FilterControlledCreaturePermanent filterWhite = new FilterControlledCreaturePermanent("three white creatures"); private static final FilterCreaturePermanent filterBlack = new FilterCreaturePermanent("another black creature you control"); + static { filterWhite.add(new ColorPredicate(ObjectColor.WHITE)); filterBlack.add(new ColorPredicate(ObjectColor.BLACK)); @@ -41,7 +37,7 @@ public final class TeysaOrzhovScion extends CardImpl { } public TeysaOrzhovScion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ADVISOR); @@ -53,9 +49,9 @@ public final class TeysaOrzhovScion extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new SacrificeTargetCost(new TargetControlledCreaturePermanent(3, 3, filterWhite, true))); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); - + // Whenever another black creature you control dies, create a 1/1 white Spirit creature token with flying. - this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken("GPT")), false, filterBlack)); + this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken()), false, filterBlack)); } public TeysaOrzhovScion(final TeysaOrzhovScion card) { diff --git a/Mage.Sets/src/mage/cards/t/TezzeretArtificeMaster.java b/Mage.Sets/src/mage/cards/t/TezzeretArtificeMaster.java index 472be04176..a8cb9d8aa3 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretArtificeMaster.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretArtificeMaster.java @@ -1,6 +1,5 @@ package mage.cards.t; -import java.util.UUID; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.condition.common.MetalcraftCondition; @@ -8,16 +7,19 @@ import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GetEmblemEffect; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.game.command.emblems.TezzeretArtificeMasterEmblem; import mage.game.permanent.token.ThopterColorlessToken; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class TezzeretArtificeMaster extends CardImpl { @@ -38,8 +40,10 @@ public final class TezzeretArtificeMaster extends CardImpl { new DrawCardSourceControllerEffect(1), MetalcraftCondition.instance, "Draw a card. If you control three or " - + "more artifacts, draw two cards instead" - ), 0)); + + "more artifacts, draw two cards instead" + ), 0) + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance)); // −9: You get an emblem with "At the beginning of your end step, search your library for a permanent card, put it into the battlefield, then shuffle your library." this.addAbility(new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java b/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java index 368382bb53..3e5f13e3c0 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java @@ -1,27 +1,19 @@ - package mage.cards.t; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.RevealCardsFromLibraryUntilEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactCard; import mage.filter.common.FilterControlledArtifactPermanent; @@ -31,8 +23,10 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetOpponent; import mage.target.targetpointer.FixedTarget; +import java.util.List; +import java.util.UUID; + /** - * * @author Styxo */ public final class TezzeretMasterOfMetal extends CardImpl { @@ -48,8 +42,10 @@ public final class TezzeretMasterOfMetal extends CardImpl { this.addAbility(new LoyaltyAbility(new RevealCardsFromLibraryUntilEffect(new FilterArtifactCard(), Zone.HAND, Zone.LIBRARY), 1)); // -3: Target opponent loses life equal to the number of artifacts you control. - Ability ability = new LoyaltyAbility(new LoseLifeTargetEffect(new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent())), -3); + DynamicValue xValue = new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent()); + Ability ability = new LoyaltyAbility(new LoseLifeTargetEffect(xValue), -3); ability.addTarget(new TargetOpponent()); + ability.addHint(new ValueHint("Artifacts you control", xValue)); this.addAbility(ability); // -8: Gain control of all artifacts and creatures target opponent controls. @@ -95,7 +91,7 @@ class TezzeretMasterOfMetalEffect extends OneShotEffect { List permanents = game.getBattlefield().getAllActivePermanents(filter, targetPointer.getFirst(game, source), game); for (Permanent permanent : permanents) { ContinuousEffect effect = new TezzeretMasterOfMetalControlEffect(source.getControllerId()); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/t/TezzeretMasterOfTheBridge.java b/Mage.Sets/src/mage/cards/t/TezzeretMasterOfTheBridge.java index 0b528fc166..2493c99a92 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretMasterOfTheBridge.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretMasterOfTheBridge.java @@ -13,7 +13,6 @@ import mage.abilities.keyword.AffinityForArtifactsAbility; import mage.cards.*; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.filter.common.FilterArtifactCard; import mage.filter.predicate.Predicates; @@ -28,7 +27,7 @@ import java.util.UUID; */ public final class TezzeretMasterOfTheBridge extends CardImpl { - private static final FilterSpell filter = new FilterSpell("creature and planeswalker spells"); + private static final FilterCard filter = new FilterCard("creature and planeswalker spells"); static { filter.add(Predicates.or( diff --git a/Mage.Sets/src/mage/cards/t/TezzeretsSimulacrum.java b/Mage.Sets/src/mage/cards/t/TezzeretsSimulacrum.java index 0749fb4e45..3c9e33e2fc 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretsSimulacrum.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretsSimulacrum.java @@ -1,28 +1,35 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.hint.ConditionHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterPlaneswalkerPermanent; +import mage.filter.common.FilterControlledPlaneswalkerPermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TezzeretsSimulacrum extends CardImpl { + private static final FilterControlledPlaneswalkerPermanent filter = new FilterControlledPlaneswalkerPermanent(); + + static { + filter.add(SubType.TEZZERET.getPredicate()); + } + public TezzeretsSimulacrum(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); @@ -31,13 +38,13 @@ public final class TezzeretsSimulacrum extends CardImpl { this.toughness = new MageInt(3); // {T}: Target opponent loses 1 life. If you control a Tezzeret planeswalker, that player loses 3 life instead. - FilterPlaneswalkerPermanent filter = new FilterPlaneswalkerPermanent(); - filter.add(SubType.TEZZERET.getPredicate()); + Condition condition = new PermanentsOnTheBattlefieldCondition(filter); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalOneShotEffect(new LoseLifeTargetEffect(3), new LoseLifeTargetEffect(1), - new PermanentsOnTheBattlefieldCondition(filter), + condition, "Target opponent loses 1 life. If you control a Tezzeret planeswalker, that player loses 3 life instead"), new TapSourceCost()); ability.addTarget(new TargetOpponent()); + ability.addHint(new ConditionHint(condition, "You control a Tezzeret planeswalker")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/ThaliaGuardianOfThraben.java b/Mage.Sets/src/mage/cards/t/ThaliaGuardianOfThraben.java index 4176be6c9c..985e87e5de 100644 --- a/Mage.Sets/src/mage/cards/t/ThaliaGuardianOfThraben.java +++ b/Mage.Sets/src/mage/cards/t/ThaliaGuardianOfThraben.java @@ -1,26 +1,28 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.abilities.keyword.FirstStrikeAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.Game; -import mage.util.CardUtil; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; + +import java.util.UUID; /** - * * @author BetaSteward */ public final class ThaliaGuardianOfThraben extends CardImpl { + private static final FilterCard filter = new FilterCard("Noncreature spells"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + public ThaliaGuardianOfThraben(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); addSuperType(SuperType.LEGENDARY); @@ -33,7 +35,7 @@ public final class ThaliaGuardianOfThraben extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // Noncreature spells cost {1} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ThaliaGuardianOfThrabenCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY))); } @@ -45,39 +47,4 @@ public final class ThaliaGuardianOfThraben extends CardImpl { public ThaliaGuardianOfThraben copy() { return new ThaliaGuardianOfThraben(this); } -} - -class ThaliaGuardianOfThrabenCostReductionEffect extends CostModificationEffectImpl { - - ThaliaGuardianOfThrabenCostReductionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Noncreature spells cost {1} more to cast"; - } - - ThaliaGuardianOfThrabenCostReductionEffect(ThaliaGuardianOfThrabenCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - CardUtil.increaseCost(abilityToModify, 1); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - Card card = game.getCard(abilityToModify.getSourceId()); - if (card != null && !card.isCreature()) { - return true; - } - } - return false; - } - - @Override - public ThaliaGuardianOfThrabenCostReductionEffect copy() { - return new ThaliaGuardianOfThrabenCostReductionEffect(this); - } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/ThaliasLieutenant.java b/Mage.Sets/src/mage/cards/t/ThaliasLieutenant.java index a22addd19b..2f13d3d180 100644 --- a/Mage.Sets/src/mage/cards/t/ThaliasLieutenant.java +++ b/Mage.Sets/src/mage/cards/t/ThaliasLieutenant.java @@ -21,7 +21,7 @@ import mage.filter.predicate.permanent.AnotherPredicate; */ public final class ThaliasLieutenant extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("each other Human you control"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another Human you control"); static { filter.add(AnotherPredicate.instance); diff --git a/Mage.Sets/src/mage/cards/t/ThassaDeepDwelling.java b/Mage.Sets/src/mage/cards/t/ThassaDeepDwelling.java index 41b047baf5..dd0345c002 100644 --- a/Mage.Sets/src/mage/cards/t/ThassaDeepDwelling.java +++ b/Mage.Sets/src/mage/cards/t/ThassaDeepDwelling.java @@ -59,7 +59,7 @@ public final class ThassaDeepDwelling extends CardImpl { new ExileTargetForSourceEffect(), TargetController.YOU, false ); - ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(true).concatBy(", then")); + ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect().concatBy(", then")); ability.addTarget(new TargetPermanent( 0, 1, filterOther, false )); diff --git a/Mage.Sets/src/mage/cards/t/ThassasOracle.java b/Mage.Sets/src/mage/cards/t/ThassasOracle.java index a7ee33cc0e..49fca6d9b8 100644 --- a/Mage.Sets/src/mage/cards/t/ThassasOracle.java +++ b/Mage.Sets/src/mage/cards/t/ThassasOracle.java @@ -29,7 +29,10 @@ public final class ThassasOracle extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(3); - // When Thassa's Oracle enters the battlefield, look at the top X cards of your library, where X is your devotion to blue. Put one of them on top of your library and the rest on the bottom of your library in a random order. If X is greater than or equal to the number of cards in your library, you win the game. + // When Thassa's Oracle enters the battlefield, look at the top X cards of your library, where X is your devotion to blue. + // Put up to one of them on top of your library and the rest on the bottom of your library in a random order. + // If X is greater than or equal to the number of cards in your library, you win the game. + // (Each Blue in the mana costs of permanents you control counts toward your devotion to blue.) this.addAbility(new EntersBattlefieldTriggeredAbility(new ThassasOracleEffect()) .addHint(DevotionCount.U.getHint())); } diff --git a/Mage.Sets/src/mage/cards/t/TheAkroanWar.java b/Mage.Sets/src/mage/cards/t/TheAkroanWar.java index 2a5cac325b..19891d2612 100644 --- a/Mage.Sets/src/mage/cards/t/TheAkroanWar.java +++ b/Mage.Sets/src/mage/cards/t/TheAkroanWar.java @@ -18,6 +18,7 @@ import mage.game.Game; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; +import mage.abilities.condition.common.SourceRemainsInZoneCondition; /** * @author TheElk801 @@ -42,8 +43,8 @@ public final class TheAkroanWar extends CardImpl { SagaChapter.CHAPTER_I, new ConditionalContinuousEffect( new GainControlTargetEffect(Duration.Custom, true), - SourceOnBattlefieldCondition.instance, "gain control of target creature " + - "for as long as {this} remains on the battlefield" + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), + "gain control of target creature for as long as {this} remains on the battlefield" ), new TargetCreaturePermanent() ); diff --git a/Mage.Sets/src/mage/cards/t/TheBattleOfNaboo.java b/Mage.Sets/src/mage/cards/t/TheBattleOfNaboo.java index 75e60e1bf5..569b56fe78 100644 --- a/Mage.Sets/src/mage/cards/t/TheBattleOfNaboo.java +++ b/Mage.Sets/src/mage/cards/t/TheBattleOfNaboo.java @@ -74,7 +74,7 @@ class TheBattleOfNabooEffect extends OneShotEffect { if (player != null) { int x = source.getManaCostsToPay().getX(); if (x > 0) { - player.drawCards(2 * x, game); + player.drawCards(2 * x, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/t/TheCauldronOfEternity.java b/Mage.Sets/src/mage/cards/t/TheCauldronOfEternity.java index e4a8026e2e..a1d627c1de 100644 --- a/Mage.Sets/src/mage/cards/t/TheCauldronOfEternity.java +++ b/Mage.Sets/src/mage/cards/t/TheCauldronOfEternity.java @@ -1,25 +1,25 @@ package mage.cards.t; -import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SuperType; +import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; -import mage.util.CardUtil; import java.util.UUID; @@ -34,7 +34,11 @@ public final class TheCauldronOfEternity extends CardImpl { this.addSuperType(SuperType.LEGENDARY); // This spell costs {2} less to cast for each creature card in your graveyard. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new TheCauldronOfEternityCostReductionEffect())); + DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(2, xValue)); + ability.setRuleAtTheTop(true); + ability.addHint(new ValueHint("Creature card in your graveyard", xValue)); + this.addAbility(ability); // Whenever a creature you control dies, put it on the bottom of its owner's library. this.addAbility(new DiesCreatureTriggeredAbility( @@ -43,7 +47,7 @@ public final class TheCauldronOfEternity extends CardImpl { )); // {2}{B}, {T}, Pay 2 life: Return target creature card from your graveyard to the battlefield. Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility( + ability = new ActivateAsSorceryActivatedAbility( Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{2}{B}") ); ability.addCost(new TapSourceCost()); @@ -61,44 +65,3 @@ public final class TheCauldronOfEternity extends CardImpl { return new TheCauldronOfEternity(this); } } - -class TheCauldronOfEternityCostReductionEffect extends CostModificationEffectImpl { - - TheCauldronOfEternityCostReductionEffect() { - super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "This spell costs {2} less to cast for each creature card in your graveyard"; - } - - private TheCauldronOfEternityCostReductionEffect(final TheCauldronOfEternityCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - int reductionAmount = player - .getGraveyard() - .getCards(game) - .stream() - .filter(MageObject::isCreature) - .mapToInt(card -> 2) - .sum(); - CardUtil.reduceCost(abilityToModify, reductionAmount); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify instanceof SpellAbility - && abilityToModify.getSourceId().equals(source.getSourceId()) - && game.getCard(abilityToModify.getSourceId()) != null; - } - - @Override - public TheCauldronOfEternityCostReductionEffect copy() { - return new TheCauldronOfEternityCostReductionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java b/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java index ef80797958..6acd774073 100644 --- a/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java +++ b/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java @@ -1,24 +1,23 @@ package mage.cards.t; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterPermanent; import mage.filter.FilterSpell; import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; import mage.game.permanent.token.KnightToken; -import mage.util.CardUtil; import java.util.UUID; @@ -27,10 +26,16 @@ import java.util.UUID; */ public final class TheCircleOfLoyalty extends CardImpl { - private static final FilterSpell filter = new FilterSpell("a legendary spell"); + private static final FilterSpell filterLegendary = new FilterSpell("a legendary spell"); static { - filter.add(SuperType.LEGENDARY.getPredicate()); + filterLegendary.add(SuperType.LEGENDARY.getPredicate()); + } + + static final FilterControlledPermanent filterKnight = new FilterControlledPermanent("Knight you control"); + + static { + filterKnight.add(SubType.KNIGHT.getPredicate()); } public TheCircleOfLoyalty(UUID ownerId, CardSetInfo setInfo) { @@ -39,7 +44,10 @@ public final class TheCircleOfLoyalty extends CardImpl { this.addSuperType(SuperType.LEGENDARY); // This spell costs {1} less to cast for each Knight you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new TheCircleOfLoyaltyCostReductionEffect())); + DynamicValue xValue = new PermanentsOnBattlefieldCount(filterKnight); + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue) + ).addHint(new ValueHint("Knight you control", xValue))); // Creatures you control get +1/+1. this.addAbility(new SimpleStaticAbility( @@ -48,7 +56,7 @@ public final class TheCircleOfLoyalty extends CardImpl { // Whenever you cast a legendary spell, create a 2/2 white Knight creature token with vigilance. this.addAbility(new SpellCastControllerTriggeredAbility( - new CreateTokenEffect(new KnightToken()), filter, false + new CreateTokenEffect(new KnightToken()), filterLegendary, false )); // {3}{W}, {T}: Create a 2/2 white Knight creature token with vigilance. @@ -67,37 +75,4 @@ public final class TheCircleOfLoyalty extends CardImpl { public TheCircleOfLoyalty copy() { return new TheCircleOfLoyalty(this); } -} - -class TheCircleOfLoyaltyCostReductionEffect extends CostModificationEffectImpl { - - private static final FilterPermanent filter = new FilterControlledPermanent(SubType.KNIGHT); - - TheCircleOfLoyaltyCostReductionEffect() { - super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "This spell costs {1} less to cast for each Knight you control"; - } - - private TheCircleOfLoyaltyCostReductionEffect(final TheCircleOfLoyaltyCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - int reductionAmount = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); - CardUtil.reduceCost(abilityToModify, reductionAmount); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify instanceof SpellAbility - && abilityToModify.getSourceId().equals(source.getSourceId()) - && game.getCard(abilityToModify.getSourceId()) != null; - } - - @Override - public TheCircleOfLoyaltyCostReductionEffect copy() { - return new TheCircleOfLoyaltyCostReductionEffect(this); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TheFirstSliver.java b/Mage.Sets/src/mage/cards/t/TheFirstSliver.java index 77fc4d84cd..d3381480e4 100644 --- a/Mage.Sets/src/mage/cards/t/TheFirstSliver.java +++ b/Mage.Sets/src/mage/cards/t/TheFirstSliver.java @@ -9,7 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterSpell; +import mage.filter.FilterCard; import java.util.UUID; @@ -18,7 +18,7 @@ import java.util.UUID; */ public final class TheFirstSliver extends CardImpl { - private static final FilterSpell filter = new FilterSpell("Sliver spells you cast"); + private static final FilterCard filter = new FilterCard("Sliver spells you cast"); static { filter.add(SubType.SLIVER.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/t/TheGitrogMonster.java b/Mage.Sets/src/mage/cards/t/TheGitrogMonster.java index 8fc2ee9ce1..a4073f519e 100644 --- a/Mage.Sets/src/mage/cards/t/TheGitrogMonster.java +++ b/Mage.Sets/src/mage/cards/t/TheGitrogMonster.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.Set; import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; @@ -29,7 +27,7 @@ import mage.target.common.TargetControlledPermanent; public final class TheGitrogMonster extends CardImpl { public TheGitrogMonster(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{G}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.FROG); this.subtype.add(SubType.HORROR); @@ -79,12 +77,10 @@ class TheGitrogMonsterTriggeredAbility extends TriggeredAbilityImpl { ZoneChangeGroupEvent zEvent = (ZoneChangeGroupEvent) event; if (zEvent != null && Zone.GRAVEYARD == zEvent.getToZone() && zEvent.getCards() != null) { for (Card card : zEvent.getCards()) { - if (card != null) { + if (card != null) { UUID cardOwnerId = card.getOwnerId(); - Set cardType = card.getCardType(); if (cardOwnerId != null && card.isOwnedBy(getControllerId()) - && cardType != null && card.isLand()) { return true; } diff --git a/Mage.Sets/src/mage/cards/t/TheGreatAurora.java b/Mage.Sets/src/mage/cards/t/TheGreatAurora.java index 5756d961aa..063ae0af37 100644 --- a/Mage.Sets/src/mage/cards/t/TheGreatAurora.java +++ b/Mage.Sets/src/mage/cards/t/TheGreatAurora.java @@ -90,7 +90,7 @@ class TheGreatAuroraEffect extends OneShotEffect { } } - game.applyEffects(); // so effects from creatures that were on the battlefield won't trigger from draw or put into play + game.getState().processAction(game); // so effects from creatures that were on the battlefield won't trigger from draw or put into play // Draw cards for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { @@ -98,7 +98,7 @@ class TheGreatAuroraEffect extends OneShotEffect { if (player != null) { int count = permanentsCount.get(playerId); if (count > 0) { - player.drawCards(count, game); + player.drawCards(count, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/t/TheImmortalSun.java b/Mage.Sets/src/mage/cards/t/TheImmortalSun.java index f2795b87c9..dd3c5078cf 100644 --- a/Mage.Sets/src/mage/cards/t/TheImmortalSun.java +++ b/Mage.Sets/src/mage/cards/t/TheImmortalSun.java @@ -1,8 +1,5 @@ - package mage.cards.t; -import java.util.Optional; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; @@ -14,19 +11,16 @@ import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterCard; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import java.util.Optional; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TheImmortalSun extends CardImpl { @@ -38,11 +32,14 @@ public final class TheImmortalSun extends CardImpl { // Players can't activate planeswalkers' loyalty abilities. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new TheImmortalSunCantActivateEffect())); + // At the beginning of your draw step, draw an additional card. this.addAbility(new BeginningOfDrawTriggeredAbility(new DrawCardSourceControllerEffect(1) .setText("draw an additional card"), TargetController.YOU, false)); + // Spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCard("Spells"), 1))); + // Creatures you control get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield))); } diff --git a/Mage.Sets/src/mage/cards/t/TheLocustGod.java b/Mage.Sets/src/mage/cards/t/TheLocustGod.java index 752b438e49..0e3c4852e2 100644 --- a/Mage.Sets/src/mage/cards/t/TheLocustGod.java +++ b/Mage.Sets/src/mage/cards/t/TheLocustGod.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.DrawCardControllerTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -52,7 +52,7 @@ public final class TheLocustGod extends CardImpl { this.addAbility(ability); // When The Locust God dies, return it to its owner's hand at the beginning of the next end step. - this.addAbility(new DiesTriggeredAbility(new TheLocustGodEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new TheLocustGodEffect())); } public TheLocustGod(final TheLocustGod card) { diff --git a/Mage.Sets/src/mage/cards/t/TheMagicMirror.java b/Mage.Sets/src/mage/cards/t/TheMagicMirror.java index 9090a80225..49b1ec23c7 100644 --- a/Mage.Sets/src/mage/cards/t/TheMagicMirror.java +++ b/Mage.Sets/src/mage/cards/t/TheMagicMirror.java @@ -1,39 +1,39 @@ package mage.cards.t; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; -import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; import mage.filter.StaticFilters; -import java.util.UUID; - /** * @author TheElk801 */ public final class TheMagicMirror extends CardImpl { - private static final DynamicValue xValue = new CountersSourceCount(CounterType.KNOWLEDGE); - public TheMagicMirror(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}{U}{U}{U}"); this.addSuperType(SuperType.LEGENDARY); // This spell costs {1} less to cast for each instant and sorcery card in your graveyard. - this.addAbility(new SimpleStaticAbility( - Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect( - StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY - )).setRuleAtTheTop(true)); + DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)); + ability.setRuleAtTheTop(true); + ability.addHint(new ValueHint("Instant and sorcery card in your graveyard", xValue)); + this.addAbility(ability); // You have no maximum hand size. this.addAbility(new SimpleStaticAbility(new MaximumHandSizeControllerEffect( @@ -42,12 +42,11 @@ public final class TheMagicMirror extends CardImpl { ))); // At the beginning of your upkeep, put a knowledge counter on The Magic Mirror, then draw a card for each knowledge counter on The Magic Mirror. - Ability ability = new BeginningOfUpkeepTriggeredAbility( + ability = new BeginningOfUpkeepTriggeredAbility( new AddCountersSourceEffect(CounterType.KNOWLEDGE.createInstance()) .setText("put a knowledge counter on {this},"), - TargetController.YOU, false - ); - ability.addEffect(new DrawCardSourceControllerEffect(xValue).concatBy("then")); + TargetController.YOU, false); + ability.addEffect(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.KNOWLEDGE)).concatBy("then")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java b/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java index bfd4f42c4d..3438e2374b 100644 --- a/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java +++ b/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java @@ -56,7 +56,7 @@ class TheMendingOfDominariaFirstEffect extends OneShotEffect { public TheMendingOfDominariaFirstEffect() { super(Outcome.ReturnToHand); - this.staticText = "Put the top two cards of your library into your graveyard, then you may return a creature card from your graveyard to your hand"; + this.staticText = "Mill two cards, then you may return a creature card from your graveyard to your hand"; } public TheMendingOfDominariaFirstEffect(final TheMendingOfDominariaFirstEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TheOzolith.java b/Mage.Sets/src/mage/cards/t/TheOzolith.java new file mode 100644 index 0000000000..549d980355 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheOzolith.java @@ -0,0 +1,181 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.LeavesBattlefieldAllTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.counters.Counter; +import mage.counters.Counters; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheOzolith extends CardImpl { + + public TheOzolith(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.addSuperType(SuperType.LEGENDARY); + + // Whenever a creature you control leaves the battlefield, if it had counters on it, put those counters on The Ozolith. + this.addAbility(new TheOzolithTriggeredAbility()); + + // At the beginning of combat on your turn, if The Ozolith has counters on it, you may move all counters from The Ozolith onto target creature. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new BeginningOfCombatTriggeredAbility( + new TheOzolithMoveCountersEffect(), TargetController.YOU, true + ), TheOzolithCondition.instance, "At the beginning of combat on your turn, " + + "if {this} has counters on it, you may move all counters from {this} onto target creature." + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private TheOzolith(final TheOzolith card) { + super(card); + } + + @Override + public TheOzolith copy() { + return new TheOzolith(this); + } +} + +class TheOzolithTriggeredAbility extends LeavesBattlefieldAllTriggeredAbility { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(CounterAnyPredicate.instance); + } + + TheOzolithTriggeredAbility() { + super(null, filter); + } + + private TheOzolithTriggeredAbility(final TheOzolithTriggeredAbility ability) { + super(ability); + } + + public TheOzolithTriggeredAbility copy() { + return new TheOzolithTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!super.checkTrigger(event, game)) { + return false; + } + Permanent permanent = ((ZoneChangeEvent) event).getTarget(); + this.getEffects().clear(); + this.addEffect(new TheOzolithLeaveEffect(permanent.getCounters(game))); + return true; + } + + public String getRule() { + return "Whenever a creature you control leaves the battlefield, " + + "if it had counters on it, put those counters on {this}."; + } +} + +class TheOzolithLeaveEffect extends OneShotEffect { + + private final Counters counters; + + TheOzolithLeaveEffect(Counters counters) { + super(Outcome.Benefit); + this.counters = counters.copy(); + } + + private TheOzolithLeaveEffect(final TheOzolithLeaveEffect effect) { + super(effect); + this.counters = effect.counters.copy(); + } + + @Override + public TheOzolithLeaveEffect copy() { + return new TheOzolithLeaveEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return false; + } + counters.values() + .stream() + .forEach(counter -> permanent.addCounters(counter, source, game)); + return true; + } +} + +enum TheOzolithCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return false; + } + return permanent != null + && permanent + .getCounters(game) + .values() + .stream() + .mapToInt(Counter::getCount) + .max() + .orElse(0) > 0; + } +} + +class TheOzolithMoveCountersEffect extends OneShotEffect { + + TheOzolithMoveCountersEffect() { + super(Outcome.Benefit); + } + + private TheOzolithMoveCountersEffect(final TheOzolithMoveCountersEffect effect) { + super(effect); + } + + @Override + public TheOzolithMoveCountersEffect copy() { + return new TheOzolithMoveCountersEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent creature = game.getPermanent(source.getFirstTarget()); + if (permanent == null || creature == null) { + return false; + } + permanent.getCounters(game) + .copy() + .values() + .stream() + .filter(counter -> creature.addCounters(counter, source, game)) + .forEach(counter -> permanent.removeCounters(counter, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheRoyalScions.java b/Mage.Sets/src/mage/cards/t/TheRoyalScions.java index 83fda327c0..291bbe9b25 100644 --- a/Mage.Sets/src/mage/cards/t/TheRoyalScions.java +++ b/Mage.Sets/src/mage/cards/t/TheRoyalScions.java @@ -1,9 +1,9 @@ package mage.cards.t; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -18,7 +18,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetCreaturePermanent; @@ -89,41 +88,12 @@ class TheRoyalScionsCreateReflexiveTriggerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { effect.apply(game, source); - game.addDelayedTriggeredAbility(new TheRoyalScionsReflexiveTriggeredAbility(), source); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId(), 0)); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(CardsInControllerHandCount.instance), false, + "{this} deals damage to any target equal to the number of cards in your hand" + ); + ability.addTarget(new TargetAnyTarget()); + game.fireReflexiveTriggeredAbility(ability, source); return true; } } - -class TheRoyalScionsReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - TheRoyalScionsReflexiveTriggeredAbility() { - super(new DamageTargetEffect(CardsInControllerHandCount.instance), Duration.OneUse, true); - this.addTarget(new TargetAnyTarget()); - } - - private TheRoyalScionsReflexiveTriggeredAbility(final TheRoyalScionsReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public TheRoyalScionsReflexiveTriggeredAbility copy() { - return new TheRoyalScionsReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you do, {this} deals damage to any target equal to the number of cards in your hand."; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TheScarabGod.java b/Mage.Sets/src/mage/cards/t/TheScarabGod.java index 7a7f4fbf4c..aa810cbebe 100644 --- a/Mage.Sets/src/mage/cards/t/TheScarabGod.java +++ b/Mage.Sets/src/mage/cards/t/TheScarabGod.java @@ -7,7 +7,7 @@ import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -54,7 +54,7 @@ public final class TheScarabGod extends CardImpl { this.addAbility(ability); // When The Scarab God dies, return it to its owner's hand at the beginning of the next end step. - this.addAbility(new DiesTriggeredAbility(new TheScarabGodEffect3())); + this.addAbility(new DiesSourceTriggeredAbility(new TheScarabGodEffect3())); } public TheScarabGod(final TheScarabGod card) { @@ -134,7 +134,7 @@ class TheScarabGodEffect2 extends OneShotEffect { if (controller != null && card != null) { controller.moveCards(card, Zone.EXILED, source, game); // Also if the move to exile is replaced, the copy takes place CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, false, 1, false, false, null, 4, 4, false); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setOnlySubType(SubType.ZOMBIE); effect.setOnlyColor(ObjectColor.BLACK); effect.apply(game, source); diff --git a/Mage.Sets/src/mage/cards/t/TheScorpionGod.java b/Mage.Sets/src/mage/cards/t/TheScorpionGod.java index b973513f0e..b912a6d28e 100644 --- a/Mage.Sets/src/mage/cards/t/TheScorpionGod.java +++ b/Mage.Sets/src/mage/cards/t/TheScorpionGod.java @@ -6,7 +6,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -63,7 +63,7 @@ public final class TheScorpionGod extends CardImpl { this.addAbility(ability); // When The Scorpion God dies, return it to its owner's hand at the beginning of the next end step. - this.addAbility(new DiesTriggeredAbility(new TheScorpionGodEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new TheScorpionGodEffect())); } public TheScorpionGod(final TheScorpionGod card) { diff --git a/Mage.Sets/src/mage/cards/t/TheTabernacleAtPendrellVale.java b/Mage.Sets/src/mage/cards/t/TheTabernacleAtPendrellVale.java index 161c21967e..2f1b15afec 100644 --- a/Mage.Sets/src/mage/cards/t/TheTabernacleAtPendrellVale.java +++ b/Mage.Sets/src/mage/cards/t/TheTabernacleAtPendrellVale.java @@ -1,24 +1,24 @@ package mage.cards.t; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DestroySourceEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author Plopman + * @author TheElk801 */ public final class TheTabernacleAtPendrellVale extends CardImpl { @@ -28,12 +28,16 @@ public final class TheTabernacleAtPendrellVale extends CardImpl { // All creatures have "At the beginning of your upkeep, destroy this creature unless you pay {1}." this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - new TheTabernacleAtPendrellValeEffect()) - ); + new GainAbilityAllEffect(new BeginningOfUpkeepTriggeredAbility( + new DoIfCostPaid( + new InfoEffect(""), new DestroySourceEffect(), new GenericManaCost(1) + ).setText("destroy this creature unless you pay {1}"), + TargetController.YOU, false + ), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES) + )); } - public TheTabernacleAtPendrellVale(final TheTabernacleAtPendrellVale card) { + private TheTabernacleAtPendrellVale(final TheTabernacleAtPendrellVale card) { super(card); } @@ -42,100 +46,3 @@ public final class TheTabernacleAtPendrellVale extends CardImpl { return new TheTabernacleAtPendrellVale(this); } } - -class TheTabernacleAtPendrellValeEffect extends ContinuousEffectImpl { - - Ability ability = new BeginningOfUpkeepTriggeredAbility( - new DestroySourceUnlessPaysEffect( - new ManaCostsImpl("{1}")), - TargetController.YOU, - false); - - public TheTabernacleAtPendrellValeEffect() { - super(Duration.WhileOnBattlefield, Outcome.AddAbility); - ability = ability.copy(); - this.ability.newId(); - staticText = "All creatures have \"At the beginning of your upkeep, destroy this creature unless you pay {1}"; - } - - public TheTabernacleAtPendrellValeEffect(final TheTabernacleAtPendrellValeEffect effect) { - super(effect); - this.ability = effect.ability.copy(); - } - - @Override - public TheTabernacleAtPendrellValeEffect copy() { - return new TheTabernacleAtPendrellValeEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(CardType.CREATURE)) { - if (permanent != null) { - switch (layer) { - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA - && !permanent.getAbilities().contains(ability)) { - permanent.addAbility(ability, source.getSourceId(), game); - } - break; - } - } - } - return true; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6; - } -} - -class DestroySourceUnlessPaysEffect extends OneShotEffect { - - protected Cost cost; - - public DestroySourceUnlessPaysEffect(Cost cost) { - super(Outcome.DestroyPermanent); - this.cost = cost; - } - - public DestroySourceUnlessPaysEffect(final DestroySourceUnlessPaysEffect effect) { - super(effect); - this.cost = effect.cost; - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null - && permanent != null - && source.getSourceObjectZoneChangeCounter() == permanent.getZoneChangeCounter(game)) { - if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + '?', source, game)) { - cost.clearPaid(); - if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { - return true; - } - } - permanent.destroy(source.getSourceId(), game, false); - return true; - } - return false; - } - - @Override - public DestroySourceUnlessPaysEffect copy() { - return new DestroySourceUnlessPaysEffect(this); - } - - @Override - public String getText(Mode mode) { - return "destroy this creature unless you pay {1}"; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TheTriumphOfAnax.java b/Mage.Sets/src/mage/cards/t/TheTriumphOfAnax.java index 6748388c58..3ccf85307b 100644 --- a/Mage.Sets/src/mage/cards/t/TheTriumphOfAnax.java +++ b/Mage.Sets/src/mage/cards/t/TheTriumphOfAnax.java @@ -11,10 +11,12 @@ import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.Targets; import mage.target.common.TargetControlledCreaturePermanent; @@ -28,11 +30,6 @@ import java.util.UUID; public final class TheTriumphOfAnax extends CardImpl { private static final DynamicValue xValue = new CountersSourceCount(CounterType.LORE); - private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } public TheTriumphOfAnax(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); @@ -61,7 +58,7 @@ public final class TheTriumphOfAnax extends CardImpl { "Target creature you control fights up to one target creature you don't control" )), new Targets( new TargetControlledCreaturePermanent(), - new TargetPermanent(0, 1, filter, false) + new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false) ) ); this.addAbility(sagaAbility); diff --git a/Mage.Sets/src/mage/cards/t/TheWretched.java b/Mage.Sets/src/mage/cards/t/TheWretched.java index 61cf1d51ce..e06617533f 100644 --- a/Mage.Sets/src/mage/cards/t/TheWretched.java +++ b/Mage.Sets/src/mage/cards/t/TheWretched.java @@ -1,6 +1,6 @@ - package mage.cards.t; +import java.util.Objects; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -13,44 +13,61 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; import mage.game.combat.CombatGroup; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; import mage.watchers.common.BlockedAttackerWatcher; +import mage.watchers.common.LostControlWatcher; /** * * @author jeffwadsworth * -5/1/2009 The ability grants you control of all creatures that are blocking it as the ability resolves. This will include -* any creatures that were put onto the battlefield blocking it. -5/1/2009 Any blocking creatures that regenerated during combat will have been removed from combat. Since such creatures -* are no longer in combat, they cannot be blocking The Wretched, which means you won't be able to gain control of them. -5/1/2009 If The Wretched itself regenerated during combat, then it will have been removed from combat. Since it is no longer -* in combat, there cannot be any creatures blocking it, which means you won't be able to gain control of any creatures. -10/1/2009 The Wretched's ability triggers only if it's still on the battlefield when the end of combat step begins (after the -* combat damage step). For example, if it's blocked by a 7/7 creature and is destroyed, its ability won't trigger at all. -10/1/2009 If The Wretched leaves the battlefield, you no longer control it, so the duration of its control-change effect ends. -10/1/2009 If you lose control of The Wretched before its ability resolves, you won't gain control of the creatures blocking it at all. -10/1/2009 Once the ability resolves, it doesn't care whether the permanents you gained control of remain creatures, only that -* they remain on the battlefield. + * 5/1/2009 The ability grants you control of all creatures that are blocking it + * as the ability resolves. This will include any creatures that were put onto + * the battlefield blocking it. + * + * 5/1/2009 Any blocking creatures that regenerated during combat will have been + * removed from combat. Since such creatures are no longer in combat, they + * cannot be blocking The Wretched, which means you won't be able to gain + * control of them. + * + * 5/1/2009 If The Wretched itself regenerated during combat, then it will have + * been removed from combat. Since it is no longer in combat, there cannot be + * any creatures blocking it, which means you won't be able to gain control of + * any creatures. + * + * 10/1/2009 The Wretched's ability triggers only if it's still on the + * battlefield when the end of combat step begins (after the combat damage + * step). For example, if it's blocked by a 7/7 creature and is destroyed, its + * ability won't trigger at all. + * + * 10/1/2009 If The Wretched leaves the battlefield, you no longer control it, + * so the duration of its control-change effect ends. + * + * 10/1/2009 If you lose control of The Wretched before its ability resolves, + * you won't gain control of the creatures blocking it at all. + * + * 10/1/2009 Once the ability resolves, it doesn't care whether the permanents + * you gained control of remain creatures, only that they remain on the + * battlefield. */ - public final class TheWretched extends CardImpl { public TheWretched(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.DEMON); this.power = new MageInt(2); this.toughness = new MageInt(5); // At end of combat, gain control of all creatures blocking The Wretched for as long as you control The Wretched. - this.addAbility(new EndOfCombatTriggeredAbility(new TheWretchedEffect(), false), new BlockedAttackerWatcher()); - + Ability ability = new EndOfCombatTriggeredAbility(new TheWretchedEffect(), false); + this.addAbility(ability, new BlockedAttackerWatcher()); + ability.addWatcher(new LostControlWatcher()); } public TheWretched(final TheWretched card) { @@ -83,16 +100,20 @@ class TheWretchedEffect extends OneShotEffect { if (theWretched.isRemovedFromCombat() || !theWretched.isAttacking()) { return false; } - if (!new SourceOnBattlefieldControlUnchangedCondition().apply(game, source)) { + // Check if control of source has changed since ability triggered????? (does it work is it neccessary???) + Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); + if (permanent != null && !Objects.equals(permanent.getControllerId(), source.getControllerId())) { return false; } - for (CombatGroup combatGroup :game.getCombat().getGroups()) { + for (CombatGroup combatGroup : game.getCombat().getGroups()) { if (combatGroup.getAttackers().contains(source.getSourceId())) { - for(UUID creatureId: combatGroup.getBlockers()) { + for (UUID creatureId : combatGroup.getBlockers()) { Permanent blocker = game.getPermanent(creatureId); if (blocker != null && blocker.getBlocking() > 0) { - ContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom, source.getControllerId()), new SourceOnBattlefieldControlUnchangedCondition(), ""); + ContinuousEffect effect = new ConditionalContinuousEffect( + new GainControlTargetEffect(Duration.Custom, source.getControllerId()), + new SourceOnBattlefieldControlUnchangedCondition(), ""); effect.setTargetPointer(new FixedTarget(blocker.getId())); game.addEffect(effect, source); diff --git a/Mage.Sets/src/mage/cards/t/Thermopod.java b/Mage.Sets/src/mage/cards/t/Thermopod.java index 8550573f7a..7c1f6e8c0e 100644 --- a/Mage.Sets/src/mage/cards/t/Thermopod.java +++ b/Mage.Sets/src/mage/cards/t/Thermopod.java @@ -7,7 +7,9 @@ import mage.Mana; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -37,7 +39,7 @@ public final class Thermopod extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect( HasteAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{S}"))); // Sacrifice a creature: Add {R}. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana(1), + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new BasicManaEffect(Mana.RedMana(1), CreaturesYouControlCount.instance), new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT)))); } diff --git a/Mage.Sets/src/mage/cards/t/ThicketBasilisk.java b/Mage.Sets/src/mage/cards/t/ThicketBasilisk.java index ceb3f94855..989d765d6a 100644 --- a/Mage.Sets/src/mage/cards/t/ThicketBasilisk.java +++ b/Mage.Sets/src/mage/cards/t/ThicketBasilisk.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -37,7 +37,7 @@ public final class ThicketBasilisk extends CardImpl { // Whenever Thicket Basilisk blocks or becomes blocked by a non-Wall creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false)); } public ThicketBasilisk(final ThicketBasilisk card) { diff --git a/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java b/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java new file mode 100644 index 0000000000..40d870a3ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java @@ -0,0 +1,89 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.Graveyard; +import mage.players.Player; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThievesGuildEnforcer extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.ROGUE, "{this} or another Rogue"); + + public ThievesGuildEnforcer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Whenever Thieves' Guild Enforcer or another Rogue enters the battlefield under your control, each opponent mills two cards. + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new PutTopCardOfLibraryIntoGraveEachPlayerEffect( + 2, TargetController.OPPONENT + ), filter, false, true + )); + + // As long as an opponent has eight or more cards in their graveyard, Thieves' Guild Enforcer gets +2/+1 and has deathtouch. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(2, 1, Duration.WhileOnBattlefield), + ThievesGuildEnforcerCondition.instance, "as long as an opponent " + + "has eight or more cards in their graveyard, {this} gets +2/+1" + )); + ability.addEffect(new ConditionalContinuousEffect(new GainAbilitySourceEffect( + DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield + ), ThievesGuildEnforcerCondition.instance, "and has deathtouch")); + this.addAbility(ability); + } + + private ThievesGuildEnforcer(final ThievesGuildEnforcer card) { + super(card); + } + + @Override + public ThievesGuildEnforcer copy() { + return new ThievesGuildEnforcer(this); + } +} + +enum ThievesGuildEnforcerCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game + .getOpponents(source.getControllerId()) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getGraveyard) + .mapToInt(Graveyard::size) + .anyMatch(i -> i >= 8); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/ThievingOtter.java b/Mage.Sets/src/mage/cards/t/ThievingOtter.java new file mode 100644 index 0000000000..5ec8999db7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThievingOtter.java @@ -0,0 +1,37 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.DealsDamageToOpponentTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThievingOtter extends CardImpl { + + public ThievingOtter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.OTTER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Thieving Otter deals damage to an opponent, draw a card. + this.addAbility(new DealsDamageToOpponentTriggeredAbility(new DrawCardSourceControllerEffect(1))); + } + + private ThievingOtter(final ThievingOtter card) { + super(card); + } + + @Override + public ThievingOtter copy() { + return new ThievingOtter(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThornMammoth.java b/Mage.Sets/src/mage/cards/t/ThornMammoth.java index a361fa3a29..9435c6c1ac 100644 --- a/Mage.Sets/src/mage/cards/t/ThornMammoth.java +++ b/Mage.Sets/src/mage/cards/t/ThornMammoth.java @@ -2,17 +2,14 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.effects.common.FightTargetSourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; import java.util.UUID; @@ -22,12 +19,6 @@ import java.util.UUID; */ public final class ThornMammoth extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public ThornMammoth(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}"); @@ -39,12 +30,11 @@ public final class ThornMammoth extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever Thorn Mammoth or another creature enters the battlefield under your control, Thorn Mammoth fights up to one target creature you don't control. - Ability ability = new EntersBattlefieldControlledTriggeredAbility( - new FightTargetSourceEffect(), StaticFilters.FILTER_PERMANENT_CREATURE, - "Whenever {this} or another creature enters the battlefield under your control, " + - "{this} fights up to one target creature you don't control." + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new FightTargetSourceEffect().setText("{this} fights up to one target creature you don't control"), + StaticFilters.FILTER_PERMANENT_CREATURE, false, true ); - ability.addTarget(new TargetPermanent(0, 1, filter, false)); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/ThornOfAmethyst.java b/Mage.Sets/src/mage/cards/t/ThornOfAmethyst.java index 3de6135ad2..78eed2b2e1 100644 --- a/Mage.Sets/src/mage/cards/t/ThornOfAmethyst.java +++ b/Mage.Sets/src/mage/cards/t/ThornOfAmethyst.java @@ -1,31 +1,33 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; +import java.util.UUID; + /** - * * @author Plopman */ public final class ThornOfAmethyst extends CardImpl { private static final FilterCard filter = new FilterCard("Noncreature spells"); + static { filter.add(Predicates.not(CardType.CREATURE.getPredicate())); } + public ThornOfAmethyst(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Noncreature spells cost {1} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(filter, 1))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY))); } public ThornOfAmethyst(final ThornOfAmethyst card) { diff --git a/Mage.Sets/src/mage/cards/t/ThoughtCollapse.java b/Mage.Sets/src/mage/cards/t/ThoughtCollapse.java index ed4faf06f7..dae6de6149 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtCollapse.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtCollapse.java @@ -8,7 +8,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetSpell; @@ -44,8 +43,7 @@ class ThoughtCollapseEffect extends OneShotEffect { ThoughtCollapseEffect() { super(Outcome.Benefit); - staticText = "Counter target spell. Its controller puts " + - "the top three cards of their library into their graveyard."; + staticText = "Counter target spell. Its controller mills three cards"; } private ThoughtCollapseEffect(final ThoughtCollapseEffect effect) { @@ -63,7 +61,7 @@ class ThoughtCollapseEffect extends OneShotEffect { if (player == null) { return false; } - player.moveCards(player.getLibrary().getTopCards(game, 3), Zone.GRAVEYARD, source, game); + player.millCards(3, source, game); return effect.apply(game, source); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/t/ThoughtDissector.java b/Mage.Sets/src/mage/cards/t/ThoughtDissector.java index f817054089..d57da0a9b6 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtDissector.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtDissector.java @@ -90,7 +90,7 @@ class ThoughtDissectorEffect extends OneShotEffect { } targetOpponent.revealCards(source, reveal, game); if (artifact != null) { - game.applyEffects(); + game.getState().processAction(game); controller.moveCards(artifact, Zone.BATTLEFIELD, source, game); Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); if (sourcePermanent != null) { diff --git a/Mage.Sets/src/mage/cards/t/ThoughtDistortion.java b/Mage.Sets/src/mage/cards/t/ThoughtDistortion.java index 743f64978c..98ca1755d9 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtDistortion.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtDistortion.java @@ -1,7 +1,7 @@ package mage.cards.t; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -28,7 +28,7 @@ public final class ThoughtDistortion extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}"); // This spell can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Target opponent reveals their hand. Exile all noncreature, nonland cards from that player's hand and graveyard. this.getSpellAbility().addEffect(new ThoughtDistortionEffect()); diff --git a/Mage.Sets/src/mage/cards/t/ThoughtGorger.java b/Mage.Sets/src/mage/cards/t/ThoughtGorger.java index 944775d342..f20363f2bb 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtGorger.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtGorger.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -11,22 +9,23 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class ThoughtGorger extends CardImpl { public ThoughtGorger(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); this.subtype.add(SubType.HORROR); this.power = new MageInt(2); @@ -35,15 +34,13 @@ public final class ThoughtGorger extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Thought Gorger enters the battlefield, put a +1/+1 counter on it for each card in your hand. If you do, discard your hand. - Ability ability1 = new EntersBattlefieldTriggeredAbility(new ThoughtGorgerEffectEnters()); - this.addAbility(ability1); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ThoughtGorgerEffectEnters())); // When Thought Gorger leaves the battlefield, draw a card for each +1/+1 counter on it. - Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ThoughtGorgerEffectLeaves(), false); - this.addAbility(ability2); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new ThoughtGorgerEffectLeaves(), false)); } - public ThoughtGorger(final ThoughtGorger card) { + private ThoughtGorger(final ThoughtGorger card) { super(card); } @@ -56,12 +53,12 @@ public final class ThoughtGorger extends CardImpl { class ThoughtGorgerEffectEnters extends OneShotEffect { - public ThoughtGorgerEffectEnters() { + ThoughtGorgerEffectEnters() { super(Outcome.Benefit); this.staticText = "put a +1/+1 counter on it for each card in your hand. If you do, discard your hand."; } - public ThoughtGorgerEffectEnters(final ThoughtGorgerEffectEnters effect) { + private ThoughtGorgerEffectEnters(final ThoughtGorgerEffectEnters effect) { super(effect); } @@ -74,24 +71,27 @@ class ThoughtGorgerEffectEnters extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Permanent thoughtGorger = game.getPermanent(source.getSourceId()); - if (player != null && !player.getHand().isEmpty() && thoughtGorger != null ) { - int cardsInHand = player.getHand().size(); - thoughtGorger.addCounters(CounterType.P1P1.createInstance(cardsInHand), source, game); - player.discard(cardsInHand, false, source, game); - return true; + if (player == null + || player.getHand().isEmpty() + || thoughtGorger == null + || !thoughtGorger.addCounters( + CounterType.P1P1.createInstance(player.getHand().size()), source, game + )) { + return false; } - return false; + player.discard(player.getHand(), source, game); + return true; } } class ThoughtGorgerEffectLeaves extends OneShotEffect { - public ThoughtGorgerEffectLeaves() { + ThoughtGorgerEffectLeaves() { super(Outcome.Neutral); this.staticText = "draw a card for each +1/+1 counter on it."; } - public ThoughtGorgerEffectLeaves(final ThoughtGorgerEffectLeaves effect) { + private ThoughtGorgerEffectLeaves(final ThoughtGorgerEffectLeaves effect) { super(effect); } @@ -106,7 +106,7 @@ class ThoughtGorgerEffectLeaves extends OneShotEffect { Permanent thoughtGorgerLastState = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); int numberCounters = thoughtGorgerLastState.getCounters(game).getCount(CounterType.P1P1); if (player != null) { - player.drawCards(numberCounters, game); + player.drawCards(numberCounters, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java b/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java index a4b9bee2e9..7c10810ef0 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java @@ -86,7 +86,7 @@ class ThoughtHemorrhageEffect extends OneShotEffect { + targetPlayer.getName(), targetPlayer.getHand(), game); int cardsFound = 0; for (Card card : targetPlayer.getHand().getCards(game)) { - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { cardsFound++; } } diff --git a/Mage.Sets/src/mage/cards/t/ThoughtReflection.java b/Mage.Sets/src/mage/cards/t/ThoughtReflection.java index 9a0c041548..591cab79ca 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtReflection.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtReflection.java @@ -75,7 +75,7 @@ class ThoughtReflectionReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player you = game.getPlayer(event.getPlayerId()); if (you != null) { - you.drawCards(2, game, event.getAppliedEffects()); + you.drawCards(2, event.getSourceId(), game, event.getAppliedEffects()); } return true; } diff --git a/Mage.Sets/src/mage/cards/t/ThoughtSponge.java b/Mage.Sets/src/mage/cards/t/ThoughtSponge.java index 6721f81d76..da1d3a75f0 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtSponge.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtSponge.java @@ -2,7 +2,7 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; @@ -45,7 +45,7 @@ public final class ThoughtSponge extends CardImpl { ), new CardsDrawnThisTurnWatcher()); // When Thought Sponge dies, draw cards equal to its power. - this.addAbility(new DiesTriggeredAbility( + this.addAbility(new DiesSourceTriggeredAbility( new DrawCardSourceControllerEffect(xValue).setText("draw cards equal to its power") )); } diff --git a/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java b/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java index f520069d78..99e954fd14 100644 --- a/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java +++ b/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java @@ -1,5 +1,7 @@ package mage.cards.t; +import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -13,7 +15,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.WatcherScope; import mage.constants.Zone; -import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; @@ -22,11 +24,12 @@ import mage.watchers.Watcher; import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.UUID; +import java.util.List; +import java.util.ArrayList; /** - * @author LevelX2 + * @author jasc7636 */ public final class ThousandYearStorm extends CardImpl { @@ -34,7 +37,7 @@ public final class ThousandYearStorm extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}{R}"); // Whenever you cast an instant or sorcery spell, copy it for each other instant and sorcery spell you've cast before it this turn. You may choose new targets for the copies. - this.addAbility(new ThousandYearStormAbility()); + this.addAbility(new ThousandYearStormAbility(), new ThousandYearStormWatcher()); } public ThousandYearStorm(final ThousandYearStorm card) { @@ -49,12 +52,12 @@ public final class ThousandYearStorm extends CardImpl { class ThousandYearStormAbility extends SpellCastControllerTriggeredAbility { - String stormCountInfo = null; + private String stormCountInfo; public ThousandYearStormAbility() { - super(Zone.BATTLEFIELD, new ThousandYearStormEffect(), new FilterInstantOrSorcerySpell(), false, true); - this.addHint(new ValueHint("You've cast instant and sorcery this turn", ThousandYearSpellsCastThatTurnValue.instance)); - this.addWatcher(new ThousandYearWatcher()); + super(Zone.BATTLEFIELD, new ThousandYearStormEffect(), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false, true); + this.addHint(new ValueHint("You've cast instant and sorcery this turn", ThousandYearStormSpellsCastThatTurnValue.instance)); + this.stormCountInfo = null; } public ThousandYearStormAbility(final ThousandYearStormAbility ability) { @@ -64,11 +67,32 @@ class ThousandYearStormAbility extends SpellCastControllerTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - // save storm count info, real count will be calculated to stack ability in resolve effect only if (super.checkTrigger(event, game)) { - int stormCount = ThousandYearSpellsCastThatTurnValue.instance.calculate(game, this, null); - stormCountInfo = " (storm count: " + Math.max(0, stormCount - 1) + ") "; - return true; + ThousandYearStormWatcher watcher = game.getState().getWatcher(ThousandYearStormWatcher.class); + if (watcher == null) { + return false; + } + UUID playerId = event.getPlayerId(); + List spellsCast = watcher.getSpellsThisTurn(playerId); + MageObject object = game.getObject(event.getTargetId()); + if (object == null || spellsCast == null) { + return false; + } + int stormCount = 0; + for (MageObjectReference mor : spellsCast) { + stormCount++; + if (mor.refersTo(object, game)) { + break; + } + } + stormCount = Math.max(0, stormCount - 1); + stormCountInfo = " (storm count: " + stormCount + ") "; + for (Effect effect : this.getEffects()) { + if (effect instanceof ThousandYearStormEffect) { + ((ThousandYearStormEffect) effect).setStormCount(stormCount); + return true; + } + } } return false; } @@ -86,13 +110,16 @@ class ThousandYearStormAbility extends SpellCastControllerTriggeredAbility { } class ThousandYearStormEffect extends OneShotEffect { + private int stormCount; public ThousandYearStormEffect() { super(Outcome.Benefit); + this.stormCount = -1; } public ThousandYearStormEffect(final ThousandYearStormEffect effect) { super(effect); + this.stormCount = effect.stormCount; } @Override @@ -103,84 +130,72 @@ class ThousandYearStormEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Spell spell = game.getSpellOrLKIStack(getTargetPointer().getFirst(game, source)); - Player controller = spell != null ? game.getPlayer(spell.getControllerId()) : null; - if (spell != null && controller != null) { - ThousandYearWatcher watcher = game.getState().getWatcher(ThousandYearWatcher.class); - if (watcher != null) { - String stateSearchId = spell.getId().toString() + source.getSourceId().toString(); - // recall only the spells cast before it - int numberOfCopies = 0; - if (game.getState().getValue(stateSearchId) != null) { - numberOfCopies = (int) game.getState().getValue(stateSearchId); - } - if (numberOfCopies > 0) { - for (int i = 0; i < numberOfCopies; i++) { - spell.createCopyOnStack(game, source, source.getControllerId(), true); - } - } - return true; - } + if (stormCount >= 0 && spell != null) { + spell.createCopyOnStack(game, source, source.getControllerId(), true, stormCount); + return true; } return false; } + public void setStormCount(int stormCount) { + this.stormCount = stormCount; + } + @Override public String getText(Mode mode) { return "copy it for each other instant and sorcery spell you've cast before it this turn. You may choose new targets for the copies"; } } -class ThousandYearWatcher extends Watcher { +class ThousandYearStormWatcher extends Watcher { - private final Map amountOfInstantSorcerySpellsCastOnCurrentTurn = new HashMap<>(); + private final Map> spellsThisTurn = new HashMap<>(); - public ThousandYearWatcher() { + public ThousandYearStormWatcher() { super(WatcherScope.GAME); } @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.SPELL_CAST && !sourceId.equals(event.getTargetId())) { - Spell spell = game.getSpellOrLKIStack(event.getTargetId()); - if (spell != null && spell.isInstantOrSorcery()) { + if (event.getType() == GameEvent.EventType.SPELL_CAST) { + MageObject object = game.getObject(event.getTargetId()); + if (object != null && (object.isInstant() || object.isSorcery())) { UUID playerId = event.getPlayerId(); - if (playerId != null) { - String stateSearchId = spell.getId().toString() + sourceId.toString(); - // calc current spell - amountOfInstantSorcerySpellsCastOnCurrentTurn.putIfAbsent(playerId, 0); - amountOfInstantSorcerySpellsCastOnCurrentTurn.compute(playerId, (k, a) -> a + 1); - // remember only the spells cast before it - game.getState().setValue(stateSearchId, amountOfInstantSorcerySpellsCastOnCurrentTurn.get(playerId) - 1); - } + List spellsCast = spellsThisTurn.getOrDefault(playerId, new ArrayList()); + spellsCast.add(new MageObjectReference(object, game)); + spellsThisTurn.put(playerId, spellsCast); } } } @Override public void reset() { - amountOfInstantSorcerySpellsCastOnCurrentTurn.clear(); + for (List mor : spellsThisTurn.values()) { + mor.clear(); + } + spellsThisTurn.clear(); } - public int getAmountOfSpellsPlayerCastOnCurrentTurn(UUID playerId) { - return amountOfInstantSorcerySpellsCastOnCurrentTurn.getOrDefault(playerId, 0); + public List getSpellsThisTurn(UUID playerId) { + return spellsThisTurn.getOrDefault(playerId, new ArrayList()); } } -enum ThousandYearSpellsCastThatTurnValue implements DynamicValue { +enum ThousandYearStormSpellsCastThatTurnValue implements DynamicValue { instance; @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - ThousandYearWatcher watcher = game.getState().getWatcher(ThousandYearWatcher.class); + ThousandYearStormWatcher watcher = game.getState().getWatcher(ThousandYearStormWatcher.class); if (watcher == null) { return 0; } - return watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(sourceAbility.getControllerId()); + return watcher.getSpellsThisTurn(sourceAbility.getControllerId()).size(); } @Override - public ThousandYearSpellsCastThatTurnValue copy() { + public ThousandYearStormSpellsCastThatTurnValue copy() { return instance; } diff --git a/Mage.Sets/src/mage/cards/t/ThrabenFoulbloods.java b/Mage.Sets/src/mage/cards/t/ThrabenFoulbloods.java index 7460f5d5a2..a2a07b2283 100644 --- a/Mage.Sets/src/mage/cards/t/ThrabenFoulbloods.java +++ b/Mage.Sets/src/mage/cards/t/ThrabenFoulbloods.java @@ -26,7 +26,7 @@ public final class ThrabenFoulbloods extends CardImpl { public ThrabenFoulbloods(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.ZOMBIE); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/t/ThrabenPurebloods.java b/Mage.Sets/src/mage/cards/t/ThrabenPurebloods.java index ecc9344de7..e5c7d92fb6 100644 --- a/Mage.Sets/src/mage/cards/t/ThrabenPurebloods.java +++ b/Mage.Sets/src/mage/cards/t/ThrabenPurebloods.java @@ -16,7 +16,7 @@ public final class ThrabenPurebloods extends CardImpl { public ThrabenPurebloods(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(5); diff --git a/Mage.Sets/src/mage/cards/t/ThranTome.java b/Mage.Sets/src/mage/cards/t/ThranTome.java new file mode 100644 index 0000000000..d67e5bd5e7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThranTome.java @@ -0,0 +1,113 @@ +package mage.cards.t; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetOpponent; + +import java.util.Set; +import java.util.UUID; + +/** + * @author arcox + */ +public final class ThranTome extends CardImpl { + + public ThranTome(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + // Reveal the top three cards of your library. Target opponent chooses one of those cards. Put that card into your graveyard, then draw two cards. + Ability ability = new SimpleActivatedAbility(new ThranTomeEffect(), new ManaCostsImpl("{5}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + public ThranTome(final ThranTome card) { + super(card); + } + + @Override + public ThranTome copy() { + return new ThranTome(this); + } +} + +class ThranTomeEffect extends OneShotEffect { + + public ThranTomeEffect() { + super(Outcome.Benefit); + this.staticText = "Reveal the top three cards of your library. Target opponent chooses one of those cards. Put that card into your graveyard, then draw two cards"; + } + + public ThranTomeEffect(final ThranTomeEffect effect) { + super(effect); + } + + @Override + public ThranTomeEffect copy() { + return new ThranTomeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + // validate source and controller exist + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + + if (sourceObject == null && controller == null) { + return false; + } + + // target an opponent, if able + Player opponent; + Set opponents = game.getOpponents(controller.getId()); + opponents.removeIf(opp -> !game.getPlayer(opp).canBeTargetedBy(sourceObject, source.getControllerId(), game)); + + if (opponents.isEmpty()) { + return false; + } else { + if (opponents.size() == 1) { + opponent = game.getPlayer(opponents.iterator().next()); + } else { + Target target = new TargetOpponent(); + controller.chooseTarget(Outcome.Detriment, target, source, game); + opponent = game.getPlayer(target.getFirstTarget()); + } + } + + // reveal the cards and choose one. put it in the graveyard + Card cardToGraveyard; + Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 3)); + controller.revealCards(sourceObject.getIdName(), cards, game); + + if (cards.size() == 1) { + cardToGraveyard = cards.getRandom(game); + } else { + TargetCardInLibrary target = new TargetCardInLibrary(1, new FilterCard()); + opponent.chooseTarget(outcome, cards, target, source, game); + cardToGraveyard = game.getCard(target.getFirstTarget()); + } + + // put the chosen card in the graveyard + if (cardToGraveyard != null) { + controller.moveCards(cardToGraveyard, Zone.GRAVEYARD, source, game); + cards.remove(cardToGraveyard); + } + + // draw 2 + controller.drawCards(2, source.getSourceId(), game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThrasherBrute.java b/Mage.Sets/src/mage/cards/t/ThrasherBrute.java index e841325ff7..640395a0e4 100644 --- a/Mage.Sets/src/mage/cards/t/ThrasherBrute.java +++ b/Mage.Sets/src/mage/cards/t/ThrasherBrute.java @@ -1,24 +1,24 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterTeamPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ThrasherBrute extends CardImpl { @@ -35,12 +35,9 @@ public final class ThrasherBrute extends CardImpl { // Whenever Thrasher Brute or another Warrior enters the battlefield under your team's control, target opponent loses 1 life and you gain 1 life. Ability ability = new EntersBattlefieldAllTriggeredAbility( - Zone.BATTLEFIELD, - new LoseLifeTargetEffect(1), - filter, - false, + Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), filter, false, "Whenever {this} or another Warrior enters the battlefield under your team's control, " - + "target opponent loses 1 life and you gain 1 life." + + "target opponent loses 1 life and you gain 1 life." ); ability.addEffect(new GainLifeEffect(1)); ability.addTarget(new TargetOpponent()); @@ -63,8 +60,8 @@ class ThrasherBruteFilter extends FilterTeamPermanent { super(); } - ThrasherBruteFilter(final ThrasherBruteFilter effect) { - super(effect); + private ThrasherBruteFilter(final ThrasherBruteFilter filter) { + super(filter); } @Override @@ -74,16 +71,8 @@ class ThrasherBruteFilter extends FilterTeamPermanent { @Override public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { - if (super.match(permanent, sourceId, playerId, game)) { - if (sourceId.equals(permanent.getId())) { - return true; - } else { - if (permanent.hasSubtype(SubType.WARRIOR, game)) { - return true; - } - } - } - return false; + return super.match(permanent, sourceId, playerId, game) + && (sourceId.equals(permanent.getId()) + || permanent.hasSubtype(SubType.WARRIOR, game)); } - } diff --git a/Mage.Sets/src/mage/cards/t/ThrashingMossdog.java b/Mage.Sets/src/mage/cards/t/ThrashingMossdog.java index d3a8501c73..c107bab3a1 100644 --- a/Mage.Sets/src/mage/cards/t/ThrashingMossdog.java +++ b/Mage.Sets/src/mage/cards/t/ThrashingMossdog.java @@ -23,7 +23,7 @@ public final class ThrashingMossdog extends CardImpl { public ThrashingMossdog (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); this.subtype.add(SubType.PLANT); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/t/ThrasiosTritonHero.java b/Mage.Sets/src/mage/cards/t/ThrasiosTritonHero.java index 510ae0081b..4f70c21434 100644 --- a/Mage.Sets/src/mage/cards/t/ThrasiosTritonHero.java +++ b/Mage.Sets/src/mage/cards/t/ThrasiosTritonHero.java @@ -89,7 +89,7 @@ class ThrasiosTritonHeroEffect extends OneShotEffect { if (card.isLand()) { controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); } else { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/t/ThrillingEncore.java b/Mage.Sets/src/mage/cards/t/ThrillingEncore.java index 9a5335ceed..e010ab07cd 100644 --- a/Mage.Sets/src/mage/cards/t/ThrillingEncore.java +++ b/Mage.Sets/src/mage/cards/t/ThrillingEncore.java @@ -65,7 +65,7 @@ class ThrillingEncoreEffect extends OneShotEffect { Card card = mor.getCard(game); if (card != null && card.isCreature()) { Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/t/ThrivingBluff.java b/Mage.Sets/src/mage/cards/t/ThrivingBluff.java new file mode 100644 index 0000000000..f3f5cfec25 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThrivingBluff.java @@ -0,0 +1,45 @@ +package mage.cards.t; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ChooseColorEffect; +import mage.abilities.effects.mana.AddManaChosenColorEffect; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThrivingBluff extends CardImpl { + + public ThrivingBluff(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Thriving Bluff enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // As Thriving Bluff enters the battlefield, choose a color other than red. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral, "Red"))); + + // {T}: Add {R} or one mana of the chosen color. + this.addAbility(new RedManaAbility()); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost())); + } + + private ThrivingBluff(final ThrivingBluff card) { + super(card); + } + + @Override + public ThrivingBluff copy() { + return new ThrivingBluff(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThrivingGrove.java b/Mage.Sets/src/mage/cards/t/ThrivingGrove.java new file mode 100644 index 0000000000..6b11f3196e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThrivingGrove.java @@ -0,0 +1,45 @@ +package mage.cards.t; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ChooseColorEffect; +import mage.abilities.effects.mana.AddManaChosenColorEffect; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThrivingGrove extends CardImpl { + + public ThrivingGrove(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Thriving Grove enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // As Thriving Grove enters the battlefield, choose a color other than green. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral, "Green"))); + + // {T}: Add {G} or one mana of the chosen color. + this.addAbility(new GreenManaAbility()); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost())); + } + + private ThrivingGrove(final ThrivingGrove card) { + super(card); + } + + @Override + public ThrivingGrove copy() { + return new ThrivingGrove(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThrivingHeath.java b/Mage.Sets/src/mage/cards/t/ThrivingHeath.java new file mode 100644 index 0000000000..1368e8cb79 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThrivingHeath.java @@ -0,0 +1,45 @@ +package mage.cards.t; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ChooseColorEffect; +import mage.abilities.effects.mana.AddManaChosenColorEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThrivingHeath extends CardImpl { + + public ThrivingHeath(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Thriving Heath enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // As Thriving Heath enters the battlefield, choose a color other than white. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral, "White"))); + + // {T}: Add {W} or one mana of the chosen color. + this.addAbility(new WhiteManaAbility()); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost())); + } + + private ThrivingHeath(final ThrivingHeath card) { + super(card); + } + + @Override + public ThrivingHeath copy() { + return new ThrivingHeath(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThrivingIsle.java b/Mage.Sets/src/mage/cards/t/ThrivingIsle.java new file mode 100644 index 0000000000..c0564e8c2a --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThrivingIsle.java @@ -0,0 +1,45 @@ +package mage.cards.t; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ChooseColorEffect; +import mage.abilities.effects.mana.AddManaChosenColorEffect; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThrivingIsle extends CardImpl { + + public ThrivingIsle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Thriving Isle enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // As Thriving Isle enters the battlefield, choose a color other than blue. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral, "Blue"))); + + // {T}: Add {U} or one mana of the chosen color. + this.addAbility(new BlueManaAbility()); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost())); + } + + private ThrivingIsle(final ThrivingIsle card) { + super(card); + } + + @Override + public ThrivingIsle copy() { + return new ThrivingIsle(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThrivingMoor.java b/Mage.Sets/src/mage/cards/t/ThrivingMoor.java new file mode 100644 index 0000000000..5765c37066 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThrivingMoor.java @@ -0,0 +1,45 @@ +package mage.cards.t; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ChooseColorEffect; +import mage.abilities.effects.mana.AddManaChosenColorEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThrivingMoor extends CardImpl { + + public ThrivingMoor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Thriving Moor enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // As Thriving Moor enters the battlefield, choose a color other than black. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral, "Black"))); + + // {T}: Add {B} or one mana of the chosen color. + this.addAbility(new BlackManaAbility()); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost())); + } + + private ThrivingMoor(final ThrivingMoor card) { + super(card); + } + + @Override + public ThrivingMoor copy() { + return new ThrivingMoor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThrullChampion.java b/Mage.Sets/src/mage/cards/t/ThrullChampion.java index e5bed4d947..a200e4607c 100644 --- a/Mage.Sets/src/mage/cards/t/ThrullChampion.java +++ b/Mage.Sets/src/mage/cards/t/ThrullChampion.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -19,6 +18,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; +import mage.watchers.common.LostControlWatcher; /** * @@ -33,7 +33,7 @@ public final class ThrullChampion extends CardImpl { } public ThrullChampion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); this.subtype.add(SubType.THRULL); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -47,6 +47,7 @@ public final class ThrullChampion extends CardImpl { "Gain control of target Thrull for as long as you control {this}"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, ThrullChampionGainControlEffect, new TapSourceCost()); ability.addTarget(new TargetPermanent(filter)); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/ThrummingStone.java b/Mage.Sets/src/mage/cards/t/ThrummingStone.java index b667972153..26900a31a5 100644 --- a/Mage.Sets/src/mage/cards/t/ThrummingStone.java +++ b/Mage.Sets/src/mage/cards/t/ThrummingStone.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect; import mage.abilities.keyword.RippleAbility; @@ -10,7 +8,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; import mage.constants.Zone; -import mage.filter.FilterSpell; +import mage.filter.FilterCard; + +import java.util.UUID; /** * @author klayhamn @@ -21,8 +21,8 @@ public final class ThrummingStone extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); addSuperType(SuperType.LEGENDARY); - // spells you cast have Ripple 4 - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledSpellsEffect(new RippleAbility(4), new FilterSpell("spells")))); + // Spells you cast have Ripple 4 + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledSpellsEffect(new RippleAbility(4), new FilterCard("Spells")))); } public ThrummingStone(final ThrummingStone card) { diff --git a/Mage.Sets/src/mage/cards/t/ThunderscapeFamiliar.java b/Mage.Sets/src/mage/cards/t/ThunderscapeFamiliar.java index d66b96c6d4..3bea2e1357 100644 --- a/Mage.Sets/src/mage/cards/t/ThunderscapeFamiliar.java +++ b/Mage.Sets/src/mage/cards/t/ThunderscapeFamiliar.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; @@ -16,8 +14,9 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author North */ public final class ThunderscapeFamiliar extends CardImpl { @@ -31,7 +30,7 @@ public final class ThunderscapeFamiliar extends CardImpl { } public ThunderscapeFamiliar(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.KAVU); this.power = new MageInt(1); @@ -39,6 +38,7 @@ public final class ThunderscapeFamiliar extends CardImpl { // First strike this.addAbility(FirstStrikeAbility.getInstance()); + // Black spells and green spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); } diff --git a/Mage.Sets/src/mage/cards/t/ThwartTheEnemy.java b/Mage.Sets/src/mage/cards/t/ThwartTheEnemy.java new file mode 100644 index 0000000000..b367882861 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThwartTheEnemy.java @@ -0,0 +1,38 @@ +package mage.cards.t; + +import mage.abilities.effects.common.PreventAllDamageByAllObjectsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterObject; +import mage.filter.common.FilterOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThwartTheEnemy extends CardImpl { + + private static final FilterObject filter + = new FilterOpponentsCreaturePermanent("creatures your opponents control"); + + public ThwartTheEnemy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // Prevent all damage that would be dealt this turn by creatures your opponents control. + this.getSpellAbility().addEffect(new PreventAllDamageByAllObjectsEffect( + filter, Duration.EndOfTurn, false + )); + } + + private ThwartTheEnemy(final ThwartTheEnemy card) { + super(card); + } + + @Override + public ThwartTheEnemy copy() { + return new ThwartTheEnemy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TianaShipsCaretaker.java b/Mage.Sets/src/mage/cards/t/TianaShipsCaretaker.java index 832e96d5de..ec1e94aef8 100644 --- a/Mage.Sets/src/mage/cards/t/TianaShipsCaretaker.java +++ b/Mage.Sets/src/mage/cards/t/TianaShipsCaretaker.java @@ -130,7 +130,7 @@ class TianaShipsCaretakerEffect extends OneShotEffect { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { Effect effect = new ReturnFromGraveyardToHandTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return that card to your hand at the beginning of the next end step"); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); return true; diff --git a/Mage.Sets/src/mage/cards/t/TibaltTheFiendBlooded.java b/Mage.Sets/src/mage/cards/t/TibaltTheFiendBlooded.java index 43fc0c641a..4817151c4a 100644 --- a/Mage.Sets/src/mage/cards/t/TibaltTheFiendBlooded.java +++ b/Mage.Sets/src/mage/cards/t/TibaltTheFiendBlooded.java @@ -86,7 +86,7 @@ class TibaltTheFiendBloodedFirstEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); Card card = player.getHand().getRandom(game); player.discard(card, source, game); return true; @@ -118,11 +118,11 @@ class TibaltTheFiendBloodedThirdEffect extends OneShotEffect { permanent.untap(game); ContinuousEffect effect = new TibaltTheFiendBloodedControlEffect(source.getControllerId()); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/t/TibaltsRager.java b/Mage.Sets/src/mage/cards/t/TibaltsRager.java index 26d3282571..8599f5a070 100644 --- a/Mage.Sets/src/mage/cards/t/TibaltsRager.java +++ b/Mage.Sets/src/mage/cards/t/TibaltsRager.java @@ -2,7 +2,7 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DamageTargetEffect; @@ -29,7 +29,7 @@ public final class TibaltsRager extends CardImpl { this.toughness = new MageInt(2); // When Tibalt's Rager dies, it deals 1 damage to any target. - Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(1, "it")); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(1, "it")); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TidalBarracuda.java b/Mage.Sets/src/mage/cards/t/TidalBarracuda.java new file mode 100644 index 0000000000..f14f6fd12d --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TidalBarracuda.java @@ -0,0 +1,84 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.events.GameEvent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TidalBarracuda extends CardImpl { + + private static final FilterCard filter = new FilterCard("spells"); + + public TidalBarracuda(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.FISH); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Any player may cast spells as though they had flash. + this.addAbility(new SimpleStaticAbility(new CastAsThoughItHadFlashAllEffect( + Duration.WhileOnBattlefield, filter, true + ))); + + // Your opponents can't cast spells during your turn. + this.addAbility(new SimpleStaticAbility(new TidalBarracudaEffect())); + } + + private TidalBarracuda(final TidalBarracuda card) { + super(card); + } + + @Override + public TidalBarracuda copy() { + return new TidalBarracuda(this); + } +} + +class TidalBarracudaEffect extends ContinuousRuleModifyingEffectImpl { + + TidalBarracudaEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Your opponents can't cast spells during your turn"; + } + + private TidalBarracudaEffect(final TidalBarracudaEffect effect) { + super(effect); + } + + @Override + public TidalBarracudaEffect copy() { + return new TidalBarracudaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return game.isActivePlayer(source.getControllerId()) && + game.getPlayer(source.getControllerId()).hasOpponent(event.getPlayerId(), game); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TideSkimmer.java b/Mage.Sets/src/mage/cards/t/TideSkimmer.java new file mode 100644 index 0000000000..c46364b2cb --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TideSkimmer.java @@ -0,0 +1,51 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TideSkimmer extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public TideSkimmer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.DRAKE); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever you attack with two or more creatures with flying, draw a card. + this.addAbility(new AttacksWithCreaturesTriggeredAbility( + new DrawCardSourceControllerEffect(1), 2, filter + )); + } + + private TideSkimmer(final TideSkimmer card) { + super(card); + } + + @Override + public TideSkimmer copy() { + return new TideSkimmer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TidyConclusion.java b/Mage.Sets/src/mage/cards/t/TidyConclusion.java index 69b74d23b7..c362070795 100644 --- a/Mage.Sets/src/mage/cards/t/TidyConclusion.java +++ b/Mage.Sets/src/mage/cards/t/TidyConclusion.java @@ -1,29 +1,29 @@ - package mage.cards.t; -import java.util.UUID; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TidyConclusion extends CardImpl { public TidyConclusion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}{B}"); // Destroy target creature. You gain 1 life for each artifact you control. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new GainLifeEffect(new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent()))); + this.getSpellAbility().addEffect(new GainLifeEffect(ArtifactYouControlCount.instance)); + this.getSpellAbility().addHint(ArtifactYouControlHint.instance); } public TidyConclusion(final TidyConclusion card) { diff --git a/Mage.Sets/src/mage/cards/t/TimeSpiral.java b/Mage.Sets/src/mage/cards/t/TimeSpiral.java index 8eec63e970..a803041fa1 100644 --- a/Mage.Sets/src/mage/cards/t/TimeSpiral.java +++ b/Mage.Sets/src/mage/cards/t/TimeSpiral.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -8,12 +7,12 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardAllEffect; import mage.abilities.effects.common.ExileSpellEffect; import mage.abilities.effects.common.UntapLandsEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; @@ -24,7 +23,7 @@ import mage.players.Player; public final class TimeSpiral extends CardImpl { public TimeSpiral(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}{U}"); // Exile Time Spiral. Each player shuffles their graveyard and hand into their library, then draws seven cards. You untap up to six lands. this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); @@ -62,13 +61,9 @@ class TimeSpiralEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - for (Card card : player.getHand().getCards(game)) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - for (Card card : player.getGraveyard().getCards(game)) { - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - } - player.shuffleLibrary(source, game); + Cards toLib = new CardsImpl(player.getHand()); + toLib.addAll(player.getGraveyard()); + player.shuffleCardsToLibrary(toLib, game, source); } } return true; diff --git a/Mage.Sets/src/mage/cards/t/TimelyHordemate.java b/Mage.Sets/src/mage/cards/t/TimelyHordemate.java index bdf734ed07..9598467d86 100644 --- a/Mage.Sets/src/mage/cards/t/TimelyHordemate.java +++ b/Mage.Sets/src/mage/cards/t/TimelyHordemate.java @@ -1,25 +1,26 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; +import mage.constants.SubType; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; import mage.target.common.TargetCardInYourGraveyard; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TimelyHordemate extends CardImpl { @@ -40,8 +41,10 @@ public final class TimelyHordemate extends CardImpl { // Raid — When Timely Hordemate enters the battlefield, if you attacked this turn, return target creature card with converted mana cost 2 or less from your graveyard to the battlefield. Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, return target creature card with converted mana cost 2 or less from your graveyard to the battlefield."); + "Raid — When {this} enters the battlefield, if you attacked this turn, return target creature card with converted mana cost 2 or less from your graveyard to the battlefield."); ability.addTarget(new TargetCardInYourGraveyard(filter)); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java b/Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java new file mode 100644 index 0000000000..dd67bdf538 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java @@ -0,0 +1,128 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TinybonesTrinketThief extends CardImpl { + + public TinybonesTrinketThief(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SKELETON); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // At the beginning of each end step, if an opponent discarded a card this turn, you draw a card and you lose 1 life. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new DrawCardSourceControllerEffect(1), + TargetController.EACH_PLAYER, false + ), TinybonesTrinketThiefCondition.instance, "At the beginning of each end step, " + + "if an opponent discarded a card this turn, you draw a card and you lose 1 life." + ); + ability.addEffect(new LoseLifeSourceControllerEffect(1)); + this.addAbility(ability, new TinybonesTrinketThiefWatcher()); + + // {4}{B}{B}: Each opponent with no cards in hand loses 10 life. + this.addAbility(new SimpleActivatedAbility(new TinybonesTrinketThiefEffect(), new ManaCostsImpl("{4}{B}{B}"))); + } + + private TinybonesTrinketThief(final TinybonesTrinketThief card) { + super(card); + } + + @Override + public TinybonesTrinketThief copy() { + return new TinybonesTrinketThief(this); + } +} + +enum TinybonesTrinketThiefCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + TinybonesTrinketThiefWatcher watcher = game.getState().getWatcher(TinybonesTrinketThiefWatcher.class); + return watcher != null && watcher.checkPlayer(source.getControllerId()); + } +} + +class TinybonesTrinketThiefWatcher extends Watcher { + + private final Set playerSet = new HashSet<>(); + + TinybonesTrinketThiefWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DISCARDED_CARD) { + playerSet.addAll(game.getOpponents(event.getPlayerId())); + } + } + + @Override + public void reset() { + playerSet.clear(); + super.reset(); + } + + boolean checkPlayer(UUID playerId) { + return playerSet.contains(playerId); + } +} + +class TinybonesTrinketThiefEffect extends OneShotEffect { + + TinybonesTrinketThiefEffect() { + super(Outcome.Benefit); + staticText = "each opponent with no cards in hand loses 10 life"; + } + + private TinybonesTrinketThiefEffect(final TinybonesTrinketThiefEffect effect) { + super(effect); + } + + @Override + public TinybonesTrinketThiefEffect copy() { + return new TinybonesTrinketThiefEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(playerId); + if (player != null && player.getHand().isEmpty()) { + player.loseLife(10, game, false); + } + } + return true; + } +} +// tiny bones comin out my +// tiny bones comin out my mouth diff --git a/Mage.Sets/src/mage/cards/t/TitanHunter.java b/Mage.Sets/src/mage/cards/t/TitanHunter.java new file mode 100644 index 0000000000..6202dd7658 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TitanHunter.java @@ -0,0 +1,88 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.InvertCondition; +import mage.abilities.condition.common.MorbidCondition; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TitanHunter extends CardImpl { + + private static final Condition condition = new InvertCondition(MorbidCondition.instance); + + public TitanHunter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // At the beginning of each player's end step, if no creatures died this turn, Titan Hunter deals 4 damage to that player. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new TitanHunterEffect(), TargetController.EACH_PLAYER, false + ), condition, "At the beginning of each player's end step, " + + "if no creatures died this turn, {this} deals 4 damage to that player." + )); + + // {1}{B}, Sacrifice a creature: You gain 4 life. + Ability ability = new SimpleActivatedAbility(new GainLifeEffect(4), new ManaCostsImpl("{1}{B}")); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); + this.addAbility(ability); + } + + private TitanHunter(final TitanHunter card) { + super(card); + } + + @Override + public TitanHunter copy() { + return new TitanHunter(this); + } +} + +class TitanHunterEffect extends OneShotEffect { + + TitanHunterEffect() { + super(Outcome.Benefit); + } + + private TitanHunterEffect(final TitanHunterEffect effect) { + super(effect); + } + + @Override + public TitanHunterEffect copy() { + return new TitanHunterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return game.damagePlayerOrPlaneswalker( + game.getActivePlayerId(), 4, source.getSourceId(), + game, false, true + ) > 0; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TitanicBrawl.java b/Mage.Sets/src/mage/cards/t/TitanicBrawl.java index b06d0c1a7d..c23972f76e 100644 --- a/Mage.Sets/src/mage/cards/t/TitanicBrawl.java +++ b/Mage.Sets/src/mage/cards/t/TitanicBrawl.java @@ -8,12 +8,11 @@ import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.CounterPredicate; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -25,30 +24,27 @@ import java.util.UUID; */ public final class TitanicBrawl extends CardImpl { - private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent("creature you don't control"); - private static final FilterPermanent filter2 + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("a creature you control with a +1/+1 counter on it"); static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - filter2.add(new CounterPredicate(CounterType.P1P1)); + filter.add(new CounterPredicate(CounterType.P1P1)); } - private static final Condition condition = new SourceTargetsPermanentCondition(filter2); + private static final Condition condition = new SourceTargetsPermanentCondition(filter); public TitanicBrawl(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // This spell costs {1} less to cast if it targets a creature you control with a +1/+1 counter on it. this.addAbility(new SimpleStaticAbility( - Zone.STACK, new SpellCostReductionSourceEffect(1, condition) + Zone.ALL, new SpellCostReductionSourceEffect(1, condition).setCanWorksOnStackOnly(true) ).setRuleAtTheTop(true)); // Target creature you control fights target creature you don't control. this.getSpellAbility().addEffect(new FightTargetsEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } private TitanicBrawl(final TitanicBrawl card) { diff --git a/Mage.Sets/src/mage/cards/t/TitanothRex.java b/Mage.Sets/src/mage/cards/t/TitanothRex.java new file mode 100644 index 0000000000..e9bc2eaffb --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TitanothRex.java @@ -0,0 +1,54 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TitanothRex extends CardImpl { + + public TitanothRex(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{7}{G}{G}"); + + this.subtype.add(SubType.DINOSAUR); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(11); + this.toughness = new MageInt(11); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Cycling {1}{G} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}{G}"))); + + // When you cycle Titanoth Rex, put a trample counter on target creature you control. + Ability ability = new CycleTriggeredAbility( + new AddCountersTargetEffect(CounterType.TRAMPLE.createInstance()) + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private TitanothRex(final TitanothRex card) { + super(card); + } + + @Override + public TitanothRex copy() { + return new TitanothRex(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TitansNest.java b/Mage.Sets/src/mage/cards/t/TitansNest.java new file mode 100644 index 0000000000..f805e124d3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TitansNest.java @@ -0,0 +1,135 @@ +package mage.cards.t; + +import mage.ConditionalMana; +import mage.MageObject; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.VariableManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.abilities.mana.conditional.ManaCondition; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TitansNest extends CardImpl { + + public TitansNest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{G}{U}"); + + // At the beginning of your upkeep, look at the top card of your library. You may put that card into your graveyard. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new TitansNestEffect(), TargetController.YOU, false + )); + + // Exile a card from your graveyard: Add {C}. Spend this mana only to cast a colored spell without {X} in its mana cost. + this.addAbility(new TitansNestManaAbility()); + } + + private TitansNest(final TitansNest card) { + super(card); + } + + @Override + public TitansNest copy() { + return new TitansNest(this); + } +} + +class TitansNestEffect extends OneShotEffect { + + TitansNestEffect() { + super(Outcome.Benefit); + staticText = "look at the top card of your library. You may put that card into your graveyard"; + } + + private TitansNestEffect(final TitansNestEffect effect) { + super(effect); + } + + @Override + public TitansNestEffect copy() { + return new TitansNestEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.lookAtCards("", card, game); + return player.chooseUse(outcome, "Put " + card.getName() + " into your graveyard?", source, game) + && player.moveCards(card, Zone.GRAVEYARD, source, game); + } +} + +class TitansNestManaAbility extends ActivatedManaAbilityImpl { + + TitansNestManaAbility() { + super(Zone.BATTLEFIELD, (BasicManaEffect) new BasicManaEffect( + new TitansNestConditionalMana()) + .setText("Add {C}. Spend this mana only to cast a colored spell without {X} in its mana cost."), + new ExileFromGraveCost(new TargetCardInYourGraveyard())); + this.netMana.add(Mana.ColorlessMana(1)); + } + + private TitansNestManaAbility(TitansNestManaAbility ability) { + super(ability); + } + + @Override + public TitansNestManaAbility copy() { + return new TitansNestManaAbility(this); + } +} + +class TitansNestConditionalMana extends ConditionalMana { + + TitansNestConditionalMana() { + super(Mana.ColorlessMana(1)); + staticText = "Spend this mana only to cast a colored spell without {X} in its mana cost."; + addCondition(new TitansNestManaCondition()); + } +} + +class TitansNestManaCondition extends ManaCondition { + + @Override + public boolean apply(Game game, Ability source, UUID originalId, Cost costToPay) { + if (!(source instanceof SpellAbility)) { + return false; + } + MageObject object = game.getObject(source.getSourceId()); + if (object == null || object.getColor(game).isColorless()) { + return false; + } + if (costToPay instanceof ManaCosts) { + return ((ManaCosts) costToPay).getVariableCosts().isEmpty(); + } else { + return !(costToPay instanceof VariableManaCost); + } + } +} diff --git a/Mage.Sets/src/mage/cards/t/TogetherForever.java b/Mage.Sets/src/mage/cards/t/TogetherForever.java index ed2a9aca89..7bb233674e 100644 --- a/Mage.Sets/src/mage/cards/t/TogetherForever.java +++ b/Mage.Sets/src/mage/cards/t/TogetherForever.java @@ -40,8 +40,8 @@ public final class TogetherForever extends CardImpl { public TogetherForever(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{W}"); - // When Together Forever enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two other target creatures.) - this.addAbility(new SupportAbility(this, 2)); + // When Together Forever enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two target creatures.) + this.addAbility(new SupportAbility(this, 2, false)); // {1}: Choose target creature with a counter on it. When that creature dies this turn, return that card to its owner's hand. Ability ability = new SimpleActivatedAbility(new TogetherForeverEffect(), new GenericManaCost(1)); diff --git a/Mage.Sets/src/mage/cards/t/TolarianKraken.java b/Mage.Sets/src/mage/cards/t/TolarianKraken.java new file mode 100644 index 0000000000..c4f5d19c42 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TolarianKraken.java @@ -0,0 +1,49 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.DrawCardControllerTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.MayTapOrUntapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TolarianKraken extends CardImpl { + + public TolarianKraken(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + + this.subtype.add(SubType.KRAKEN); + this.power = new MageInt(4); + this.toughness = new MageInt(6); + + // Whenever you draw a card, you may pay {1}. When you do, you may tap or untap target creature. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new MayTapOrUntapTargetEffect(), false, + "you may tap or untap target creature" + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(new DrawCardControllerTriggeredAbility(new DoWhenCostPaid( + ability, new GenericManaCost(1), + "Pay {1} to tap or untap a creature?" + ), false)); + } + + private TolarianKraken(final TolarianKraken card) { + super(card); + } + + @Override + public TolarianKraken copy() { + return new TolarianKraken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TomeAnima.java b/Mage.Sets/src/mage/cards/t/TomeAnima.java new file mode 100644 index 0000000000..28a29266ea --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TomeAnima.java @@ -0,0 +1,58 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.CantBeBlockedSourceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.game.Game; +import mage.watchers.common.CardsDrawnThisTurnWatcher; + +import java.util.UUID; + +/** + * @author arcox + */ +public final class TomeAnima extends CardImpl { + + public TomeAnima(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Tome Anima can’t be blocked as long as you’ve drawn two or more cards this turn. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(new CantBeBlockedSourceAbility(), Duration.WhileOnBattlefield), + TomeAnimaCondition.instance, + "{this} can't be blocked as long as you've drawn two or more cards this turn" + )), new CardsDrawnThisTurnWatcher()); + } + + private TomeAnima(final TomeAnima card) { + super(card); + } + + @Override + public TomeAnima copy() { + return new TomeAnima(this); + } +} + +enum TomeAnimaCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + CardsDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsDrawnThisTurnWatcher.class); + return watcher != null && watcher.getCardsDrawnThisTurn(source.getControllerId()) > 1; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java b/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java index 659c6588b9..7f0b09a09d 100644 --- a/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java +++ b/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java @@ -1,5 +1,6 @@ package mage.cards.t; +import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -14,8 +15,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.CardUtil; -import java.util.UUID; - /** * @author TheElk801 */ @@ -30,7 +29,8 @@ public final class TorbranThaneOfRedFell extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(4); - // If a red source you control would deal damage to an opponent or a permanent an opponent controls, it deals that much damage plus 2 instead. + // If a red source you control would deal damage to an opponent or a + // permanent an opponent controls, it deals that much damage plus 2 instead. this.addAbility(new SimpleStaticAbility(new TorbranThaneOfRedFellEffect())); } @@ -48,8 +48,8 @@ class TorbranThaneOfRedFellEffect extends ReplacementEffectImpl { TorbranThaneOfRedFellEffect() { super(Duration.WhileOnBattlefield, Outcome.Damage); - this.staticText = "If a red source you control would deal damage to an opponent " + - "or a permanent an opponent controls, it deals that much damage plus 2 instead."; + this.staticText = "If a red source you control would deal damage to an opponent " + + "or a permanent an opponent controls, it deals that much damage plus 2 instead."; } private TorbranThaneOfRedFellEffect(final TorbranThaneOfRedFellEffect effect) { @@ -76,9 +76,9 @@ class TorbranThaneOfRedFellEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null - || !player.hasOpponent(getControllerOrSelf(event.getTargetId(), game), game) + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null + || !controller.hasOpponent(getControllerOrSelf(event.getTargetId(), game), game) || !source.isControlledBy(game.getControllerId(event.getSourceId()))) { return false; } @@ -89,8 +89,10 @@ class TorbranThaneOfRedFellEffect extends ReplacementEffectImpl { } else { sourceObject = sourcePermanent; } + return sourceObject != null - && sourceObject.getColor(game).isRed(); + && sourceObject.getColor(game).isRed() + && event.getAmount() > 0; } private static UUID getControllerOrSelf(UUID id, Game game) { diff --git a/Mage.Sets/src/mage/cards/t/Torchling.java b/Mage.Sets/src/mage/cards/t/Torchling.java index 1982ee257f..0cae200ef7 100644 --- a/Mage.Sets/src/mage/cards/t/Torchling.java +++ b/Mage.Sets/src/mage/cards/t/Torchling.java @@ -1,12 +1,7 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ColoredManaCost; import mage.abilities.costs.mana.GenericManaCost; @@ -17,50 +12,65 @@ import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ColoredManaSymbol; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.FilterSpell; -import mage.filter.predicate.Predicate; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; -import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.target.Target; import mage.target.TargetSpell; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; +import mage.abilities.Mode; /** - * * @author emerald000 */ public final class Torchling extends CardImpl { + private static final FilterSpell filter = new FilterSpell(); + + static { + filter.add(TorchlingPredicate.instance); + } + public Torchling(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); this.subtype.add(SubType.SHAPESHIFTER); this.power = new MageInt(3); this.toughness = new MageInt(3); // {R}: Untap Torchling. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), new ColoredManaCost(ColoredManaSymbol.R))); + this.addAbility(new SimpleActivatedAbility(new UntapSourceEffect(), new ColoredManaCost(ColoredManaSymbol.R))); // {R}: Target creature blocks Torchling this turn if able. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MustBeBlockedByTargetSourceEffect(), new ColoredManaCost(ColoredManaSymbol.R)); + Ability ability = new SimpleActivatedAbility( + new MustBeBlockedByTargetSourceEffect(), new ColoredManaCost(ColoredManaSymbol.R) + ); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // {R}: Change the target of target spell that targets only Torchling. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChooseNewTargetsTargetEffect(true, true), new ColoredManaCost(ColoredManaSymbol.R)); - FilterSpell filter = new FilterSpell("spell that targets only " + this.getName()); - filter.add(new TorchlingTargetPredicate(this.getId())); + ability = new SimpleActivatedAbility( + new ChooseNewTargetsTargetEffect(true, true) + .setText("change the target of target spell that targets only {this}"), + new ColoredManaCost(ColoredManaSymbol.R) + ); ability.addTarget(new TargetSpell(filter)); this.addAbility(ability); // {1}: Torchling gets +1/-1 until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, -1, Duration.EndOfTurn), new GenericManaCost(1))); + this.addAbility(new SimpleActivatedAbility( + new BoostSourceEffect(1, -1, Duration.EndOfTurn), new GenericManaCost(1) + )); // {1}: Torchling gets -1/+1 until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(-1, 1, Duration.EndOfTurn), new GenericManaCost(1))); + this.addAbility(new SimpleActivatedAbility( + new BoostSourceEffect(-1, 1, Duration.EndOfTurn), new GenericManaCost(1) + )); } public Torchling(final Torchling card) { @@ -73,40 +83,24 @@ public final class Torchling extends CardImpl { } } -class TorchlingTargetPredicate implements Predicate { +enum TorchlingPredicate implements ObjectSourcePlayerPredicate> { - private final UUID sourceId; - - TorchlingTargetPredicate(UUID sourceId) { - this.sourceId = sourceId; - } + instance; @Override - public boolean apply(MageObject input, Game game) { - Spell spell = game.getStack().getSpell(input.getId()); - if (spell != null) { - int numberOfTargets = 0; - for (SpellAbility spellAbility : spell.getSpellAbilities()) { - for (UUID modeId : spellAbility.getModes().getSelectedModes()) { - Mode mode = spellAbility.getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetId : target.getTargets()) { - if (!targetId.equals(sourceId)) { - return false; - } else { - numberOfTargets++; - } - } + public boolean apply(ObjectSourcePlayer input, Game game) { + StackObject stackObject = game.getState().getStack().getStackObject(input.getObject().getId()); + if (stackObject != null) { + for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) { + Mode mode = stackObject.getStackAbility().getModes().get(modeId); + for (Target target : mode.getTargets()) { + if (target.getTargets().contains(input.getSourceId()) // contains this card + && target.getTargets().size() == 1) { // only one target + return true; } } } - return numberOfTargets > 0; } return false; } - - @Override - public String toString() { - return "target spell that targets only {this}"; - } } diff --git a/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java b/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java index f25874461b..18238ef627 100644 --- a/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java +++ b/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -13,21 +11,16 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TorgaarFamineIncarnate extends CardImpl { @@ -42,10 +35,10 @@ public final class TorgaarFamineIncarnate extends CardImpl { // As an additional cost to cast this spell, you may sacrifice any number of creatures. Cost cost = new SacrificeXTargetCost(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT); - cost.setText("As an additional cost to cast this spell, you may sacrifice any number of creatures."); + cost.setText("As an additional cost to cast this spell, you may sacrifice any number of creatures"); this.getSpellAbility().addCost(cost); // This spell costs {2} less to cast for each creature sacrificed this way. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new TorgaarFamineIncarnateEffectCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new TorgaarFamineIncarnateEffectCostReductionEffect())); // When Torgaar, Famine Incarnate enters the battlefield, up to one target player's life total becomes half their starting life total, rounded down. Ability ability = new EntersBattlefieldTriggeredAbility(new TorgaarFamineIncarnateEffect(), false); @@ -107,8 +100,16 @@ class TorgaarFamineIncarnateEffectCostReductionEffect extends CostModificationEf SpellAbility spellAbility = (SpellAbility) abilityToModify; for (Cost cost : spellAbility.getCosts()) { if (cost instanceof SacrificeXTargetCost) { - int reduction = ((SacrificeXTargetCost) cost).getAmount(); - CardUtil.adjustCost(spellAbility, reduction * 2); + if (game.inCheckPlayableState()) { + // allows to cast in getPlayable + int reduction = ((SacrificeXTargetCost) cost).getMaxValue(spellAbility, game); + CardUtil.adjustCost(spellAbility, reduction * 2); + } else { + // real cast + int reduction = ((SacrificeXTargetCost) cost).getAmount(); + CardUtil.adjustCost(spellAbility, reduction * 2); + } + break; } } diff --git a/Mage.Sets/src/mage/cards/t/TormentedThoughts.java b/Mage.Sets/src/mage/cards/t/TormentedThoughts.java index 463a25d08c..004d9c4e58 100644 --- a/Mage.Sets/src/mage/cards/t/TormentedThoughts.java +++ b/Mage.Sets/src/mage/cards/t/TormentedThoughts.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -28,7 +28,7 @@ public final class TormentedThoughts extends CardImpl { // As an additional cost to cast Tormented Thoughts, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1,new FilterControlledCreaturePermanent("a creature"), false))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1,StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, false))); // Target player discards a number of cards equal to the sacrificed creature's power. this.getSpellAbility().addEffect(new TormentedThoughtsDiscardEffect()); diff --git a/Mage.Sets/src/mage/cards/t/TorturedExistence.java b/Mage.Sets/src/mage/cards/t/TorturedExistence.java index 8ff13eb53e..3de0c9747d 100644 --- a/Mage.Sets/src/mage/cards/t/TorturedExistence.java +++ b/Mage.Sets/src/mage/cards/t/TorturedExistence.java @@ -24,7 +24,7 @@ public final class TorturedExistence extends CardImpl { // {B}, Discard a creature card: Return target creature card from your graveyard to your hand. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToHandTargetEffect(), new ManaCostsImpl("{B}")); - ability.addCost(new DiscardCardCost(StaticFilters.FILTER_CARD_CREATURE)); + ability.addCost(new DiscardCardCost(StaticFilters.FILTER_CARD_CREATURE_A)); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java b/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java index 6b9c5c8582..4cdb286fa2 100644 --- a/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java +++ b/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java @@ -28,22 +28,22 @@ import mage.target.common.TargetCardInYourGraveyard; * @author LevelX2 */ public final class ToshiroUmezawa extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature an opponent controls"); private static final FilterCard filterInstant = new FilterCard("instant card from your graveyard"); - + static { filter.add(TargetController.OPPONENT.getControllerPredicate()); filterInstant.add(CardType.INSTANT.getPredicate()); } - + public ToshiroUmezawa(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SAMURAI); - + this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -55,13 +55,13 @@ public final class ToshiroUmezawa extends CardImpl { Ability ability = new DiesCreatureTriggeredAbility(new ToshiroUmezawaEffect(), true, filter); ability.addTarget(new TargetCardInYourGraveyard(1, 1, filterInstant)); this.addAbility(ability); - + } - + public ToshiroUmezawa(final ToshiroUmezawa card) { super(card); } - + @Override public ToshiroUmezawa copy() { return new ToshiroUmezawa(this); @@ -69,22 +69,22 @@ public final class ToshiroUmezawa extends CardImpl { } class ToshiroUmezawaEffect extends OneShotEffect { - + public ToshiroUmezawaEffect() { super(Outcome.Benefit); this.staticText = "cast target instant card from your graveyard. " + "If that card would be put into a graveyard this turn, exile it instead"; } - + public ToshiroUmezawaEffect(final ToshiroUmezawaEffect effect) { super(effect); } - + @Override public ToshiroUmezawaEffect copy() { return new ToshiroUmezawaEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); @@ -104,45 +104,45 @@ class ToshiroUmezawaEffect extends OneShotEffect { } class ToshiroUmezawaReplacementEffect extends ReplacementEffectImpl { - + private final UUID cardId; - + public ToshiroUmezawaReplacementEffect(UUID cardId) { super(Duration.EndOfTurn, Outcome.Exile); this.cardId = cardId; } - + public ToshiroUmezawaReplacementEffect(final ToshiroUmezawaReplacementEffect effect) { super(effect); this.cardId = effect.cardId; } - + @Override public ToshiroUmezawaReplacementEffect copy() { return new ToshiroUmezawaReplacementEffect(this); } - + @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { UUID eventObject = event.getTargetId(); StackObject stackObject = game.getStack().getStackObject(eventObject); - if (stackObject != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (stackObject != null && controller != null) { if (stackObject instanceof Spell) { game.rememberLKI(stackObject.getId(), Zone.STACK, stackObject); } if (stackObject instanceof Card && eventObject.equals(cardId)) { - ((Card) stackObject).moveToExile(null, null, source.getSourceId(), game); - return true; + return controller.moveCards((Card) stackObject, Zone.EXILED, source, game); } } return false; } - + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == EventType.ZONE_CHANGE; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; diff --git a/Mage.Sets/src/mage/cards/t/TouchOfMoonglove.java b/Mage.Sets/src/mage/cards/t/TouchOfMoonglove.java index f7353561b4..fe522bb656 100644 --- a/Mage.Sets/src/mage/cards/t/TouchOfMoonglove.java +++ b/Mage.Sets/src/mage/cards/t/TouchOfMoonglove.java @@ -74,7 +74,7 @@ class TouchOfMoongloveAddTriggerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { DelayedTriggeredAbility delayedAbility = new TouchOfMoongloveDelayedTriggeredAbility(new MageObjectReference(permanent, game)); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/t/ToweringTitan.java b/Mage.Sets/src/mage/cards/t/ToweringTitan.java new file mode 100644 index 0000000000..777e7118a3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ToweringTitan.java @@ -0,0 +1,100 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.target.common.TargetControlledPermanent; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ToweringTitan extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledCreaturePermanent("a creature with defender"); + + static { + filter.add(new AbilityPredicate(DefenderAbility.class)); + } + + public ToweringTitan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.GIANT); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Towering Titan enters the battlefield with X +1/+1 counters on it, where X is the total toughness of other creatures you control. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(), ToweringTitanCount.instance, false + ), "with X +1/+1 counters on it, where X is the total toughness of other creatures you control")); + + // Sacrifice a creature with defender: All creatures gain trample until end of turn. + this.addAbility(new SimpleActivatedAbility( + new GainAbilityAllEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE + ).setText("All creatures gain trample until end of turn"), + new SacrificeTargetCost(new TargetControlledPermanent(filter)) + )); + } + + private ToweringTitan(final ToweringTitan card) { + super(card); + } + + @Override + public ToweringTitan copy() { + return new ToweringTitan(this); + } +} + +enum ToweringTitanCount implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, + sourceAbility.getControllerId(), sourceAbility.getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .map(MageObject::getToughness) + .mapToInt(MageInt::getValue) + .sum(); + } + + @Override + public ToweringTitanCount copy() { + return null; + } + + @Override + public String getMessage() { + return ""; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java b/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java index ba686dda8f..e6efedf261 100644 --- a/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java +++ b/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java @@ -76,7 +76,6 @@ class ToweringWaveMysticTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever {this} deals damage, target player puts that many cards " + - "from the top of their library into their graveyard."; + return "Whenever {this} deals damage, target player mills that many cards."; } } diff --git a/Mage.Sets/src/mage/cards/t/TrackDown.java b/Mage.Sets/src/mage/cards/t/TrackDown.java new file mode 100644 index 0000000000..08506dd9d0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TrackDown.java @@ -0,0 +1,84 @@ +package mage.cards.t; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author arcox + */ +public final class TrackDown extends CardImpl { + + public TrackDown(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); + + // Scry 3, then reveal the top card of your library. If it's a creature or land card, draw a card. + this.getSpellAbility().addEffect(new ScryEffect(3, false)); + this.getSpellAbility().addEffect(new TrackDownEffect().concatBy(", then")); + } + + public TrackDown(final TrackDown card) { + super(card); + } + + @Override + public TrackDown copy() { + return new TrackDown(this); + } +} + +class TrackDownEffect extends OneShotEffect { + + public TrackDownEffect() { + super(Outcome.DrawCard); + this.staticText = "reveal the top card of your library. If it's a creature or land card, draw a card"; + } + + public TrackDownEffect(final TrackDownEffect effect) { + super(effect); + } + + @Override + public TrackDownEffect copy() { + return new TrackDownEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + + if (sourceObject == null || controller == null) { + return false; + } + + if (!controller.getLibrary().hasCards()) { + return false; + } + + Card card = controller.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + + CardsImpl cards = new CardsImpl(); + cards.add(card); + controller.revealCards(sourceObject.getName(), cards, game); + if (card.isLand() || card.isCreature()) { + controller.drawCards(1, source.getSourceId(), game); + } + + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TrainedCheetah.java b/Mage.Sets/src/mage/cards/t/TrainedCheetah.java index df354300b1..4b0d1ae315 100644 --- a/Mage.Sets/src/mage/cards/t/TrainedCheetah.java +++ b/Mage.Sets/src/mage/cards/t/TrainedCheetah.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class TrainedCheetah extends CardImpl { this.toughness = new MageInt(2); // Whenever Trained Cheetah becomes blocked, it gets +1/+1 until end of turn. - this.addAbility(new BecomesBlockedTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false)); } public TrainedCheetah(final TrainedCheetah card) { diff --git a/Mage.Sets/src/mage/cards/t/TrainingGrounds.java b/Mage.Sets/src/mage/cards/t/TrainingGrounds.java index 354f42c0bd..4e4488b5d4 100644 --- a/Mage.Sets/src/mage/cards/t/TrainingGrounds.java +++ b/Mage.Sets/src/mage/cards/t/TrainingGrounds.java @@ -13,7 +13,6 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.CardUtil; - import java.util.LinkedHashSet; import java.util.Set; import java.util.UUID; @@ -26,7 +25,8 @@ public final class TrainingGrounds extends CardImpl { public TrainingGrounds(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); - // Activated abilities of creatures you control cost up to {2} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana. + // Activated abilities of creatures you control cost up to {2} less to activate. + // This effect can't reduce the amount of mana an ability costs to activate to less than one mana. this.addAbility(new SimpleStaticAbility(new TrainingGroundsEffect())); } @@ -42,8 +42,9 @@ public final class TrainingGrounds extends CardImpl { class TrainingGroundsEffect extends CostModificationEffectImpl { - private static final String effectText = "Activated abilities of creatures you control cost {2} less to activate. " + - "This effect can't reduce the amount of mana an ability costs to activate to less than one mana"; + private static final String effectText = "Activated abilities of creatures you control " + + "cost up to {2} less to activate. " + + "This effect can't reduce the mana in that cost to less than one mana"; TrainingGroundsEffect() { super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); @@ -74,15 +75,20 @@ class TrainingGroundsEffect extends CostModificationEffectImpl { ChoiceImpl choice = new ChoiceImpl(true); Set set = new LinkedHashSet<>(); - for (int i = 0; i <= reduceMax; i++) { - set.add(String.valueOf(i)); + int reduce; + if (game.inCheckPlayableState()) { + reduce = reduceMax; + } else { + for (int i = 0; i <= reduceMax; i++) { + set.add(String.valueOf(i)); + } + choice.setChoices(set); + choice.setMessage("Reduce ability cost"); + if (!controller.choose(Outcome.Benefit, choice, game)) { + return false; + } + reduce = Integer.parseInt(choice.getChoice()); } - choice.setChoices(set); - choice.setMessage("Reduce ability cost"); - if (!controller.choose(Outcome.Benefit, choice, game)) { - return false; - } - int reduce = Integer.parseInt(choice.getChoice()); CardUtil.reduceCost(abilityToModify, reduce); return true; @@ -96,8 +102,9 @@ class TrainingGroundsEffect extends CostModificationEffectImpl { return false; } //Activated abilities of creatures you control - Permanent permanent = game.getPermanent(abilityToModify.getSourceId()); - return permanent != null && permanent.isCreature() + Permanent permanent = game.getPermanentOrLKIBattlefield(abilityToModify.getSourceId()); + return permanent != null + && permanent.isCreature() && permanent.isControlledBy(source.getControllerId()); } diff --git a/Mage.Sets/src/mage/cards/t/TraitorousGreed.java b/Mage.Sets/src/mage/cards/t/TraitorousGreed.java new file mode 100644 index 0000000000..86870ecba2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TraitorousGreed.java @@ -0,0 +1,42 @@ +package mage.cards.t; + +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TraitorousGreed extends CardImpl { + + public TraitorousGreed(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Add two mana of any one color. + this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains haste until end of turn.")); + this.getSpellAbility().addEffect(new AddManaOfAnyColorEffect(2)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private TraitorousGreed(final TraitorousGreed card) { + super(card); + } + + @Override + public TraitorousGreed copy() { + return new TraitorousGreed(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/Transmogrify.java b/Mage.Sets/src/mage/cards/t/Transmogrify.java new file mode 100644 index 0000000000..4078021fd4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/Transmogrify.java @@ -0,0 +1,92 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Library; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author jmharmon + */ + +public final class Transmogrify extends CardImpl { + + public Transmogrify(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // Exile target creature. + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new ExileTargetEffect()); + // That creature’s controller reveals cards from the top of their library until they reveal a creature card. + // That player puts that card onto the battlefield, then shuffles the rest into their library. + this.getSpellAbility().addEffect(new TransmogrifyEffect()); + } + + public Transmogrify(final Transmogrify card) { + super(card); + } + + @Override + public Transmogrify copy() { + return new Transmogrify(this); + } +} + +class TransmogrifyEffect extends OneShotEffect { + + public TransmogrifyEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library"; + } + + public TransmogrifyEffect(final TransmogrifyEffect effect) { + super(effect); + } + + @Override + public TransmogrifyEffect copy() { + return new TransmogrifyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (permanent != null) { + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null) { + Library library = player.getLibrary(); + if (library.hasCards()) { + Cards cards = new CardsImpl(); + Card toBattlefield = null; + for (Card card : library.getCards(game)) { + cards.add(card); + if (card.isCreature()) { + toBattlefield = card; + break; + } + } + if (toBattlefield != null) { + player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game); + } + player.revealCards(source, cards, game); + cards.remove(toBattlefield); + if (!cards.isEmpty()) { + player.shuffleLibrary(source, game); + } + } + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TrapRunner.java b/Mage.Sets/src/mage/cards/t/TrapRunner.java index 6d4b06b2f5..1b009185d0 100644 --- a/Mage.Sets/src/mage/cards/t/TrapRunner.java +++ b/Mage.Sets/src/mage/cards/t/TrapRunner.java @@ -1,45 +1,47 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.AfterBlockersAreDeclaredCondition; import mage.abilities.condition.common.IsPhaseCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.BecomeBlockedTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.TurnPhase; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.AttackingPredicate; import mage.filter.predicate.permanent.BlockedPredicate; -import mage.game.Game; -import mage.game.combat.CombatGroup; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TrapRunner extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("unblocked attacking creature"); + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("unblocked attacking creature"); static { filter.add(AttackingPredicate.instance); filter.add(Predicates.not(BlockedPredicate.instance)); } + private static final Condition condition = new CompoundCondition( + "during combat after blockers are declared", + new IsPhaseCondition(TurnPhase.COMBAT), + AfterBlockersAreDeclaredCondition.instance + ); + public TrapRunner(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.subtype.add(SubType.HUMAN); @@ -48,13 +50,14 @@ public final class TrapRunner extends CardImpl { this.toughness = new MageInt(3); // {T}: Target unblocked attacking creature becomes blocked. Activate this ability only during combat after blockers are declared. - Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new TrapRunnerEffect(), new TapSourceCost(), - new CompoundCondition("during combat after blockers are declared", new IsPhaseCondition(TurnPhase.COMBAT), AfterBlockersAreDeclaredCondition.instance)); + Ability ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new BecomeBlockedTargetEffect(), new TapSourceCost(), condition + ); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); } - public TrapRunner(final TrapRunner card) { + private TrapRunner(final TrapRunner card) { super(card); } @@ -64,35 +67,3 @@ public final class TrapRunner extends CardImpl { } } - -class TrapRunnerEffect extends OneShotEffect { - - public TrapRunnerEffect() { - super(Outcome.Benefit); - this.staticText = "Target unblocked attacking creature becomes blocked"; - } - - public TrapRunnerEffect(final TrapRunnerEffect effect) { - super(effect); - } - - @Override - public TrapRunnerEffect copy() { - return new TrapRunnerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (controller != null && permanent != null) { - CombatGroup combatGroup = game.getCombat().findGroup(permanent.getId()); - if (combatGroup != null) { - combatGroup.setBlocked(true, game); - game.informPlayers(permanent.getLogName() + " has become blocked"); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TrapfindersTrick.java b/Mage.Sets/src/mage/cards/t/TrapfindersTrick.java index 970b08a6f7..03c02e1b03 100644 --- a/Mage.Sets/src/mage/cards/t/TrapfindersTrick.java +++ b/Mage.Sets/src/mage/cards/t/TrapfindersTrick.java @@ -1,37 +1,34 @@ - package mage.cards.t; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; +import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author North */ public final class TrapfindersTrick extends CardImpl { public TrapfindersTrick(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}"); // Target player reveals their hand and discards all Trap cards. this.getSpellAbility().addEffect(new TrapfindersTrickEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); } - public TrapfindersTrick(final TrapfindersTrick card) { + private TrapfindersTrick(final TrapfindersTrick card) { super(card); } @@ -43,12 +40,18 @@ public final class TrapfindersTrick extends CardImpl { class TrapfindersTrickEffect extends OneShotEffect { - public TrapfindersTrickEffect() { + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(SubType.TRAP.getPredicate()); + } + + TrapfindersTrickEffect() { super(Outcome.Discard); this.staticText = "Target player reveals their hand and discards all Trap cards"; } - public TrapfindersTrickEffect(final TrapfindersTrickEffect effect) { + private TrapfindersTrickEffect(final TrapfindersTrickEffect effect) { super(effect); } @@ -60,17 +63,11 @@ class TrapfindersTrickEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - Cards hand = player.getHand(); - player.revealCards("Trapfinder's Trick", hand, game); - Set cards = hand.getCards(game); - for (Card card : cards) { - if (card != null && card.hasSubtype(SubType.TRAP, game)) { - player.discard(card, source, game); - } - } - return true; + if (player == null) { + return false; } - return false; + player.revealCards(source, player.getHand(), game); + player.discard(new CardsImpl(player.getHand().getCards(filter, game)), source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/t/Traumatize.java b/Mage.Sets/src/mage/cards/t/Traumatize.java index 7c07bf1795..3e8bc6598b 100644 --- a/Mage.Sets/src/mage/cards/t/Traumatize.java +++ b/Mage.Sets/src/mage/cards/t/Traumatize.java @@ -1,5 +1,3 @@ - - package mage.cards.t; import java.util.UUID; @@ -9,7 +7,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; @@ -21,7 +18,7 @@ import mage.target.TargetPlayer; public final class Traumatize extends CardImpl { public Traumatize(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}{U}"); this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new TraumatizeEffect()); @@ -41,7 +38,7 @@ class TraumatizeEffect extends OneShotEffect { public TraumatizeEffect() { super(Outcome.Detriment); - staticText = "Target player puts the top half of their library, rounded down, into their graveyard"; + staticText = "Target player mills half their library, rounded down"; } public TraumatizeEffect(final TraumatizeEffect effect) { @@ -53,7 +50,8 @@ class TraumatizeEffect extends OneShotEffect { Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { int amount = player.getLibrary().size() / 2; - return player.moveCards(player.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game); + player.millCards(amount, source, game); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/t/TreacherousVampire.java b/Mage.Sets/src/mage/cards/t/TreacherousVampire.java index b95c66a613..59d0625cc4 100644 --- a/Mage.Sets/src/mage/cards/t/TreacherousVampire.java +++ b/Mage.Sets/src/mage/cards/t/TreacherousVampire.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksOrBlocksTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CardsInControllerGraveCondition; import mage.abilities.costs.common.ExileFromGraveCost; @@ -55,7 +55,7 @@ public final class TreacherousVampire extends CardImpl { new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), new CardsInControllerGraveCondition(7), "As long as seven or more cards are in your graveyard, {this} gets +2/+2")); Effect effect = new ConditionalContinuousEffect( - new GainAbilitySourceEffect(new DiesTriggeredAbility(new LoseLifeSourceControllerEffect(6))), + new GainAbilitySourceEffect(new DiesSourceTriggeredAbility(new LoseLifeSourceControllerEffect(6))), new CardsInControllerGraveCondition(7), "and has \"When {this} dies, you lose 6 life.\"" ); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/t/TreacherousWerewolf.java b/Mage.Sets/src/mage/cards/t/TreacherousWerewolf.java index 1f565dea59..4d980d39d1 100644 --- a/Mage.Sets/src/mage/cards/t/TreacherousWerewolf.java +++ b/Mage.Sets/src/mage/cards/t/TreacherousWerewolf.java @@ -4,7 +4,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CardsInControllerGraveCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -39,7 +39,7 @@ public final class TreacherousWerewolf extends CardImpl { new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), new CardsInControllerGraveCondition(7), "As long as seven or more cards are in your graveyard, {this} gets +2/+2")); Effect effect = new ConditionalContinuousEffect( - new GainAbilitySourceEffect(new DiesTriggeredAbility(new LoseLifeSourceControllerEffect(4))), + new GainAbilitySourceEffect(new DiesSourceTriggeredAbility(new LoseLifeSourceControllerEffect(4))), new CardsInControllerGraveCondition(7), "and has \"When {this} dies, you lose 4 life.\"" ); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/t/TreasonousOgre.java b/Mage.Sets/src/mage/cards/t/TreasonousOgre.java index ea75543fd4..478ba8d150 100644 --- a/Mage.Sets/src/mage/cards/t/TreasonousOgre.java +++ b/Mage.Sets/src/mage/cards/t/TreasonousOgre.java @@ -5,6 +5,8 @@ import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.dynamicvalue.common.ControllerLifeDividedValue; +import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.keyword.DethroneAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -31,7 +33,10 @@ public final class TreasonousOgre extends CardImpl { // Dethrone this.addAbility(new DethroneAbility()); // Pay 3 life: Add {R}. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new Mana(ColoredManaSymbol.R), new PayLifeCost(3))); + + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, + new BasicManaEffect(new Mana(ColoredManaSymbol.R), new ControllerLifeDividedValue(3)), + new PayLifeCost(3))); } public TreasonousOgre(final TreasonousOgre card) { diff --git a/Mage.Sets/src/mage/cards/t/TreasureKeeper.java b/Mage.Sets/src/mage/cards/t/TreasureKeeper.java index 1bd30575c3..cd6019caee 100644 --- a/Mage.Sets/src/mage/cards/t/TreasureKeeper.java +++ b/Mage.Sets/src/mage/cards/t/TreasureKeeper.java @@ -3,7 +3,7 @@ package mage.cards.t; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -32,7 +32,7 @@ public final class TreasureKeeper extends CardImpl { // When Treasure Keeper dies, reveal cards from the top of your library until you reveal a nonland card with converted mana cost 3 or less. // You may cast that card without paying its mana cost. Put all revealed cards not cast this way on the bottom of your library in a random order. - this.addAbility(new DiesTriggeredAbility(new TreasureKeeperEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new TreasureKeeperEffect())); } public TreasureKeeper(final TreasureKeeper card) { @@ -63,8 +63,7 @@ class TreasureKeeperEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Boolean cardWasCast = false; Player controller = game.getPlayer(source.getControllerId()); - if (controller != null - && !controller.getLibrary().isEmptyDraw()) { + if (controller != null && controller.getLibrary().hasCards()) { CardsImpl toReveal = new CardsImpl(); Card nonLandCard = null; for (Card card : controller.getLibrary().getCards(game)) { diff --git a/Mage.Sets/src/mage/cards/t/TreasureMap.java b/Mage.Sets/src/mage/cards/t/TreasureMap.java index 7c0035dcbd..b0abbf0a74 100644 --- a/Mage.Sets/src/mage/cards/t/TreasureMap.java +++ b/Mage.Sets/src/mage/cards/t/TreasureMap.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -21,8 +19,9 @@ import mage.game.permanent.Permanent; import mage.game.permanent.token.TreasureToken; import mage.players.Player; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class TreasureMap extends CardImpl { @@ -81,7 +80,7 @@ class TreasureMapEffect extends OneShotEffect { if (counters > 2) { permanent.removeCounters("landmark", counters, game); new TransformSourceEffect(true).apply(game, source); - new CreateTokenEffect(new TreasureToken("XLN"), 3).apply(game, source); + new CreateTokenEffect(new TreasureToken(), 3).apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/t/TreasureNabber.java b/Mage.Sets/src/mage/cards/t/TreasureNabber.java index ba23895c9d..eb4f26045a 100644 --- a/Mage.Sets/src/mage/cards/t/TreasureNabber.java +++ b/Mage.Sets/src/mage/cards/t/TreasureNabber.java @@ -64,7 +64,7 @@ class TreasureNabberAbility extends TriggeredAbilityImpl { if (game.getOpponents(controllerId).contains(event.getPlayerId())) { Permanent permanent = game.getPermanent(event.getSourceId()); if (permanent != null && permanent.isArtifact()) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/t/TreeOfPerdition.java b/Mage.Sets/src/mage/cards/t/TreeOfPerdition.java index 78a0d448b8..131b6a1d5d 100644 --- a/Mage.Sets/src/mage/cards/t/TreeOfPerdition.java +++ b/Mage.Sets/src/mage/cards/t/TreeOfPerdition.java @@ -54,7 +54,7 @@ class TreeOfPerditionEffect extends OneShotEffect { public TreeOfPerditionEffect() { super(Outcome.Neutral); - staticText = "Exchange target opponent's life total with Tree of Perdition's toughness"; + staticText = "Exchange target opponent's life total with {this}'s toughness"; } public TreeOfPerditionEffect(final TreeOfPerditionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TreefolkMystic.java b/Mage.Sets/src/mage/cards/t/TreefolkMystic.java index a7750637f7..e9a4807fc1 100644 --- a/Mage.Sets/src/mage/cards/t/TreefolkMystic.java +++ b/Mage.Sets/src/mage/cards/t/TreefolkMystic.java @@ -6,7 +6,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -30,7 +30,7 @@ public final class TreefolkMystic extends CardImpl { this.toughness = new MageInt(4); // Whenever Treefolk Mystic blocks or becomes blocked by a creature, destroy all Auras attached to that creature. - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new TreefolkMysticEffect(), false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new TreefolkMysticEffect(), false)); } public TreefolkMystic(final TreefolkMystic card) { diff --git a/Mage.Sets/src/mage/cards/t/TreeshakerChimera.java b/Mage.Sets/src/mage/cards/t/TreeshakerChimera.java index d9f9476d45..d61b3b66bd 100644 --- a/Mage.Sets/src/mage/cards/t/TreeshakerChimera.java +++ b/Mage.Sets/src/mage/cards/t/TreeshakerChimera.java @@ -1,7 +1,7 @@ package mage.cards.t; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.combat.MustBeBlockedByAllSourceEffect; @@ -28,7 +28,7 @@ public final class TreeshakerChimera extends CardImpl { this.addAbility(new SimpleStaticAbility(new MustBeBlockedByAllSourceEffect())); // When Treeshaker Chimera dies, draw three cards. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(3))); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(3))); } private TreeshakerChimera(final TreeshakerChimera card) { diff --git a/Mage.Sets/src/mage/cards/t/TriadOfFates.java b/Mage.Sets/src/mage/cards/t/TriadOfFates.java index 472b1da334..0fcbfbdeca 100644 --- a/Mage.Sets/src/mage/cards/t/TriadOfFates.java +++ b/Mage.Sets/src/mage/cards/t/TriadOfFates.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -14,11 +12,7 @@ import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetE import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; @@ -29,8 +23,9 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TriadOfFates extends CardImpl { @@ -44,7 +39,7 @@ public final class TriadOfFates extends CardImpl { } public TriadOfFates(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); @@ -64,7 +59,7 @@ public final class TriadOfFates extends CardImpl { ability.addCost(new TapSourceCost()); target = new TargetCreaturePermanent(filterCounter); ability.addTarget(target); - ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); this.addAbility(ability); // {B}, {T}: Exile target creature that has a fate counter on it. Its controller draws two cards. @@ -108,7 +103,7 @@ class DrawCardControllerTargetEffect extends OneShotEffect { if (creature != null) { Player controllerOfTarget = game.getPlayer(creature.getControllerId()); if (controllerOfTarget != null) { - controllerOfTarget.drawCards(2, game); + controllerOfTarget.drawCards(2, source.getSourceId(), game); } } return false; diff --git a/Mage.Sets/src/mage/cards/t/TrialOfAmbition.java b/Mage.Sets/src/mage/cards/t/TrialOfAmbition.java index 57ff5b1666..ec915f1734 100644 --- a/Mage.Sets/src/mage/cards/t/TrialOfAmbition.java +++ b/Mage.Sets/src/mage/cards/t/TrialOfAmbition.java @@ -31,7 +31,7 @@ public final class TrialOfAmbition extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); // When Trial of Ambition enters the battlefield, target opponent sacrifices a creature. - Ability ability = new EntersBattlefieldTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURES, 1, "target opponent")); + Ability ability = new EntersBattlefieldTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE_A, 1, "target opponent")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TribalGolem.java b/Mage.Sets/src/mage/cards/t/TribalGolem.java index 5bbc46385f..9f86d2fe77 100644 --- a/Mage.Sets/src/mage/cards/t/TribalGolem.java +++ b/Mage.Sets/src/mage/cards/t/TribalGolem.java @@ -54,22 +54,22 @@ public final class TribalGolem extends CardImpl { Effect effect1 = new ConditionalContinuousEffect( new GainAbilitySourceEffect(TrampleAbility.getInstance()), new PermanentsOnTheBattlefieldCondition(filter1), - "{this} has trample as long as you control a Beast," + "{this} has trample as long as you control a Beast" ); Effect effect2 = new ConditionalContinuousEffect( new GainAbilitySourceEffect(HasteAbility.getInstance()), new PermanentsOnTheBattlefieldCondition(filter2), - "haste as long as you control a Goblin," + ", haste as long as you control a Goblin" ); Effect effect3 = new ConditionalContinuousEffect( new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()), new PermanentsOnTheBattlefieldCondition(filter3), - "first strike as long as you control a Soldier," + ", first strike as long as you control a Soldier" ); Effect effect4 = new ConditionalContinuousEffect( new GainAbilitySourceEffect(FlyingAbility.getInstance()), new PermanentsOnTheBattlefieldCondition(filter4), - "flying as long as you control a Wizard," + ", flying as long as you control a Wizard" ); Effect effect5 = new ConditionalContinuousEffect( new GainAbilitySourceEffect(new SimpleActivatedAbility( @@ -78,9 +78,9 @@ public final class TribalGolem extends CardImpl { new ManaCostsImpl("{B}") )), new PermanentsOnTheBattlefieldCondition(filter5), - "and \"{B}: Regenerate {this}\" as long as you control a Zombie" + ", and \"{B}: Regenerate {this}\" as long as you control a Zombie" ); - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect1); + Ability ability = new SimpleStaticAbility(effect1); ability.addEffect(effect2); ability.addEffect(effect3); ability.addEffect(effect4); diff --git a/Mage.Sets/src/mage/cards/t/TrinketMage.java b/Mage.Sets/src/mage/cards/t/TrinketMage.java index 2d39f8b52b..0459e8fe6e 100644 --- a/Mage.Sets/src/mage/cards/t/TrinketMage.java +++ b/Mage.Sets/src/mage/cards/t/TrinketMage.java @@ -21,7 +21,7 @@ import mage.target.common.TargetCardInLibrary; */ public final class TrinketMage extends CardImpl { - private static final FilterCard filter = new FilterCard("an artifact card with converted mana cost 1 or less"); + private static final FilterCard filter = new FilterCard("artifact card with converted mana cost 1 or less"); static { filter.add(CardType.ARTIFACT.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/t/TriplicateSpirits.java b/Mage.Sets/src/mage/cards/t/TriplicateSpirits.java index fcdd7040df..c78169a471 100644 --- a/Mage.Sets/src/mage/cards/t/TriplicateSpirits.java +++ b/Mage.Sets/src/mage/cards/t/TriplicateSpirits.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.ConvokeAbility; import mage.cards.CardImpl; @@ -9,21 +7,22 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TriplicateSpirits extends CardImpl { public TriplicateSpirits(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}{W}"); // Convoke this.addAbility(new ConvokeAbility()); // Create three 1/1 white Spirit creature tokens with flying. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken("M15"), 3)); - + this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken(), 3)); + } public TriplicateSpirits(final TriplicateSpirits card) { diff --git a/Mage.Sets/src/mage/cards/t/TriumphantSurge.java b/Mage.Sets/src/mage/cards/t/TriumphantSurge.java index 4e512e48f3..2d35083b54 100644 --- a/Mage.Sets/src/mage/cards/t/TriumphantSurge.java +++ b/Mage.Sets/src/mage/cards/t/TriumphantSurge.java @@ -19,7 +19,7 @@ import java.util.UUID; public final class TriumphantSurge extends CardImpl { private static final FilterPermanent filter - = new FilterCreaturePermanent("creature with power 4 or greater."); + = new FilterCreaturePermanent("creature with power 4 or greater"); static { filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); diff --git a/Mage.Sets/src/mage/cards/t/Truce.java b/Mage.Sets/src/mage/cards/t/Truce.java index 7cd117dd16..49762b869b 100644 --- a/Mage.Sets/src/mage/cards/t/Truce.java +++ b/Mage.Sets/src/mage/cards/t/Truce.java @@ -58,7 +58,7 @@ class TruceEffect extends OneShotEffect { Player player = game.getPlayer(playerId); if (player != null) { int cardsToDraw = player.getAmount(0, 2, "Draw how many cards?", game); - player.drawCards(cardsToDraw, game); + player.drawCards(cardsToDraw, source.getSourceId(), game); player.gainLife((2 - cardsToDraw) * 2, game, source); } } diff --git a/Mage.Sets/src/mage/cards/t/Trufflesnout.java b/Mage.Sets/src/mage/cards/t/Trufflesnout.java new file mode 100644 index 0000000000..4a60fa0da0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/Trufflesnout.java @@ -0,0 +1,48 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Trufflesnout extends CardImpl { + + public Trufflesnout(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.BOAR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Trufflesnout enters the battlefield, choose one — + // • Put a +1/+1 counter on Trufflesnout. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + ); + + // • You gain 4 life. + ability.addMode(new Mode(new GainLifeEffect(4))); + this.addAbility(ability); + } + + private Trufflesnout(final Trufflesnout card) { + super(card); + } + + @Override + public Trufflesnout copy() { + return new Trufflesnout(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TrumpetingGnarr.java b/Mage.Sets/src/mage/cards/t/TrumpetingGnarr.java new file mode 100644 index 0000000000..64d0a7ada0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TrumpetingGnarr.java @@ -0,0 +1,42 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.BeastToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TrumpetingGnarr extends CardImpl { + + public TrumpetingGnarr(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Mutate {3}{G/U}{G/U} + this.addAbility(new MutateAbility(this, "{3}{G/U}{G/U}")); + + // Whenever this creature mutates, create a 3/3 green Beast creature token. + this.addAbility(new MutatesSourceTriggeredAbility(new CreateTokenEffect(new BeastToken()))); + } + + private TrumpetingGnarr(final TrumpetingGnarr card) { + super(card); + } + + @Override + public TrumpetingGnarr copy() { + return new TrumpetingGnarr(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TrustyRetriever.java b/Mage.Sets/src/mage/cards/t/TrustyRetriever.java new file mode 100644 index 0000000000..aab1fa609e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TrustyRetriever.java @@ -0,0 +1,54 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactOrEnchantmentCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TrustyRetriever extends CardImpl { + + private static final FilterCard filter + = new FilterArtifactOrEnchantmentCard("artifact or enchantment card from your graveyard"); + + public TrustyRetriever(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.DOG); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // When Trusty Retriever enters the battlefield, choose one — + // • Put a +1/+1 counter on Trusty Retriever. + Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())); + + // • Return target artifact or enchantment card from your graveyard to your hand. + Mode mode = new Mode(new ReturnFromGraveyardToHandTargetEffect()); + mode.addTarget(new TargetCardInYourGraveyard(filter)); + ability.addMode(mode); + this.addAbility(ability); + } + + private TrustyRetriever(final TrustyRetriever card) { + super(card); + } + + @Override + public TrustyRetriever copy() { + return new TrustyRetriever(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TrynnChampionOfFreedom.java b/Mage.Sets/src/mage/cards/t/TrynnChampionOfFreedom.java new file mode 100644 index 0000000000..1554b12592 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TrynnChampionOfFreedom.java @@ -0,0 +1,55 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.common.AttackedThisTurnCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.game.permanent.token.HumanSoldierToken; +import mage.watchers.common.AttackedThisTurnWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TrynnChampionOfFreedom extends CardImpl { + + public TrynnChampionOfFreedom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Partner with Silvar, Devourer of the Free + this.addAbility(new PartnerWithAbility("Silvar, Devourer of the Free")); + + // At the beginning of your end step, if you attacked this turn, create a 1/1 white Human Soldier creature token. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new CreateTokenEffect(new HumanSoldierToken()), + TargetController.YOU, false + ), AttackedThisTurnCondition.instance, "At the beginning of your end step, " + + "if you attacked this turn, create a 1/1 white Human Soldier creature token." + ), new AttackedThisTurnWatcher()); + } + + private TrynnChampionOfFreedom(final TrynnChampionOfFreedom card) { + super(card); + } + + @Override + public TrynnChampionOfFreedom copy() { + return new TrynnChampionOfFreedom(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TsabosAssassin.java b/Mage.Sets/src/mage/cards/t/TsabosAssassin.java index f0b401a4d9..220405edbd 100644 --- a/Mage.Sets/src/mage/cards/t/TsabosAssassin.java +++ b/Mage.Sets/src/mage/cards/t/TsabosAssassin.java @@ -74,7 +74,7 @@ class TsabosAssasinEffect extends OneShotEffect { Condition condition = new MostCommonColorCondition(permanent.getColor(game)); if (condition.apply(game, source)) { Effect effect = new DestroyTargetEffect(); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/t/TsabosDecree.java b/Mage.Sets/src/mage/cards/t/TsabosDecree.java index 193105e8b1..ab5b862b09 100644 --- a/Mage.Sets/src/mage/cards/t/TsabosDecree.java +++ b/Mage.Sets/src/mage/cards/t/TsabosDecree.java @@ -1,15 +1,11 @@ - package mage.cards.t; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.choices.Choice; import mage.choices.ChoiceCreatureType; import mage.constants.CardType; @@ -22,8 +18,9 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author fireshoes */ public final class TsabosDecree extends CardImpl { @@ -36,7 +33,7 @@ public final class TsabosDecree extends CardImpl { this.getSpellAbility().addTarget(new TargetPlayer()); } - public TsabosDecree(final TsabosDecree card) { + private TsabosDecree(final TsabosDecree card) { super(card); } @@ -48,12 +45,14 @@ public final class TsabosDecree extends CardImpl { class TsabosDecreeEffect extends OneShotEffect { - public TsabosDecreeEffect() { + TsabosDecreeEffect() { super(Outcome.UnboostCreature); - staticText = "Choose a creature type. Target player reveals their hand and discards all creature cards of that type. Then destroy all creatures of that type that player controls. They can't be regenerated"; + staticText = "Choose a creature type. Target player reveals their hand and discards " + + "all creature cards of that type. Then destroy all creatures of that type that player controls. " + + "They can't be regenerated"; } - public TsabosDecreeEffect(final TsabosDecreeEffect effect) { + private TsabosDecreeEffect(final TsabosDecreeEffect effect) { super(effect); } @@ -62,36 +61,29 @@ class TsabosDecreeEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); MageObject sourceObject = game.getObject(source.getSourceId()); - if (player != null) { - if(sourceObject != null) { - Choice typeChoice = new ChoiceCreatureType(sourceObject); - if (!player.choose(outcome, typeChoice, game)) { - return false; - } - game.informPlayers(sourceObject.getLogName() + " chosen type: " + typeChoice.getChoice()); - targetPlayer.revealCards("hand of " + targetPlayer.getName(), targetPlayer.getHand(), game); - FilterCard filterCard = new FilterCard(); - filterCard.add(SubType.byDescription(typeChoice.getChoice()).getPredicate()); - List toDiscard = new ArrayList<>(); - for (Card card : targetPlayer.getHand().getCards(game)) { - if (filterCard.match(card, game)) { - toDiscard.add(card); - } - } - for (Card card : toDiscard) { - targetPlayer.discard(card, source, game); - } - FilterCreaturePermanent filterCreaturePermanent = new FilterCreaturePermanent(); - filterCreaturePermanent.add(SubType.byDescription(typeChoice.getChoice()).getPredicate()); - for (Permanent creature : game.getBattlefield().getActivePermanents(filterCreaturePermanent, source.getSourceId(), game)) { - if (creature.isControlledBy(targetPlayer.getId())) { - creature.destroy(source.getSourceId(), game, true); - } - } - return true; + if (player == null) { + return false; + } + if (sourceObject == null) { + return false; + } + Choice typeChoice = new ChoiceCreatureType(sourceObject); + if (!player.choose(outcome, typeChoice, game)) { + return false; + } + game.informPlayers(sourceObject.getLogName() + " chosen type: " + typeChoice.getChoice()); + targetPlayer.revealCards("hand of " + targetPlayer.getName(), targetPlayer.getHand(), game); + FilterCard filterCard = new FilterCard(); + filterCard.add(SubType.byDescription(typeChoice.getChoice()).getPredicate()); + targetPlayer.discard(new CardsImpl(targetPlayer.getHand().getCards(filterCard, game)), source, game); + FilterCreaturePermanent filterCreaturePermanent = new FilterCreaturePermanent(); + filterCreaturePermanent.add(SubType.byDescription(typeChoice.getChoice()).getPredicate()); + for (Permanent creature : game.getBattlefield().getActivePermanents(filterCreaturePermanent, source.getSourceId(), game)) { + if (creature.isControlledBy(targetPlayer.getId())) { + creature.destroy(source.getSourceId(), game, true); } } - return false; + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/t/TukatongueThallid.java b/Mage.Sets/src/mage/cards/t/TukatongueThallid.java index 4d87a9caf8..ba0779473a 100644 --- a/Mage.Sets/src/mage/cards/t/TukatongueThallid.java +++ b/Mage.Sets/src/mage/cards/t/TukatongueThallid.java @@ -4,7 +4,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class TukatongueThallid extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SaprolingToken()), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SaprolingToken()), false)); } public TukatongueThallid(final TukatongueThallid card) { diff --git a/Mage.Sets/src/mage/cards/t/TuktukTheExplorer.java b/Mage.Sets/src/mage/cards/t/TuktukTheExplorer.java index 29eb1f246b..b6a67f4bd4 100644 --- a/Mage.Sets/src/mage/cards/t/TuktukTheExplorer.java +++ b/Mage.Sets/src/mage/cards/t/TuktukTheExplorer.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class TuktukTheExplorer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(HasteAbility.getInstance()); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new TuktukTheReturnedToken(expansionSetCode)))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new TuktukTheReturnedToken(expansionSetCode)))); } public TuktukTheExplorer(final TuktukTheExplorer card) { diff --git a/Mage.Sets/src/mage/cards/t/TunnelVision.java b/Mage.Sets/src/mage/cards/t/TunnelVision.java index 33b58c266c..ff1a97a640 100644 --- a/Mage.Sets/src/mage/cards/t/TunnelVision.java +++ b/Mage.Sets/src/mage/cards/t/TunnelVision.java @@ -73,7 +73,7 @@ class TunnelVisionEffect extends OneShotEffect { for (Card card : targetPlayer.getLibrary().getCards(game)) { cardsToReveal.add(card); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { namedCard = card; break; } diff --git a/Mage.Sets/src/mage/cards/t/TurbulentDreams.java b/Mage.Sets/src/mage/cards/t/TurbulentDreams.java index 860c8a76d0..999393a231 100644 --- a/Mage.Sets/src/mage/cards/t/TurbulentDreams.java +++ b/Mage.Sets/src/mage/cards/t/TurbulentDreams.java @@ -1,4 +1,3 @@ - package mage.cards.t; import mage.abilities.Ability; @@ -27,7 +26,7 @@ public final class TurbulentDreams extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}{U}"); // As an additional cost to cast Turbulent Dreams, discard X cards. - this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, true)); + this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, false)); // Return X target nonland permanents to their owners' hands. Effect effect = new ReturnToHandTargetEffect(); diff --git a/Mage.Sets/src/mage/cards/t/TurfWound.java b/Mage.Sets/src/mage/cards/t/TurfWound.java index a58cade017..7825d60fdc 100644 --- a/Mage.Sets/src/mage/cards/t/TurfWound.java +++ b/Mage.Sets/src/mage/cards/t/TurfWound.java @@ -46,7 +46,7 @@ class TurfWoundEffect extends ContinuousRuleModifyingEffectImpl { public TurfWoundEffect() { super(Duration.EndOfTurn, Outcome.Detriment); - staticText = "Target player can't play land cards this turn."; + staticText = "Target player can't play land cards this turn"; } public TurfWoundEffect(final TurfWoundEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TurntimberSower.java b/Mage.Sets/src/mage/cards/t/TurntimberSower.java index 2db016a36c..ed47b60134 100644 --- a/Mage.Sets/src/mage/cards/t/TurntimberSower.java +++ b/Mage.Sets/src/mage/cards/t/TurntimberSower.java @@ -1,6 +1,5 @@ package mage.cards.t; -import java.util.Set; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -11,10 +10,10 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.Card; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; @@ -94,10 +93,8 @@ class TurntimberSowerTriggeredAbility extends TriggeredAbilityImpl { for (Card card : zEvent.getCards()) { if (card != null) { UUID cardOwnerId = card.getOwnerId(); - Set cardType = card.getCardType(); if (cardOwnerId != null && card.isOwnedBy(getControllerId()) - && cardType != null && card.isLand()) { return true; } diff --git a/Mage.Sets/src/mage/cards/t/TwilightDrover.java b/Mage.Sets/src/mage/cards/t/TwilightDrover.java index 9b230eea48..2a7f1c2dd6 100644 --- a/Mage.Sets/src/mage/cards/t/TwilightDrover.java +++ b/Mage.Sets/src/mage/cards/t/TwilightDrover.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.LeavesBattlefieldAllTriggeredAbility; @@ -20,8 +18,9 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** - * * @author emerald000 */ public final class TwilightDrover extends CardImpl { @@ -33,7 +32,7 @@ public final class TwilightDrover extends CardImpl { } public TwilightDrover(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.SPIRIT); this.power = new MageInt(1); @@ -43,7 +42,7 @@ public final class TwilightDrover extends CardImpl { this.addAbility(new LeavesBattlefieldAllTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter)); // {2}{W}, Remove a +1/+1 counter from Twilight Drover: Create two 1/1 white Spirit creature tokens with flying. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken("RAV"), 2), new ManaCostsImpl<>("{2}{W}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken(), 2), new ManaCostsImpl<>("{2}{W}")); ability.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TwilightProphet.java b/Mage.Sets/src/mage/cards/t/TwilightProphet.java index 1d09e42beb..bdc5347216 100644 --- a/Mage.Sets/src/mage/cards/t/TwilightProphet.java +++ b/Mage.Sets/src/mage/cards/t/TwilightProphet.java @@ -86,7 +86,7 @@ class TwilightProphetEffect extends OneShotEffect { if (card != null) { controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); controller.moveCards(card, Zone.HAND, source, game); - game.applyEffects(); + game.getState().processAction(game); int amount = card.getConvertedManaCost(); if (amount > 0) { new LoseLifeOpponentsEffect(amount).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/t/TwinbladeAssassins.java b/Mage.Sets/src/mage/cards/t/TwinbladeAssassins.java new file mode 100644 index 0000000000..d67fef4433 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TwinbladeAssassins.java @@ -0,0 +1,47 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.common.MorbidCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.watchers.common.MorbidWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TwinbladeAssassins extends CardImpl { + + public TwinbladeAssassins(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{G}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.ASSASSIN); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // At the beginning of your end step, if a creature died this turn, draw a card. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new DrawCardSourceControllerEffect(1), TargetController.YOU, false + ), MorbidCondition.instance, "At the beginning of your end step, " + + "if a creature died this turn, draw a card." + ), new MorbidWatcher()); + } + + private TwinbladeAssassins(final TwinbladeAssassins card) { + super(card); + } + + @Override + public TwinbladeAssassins copy() { + return new TwinbladeAssassins(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/Twinflame.java b/Mage.Sets/src/mage/cards/t/Twinflame.java index c1f6f7cd67..5dc10c2239 100644 --- a/Mage.Sets/src/mage/cards/t/Twinflame.java +++ b/Mage.Sets/src/mage/cards/t/Twinflame.java @@ -1,4 +1,3 @@ - package mage.cards.t; import mage.abilities.Ability; @@ -6,8 +5,8 @@ import mage.abilities.DelayedTriggeredAbility; import mage.abilities.abilityword.StriveAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -25,16 +24,16 @@ import java.util.List; import java.util.UUID; /** - * * @author LevelX2 */ public final class Twinflame extends CardImpl { public Twinflame(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); // Strive - Twinflame costs 2R more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{R}")); + // Choose any number of target creatures you control. For each of them, create a token that's a copy of that creature, except it has haste. Exile them at the beginning of the next end step. this.getSpellAbility().addEffect(new TwinflameCopyEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, new FilterControlledCreaturePermanent(), false)); diff --git a/Mage.Sets/src/mage/cards/t/TwinningGlass.java b/Mage.Sets/src/mage/cards/t/TwinningGlass.java index 2498178e5c..64d6480603 100644 --- a/Mage.Sets/src/mage/cards/t/TwinningGlass.java +++ b/Mage.Sets/src/mage/cards/t/TwinningGlass.java @@ -1,5 +1,6 @@ package mage.cards.t; +import java.util.ArrayList; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,18 +13,18 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; -import mage.target.common.TargetCardInHand; import mage.watchers.common.SpellsCastWatcher; - import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; +import mage.filter.common.FilterNonlandCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.target.TargetCard; /** * @author jeffwadsworth @@ -72,8 +73,7 @@ class TwinningGlassEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - FilterCard filterCard = new FilterCard(); - filterCard.add(Predicates.not(CardType.LAND.getPredicate())); + List spells = new ArrayList<>(); Permanent twinningGlass = game.getPermanent(source.getSourceId()); Player controller = game.getPlayer(source.getControllerId()); SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); @@ -84,19 +84,28 @@ class TwinningGlassEffect extends OneShotEffect { && controller != null && watcher != null) { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - List spells = watcher.getSpellsCastThisTurn(playerId); - if (spells != null - && !spells.isEmpty()) { - for (Spell spell : spells) { - filterCard.add(new NamePredicate(spell.getName())); + if (watcher.getSpellsCastThisTurn(playerId) != null) { + for (Spell spell : watcher.getSpellsCastThisTurn(playerId)) { + spells.add(spell); } } } - TargetCardInHand target = new TargetCardInHand(0, 1, filterCard); + if (spells.isEmpty()) { + return false; + } + List predicates = spells.stream() + .map(Spell::getName) + .filter(s -> !"".equals(s)) + .map(NamePredicate::new) + .collect(Collectors.toList()); + FilterNonlandCard filterCard = new FilterNonlandCard("nonland card that was cast this turn"); + filterCard.add(Predicates.or(predicates)); + TargetCard target = new TargetCard(0, 1, Zone.HAND, filterCard); if (controller.choose(Outcome.PlayForFree, controller.getHand(), target, game)) { Card chosenCard = game.getCard(target.getFirstTarget()); if (chosenCard != null) { - if (controller.chooseUse(Outcome.PlayForFree, "Cast the card without paying mana cost?", source, game)) { + if (controller.chooseUse(Outcome.PlayForFree, "Cast " + + chosenCard.getName() + " without paying its mana cost?", source, game)) { game.getState().setValue("PlayFromNotOwnHandZone" + chosenCard.getId(), Boolean.TRUE); Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(chosenCard, game, true), game, true, new MageObjectReference(source.getSourceObject(game), game)); diff --git a/Mage.Sets/src/mage/cards/t/TwinningStaff.java b/Mage.Sets/src/mage/cards/t/TwinningStaff.java new file mode 100644 index 0000000000..75ccd4506d --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TwinningStaff.java @@ -0,0 +1,92 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CopyTargetSpellEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.filter.FilterSpell; +import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TwinningStaff extends CardImpl { + + private static final FilterSpell filter = new FilterInstantOrSorcerySpell("instant or sorcery spell you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public TwinningStaff(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // If you would copy a spell one or more times, instead copy it that many times plus an additional time. You may choose new targets for the additional copy. + this.addAbility(new SimpleStaticAbility(new TwinningStaffEffect())); + + // {7}, {T}: Copy target instant or sorcery spell you control. You may choose new targets for the copy. + Ability ability = new SimpleActivatedAbility(new CopyTargetSpellEffect(), new GenericManaCost(7)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetSpell(filter)); + this.addAbility(ability); + } + + private TwinningStaff(final TwinningStaff card) { + super(card); + } + + @Override + public TwinningStaff copy() { + return new TwinningStaff(this); + } +} + +class TwinningStaffEffect extends ReplacementEffectImpl { + + TwinningStaffEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If you would copy a spell one or more times, " + + "instead copy it that many times plus an additional time. " + + "You may choose new targets for the additional copy."; + } + + private TwinningStaffEffect(final TwinningStaffEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(event.getAmount() + 1); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COPY_STACKOBJECT; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(source.getControllerId()) + && game.getSpellOrLKIStack(event.getTargetId()) != null; + } + + @Override + public TwinningStaffEffect copy() { + return new TwinningStaffEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TwistAllegiance.java b/Mage.Sets/src/mage/cards/t/TwistAllegiance.java index 625aa01123..8b974cfcbe 100644 --- a/Mage.Sets/src/mage/cards/t/TwistAllegiance.java +++ b/Mage.Sets/src/mage/cards/t/TwistAllegiance.java @@ -70,11 +70,11 @@ class TwistAllegianceEffect extends OneShotEffect { if (permanent.isControlledBy(source.getControllerId()) || permanent.isControlledBy(targetOpponent.getId())) { UUID newController = permanent.isControlledBy(source.getControllerId()) ? targetOpponent.getId() : source.getControllerId(); ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfTurn, true, newController); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); permanent.untap(game); effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/t/TwistedJustice.java b/Mage.Sets/src/mage/cards/t/TwistedJustice.java index 3fa21371ee..dff11f79f6 100644 --- a/Mage.Sets/src/mage/cards/t/TwistedJustice.java +++ b/Mage.Sets/src/mage/cards/t/TwistedJustice.java @@ -69,7 +69,7 @@ class TwistedJusticeEffect extends OneShotEffect { Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null) { permanent.sacrifice(source.getSourceId(), game); - controller.drawCards(permanent.getPower().getValue(), game); + controller.drawCards(permanent.getPower().getValue(), source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/t/TwoHeadedCerberus.java b/Mage.Sets/src/mage/cards/t/TwoHeadedCerberus.java index 495ca8c029..476abb0bce 100644 --- a/Mage.Sets/src/mage/cards/t/TwoHeadedCerberus.java +++ b/Mage.Sets/src/mage/cards/t/TwoHeadedCerberus.java @@ -17,7 +17,7 @@ public final class TwoHeadedCerberus extends CardImpl { public TwoHeadedCerberus(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java b/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java index 8ed90c07f2..9086b58083 100644 --- a/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java +++ b/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java @@ -71,8 +71,7 @@ class TymaretCallsTheDeadFirstEffect extends OneShotEffect { TymaretCallsTheDeadFirstEffect() { super(Benefit); - staticText = "put the top three cards of your library into your graveyard. " + - "Then you may exile a creature or enchantment card from your graveyard. " + + staticText = "mill three cards. Then you may exile a creature or enchantment card from your graveyard. " + "If you do, create a 2/2 black Zombie creature token"; } diff --git a/Mage.Sets/src/mage/cards/t/TymnaTheWeaver.java b/Mage.Sets/src/mage/cards/t/TymnaTheWeaver.java index 9dd2b4b8d4..85fe419392 100644 --- a/Mage.Sets/src/mage/cards/t/TymnaTheWeaver.java +++ b/Mage.Sets/src/mage/cards/t/TymnaTheWeaver.java @@ -82,7 +82,7 @@ class TymnaTheWeaverEffect extends OneShotEffect { Cost cost = new PayLifeCost(cardsToDraw); if (cost.canPay(source, source.getSourceId(), source.getControllerId(), game) && cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { - controller.drawCards(cardsToDraw, game); + controller.drawCards(cardsToDraw, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/t/Tyrannize.java b/Mage.Sets/src/mage/cards/t/Tyrannize.java index c7055ae97a..90c56333a0 100644 --- a/Mage.Sets/src/mage/cards/t/Tyrannize.java +++ b/Mage.Sets/src/mage/cards/t/Tyrannize.java @@ -29,7 +29,6 @@ public final class Tyrannize extends CardImpl { // Target player discards their hand unless they pay 7 life. this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new TyrannizeEffect()); - } public Tyrannize(final Tyrannize card) { @@ -66,9 +65,7 @@ class TyrannizeEffect extends OneShotEffect { if (!cost.canPay(source, source.getSourceId(), player.getId(), game) || !player.chooseUse(Outcome.LoseLife, "Pay 7 life?", source, game) || !cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) { - for (Card card : player.getHand().getCards(game)) { - player.discard(card, source, game); - } + player.discard(player.getHand(),source,game); } return true; } diff --git a/Mage.Sets/src/mage/cards/t/TyrantsChoice.java b/Mage.Sets/src/mage/cards/t/TyrantsChoice.java index e18b11b7a2..94415038ab 100644 --- a/Mage.Sets/src/mage/cards/t/TyrantsChoice.java +++ b/Mage.Sets/src/mage/cards/t/TyrantsChoice.java @@ -9,7 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; @@ -71,7 +71,7 @@ class TyrantsChoiceEffect extends OneShotEffect { } } if (deathCount > tortureCount) { - new SacrificeOpponentsEffect(new FilterControlledCreaturePermanent("a creature")).apply(game, source); + new SacrificeOpponentsEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT).apply(game, source); } else { new TyrantsChoiceLoseLifeEffect().apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java b/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java index 880666f4e9..fa5a3d07f6 100644 --- a/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java +++ b/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java @@ -129,7 +129,7 @@ class UginTheSpiritDragonEffect3 extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { controller.gainLife(7, game, source); - controller.drawCards(7, game); + controller.drawCards(7, source.getSourceId(), game); TargetCardInHand target = new TargetCardInHand(0, 7, new FilterPermanentCard("permanent cards")); if (controller.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) { controller.moveCards(new CardsImpl(target.getTargets()), Zone.BATTLEFIELD, source, game); diff --git a/Mage.Sets/src/mage/cards/u/UginsInsight.java b/Mage.Sets/src/mage/cards/u/UginsInsight.java index d9ac5d10b6..cf9309d550 100644 --- a/Mage.Sets/src/mage/cards/u/UginsInsight.java +++ b/Mage.Sets/src/mage/cards/u/UginsInsight.java @@ -59,7 +59,7 @@ class UginsInsightEffect extends OneShotEffect { if (highCMC > 0) { controller.scry(highCMC, source, game); } - controller.drawCards(3, game); + controller.drawCards(3, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/u/UkkimaStalkingShadow.java b/Mage.Sets/src/mage/cards/u/UkkimaStalkingShadow.java new file mode 100644 index 0000000000..3f49706835 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UkkimaStalkingShadow.java @@ -0,0 +1,90 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CantBeBlockedSourceAbility; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UkkimaStalkingShadow extends CardImpl { + + public UkkimaStalkingShadow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.WHALE); + this.subtype.add(SubType.WOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Partner with Cazur, Ruthless Stalker + this.addAbility(new PartnerWithAbility("Cazur, Ruthless Stalker")); + + // Ukkima, Stalking Shadow can't be blocked. + this.addAbility(new CantBeBlockedSourceAbility()); + + // When Ukkima leaves the battlefield, it deals X damage to target player and you gain X life, where X is its power. + Ability ability = new LeavesBattlefieldTriggeredAbility(new UkkimaStalkingShadowEffect(), false); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + } + + private UkkimaStalkingShadow(final UkkimaStalkingShadow card) { + super(card); + } + + @Override + public UkkimaStalkingShadow copy() { + return new UkkimaStalkingShadow(this); + } +} + +class UkkimaStalkingShadowEffect extends OneShotEffect { + + UkkimaStalkingShadowEffect() { + super(Outcome.Benefit); + staticText = "it deals X damage to target player and you gain X life, where X is its power."; + } + + private UkkimaStalkingShadowEffect(final UkkimaStalkingShadowEffect effect) { + super(effect); + } + + @Override + public UkkimaStalkingShadowEffect copy() { + return new UkkimaStalkingShadowEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (permanent == null || permanent.getPower().getValue() <= 0) { + return false; + } + Player player = game.getPlayer(source.getFirstTarget()); + if (player != null) { + player.damage(permanent.getPower().getValue(), source.getSourceId(), game); + } + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + controller.gainLife(permanent.getPower().getValue(), game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/u/UmoriTheCollector.java b/Mage.Sets/src/mage/cards/u/UmoriTheCollector.java new file mode 100644 index 0000000000..a56cab1d0a --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UmoriTheCollector.java @@ -0,0 +1,86 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.ChooseCardTypeEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionAllOfChosenCardTypeEffect; +import mage.abilities.keyword.CompanionAbility; +import mage.abilities.keyword.CompanionCondition; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author emerald000 + */ +public final class UmoriTheCollector extends CardImpl { + + private static final FilterCard filter = new FilterCard("Spells you cast of the chosen type"); + + public UmoriTheCollector(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B/G}{B/G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.OOZE); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Companion — Each nonland card in your starting deck shares a card type. + this.addAbility(new CompanionAbility(UmoriCondition.instance)); + + // As Umori, the Collector enters the battlefield, choose a card type. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseCardTypeEffect(Outcome.Benefit))); + + // Spells you cast of the chosen type cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionAllOfChosenCardTypeEffect(filter, 1, true))); + } + + private UmoriTheCollector(final UmoriTheCollector card) { + super(card); + } + + @Override + public UmoriTheCollector copy() { + return new UmoriTheCollector(this); + } +} + +enum UmoriCondition implements CompanionCondition { + instance; + + @Override + public String getRule() { + return "Each nonland card in your starting deck shares a card type."; + } + + @Override + public boolean isLegal(Set deck, int startingSize) { + Set cardTypes = new HashSet<>(); + for (Card card : deck) { + // Lands are fine. + if (card.isLand()) { + continue; + } + // First nonland checked. + if (cardTypes.isEmpty()) { + cardTypes.addAll(card.getCardType()); + } else { + cardTypes.retainAll(card.getCardType()); + if (cardTypes.isEmpty()) { + return false; + } + } + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/u/UnbreakableBond.java b/Mage.Sets/src/mage/cards/u/UnbreakableBond.java new file mode 100644 index 0000000000..83db5b065b --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnbreakableBond.java @@ -0,0 +1,87 @@ +package mage.cards.u; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnbreakableBond extends CardImpl { + + public UnbreakableBond(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); + + // Return target creature card from your graveyard to the battlefield with a lifelink counter on it. + this.getSpellAbility().addEffect(new UnbreakableBondReplacementEffect()); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + this.getSpellAbility().addEffect(new InfoEffect("with a lifelink counter on it")); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + } + + private UnbreakableBond(final UnbreakableBond card) { + super(card); + } + + @Override + public UnbreakableBond copy() { + return new UnbreakableBond(this); + } +} + +class UnbreakableBondReplacementEffect extends ReplacementEffectImpl { + + UnbreakableBondReplacementEffect() { + super(Duration.EndOfStep, Outcome.BoostCreature); + } + + private UnbreakableBondReplacementEffect(UnbreakableBondReplacementEffect effect) { + super(effect); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getTargetId().equals(getTargetPointer().getFirst(game, source)); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + if (creature == null) { + return false; + } + creature.addCounters(CounterType.LIFELINK.createInstance(), source, game, event.getAppliedEffects()); + discard(); + return false; + } + + @Override + public UnbreakableBondReplacementEffect copy() { + return new UnbreakableBondReplacementEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/u/UnburialRites.java b/Mage.Sets/src/mage/cards/u/UnburialRites.java index 985323e848..fea1ab9eb2 100644 --- a/Mage.Sets/src/mage/cards/u/UnburialRites.java +++ b/Mage.Sets/src/mage/cards/u/UnburialRites.java @@ -21,7 +21,7 @@ public final class UnburialRites extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); // Return target creature card from your graveyard to the battlefield. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false)); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); // Flashback {3}{W} diff --git a/Mage.Sets/src/mage/cards/u/UncheckedGrowth.java b/Mage.Sets/src/mage/cards/u/UncheckedGrowth.java index bcac28c4e4..6ca3ce9e35 100644 --- a/Mage.Sets/src/mage/cards/u/UncheckedGrowth.java +++ b/Mage.Sets/src/mage/cards/u/UncheckedGrowth.java @@ -63,7 +63,7 @@ public final class UncheckedGrowth extends CardImpl { for (UUID permanentId : targetPointer.getTargets(game, source)) { Permanent permanent = game.getPermanent(permanentId); if (permanent != null && permanent.hasSubtype(SubType.SPIRIT, game)) { - permanent.addAbility(TrampleAbility.getInstance(), game); + permanent.addAbility(TrampleAbility.getInstance(), source.getSourceId(), game); affectedTargets++; } } diff --git a/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java b/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java index 2953587af9..37341042a4 100644 --- a/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java +++ b/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java @@ -24,13 +24,12 @@ import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * * @author BetaSteward */ public final class UndeadAlchemist extends CardImpl { public UndeadAlchemist(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(4); @@ -80,7 +79,9 @@ class UndeadAlchemistTriggeredAbility extends TriggeredAbilityImpl { if (zEvent.getFromZone() == Zone.LIBRARY && zEvent.getToZone() == Zone.GRAVEYARD && game.getOpponents(this.getControllerId()).contains(zEvent.getPlayerId())) { Card card = game.getCard(event.getTargetId()); if (card != null && card.isCreature()) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId())); + if (game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { + this.getEffects().get(0).setTargetPointer(new FixedTarget(card, game)); + } return true; } } @@ -97,10 +98,10 @@ class UndeadAlchemistEffect extends ReplacementEffectImpl { UndeadAlchemistEffect() { super(Duration.WhileOnBattlefield, Outcome.RedirectDamage); - staticText = "If a Zombie you control would deal combat damage to a player, instead that player puts that many cards from the top of their library into their graveyard"; + staticText = "If a Zombie you control would deal combat damage to a player, instead that player mills that many cards"; } - UndeadAlchemistEffect(final UndeadAlchemistEffect effect) { + private UndeadAlchemistEffect(final UndeadAlchemistEffect effect) { super(effect); } @@ -108,7 +109,8 @@ class UndeadAlchemistEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getTargetId()); if (player != null) { - return player.moveCards(player.getLibrary().getTopCards(game, event.getAmount()), Zone.GRAVEYARD, source, game); + player.millCards(event.getAmount(), source, game); + return true; } return true; } diff --git a/Mage.Sets/src/mage/cards/u/UndeadExecutioner.java b/Mage.Sets/src/mage/cards/u/UndeadExecutioner.java index 51f086068f..681b724702 100644 --- a/Mage.Sets/src/mage/cards/u/UndeadExecutioner.java +++ b/Mage.Sets/src/mage/cards/u/UndeadExecutioner.java @@ -4,7 +4,7 @@ package mage.cards.u; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -28,7 +28,7 @@ public final class UndeadExecutioner extends CardImpl { this.toughness = new MageInt(2); // When Undead Executioner dies, you may have target creature get -2/-2 until end of turn. - Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), true); + Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/u/UndeadWarchief.java b/Mage.Sets/src/mage/cards/u/UndeadWarchief.java index c8a10d9f5c..6725f512d4 100644 --- a/Mage.Sets/src/mage/cards/u/UndeadWarchief.java +++ b/Mage.Sets/src/mage/cards/u/UndeadWarchief.java @@ -1,7 +1,5 @@ - package mage.cards.u; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; @@ -15,8 +13,9 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class UndeadWarchief extends CardImpl { @@ -30,7 +29,7 @@ public final class UndeadWarchief extends CardImpl { } public UndeadWarchief(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(1); @@ -38,6 +37,7 @@ public final class UndeadWarchief extends CardImpl { // Zombie spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); + // Zombie creatures you control get +2/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(2, 1, Duration.WhileOnBattlefield, filterCreatures, false))); } diff --git a/Mage.Sets/src/mage/cards/u/UndercityUprising.java b/Mage.Sets/src/mage/cards/u/UndercityUprising.java index c24d0304fb..403f06dea3 100644 --- a/Mage.Sets/src/mage/cards/u/UndercityUprising.java +++ b/Mage.Sets/src/mage/cards/u/UndercityUprising.java @@ -1,6 +1,5 @@ package mage.cards.u; -import java.util.UUID; import mage.abilities.effects.common.FightTargetsEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.DeathtouchAbility; @@ -8,25 +7,17 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class UndercityUprising extends CardImpl { - private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public UndercityUprising(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{G}"); @@ -38,10 +29,10 @@ public final class UndercityUprising extends CardImpl { this.getSpellAbility().addEffect(new FightTargetsEffect() .setText("Then target creature you control fights target creature you don't control")); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } - public UndercityUprising(final UndercityUprising card) { + private UndercityUprising(final UndercityUprising card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java b/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java index 85bed3bdbc..e648342fc3 100644 --- a/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java +++ b/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java @@ -4,7 +4,7 @@ package mage.cards.u; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CantBeTargetedCardsGraveyardsEffect; import mage.abilities.effects.common.ExileSourceEffect; @@ -25,7 +25,7 @@ public final class UnderworldCerberus extends CardImpl { public UnderworldCerberus(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(6); this.toughness = new MageInt(6); @@ -37,7 +37,7 @@ public final class UnderworldCerberus extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeTargetedCardsGraveyardsEffect())); // When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand. - Ability ability = new DiesTriggeredAbility(new ExileSourceEffect()); + Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect()); ability.addEffect(new ReturnToHandFromGraveyardAllEffect(new FilterCreatureCard("creature cards"))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/u/UnderworldRageHound.java b/Mage.Sets/src/mage/cards/u/UnderworldRageHound.java index ee3ee87feb..0040b79403 100644 --- a/Mage.Sets/src/mage/cards/u/UnderworldRageHound.java +++ b/Mage.Sets/src/mage/cards/u/UnderworldRageHound.java @@ -20,7 +20,7 @@ public final class UnderworldRageHound extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.ELEMENTAL); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(3); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java b/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java index 1ad813bb1a..40db7ee597 100644 --- a/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java +++ b/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java @@ -3,7 +3,7 @@ package mage.cards.u; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.cards.CardImpl; @@ -40,7 +40,7 @@ public final class UnderworldSentinel extends CardImpl { this.addAbility(ability); // When Underworld Sentinel dies, put all cards exiled with it onto the battlefield. - this.addAbility(new DiesTriggeredAbility(new UnderworldSentinelEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new UnderworldSentinelEffect())); } private UnderworldSentinel(final UnderworldSentinel card) { diff --git a/Mage.Sets/src/mage/cards/u/UndyingBeast.java b/Mage.Sets/src/mage/cards/u/UndyingBeast.java index 20bf25c5a6..b9116c7a44 100644 --- a/Mage.Sets/src/mage/cards/u/UndyingBeast.java +++ b/Mage.Sets/src/mage/cards/u/UndyingBeast.java @@ -1,19 +1,19 @@ - package mage.cards.u; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; /** @@ -23,13 +23,13 @@ import mage.players.Player; public final class UndyingBeast extends CardImpl { public UndyingBeast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.BEAST); this.power = new MageInt(3); this.toughness = new MageInt(2); // When Undying Beast dies, put it on top of its owner's library. - this.addAbility(new DiesTriggeredAbility(new UndyingBeastEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new UndyingBeastEffect())); } public UndyingBeast(final UndyingBeast card) { @@ -62,11 +62,17 @@ class UndyingBeastEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getSourceId()); if (card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { - Player owner = game.getPlayer(card.getOwnerId()); - if(owner != null) { - owner.getGraveyard().remove(card); - return card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + Object object = this.getValue("permanentLeftBattlefield"); + if (object instanceof Permanent) { + Permanent permanent = (Permanent) object; + if (permanent.getZoneChangeCounter(game) + 1 == card.getZoneChangeCounter(game)) { + Player owner = game.getPlayer(card.getOwnerId()); + if (owner != null) { + return owner.putCardsOnTopOfLibrary(card, game, source, true); + } + } } + } return true; } diff --git a/Mage.Sets/src/mage/cards/u/UndyingFlames.java b/Mage.Sets/src/mage/cards/u/UndyingFlames.java index acbfe4a566..9f61def4f2 100644 --- a/Mage.Sets/src/mage/cards/u/UndyingFlames.java +++ b/Mage.Sets/src/mage/cards/u/UndyingFlames.java @@ -1,6 +1,5 @@ package mage.cards.u; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -15,10 +14,10 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author jeffwadsworth - * */ public final class UndyingFlames extends CardImpl { @@ -60,7 +59,7 @@ class UndyingFlamesEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - while (controller.getLibrary().hasCards() && controller.isInGame()) { + while (controller.canRespond() && controller.getLibrary().hasCards()) { Card card = controller.getLibrary().getFromTop(game); if (card != null) { controller.moveCards(card, Zone.EXILED, source, game); diff --git a/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java b/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java index a3539f9a0a..618c2ea19e 100644 --- a/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java +++ b/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java @@ -1,44 +1,35 @@ - package mage.cards.u; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetCard; +import mage.target.common.TargetOpponent; + import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; -import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; -import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.TargetCard; /** - * * @author spjspj */ public final class UneshCriosphinxSovereign extends CardImpl { private static final FilterCard filter = new FilterCard("Sphinx spells"); + private static final FilterPermanent filter2 = new FilterPermanent(SubType.SPHINX, "Sphinx"); static { filter.add(SubType.SPHINX.getPredicate()); @@ -56,13 +47,15 @@ public final class UneshCriosphinxSovereign extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Sphinx spells you cast cost {2} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 2))); + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 2))); // Whenever Unesh, Criosphinx Sovereign or another Sphinx enters the battlefield under your control, reveal the top four cards of your library. An opponent seperates those cards into two piles. Put one pile into your hand and the other into your graveyard. - this.addAbility(new UneshCriosphinxSovereignTriggeredAbility()); + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new UneshCriosphinxSovereignEffect(), filter2, false, true + )); } - public UneshCriosphinxSovereign(final UneshCriosphinxSovereign card) { + private UneshCriosphinxSovereign(final UneshCriosphinxSovereign card) { super(card); } @@ -72,58 +65,14 @@ public final class UneshCriosphinxSovereign extends CardImpl { } } -class UneshCriosphinxSovereignTriggeredAbility extends TriggeredAbilityImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(SubType.SPHINX.getPredicate()); - } - - public UneshCriosphinxSovereignTriggeredAbility() { - super(Zone.BATTLEFIELD, new UneshCriosphinxSovereignEffect(), false); - } - - public UneshCriosphinxSovereignTriggeredAbility(UneshCriosphinxSovereignTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null - && permanent.isOwnedBy(controllerId) - && permanent.isCreature() - && (event.getTargetId().equals(getSourceId()) || filter.match(permanent, game))) { - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} or another Sphinx enters the battlefield under your control, " + super.getRule(); - } - - @Override - public UneshCriosphinxSovereignTriggeredAbility copy() { - return new UneshCriosphinxSovereignTriggeredAbility(this); - } -} - class UneshCriosphinxSovereignEffect extends OneShotEffect { - public UneshCriosphinxSovereignEffect() { + UneshCriosphinxSovereignEffect() { super(Outcome.DrawCard); this.staticText = "reveal the top four cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard"; } - public UneshCriosphinxSovereignEffect(final UneshCriosphinxSovereignEffect effect) { + private UneshCriosphinxSovereignEffect(final UneshCriosphinxSovereignEffect effect) { super(effect); } @@ -146,9 +95,16 @@ class UneshCriosphinxSovereignEffect extends OneShotEffect { Set opponents = game.getOpponents(source.getControllerId()); if (!opponents.isEmpty()) { Player opponent = game.getPlayer(opponents.iterator().next()); + if (opponents.size() > 1) { + Target targetOpponent = new TargetOpponent(true); + if (controller.chooseTarget(Outcome.Neutral, targetOpponent, source, game)) { + opponent = game.getPlayer(targetOpponent.getFirstTarget()); + game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " to separate the revealed cards"); + } + } TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile")); List pile1 = new ArrayList<>(); - if (opponent != null && opponent.choose(Outcome.Neutral, cards, target, game)) { + if (opponent.choose(Outcome.Neutral, cards, target, game)) { List targets = target.getTargets(); for (UUID targetId : targets) { Card card = cards.get(targetId, game); diff --git a/Mage.Sets/src/mage/cards/u/UnexpectedFangs.java b/Mage.Sets/src/mage/cards/u/UnexpectedFangs.java new file mode 100644 index 0000000000..0da1fe467b --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnexpectedFangs.java @@ -0,0 +1,36 @@ +package mage.cards.u; + +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnexpectedFangs extends CardImpl { + + public UnexpectedFangs(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Put a +1/+1 counter and a lifelink counter on target creature. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter")); + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.LIFELINK.createInstance()) + .setText("and a lifelink counter on target creature")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private UnexpectedFangs(final UnexpectedFangs card) { + super(card); + } + + @Override + public UnexpectedFangs copy() { + return new UnexpectedFangs(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/Unforge.java b/Mage.Sets/src/mage/cards/u/Unforge.java index f1f777176b..190f6de98d 100644 --- a/Mage.Sets/src/mage/cards/u/Unforge.java +++ b/Mage.Sets/src/mage/cards/u/Unforge.java @@ -61,7 +61,7 @@ class UnforgeEffect extends OneShotEffect{ @Override public boolean apply(Game game, Ability source) { - Permanent equipment = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent equipment = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if(equipment != null){ Permanent creature = game.getPermanent(equipment.getAttachedTo()); if(creature != null){ diff --git a/Mage.Sets/src/mage/cards/u/UnholyCitadel.java b/Mage.Sets/src/mage/cards/u/UnholyCitadel.java index 7b645a52a6..6acc2a6235 100644 --- a/Mage.Sets/src/mage/cards/u/UnholyCitadel.java +++ b/Mage.Sets/src/mage/cards/u/UnholyCitadel.java @@ -1,7 +1,5 @@ - package mage.cards.u; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -15,8 +13,9 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author L_J */ public final class UnholyCitadel extends CardImpl { @@ -32,7 +31,10 @@ public final class UnholyCitadel extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Black legendary creatures you control have "bands with other legendary creatures." - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(new BandsWithOtherAbility(SuperType.LEGENDARY), Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect( + new BandsWithOtherAbility(SuperType.LEGENDARY), Duration.WhileOnBattlefield, filter) + .withForceQuotes() + )); } public UnholyCitadel(final UnholyCitadel card) { diff --git a/Mage.Sets/src/mage/cards/u/UnifyingTheory.java b/Mage.Sets/src/mage/cards/u/UnifyingTheory.java index 365f47ad42..653142390e 100644 --- a/Mage.Sets/src/mage/cards/u/UnifyingTheory.java +++ b/Mage.Sets/src/mage/cards/u/UnifyingTheory.java @@ -64,7 +64,7 @@ class UnifyingTheoryEffect extends OneShotEffect { if (caster.chooseUse(Outcome.DrawCard, "Pay {2} to draw a card?", source, game)) { Cost cost = new ManaCostsImpl("{2}"); if (cost.pay(source, game, source.getSourceId(), caster.getId(), false, null)) { - caster.drawCards(1, game); + caster.drawCards(1, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/u/UnleashFury.java b/Mage.Sets/src/mage/cards/u/UnleashFury.java new file mode 100644 index 0000000000..564a6ccfe9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnleashFury.java @@ -0,0 +1,67 @@ +package mage.cards.u; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnleashFury extends CardImpl { + + public UnleashFury(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Double the power of target creature until end of turn. + this.getSpellAbility().addEffect(new UnleashFuryEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private UnleashFury(final UnleashFury card) { + super(card); + } + + @Override + public UnleashFury copy() { + return new UnleashFury(this); + } +} + +class UnleashFuryEffect extends OneShotEffect { + + UnleashFuryEffect() { + super(Outcome.Benefit); + staticText = "double the power of target creature until end of turn"; + } + + private UnleashFuryEffect(final UnleashFuryEffect effect) { + super(effect); + } + + @Override + public UnleashFuryEffect copy() { + return new UnleashFuryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + game.addEffect(new BoostTargetEffect( + permanent.getPower().getValue(), 0, Duration.EndOfTurn + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java b/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java index 5d05edd564..712139157a 100644 --- a/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java +++ b/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java @@ -27,8 +27,8 @@ public final class UnlicensedDisintegration extends CardImpl { this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new DamageTargetControllerEffect(3), new PermanentsOnTheBattlefieldCondition(new FilterControlledArtifactPermanent()), - "If you control an artifact, Unlicensed Disintegration deals 3 damage to that creature's controller")); - + "If you control an artifact, {this} deals 3 damage to that creature's controller")); + } public UnlicensedDisintegration(final UnlicensedDisintegration card) { diff --git a/Mage.Sets/src/mage/cards/u/UnmooredEgo.java b/Mage.Sets/src/mage/cards/u/UnmooredEgo.java index 8b09521818..d502014e6f 100644 --- a/Mage.Sets/src/mage/cards/u/UnmooredEgo.java +++ b/Mage.Sets/src/mage/cards/u/UnmooredEgo.java @@ -1,124 +1,124 @@ -package mage.cards.u; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ChooseACardNameEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.NamePredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; -import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetOpponent; - -/** - * - * @author LevelX2 - */ -public final class UnmooredEgo extends CardImpl { - - public UnmooredEgo(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}{B}"); - - // Choose a card name. Search target opponent's graveyard, hand, and library for up to four cards with that name and exile them. That player shuffles their library, then draws a card for each card exiled from their hand this way. - this.getSpellAbility().addEffect((new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.ALL))); - this.getSpellAbility().addEffect(new UnmooredEgoEffect()); - this.getSpellAbility().addTarget(new TargetOpponent()); - } - - public UnmooredEgo(final UnmooredEgo card) { - super(card); - } - - @Override - public UnmooredEgo copy() { - return new UnmooredEgo(this); - } -} - -class UnmooredEgoEffect extends OneShotEffect { - - public UnmooredEgoEffect() { - super(Outcome.Benefit); - this.staticText = "Search target opponent's graveyard, hand, and library for up to four cards with that name and exile them. That player shuffles their library, then draws a card for each card exiled from their hand this way"; - } - - public UnmooredEgoEffect(final UnmooredEgoEffect effect) { - super(effect); - } - - @Override - public UnmooredEgoEffect copy() { - return new UnmooredEgoEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - Player controller = game.getPlayer(source.getControllerId()); - if (cardName != null && controller != null) { - int numberOfCardsStillToRemove = 4; - int numberOfCardsExiledFromHand = 0; - Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (targetPlayer != null) { - FilterCard filter = new FilterCard("card named " + cardName); - filter.add(new NamePredicate(cardName)); - - // cards in Graveyard - int cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getGraveyard().count(filter, game)); - if (cardsCount > 0) { - filter.setMessage("card named " + cardName + " in the graveyard of " + targetPlayer.getName()); - TargetCard target = new TargetCard(Math.min(cardsCount, numberOfCardsStillToRemove), - Math.min(cardsCount, numberOfCardsStillToRemove), Zone.GRAVEYARD, filter); - if (controller.choose(Outcome.Exile, targetPlayer.getGraveyard(), target, game)) { - numberOfCardsStillToRemove -= target.getTargets().size(); - controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); - } - } - - // cards in Hand - if (numberOfCardsStillToRemove > 0) { - cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getHand().count(filter, game)); - filter.setMessage("card named " + cardName + " in the hand of " + targetPlayer.getName()); - TargetCard target = new TargetCard(0, Math.min(cardsCount, numberOfCardsStillToRemove), Zone.HAND, filter); - if (controller.choose(Outcome.Exile, targetPlayer.getHand(), target, game)) { - numberOfCardsExiledFromHand = target.getTargets().size(); - numberOfCardsStillToRemove -= target.getTargets().size(); - controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); - } - } - - // cards in Library - if (numberOfCardsStillToRemove > 0) { - Cards cardsInLibrary = new CardsImpl(); - cardsInLibrary.addAll(targetPlayer.getLibrary().getCards(game)); - cardsCount = (cardName.isEmpty() ? 0 : cardsInLibrary.count(filter, game)); - filter.setMessage("card named " + cardName + " in the library of " + targetPlayer.getLogName()); - TargetCardInLibrary targetLib = new TargetCardInLibrary(0, Math.min(cardsCount, numberOfCardsStillToRemove), filter); - if (controller.choose(Outcome.Exile, cardsInLibrary, targetLib, game)) { - controller.moveCards(new CardsImpl(targetLib.getTargets()), Zone.EXILED, source, game); - } - } - targetPlayer.shuffleLibrary(source, game); - - if (numberOfCardsExiledFromHand > 0) { - game.getState().applyEffects(game); - targetPlayer.drawCards(numberOfCardsExiledFromHand, game); - } - } - - return true; - } - - return false; - } - -} +package mage.cards.u; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseACardNameEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetOpponent; + +/** + * + * @author LevelX2 + */ +public final class UnmooredEgo extends CardImpl { + + public UnmooredEgo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}{B}"); + + // Choose a card name. Search target opponent's graveyard, hand, and library for up to four cards with that name and exile them. That player shuffles their library, then draws a card for each card exiled from their hand this way. + this.getSpellAbility().addEffect((new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.ALL))); + this.getSpellAbility().addEffect(new UnmooredEgoEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + public UnmooredEgo(final UnmooredEgo card) { + super(card); + } + + @Override + public UnmooredEgo copy() { + return new UnmooredEgo(this); + } +} + +class UnmooredEgoEffect extends OneShotEffect { + + public UnmooredEgoEffect() { + super(Outcome.Benefit); + this.staticText = "Search target opponent's graveyard, hand, and library for up to four cards with that name and exile them. That player shuffles their library, then draws a card for each card exiled from their hand this way"; + } + + public UnmooredEgoEffect(final UnmooredEgoEffect effect) { + super(effect); + } + + @Override + public UnmooredEgoEffect copy() { + return new UnmooredEgoEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + Player controller = game.getPlayer(source.getControllerId()); + if (cardName != null && controller != null) { + int numberOfCardsStillToRemove = 4; + int numberOfCardsExiledFromHand = 0; + Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (targetPlayer != null) { + FilterCard filter = new FilterCard("card named " + cardName); + filter.add(new NamePredicate(cardName)); + + // cards in Graveyard + int cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getGraveyard().count(filter, game)); + if (cardsCount > 0) { + filter.setMessage("card named " + cardName + " in the graveyard of " + targetPlayer.getName()); + TargetCard target = new TargetCard(Math.min(cardsCount, numberOfCardsStillToRemove), + Math.min(cardsCount, numberOfCardsStillToRemove), Zone.GRAVEYARD, filter); + if (controller.choose(Outcome.Exile, targetPlayer.getGraveyard(), target, game)) { + numberOfCardsStillToRemove -= target.getTargets().size(); + controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); + } + } + + // cards in Hand + if (numberOfCardsStillToRemove > 0) { + cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getHand().count(filter, game)); + filter.setMessage("card named " + cardName + " in the hand of " + targetPlayer.getName()); + TargetCard target = new TargetCard(0, Math.min(cardsCount, numberOfCardsStillToRemove), Zone.HAND, filter); + if (controller.choose(Outcome.Exile, targetPlayer.getHand(), target, game)) { + numberOfCardsExiledFromHand = target.getTargets().size(); + numberOfCardsStillToRemove -= target.getTargets().size(); + controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); + } + } + + // cards in Library + if (numberOfCardsStillToRemove > 0) { + Cards cardsInLibrary = new CardsImpl(); + cardsInLibrary.addAll(targetPlayer.getLibrary().getCards(game)); + cardsCount = (cardName.isEmpty() ? 0 : cardsInLibrary.count(filter, game)); + filter.setMessage("card named " + cardName + " in the library of " + targetPlayer.getLogName()); + TargetCardInLibrary targetLib = new TargetCardInLibrary(0, Math.min(cardsCount, numberOfCardsStillToRemove), filter); + if (controller.choose(Outcome.Exile, cardsInLibrary, targetLib, game)) { + controller.moveCards(new CardsImpl(targetLib.getTargets()), Zone.EXILED, source, game); + } + } + targetPlayer.shuffleLibrary(source, game); + + if (numberOfCardsExiledFromHand > 0) { + game.getState().applyEffects(game); + targetPlayer.drawCards(numberOfCardsExiledFromHand, source.getSourceId(), game); + } + } + + return true; + } + + return false; + } + +} diff --git a/Mage.Sets/src/mage/cards/u/UnpredictableCyclone.java b/Mage.Sets/src/mage/cards/u/UnpredictableCyclone.java new file mode 100644 index 0000000000..c6cb82b273 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnpredictableCyclone.java @@ -0,0 +1,124 @@ +package mage.cards.u; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.StackObject; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnpredictableCyclone extends CardImpl { + + public UnpredictableCyclone(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}"); + + // If a cycling ability of another nonland card would cause you to draw a card, instead exile cards from the top of your library until you exile a card that shares a card type with the cycled card. You may cast that card without paying its mana cost. Then put the exiled cards that weren't cast this way on the bottom of your library in a random order. + this.addAbility(new SimpleStaticAbility(new UnpredictableCycloneReplacementEffect())); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private UnpredictableCyclone(final UnpredictableCyclone card) { + super(card); + } + + @Override + public UnpredictableCyclone copy() { + return new UnpredictableCyclone(this); + } +} + +class UnpredictableCycloneReplacementEffect extends ReplacementEffectImpl { + + UnpredictableCycloneReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If a cycling ability of another nonland card would cause you to draw a card, " + + "instead exile cards from the top of your library until you exile a card " + + "that shares a card type with the cycled card. You may cast that card without paying its mana cost. " + + "Then put the exiled cards that weren't cast this way on the bottom of your library in a random order."; + } + + private UnpredictableCycloneReplacementEffect(final UnpredictableCycloneReplacementEffect effect) { + super(effect); + } + + @Override + public UnpredictableCycloneReplacementEffect copy() { + return new UnpredictableCycloneReplacementEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(event.getPlayerId()); + StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); + if (player == null || stackObject == null) { + return false; + } + Card sourceCard = game.getCard(stackObject.getSourceId()); + if (sourceCard == null) { + return false; + } + Cards cards = new CardsImpl(); + Card toCast = null; + for (Card card : player.getLibrary().getCards(game)) { + cards.add(card); + if (card.getCardType().stream().anyMatch(sourceCard.getCardType()::contains)) { + toCast = card; + break; + } + } + player.moveCards(cards, Zone.EXILED, source, game); + if (toCast != null && player.chooseUse(outcome, "Cast the exiled card?", source, game)) { + game.getState().setValue("PlayFromNotOwnHandZone" + toCast.getId(), Boolean.TRUE); + Boolean cardWasCast = player.cast(player.chooseAbilityForCast(toCast, game, true), + game, true, new MageObjectReference(source.getSourceObject(game), game)); + game.getState().setValue("PlayFromNotOwnHandZone" + toCast.getId(), null); + if (cardWasCast) { + cards.remove(toCast); + } + } + player.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DRAW_CARD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!event.getPlayerId().equals(source.getControllerId())) { + return false; + } + Player player = game.getPlayer(event.getPlayerId()); + StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); + if (player == null || stackObject == null + || stackObject.getStackAbility() == null + || !(stackObject.getStackAbility() instanceof CyclingAbility)) { + return false; + } + Card sourceCard = game.getCard(stackObject.getSourceId()); + return sourceCard != null && !sourceCard.isLand(); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UnravelTheAether.java b/Mage.Sets/src/mage/cards/u/UnravelTheAether.java index b508910aac..88cc750b08 100644 --- a/Mage.Sets/src/mage/cards/u/UnravelTheAether.java +++ b/Mage.Sets/src/mage/cards/u/UnravelTheAether.java @@ -1,6 +1,6 @@ - package mage.cards.u; +import java.util.UUID; import mage.abilities.effects.common.ShuffleIntoLibraryTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -8,8 +8,6 @@ import mage.constants.CardType; import mage.filter.StaticFilters; import mage.target.TargetPermanent; -import java.util.UUID; - /** * * @author LevelX2 @@ -17,7 +15,7 @@ import java.util.UUID; public final class UnravelTheAether extends CardImpl { public UnravelTheAether(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Choose target artifact or enchantment. Its owner shuffles it into their library. this.getSpellAbility().addEffect(new ShuffleIntoLibraryTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java b/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java index d91b45043f..04e3696a36 100644 --- a/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java +++ b/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java @@ -73,11 +73,11 @@ class UnstableShapeshifterEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); - Permanent targetCreature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent targetCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (targetCreature != null && permanent != null) { Permanent blueprintPermanent = game.copyPermanent(Duration.Custom, targetCreature, permanent.getId(), source, new EmptyApplyToPermanent()); blueprintPermanent.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, - new UnstableShapeshifterEffect(), filterAnotherCreature, false, SetTargetPointer.PERMANENT, ""), game); + new UnstableShapeshifterEffect(), filterAnotherCreature, false, SetTargetPointer.PERMANENT, ""), source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/u/UrborgPanther.java b/Mage.Sets/src/mage/cards/u/UrborgPanther.java index 657d34d585..fc0cae6945 100644 --- a/Mage.Sets/src/mage/cards/u/UrborgPanther.java +++ b/Mage.Sets/src/mage/cards/u/UrborgPanther.java @@ -1,81 +1,81 @@ -package mage.cards.u; - -import java.util.UUID; - -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.costs.mana.ColoredManaCost; -import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ColoredManaSymbol; -import mage.constants.SubType; -import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreatureCard; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.permanent.BlockingAttackerIdPredicate; -import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.common.TargetCreaturePermanent; - -/** - * - * @author Ketsuban - */ -public class UrborgPanther extends CardImpl { - - private static final FilterControlledCreaturePermanent filter1 = new FilterControlledCreaturePermanent("creature named Feral Shadow"); - private static final FilterControlledCreaturePermanent filter2 = new FilterControlledCreaturePermanent("creature named Breathstealer"); - - private static final FilterCard filterCard = new FilterCreatureCard("card named Spirit of the Night"); - - static { - filter1.add(new NamePredicate("Feral Shadow")); - filter2.add(new NamePredicate("Breathstealer")); - filterCard.add(new NamePredicate("Spirit of the Night")); - } - - public UrborgPanther(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{2}{B}"); - this.subtype.add(SubType.NIGHTSTALKER); - this.subtype.add(SubType.CAT); - this.power = new MageInt(2); - this.toughness = new MageInt(2); - - // B, Sacrifice Urborg Panther: Destroy target creature blocking Urborg Panther. - Ability ability1 = new SimpleActivatedAbility(new DestroyTargetEffect(), - new ColoredManaCost(ColoredManaSymbol.B)); - ability1.addCost(new SacrificeSourceCost()); - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature blocking {this}"); - filter.add(new BlockingAttackerIdPredicate(this.getId())); - ability1.addTarget(new TargetCreaturePermanent(filter)); - this.addAbility(ability1); - - // Sacrifice a creature named Feral Shadow, a creature named Breathstealer, and - // Urborg Panther: Search your library for a card named Spirit of the Night and - // put that card onto the battlefield. Then shuffle your library. - Ability ability2 = new SimpleActivatedAbility( - new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(1, 1, new FilterCard(filterCard))), - new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter1, true))); - ability2.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter2, true))); - ability2.addCost(new SacrificeSourceCost()); - this.addAbility(ability2); - } - - public UrborgPanther(final UrborgPanther card) { - super(card); - } - - @Override - public UrborgPanther copy() { - return new UrborgPanther(this); - } - -} +package mage.cards.u; + +import java.util.UUID; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.predicate.permanent.BlockingAttackerIdPredicate; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Ketsuban + */ +public class UrborgPanther extends CardImpl { + + private static final FilterControlledCreaturePermanent filter1 = new FilterControlledCreaturePermanent("creature named Feral Shadow"); + private static final FilterControlledCreaturePermanent filter2 = new FilterControlledCreaturePermanent("creature named Breathstealer"); + + private static final FilterCard filterCard = new FilterCreatureCard("card named Spirit of the Night"); + + static { + filter1.add(new NamePredicate("Feral Shadow")); + filter2.add(new NamePredicate("Breathstealer")); + filterCard.add(new NamePredicate("Spirit of the Night")); + } + + public UrborgPanther(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{2}{B}"); + this.subtype.add(SubType.NIGHTSTALKER); + this.subtype.add(SubType.CAT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // B, Sacrifice Urborg Panther: Destroy target creature blocking Urborg Panther. + Ability ability1 = new SimpleActivatedAbility(new DestroyTargetEffect(), + new ColoredManaCost(ColoredManaSymbol.B)); + ability1.addCost(new SacrificeSourceCost()); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature blocking {this}"); + filter.add(new BlockingAttackerIdPredicate(this.getId())); + ability1.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability1); + + // Sacrifice a creature named Feral Shadow, a creature named Breathstealer, and + // Urborg Panther: Search your library for a card named Spirit of the Night and + // put that card onto the battlefield. Then shuffle your library. + Ability ability2 = new SimpleActivatedAbility( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(1, 1, new FilterCard(filterCard))), + new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter1, true))); + ability2.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter2, true))); + ability2.addCost(new SacrificeSourceCost()); + this.addAbility(ability2); + } + + public UrborgPanther(final UrborgPanther card) { + super(card); + } + + @Override + public UrborgPanther copy() { + return new UrborgPanther(this); + } + +} diff --git a/Mage.Sets/src/mage/cards/u/UrgorosTheEmptyOne.java b/Mage.Sets/src/mage/cards/u/UrgorosTheEmptyOne.java index daf81034bb..bd4571d359 100644 --- a/Mage.Sets/src/mage/cards/u/UrgorosTheEmptyOne.java +++ b/Mage.Sets/src/mage/cards/u/UrgorosTheEmptyOne.java @@ -69,7 +69,7 @@ class UrgorosTheEmptyOneEffect extends OneShotEffect { Player attackedPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); if (controller != null && attackedPlayer != null) { if (attackedPlayer.getHand().isEmpty()) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } else { attackedPlayer.discardOne(true, source, game); } diff --git a/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java b/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java index 7119d1df9f..137e2b4653 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java +++ b/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java @@ -268,7 +268,7 @@ class UrzaAcademyHeadmasterRandomEffect extends OneShotEffect { effects.add(new UrzaAcademyHeadmasterBrainstormEffect()); break; case 8: // JACE MEMORY ADEPT 2 - sb.append("Target player puts the top ten cards of their library into their graveyard."); + sb.append("Target player mills ten cards."); effects.add(new PutLibraryIntoGraveTargetEffect(10)); target = new TargetPlayer(); break; @@ -550,7 +550,7 @@ class UrzaAcademyHeadmasterBrainstormEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); putOnLibrary(player, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java b/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java index be880a8f95..0599b62e1e 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java +++ b/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java @@ -1,16 +1,19 @@ package mage.cards.u; +import java.util.ArrayList; +import java.util.List; import mage.MageInt; +import mage.MageObject; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapTargetCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.abilities.mana.SimpleManaAbility; import mage.cards.Card; import mage.cards.CardImpl; @@ -24,23 +27,16 @@ import mage.game.Game; import mage.game.permanent.token.KarnConstructToken; import mage.players.Player; import mage.target.common.TargetControlledPermanent; -import mage.target.targetpointer.FixedTargets; -import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.filter.FilterPermanent; /** * @author TheElk801 */ public final class UrzaLordHighArtificer extends CardImpl { - private static final FilterControlledPermanent filter - = new FilterControlledArtifactPermanent("untapped artifact you control"); - - static { - filter.add(Predicates.not(TappedPredicate.instance)); - } - public UrzaLordHighArtificer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); @@ -51,12 +47,17 @@ public final class UrzaLordHighArtificer extends CardImpl { this.toughness = new MageInt(4); // When Urza, Lord High Artificer enters the battlefield, create a 0/0 colorless Construct artifact creature token with "This creature gets +1/+1 for each artifact you control." - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KarnConstructToken()))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KarnConstructToken())) + .addHint(ArtifactYouControlHint.instance) + ); // Tap an untapped artifact you control: Add {U}. + FilterControlledPermanent filter = new FilterControlledArtifactPermanent("untapped artifact you control"); + filter.add(Predicates.not(TappedPredicate.instance)); this.addAbility(new SimpleManaAbility( - Zone.BATTLEFIELD, Mana.BlueMana(1), new TapTargetCost(new TargetControlledPermanent(filter)) - )); + Zone.BATTLEFIELD, + new UrzaLordHighArtificerManaEffect(filter), + new TapTargetCost(new TargetControlledPermanent(filter)))); // {5}: Shuffle your library, then exile the top card. Until end of turn, you may play that card without paying its mana cost. this.addAbility(new SimpleActivatedAbility(new UrzaLordHighArtificerEffect(), new GenericManaCost(5))); @@ -92,68 +93,47 @@ class UrzaLordHighArtificerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { return false; } controller.shuffleLibrary(source, game); Card card = controller.getLibrary().getFromTop(game); - if (card == null) { - return false; - } - UUID exileId = CardUtil.getExileZoneId( - controller.getId().toString() - + "-" + game.getState().getTurnNum() - + "-" + UrzaLordHighArtificer.class.toString(), game - ); - String exileName = "Urza, Lord High Artificer free cast on " - + game.getState().getTurnNum() + " turn for " + controller.getName(); - game.getExile().createZone(exileId, exileName).setCleanupOnEndTurn(true); - if (!controller.moveCardsToExile(card, source, game, true, exileId, exileName)) { - return false; - } - ContinuousEffect effect = new UrzaLordHighArtificerFromExileEffect(); - effect.setTargetPointer(new FixedTargets(game.getExile().getExileZone(exileId).getCards(game), game)); - game.addEffect(effect, source); - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, card, + TargetController.YOU, Duration.EndOfTurn, true); } } -class UrzaLordHighArtificerFromExileEffect extends AsThoughEffectImpl { +class UrzaLordHighArtificerManaEffect extends BasicManaEffect { - UrzaLordHighArtificerFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "you may play that card without paying its mana cost"; + private final FilterPermanent filter; + + public UrzaLordHighArtificerManaEffect(FilterPermanent filter) { + super(Mana.BlueMana(1)); + this.filter = filter; } - private UrzaLordHighArtificerFromExileEffect(final UrzaLordHighArtificerFromExileEffect effect) { + public UrzaLordHighArtificerManaEffect(final UrzaLordHighArtificerManaEffect effect) { super(effect); + this.filter = effect.filter.copy(); } @Override - public boolean apply(Game game, Ability source) { - return true; + public UrzaLordHighArtificerManaEffect copy() { + return new UrzaLordHighArtificerManaEffect(this); } @Override - public UrzaLordHighArtificerFromExileEffect copy() { - return new UrzaLordHighArtificerFromExileEffect(this); + public List getNetMana(Game game, Ability source) { + if (game != null && game.inCheckPlayableState()) { + int count = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + List netMana = new ArrayList<>(); + if (count > 0) { + netMana.add(Mana.BlueMana(count)); + } + return netMana; + } + return super.getNetMana(game, source); } - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!affectedControllerId.equals(source.getControllerId()) - || !getTargetPointer().getTargets(game, source).contains(objectId)) { - return false; - } - Card card = game.getCard(objectId); - if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) { - return true; - } - Player player = game.getPlayer(affectedControllerId); - if (player == null) { - return false; - } - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - return true; - } } diff --git a/Mage.Sets/src/mage/cards/u/UrzasMine.java b/Mage.Sets/src/mage/cards/u/UrzasMine.java index ed1530503e..f4b4470351 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasMine.java +++ b/Mage.Sets/src/mage/cards/u/UrzasMine.java @@ -1,8 +1,6 @@ - package mage.cards.u; import mage.Mana; -import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.UrzaTerrainValue; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; @@ -13,21 +11,22 @@ import mage.constants.SubType; import java.util.UUID; /** - * * @author Melkhior */ public final class UrzasMine extends CardImpl { + public UrzasMine(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); this.subtype.add(SubType.URZAS, SubType.MINE); // {T}: Add {C}. If you control an Urza's Power-Plant and an Urza's Tower, add {C}{C} instead. - Ability urzaManaAbility = new DynamicManaAbility(Mana.ColorlessMana(1), new UrzaTerrainValue(2), - "Add {C}. If you control an Urza's Power-Plant and an Urza's Tower, add {C}{C} instead"); - this.addAbility(urzaManaAbility); + this.addAbility(new DynamicManaAbility( + Mana.ColorlessMana(1), UrzaTerrainValue.MINE, + "Add {C}. If you control an Urza's Power-Plant and an Urza's Tower, add {C}{C} instead" + )); } - public UrzasMine(final UrzasMine card) { + private UrzasMine(final UrzasMine card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/u/UrzasPowerPlant.java b/Mage.Sets/src/mage/cards/u/UrzasPowerPlant.java index bb10e205dc..1ed40cbcc9 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasPowerPlant.java +++ b/Mage.Sets/src/mage/cards/u/UrzasPowerPlant.java @@ -1,8 +1,6 @@ - package mage.cards.u; import mage.Mana; -import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.UrzaTerrainValue; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; @@ -13,21 +11,22 @@ import mage.constants.SubType; import java.util.UUID; /** - * * @author Melkhior */ public final class UrzasPowerPlant extends CardImpl { + public UrzasPowerPlant(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); this.subtype.add(SubType.URZAS, SubType.POWER_PLANT); // {T}: Add {C}. If you control an Urza's Mine and an Urza's Tower, add {C}{C} instead. - Ability urzaManaAbility = new DynamicManaAbility(Mana.ColorlessMana(1), new UrzaTerrainValue(2), - "Add {C}. If you control an Urza's Mine and an Urza's Tower, add {C}{C} instead"); - this.addAbility(urzaManaAbility); + this.addAbility(new DynamicManaAbility( + Mana.ColorlessMana(1), UrzaTerrainValue.POWER_PLANT, + "Add {C}. If you control an Urza's Mine and an Urza's Tower, add {C}{C} instead" + )); } - public UrzasPowerPlant(final UrzasPowerPlant card) { + private UrzasPowerPlant(final UrzasPowerPlant card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/u/UrzasTower.java b/Mage.Sets/src/mage/cards/u/UrzasTower.java index 230c5711a7..33660e8981 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasTower.java +++ b/Mage.Sets/src/mage/cards/u/UrzasTower.java @@ -1,8 +1,6 @@ - package mage.cards.u; import mage.Mana; -import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.UrzaTerrainValue; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; @@ -13,21 +11,22 @@ import mage.constants.SubType; import java.util.UUID; /** - * * @author Melkhior */ public final class UrzasTower extends CardImpl { + public UrzasTower(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); this.subtype.add(SubType.URZAS, SubType.TOWER); // {T}: Add {C}. If you control an Urza's Mine and an Urza's Power-Plant, add {C}{C}{C} instead. - Ability urzaManaAbility = new DynamicManaAbility(Mana.ColorlessMana(1), new UrzaTerrainValue(3), - "Add {C}. If you control an Urza's Mine and an Urza's Power-Plant, add {C}{C}{C} instead"); - this.addAbility(urzaManaAbility); + this.addAbility(new DynamicManaAbility( + Mana.ColorlessMana(1), UrzaTerrainValue.TOWER, + "Add {C}. If you control an Urza's Mine and an Urza's Power-Plant, add {C}{C}{C} instead" + )); } - public UrzasTower(final UrzasTower card) { + private UrzasTower(final UrzasTower card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/u/UtopiaMycon.java b/Mage.Sets/src/mage/cards/u/UtopiaMycon.java index 3ff6b167c6..dab79d3ab9 100644 --- a/Mage.Sets/src/mage/cards/u/UtopiaMycon.java +++ b/Mage.Sets/src/mage/cards/u/UtopiaMycon.java @@ -21,6 +21,7 @@ import mage.game.permanent.token.SaprolingToken; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; /** * @author fireshoes @@ -46,7 +47,8 @@ public final class UtopiaMycon extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken()), new RemoveCountersSourceCost(CounterType.SPORE.createInstance(3)))); // Sacrifice a Saproling: Add one mana of any color. - Ability ability = new AnyColorManaAbility(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, false))); + Ability ability = new AnyColorManaAbility(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, false)), + new PermanentsOnBattlefieldCount(filter), false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java b/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java new file mode 100644 index 0000000000..51df925111 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java @@ -0,0 +1,69 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.PlayTargetWithoutPayingManaEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.common.FilterNoncreatureCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VadrokApexOfThunder extends CardImpl { + + private static final FilterCard filter = new FilterNoncreatureCard( + "noncreature card with converted mana cost 3 or less from your graveyard" + ); + + static { + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4)); + } + + public VadrokApexOfThunder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.DINOSAUR); + this.subtype.add(SubType.CAT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Mutate {1}{W/U}{R}{R} + this.addAbility(new MutateAbility(this, "{1}{W/U}{R}{R}")); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Whenever this creature mutates, you may cast target noncreature card with converted mana cost 3 or less from your graveyard without paying its mana cost. + Ability ability = new MutatesSourceTriggeredAbility(new PlayTargetWithoutPayingManaEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private VadrokApexOfThunder(final VadrokApexOfThunder card) { + super(card); + } + + @Override + public VadrokApexOfThunder copy() { + return new VadrokApexOfThunder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/ValiantRescuer.java b/Mage.Sets/src/mage/cards/v/ValiantRescuer.java new file mode 100644 index 0000000000..52df6596b7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/ValiantRescuer.java @@ -0,0 +1,135 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.HumanSoldierToken; +import mage.game.stack.StackAbility; +import mage.game.stack.StackObject; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ValiantRescuer extends CardImpl { + + public ValiantRescuer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Whenever you cycle another card for the first time each turn, create a 1/1 white Human Soldier creature token. + this.addAbility(new ValiantRescuerTriggeredAbility()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private ValiantRescuer(final ValiantRescuer card) { + super(card); + } + + @Override + public ValiantRescuer copy() { + return new ValiantRescuer(this); + } +} + +class ValiantRescuerTriggeredAbility extends TriggeredAbilityImpl { + + ValiantRescuerTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new HumanSoldierToken())); + this.addWatcher(new ValiantRescuerWatcher()); + } + + private ValiantRescuerTriggeredAbility(final ValiantRescuerTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ValiantRescuerWatcher watcher = game.getState().getWatcher(ValiantRescuerWatcher.class); + if (watcher == null + || !watcher.checkSpell(event.getPlayerId(), event.getSourceId()) + || game.getState().getStack().isEmpty() + || !event.getPlayerId().equals(this.getControllerId()) + || event.getSourceId().equals(this.getSourceId())) { + return false; + } + StackObject item = game.getState().getStack().getFirst(); + return item instanceof StackAbility + && item.getStackAbility() instanceof CyclingAbility; + } + + @Override + public ValiantRescuerTriggeredAbility copy() { + return new ValiantRescuerTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever you cycle another card for the first time each turn, " + + "create a 1/1 white Human Soldier creature token."; + } +} + +class ValiantRescuerWatcher extends Watcher { + + private final Map> playerMap = new HashMap<>(); + + ValiantRescuerWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ACTIVATED_ABILITY + || game.getState().getStack().isEmpty()) { + return; + } + StackObject item = game.getState().getStack().getFirst(); + if (item instanceof StackAbility + && item.getStackAbility() instanceof CyclingAbility) { + playerMap.computeIfAbsent(event.getPlayerId(), u -> new HashMap<>()); + playerMap.get(event.getPlayerId()).compute( + event.getSourceId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1) + ); + } + } + + @Override + public void reset() { + super.reset(); + playerMap.clear(); + } + + boolean checkSpell(UUID playerId, UUID cardId) { + if (!playerMap.containsKey(playerId)) { + return true; + } + Map cardMap = playerMap.get(playerId); + return cardMap.keySet().stream().filter(uuid -> !uuid.equals(cardId)).mapToInt(cardMap::get).sum() < 1; + } +} diff --git a/Mage.Sets/src/mage/cards/v/ValorousSteed.java b/Mage.Sets/src/mage/cards/v/ValorousSteed.java new file mode 100644 index 0000000000..e704aa72e9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/ValorousSteed.java @@ -0,0 +1,42 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.KnightToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ValorousSteed extends CardImpl { + + public ValorousSteed(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); + + this.subtype.add(SubType.UNICORN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // When Valorous Steed enters the battlefield, create a 2/2 white Knight creature token with vigilance. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KnightToken()))); + } + + private ValorousSteed(final ValorousSteed card) { + super(card); + } + + @Override + public ValorousSteed copy() { + return new ValorousSteed(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VampireHounds.java b/Mage.Sets/src/mage/cards/v/VampireHounds.java index f231c28a65..7cfaeabfd4 100644 --- a/Mage.Sets/src/mage/cards/v/VampireHounds.java +++ b/Mage.Sets/src/mage/cards/v/VampireHounds.java @@ -23,7 +23,7 @@ public final class VampireHounds extends CardImpl { public VampireHounds(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.VAMPIRE); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/v/VampireSovereign.java b/Mage.Sets/src/mage/cards/v/VampireSovereign.java index 66b5549d08..1eb0bd6b42 100644 --- a/Mage.Sets/src/mage/cards/v/VampireSovereign.java +++ b/Mage.Sets/src/mage/cards/v/VampireSovereign.java @@ -31,7 +31,7 @@ public final class VampireSovereign extends CardImpl { // When Vampire Sovereign enters the battlefield, target opponent loses 3 life and you gain 3 life. Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(3)); - ability.addEffect(new GainLifeEffect(3).setText("and you gain 3 life")); + ability.addEffect(new GainLifeEffect(3).concatBy("and")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VancesBlastingCannons.java b/Mage.Sets/src/mage/cards/v/VancesBlastingCannons.java index ec03cc2a0d..4eb7fa8560 100644 --- a/Mage.Sets/src/mage/cards/v/VancesBlastingCannons.java +++ b/Mage.Sets/src/mage/cards/v/VancesBlastingCannons.java @@ -85,7 +85,7 @@ class VancesBlastingCannonsExileEffect extends OneShotEffect { controller.moveCardsToExile(card, source, game, true, source.getSourceId(), exileName); if (game.getState().getZone(card.getId()) == Zone.EXILED && !card.isLand()) { ContinuousEffect effect = new CastFromNonHandZoneTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/v/VanishIntoMemory.java b/Mage.Sets/src/mage/cards/v/VanishIntoMemory.java index e2274dea49..6ff09bc079 100644 --- a/Mage.Sets/src/mage/cards/v/VanishIntoMemory.java +++ b/Mage.Sets/src/mage/cards/v/VanishIntoMemory.java @@ -72,7 +72,7 @@ class VanishIntoMemoryEffect extends OneShotEffect { MageObject sourceObject = game.getObject(source.getSourceId()); if (controller != null && permanent != null && sourceObject != null) { if (controller.moveCardsToExile(permanent, source, game, true, source.getSourceId(), sourceObject.getIdName())) { - controller.drawCards(permanent.getPower().getValue(), game); + controller.drawCards(permanent.getPower().getValue(), source.getSourceId(), game); ExileZone exile = game.getExile().getExileZone(source.getSourceId()); // only if permanent is in exile (tokens would be stop to exist) if (exile != null && !exile.isEmpty()) { diff --git a/Mage.Sets/src/mage/cards/v/VarchildsWarRiders.java b/Mage.Sets/src/mage/cards/v/VarchildsWarRiders.java index 10471c8723..3962f9c441 100644 --- a/Mage.Sets/src/mage/cards/v/VarchildsWarRiders.java +++ b/Mage.Sets/src/mage/cards/v/VarchildsWarRiders.java @@ -1,4 +1,3 @@ - package mage.cards.v; import java.util.UUID; @@ -16,11 +15,10 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; -import mage.filter.FilterOpponent; import mage.game.Game; import mage.game.permanent.token.SurvivorToken; import mage.players.Player; -import mage.target.TargetPlayer; +import mage.target.common.TargetOpponent; import mage.target.targetpointer.FixedTarget; /** @@ -57,10 +55,8 @@ public final class VarchildsWarRiders extends CardImpl { class OpponentCreateSurvivorTokenCost extends CostImpl { - private static final FilterOpponent filter = new FilterOpponent(); - public OpponentCreateSurvivorTokenCost() { - this.text = "have an opponent create a 1/1 red Survivor creature token"; + this.text = "Have an opponent create a 1/1 red Survivor creature token."; } public OpponentCreateSurvivorTokenCost(OpponentCreateSurvivorTokenCost cost) { @@ -82,8 +78,8 @@ class OpponentCreateSurvivorTokenCost extends CostImpl { public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { Player controller = game.getPlayer(controllerId); if (controller != null) { - TargetPlayer target = new TargetPlayer(1, 1, true, filter); - if (controller.chooseTarget(Outcome.Detriment, target, ability, game)) { + TargetOpponent target = new TargetOpponent(); + if (controller.chooseTarget(Outcome.Neutral, target, ability, game)) { Player opponent = game.getPlayer(target.getFirstTarget()); if (opponent != null) { Effect effect = new CreateTokenTargetEffect(new SurvivorToken()); diff --git a/Mage.Sets/src/mage/cards/v/VastwoodHydra.java b/Mage.Sets/src/mage/cards/v/VastwoodHydra.java index 0aaed1b552..eea7f827e3 100644 --- a/Mage.Sets/src/mage/cards/v/VastwoodHydra.java +++ b/Mage.Sets/src/mage/cards/v/VastwoodHydra.java @@ -4,7 +4,7 @@ package mage.cards.v; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.OneShotEffect; @@ -45,7 +45,7 @@ public final class VastwoodHydra extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); // When Vastwood Hydra dies, you may distribute a number of +1/+1 counters equal to the number of +1/+1 counters on Vastwood Hydra among any number of creatures you control. - Ability ability = new DiesTriggeredAbility(new VastwoodHydraDistributeEffect(), true); + Ability ability = new DiesSourceTriggeredAbility(new VastwoodHydraDistributeEffect(), true); ability.addTarget(new TargetCreaturePermanentAmount(new CountersSourceCount(CounterType.P1P1), filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VaultOfCatlacan.java b/Mage.Sets/src/mage/cards/v/VaultOfCatlacan.java index 06ea9db04b..2fa55288f7 100644 --- a/Mage.Sets/src/mage/cards/v/VaultOfCatlacan.java +++ b/Mage.Sets/src/mage/cards/v/VaultOfCatlacan.java @@ -1,14 +1,13 @@ - package mage.cards.v; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.mana.DynamicManaEffect; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.mana.DynamicManaEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.abilities.mana.AnyColorManaAbility; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -16,10 +15,10 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; import mage.constants.Zone; -import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class VaultOfCatlacan extends CardImpl { @@ -40,8 +39,9 @@ public final class VaultOfCatlacan extends CardImpl { // {T}: Add {U} for each artifact you control. this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, - new DynamicManaEffect(Mana.BlueMana(1), new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)), - new TapSourceCost())); + new DynamicManaEffect(Mana.BlueMana(1), ArtifactYouControlCount.instance), + new TapSourceCost()) + .addHint(ArtifactYouControlHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/v/VedalkenCertarch.java b/Mage.Sets/src/mage/cards/v/VedalkenCertarch.java index 1ec8de0648..332e852c77 100644 --- a/Mage.Sets/src/mage/cards/v/VedalkenCertarch.java +++ b/Mage.Sets/src/mage/cards/v/VedalkenCertarch.java @@ -1,14 +1,12 @@ - - package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityWord; @@ -19,9 +17,10 @@ import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author Loki */ public final class VedalkenCertarch extends CardImpl { @@ -34,22 +33,22 @@ public final class VedalkenCertarch extends CardImpl { CardType.LAND.getPredicate())); } - public VedalkenCertarch (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}"); + public VedalkenCertarch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); this.subtype.add(SubType.VEDALKEN); this.subtype.add(SubType.WIZARD); - this.power = new MageInt(1); this.toughness = new MageInt(1); // Metalcraft — {T}: Tap target artifact, creature, or land. Activate this ability only if you control three or more artifacts. Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new TapSourceCost(), MetalcraftCondition.instance); - ability.setAbilityWord(AbilityWord.METALCRAFT); ability.addTarget(new TargetPermanent(filter)); + ability.setAbilityWord(AbilityWord.METALCRAFT); + ability.addHint(MetalcraftHint.instance); this.addAbility(ability); } - public VedalkenCertarch (final VedalkenCertarch card) { + public VedalkenCertarch(final VedalkenCertarch card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/v/VedalkenGhoul.java b/Mage.Sets/src/mage/cards/v/VedalkenGhoul.java index b8b15b2d2e..9911b85d8e 100644 --- a/Mage.Sets/src/mage/cards/v/VedalkenGhoul.java +++ b/Mage.Sets/src/mage/cards/v/VedalkenGhoul.java @@ -3,7 +3,7 @@ package mage.cards.v; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeDefendingPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class VedalkenGhoul extends CardImpl { this.toughness = new MageInt(1); // Whenever Vedalken Ghoul becomes blocked, defending player loses 4 life. - this.addAbility(new BecomesBlockedTriggeredAbility(new LoseLifeDefendingPlayerEffect(4, true), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new LoseLifeDefendingPlayerEffect(4, true), false)); } diff --git a/Mage.Sets/src/mage/cards/v/VedalkenHumiliator.java b/Mage.Sets/src/mage/cards/v/VedalkenHumiliator.java index 9997b830b2..573d0f5033 100644 --- a/Mage.Sets/src/mage/cards/v/VedalkenHumiliator.java +++ b/Mage.Sets/src/mage/cards/v/VedalkenHumiliator.java @@ -1,6 +1,5 @@ package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.AttacksTriggeredAbility; @@ -8,15 +7,18 @@ import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.continuous.LoseAllAbilitiesAllEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessAllEffect; -import mage.constants.SubType; +import mage.abilities.hint.common.MetalcraftHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class VedalkenHumiliator extends CardImpl { @@ -44,10 +46,12 @@ public final class VedalkenHumiliator extends CardImpl { this.addAbility(new ConditionalTriggeredAbility( ability, MetalcraftCondition.instance, "Metalcraft — Whenever {this} attacks, " - + "if you control three or more artifacts, " - + "creatures your opponents control lose all abilities " - + "and have base power and toughness 1/1 until end of turn." - )); + + "if you control three or more artifacts, " + + "creatures your opponents control lose all abilities " + + "and have base power and toughness 1/1 until end of turn.") + .setAbilityWord(AbilityWord.METALCRAFT) + .addHint(MetalcraftHint.instance) + ); } public VedalkenHumiliator(final VedalkenHumiliator card) { diff --git a/Mage.Sets/src/mage/cards/v/VeilOfBirds.java b/Mage.Sets/src/mage/cards/v/VeilOfBirds.java index 53e476fca4..ce1739b3e4 100644 --- a/Mage.Sets/src/mage/cards/v/VeilOfBirds.java +++ b/Mage.Sets/src/mage/cards/v/VeilOfBirds.java @@ -1,66 +1,66 @@ -package mage.cards.v; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.SpellCastOpponentTriggeredAbility; -import mage.abilities.condition.common.SourceMatchesFilterCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.abilities.keyword.FlyingAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.filter.FilterSpell; -import mage.filter.StaticFilters; -import mage.game.permanent.token.TokenImpl; - -/** - * - * @author jeffwadsworth - */ -public final class VeilOfBirds extends CardImpl { - - private static final FilterSpell filter = new FilterSpell(); - - public VeilOfBirds(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); - - // When an opponent casts a spell, if Veil of Birds is an enchantment, Veil of Birds becomes a 1/1 Bird creature with flying. - TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeilOfBirdsToken(), "", Duration.WhileOnBattlefield, true, false), - filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), - "Whenever an opponent casts a spell, if Veil of Birds is an enchantment, Veil of Birds becomes a 1/1 Bird creature with flying.")); - } - - public VeilOfBirds(final VeilOfBirds card) { - super(card); - } - - @Override - public VeilOfBirds copy() { - return new VeilOfBirds(this); - } -} - -class VeilOfBirdsToken extends TokenImpl { - - public VeilOfBirdsToken() { - super("Bird", "1/1 creature with flying"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.BIRD); - power = new MageInt(1); - toughness = new MageInt(1); - this.addAbility(FlyingAbility.getInstance()); - } - - public VeilOfBirdsToken(final VeilOfBirdsToken token) { - super(token); - } - - public VeilOfBirdsToken copy() { - return new VeilOfBirdsToken(this); - } -} +package mage.cards.v; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.game.permanent.token.TokenImpl; + +/** + * + * @author jeffwadsworth + */ +public final class VeilOfBirds extends CardImpl { + + private static final FilterSpell filter = new FilterSpell(); + + public VeilOfBirds(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); + + // When an opponent casts a spell, if Veil of Birds is an enchantment, Veil of Birds becomes a 1/1 Bird creature with flying. + TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeilOfBirdsToken(), "", Duration.WhileOnBattlefield, true, false), + filter, false); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + "Whenever an opponent casts a spell, if Veil of Birds is an enchantment, Veil of Birds becomes a 1/1 Bird creature with flying.")); + } + + public VeilOfBirds(final VeilOfBirds card) { + super(card); + } + + @Override + public VeilOfBirds copy() { + return new VeilOfBirds(this); + } +} + +class VeilOfBirdsToken extends TokenImpl { + + public VeilOfBirdsToken() { + super("Bird", "1/1 creature with flying"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.BIRD); + power = new MageInt(1); + toughness = new MageInt(1); + this.addAbility(FlyingAbility.getInstance()); + } + + public VeilOfBirdsToken(final VeilOfBirdsToken token) { + super(token); + } + + public VeilOfBirdsToken copy() { + return new VeilOfBirdsToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VeilOfSummer.java b/Mage.Sets/src/mage/cards/v/VeilOfSummer.java index c6a5284ff5..a586809ab1 100644 --- a/Mage.Sets/src/mage/cards/v/VeilOfSummer.java +++ b/Mage.Sets/src/mage/cards/v/VeilOfSummer.java @@ -34,7 +34,9 @@ public final class VeilOfSummer extends CardImpl { public VeilOfSummer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); - // Draw a card if an opponent has cast a blue or black spell this turn. Spells you control can't be countered this turn. You and permanents you control gain hexproof from blue and from black until end of turn. + // Draw a card if an opponent has cast a blue or black spell this turn. + // Spells you control can't be countered this turn. You and permanents you control + // gain hexproof from blue and from black until end of turn. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new DrawCardSourceControllerEffect(1), VeilOfSummerCondition.instance, "Draw a card " + diff --git a/Mage.Sets/src/mage/cards/v/VeiledApparition.java b/Mage.Sets/src/mage/cards/v/VeiledApparition.java index 2bf24f1339..f1adbce262 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledApparition.java +++ b/Mage.Sets/src/mage/cards/v/VeiledApparition.java @@ -1,74 +1,74 @@ -package mage.cards.v; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.SpellCastOpponentTriggeredAbility; -import mage.abilities.condition.common.SourceMatchesFilterCondition; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.DoUnlessControllerPaysEffect; -import mage.abilities.effects.common.SacrificeSourceEffect; -import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.FilterSpell; -import mage.filter.StaticFilters; -import mage.game.permanent.token.TokenImpl; - -/** - * - * @author jeffwadsworth - */ -public final class VeiledApparition extends CardImpl { - - private static final FilterSpell filter = new FilterSpell(); - - public VeiledApparition(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); - - - // When an opponent casts a spell, if Veiled Apparition is an enchantment, Veiled Apparition becomes a 3/3 Illusion creature with flying and "At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}." - TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeilApparitionToken(), "", Duration.WhileOnBattlefield, true, false), - filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), - "Whenever an opponent casts a spell, if Veiled Apparition is an enchantment, Veiled Apparition becomes a 3/3 Illusion creature with flying and \"At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}.")); - - } - - public VeiledApparition(final VeiledApparition card) { - super(card); - } - - @Override - public VeiledApparition copy() { - return new VeiledApparition(this); - } -} - -class VeilApparitionToken extends TokenImpl { - - public VeilApparitionToken() { - super("Illusion", "3/3 Illusion creature with flying and \"At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}."); - cardType.add(CardType.CREATURE); - subtype.add(SubType.ILLUSION); - power = new MageInt(3); - toughness = new MageInt(3); - Ability ability = new BeginningOfUpkeepTriggeredAbility(new DoUnlessControllerPaysEffect(new SacrificeSourceEffect(), new ManaCostsImpl("{1}{U}")), TargetController.YOU, false); - this.addAbility(ability); - } - - public VeilApparitionToken(final VeilApparitionToken token) { - super(token); - } - - public VeilApparitionToken copy() { - return new VeilApparitionToken(this); - } +package mage.cards.v; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DoUnlessControllerPaysEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.game.permanent.token.TokenImpl; + +/** + * + * @author jeffwadsworth + */ +public final class VeiledApparition extends CardImpl { + + private static final FilterSpell filter = new FilterSpell(); + + public VeiledApparition(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + + // When an opponent casts a spell, if Veiled Apparition is an enchantment, Veiled Apparition becomes a 3/3 Illusion creature with flying and "At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}." + TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeilApparitionToken(), "", Duration.WhileOnBattlefield, true, false), + filter, false); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + "Whenever an opponent casts a spell, if Veiled Apparition is an enchantment, Veiled Apparition becomes a 3/3 Illusion creature with flying and \"At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}.")); + + } + + public VeiledApparition(final VeiledApparition card) { + super(card); + } + + @Override + public VeiledApparition copy() { + return new VeiledApparition(this); + } +} + +class VeilApparitionToken extends TokenImpl { + + public VeilApparitionToken() { + super("Illusion", "3/3 Illusion creature with flying and \"At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}."); + cardType.add(CardType.CREATURE); + subtype.add(SubType.ILLUSION); + power = new MageInt(3); + toughness = new MageInt(3); + Ability ability = new BeginningOfUpkeepTriggeredAbility(new DoUnlessControllerPaysEffect(new SacrificeSourceEffect(), new ManaCostsImpl("{1}{U}")), TargetController.YOU, false); + this.addAbility(ability); + } + + public VeilApparitionToken(final VeilApparitionToken token) { + super(token); + } + + public VeilApparitionToken copy() { + return new VeilApparitionToken(this); + } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java b/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java index ba72786c3f..2819a96a1e 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java +++ b/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java @@ -1,126 +1,126 @@ -package mage.cards.v; - -import mage.MageInt; -import mage.abilities.StateTriggeredAbility; -import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.token.TokenImpl; -import mage.players.Player; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public final class VeiledCrocodile extends CardImpl { - - public VeiledCrocodile(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); - - // When a player has no cards in hand, if Veiled Crocodile is an enchantment, Veiled Crocodile becomes a 4/4 Crocodile creature. - this.addAbility(new VeiledCrocodileStateTriggeredAbility()); - } - - public VeiledCrocodile(final VeiledCrocodile card) { - super(card); - } - - @Override - public VeiledCrocodile copy() { - return new VeiledCrocodile(this); - } -} - -class VeiledCrocodileStateTriggeredAbility extends StateTriggeredAbility { - - public VeiledCrocodileStateTriggeredAbility() { - super(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new VeilCrocodileToken(), "", Duration.Custom, true, false)); - } - - public VeiledCrocodileStateTriggeredAbility(final VeiledCrocodileStateTriggeredAbility ability) { - super(ability); - } - - @Override - public VeiledCrocodileStateTriggeredAbility copy() { - return new VeiledCrocodileStateTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - for (UUID playerId : game.getState().getPlayersInRange(controllerId, game)) { - Player player = game.getPlayer(playerId); - if (player != null - && player.getHand().isEmpty()) { - return true; - } - } - return false; - } - - @Override - public boolean checkInterveningIfClause(Game game) { - if (getSourcePermanentIfItStillExists(game) != null) { - return getSourcePermanentIfItStillExists(game).isEnchantment(); - } - return false; - } - - @Override - public boolean canTrigger(Game game) { - //20100716 - 603.8 - return !Boolean.TRUE.equals(game.getState().getValue(getSourceId().toString() + "triggered")); - } - - @Override - public void trigger(Game game, UUID controllerId) { - //20100716 - 603.8 - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); - } - - @Override - public boolean resolve(Game game) { - //20100716 - 603.8 - boolean result = super.resolve(game); - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); - return result; - } - - @Override - public void counter(Game game) { - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); - } - - @Override - public String getRule() { - return "When a player has no cards in hand, if {this} is an enchantment, " + super.getRule(); - } - -} - -class VeilCrocodileToken extends TokenImpl { - - public VeilCrocodileToken() { - super("Crocodile", "4/4 Crocodile creature"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.CROCODILE); - power = new MageInt(4); - toughness = new MageInt(4); - } - - public VeilCrocodileToken(final VeilCrocodileToken token) { - super(token); - } - - public VeilCrocodileToken copy() { - return new VeilCrocodileToken(this); - } -} +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.StateTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.TokenImpl; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public final class VeiledCrocodile extends CardImpl { + + public VeiledCrocodile(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + // When a player has no cards in hand, if Veiled Crocodile is an enchantment, Veiled Crocodile becomes a 4/4 Crocodile creature. + this.addAbility(new VeiledCrocodileStateTriggeredAbility()); + } + + public VeiledCrocodile(final VeiledCrocodile card) { + super(card); + } + + @Override + public VeiledCrocodile copy() { + return new VeiledCrocodile(this); + } +} + +class VeiledCrocodileStateTriggeredAbility extends StateTriggeredAbility { + + public VeiledCrocodileStateTriggeredAbility() { + super(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new VeilCrocodileToken(), "", Duration.Custom, true, false)); + } + + public VeiledCrocodileStateTriggeredAbility(final VeiledCrocodileStateTriggeredAbility ability) { + super(ability); + } + + @Override + public VeiledCrocodileStateTriggeredAbility copy() { + return new VeiledCrocodileStateTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + for (UUID playerId : game.getState().getPlayersInRange(controllerId, game)) { + Player player = game.getPlayer(playerId); + if (player != null + && player.getHand().isEmpty()) { + return true; + } + } + return false; + } + + @Override + public boolean checkInterveningIfClause(Game game) { + if (getSourcePermanentIfItStillExists(game) != null) { + return getSourcePermanentIfItStillExists(game).isEnchantment(); + } + return false; + } + + @Override + public boolean canTrigger(Game game) { + //20100716 - 603.8 + return !Boolean.TRUE.equals(game.getState().getValue(getSourceId().toString() + "triggered")); + } + + @Override + public void trigger(Game game, UUID controllerId) { + //20100716 - 603.8 + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); + super.trigger(game, controllerId); + } + + @Override + public boolean resolve(Game game) { + //20100716 - 603.8 + boolean result = super.resolve(game); + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); + return result; + } + + @Override + public void counter(Game game) { + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); + } + + @Override + public String getRule() { + return "When a player has no cards in hand, if {this} is an enchantment, " + super.getRule(); + } + +} + +class VeilCrocodileToken extends TokenImpl { + + public VeilCrocodileToken() { + super("Crocodile", "4/4 Crocodile creature"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.CROCODILE); + power = new MageInt(4); + toughness = new MageInt(4); + } + + public VeilCrocodileToken(final VeilCrocodileToken token) { + super(token); + } + + public VeilCrocodileToken copy() { + return new VeilCrocodileToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VeiledSentry.java b/Mage.Sets/src/mage/cards/v/VeiledSentry.java index 3c6ce6f61e..933b8aaca4 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledSentry.java +++ b/Mage.Sets/src/mage/cards/v/VeiledSentry.java @@ -1,115 +1,115 @@ -package mage.cards.v; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.SpellCastOpponentTriggeredAbility; -import mage.abilities.condition.common.SourceMatchesFilterCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import static mage.constants.Layer.PTChangingEffects_7; -import static mage.constants.Layer.TypeChangingEffects_4; -import mage.constants.Outcome; -import mage.constants.SetTargetPointer; -import mage.constants.SubLayer; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterSpell; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; - -/** - * - * @author jeffwadsworth - */ -public final class VeiledSentry extends CardImpl { - - public VeiledSentry(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); - - // When an opponent casts a spell, if Veiled Sentry is an enchantment, Veiled Sentry becomes an Illusion creature with power and toughness each equal to that spell's converted mana cost. - TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new VeiledSentryEffect(), new FilterSpell(), false, SetTargetPointer.SPELL); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), - "Whenever an opponent casts a spell, if Veiled Sentry is an enchantment, Veil Sentry becomes an Illusion creature with power and toughness equal to that spell's converted mana cost.")); - - } - - public VeiledSentry(final VeiledSentry card) { - super(card); - } - - @Override - public VeiledSentry copy() { - return new VeiledSentry(this); - } -} - -class VeiledSentryEffect extends ContinuousEffectImpl { - - public VeiledSentryEffect() { - super(Duration.Custom, Outcome.BecomeCreature); - staticText = "{this} becomes an Illusion creature with power and toughness equal to that spell's converted mana cost"; - } - - public VeiledSentryEffect(final VeiledSentryEffect effect) { - super(effect); - } - - @Override - public VeiledSentryEffect copy() { - return new VeiledSentryEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent veiledSentry = game.getPermanent(source.getSourceId()); - Spell spellCast = game.getSpell(targetPointer.getFirst(game, source)); - if (spellCast != null) { - game.getState().setValue(source + "cmcSpell", spellCast.getConvertedManaCost()); - } - if (veiledSentry == null) { - return false; - } - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - veiledSentry.getCardType().clear(); - if (!veiledSentry.isCreature()) { - veiledSentry.addCardType(CardType.CREATURE); - } - if (!veiledSentry.getSubtype(game).contains(SubType.ILLUSION)) { - veiledSentry.getSubtype(game).add(SubType.ILLUSION); - } - } - break; - - case PTChangingEffects_7: - if (game.getState().getValue(source + "cmcSpell") != null) { - int cmc = (int) game.getState().getValue(source + "cmcSpell"); - if (sublayer == SubLayer.SetPT_7b) { - veiledSentry.addPower(cmc); - veiledSentry.addToughness(cmc); - } - } - } - return true; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.PTChangingEffects_7 - || layer == Layer.TypeChangingEffects_4; - } -} +package mage.cards.v; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import static mage.constants.Layer.PTChangingEffects_7; +import static mage.constants.Layer.TypeChangingEffects_4; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.SubLayer; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; + +/** + * + * @author jeffwadsworth + */ +public final class VeiledSentry extends CardImpl { + + public VeiledSentry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); + + // When an opponent casts a spell, if Veiled Sentry is an enchantment, Veiled Sentry becomes an Illusion creature with power and toughness each equal to that spell's converted mana cost. + TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new VeiledSentryEffect(), new FilterSpell(), false, SetTargetPointer.SPELL); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + "Whenever an opponent casts a spell, if Veiled Sentry is an enchantment, Veil Sentry becomes an Illusion creature with power and toughness equal to that spell's converted mana cost.")); + + } + + public VeiledSentry(final VeiledSentry card) { + super(card); + } + + @Override + public VeiledSentry copy() { + return new VeiledSentry(this); + } +} + +class VeiledSentryEffect extends ContinuousEffectImpl { + + public VeiledSentryEffect() { + super(Duration.Custom, Outcome.BecomeCreature); + staticText = "{this} becomes an Illusion creature with power and toughness equal to that spell's converted mana cost"; + } + + public VeiledSentryEffect(final VeiledSentryEffect effect) { + super(effect); + } + + @Override + public VeiledSentryEffect copy() { + return new VeiledSentryEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent veiledSentry = game.getPermanent(source.getSourceId()); + Spell spellCast = game.getSpell(targetPointer.getFirst(game, source)); + if (spellCast != null) { + game.getState().setValue(source + "cmcSpell", spellCast.getConvertedManaCost()); + } + if (veiledSentry == null) { + return false; + } + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == SubLayer.NA) { + veiledSentry.getCardType().clear(); + if (!veiledSentry.isCreature()) { + veiledSentry.addCardType(CardType.CREATURE); + } + if (!veiledSentry.getSubtype(game).contains(SubType.ILLUSION)) { + veiledSentry.getSubtype(game).add(SubType.ILLUSION); + } + } + break; + + case PTChangingEffects_7: + if (game.getState().getValue(source + "cmcSpell") != null) { + int cmc = (int) game.getState().getValue(source + "cmcSpell"); + if (sublayer == SubLayer.SetPT_7b) { + veiledSentry.addPower(cmc); + veiledSentry.addToughness(cmc); + } + } + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.PTChangingEffects_7 + || layer == Layer.TypeChangingEffects_4; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VeiledSerpent.java b/Mage.Sets/src/mage/cards/v/VeiledSerpent.java index 216133ebfd..9a95b8c023 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledSerpent.java +++ b/Mage.Sets/src/mage/cards/v/VeiledSerpent.java @@ -1,75 +1,75 @@ -package mage.cards.v; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.SpellCastOpponentTriggeredAbility; -import mage.abilities.condition.common.SourceMatchesFilterCondition; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.combat.CantAttackUnlessDefenderControllsPermanent; -import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; -import mage.abilities.keyword.CyclingAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterSpell; -import mage.filter.StaticFilters; -import mage.filter.common.FilterLandPermanent; -import mage.game.permanent.token.TokenImpl; - -/** - * - * @author jeffwadsworth - */ -public final class VeiledSerpent extends CardImpl { - - public VeiledSerpent(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); - - // When an opponent casts a spell, if Veiled Serpent is an enchantment, Veiled Serpent becomes a 4/4 Serpent creature that can't attack unless defending player controls an Island. - TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeiledSerpentToken(), "", Duration.WhileOnBattlefield, true, false), - new FilterSpell(), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), - "Whenever an opponent casts a spell, if Veiled Serpent is an enchantment, Veiled Serpent becomes a 4/4 Serpent creature that can't attack unless defending player controls an Island.")); - - // Cycling {2} - this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); - - } - - public VeiledSerpent(final VeiledSerpent card) { - super(card); - } - - @Override - public VeiledSerpent copy() { - return new VeiledSerpent(this); - } -} - -class VeiledSerpentToken extends TokenImpl { - - public VeiledSerpentToken() { - super("Serpent", "4/4 Serpent creature"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.SERPENT); - power = new MageInt(4); - toughness = new MageInt(4); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new CantAttackUnlessDefenderControllsPermanent( - new FilterLandPermanent(SubType.ISLAND, "an Island")))); - } - - public VeiledSerpentToken(final VeiledSerpentToken token) { - super(token); - } - - public VeiledSerpentToken copy() { - return new VeiledSerpentToken(this); - } -} +package mage.cards.v; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.combat.CantAttackUnlessDefenderControllsPermanent; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.filter.common.FilterLandPermanent; +import mage.game.permanent.token.TokenImpl; + +/** + * + * @author jeffwadsworth + */ +public final class VeiledSerpent extends CardImpl { + + public VeiledSerpent(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + // When an opponent casts a spell, if Veiled Serpent is an enchantment, Veiled Serpent becomes a 4/4 Serpent creature that can't attack unless defending player controls an Island. + TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeiledSerpentToken(), "", Duration.WhileOnBattlefield, true, false), + new FilterSpell(), false); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + "Whenever an opponent casts a spell, if Veiled Serpent is an enchantment, Veiled Serpent becomes a 4/4 Serpent creature that can't attack unless defending player controls an Island.")); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + + } + + public VeiledSerpent(final VeiledSerpent card) { + super(card); + } + + @Override + public VeiledSerpent copy() { + return new VeiledSerpent(this); + } +} + +class VeiledSerpentToken extends TokenImpl { + + public VeiledSerpentToken() { + super("Serpent", "4/4 Serpent creature"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.SERPENT); + power = new MageInt(4); + toughness = new MageInt(4); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new CantAttackUnlessDefenderControllsPermanent( + new FilterLandPermanent(SubType.ISLAND, "an Island")))); + } + + public VeiledSerpentToken(final VeiledSerpentToken token) { + super(token); + } + + public VeiledSerpentToken copy() { + return new VeiledSerpentToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/Vendetta.java b/Mage.Sets/src/mage/cards/v/Vendetta.java index 3befedd303..0057abd3e2 100644 --- a/Mage.Sets/src/mage/cards/v/Vendetta.java +++ b/Mage.Sets/src/mage/cards/v/Vendetta.java @@ -69,7 +69,7 @@ class VendettaEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Permanent target = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + Permanent target = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (player != null && target != null) { player.loseLife(target.getToughness().getValue(), game, false); return true; diff --git a/Mage.Sets/src/mage/cards/v/VendilionClique.java b/Mage.Sets/src/mage/cards/v/VendilionClique.java index a2dd78280e..dd054298ab 100644 --- a/Mage.Sets/src/mage/cards/v/VendilionClique.java +++ b/Mage.Sets/src/mage/cards/v/VendilionClique.java @@ -85,7 +85,7 @@ class VendilionCliqueEffect extends OneShotEffect { cards.add(card); player.revealCards(sourceObject.getIdName(), cards, game); player.putCardsOnBottomOfLibrary(cards, game, source, true); - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/v/VenerableKnight.java b/Mage.Sets/src/mage/cards/v/VenerableKnight.java index dc14f248f3..475806ee62 100644 --- a/Mage.Sets/src/mage/cards/v/VenerableKnight.java +++ b/Mage.Sets/src/mage/cards/v/VenerableKnight.java @@ -2,7 +2,7 @@ package mage.cards.v; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -31,7 +31,7 @@ public final class VenerableKnight extends CardImpl { this.toughness = new MageInt(1); // When Venerable Knight dies, put a +1/+1 counter on target Knight you control. - Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VengeantVampire.java b/Mage.Sets/src/mage/cards/v/VengeantVampire.java index 5e15e894da..b8b89d0249 100644 --- a/Mage.Sets/src/mage/cards/v/VengeantVampire.java +++ b/Mage.Sets/src/mage/cards/v/VengeantVampire.java @@ -2,7 +2,7 @@ package mage.cards.v; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.keyword.LifelinkAbility; @@ -32,7 +32,7 @@ public final class VengeantVampire extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // When Vengeant Vampire dies, destroy target creature an opponent controls and you gain 4 life. - Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect()); + Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect()); ability.addEffect(new GainLifeEffect(4).concatBy("and")); ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/v/VengefulDreams.java b/Mage.Sets/src/mage/cards/v/VengefulDreams.java index 073079d0a4..c354c7d7e8 100644 --- a/Mage.Sets/src/mage/cards/v/VengefulDreams.java +++ b/Mage.Sets/src/mage/cards/v/VengefulDreams.java @@ -1,6 +1,5 @@ package mage.cards.v; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.common.DiscardXTargetCost; import mage.abilities.effects.Effect; @@ -15,6 +14,8 @@ import mage.target.Target; import mage.target.TargetPermanent; import mage.target.targetadjustment.TargetAdjuster; +import java.util.UUID; + /** * @author fireshoes */ @@ -24,7 +25,7 @@ public final class VengefulDreams extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}{W}"); // As an additional cost to cast Vengeful Dreams, discard X cards. - this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), true)); + this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), false)); // Exile X target attacking creatures. Effect effect = new ExileTargetEffect(); diff --git a/Mage.Sets/src/mage/cards/v/VenomousDragonfly.java b/Mage.Sets/src/mage/cards/v/VenomousDragonfly.java index 4815d4b859..7da8c928d2 100644 --- a/Mage.Sets/src/mage/cards/v/VenomousDragonfly.java +++ b/Mage.Sets/src/mage/cards/v/VenomousDragonfly.java @@ -3,7 +3,7 @@ package mage.cards.v; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -32,7 +32,7 @@ public final class VenomousDragonfly extends CardImpl { // Whenever Venomous Dragonfly blocks or becomes blocked by a creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect())); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false)); } public VenomousDragonfly(final VenomousDragonfly card) { diff --git a/Mage.Sets/src/mage/cards/v/VenomousFangs.java b/Mage.Sets/src/mage/cards/v/VenomousFangs.java index 1be68f955f..2cff30f807 100644 --- a/Mage.Sets/src/mage/cards/v/VenomousFangs.java +++ b/Mage.Sets/src/mage/cards/v/VenomousFangs.java @@ -1,48 +1,48 @@ -package mage.cards.v; - -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.DealsDamageToACreatureAttachedTriggeredAbility; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DestroyTargetEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; - -/** - * - * @author jeffwadsworth - */ -public final class VenomousFangs extends CardImpl { - - public VenomousFangs(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); - - this.subtype.add(SubType.AURA); - - // Enchant creature - TargetPermanent auraTarget = new TargetCreaturePermanent(); - this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - - // Whenever enchanted creature deals damage to a creature, destroy the other creature. - this.addAbility(new DealsDamageToACreatureAttachedTriggeredAbility(new DestroyTargetEffect(), false, "enchanted creature", false, true)); - - } - - public VenomousFangs(final VenomousFangs card) { - super(card); - } - - @Override - public VenomousFangs copy() { - return new VenomousFangs(this); - } -} +package mage.cards.v; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToACreatureAttachedTriggeredAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +/** + * + * @author jeffwadsworth + */ +public final class VenomousFangs extends CardImpl { + + public VenomousFangs(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Whenever enchanted creature deals damage to a creature, destroy the other creature. + this.addAbility(new DealsDamageToACreatureAttachedTriggeredAbility(new DestroyTargetEffect(), false, "enchanted creature", false, true)); + + } + + public VenomousFangs(final VenomousFangs card) { + super(card); + } + + @Override + public VenomousFangs copy() { + return new VenomousFangs(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VerdantRebirth.java b/Mage.Sets/src/mage/cards/v/VerdantRebirth.java index db3c9e55dc..6197df2355 100644 --- a/Mage.Sets/src/mage/cards/v/VerdantRebirth.java +++ b/Mage.Sets/src/mage/cards/v/VerdantRebirth.java @@ -3,7 +3,7 @@ package mage.cards.v; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; @@ -24,7 +24,7 @@ public final class VerdantRebirth extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Until end of turn, target creature gains "When this creature dies, return it to its owner's hand." - Ability gainedAbility = new DiesTriggeredAbility(new ReturnToHandSourceEffect(), false); + Ability gainedAbility = new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect(), false); Effect effect = new GainAbilityTargetEffect(gainedAbility, Duration.EndOfTurn); effect.setText("Until end of turn, target creature gains \"When this creature dies, return it to its owner's hand.\""); this.getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/v/VergeRangers.java b/Mage.Sets/src/mage/cards/v/VergeRangers.java new file mode 100644 index 0000000000..f4b9228294 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VergeRangers.java @@ -0,0 +1,82 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; + +import java.util.Comparator; +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class VergeRangers extends CardImpl { + + public VergeRangers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // You may look at the top card of your library any time. + this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); + + // As long as an opponent controls more lands than you, you may play lands from the top of your library. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new VergeRangersEffect())); + } + + private VergeRangers(final VergeRangers card) { + super(card); + } + + @Override + public VergeRangers copy() { + return new VergeRangers(this); + } +} + +class VergeRangersEffect extends PlayTheTopCardEffect { + + public VergeRangersEffect() { + super(StaticFilters.FILTER_CARD_LAND); + staticText = "As long as an opponent controls more lands than you, you may play lands from the top of your library."; + } + + public VergeRangersEffect(final VergeRangersEffect effect) { + super(effect); + } + + @Override + public VergeRangersEffect copy() { + return new VergeRangersEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + if (super.applies(objectId, affectedAbility, source, game, playerId)) { + int myLandCount = game.getBattlefield().countAll(StaticFilters.FILTER_LAND, playerId, game); + int maxOpponentLandCount = game.getOpponents(playerId).stream() + .map(opponentId -> game.getBattlefield().countAll(StaticFilters.FILTER_LAND, opponentId, game)) + .max(Comparator.naturalOrder()) + .orElse(0); + return maxOpponentLandCount > myLandCount; + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java b/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java index 234266a6fa..3d8109bd95 100644 --- a/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java +++ b/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java @@ -120,7 +120,7 @@ class VesuvanShapeshifterEffect extends OneShotEffect { if (copyFromCreature != null) { game.copyPermanent(Duration.Custom, copyFromCreature, copyToCreature.getId(), source, new VesuvanShapeShifterFaceUpApplier()); source.getTargets().clear(); - game.applyEffects(); // needed to get effects ready if copy happens in replacment and the copied abilities react of the same event (e.g. turn face up) + game.getState().processAction(game); // needed to get effects ready if copy happens in replacment and the copied abilities react of the same event (e.g. turn face up) return true; } } diff --git a/Mage.Sets/src/mage/cards/v/VeteranExplorer.java b/Mage.Sets/src/mage/cards/v/VeteranExplorer.java index 0e34e7fb8c..a77af3879e 100644 --- a/Mage.Sets/src/mage/cards/v/VeteranExplorer.java +++ b/Mage.Sets/src/mage/cards/v/VeteranExplorer.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -36,7 +36,7 @@ public final class VeteranExplorer extends CardImpl { this.toughness = new MageInt(1); // When Veteran Explorer dies, each player may search their library for up to two basic land cards and put them onto the battlefield. Then each player who searched their library this way shuffles it. - this.addAbility(new DiesTriggeredAbility(new VeteranExplorerEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new VeteranExplorerEffect())); } public VeteranExplorer(final VeteranExplorer card) { diff --git a/Mage.Sets/src/mage/cards/v/Vex.java b/Mage.Sets/src/mage/cards/v/Vex.java index 48dfa184ec..6290902bd3 100644 --- a/Mage.Sets/src/mage/cards/v/Vex.java +++ b/Mage.Sets/src/mage/cards/v/Vex.java @@ -65,7 +65,7 @@ class VexEffect extends OneShotEffect { countered = true; } if (controller != null) { - controller.drawCards(1, game); + controller.drawCards(1, source.getSourceId(), game); } return countered; } diff --git a/Mage.Sets/src/mage/cards/v/VexingArcanix.java b/Mage.Sets/src/mage/cards/v/VexingArcanix.java index 7a77fc4238..742e51c56a 100644 --- a/Mage.Sets/src/mage/cards/v/VexingArcanix.java +++ b/Mage.Sets/src/mage/cards/v/VexingArcanix.java @@ -74,7 +74,7 @@ class VexingArcanixEffect extends OneShotEffect { if (card != null) { Cards cards = new CardsImpl(card); player.revealCards(sourceObject.getIdName(), cards, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { player.moveCards(cards, Zone.HAND, source, game); } else { player.moveCards(cards, Zone.GRAVEYARD, source, game); diff --git a/Mage.Sets/src/mage/cards/v/VexingBeetle.java b/Mage.Sets/src/mage/cards/v/VexingBeetle.java index 2d0b03297d..a064bfbd2c 100644 --- a/Mage.Sets/src/mage/cards/v/VexingBeetle.java +++ b/Mage.Sets/src/mage/cards/v/VexingBeetle.java @@ -1,34 +1,27 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.InvertCondition; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.CantBeCounteredSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.constants.*; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class VexingBeetle extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, ComparisonType.EQUAL_TO, 0, false + ); public VexingBeetle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); @@ -41,14 +34,13 @@ public final class VexingBeetle extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.STACK, new CantBeCounteredSourceEffect())); // Vexing Beetle gets +3/+3 as long as no opponent controls a creature. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostSourceEffect(3, 3, Duration.WhileOnBattlefield), - new InvertCondition(new PermanentsOnTheBattlefieldCondition(filter)), - "{this} gets +3/+3 as long as no opponent controls a creature") - )); + condition, "{this} gets +3/+3 as long as no opponent controls a creature" + ))); } - public VexingBeetle(final VexingBeetle card) { + private VexingBeetle(final VexingBeetle card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/v/VexingShusher.java b/Mage.Sets/src/mage/cards/v/VexingShusher.java index a4a58098a4..c82812672e 100644 --- a/Mage.Sets/src/mage/cards/v/VexingShusher.java +++ b/Mage.Sets/src/mage/cards/v/VexingShusher.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; @@ -36,7 +36,7 @@ public final class VexingShusher extends CardImpl { this.toughness = new MageInt(2); // Vexing Shusher can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // {R/G}: Target spell can't be countered. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new VexingShusherCantCounterTargetEffect(), new ManaCostsImpl("{R/G}")); ability.addTarget(new TargetSpell()); diff --git a/Mage.Sets/src/mage/cards/v/VexingSphinx.java b/Mage.Sets/src/mage/cards/v/VexingSphinx.java index a7c10ca324..7e98531098 100644 --- a/Mage.Sets/src/mage/cards/v/VexingSphinx.java +++ b/Mage.Sets/src/mage/cards/v/VexingSphinx.java @@ -3,7 +3,7 @@ package mage.cards.v; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.keyword.FlyingAbility; import mage.abilities.dynamicvalue.common.CountersSourceCount; @@ -35,7 +35,7 @@ public final class VexingSphinx extends CardImpl { this.addAbility(new CumulativeUpkeepAbility(new DiscardCardCost())); // When Vexing Sphinx dies, draw a card for each age counter on it. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.AGE)))); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.AGE)))); } diff --git a/Mage.Sets/src/mage/cards/v/ViashinoBey.java b/Mage.Sets/src/mage/cards/v/ViashinoBey.java index fe4c657c4f..72fc8aff40 100644 --- a/Mage.Sets/src/mage/cards/v/ViashinoBey.java +++ b/Mage.Sets/src/mage/cards/v/ViashinoBey.java @@ -1,93 +1,93 @@ -package mage.cards.v; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetOpponent; - -/** - * - * @author jeffwadsworth - */ -public final class ViashinoBey extends CardImpl { - - private static final String rule = "If {this} attacks, all creatures you control attack if able."; - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - - public ViashinoBey(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); - - this.subtype.add(SubType.VIASHINO); - this.power = new MageInt(4); - this.toughness = new MageInt(3); - - // If Viashino Bey attacks, all creatures you control attack if able. - this.addAbility(new AttacksTriggeredAbility(new ViashinoBeyEffect(), false, rule)); - - } - - public ViashinoBey(final ViashinoBey card) { - super(card); - } - - @Override - public ViashinoBey copy() { - return new ViashinoBey(this); - } -} - -class ViashinoBeyEffect extends OneShotEffect { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - - public ViashinoBeyEffect() { - super(Outcome.Benefit); - } - - public ViashinoBeyEffect(final ViashinoBeyEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - TargetOpponent targetDefender = new TargetOpponent(); - if (controller != null) { - game.getBattlefield().getAllActivePermanents(CardType.CREATURE).stream().filter((permanent) -> (filter.match(permanent, source.getSourceId(), source.getControllerId(), game))).forEachOrdered((permanent) -> { - if (game.getOpponents(controller.getId()).size() > 1) { - controller.choose(outcome.Benefit, targetDefender, source.getSourceId(), game); - } else { - targetDefender.add(game.getOpponents(controller.getId()).iterator().next(), game); - } - if (permanent.canAttack(targetDefender.getFirstTarget(), game)) { - controller.declareAttacker(permanent.getId(), targetDefender.getFirstTarget(), game, false); - } - }); - } - return false; - } - - @Override - public ViashinoBeyEffect copy() { - return new ViashinoBeyEffect(this); - } -} +package mage.cards.v; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +/** + * + * @author jeffwadsworth + */ +public final class ViashinoBey extends CardImpl { + + private static final String rule = "If {this} attacks, all creatures you control attack if able."; + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public ViashinoBey(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.VIASHINO); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // If Viashino Bey attacks, all creatures you control attack if able. + this.addAbility(new AttacksTriggeredAbility(new ViashinoBeyEffect(), false, rule)); + + } + + public ViashinoBey(final ViashinoBey card) { + super(card); + } + + @Override + public ViashinoBey copy() { + return new ViashinoBey(this); + } +} + +class ViashinoBeyEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public ViashinoBeyEffect() { + super(Outcome.Benefit); + } + + public ViashinoBeyEffect(final ViashinoBeyEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + TargetOpponent targetDefender = new TargetOpponent(); + if (controller != null) { + game.getBattlefield().getAllActivePermanents(CardType.CREATURE).stream().filter((permanent) -> (filter.match(permanent, source.getSourceId(), source.getControllerId(), game))).forEachOrdered((permanent) -> { + if (game.getOpponents(controller.getId()).size() > 1) { + controller.choose(outcome.Benefit, targetDefender, source.getSourceId(), game); + } else { + targetDefender.add(game.getOpponents(controller.getId()).iterator().next(), game); + } + if (permanent.canAttack(targetDefender.getFirstTarget(), game)) { + controller.declareAttacker(permanent.getId(), targetDefender.getFirstTarget(), game, false); + } + }); + } + return false; + } + + @Override + public ViashinoBeyEffect copy() { + return new ViashinoBeyEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/ViciousRumors.java b/Mage.Sets/src/mage/cards/v/ViciousRumors.java index 245ce9a78e..34e937b174 100644 --- a/Mage.Sets/src/mage/cards/v/ViciousRumors.java +++ b/Mage.Sets/src/mage/cards/v/ViciousRumors.java @@ -31,8 +31,7 @@ public final class ViciousRumors extends CardImpl { this.getSpellAbility().addEffect( new PutTopCardOfLibraryIntoGraveEachPlayerEffect( 1, TargetController.OPPONENT - ).setText(", then puts the top card of their library " - + "into their graveyard") + ).setText(", then mills a card") ); this.getSpellAbility().addEffect(new GainLifeEffect(1)); } diff --git a/Mage.Sets/src/mage/cards/v/Victimize.java b/Mage.Sets/src/mage/cards/v/Victimize.java index 89a75c8500..159bbf14b3 100644 --- a/Mage.Sets/src/mage/cards/v/Victimize.java +++ b/Mage.Sets/src/mage/cards/v/Victimize.java @@ -64,7 +64,7 @@ class VictimizeEffect extends OneShotEffect { if (controller != null) { SacrificeTargetCost cost = new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT)); if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { - game.applyEffects(); // To end effects of the sacrificed creature + game.getState().processAction(game); // To end effects of the sacrificed creature controller.moveCards(new CardsImpl(getTargetPointer().getTargets(game, source)).getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null); } diff --git a/Mage.Sets/src/mage/cards/v/VillageRites.java b/Mage.Sets/src/mage/cards/v/VillageRites.java new file mode 100644 index 0000000000..3217fc3d6e --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VillageRites.java @@ -0,0 +1,38 @@ +package mage.cards.v; + +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VillageRites extends CardImpl { + + public VillageRites(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // As an additional cost to cast this spell, sacrifice a creature. + this.getSpellAbility().addCost(new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + )); + + // Draw two cards. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); + } + + private VillageRites(final VillageRites card) { + super(card); + } + + @Override + public VillageRites copy() { + return new VillageRites(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VillainousWealth.java b/Mage.Sets/src/mage/cards/v/VillainousWealth.java index d404f5b112..8b8366c186 100644 --- a/Mage.Sets/src/mage/cards/v/VillainousWealth.java +++ b/Mage.Sets/src/mage/cards/v/VillainousWealth.java @@ -50,8 +50,8 @@ class VillainousWealthEffect extends OneShotEffect { public VillainousWealthEffect() { super(Outcome.PlayForFree); this.staticText = "Target opponent exiles the top X cards of their library. " - + "You may cast any number of nonland cards with converted mana cost X " - + "or less from among them without paying their mana cost"; + + "You may cast any number of spells with converted mana cost X " + + "or less from among them without paying their mana costs"; } public VillainousWealthEffect(final VillainousWealthEffect effect) { diff --git a/Mage.Sets/src/mage/cards/v/VindictiveLich.java b/Mage.Sets/src/mage/cards/v/VindictiveLich.java index 281345c030..1ffd56cfa8 100644 --- a/Mage.Sets/src/mage/cards/v/VindictiveLich.java +++ b/Mage.Sets/src/mage/cards/v/VindictiveLich.java @@ -3,7 +3,7 @@ package mage.cards.v; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; @@ -34,7 +34,7 @@ public final class VindictiveLich extends CardImpl { // When Vindictive Lich dies, choose one or more. Each mode must target a different player. // * Target opponent sacrifices a creature. - Ability ability = new DiesTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target opponent")); + Ability ability = new DiesSourceTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target opponent")); ability.getModes().setMinModes(1); ability.getModes().setMaxModes(3); ability.getModes().setEachModeOnlyOnce(true); diff --git a/Mage.Sets/src/mage/cards/v/ViridianEmissary.java b/Mage.Sets/src/mage/cards/v/ViridianEmissary.java index 9b4dc18c0a..5e5c53710d 100644 --- a/Mage.Sets/src/mage/cards/v/ViridianEmissary.java +++ b/Mage.Sets/src/mage/cards/v/ViridianEmissary.java @@ -2,7 +2,7 @@ package mage.cards.v; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -28,7 +28,7 @@ public final class ViridianEmissary extends CardImpl { this.toughness = new MageInt(1); // When Viridian Emissary dies, you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. - this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true), true)); + this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true), true)); } public ViridianEmissary(final ViridianEmissary card) { diff --git a/Mage.Sets/src/mage/cards/v/ViridianJoiner.java b/Mage.Sets/src/mage/cards/v/ViridianJoiner.java index 563743319f..7b1e6837d7 100644 --- a/Mage.Sets/src/mage/cards/v/ViridianJoiner.java +++ b/Mage.Sets/src/mage/cards/v/ViridianJoiner.java @@ -25,7 +25,7 @@ public final class ViridianJoiner extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - // {tap}: Add an amount of {G} equal to Viridian Joiner's power. + // {T}: Add an amount of {G} equal to Viridian Joiner's power. this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), new SourcePermanentPowerCount())); } diff --git a/Mage.Sets/src/mage/cards/v/VisionsOfBeyond.java b/Mage.Sets/src/mage/cards/v/VisionsOfBeyond.java index bd4e1e68ea..59f51ea1b9 100644 --- a/Mage.Sets/src/mage/cards/v/VisionsOfBeyond.java +++ b/Mage.Sets/src/mage/cards/v/VisionsOfBeyond.java @@ -59,7 +59,7 @@ class VisionsOfBeyondEffect extends OneShotEffect { } } } - sourcePlayer.drawCards(count, game); + sourcePlayer.drawCards(count, source.getSourceId(), game); return true; } diff --git a/Mage.Sets/src/mage/cards/v/VitalityHunter.java b/Mage.Sets/src/mage/cards/v/VitalityHunter.java new file mode 100644 index 0000000000..f0d1a180e6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VitalityHunter.java @@ -0,0 +1,66 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BecomesMonstrousSourceTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.MonstrosityAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VitalityHunter extends CardImpl { + + public VitalityHunter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.NIGHTMARE); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // {X}{W}{W}: Monstrosity X. + this.addAbility(new MonstrosityAbility("{X}{W}{W}", Integer.MAX_VALUE)); + + // When Vitality Hunter becomes monstrous, put a lifelink counter on each of up to X target creatures. + Ability ability = new BecomesMonstrousSourceTriggeredAbility( + new AddCountersTargetEffect(CounterType.LIFELINK.createInstance()) + .setText("put a lifelink counter on each of up to X target creatures") + ); + ability.setTargetAdjuster(VitalityHunterAdjuster.instance); + this.addAbility(ability); + } + + private VitalityHunter(final VitalityHunter card) { + super(card); + } + + @Override + public VitalityHunter copy() { + return new VitalityHunter(this); + } +} + +enum VitalityHunterAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + int xValue = ((BecomesMonstrousSourceTriggeredAbility) ability).getMonstrosityValue(); + ability.getTargets().clear(); + ability.addTarget(new TargetCreaturePermanent(0, xValue)); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VitoThornOfTheDuskRose.java b/Mage.Sets/src/mage/cards/v/VitoThornOfTheDuskRose.java new file mode 100644 index 0000000000..77e717e596 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VitoThornOfTheDuskRose.java @@ -0,0 +1,96 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.hint.StaticHint; + +/** + * @author TheElk801 + */ +public final class VitoThornOfTheDuskRose extends CardImpl { + + public VitoThornOfTheDuskRose(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Whenever you gain life, target opponent loses that much life. + this.addAbility(new VitoThornOfTheDuskRoseTriggeredAbility()); + + // {3}{B}{B}: Creatures you control gain lifelink until end of turn. + this.addAbility(new SimpleActivatedAbility(new GainAbilityControlledEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES + ), new ManaCostsImpl("{3}{B}{B}"))); + } + + private VitoThornOfTheDuskRose(final VitoThornOfTheDuskRose card) { + super(card); + } + + @Override + public VitoThornOfTheDuskRose copy() { + return new VitoThornOfTheDuskRose(this); + } +} + +class VitoThornOfTheDuskRoseTriggeredAbility extends TriggeredAbilityImpl { + + VitoThornOfTheDuskRoseTriggeredAbility() { + super(Zone.BATTLEFIELD, null); + this.addTarget(new TargetOpponent()); + } + + private VitoThornOfTheDuskRoseTriggeredAbility(final VitoThornOfTheDuskRoseTriggeredAbility ability) { + super(ability); + } + + @Override + public VitoThornOfTheDuskRoseTriggeredAbility copy() { + return new VitoThornOfTheDuskRoseTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.GAINED_LIFE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getPlayerId().equals(getControllerId())) { + MageObject mageObject = game.getObject(event.getSourceId()); + this.getEffects().clear(); + this.getHints().clear(); + this.addHint(new StaticHint("Lose life amount: " + event.getAmount())); + if (mageObject != null) { + this.addHint(new StaticHint("Caused by: " + mageObject.getLogName())); + } + this.addEffect(new LoseLifeTargetEffect(event.getAmount())); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever you gain life, target opponent loses that much life."; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java b/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java new file mode 100644 index 0000000000..3964e955a6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java @@ -0,0 +1,164 @@ +package mage.cards.v; + +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.BeastToken; +import mage.game.permanent.token.Token; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; +import java.util.Arrays; +import java.util.Locale; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class VivienMonstersAdvocate extends CardImpl { + + private static final FilterCard filter = new FilterCreatureCard("cast creature spells"); + + public VivienMonstersAdvocate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VIVIEN); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + + // You may look at the top card of your library any time. + this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); + + // You may cast creature spells from the top of your library. + this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); + + // +1: Create a 3/3 green Beast creature token. Put your choice of a vigilance + // counter, a reach counter, or a trample counter on it. + this.addAbility(new LoyaltyAbility(new VivienMonstersAdvocateTokenEffect(), 1)); + + // −2: When you cast your next creature spell this turn, search your library for a + // creature card with lesser converted mana cost, put it onto the battlefield, then shuffle your library. + this.addAbility(new LoyaltyAbility( + new CreateDelayedTriggeredAbilityEffect(new VivienMonstersAdvocateTriggeredAbility()), -2 + )); + } + + private VivienMonstersAdvocate(final VivienMonstersAdvocate card) { + super(card); + } + + @Override + public VivienMonstersAdvocate copy() { + return new VivienMonstersAdvocate(this); + } +} + +class VivienMonstersAdvocateTokenEffect extends OneShotEffect { + + private static final Token token = new BeastToken(); + private static final Set choices + = Arrays.asList("Vigilance", "Reach", "Trample").stream().collect(Collectors.toSet()); + + VivienMonstersAdvocateTokenEffect() { + super(Outcome.Benefit); + staticText = "Create a 3/3 green Beast creature token. Put your choice of a vigilance counter, " + + "a reach counter, or a trample counter on it."; + } + + private VivienMonstersAdvocateTokenEffect(final VivienMonstersAdvocateTokenEffect effect) { + super(effect); + } + + @Override + public VivienMonstersAdvocateTokenEffect copy() { + return new VivienMonstersAdvocateTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + for (UUID tokenId : token.getLastAddedTokenIds()) { + Permanent permanent = game.getPermanent(tokenId); + if (permanent == null) { + continue; + } + Choice choice = new ChoiceImpl(); + choice.setChoices(choices); + player.choose(outcome, choice, game); + String chosen = choice.getChoice(); + if (chosen != null) { + permanent.addCounters(CounterType.findByName(chosen.toLowerCase( + Locale.ENGLISH)).createInstance(), source, game); + } + } + return true; + } +} + +class VivienMonstersAdvocateTriggeredAbility extends DelayedTriggeredAbility { + + VivienMonstersAdvocateTriggeredAbility() { + super(null, Duration.EndOfTurn, true, false); + } + + private VivienMonstersAdvocateTriggeredAbility(final VivienMonstersAdvocateTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Spell spell = game.getSpell(event.getTargetId()); + if (spell != null + && spell.isCreature()) { + int cmc = spell.getConvertedManaCost(); + FilterCard filter = new FilterCreatureCard("creature card with converted mana cost less than " + cmc); + filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, cmc)); + this.getEffects().clear(); + this.getEffects().add(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter))); + return true; + } + return false; + } + + @Override + public DelayedTriggeredAbility copy() { + return new VivienMonstersAdvocateTriggeredAbility(this); + } + + @Override + public String getRule() { + return "When you cast your next creature spell this turn, " + + "search your library for a creature card with lesser converted mana cost, " + + "put it onto the battlefield, then shuffle your library."; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VivienOfTheArkbow.java b/Mage.Sets/src/mage/cards/v/VivienOfTheArkbow.java index 82b52a429e..34c82d0c40 100644 --- a/Mage.Sets/src/mage/cards/v/VivienOfTheArkbow.java +++ b/Mage.Sets/src/mage/cards/v/VivienOfTheArkbow.java @@ -10,10 +10,12 @@ import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -24,12 +26,6 @@ import java.util.UUID; */ public final class VivienOfTheArkbow extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public VivienOfTheArkbow(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{G}{G}"); @@ -45,7 +41,7 @@ public final class VivienOfTheArkbow extends CardImpl { // −3: Target creature you control deals damage equal to its power to target creature you don't control. ability = new LoyaltyAbility(new DamageWithPowerFromOneToAnotherTargetEffect(), -3); ability.addTarget(new TargetControlledCreaturePermanent()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); // −9: Creatures you control get +4/+4 and gain trample until end of turn. @@ -61,7 +57,7 @@ public final class VivienOfTheArkbow extends CardImpl { this.addAbility(ability); } - public VivienOfTheArkbow(final VivienOfTheArkbow card) { + private VivienOfTheArkbow(final VivienOfTheArkbow card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/v/ViviensInvocation.java b/Mage.Sets/src/mage/cards/v/ViviensInvocation.java index 23d46f0a18..b3ed065a12 100644 --- a/Mage.Sets/src/mage/cards/v/ViviensInvocation.java +++ b/Mage.Sets/src/mage/cards/v/ViviensInvocation.java @@ -1,30 +1,24 @@ package mage.cards.v; -import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; +import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponentsCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ViviensInvocation extends CardImpl { @@ -36,7 +30,7 @@ public final class ViviensInvocation extends CardImpl { this.getSpellAbility().addEffect(new ViviensInvocationEffect()); } - public ViviensInvocation(final ViviensInvocation card) { + private ViviensInvocation(final ViviensInvocation card) { super(card); } @@ -48,11 +42,9 @@ public final class ViviensInvocation extends CardImpl { class ViviensInvocationEffect extends OneShotEffect { - public ViviensInvocationEffect(final ViviensInvocationEffect effect) { - super(effect); - } + private static final FilterCard filter = new FilterCreatureCard("creature card to put on the battlefield"); - public ViviensInvocationEffect() { + ViviensInvocationEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "Look at the top seven cards of your library. " + "You may put a creature card from among them onto the battlefield. " @@ -61,6 +53,10 @@ class ViviensInvocationEffect extends OneShotEffect { + "it deals damage equal to its power to target creature an opponent controls"; } + private ViviensInvocationEffect(final ViviensInvocationEffect effect) { + super(effect); + } + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); @@ -68,31 +64,32 @@ class ViviensInvocationEffect extends OneShotEffect { return false; } Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 7)); - if (!cards.isEmpty()) { - TargetCard target = new TargetCard( - Zone.LIBRARY, - new FilterCreatureCard("creature card to put on the battlefield") - ); - target.setNotTarget(true); - if (controller.choose(Outcome.PutCreatureInPlay, cards, target, game)) { - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - cards.remove(card); - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); - if (permanent != null) { - game.addDelayedTriggeredAbility( - new ViviensInvocationReflexiveTriggeredAbility( - new MageObjectReference(permanent, game) - ), source); - new SendOptionUsedEventEffect().apply(game, source); - } - } - } - if (!cards.isEmpty()) { - controller.putCardsOnBottomOfLibrary(cards, game, source, false); - } + if (cards.isEmpty()) { + return true; } + TargetCard target = new TargetCard(Zone.LIBRARY, filter); + target.setNotTarget(true); + controller.choose(Outcome.PutCreatureInPlay, cards, target, game); + Card card = cards.get(target.getFirstTarget(), game); + if (card == null) { + controller.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; + } + if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { + cards.remove(card); + } + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { + controller.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ViviensInvocationDamageEffect(new MageObjectReference(permanent, game)), false, + "it deals damage equals to its power to target creature an opponent controls" + ); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + game.fireReflexiveTriggeredAbility(ability, source); + controller.putCardsOnBottomOfLibrary(cards, game, source, false); return true; } @@ -102,50 +99,16 @@ class ViviensInvocationEffect extends OneShotEffect { } } -class ViviensInvocationReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - public ViviensInvocationReflexiveTriggeredAbility(MageObjectReference mor) { - super(new ViviensInvocationDamageEffect(mor), Duration.OneUse, false); - this.addTarget(new TargetOpponentsCreaturePermanent()); - } - - public ViviensInvocationReflexiveTriggeredAbility(final ViviensInvocationReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public ViviensInvocationReflexiveTriggeredAbility copy() { - return new ViviensInvocationReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When a creature is put onto the battlefield with {this}, " - + "it deals damage equal to its power to target creature an opponent controls"; - } -} - class ViviensInvocationDamageEffect extends OneShotEffect { private final MageObjectReference mor; - public ViviensInvocationDamageEffect(MageObjectReference mor) { + ViviensInvocationDamageEffect(MageObjectReference mor) { super(Outcome.Benefit); this.mor = mor; } - public ViviensInvocationDamageEffect(final ViviensInvocationDamageEffect effect) { + private ViviensInvocationDamageEffect(final ViviensInvocationDamageEffect effect) { super(effect); mor = effect.mor; } diff --git a/Mage.Sets/src/mage/cards/v/VizierOfDeferment.java b/Mage.Sets/src/mage/cards/v/VizierOfDeferment.java index e7b275a073..33389b3b54 100644 --- a/Mage.Sets/src/mage/cards/v/VizierOfDeferment.java +++ b/Mage.Sets/src/mage/cards/v/VizierOfDeferment.java @@ -1,4 +1,3 @@ - package mage.cards.v; import mage.MageInt; @@ -89,7 +88,7 @@ class VizierOfDefermentEffect extends OneShotEffect { && attackedOrBlocked && sourcePermanent != null) { if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/v/VizierOfTheMenagerie.java b/Mage.Sets/src/mage/cards/v/VizierOfTheMenagerie.java index 8583afb86c..28cab7afd1 100644 --- a/Mage.Sets/src/mage/cards/v/VizierOfTheMenagerie.java +++ b/Mage.Sets/src/mage/cards/v/VizierOfTheMenagerie.java @@ -7,13 +7,14 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.AsThoughManaEffect; import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; -import mage.cards.Card; +import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.players.ManaPoolItem; -import mage.players.Player; import mage.util.CardUtil; import java.util.UUID; @@ -23,6 +24,8 @@ import java.util.UUID; */ public final class VizierOfTheMenagerie extends CardImpl { + private static final FilterCard filter = new FilterCreatureCard("cast creature spells"); + public VizierOfTheMenagerie(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); this.subtype.add(SubType.NAGA); @@ -34,7 +37,7 @@ public final class VizierOfTheMenagerie extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LookAtTopCardOfLibraryAnyTimeEffect())); // You may cast the top card of your library if it's a creature card. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new VizierOfTheMenagerieTopCardCastEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter))); // You may spend mana as though it were mana of any type to cast creature spells. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new VizierOfTheMenagerieManaEffect())); @@ -51,50 +54,6 @@ public final class VizierOfTheMenagerie extends CardImpl { } } -class VizierOfTheMenagerieTopCardCastEffect extends AsThoughEffectImpl { - - public VizierOfTheMenagerieTopCardCastEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "You may cast the top card of your library if it's a creature card"; - } - - public VizierOfTheMenagerieTopCardCastEffect(final VizierOfTheMenagerieTopCardCastEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public VizierOfTheMenagerieTopCardCastEffect copy() { - return new VizierOfTheMenagerieTopCardCastEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(objectId); - if (card != null) { - Player controller = game.getPlayer(affectedControllerId); - if (controller != null) { - Card topCard = controller.getLibrary().getFromTop(game); - MageObject vizierOfTheMenagerie = game.getObject(source.getSourceId()); - if (vizierOfTheMenagerie != null - && topCard != null) { - return topCard == card - && topCard.isCreature() - && topCard.getSpellAbility() != null - && topCard.getSpellAbility().spellCanBeActivatedRegularlyNow(controller.getId(), game); - } - } - } - } - return false; - } -} - class VizierOfTheMenagerieManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect { public VizierOfTheMenagerieManaEffect() { diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java index 9cfe01d0df..eaa0fb7424 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -9,7 +9,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapTargetCost; @@ -80,7 +80,7 @@ public final class VodalianWarMachine extends CardImpl { } } -class VodalianWarMachineTriggeredAbility extends DiesTriggeredAbility { +class VodalianWarMachineTriggeredAbility extends DiesSourceTriggeredAbility { public VodalianWarMachineTriggeredAbility() { super(new VodalianWarMachineEffect(), false); diff --git a/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java b/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java index dcdecac26e..265a2b732e 100644 --- a/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java +++ b/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java @@ -1,7 +1,7 @@ package mage.cards.v; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SpellCastOpponentTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; @@ -37,7 +37,7 @@ public final class VoiceOfResurgence extends CardImpl { new SpellCastOpponentTriggeredAbility(null, new FilterSpell("a spell"), false), MyTurnCondition.instance, "Whenever an opponent casts a spell during your turn, "), - new DiesTriggeredAbility(null, false)); + new DiesSourceTriggeredAbility(null, false)); ability.setLeavesTheBattlefieldTrigger(true); ability.addHint(MyTurnHint.instance); ability.addHint(CreaturesYouControlHint.instance); diff --git a/Mage.Sets/src/mage/cards/v/Void.java b/Mage.Sets/src/mage/cards/v/Void.java index 2f11a51224..71c053f17c 100644 --- a/Mage.Sets/src/mage/cards/v/Void.java +++ b/Mage.Sets/src/mage/cards/v/Void.java @@ -1,16 +1,10 @@ - package mage.cards.v; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; @@ -22,6 +16,8 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** * @author LevelX2 */ @@ -33,7 +29,6 @@ public final class Void extends CardImpl { // Choose a number. Destroy all artifacts and creatures with converted mana cost equal to that number. Then target player reveals their hand and discards all nonland cards with converted mana cost equal to the number. this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new VoidEffect()); - } public Void(final Void card) { @@ -47,13 +42,13 @@ public final class Void extends CardImpl { } class VoidEffect extends OneShotEffect { - - public VoidEffect() { + + VoidEffect() { super(Outcome.DestroyPermanent); this.staticText = "Choose a number. Destroy all artifacts and creatures with converted mana cost equal to that number. Then target player reveals their hand and discards all nonland cards with converted mana cost equal to the number"; } - public VoidEffect(final VoidEffect effect) { + private VoidEffect(final VoidEffect effect) { super(effect); } @@ -65,39 +60,29 @@ class VoidEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Choice numberChoice = new ChoiceImpl(); - Set numbers = new HashSet<>(16); - for (int i = 0; i <= 15; i++) { - numbers.add(Integer.toString(i)); + if (controller == null) { + return false; + } + + int number = controller.announceXMana(0, Integer.MAX_VALUE, this.staticText, game, source); + game.informPlayers(controller.getLogName() + " chooses " + number + '.'); + + for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { + if ((permanent.isArtifact() || permanent.isCreature()) + && permanent.getConvertedManaCost() == number) { + permanent.destroy(source.getSourceId(), game, false); } - numberChoice.setChoices(numbers); - numberChoice.setMessage("Choose a number"); - if (!controller.choose(Outcome.DestroyPermanent, numberChoice, game)) { - return false; - } - int number = Integer.parseInt(numberChoice.getChoice()); - for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - if ((permanent.isArtifact() || permanent.isCreature()) - && permanent.getConvertedManaCost() == number) { - permanent.destroy(source.getSourceId(), game, false); - } - } - FilterCard filterCard = new FilterCard(); - filterCard.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, number)); - filterCard.add(Predicates.not(CardType.LAND.getPredicate())); + } + FilterCard filterCard = new FilterCard(); + filterCard.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, number)); + filterCard.add(Predicates.not(CardType.LAND.getPredicate())); - Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (targetPlayer != null) { - targetPlayer.revealCards("Void", targetPlayer.getHand(), game); - for (Card card : targetPlayer.getHand().getCards(game)) { - if (filterCard.match(card, game)) { - targetPlayer.discard(card, source, game); - } - } - } + Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); + if (targetPlayer == null) { return true; } - return false; + targetPlayer.revealCards(source, targetPlayer.getHand(), game); + targetPlayer.discard(new CardsImpl(targetPlayer.getHand().getCards(filterCard, game)), source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/v/VoidBeckoner.java b/Mage.Sets/src/mage/cards/v/VoidBeckoner.java new file mode 100644 index 0000000000..e4ce9c7a2e --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoidBeckoner.java @@ -0,0 +1,54 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VoidBeckoner extends CardImpl { + + public VoidBeckoner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{B}{B}"); + + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Cycling {2}{B} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}{B}"))); + + // When you cycle Void Beckoner, put a deathtouch counter on target creature you control. + Ability ability = new CycleTriggeredAbility( + new AddCountersTargetEffect(CounterType.DEATHTOUCH.createInstance()) + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private VoidBeckoner(final VoidBeckoner card) { + super(card); + } + + @Override + public VoidBeckoner copy() { + return new VoidBeckoner(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoidStalker.java b/Mage.Sets/src/mage/cards/v/VoidStalker.java index 514476c33d..1941bfe4cc 100644 --- a/Mage.Sets/src/mage/cards/v/VoidStalker.java +++ b/Mage.Sets/src/mage/cards/v/VoidStalker.java @@ -1,6 +1,7 @@ - package mage.cards.v; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -11,8 +12,8 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -26,7 +27,7 @@ import mage.target.common.TargetCreaturePermanent; public final class VoidStalker extends CardImpl { public VoidStalker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.ELEMENTAL); this.power = new MageInt(2); @@ -50,6 +51,7 @@ public final class VoidStalker extends CardImpl { } class VoidStalkerEffect extends OneShotEffect { + VoidStalkerEffect() { super(Outcome.ReturnToHand); staticText = "Put {this} and target creature on top of their owners' libraries, then those players shuffle their libraries"; @@ -61,19 +63,25 @@ class VoidStalkerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent p = game.getPermanent(targetPointer.getFirst(game, source)); - Permanent s = game.getPermanent(source.getSourceId()); - if (p != null) { - p.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - Player pl = game.getPlayer(p.getControllerId()); - if (pl != null) - pl.shuffleLibrary(source, game); + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + Set toShuffle = new LinkedHashSet<>(); + if (targetCreature != null) { + Player owner = game.getPlayer(targetCreature.getOwnerId()); + if (owner != null) { + owner.putCardsOnTopOfLibrary(targetCreature, game, source, true); + toShuffle.add(owner); + } } - if (s != null) { - s.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - Player pl = game.getPlayer(s.getControllerId()); - if (pl != null) - pl.shuffleLibrary(source, game); + if (sourcePermanent != null) { + Player owner = game.getPlayer(sourcePermanent.getOwnerId()); + if (owner != null) { + owner.putCardsOnTopOfLibrary(sourcePermanent, game, source, true); + toShuffle.add(owner); + } + } + for (Player player : toShuffle) { + player.shuffleLibrary(source, game); } return true; } @@ -82,4 +90,4 @@ class VoidStalkerEffect extends OneShotEffect { public VoidStalkerEffect copy() { return new VoidStalkerEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java b/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java index c2cdda7262..16294ad597 100644 --- a/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java +++ b/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java @@ -1,6 +1,5 @@ package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -11,17 +10,15 @@ import mage.abilities.effects.common.ChooseACardNameEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author Plopman */ public final class VoidstoneGargoyle extends CardImpl { @@ -78,7 +75,7 @@ class VoidstoneGargoyleReplacementEffect1 extends ContinuousRuleModifyingEffectI public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source.getSourceId()); if (mageObject != null) { - return "You can't cast a spell with that name (" + mageObject.getIdName() + ")."; + return "You can't cast a spell with that name (" + mageObject.getName() + ")."; } return null; } @@ -87,10 +84,8 @@ class VoidstoneGargoyleReplacementEffect1 extends ContinuousRuleModifyingEffectI public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == GameEvent.EventType.CAST_SPELL) { MageObject object = game.getObject(event.getSourceId()); - if (object != null - && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) { - return true; - } + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(object, cardName, game); } return false; } @@ -122,7 +117,7 @@ class VoidstoneGargoyleRuleModifyingEffect2 extends ContinuousRuleModifyingEffec public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source.getSourceId()); if (mageObject != null) { - return "You can't activate abilities of sources with that name (" + mageObject.getLogName() + " in play)."; + return "You can't activate abilities of sources with that name (" + mageObject.getName() + " in play)."; } return null; } @@ -131,9 +126,8 @@ class VoidstoneGargoyleRuleModifyingEffect2 extends ContinuousRuleModifyingEffec public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == EventType.ACTIVATE_ABILITY) { MageObject object = game.getObject(event.getSourceId()); - if (object != null && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) { - return true; - } + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(object, cardName, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/v/Voidwalk.java b/Mage.Sets/src/mage/cards/v/Voidwalk.java index bd3c91baea..17890ff0e2 100644 --- a/Mage.Sets/src/mage/cards/v/Voidwalk.java +++ b/Mage.Sets/src/mage/cards/v/Voidwalk.java @@ -1,7 +1,5 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -20,8 +18,9 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class Voidwalk extends CardImpl { @@ -66,7 +65,7 @@ class VoidwalkEffect extends OneShotEffect { if (controller != null && permanent != null && sourceObject != null) { if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/v/VolatileRig.java b/Mage.Sets/src/mage/cards/v/VolatileRig.java index a3091c63a4..cd5be00e17 100644 --- a/Mage.Sets/src/mage/cards/v/VolatileRig.java +++ b/Mage.Sets/src/mage/cards/v/VolatileRig.java @@ -4,7 +4,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.AttacksEachCombatStaticAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; @@ -42,7 +42,7 @@ public final class VolatileRig extends CardImpl { this.addAbility(new VolatileRigTriggeredAbility()); // When Volatile Rig dies, flip a coin. If you lose the flip, it deals 4 damage to each creature and each player. - this.addAbility(new DiesTriggeredAbility(new VolatileRigEffect2())); + this.addAbility(new DiesSourceTriggeredAbility(new VolatileRigEffect2())); } diff --git a/Mage.Sets/src/mage/cards/v/VolcanicFallout.java b/Mage.Sets/src/mage/cards/v/VolcanicFallout.java index cd3e245bf2..829ef5a25d 100644 --- a/Mage.Sets/src/mage/cards/v/VolcanicFallout.java +++ b/Mage.Sets/src/mage/cards/v/VolcanicFallout.java @@ -2,7 +2,7 @@ package mage.cards.v; import java.util.UUID; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.common.DamageEverythingEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -19,7 +19,7 @@ public final class VolcanicFallout extends CardImpl { // Volcanic Fallout can't be countered. - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); // Volcanic Fallout deals 2 damage to each creature and each player. this.getSpellAbility().addEffect(new DamageEverythingEffect(2)); } diff --git a/Mage.Sets/src/mage/cards/v/VolcanicOffering.java b/Mage.Sets/src/mage/cards/v/VolcanicOffering.java index 56efb6faae..d329a5f05b 100644 --- a/Mage.Sets/src/mage/cards/v/VolcanicOffering.java +++ b/Mage.Sets/src/mage/cards/v/VolcanicOffering.java @@ -8,6 +8,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SuperType; import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.Predicates; @@ -35,7 +36,7 @@ public final class VolcanicOffering extends CardImpl { this.getSpellAbility().setTargetAdjuster(VolcanicOfferingAdjuster.instance); } - public VolcanicOffering(final VolcanicOffering card) { + private VolcanicOffering(final VolcanicOffering card) { super(card); } @@ -48,12 +49,10 @@ public final class VolcanicOffering extends CardImpl { enum VolcanicOfferingAdjuster implements TargetAdjuster { instance; private static final FilterLandPermanent filterLand = new FilterLandPermanent("nonbasic land you don't control"); - private static final FilterCreaturePermanent filterCreature = new FilterCreaturePermanent("creature you don't control"); static { filterLand.add(TargetController.NOT_YOU.getControllerPredicate()); filterLand.add(Predicates.not(SuperType.BASIC.getPredicate())); - filterCreature.add(TargetController.NOT_YOU.getControllerPredicate()); } @Override @@ -69,7 +68,7 @@ enum VolcanicOfferingAdjuster implements TargetAdjuster { filterLandForOpponent.add(Predicates.not(new ControllerIdPredicate(controller.getId()))); ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, filterLandForOpponent, false)); - ability.addTarget(new TargetPermanent(filterCreature)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); FilterCreaturePermanent filterCreatureForOpponent = new FilterCreaturePermanent("creature not controlled by " + controller.getLogName()); filterCreatureForOpponent.add(Predicates.not(new ControllerIdPredicate(controller.getId()))); ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, filterCreatureForOpponent, false)); @@ -78,13 +77,13 @@ enum VolcanicOfferingAdjuster implements TargetAdjuster { class VolcanicOfferingEffect extends OneShotEffect { - public VolcanicOfferingEffect() { + VolcanicOfferingEffect() { super(Outcome.Benefit); this.staticText = "Destroy target nonbasic land you don't control and target nonbasic land of an opponent's choice you don't control.
" + "{this} deals 7 damage to target creature you don't control and 7 damage to target creature of an opponent's choice you don't control"; } - public VolcanicOfferingEffect(final VolcanicOfferingEffect effect) { + private VolcanicOfferingEffect(final VolcanicOfferingEffect effect) { super(effect); } @@ -95,26 +94,22 @@ class VolcanicOfferingEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - permanent.destroy(source.getSourceId(), game, false); - } - permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (permanent != null) { - permanent.destroy(source.getSourceId(), game, false); - } - permanent = game.getPermanent(source.getTargets().get(2).getFirstTarget()); - if (permanent != null) { - permanent.damage(7, source.getSourceId(), game, false, true); - } - permanent = game.getPermanent(source.getTargets().get(3).getFirstTarget()); - if (permanent != null) { - permanent.damage(7, source.getSourceId(), game, false, true); - } - return true; + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + permanent.destroy(source.getSourceId(), game, false); } - return false; + permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + if (permanent != null) { + permanent.destroy(source.getSourceId(), game, false); + } + permanent = game.getPermanent(source.getTargets().get(2).getFirstTarget()); + if (permanent != null) { + permanent.damage(7, source.getSourceId(), game, false, true); + } + permanent = game.getPermanent(source.getTargets().get(3).getFirstTarget()); + if (permanent != null) { + permanent.damage(7, source.getSourceId(), game, false, true); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/v/VolcanicSalvo.java b/Mage.Sets/src/mage/cards/v/VolcanicSalvo.java new file mode 100644 index 0000000000..7c17f54e1d --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VolcanicSalvo.java @@ -0,0 +1,83 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreatureOrPlaneswalker; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VolcanicSalvo extends CardImpl { + + public VolcanicSalvo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{10}{R}{R}"); + + // This spell costs {X} less to cast, where X is the total power of creatures you control. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new VolcanicSalvoCostReductionEffect())); + + // Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers. + this.getSpellAbility().addEffect(new DamageTargetEffect(6) + .setText("{this} deals 6 damage to each of up to two target creatures and/or planeswalkers") + ); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker(0, 2)); + } + + private VolcanicSalvo(final VolcanicSalvo card) { + super(card); + } + + @Override + public VolcanicSalvo copy() { + return new VolcanicSalvo(this); + } +} + +class VolcanicSalvoCostReductionEffect extends CostModificationEffectImpl { + + VolcanicSalvoCostReductionEffect() { + super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "This spell costs {X} less to cast, where X is the total power of creatures you control"; + } + + private VolcanicSalvoCostReductionEffect(final VolcanicSalvoCostReductionEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + int reductionAmount = game.getBattlefield() + .getAllActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game + ).stream() + .map(Permanent::getPower) + .mapToInt(MageInt::getValue) + .sum(); + CardUtil.reduceCost(abilityToModify, Math.max(0, reductionAmount)); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility + && abilityToModify.getSourceId().equals(source.getSourceId()) + && game.getCard(abilityToModify.getSourceId()) != null; + } + + @Override + public VolcanicSalvoCostReductionEffect copy() { + return new VolcanicSalvoCostReductionEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoraciousGreatshark.java b/Mage.Sets/src/mage/cards/v/VoraciousGreatshark.java new file mode 100644 index 0000000000..eb52055ada --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoraciousGreatshark.java @@ -0,0 +1,56 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VoraciousGreatshark extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("artifact or creature spell"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.ARTIFACT.getPredicate() + )); + } + + public VoraciousGreatshark(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.SHARK); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Voracious Greatshark enters the battlefield, counter target artifact or creature spell. + Ability ability = new EntersBattlefieldTriggeredAbility(new CounterTargetEffect()); + ability.addTarget(new TargetSpell(filter)); + this.addAbility(ability); + } + + private VoraciousGreatshark(final VoraciousGreatshark card) { + super(card); + } + + @Override + public VoraciousGreatshark copy() { + return new VoraciousGreatshark(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoraciousHydra.java b/Mage.Sets/src/mage/cards/v/VoraciousHydra.java index 374f5890f2..72b0fe566c 100644 --- a/Mage.Sets/src/mage/cards/v/VoraciousHydra.java +++ b/Mage.Sets/src/mage/cards/v/VoraciousHydra.java @@ -14,10 +14,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -29,12 +27,6 @@ import java.util.UUID; */ public final class VoraciousHydra extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public VoraciousHydra(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{G}{G}"); @@ -59,7 +51,7 @@ public final class VoraciousHydra extends CardImpl { new FightTargetSourceEffect() .setText("{this} fights target creature you don't control") ); - mode.addTarget(new TargetPermanent(filter)); + mode.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); ability.addMode(mode); this.addAbility(ability); } @@ -100,4 +92,4 @@ class VoraciousHydraEffect extends OneShotEffect { permanent.getCounters(game).getCount(CounterType.P1P1) ), source, game); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/v/VorinclexVoiceOfHunger.java b/Mage.Sets/src/mage/cards/v/VorinclexVoiceOfHunger.java index 4a63393979..1f3684a3c6 100644 --- a/Mage.Sets/src/mage/cards/v/VorinclexVoiceOfHunger.java +++ b/Mage.Sets/src/mage/cards/v/VorinclexVoiceOfHunger.java @@ -18,7 +18,6 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledLandPermanent; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -72,15 +71,18 @@ class VorinclexTriggeredAbility2 extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.TAPPED_FOR_MANA; + return event.getType() == GameEvent.EventType.TAPPED_FOR_MANA; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(controllerId).contains(event.getPlayerId())) { + if (game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA + return false; + } + if (game.getOpponents(getControllerId()).contains(event.getPlayerId())) { Permanent permanent = game.getPermanent(event.getSourceId()); if (permanent != null && permanent.isLand()) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/v/VortexElemental.java b/Mage.Sets/src/mage/cards/v/VortexElemental.java index 1194946fcd..5217d92b93 100644 --- a/Mage.Sets/src/mage/cards/v/VortexElemental.java +++ b/Mage.Sets/src/mage/cards/v/VortexElemental.java @@ -1,4 +1,3 @@ - package mage.cards.v; import java.util.HashSet; @@ -12,9 +11,11 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.MustBeBlockedByTargetSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.combat.Combat; @@ -30,7 +31,7 @@ import mage.target.common.TargetCreaturePermanent; public final class VortexElemental extends CardImpl { public VortexElemental(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); this.subtype.add(SubType.ELEMENTAL); this.power = new MageInt(0); @@ -80,7 +81,7 @@ class VortexElementalEffect extends OneShotEffect { Set playersToShuffle = new HashSet<>(); creaturesToReturn.add(source.getSourceId()); if (combat != null) { - for(CombatGroup combatGroup: combat.getGroups()) { + for (CombatGroup combatGroup : combat.getGroups()) { if (combatGroup.getAttackers().contains(source.getSourceId())) { creaturesToReturn.addAll(combatGroup.getBlockers()); } else if (combatGroup.getBlockers().contains(source.getSourceId())) { @@ -88,14 +89,15 @@ class VortexElementalEffect extends OneShotEffect { } } } - for (UUID creatureId: creaturesToReturn) { + for (UUID creatureId : creaturesToReturn) { Permanent creature = game.getPermanent(creatureId); if (creature != null) { playersToShuffle.add(creature.getControllerId()); - creature.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); } } - for (UUID playerId: playersToShuffle){ + Cards toLib = new CardsImpl(creaturesToReturn); + controller.putCardsOnTopOfLibrary(toLib, game, source, false); + for (UUID playerId : playersToShuffle) { Player player = game.getPlayer(playerId); if (player != null) { player.shuffleLibrary(source, game); @@ -105,7 +107,6 @@ class VortexElementalEffect extends OneShotEffect { return true; } - return false; } } diff --git a/Mage.Sets/src/mage/cards/v/VoyagerStaff.java b/Mage.Sets/src/mage/cards/v/VoyagerStaff.java index 2e14d389da..f25ac1c5c7 100644 --- a/Mage.Sets/src/mage/cards/v/VoyagerStaff.java +++ b/Mage.Sets/src/mage/cards/v/VoyagerStaff.java @@ -1,7 +1,5 @@ - package mage.cards.v; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -21,8 +19,9 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author fireshoes */ public final class VoyagerStaff extends CardImpl { @@ -66,7 +65,7 @@ class VoyagerStaffEffect extends OneShotEffect { if (controller != null && creature != null && sourcePermanent != null) { if (controller.moveCardToExileWithInfo(creature, source.getSourceId(), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, true); effect.setText("Return the exiled card to the battlefield under its owner's control at the beginning of the next end step"); effect.setTargetPointer(new FixedTarget(creature.getId(), game.getState().getZoneChangeCounter(creature.getId()))); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); diff --git a/Mage.Sets/src/mage/cards/v/VraskaRegalGorgon.java b/Mage.Sets/src/mage/cards/v/VraskaRegalGorgon.java index 8e35dd757a..c3a5bfc471 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaRegalGorgon.java +++ b/Mage.Sets/src/mage/cards/v/VraskaRegalGorgon.java @@ -39,10 +39,10 @@ public final class VraskaRegalGorgon extends CardImpl { // +2: Put a +1/+1 counter on up to one target creature. That creature gains menace until end of turn. Ability ability = new LoyaltyAbility(new AddCountersTargetEffect( CounterType.P1P1.createInstance() - ).setText("Put a +1/+1 counter on up to one target creature."), 2); + ).setText("Put a +1/+1 counter on up to one target creature"), 2); ability.addEffect(new GainAbilityTargetEffect( new MenaceAbility(), Duration.EndOfTurn - ).setText("That creature gains menace until end of turn.")); + ).setText("That creature gains menace until end of turn")); ability.addTarget(new TargetCreaturePermanent(0, 1)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/v/VraskaSchemingGorgon.java b/Mage.Sets/src/mage/cards/v/VraskaSchemingGorgon.java index dd25a8487d..6a7e023b2b 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaSchemingGorgon.java +++ b/Mage.Sets/src/mage/cards/v/VraskaSchemingGorgon.java @@ -1,12 +1,9 @@ - package mage.cards.v; -import java.util.UUID; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.LoseGameTargetPlayerEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; @@ -18,11 +15,11 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class VraskaSchemingGorgon extends CardImpl { @@ -35,9 +32,7 @@ public final class VraskaSchemingGorgon extends CardImpl { this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); // +2: Creatures you control get +1/+0 until end of turn. - Effect effect = new BoostControlledEffect(1, 0, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE); - effect.setText("Creatures you control get +1/+0"); - this.addAbility(new LoyaltyAbility(effect, 2)); + this.addAbility(new LoyaltyAbility(new BoostControlledEffect(1, 0, Duration.EndOfTurn), 2)); // -3: Destroy target creature. LoyaltyAbility loyaltyAbility = new LoyaltyAbility(new DestroyTargetEffect(), -3); @@ -53,7 +48,7 @@ public final class VraskaSchemingGorgon extends CardImpl { this.addAbility(loyaltyAbility); } - public VraskaSchemingGorgon(final VraskaSchemingGorgon card) { + private VraskaSchemingGorgon(final VraskaSchemingGorgon card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java b/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java index 172a230eaa..3cc609ca21 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java +++ b/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java @@ -86,7 +86,7 @@ class VraskaTheUnseenGainAbilityEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { - permanent.addAbility(ability, game); + permanent.addAbility(ability, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/v/VraskasConquistador.java b/Mage.Sets/src/mage/cards/v/VraskasConquistador.java index 43ec42ffa0..f7caaae0c3 100644 --- a/Mage.Sets/src/mage/cards/v/VraskasConquistador.java +++ b/Mage.Sets/src/mage/cards/v/VraskasConquistador.java @@ -1,14 +1,14 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbility; import mage.abilities.common.AttacksOrBlocksTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.hint.ConditionHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -16,8 +16,9 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class VraskasConquistador extends CardImpl { @@ -38,11 +39,13 @@ public final class VraskasConquistador extends CardImpl { this.toughness = new MageInt(1); // Whenever Vraska's Conquistador attacks or blocks, if you control a Vraska planeswalker, target opponent loses 2 life and you gain 2 life. + Condition condition = new PermanentsOnTheBattlefieldCondition(filter); TriggeredAbility ability = new AttacksOrBlocksTriggeredAbility(new LoseLifeTargetEffect(2), false); - ability.addEffect(new GainLifeEffect(2).setText("and you gain 2 life")); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); ability.addTarget(new TargetOpponent()); + ability.addHint(new ConditionHint(condition, "You control a Vraska planeswalker")); this.addAbility(new ConditionalInterveningIfTriggeredAbility( - ability, new PermanentsOnTheBattlefieldCondition(filter), + ability, condition, "Whenever {this} attacks or blocks, if you control a Vraska planeswalker, target opponent loses 2 life and you gain 2 life.")); } diff --git a/Mage.Sets/src/mage/cards/v/VrynWingmare.java b/Mage.Sets/src/mage/cards/v/VrynWingmare.java index e35cf3c18e..8ecdac26b4 100644 --- a/Mage.Sets/src/mage/cards/v/VrynWingmare.java +++ b/Mage.Sets/src/mage/cards/v/VrynWingmare.java @@ -1,31 +1,31 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; -import mage.game.Game; -import mage.util.CardUtil; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; + +import java.util.UUID; /** - * * @author fireshoes */ public final class VrynWingmare extends CardImpl { + private static final FilterCard filter = new FilterCard("Noncreature spells"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + public VrynWingmare(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.PEGASUS); @@ -36,7 +36,7 @@ public final class VrynWingmare extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Noncreature spells cost {1} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new VrynWingmareCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY))); } public VrynWingmare(final VrynWingmare card) { @@ -48,38 +48,3 @@ public final class VrynWingmare extends CardImpl { return new VrynWingmare(this); } } - -class VrynWingmareCostReductionEffect extends CostModificationEffectImpl { - - VrynWingmareCostReductionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - staticText = "Noncreature spells cost {1} more to cast"; - } - - VrynWingmareCostReductionEffect(VrynWingmareCostReductionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - CardUtil.increaseCost(abilityToModify, 1); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - Card card = game.getCard(abilityToModify.getSourceId()); - if (card != null && !card.isCreature()) { - return true; - } - } - return false; - } - - @Override - public VrynWingmareCostReductionEffect copy() { - return new VrynWingmareCostReductionEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/v/Vulpikeet.java b/Mage.Sets/src/mage/cards/v/Vulpikeet.java new file mode 100644 index 0000000000..3c49f051c4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/Vulpikeet.java @@ -0,0 +1,50 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MutateAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Vulpikeet extends CardImpl { + + public Vulpikeet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.FOX); + this.subtype.add(SubType.BIRD); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Mutate {2}{W} + this.addAbility(new MutateAbility(this, "{2}{W}")); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever this creature mutates, put a +1/+1 counter on it. + this.addAbility(new MutatesSourceTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it") + )); + } + + private Vulpikeet(final Vulpikeet card) { + super(card); + } + + @Override + public Vulpikeet copy() { + return new Vulpikeet(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java b/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java index d7e6015625..114c0fa813 100644 --- a/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java +++ b/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java @@ -14,7 +14,7 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.token.Token; -import mage.game.permanent.token.WaitingInTheWeedsCatToken; +import mage.game.permanent.token.GreenCatToken; import mage.players.Player; /** @@ -68,7 +68,7 @@ class WaitingInTheWeedsEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Token token = new WaitingInTheWeedsCatToken(); + Token token = new GreenCatToken(); int amount = game.getBattlefield().getAllActivePermanents(filter, playerId, game).size(); token.putOntoBattlefield(amount, game, source.getSourceId(), playerId); } diff --git a/Mage.Sets/src/mage/cards/w/WakeningSunsAvatar.java b/Mage.Sets/src/mage/cards/w/WakeningSunsAvatar.java index c80a09564e..2216a53e5f 100644 --- a/Mage.Sets/src/mage/cards/w/WakeningSunsAvatar.java +++ b/Mage.Sets/src/mage/cards/w/WakeningSunsAvatar.java @@ -4,7 +4,7 @@ package mage.cards.w; import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.CastFromHandSourceCondition; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DestroyAllEffect; import mage.cards.CardImpl; @@ -38,7 +38,7 @@ public final class WakeningSunsAvatar extends CardImpl { // When Wakening Sun's Avatar enters the battlefield, if you cast it from you hand, destroy all non-Dinosaur creatures. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter), false), - CastFromHandSourceCondition.instance, + CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, destroy all non-Dinosaur creatures."), new CastFromHandWatcher()); } diff --git a/Mage.Sets/src/mage/cards/w/WakerOfWaves.java b/Mage.Sets/src/mage/cards/w/WakerOfWaves.java new file mode 100644 index 0000000000..b402d3d56b --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WakerOfWaves.java @@ -0,0 +1,59 @@ + +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.DiscardSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author mikalinn777 + */ +public final class WakerOfWaves extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures your opponents control"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public WakerOfWaves(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); + this.subtype.add(SubType.WHALE); + + this.power = new MageInt(7); + this.toughness = new MageInt(7); + + // Creatures your opponents control get -1/-0. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(-1, -0, Duration.WhileOnBattlefield, filter, false))); + + // {1}{U}, Discard Waker of Waves: Look at the top two cards of your library. Put one of them into your hand and the other into your graveyard. + Ability ability = new SimpleActivatedAbility(Zone.HAND, new LookLibraryAndPickControllerEffect(StaticValue.get(2), false, StaticValue.get(1), + StaticFilters.FILTER_CARD, Zone.GRAVEYARD, false, false), new ManaCostsImpl("{1}{U}")); + ability.addCost(new DiscardSourceCost()); + this.addAbility(ability); + } + + public WakerOfWaves(final WakerOfWaves card) { + super(card); + } + + @Override + public WakerOfWaves copy() { + return new WakerOfWaves(this); + } +} + diff --git a/Mage.Sets/src/mage/cards/w/WalkingDream.java b/Mage.Sets/src/mage/cards/w/WalkingDream.java index 30cacb3dae..cd74d6f396 100644 --- a/Mage.Sets/src/mage/cards/w/WalkingDream.java +++ b/Mage.Sets/src/mage/cards/w/WalkingDream.java @@ -1,59 +1,59 @@ -package mage.cards.w; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.OpponentControlsPermanentCondition; -import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; -import mage.abilities.effects.ContinuousRuleModifyingEffect; -import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; -import mage.abilities.keyword.CantBeBlockedSourceAbility; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; - -/** - * - * @author jeffwadsworth - */ -public final class WalkingDream extends CardImpl { - - private static final String rule = "{this} doesn't untap during your untap step if an opponent controls two or more creatures."; - - public WalkingDream(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); - - this.subtype.add(SubType.ILLUSION); - this.power = new MageInt(3); - this.toughness = new MageInt(3); - - // Walking Dream is unblockable. - this.addAbility(new CantBeBlockedSourceAbility()); - - // Walking Dream doesn't untap during your untap step if an opponent controls two or more creatures. - ContinuousRuleModifyingEffect dontUntap = new DontUntapInControllersUntapStepSourceEffect(false, true); - dontUntap.setText(rule); - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, - new ConditionalContinuousRuleModifyingEffect( - dontUntap, - new OpponentControlsPermanentCondition( - new FilterCreaturePermanent(), - ComparisonType.MORE_THAN, 1))); - this.addAbility(ability); - - } - - public WalkingDream(final WalkingDream card) { - super(card); - } - - @Override - public WalkingDream copy() { - return new WalkingDream(this); - } -} +package mage.cards.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.OpponentControlsPermanentCondition; +import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; +import mage.abilities.effects.ContinuousRuleModifyingEffect; +import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; +import mage.abilities.keyword.CantBeBlockedSourceAbility; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; + +/** + * + * @author jeffwadsworth + */ +public final class WalkingDream extends CardImpl { + + private static final String rule = "{this} doesn't untap during your untap step if an opponent controls two or more creatures."; + + public WalkingDream(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.ILLUSION); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Walking Dream is unblockable. + this.addAbility(new CantBeBlockedSourceAbility()); + + // Walking Dream doesn't untap during your untap step if an opponent controls two or more creatures. + ContinuousRuleModifyingEffect dontUntap = new DontUntapInControllersUntapStepSourceEffect(false, true); + dontUntap.setText(rule); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousRuleModifyingEffect( + dontUntap, + new OpponentControlsPermanentCondition( + new FilterCreaturePermanent(), + ComparisonType.MORE_THAN, 1))); + this.addAbility(ability); + + } + + public WalkingDream(final WalkingDream card) { + super(card); + } + + @Override + public WalkingDream copy() { + return new WalkingDream(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WandOfDenial.java b/Mage.Sets/src/mage/cards/w/WandOfDenial.java index 5137e4b110..a30a6cd70b 100644 --- a/Mage.Sets/src/mage/cards/w/WandOfDenial.java +++ b/Mage.Sets/src/mage/cards/w/WandOfDenial.java @@ -69,7 +69,7 @@ class WandOfDenialEffect extends OneShotEffect { MageObject sourceObject = game.getObject(source.getSourceId()); controller.lookAtCards(sourceObject != null ? sourceObject.getName() : "", new CardsImpl(card), game); if (!card.isLand() - && controller.canPayLifeCost() + && controller.canPayLifeCost(source) && controller.getLife() >= 2 && controller.chooseUse(Outcome.Neutral, "Pay 2 life to put " + card.getLogName() + " into graveyard?", source, game)) { controller.loseLife(2, game, false); diff --git a/Mage.Sets/src/mage/cards/w/WantedScoundrels.java b/Mage.Sets/src/mage/cards/w/WantedScoundrels.java index 3c55abe065..df8e00ce74 100644 --- a/Mage.Sets/src/mage/cards/w/WantedScoundrels.java +++ b/Mage.Sets/src/mage/cards/w/WantedScoundrels.java @@ -4,7 +4,7 @@ package mage.cards.w; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -28,7 +28,7 @@ public final class WantedScoundrels extends CardImpl { this.toughness = new MageInt(3); // When Wanted Scoundrels dies, target opponent creates two colorless Treasure artifact tokens with "T, Sacrifice this artifact: Add one mana of any color." - Ability ability = new DiesTriggeredAbility(new CreateTokenTargetEffect(new TreasureToken(), 2), false); + Ability ability = new DiesSourceTriggeredAbility(new CreateTokenTargetEffect(new TreasureToken(), 2), false); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WarNameAspirant.java b/Mage.Sets/src/mage/cards/w/WarNameAspirant.java index f5739239d1..2ab466222d 100644 --- a/Mage.Sets/src/mage/cards/w/WarNameAspirant.java +++ b/Mage.Sets/src/mage/cards/w/WarNameAspirant.java @@ -1,26 +1,23 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleEvasionAbility; import mage.abilities.condition.common.RaidCondition; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.ComparisonType; -import mage.constants.Duration; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author emerald000 */ public final class WarNameAspirant extends CardImpl { @@ -39,11 +36,13 @@ public final class WarNameAspirant extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - // Raid — War-Name Aspirant enters the battlefield with a +1/+1 counter on it if you attacked with a creature this turn. + // Raid — War-Name Aspirant enters the battlefield with a +1/+1 counter on it if you attacked this turn. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1), false), - RaidCondition.instance, - "Raid — {this} enters the battlefield with a +1/+1 counter on it if you attacked with a creature this turn", - "{this} enters the battlefield with a +1/+1 counter"), + RaidCondition.instance, + "Raid — {this} enters the battlefield with a +1/+1 counter on it if you attacked this turn", + "{this} enters the battlefield with a +1/+1 counter") + .setAbilityWord(AbilityWord.RAID) + .addHint(RaidHint.instance), new PlayerAttackedWatcher()); // War-Name Aspirant can't be blocked by creatures with power 1 or less. diff --git a/Mage.Sets/src/mage/cards/w/WarbriarBlessing.java b/Mage.Sets/src/mage/cards/w/WarbriarBlessing.java index 35a72598d6..b5f4903853 100644 --- a/Mage.Sets/src/mage/cards/w/WarbriarBlessing.java +++ b/Mage.Sets/src/mage/cards/w/WarbriarBlessing.java @@ -12,10 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -27,12 +24,6 @@ import java.util.UUID; */ public final class WarbriarBlessing extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public WarbriarBlessing(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); @@ -47,7 +38,7 @@ public final class WarbriarBlessing extends CardImpl { // When Warbriar Blessing enters the battlefield, enchanted creature fights up to one target creature you don't control. ability = new EntersBattlefieldTriggeredAbility(new WarbriarBlessingEffect()); - ability.addTarget(new TargetPermanent(0, 1, filter, false)); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); this.addAbility(ability); // Enchanted creature gets +0/+2. diff --git a/Mage.Sets/src/mage/cards/w/Warbringer.java b/Mage.Sets/src/mage/cards/w/Warbringer.java index 4d40a5f31e..d678a0de71 100644 --- a/Mage.Sets/src/mage/cards/w/Warbringer.java +++ b/Mage.Sets/src/mage/cards/w/Warbringer.java @@ -9,6 +9,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.DashedCondition; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.abilities.keyword.DashAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -72,10 +73,7 @@ class WarbringerSpellsCostReductionEffect extends CostModificationEffectImpl { public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify instanceof SpellAbility) { if (abilityToModify.isControlledBy(source.getControllerId())) { - Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); - if (spell != null) { - return DashedCondition.instance.apply(game, abilityToModify); - } + return DashedCondition.instance.apply(game, abilityToModify); } } return false; diff --git a/Mage.Sets/src/mage/cards/w/WarclampMastiff.java b/Mage.Sets/src/mage/cards/w/WarclampMastiff.java index 80478a69b6..1235a7b540 100644 --- a/Mage.Sets/src/mage/cards/w/WarclampMastiff.java +++ b/Mage.Sets/src/mage/cards/w/WarclampMastiff.java @@ -17,7 +17,7 @@ public final class WarclampMastiff extends CardImpl { public WarclampMastiff(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/w/WardedBattlements.java b/Mage.Sets/src/mage/cards/w/WardedBattlements.java new file mode 100644 index 0000000000..db605fb69f --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WardedBattlements.java @@ -0,0 +1,46 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WardedBattlements extends CardImpl { + + public WardedBattlements(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.WALL); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Attacking creatures you control get +1/+0. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 0, Duration.WhileOnBattlefield, + StaticFilters.FILTER_ATTACKING_CREATURES + ))); + } + + private WardedBattlements(final WardedBattlements card) { + super(card); + } + + @Override + public WardedBattlements copy() { + return new WardedBattlements(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WardenOfEvosIsle.java b/Mage.Sets/src/mage/cards/w/WardenOfEvosIsle.java index d8f5556047..a3834c1c39 100644 --- a/Mage.Sets/src/mage/cards/w/WardenOfEvosIsle.java +++ b/Mage.Sets/src/mage/cards/w/WardenOfEvosIsle.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; @@ -14,20 +12,22 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.AbilityPredicate; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class WardenOfEvosIsle extends CardImpl { private static final FilterCard filter = new FilterCard("Creature spells with flying"); + static { filter.add(CardType.CREATURE.getPredicate()); filter.add(new AbilityPredicate(FlyingAbility.class)); } public WardenOfEvosIsle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.BIRD); this.subtype.add(SubType.WIZARD); @@ -36,6 +36,7 @@ public final class WardenOfEvosIsle extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Creature spells with flying you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); diff --git a/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java new file mode 100644 index 0000000000..4091621422 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java @@ -0,0 +1,48 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.BecomesTargetTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WardenOfTheWoods extends CardImpl { + + public WardenOfTheWoods(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.TREEFOLK); + this.power = new MageInt(5); + this.toughness = new MageInt(7); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards. + this.addAbility(new BecomesTargetTriggeredAbility( + new DrawCardSourceControllerEffect(2), + StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, + SetTargetPointer.NONE, true + )); + + } + + private WardenOfTheWoods(final WardenOfTheWoods card) { + super(card); + } + + @Override + public WardenOfTheWoods copy() { + return new WardenOfTheWoods(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WarpWorld.java b/Mage.Sets/src/mage/cards/w/WarpWorld.java index 08be3d7186..fa93b55655 100644 --- a/Mage.Sets/src/mage/cards/w/WarpWorld.java +++ b/Mage.Sets/src/mage/cards/w/WarpWorld.java @@ -97,7 +97,7 @@ class WarpWorldEffect extends OneShotEffect { } } - game.applyEffects(); // so effects from creatures that were on the battlefield won't trigger from draw or later put into play + game.getState().processAction(game); // so effects from creatures that were on the battlefield won't trigger from draw or later put into play Map cardsRevealed = new HashMap<>(); @@ -111,7 +111,7 @@ class WarpWorldEffect extends OneShotEffect { cardsRevealed.put(player.getId(), cards); } } - game.applyEffects(); + game.getState().processAction(game); // put artifacts, creaturs and lands onto the battlefield for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); @@ -129,7 +129,7 @@ class WarpWorldEffect extends OneShotEffect { player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game); } } - game.applyEffects(); + game.getState().processAction(game); // put enchantments onto the battlefield for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); diff --git a/Mage.Sets/src/mage/cards/w/WarrenScourgeElf.java b/Mage.Sets/src/mage/cards/w/WarrenScourgeElf.java index 2969aa1cfd..739f123d83 100644 --- a/Mage.Sets/src/mage/cards/w/WarrenScourgeElf.java +++ b/Mage.Sets/src/mage/cards/w/WarrenScourgeElf.java @@ -1,7 +1,6 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; @@ -10,13 +9,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterCard; +import java.util.UUID; + /** * * @author North */ public final class WarrenScourgeElf extends CardImpl { - private static final FilterCard filter = new FilterCard("Goblin"); + private static final FilterCard filter = new FilterCard("Goblins"); static { filter.add(SubType.GOBLIN.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/w/Watchdog.java b/Mage.Sets/src/mage/cards/w/Watchdog.java index f2fe75c581..e3abcaf92c 100644 --- a/Mage.Sets/src/mage/cards/w/Watchdog.java +++ b/Mage.Sets/src/mage/cards/w/Watchdog.java @@ -28,7 +28,7 @@ public final class Watchdog extends CardImpl { public Watchdog(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java b/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java new file mode 100644 index 0000000000..2e23709dbf --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java @@ -0,0 +1,64 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; + +import java.util.UUID; + +/** + * @author jmharmon + */ + +public final class WatcherOfTheSpheres extends CardImpl { + + private static final FilterCreatureCard filter = new FilterCreatureCard("Creature spells with flying"); + private static final FilterPermanent filter1 = new FilterControlledCreaturePermanent("another creature with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + filter1.add(AnotherPredicate.instance); + filter1.add(new AbilityPredicate(FlyingAbility.class)); + } + + public WatcherOfTheSpheres(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Creature spells with flying you cast cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); + + // Whenever another creature with flying enters the battlefield under your control, Watcher of the Spheres gets +1/+1 until end of turn. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn), filter1, false)); + } + + public WatcherOfTheSpheres(final WatcherOfTheSpheres card) { + super(card); + } + + @Override + public WatcherOfTheSpheres copy() { + return new WatcherOfTheSpheres(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java b/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java index 77496dc4f9..528d4f14ec 100644 --- a/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java +++ b/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java @@ -92,7 +92,7 @@ class WaveOfVitriolEffect extends OneShotEffect { } } } - game.applyEffects(); + game.getState().processAction(game); Cards toBattlefield = new CardsImpl(); Set playersToShuffle = new LinkedHashSet<>(); for (Map.Entry entry : sacrificedLands.entrySet()) { diff --git a/Mage.Sets/src/mage/cards/w/WaywardDisciple.java b/Mage.Sets/src/mage/cards/w/WaywardDisciple.java index 04e8904255..a0d2ca3990 100644 --- a/Mage.Sets/src/mage/cards/w/WaywardDisciple.java +++ b/Mage.Sets/src/mage/cards/w/WaywardDisciple.java @@ -1,11 +1,8 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; @@ -16,8 +13,9 @@ import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class WaywardDisciple extends CardImpl { @@ -29,7 +27,7 @@ public final class WaywardDisciple extends CardImpl { } public WaywardDisciple(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.CLERIC); this.power = new MageInt(2); @@ -41,10 +39,8 @@ public final class WaywardDisciple extends CardImpl { // Whenever Wayward Disciple or another creature you control dies, target opponent loses 1 life and you gain 1 life. Ability ability = new DiesThisOrAnotherCreatureTriggeredAbility(new LoseLifeTargetEffect(1), false, filter); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addTarget(new TargetOpponent()); - Effect effect = new GainLifeEffect(1); - effect.setText("and you gain 1 life"); - ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WeaponizeTheMonsters.java b/Mage.Sets/src/mage/cards/w/WeaponizeTheMonsters.java new file mode 100644 index 0000000000..34ac6e6341 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeaponizeTheMonsters.java @@ -0,0 +1,42 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WeaponizeTheMonsters extends CardImpl { + + public WeaponizeTheMonsters(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); + + // {2}, Sacrifice a creature: Weaponize the Monsters deals 2 damage to any target. + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(2), new GenericManaCost(2)); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent( + StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT + ))); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + } + + private WeaponizeTheMonsters(final WeaponizeTheMonsters card) { + super(card); + } + + @Override + public WeaponizeTheMonsters copy() { + return new WeaponizeTheMonsters(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeatherseedTreefolk.java b/Mage.Sets/src/mage/cards/w/WeatherseedTreefolk.java index 491122aa0e..8e70080ca5 100644 --- a/Mage.Sets/src/mage/cards/w/WeatherseedTreefolk.java +++ b/Mage.Sets/src/mage/cards/w/WeatherseedTreefolk.java @@ -3,7 +3,7 @@ package mage.cards.w; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; @@ -27,7 +27,7 @@ public final class WeatherseedTreefolk extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); // When Weatherseed Treefolk dies, return it to its owner's hand. - this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect())); } public WeatherseedTreefolk(final WeatherseedTreefolk card) { diff --git a/Mage.Sets/src/mage/cards/w/WelkinHawk.java b/Mage.Sets/src/mage/cards/w/WelkinHawk.java index 2cbd7fb5b2..6228cc3c94 100644 --- a/Mage.Sets/src/mage/cards/w/WelkinHawk.java +++ b/Mage.Sets/src/mage/cards/w/WelkinHawk.java @@ -3,7 +3,7 @@ package mage.cards.w; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -37,7 +37,7 @@ public final class WelkinHawk extends CardImpl { // When Welkin Hawk dies, you may search your library for a card named Welkin Hawk, reveal that card, put it into your hand, then shuffle your library. TargetCardInLibrary target = new TargetCardInLibrary(1, 1, filter); - this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInHandEffect(target, true, true), true)); + this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInHandEffect(target, true, true), true)); } public WelkinHawk(final WelkinHawk card) { diff --git a/Mage.Sets/src/mage/cards/w/WellOfKnowledge.java b/Mage.Sets/src/mage/cards/w/WellOfKnowledge.java index 96cd3c87fb..a054e2ced6 100644 --- a/Mage.Sets/src/mage/cards/w/WellOfKnowledge.java +++ b/Mage.Sets/src/mage/cards/w/WellOfKnowledge.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.condition.common.IsStepCondition; @@ -10,16 +8,13 @@ import mage.abilities.effects.Effects; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.EffectType; -import mage.constants.Outcome; -import mage.constants.PhaseStep; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author Styxo */ public final class WellOfKnowledge extends CardImpl { @@ -68,7 +63,7 @@ class WellOfKnowledgeConditionalActivatedAbility extends ActivatedAbilityImpl { && costs.canPay(this, sourceId, playerId, game) && game.isActivePlayer(playerId)) { this.activatorId = playerId; - return ActivationStatus.getTrue(); + return ActivationStatus.getTrue(this, game); } return ActivationStatus.getFalse(); @@ -105,7 +100,7 @@ class WellOfKnowledgeEffect extends OneShotEffect { if (source instanceof ActivatedAbilityImpl) { Player activator = game.getPlayer(((ActivatedAbilityImpl) source).getActivatorId()); if (activator != null) { - activator.drawCards(1, game); + activator.drawCards(1, source.getSourceId(), game); return true; } diff --git a/Mage.Sets/src/mage/cards/w/WellOfLostDreams.java b/Mage.Sets/src/mage/cards/w/WellOfLostDreams.java index 523c6df0cd..6b05181425 100644 --- a/Mage.Sets/src/mage/cards/w/WellOfLostDreams.java +++ b/Mage.Sets/src/mage/cards/w/WellOfLostDreams.java @@ -62,7 +62,7 @@ class WellOfLostDreamsEffect extends OneShotEffect { if (xValue > 0) { if (new GenericManaCost(xValue).pay(source, game, source.getSourceId(), controller.getId(), false)) { game.informPlayers(controller.getLogName() + " payed {" + xValue + '}'); - controller.drawCards(xValue, game); + controller.drawCards(xValue, source.getSourceId(), game); } else { return false; } diff --git a/Mage.Sets/src/mage/cards/w/Wellspring.java b/Mage.Sets/src/mage/cards/w/Wellspring.java index 56173302cc..01345b8d94 100644 --- a/Mage.Sets/src/mage/cards/w/Wellspring.java +++ b/Mage.Sets/src/mage/cards/w/Wellspring.java @@ -44,7 +44,7 @@ public final class Wellspring extends CardImpl { // At the beginning of your upkeep, untap enchanted land. You gain control of that land until end of turn. ability = new BeginningOfUpkeepTriggeredAbility( - new UntapEnchantedEffect().setText("untap enchanted land."), TargetController.YOU, false + new UntapEnchantedEffect().setText("untap enchanted land"), TargetController.YOU, false ); ability.addEffect(new WellspringEffect("You gain control of that land until end of turn")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/w/WhenFluffyBunniesAttack.java b/Mage.Sets/src/mage/cards/w/WhenFluffyBunniesAttack.java index 0b868b021b..b921adc884 100644 --- a/Mage.Sets/src/mage/cards/w/WhenFluffyBunniesAttack.java +++ b/Mage.Sets/src/mage/cards/w/WhenFluffyBunniesAttack.java @@ -86,7 +86,7 @@ class WhenFluffyBunniesAttackEffect extends OneShotEffect { } } BoostTargetEffect effect = new BoostTargetEffect(unboostValue, unboostValue, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/w/Whetstone.java b/Mage.Sets/src/mage/cards/w/Whetstone.java index b151b288e2..84f564588e 100644 --- a/Mage.Sets/src/mage/cards/w/Whetstone.java +++ b/Mage.Sets/src/mage/cards/w/Whetstone.java @@ -1,34 +1,30 @@ - package mage.cards.w; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; +import mage.constants.TargetController; + +import java.util.UUID; /** - * * @author Backfir3 */ public final class Whetstone extends CardImpl { public Whetstone(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); //{3}: Each player puts the top two cards of their library into their graveyard. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new WhetstoneEffect(), new ManaCostsImpl("{3}")); - this.addAbility(ability); + this.addAbility(new SimpleActivatedAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect( + 2, TargetController.ANY + ), new GenericManaCost(3))); } - public Whetstone(final Whetstone card) { + private Whetstone(final Whetstone card) { super(card); } @@ -37,31 +33,3 @@ public final class Whetstone extends CardImpl { return new Whetstone(this); } } - -class WhetstoneEffect extends OneShotEffect { - - WhetstoneEffect() { - super(Outcome.Detriment); - staticText = "Each player puts the top two cards of their library into their graveyard"; - } - - WhetstoneEffect(final WhetstoneEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.moveCards(player.getLibrary().getTopCards(game, 2), Zone.GRAVEYARD, source, game); - } - } - return true; - } - - @Override - public WhetstoneEffect copy() { - return new WhetstoneEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/w/WhiplashTrap.java b/Mage.Sets/src/mage/cards/w/WhiplashTrap.java index 26be4131c9..3fc89da184 100644 --- a/Mage.Sets/src/mage/cards/w/WhiplashTrap.java +++ b/Mage.Sets/src/mage/cards/w/WhiplashTrap.java @@ -31,7 +31,7 @@ public final class WhiplashTrap extends CardImpl { this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{U}"), WhiplashTrapCondition.instance), new PermanentsEnteredBattlefieldWatcher()); // Return two target creatures to their owners' hands. - this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect().setText("return two target creatures to their owners' hands")); this.getSpellAbility().addTarget(new TargetCreaturePermanent(2)); } diff --git a/Mage.Sets/src/mage/cards/w/WhirlpoolDrake.java b/Mage.Sets/src/mage/cards/w/WhirlpoolDrake.java index 35f578eccf..73d9dea2dd 100644 --- a/Mage.Sets/src/mage/cards/w/WhirlpoolDrake.java +++ b/Mage.Sets/src/mage/cards/w/WhirlpoolDrake.java @@ -3,7 +3,7 @@ package mage.cards.w; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.ShuffleHandIntoLibraryDrawThatManySourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -31,7 +31,7 @@ public final class WhirlpoolDrake extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new ShuffleHandIntoLibraryDrawThatManySourceEffect(), false)); // When Whirlpool Drake dies, shuffle the cards from your hand into your library, then draw that many cards. - this.addAbility(new DiesTriggeredAbility(new ShuffleHandIntoLibraryDrawThatManySourceEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new ShuffleHandIntoLibraryDrawThatManySourceEffect(), false)); } public WhirlpoolDrake(final WhirlpoolDrake card) { diff --git a/Mage.Sets/src/mage/cards/w/WhirlpoolWarrior.java b/Mage.Sets/src/mage/cards/w/WhirlpoolWarrior.java index 158017976c..aeef381aef 100644 --- a/Mage.Sets/src/mage/cards/w/WhirlpoolWarrior.java +++ b/Mage.Sets/src/mage/cards/w/WhirlpoolWarrior.java @@ -90,7 +90,7 @@ class WhirlpoolWarriorActivatedEffect extends OneShotEffect { for (Entry entry : playerCards.entrySet()) { Player player = game.getPlayer(entry.getKey()); if (player != null) { - player.drawCards(entry.getValue(), game); + player.drawCards(entry.getValue(), source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/w/WhirlwindOfThought.java b/Mage.Sets/src/mage/cards/w/WhirlwindOfThought.java new file mode 100644 index 0000000000..2c7b69aaa6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WhirlwindOfThought.java @@ -0,0 +1,35 @@ +package mage.cards.w; + +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WhirlwindOfThought extends CardImpl { + + public WhirlwindOfThought(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}{R}{W}"); + + // Whenever you cast a noncreature spell, draw a card. + this.addAbility(new SpellCastControllerTriggeredAbility( + new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + )); + } + + private WhirlwindOfThought(final WhirlwindOfThought card) { + super(card); + } + + @Override + public WhirlwindOfThought copy() { + return new WhirlwindOfThought(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WhiskAway.java b/Mage.Sets/src/mage/cards/w/WhiskAway.java index ebc9e133a5..d3a3c7336f 100644 --- a/Mage.Sets/src/mage/cards/w/WhiskAway.java +++ b/Mage.Sets/src/mage/cards/w/WhiskAway.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -8,10 +7,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.common.FilterAttackingOrBlockingCreature; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; /** @@ -19,11 +18,11 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class WhiskAway extends CardImpl { - + private static final FilterAttackingOrBlockingCreature filter = new FilterAttackingOrBlockingCreature(); public WhiskAway(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); // Put target attacking or blocking creature on top of its owner's library. this.getSpellAbility().addEffect(new WhiskAwayEffect()); @@ -53,10 +52,10 @@ class WhiskAwayEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source)); - if (targetCreature != null) { - targetCreature.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - return true; + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + Player controller = game.getPlayer(source.getControllerId()); + if (targetCreature != null && controller != null) { + return controller.putCardsOnTopOfLibrary(targetCreature, game, source, true); } return false; } @@ -65,4 +64,4 @@ class WhiskAwayEffect extends OneShotEffect { public WhiskAwayEffect copy() { return new WhiskAwayEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/w/WhisperSquad.java b/Mage.Sets/src/mage/cards/w/WhisperSquad.java new file mode 100644 index 0000000000..8287ccb25e --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WhisperSquad.java @@ -0,0 +1,50 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WhisperSquad extends CardImpl { + + private static final FilterCard filter = new FilterCard("a card named Whisper Squad"); + + static { + filter.add(new NamePredicate("Whisper Squad")); + } + + public WhisperSquad(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {1}{B}: Search your library for a card named Whisper Squad, put it onto the battlefield tapped, then shuffle your library. + this.addAbility(new SimpleActivatedAbility(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(filter), true, true + ), new ManaCostsImpl("{1}{B}"))); + } + + private WhisperSquad(final WhisperSquad card) { + super(card); + } + + @Override + public WhisperSquad copy() { + return new WhisperSquad(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WhisperingMadness.java b/Mage.Sets/src/mage/cards/w/WhisperingMadness.java index 36ec992422..eb04fd9fa2 100644 --- a/Mage.Sets/src/mage/cards/w/WhisperingMadness.java +++ b/Mage.Sets/src/mage/cards/w/WhisperingMadness.java @@ -1,11 +1,8 @@ - package mage.cards.w; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CipherEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -13,23 +10,24 @@ import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class WhisperingMadness extends CardImpl { public WhisperingMadness(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{B}"); // Each player discards their hand, then draws cards equal to the greatest number of cards a player discarded this way. this.getSpellAbility().addEffect(new WhisperingMadnessEffect()); + // Cipher this.getSpellAbility().addEffect(new CipherEffect()); } - public WhisperingMadness(final WhisperingMadness card) { + private WhisperingMadness(final WhisperingMadness card) { super(card); } @@ -45,38 +43,33 @@ class WhisperingMadnessEffect extends OneShotEffect { staticText = "Each player discards their hand, then draws cards equal to the greatest number of cards a player discarded this way"; } - WhisperingMadnessEffect(final WhisperingMadnessEffect effect) { + private WhisperingMadnessEffect(final WhisperingMadnessEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { int maxDiscarded = 0; - Player sourcePlayer = game.getPlayer(source.getControllerId()); - if (sourcePlayer == null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } - for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) { + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); - if (player != null) { - int discarded = 0; - for (Card c : player.getHand().getCards(game)) { - if (player.discard(c, source, game)) { - discarded++; - } - } - if (discarded > maxDiscarded) { - maxDiscarded = discarded; - } + if (player == null) { + continue; + } + int discarded = player.discard(player.getHand(), source, game).size(); + if (discarded > maxDiscarded) { + maxDiscarded = discarded; } } - for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) { + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(maxDiscarded, game); + player.drawCards(maxDiscarded, source.getSourceId(), game); } } - return true; } @@ -84,4 +77,4 @@ class WhisperingMadnessEffect extends OneShotEffect { public WhisperingMadnessEffect copy() { return new WhisperingMadnessEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java b/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java index 52b1ca830c..fd8ff5063b 100644 --- a/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java +++ b/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.Effect; @@ -46,7 +46,7 @@ public final class WhisperwoodElemental extends CardImpl { this.addAbility(new BeginningOfEndStepTriggeredAbility(new ManifestEffect(1), TargetController.YOU, false)); // Sacrifice Whisperwood Elemental: Until end of turn, face-up, nontoken creatures you control gain "When this creature dies, manifest the top card of your library." - Ability abilityToGain = new DiesTriggeredAbility(new ManifestEffect(1)); + Ability abilityToGain = new DiesSourceTriggeredAbility(new ManifestEffect(1)); Effect effect = new GainAbilityControlledEffect(abilityToGain, Duration.EndOfTurn, filter); effect.setText("Until end of turn, face-up, nontoken creatures you control gain \"When this creature dies, manifest the top card of your library.\""); this.addAbility(new SimpleActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/w/WhiteManaBattery.java b/Mage.Sets/src/mage/cards/w/WhiteManaBattery.java index b08a197b18..ab03331163 100644 --- a/Mage.Sets/src/mage/cards/w/WhiteManaBattery.java +++ b/Mage.Sets/src/mage/cards/w/WhiteManaBattery.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -28,18 +27,20 @@ public final class WhiteManaBattery extends CardImpl { public WhiteManaBattery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {2}, {tap}: Put a charge counter on White Mana Battery. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance(1)), new GenericManaCost(2)); + // {2}, {T}: Put a charge counter on White Mana Battery. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1)), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); this.addAbility(ability); - // {tap}, Remove any number of charge counters from White Mana Battery: Add {W}, then add an additional {W} for each charge counter removed this way. + // {T}, Remove any number of charge counters from White Mana Battery: Add {W}, + // then add an additional {W} for each charge counter removed this way. ability = new DynamicManaAbility( Mana.WhiteMana(1), new IntPlusDynamicValue(1, RemovedCountersForCostValue.instance), new TapSourceCost(), "Add {W}, then add {W} for each charge counter removed this way", - true, new CountersSourceCount(CounterType.CHARGE)); + true, new IntPlusDynamicValue(1, new CountersSourceCount(CounterType.CHARGE))); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE.createInstance(), "Remove any number of charge counters from {this}")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/w/WickedGuardian.java b/Mage.Sets/src/mage/cards/w/WickedGuardian.java index 23136034fb..946cbb96ff 100644 --- a/Mage.Sets/src/mage/cards/w/WickedGuardian.java +++ b/Mage.Sets/src/mage/cards/w/WickedGuardian.java @@ -87,6 +87,6 @@ class WickedGuardianEffect extends OneShotEffect { return false; } permanent.damage(2, source.getSourceId(), game); - return player.drawCards(1, game) > 0; + return player.drawCards(1, source.getSourceId(), game) > 0; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/w/WickedWolf.java b/Mage.Sets/src/mage/cards/w/WickedWolf.java index 917d7bd0b0..9661a39126 100644 --- a/Mage.Sets/src/mage/cards/w/WickedWolf.java +++ b/Mage.Sets/src/mage/cards/w/WickedWolf.java @@ -15,11 +15,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; import mage.target.common.TargetControlledPermanent; @@ -30,15 +28,9 @@ import java.util.UUID; */ public final class WickedWolf extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("creature you don't control"); - private static final FilterControlledPermanent filter2 + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.FOOD, "a Food"); - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - public WickedWolf(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); @@ -50,17 +42,17 @@ public final class WickedWolf extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility( new FightTargetSourceEffect().setText("it fights up to one target creature you don't control") ); - ability.addTarget(new TargetPermanent(0, 1, filter, false)); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); this.addAbility(ability); // Sacrifice a Food: Put a +1/+1 counter on Wicked Wolf. It gains indestructible until end of turn. Tap it. ability = new SimpleActivatedAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance()), - new SacrificeTargetCost(new TargetControlledPermanent(filter2)) + new SacrificeTargetCost(new TargetControlledPermanent(filter)) ); ability.addEffect(new GainAbilitySourceEffect( IndestructibleAbility.getInstance(), Duration.EndOfTurn - ).setText("it gains indestructible until end of turn.")); + ).setText("it gains indestructible until end of turn")); ability.addEffect(new TapSourceEffect().setText("Tap it")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WidespreadBrutality.java b/Mage.Sets/src/mage/cards/w/WidespreadBrutality.java index bc301ae444..5cdb9dd9f1 100644 --- a/Mage.Sets/src/mage/cards/w/WidespreadBrutality.java +++ b/Mage.Sets/src/mage/cards/w/WidespreadBrutality.java @@ -61,7 +61,7 @@ class WidespreadBrutalityEffect extends OneShotEffect { if (amassedArmy == null) { return false; } - game.applyEffects(); + game.getState().processAction(game); int power = amassedArmy.getPower().getValue(); for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { if (permanent != null && permanent.isCreature() && !permanent.hasSubtype(SubType.ARMY, game)) { diff --git a/Mage.Sets/src/mage/cards/w/Wiitigo.java b/Mage.Sets/src/mage/cards/w/Wiitigo.java index 74ba69202a..ce0ba21ec0 100644 --- a/Mage.Sets/src/mage/cards/w/Wiitigo.java +++ b/Mage.Sets/src/mage/cards/w/Wiitigo.java @@ -1,123 +1,123 @@ -package mage.cards.w; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import mage.MageInt; -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; -import mage.constants.SubType; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.TargetController; -import mage.constants.WatcherScope; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.watchers.Watcher; - -/** - * - * @author jeffwadsworth - */ -public final class Wiitigo extends CardImpl { - - public Wiitigo(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}{G}"); - - this.subtype.add(SubType.YETI); - this.power = new MageInt(0); - this.toughness = new MageInt(0); - - // Wiitigo enters the battlefield with six +1/+1 counters on it. - this.addAbility(new EntersBattlefieldAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance(6)))); - - // At the beginning of your upkeep, put a +1/+1 counter on Wiitigo if it has blocked or been blocked since your last upkeep. Otherwise, remove a +1/+1 counter from it. - Ability triggeredAbility = new BeginningOfUpkeepTriggeredAbility( - new ConditionalOneShotEffect( - new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), - new RemoveCounterSourceEffect(CounterType.P1P1.createInstance(1)), - new BlockedOrBeenBlockedSinceYourLastUpkeepCondition(), - "put a +1/+1 counter on enchanted creature if it blocked or been blocked since your last " - + "upkeep. Otherwise, remove a +1/+1 counter from it"), - TargetController.YOU, - false); - triggeredAbility.addWatcher(new BlockedOrBeenBlockedSinceYourLastUpkeepWatcher()); - this.addAbility(triggeredAbility); - } - - private Wiitigo(final Wiitigo card) { - super(card); - } - - @Override - public Wiitigo copy() { - return new Wiitigo(this); - } -} - -class BlockedOrBeenBlockedSinceYourLastUpkeepCondition implements Condition { - - @Override - public boolean apply(Game game, Ability source) { - Permanent wiitigo = game.getBattlefield().getPermanent(source.getSourceId()); - BlockedOrBeenBlockedSinceYourLastUpkeepWatcher watcher = game.getState().getWatcher( - BlockedOrBeenBlockedSinceYourLastUpkeepWatcher.class); - if (wiitigo != null - && watcher != null) { - return watcher.blockedOrBeenBlockedSinceLastUpkeep( - new MageObjectReference(wiitigo.getId(), - wiitigo.getZoneChangeCounter(game), - game), - wiitigo.getControllerId()); - } - return false; - } - - @Override - public String toString() { - return "it blocked or was blocked since your last upkeep"; - } -} - -class BlockedOrBeenBlockedSinceYourLastUpkeepWatcher extends Watcher { - - private final Map> blockedOrBeenBlockedCreatures = new HashMap<>(); - - public BlockedOrBeenBlockedSinceYourLastUpkeepWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.UPKEEP_STEP_POST) { - blockedOrBeenBlockedCreatures.put(event.getPlayerId(), new HashSet<>()); // clear the watcher after upkeep - } else if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) { - MageObjectReference morBlocker = new MageObjectReference(event.getSourceId(), game); // store blocker - MageObjectReference morAttackerBlocked = new MageObjectReference(event.getTargetId(), game); // store attacker blocked - for (UUID player : game.getPlayerList()) { - if (!blockedOrBeenBlockedCreatures.containsKey(player)) { - blockedOrBeenBlockedCreatures.put(player, new HashSet<>()); - } - blockedOrBeenBlockedCreatures.get(player).add(morBlocker); - blockedOrBeenBlockedCreatures.get(player).add(morAttackerBlocked); - } - } - } - - public boolean blockedOrBeenBlockedSinceLastUpkeep(MageObjectReference mor, UUID player) { - return (blockedOrBeenBlockedCreatures.get(player) != null) - && blockedOrBeenBlockedCreatures.get(player).contains(mor); - } -} +package mage.cards.w; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.WatcherScope; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +/** + * + * @author jeffwadsworth + */ +public final class Wiitigo extends CardImpl { + + public Wiitigo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}{G}"); + + this.subtype.add(SubType.YETI); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Wiitigo enters the battlefield with six +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(6)))); + + // At the beginning of your upkeep, put a +1/+1 counter on Wiitigo if it has blocked or been blocked since your last upkeep. Otherwise, remove a +1/+1 counter from it. + Ability triggeredAbility = new BeginningOfUpkeepTriggeredAbility( + new ConditionalOneShotEffect( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), + new RemoveCounterSourceEffect(CounterType.P1P1.createInstance(1)), + new BlockedOrBeenBlockedSinceYourLastUpkeepCondition(), + "put a +1/+1 counter on enchanted creature if it blocked or been blocked since your last " + + "upkeep. Otherwise, remove a +1/+1 counter from it"), + TargetController.YOU, + false); + triggeredAbility.addWatcher(new BlockedOrBeenBlockedSinceYourLastUpkeepWatcher()); + this.addAbility(triggeredAbility); + } + + private Wiitigo(final Wiitigo card) { + super(card); + } + + @Override + public Wiitigo copy() { + return new Wiitigo(this); + } +} + +class BlockedOrBeenBlockedSinceYourLastUpkeepCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + Permanent wiitigo = game.getBattlefield().getPermanent(source.getSourceId()); + BlockedOrBeenBlockedSinceYourLastUpkeepWatcher watcher = game.getState().getWatcher( + BlockedOrBeenBlockedSinceYourLastUpkeepWatcher.class); + if (wiitigo != null + && watcher != null) { + return watcher.blockedOrBeenBlockedSinceLastUpkeep( + new MageObjectReference(wiitigo.getId(), + wiitigo.getZoneChangeCounter(game), + game), + wiitigo.getControllerId()); + } + return false; + } + + @Override + public String toString() { + return "it blocked or was blocked since your last upkeep"; + } +} + +class BlockedOrBeenBlockedSinceYourLastUpkeepWatcher extends Watcher { + + private final Map> blockedOrBeenBlockedCreatures = new HashMap<>(); + + public BlockedOrBeenBlockedSinceYourLastUpkeepWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.UPKEEP_STEP_POST) { + blockedOrBeenBlockedCreatures.put(event.getPlayerId(), new HashSet<>()); // clear the watcher after upkeep + } else if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) { + MageObjectReference morBlocker = new MageObjectReference(event.getSourceId(), game); // store blocker + MageObjectReference morAttackerBlocked = new MageObjectReference(event.getTargetId(), game); // store attacker blocked + for (UUID player : game.getPlayerList()) { + if (!blockedOrBeenBlockedCreatures.containsKey(player)) { + blockedOrBeenBlockedCreatures.put(player, new HashSet<>()); + } + blockedOrBeenBlockedCreatures.get(player).add(morBlocker); + blockedOrBeenBlockedCreatures.get(player).add(morAttackerBlocked); + } + } + } + + public boolean blockedOrBeenBlockedSinceLastUpkeep(MageObjectReference mor, UUID player) { + return (blockedOrBeenBlockedCreatures.get(player) != null) + && blockedOrBeenBlockedCreatures.get(player).contains(mor); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WildDogs.java b/Mage.Sets/src/mage/cards/w/WildDogs.java index 6d5b91cd19..58202239c7 100644 --- a/Mage.Sets/src/mage/cards/w/WildDogs.java +++ b/Mage.Sets/src/mage/cards/w/WildDogs.java @@ -30,7 +30,7 @@ public final class WildDogs extends CardImpl { public WildDogs(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/w/WildMongrel.java b/Mage.Sets/src/mage/cards/w/WildMongrel.java index 861fc78d4c..004c7b44fa 100644 --- a/Mage.Sets/src/mage/cards/w/WildMongrel.java +++ b/Mage.Sets/src/mage/cards/w/WildMongrel.java @@ -24,7 +24,7 @@ public final class WildMongrel extends CardImpl { public WildMongrel(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/w/WildPair.java b/Mage.Sets/src/mage/cards/w/WildPair.java index ad3dab657c..0676c3ae30 100644 --- a/Mage.Sets/src/mage/cards/w/WildPair.java +++ b/Mage.Sets/src/mage/cards/w/WildPair.java @@ -75,7 +75,7 @@ class WildPairEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { int totalPT = permanent.getPower().getValue() + permanent.getToughness().getValue(); FilterCreatureCard filter = new FilterCreatureCard("creature card with total power and toughness " + totalPT); diff --git a/Mage.Sets/src/mage/cards/w/WildbornPreserver.java b/Mage.Sets/src/mage/cards/w/WildbornPreserver.java index 0fe9b9256a..49ba457eaf 100644 --- a/Mage.Sets/src/mage/cards/w/WildbornPreserver.java +++ b/Mage.Sets/src/mage/cards/w/WildbornPreserver.java @@ -2,8 +2,8 @@ package mage.cards.w; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; @@ -14,7 +14,6 @@ import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; import mage.counters.CounterType; @@ -23,7 +22,6 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.players.Player; import java.util.UUID; @@ -74,11 +72,11 @@ class WildbornPreserverCreateReflexiveTriggerEffect extends OneShotEffect { WildbornPreserverCreateReflexiveTriggerEffect() { super(Outcome.Benefit); + staticText = "you may pay {X}. When you do, put X +1/+1 counters on {this}"; } private WildbornPreserverCreateReflexiveTriggerEffect(final WildbornPreserverCreateReflexiveTriggerEffect effect) { super(effect); - staticText = "you may pay {X}. When you do, put X +1/+1 counters on {this}"; } @Override @@ -101,40 +99,10 @@ class WildbornPreserverCreateReflexiveTriggerEffect extends OneShotEffect { if (!cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { return false; } - game.addDelayedTriggeredAbility(new WildbornPreserverReflexiveTriggeredAbility(costX), source); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId(), 0)); + game.fireReflexiveTriggeredAbility(new ReflexiveTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(costX)), + false, "put X +1/+1 counters on {this}" + ), source); return true; } } - -class WildbornPreserverReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - WildbornPreserverReflexiveTriggeredAbility(int counters) { - super(new AddCountersSourceEffect(CounterType.P1P1.createInstance(counters)), Duration.OneUse, true); - } - - private WildbornPreserverReflexiveTriggeredAbility(final WildbornPreserverReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public WildbornPreserverReflexiveTriggeredAbility copy() { - return new WildbornPreserverReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you do, put X +1/+1 counters on {this}."; - } -} diff --git a/Mage.Sets/src/mage/cards/w/WildfireCerberus.java b/Mage.Sets/src/mage/cards/w/WildfireCerberus.java index 33bedaa6ae..109b94a19a 100644 --- a/Mage.Sets/src/mage/cards/w/WildfireCerberus.java +++ b/Mage.Sets/src/mage/cards/w/WildfireCerberus.java @@ -30,7 +30,7 @@ public final class WildfireCerberus extends CardImpl { public WildfireCerberus(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(4); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/w/WildwoodPatrol.java b/Mage.Sets/src/mage/cards/w/WildwoodPatrol.java new file mode 100644 index 0000000000..e0ab07f65b --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WildwoodPatrol.java @@ -0,0 +1,37 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WildwoodPatrol extends CardImpl { + + public WildwoodPatrol(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.CENTAUR); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + } + + private WildwoodPatrol(final WildwoodPatrol card) { + super(card); + } + + @Override + public WildwoodPatrol copy() { + return new WildwoodPatrol(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WildwoodScourge.java b/Mage.Sets/src/mage/cards/w/WildwoodScourge.java new file mode 100644 index 0000000000..737b4d5b49 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WildwoodScourge.java @@ -0,0 +1,89 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WildwoodScourge extends CardImpl { + + public WildwoodScourge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{G}"); + + this.subtype.add(SubType.HYDRA); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Wildwood Scourge enters the battlefield with X +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); + + // Whenever one or more +1/+1 counters are put on another non-Hydra creature you control, put a +1/+1 counter on Wildwood Scourge. + this.addAbility(new EnduringScalelordTriggeredAbility()); + } + + private WildwoodScourge(final WildwoodScourge card) { + super(card); + } + + @Override + public WildwoodScourge copy() { + return new WildwoodScourge(this); + } +} + +class EnduringScalelordTriggeredAbility extends TriggeredAbilityImpl { + + EnduringScalelordTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); + } + + private EnduringScalelordTriggeredAbility(final EnduringScalelordTriggeredAbility ability) { + super(ability); + } + + @Override + public EnduringScalelordTriggeredAbility copy() { + return new EnduringScalelordTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTERS_ADDED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getData().equals(CounterType.P1P1.getName())) { + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent == null) { + permanent = game.getPermanentEntering(event.getTargetId()); + } + return (permanent != null + && !event.getTargetId().equals(this.getSourceId()) + && permanent.isCreature() + && !permanent.getSubtype(game).contains(SubType.HYDRA) + && permanent.isControlledBy(this.getControllerId())); + } + return false; + } + + @Override + public String getRule() { + return "Whenever one or more +1/+1 counters are put on another non-Hydra creature you control, put a +1/+1 counter on {this}."; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WillKenrith.java b/Mage.Sets/src/mage/cards/w/WillKenrith.java index 8af6ce1385..e50d290473 100644 --- a/Mage.Sets/src/mage/cards/w/WillKenrith.java +++ b/Mage.Sets/src/mage/cards/w/WillKenrith.java @@ -89,7 +89,7 @@ class WillKenrithCostReductionEffect extends OneShotEffect { WillKenrithCostReductionEffect() { super(Outcome.Benefit); - this.staticText = "Until your next turn, instant, sorcery, and planeswalker spells that player casts cost {2} less to cast."; + this.staticText = "Until your next turn, instant, sorcery, and planeswalker spells that player casts cost {2} less to cast"; } WillKenrithCostReductionEffect(final WillKenrithCostReductionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/w/WillOfTheAllHunter.java b/Mage.Sets/src/mage/cards/w/WillOfTheAllHunter.java new file mode 100644 index 0000000000..1921afa4bc --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WillOfTheAllHunter.java @@ -0,0 +1,74 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WillOfTheAllHunter extends CardImpl { + + public WillOfTheAllHunter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Target creature gets +2/+2 until end of turn. If it's blocking, instead put two +1/+1 counters on it. + this.getSpellAbility().addEffect(new WillOfTheAllHunterEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private WillOfTheAllHunter(final WillOfTheAllHunter card) { + super(card); + } + + @Override + public WillOfTheAllHunter copy() { + return new WillOfTheAllHunter(this); + } +} + +class WillOfTheAllHunterEffect extends OneShotEffect { + + WillOfTheAllHunterEffect() { + super(Outcome.Benefit); + staticText = "Target creature gets +2/+2 until end of turn. " + + "If it's blocking, instead put two +1/+1 counters on it."; + } + + private WillOfTheAllHunterEffect(final WillOfTheAllHunterEffect effect) { + super(effect); + } + + @Override + public WillOfTheAllHunterEffect copy() { + return new WillOfTheAllHunterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + if (permanent.getBlocking() > 0) { + return permanent.addCounters(CounterType.P1P1.createInstance(2), source, game); + } + game.addEffect(new BoostTargetEffect(2, 2), source); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/w/Willbreaker.java b/Mage.Sets/src/mage/cards/w/Willbreaker.java index 0c1faff0ba..3a34901480 100644 --- a/Mage.Sets/src/mage/cards/w/Willbreaker.java +++ b/Mage.Sets/src/mage/cards/w/Willbreaker.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -11,8 +10,8 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -20,6 +19,7 @@ import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.LostControlWatcher; /** * @@ -28,7 +28,7 @@ import mage.target.targetpointer.FixedTarget; public final class Willbreaker extends CardImpl { public Willbreaker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); this.power = new MageInt(2); @@ -37,7 +37,7 @@ public final class Willbreaker extends CardImpl { // Whenever a creature an opponent controls becomes the target of a spell or ability you control, gain control of that creature for as long as you control Willbreaker. ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), new SourceOnBattlefieldControlUnchangedCondition(), null); effect.setText("gain control of that creature for as long as you control {this}"); - this.addAbility(new WillbreakerTriggeredAbility(effect)); + this.addAbility(new WillbreakerTriggeredAbility(effect), new LostControlWatcher()); } public Willbreaker(final Willbreaker card) { diff --git a/Mage.Sets/src/mage/cards/w/WillowSatyr.java b/Mage.Sets/src/mage/cards/w/WillowSatyr.java index 55c3eadd1d..fdc4d91a6b 100644 --- a/Mage.Sets/src/mage/cards/w/WillowSatyr.java +++ b/Mage.Sets/src/mage/cards/w/WillowSatyr.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -15,19 +14,20 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.LostControlWatcher; /** * * @author fireshoes */ public final class WillowSatyr extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("legendary creature"); static { @@ -35,7 +35,7 @@ public final class WillowSatyr extends CardImpl { } public WillowSatyr(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); this.subtype.add(SubType.SATYR); this.power = new MageInt(1); this.toughness = new MageInt(1); @@ -44,10 +44,11 @@ public final class WillowSatyr extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {tap}: Gain control of target legendary creature for as long as you control Willow Satyr and Willow Satyr remains tapped. ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), new CompoundCondition(SourceTappedCondition.instance, new SourceOnBattlefieldControlUnchangedCondition()), - "Gain control of target legendary creature for as long as you control {this} and {this} remains tapped"); + new GainControlTargetEffect(Duration.Custom), new CompoundCondition(SourceTappedCondition.instance, new SourceOnBattlefieldControlUnchangedCondition()), + "Gain control of target legendary creature for as long as you control {this} and {this} remains tapped"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/Wilt.java b/Mage.Sets/src/mage/cards/w/Wilt.java new file mode 100644 index 0000000000..4a44f03293 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/Wilt.java @@ -0,0 +1,38 @@ +package mage.cards.w; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Wilt extends CardImpl { + + public Wilt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Destroy target artifact or enchantment. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private Wilt(final Wilt card) { + super(card); + } + + @Override + public Wilt copy() { + return new Wilt(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/Windfall.java b/Mage.Sets/src/mage/cards/w/Windfall.java index ed5d20aed7..b7c641a9a4 100644 --- a/Mage.Sets/src/mage/cards/w/Windfall.java +++ b/Mage.Sets/src/mage/cards/w/Windfall.java @@ -1,10 +1,7 @@ - package mage.cards.w; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -12,14 +9,15 @@ import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author emerald000 */ public final class Windfall extends CardImpl { public Windfall(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); // Each player discards their hand, then draws cards equal to the greatest number of cards a player discarded this way. this.getSpellAbility().addEffect(new WindfallEffect()); @@ -42,7 +40,7 @@ class WindfallEffect extends OneShotEffect { staticText = "Each player discards their hand, then draws cards equal to the greatest number of cards a player discarded this way."; } - WindfallEffect(final WindfallEffect effect) { + private WindfallEffect(final WindfallEffect effect) { super(effect); } @@ -55,22 +53,18 @@ class WindfallEffect extends OneShotEffect { } for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); - if (player != null) { - int discarded = 0; - for (Card c : player.getHand().getCards(game)) { - if (player.discard(c, source, game)) { - discarded++; - } - } - if (discarded > maxDiscarded) { - maxDiscarded = discarded; - } + if (player == null) { + continue; + } + int discarded = player.discard(player.getHand(), source, game).size(); + if (discarded > maxDiscarded) { + maxDiscarded = discarded; } } for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(maxDiscarded, game); + player.drawCards(maxDiscarded, source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/w/WindsOfAbandon.java b/Mage.Sets/src/mage/cards/w/WindsOfAbandon.java index b3233eb2be..f425edc8b3 100644 --- a/Mage.Sets/src/mage/cards/w/WindsOfAbandon.java +++ b/Mage.Sets/src/mage/cards/w/WindsOfAbandon.java @@ -10,11 +10,8 @@ import mage.cards.CardSetInfo; import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -36,7 +33,7 @@ public final class WindsOfAbandon extends CardImpl { // Exile target creature you don't control. For each creature exiled this way, its controller searches their library for a basic land card. Those players put those cards onto the battlefield tapped, then shuffle their libraries. this.getSpellAbility().addEffect(new WindsOfAbandonEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(WindsOfAbandonOverloadEffect.filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); // Overload {4}{W}{W} this.addAbility(new OverloadAbility( @@ -102,12 +99,6 @@ class WindsOfAbandonEffect extends OneShotEffect { class WindsOfAbandonOverloadEffect extends OneShotEffect { - static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - WindsOfAbandonOverloadEffect() { super(Outcome.Exile); staticText = "Exile each creature you don't control. For each creature exiled this way, " + @@ -132,7 +123,7 @@ class WindsOfAbandonOverloadEffect extends OneShotEffect { } Map playerMap = new HashMap<>(); CardsImpl cards = new CardsImpl(); - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, source.getControllerId(), source.getSourceId(), game)) { int count = playerMap.getOrDefault(permanent.getControllerId(), 0); playerMap.put(permanent.getControllerId(), count + 1); cards.add(permanent); diff --git a/Mage.Sets/src/mage/cards/w/WindsOfChange.java b/Mage.Sets/src/mage/cards/w/WindsOfChange.java index 035f882abc..a2584e505f 100644 --- a/Mage.Sets/src/mage/cards/w/WindsOfChange.java +++ b/Mage.Sets/src/mage/cards/w/WindsOfChange.java @@ -69,7 +69,7 @@ class WindsOfChangeEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); if (player != null && permanentsCount.containsKey(playerId)) { - player.drawCards(permanentsCount.get(playerId), game); + player.drawCards(permanentsCount.get(playerId), source.getSourceId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/w/WingedTempleOfOrazca.java b/Mage.Sets/src/mage/cards/w/WingedTempleOfOrazca.java index 65d66dd4c1..8d08df6040 100644 --- a/Mage.Sets/src/mage/cards/w/WingedTempleOfOrazca.java +++ b/Mage.Sets/src/mage/cards/w/WingedTempleOfOrazca.java @@ -1,7 +1,6 @@ package mage.cards.w; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -16,16 +15,14 @@ import mage.abilities.keyword.FlyingAbility; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** * * @author LevelX2 @@ -68,7 +65,7 @@ class WingedTempleOfOrazcaEffect extends OneShotEffect { public WingedTempleOfOrazcaEffect() { super(Outcome.Benefit); - this.staticText = "it gains flying and gets +X/+X until end of turn, where X is its power"; + this.staticText = "target creature you control gains flying and gets +X/+X until end of turn, where X is its power"; } public WingedTempleOfOrazcaEffect(final WingedTempleOfOrazcaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/w/WingedWords.java b/Mage.Sets/src/mage/cards/w/WingedWords.java index 146ebf5111..316f39b006 100644 --- a/Mage.Sets/src/mage/cards/w/WingedWords.java +++ b/Mage.Sets/src/mage/cards/w/WingedWords.java @@ -1,9 +1,11 @@ package mage.cards.w; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -30,10 +32,11 @@ public final class WingedWords extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); // This spell costs {1} less to cast if you control a creature with flying. - this.addAbility(new SimpleStaticAbility( - Zone.STACK, new SpellCostReductionSourceEffect( - 1, new PermanentsOnTheBattlefieldCondition(filter) - )).setRuleAtTheTop(true)); + Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + this.addAbility(new SimpleStaticAbility(Zone.ALL, + new SpellCostReductionSourceEffect(1, condition)) + .setRuleAtTheTop(true) + .addHint(new ConditionHint(condition, "You control a creature with flying"))); // Draw two cards. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); diff --git a/Mage.Sets/src/mage/cards/w/WingfoldPteron.java b/Mage.Sets/src/mage/cards/w/WingfoldPteron.java new file mode 100644 index 0000000000..ee968dcc23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WingfoldPteron.java @@ -0,0 +1,40 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.common.counter.AddCounterChoiceSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WingfoldPteron extends CardImpl { + + public WingfoldPteron(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}"); + + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(3); + this.toughness = new MageInt(6); + + // Wingfold Pteron enters the battlefield with your choice of a flying counter or a hexproof counter on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCounterChoiceSourceEffect(CounterType.FLYING, CounterType.HEXPROOF) + )); + } + + private WingfoldPteron(final WingfoldPteron card) { + super(card); + } + + @Override + public WingfoldPteron copy() { + return new WingfoldPteron(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WingmateRoc.java b/Mage.Sets/src/mage/cards/w/WingmateRoc.java index 04d872751d..a2a9ff5c91 100644 --- a/Mage.Sets/src/mage/cards/w/WingmateRoc.java +++ b/Mage.Sets/src/mage/cards/w/WingmateRoc.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -11,16 +9,19 @@ import mage.abilities.dynamicvalue.common.AttackingCreatureCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.common.RaidHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.game.permanent.token.WingmateRocToken; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author emerald000 */ public final class WingmateRoc extends CardImpl { @@ -35,9 +36,11 @@ public final class WingmateRoc extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Raid - When Wingmate Roc enters the battlefield, if you attacked with a creature this turn, create a 3/4 white Bird creature token with flying. + // Raid — When Wingmate Roc enters the battlefield, if you attacked this turn, create a 3/4 white Bird creature token with flying. this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WingmateRocToken())), RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked with a creature this turn, create a 3/4 white Bird creature token with flying."), + "Raid — When {this} enters the battlefield, if you attacked this turn, create a 3/4 white Bird creature token with flying.") + .setAbilityWord(AbilityWord.RAID) + .addHint(RaidHint.instance), new PlayerAttackedWatcher()); // Whenever Wingmate Roc attacks, you gain 1 life for each attacking creature. diff --git a/Mage.Sets/src/mage/cards/w/WingspanMentor.java b/Mage.Sets/src/mage/cards/w/WingspanMentor.java new file mode 100644 index 0000000000..ef784e55b1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WingspanMentor.java @@ -0,0 +1,71 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WingspanMentor extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("non-Human creature you control"); + private static final FilterPermanent filter2 + = new FilterControlledCreaturePermanent("creature you control with flying"); + + static { + filter.add(Predicates.not(SubType.HUMAN.getPredicate())); + filter2.add(new AbilityPredicate(FlyingAbility.class)); + } + + public WingspanMentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // When Wingspan Mentor enters the battlefield, put a flying counter on target non-Human creature you control. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.FLYING.createInstance()) + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {2}{U}, {T}: Put a +1/+1 counter on each creature you control with flying. + ability = new SimpleActivatedAbility( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter2), new ManaCostsImpl("{2}{U}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private WingspanMentor(final WingspanMentor card) { + super(card); + } + + @Override + public WingspanMentor copy() { + return new WingspanMentor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/Winnow.java b/Mage.Sets/src/mage/cards/w/Winnow.java index ba91f419b6..bb08a8a3d0 100644 --- a/Mage.Sets/src/mage/cards/w/Winnow.java +++ b/Mage.Sets/src/mage/cards/w/Winnow.java @@ -45,7 +45,7 @@ class WinnowEffect extends DestroyTargetEffect { public WinnowEffect() { super(); - staticText = "Destroy target nonland permanent if another permanent with the same name is on the battlefield."; + staticText = "Destroy target nonland permanent if another permanent with the same name is on the battlefield"; } public WinnowEffect(final WinnowEffect effect) { diff --git a/Mage.Sets/src/mage/cards/w/WinotaJoinerOfForces.java b/Mage.Sets/src/mage/cards/w/WinotaJoinerOfForces.java new file mode 100644 index 0000000000..5d7588b22c --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WinotaJoinerOfForces.java @@ -0,0 +1,112 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WinotaJoinerOfForces extends CardImpl { + + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent(" non-Human creature you control"); + + static { + filter.add(Predicates.not(SubType.HUMAN.getPredicate())); + } + + public WinotaJoinerOfForces(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever a non-Human creature you control attacks, look at the top six cards of your library. You may put a Human creature card from among them onto the battlefield tapped and attacking. It gains indestructible until end of turn. Put the rest of the cards on the bottom of your library in a random order. + this.addAbility(new AttacksCreatureYouControlTriggeredAbility( + new WinotaJoinerOfForcesEffect(), false, filter + )); + } + + private WinotaJoinerOfForces(final WinotaJoinerOfForces card) { + super(card); + } + + @Override + public WinotaJoinerOfForces copy() { + return new WinotaJoinerOfForces(this); + } +} + +class WinotaJoinerOfForcesEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCreatureCard("Human creature card"); + + static { + filter.add(SubType.HUMAN.getPredicate()); + } + + WinotaJoinerOfForcesEffect() { + super(Outcome.Benefit); + staticText = "look at the top six cards of your library. " + + "You may put a Human creature card from among them onto the battlefield tapped and attacking. " + + "It gains indestructible until end of turn. " + + "Put the rest of the cards on the bottom of your library in a random order."; + } + + private WinotaJoinerOfForcesEffect(final WinotaJoinerOfForcesEffect effect) { + super(effect); + } + + @Override + public WinotaJoinerOfForcesEffect copy() { + return new WinotaJoinerOfForcesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 6)); + TargetCardInLibrary targetCardInLibrary = new TargetCardInLibrary(0, 1, filter); + player.choose(outcome, cards, targetCardInLibrary, game); + Card card = game.getCard(targetCardInLibrary.getFirstTarget()); + if (card == null || !player.moveCards( + card, Zone.BATTLEFIELD, source, game, true, + false, true, null + )) { + return player.putCardsOnBottomOfLibrary(cards, game, source, false); + } + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { + return player.putCardsOnBottomOfLibrary(cards, game, source, false); + } + game.getCombat().addAttackingCreature(permanent.getId(), game); + cards.remove(card); + game.addEffect(new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setTargetPointer(new FixedTarget(permanent, game)), source); + return player.putCardsOnBottomOfLibrary(cards, game, source, false); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/w/WintersNight.java b/Mage.Sets/src/mage/cards/w/WintersNight.java index 632b1b3a6e..550e0b7d81 100644 --- a/Mage.Sets/src/mage/cards/w/WintersNight.java +++ b/Mage.Sets/src/mage/cards/w/WintersNight.java @@ -22,7 +22,8 @@ import mage.filter.common.FilterLandPermanent; public final class WintersNight extends CardImpl { private static final FilterLandPermanent filter = new FilterLandPermanent("a player taps a snow land"); - { + + static { filter.add(SuperType.SNOW.getPredicate()); } diff --git a/Mage.Sets/src/mage/cards/w/WireflyHive.java b/Mage.Sets/src/mage/cards/w/WireflyHive.java index fbfc8cecaa..69a56320ac 100644 --- a/Mage.Sets/src/mage/cards/w/WireflyHive.java +++ b/Mage.Sets/src/mage/cards/w/WireflyHive.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -12,25 +10,31 @@ import mage.abilities.effects.common.FlipCoinEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.NamePredicate; import mage.game.permanent.token.WireflyToken; +import java.util.UUID; + /** - * * @author fireshoes */ public final class WireflyHive extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("permanents named Wirefly"); + + static { + filter.add(new NamePredicate("Wirefly")); + } + public WireflyHive(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + // {3}, {tap}: Flip a coin. If you win the flip, create a 2/2 colorless Insect artifact creature token with flying named Wirefly. // If you lose the flip, destroy all permanents named Wirefly. - FilterPermanent filter = new FilterPermanent("permanents named Wirefly"); - filter.add(new NamePredicate("Wirefly")); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new FlipCoinEffect(new CreateTokenEffect(new WireflyToken()), new DestroyAllEffect(filter)), new GenericManaCost(3)); + Ability ability = new SimpleActivatedAbility(new FlipCoinEffect( + new CreateTokenEffect(new WireflyToken()), new DestroyAllEffect(filter) + ), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WirewoodHerald.java b/Mage.Sets/src/mage/cards/w/WirewoodHerald.java index c55fdbfb25..b0300130b0 100644 --- a/Mage.Sets/src/mage/cards/w/WirewoodHerald.java +++ b/Mage.Sets/src/mage/cards/w/WirewoodHerald.java @@ -3,7 +3,7 @@ package mage.cards.w; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -32,7 +32,7 @@ public final class WirewoodHerald extends CardImpl { this.toughness = new MageInt(1); // When Wirewood Herald dies, you may search your library for an Elf card, reveal that card, put it into your hand, then shuffle your library. - this.addAbility(new DiesTriggeredAbility( + this.addAbility(new DiesSourceTriggeredAbility( new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true), true)); } diff --git a/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java b/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java index 09ce7ad911..0badf53c39 100644 --- a/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java +++ b/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java @@ -68,7 +68,7 @@ class WishfulMerfolkEffect extends ContinuousEffectImpl { switch (layer) { case AbilityAddingRemovingEffects_6: if (sublayer == SubLayer.NA) { - permanent.getAbilities().removeIf(entry -> entry.getId().equals(DefenderAbility.getInstance().getId())); + permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game); } break; case TypeChangingEffects_4: diff --git a/Mage.Sets/src/mage/cards/w/WispweaverAngel.java b/Mage.Sets/src/mage/cards/w/WispweaverAngel.java index 2de4396f08..05554b778f 100644 --- a/Mage.Sets/src/mage/cards/w/WispweaverAngel.java +++ b/Mage.Sets/src/mage/cards/w/WispweaverAngel.java @@ -1,34 +1,22 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.cards.MeldCard; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.ExileZone; -import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; -import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author fireshoes */ public final class WispweaverAngel extends CardImpl { @@ -49,10 +37,9 @@ public final class WispweaverAngel extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Wispweaver Angel enters the battlefield, you may exile another target creature you control, then return that card to the battlefield under its owner's control. - Effect effect = new ExileTargetForSourceEffect(); - Ability ability = new EntersBattlefieldTriggeredAbility(effect, true); + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), true); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); ability.addTarget(new TargetControlledCreaturePermanent(1, 1, filter, false)); - ability.addEffect(new WispweaverAngelEffect()); this.addAbility(ability); } @@ -65,57 +52,3 @@ public final class WispweaverAngel extends CardImpl { return new WispweaverAngel(this); } } - -class WispweaverAngelEffect extends OneShotEffect { - - WispweaverAngelEffect() { - super(Outcome.Benefit); - staticText = "return that card to the battlefield under its owner's control"; - } - - WispweaverAngelEffect(final WispweaverAngelEffect effect) { - super(effect); - } - - @Override - public WispweaverAngelEffect copy() { - return new WispweaverAngelEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Cards cardsToBattlefield = new CardsImpl(); - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - if (exileZoneId != null) { - ExileZone exileZone = game.getExile().getExileZone(exileZoneId); - if (exileZone != null) { - for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { - if (exileZone.contains(targetId)) { - cardsToBattlefield.add(targetId); - } else { - Card card = game.getCard(targetId); - if (card instanceof MeldCard) { - MeldCard meldCard = (MeldCard) card; - Card topCard = meldCard.getTopHalfCard(); - Card bottomCard = meldCard.getBottomHalfCard(); - if (topCard.getZoneChangeCounter(game) == meldCard.getTopLastZoneChangeCounter() && exileZone.contains(topCard.getId())) { - cardsToBattlefield.add(topCard); - } - if (bottomCard.getZoneChangeCounter(game) == meldCard.getBottomLastZoneChangeCounter() && exileZone.contains(bottomCard.getId())) { - cardsToBattlefield.add(bottomCard); - } - } - } - } - } - } - if (!cardsToBattlefield.isEmpty()) { - controller.moveCards(cardsToBattlefield.getCards(game), Zone.BATTLEFIELD, source, game, false, false, true, null); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java b/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java new file mode 100644 index 0000000000..4a3f809262 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java @@ -0,0 +1,67 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.SacrificeOpponentsEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WitchOfTheMoors extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0); + + public WitchOfTheMoors(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // At the beginning of your end step, if you gained life this turn, each opponent sacrifices a + // creature and you return up to one target creature card from your graveyard to your hand. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility(new SacrificeOpponentsEffect( + StaticFilters.FILTER_PERMANENT_A_CREATURE + ), TargetController.YOU, false), + condition, "At the beginning of your end step, if you gained life this turn, " + + "each opponent sacrifices a creature and you return up to one target creature card " + + "from your graveyard to your hand." + ); + ability.addEffect(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard( + 0, 1, StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD + )); + this.addAbility(ability, new PlayerGainedLifeWatcher()); + } + + private WitchOfTheMoors(final WitchOfTheMoors card) { + super(card); + } + + @Override + public WitchOfTheMoors copy() { + return new WitchOfTheMoors(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WitchsCauldron.java b/Mage.Sets/src/mage/cards/w/WitchsCauldron.java new file mode 100644 index 0000000000..dec55cffba --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WitchsCauldron.java @@ -0,0 +1,42 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WitchsCauldron extends CardImpl { + + public WitchsCauldron(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{B}"); + + // {1}{B}, {T}, Sacrifice a creature: You gain 1 life and draw a card. + Ability ability = new SimpleActivatedAbility(new GainLifeEffect(1), new ManaCostsImpl("{1}{B}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and")); + this.addAbility(ability); + } + + private WitchsCauldron(final WitchsCauldron card) { + super(card); + } + + @Override + public WitchsCauldron copy() { + return new WitchsCauldron(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WitheringGaze.java b/Mage.Sets/src/mage/cards/w/WitheringGaze.java index 2a0c3b8ff4..6ce7cc4b71 100644 --- a/Mage.Sets/src/mage/cards/w/WitheringGaze.java +++ b/Mage.Sets/src/mage/cards/w/WitheringGaze.java @@ -70,7 +70,7 @@ class WitheringGazeEffect extends OneShotEffect { count++; } } - controller.drawCards(count, game); + controller.drawCards(count, source.getSourceId(), game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java b/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java index 6d8d46bc86..dab327b5f5 100644 --- a/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java +++ b/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java @@ -4,7 +4,7 @@ package mage.cards.w; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.DealsDamageToOpponentTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RemoveAllCountersSourceEffect; @@ -34,7 +34,7 @@ public final class WitherscaleWurm extends CardImpl { // Whenever Witherscale Wurm blocks or becomes blocked by a creature, that creature gains wither until end of turn. Effect effect = new GainAbilityTargetEffect(WitherAbility.getInstance(), Duration.EndOfTurn); effect.setText("that creature gains wither until end of turn"); - Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(effect, new FilterCreaturePermanent("a creature"), false, null, true); + Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, new FilterCreaturePermanent("a creature"), false, null, true); this.addAbility(ability); // Whenever Witherscale Wurm deals damage to an opponent, remove all -1/-1 counters from it. diff --git a/Mage.Sets/src/mage/cards/w/WitsEnd.java b/Mage.Sets/src/mage/cards/w/WitsEnd.java index 3bb57d18e8..ff2e542b7d 100644 --- a/Mage.Sets/src/mage/cards/w/WitsEnd.java +++ b/Mage.Sets/src/mage/cards/w/WitsEnd.java @@ -1,11 +1,7 @@ - package mage.cards.w; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -14,22 +10,22 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author North */ public final class WitsEnd extends CardImpl { public WitsEnd(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}{B}"); // Target player discards their hand. this.getSpellAbility().addEffect(new WitsEndEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); } - public WitsEnd(final WitsEnd card) { + private WitsEnd(final WitsEnd card) { super(card); } @@ -41,12 +37,12 @@ public final class WitsEnd extends CardImpl { class WitsEndEffect extends OneShotEffect { - public WitsEndEffect() { + WitsEndEffect() { super(Outcome.Benefit); this.staticText = "Target player discards their hand"; } - public WitsEndEffect(final WitsEndEffect effect) { + private WitsEndEffect(final WitsEndEffect effect) { super(effect); } @@ -58,13 +54,10 @@ class WitsEndEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - Set cards = player.getHand().getCards(game); - for (Card card : cards) { - player.discard(card, source, game); - } - return true; + if (player == null) { + return false; } - return false; + player.discard(player.getHand(), source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/w/WizardsLightning.java b/Mage.Sets/src/mage/cards/w/WizardsLightning.java index b13e313de1..b8f76e0ffc 100644 --- a/Mage.Sets/src/mage/cards/w/WizardsLightning.java +++ b/Mage.Sets/src/mage/cards/w/WizardsLightning.java @@ -1,12 +1,12 @@ - package mage.cards.w; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -14,8 +14,10 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + /** - * * @author Will */ public final class WizardsLightning extends CardImpl { @@ -30,8 +32,10 @@ public final class WizardsLightning extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); // Wizard's Lightning costs {2} less to cast if you control a Wizard. - Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(2, new PermanentsOnTheBattlefieldCondition(filter))); + Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, condition)); ability.setRuleAtTheTop(true); + ability.addHint(new ConditionHint(condition, "You control a Wizard")); this.addAbility(ability); // Wizard's Lightning deals 3 damage to any target. diff --git a/Mage.Sets/src/mage/cards/w/WizardsRetort.java b/Mage.Sets/src/mage/cards/w/WizardsRetort.java index aecee64c22..192b9e449e 100644 --- a/Mage.Sets/src/mage/cards/w/WizardsRetort.java +++ b/Mage.Sets/src/mage/cards/w/WizardsRetort.java @@ -1,12 +1,12 @@ - package mage.cards.w; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -15,8 +15,9 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author Will */ public final class WizardsRetort extends CardImpl { @@ -31,8 +32,10 @@ public final class WizardsRetort extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}"); // Wizard's Retort costs {1} less to cast if you control a Wizard. - Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter))); + Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, condition)); ability.setRuleAtTheTop(true); + ability.addHint(new ConditionHint(condition, "You control a Wizard")); this.addAbility(ability); // Counter target spell. diff --git a/Mage.Sets/src/mage/cards/w/WoebringerDemon.java b/Mage.Sets/src/mage/cards/w/WoebringerDemon.java index 875ec964ae..d61f395196 100644 --- a/Mage.Sets/src/mage/cards/w/WoebringerDemon.java +++ b/Mage.Sets/src/mage/cards/w/WoebringerDemon.java @@ -35,8 +35,10 @@ public final class WoebringerDemon extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // At the beginning of each player's upkeep, that player sacrifices a creature. If the player can't, sacrifice Woebringer Demon. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new WoebringerDemonEffect(), TargetController.ANY, false, true)); + // At the beginning of each player's upkeep, that player sacrifices a creature. + // If the player can't, sacrifice Woebringer Demon. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new WoebringerDemonEffect(), TargetController.ANY, false, true)); } public WoebringerDemon(final WoebringerDemon card) { diff --git a/Mage.Sets/src/mage/cards/w/WordsOfWisdom.java b/Mage.Sets/src/mage/cards/w/WordsOfWisdom.java index 6351d1eb27..57edc3de52 100644 --- a/Mage.Sets/src/mage/cards/w/WordsOfWisdom.java +++ b/Mage.Sets/src/mage/cards/w/WordsOfWisdom.java @@ -62,7 +62,7 @@ class WordsOfWisdomEffect extends OneShotEffect { if (!playerId.equals(controller.getId())) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/w/Workhorse.java b/Mage.Sets/src/mage/cards/w/Workhorse.java index 35bd41e750..deb2f895b4 100644 --- a/Mage.Sets/src/mage/cards/w/Workhorse.java +++ b/Mage.Sets/src/mage/cards/w/Workhorse.java @@ -6,6 +6,7 @@ import mage.MageInt; import mage.Mana; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -32,7 +33,8 @@ public final class Workhorse extends CardImpl { // Remove a +1/+1 counter from Workhorse: Add {C}. this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1), - new RemoveCountersSourceCost(CounterType.P1P1.createInstance()))); + new RemoveCountersSourceCost(CounterType.P1P1.createInstance()), + new CountersSourceCount(CounterType.P1P1))); } public Workhorse(final Workhorse card) { diff --git a/Mage.Sets/src/mage/cards/w/WorkshopAssistant.java b/Mage.Sets/src/mage/cards/w/WorkshopAssistant.java index 9adc768912..9b2bf29ef3 100644 --- a/Mage.Sets/src/mage/cards/w/WorkshopAssistant.java +++ b/Mage.Sets/src/mage/cards/w/WorkshopAssistant.java @@ -4,7 +4,7 @@ package mage.cards.w; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; @@ -36,7 +36,7 @@ public final class WorkshopAssistant extends CardImpl { // When Workshop Assistant dies, return another target artifact card from your graveyard to your hand. Effect effect = new ReturnFromGraveyardToHandTargetEffect(); effect.setText("return another target artifact card from your graveyard to your hand"); - Ability ability = new DiesTriggeredAbility(effect); + Ability ability = new DiesSourceTriggeredAbility(effect); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WorkshopElders.java b/Mage.Sets/src/mage/cards/w/WorkshopElders.java index 3b4cbd9065..8f6abe6ab1 100644 --- a/Mage.Sets/src/mage/cards/w/WorkshopElders.java +++ b/Mage.Sets/src/mage/cards/w/WorkshopElders.java @@ -57,10 +57,10 @@ public final class WorkshopElders extends CardImpl { ).setText("have target noncreature artifact you control become"), TargetController.YOU, true); ability.addEffect(new SetPowerToughnessTargetEffect( 0, 0, Duration.EndOfGame - ).setText("a 0/0 artifact creature.")); + ).setText("a 0/0 artifact creature")); ability.addEffect(new AddCountersTargetEffect( CounterType.P1P1.createInstance(4) - ).setText("If you do, put four +1/+1 counters on it.")); + ).setText("If you do, put four +1/+1 counters on it")); ability.addTarget(new TargetPermanent(filter2)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WorldShaper.java b/Mage.Sets/src/mage/cards/w/WorldShaper.java index e134a44a4f..ce75ae985e 100644 --- a/Mage.Sets/src/mage/cards/w/WorldShaper.java +++ b/Mage.Sets/src/mage/cards/w/WorldShaper.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; import mage.cards.CardImpl; @@ -36,7 +36,7 @@ public final class WorldShaper extends CardImpl { this.addAbility(new AttacksTriggeredAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(3), true)); // When World Shaper dies, put all land cards from your graveyard onto the battlefield tapped. - this.addAbility(new DiesTriggeredAbility(new WorldShaperEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new WorldShaperEffect(), false)); } public WorldShaper(final WorldShaper card) { diff --git a/Mage.Sets/src/mage/cards/w/Worldpurge.java b/Mage.Sets/src/mage/cards/w/Worldpurge.java index 5fff60ffe7..7a05c398b4 100644 --- a/Mage.Sets/src/mage/cards/w/Worldpurge.java +++ b/Mage.Sets/src/mage/cards/w/Worldpurge.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.HashSet; @@ -78,16 +77,11 @@ class WorldpurgeEffect extends OneShotEffect { int numberInHand = Math.min(7, hand.size()); TargetCardInHand target = new TargetCardInHand(0, numberInHand, new FilterCard("cards to keep in hand")); Cards cardsToLibrary = new CardsImpl(); + cardsToLibrary.addAll(player.getHand()); if (player.choose(Outcome.Benefit, target, source.getSourceId(), game)) { - for (Card card : hand.getCards(game)) { - if (!target.getTargets().contains(card.getId())) { - cardsToLibrary.add(card); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); - } - } + cardsToLibrary.removeAll(target.getTargets()); } - player.putCardsOnTopOfLibrary(cardsToLibrary, game, source, false); - player.shuffleLibrary(source, game); + player.shuffleCardsToLibrary(cardsToLibrary, game, source); } } game.emptyManaPools(); diff --git a/Mage.Sets/src/mage/cards/w/WorldspineWurm.java b/Mage.Sets/src/mage/cards/w/WorldspineWurm.java index 024220338e..ebd27a0ebe 100644 --- a/Mage.Sets/src/mage/cards/w/WorldspineWurm.java +++ b/Mage.Sets/src/mage/cards/w/WorldspineWurm.java @@ -1,9 +1,7 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.PutIntoGraveFromAnywhereSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; @@ -12,16 +10,17 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.game.permanent.token.WurmToken2; +import mage.game.permanent.token.WurmWithTrampleToken; + +import java.util.UUID; /** - * * @author Plopman */ public final class WorldspineWurm extends CardImpl { public WorldspineWurm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{8}{G}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{8}{G}{G}{G}"); this.subtype.add(SubType.WURM); this.power = new MageInt(15); @@ -31,7 +30,7 @@ public final class WorldspineWurm extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Worldspine Wurm dies, create three 5/5 green Wurm creature tokens with trample. - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new WurmToken2(), 3))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new WurmWithTrampleToken(), 3))); // When Worldspine Wurm is put into a graveyard from anywhere, shuffle it into its owner's library. this.addAbility(new PutIntoGraveFromAnywhereSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect())); diff --git a/Mage.Sets/src/mage/cards/w/WorryBeads.java b/Mage.Sets/src/mage/cards/w/WorryBeads.java index 47423bd82f..6f206e45f4 100644 --- a/Mage.Sets/src/mage/cards/w/WorryBeads.java +++ b/Mage.Sets/src/mage/cards/w/WorryBeads.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; import mage.cards.CardImpl; @@ -10,8 +8,9 @@ import mage.constants.CardType; import mage.constants.TargetController; import mage.constants.Zone; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class WorryBeads extends CardImpl { @@ -20,12 +19,13 @@ public final class WorryBeads extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // At the beginning of each player's upkeep, that player puts the top card of their library into their graveyard. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, - new PutLibraryIntoGraveTargetEffect(1).setText("that player puts the top card of their library into their graveyard"), - TargetController.ANY, false, true)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, new PutLibraryIntoGraveTargetEffect(1).setText("that player mills a card"), + TargetController.ANY, false, true + )); } - public WorryBeads(final WorryBeads card) { + private WorryBeads(final WorryBeads card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java b/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java index 9e7608a8e1..a45c1d1b00 100644 --- a/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java +++ b/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; @@ -17,12 +15,13 @@ import mage.filter.common.FilterInstantOrSorcerySpell; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; -import mage.game.permanent.token.WortTheRaidmotherToken; +import mage.game.permanent.token.GoblinWarriorToken; import mage.game.stack.Spell; import mage.game.stack.StackObject; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class WortTheRaidmother extends CardImpl { @@ -36,7 +35,7 @@ public final class WortTheRaidmother extends CardImpl { this.toughness = new MageInt(3); // When Wort, the Raidmother enters the battlefield, create two 1/1 red and green Goblin Warrior creature tokens. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WortTheRaidmotherToken(), 2), false)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GoblinWarriorToken(), 2), false)); // Each red or green instant or sorcery spell you cast has conspire. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WortGainConspireEffect())); @@ -59,6 +58,7 @@ class WortGainConspireEffect extends ContinuousEffectImpl { static { filter.add(Predicates.or(new ColorPredicate(ObjectColor.RED), new ColorPredicate(ObjectColor.GREEN))); } + private final ConspireAbility conspireAbility; public WortGainConspireEffect() { diff --git a/Mage.Sets/src/mage/cards/w/WreakHavoc.java b/Mage.Sets/src/mage/cards/w/WreakHavoc.java index ee85f28431..ea29cbbd8a 100644 --- a/Mage.Sets/src/mage/cards/w/WreakHavoc.java +++ b/Mage.Sets/src/mage/cards/w/WreakHavoc.java @@ -2,7 +2,7 @@ package mage.cards.w; import java.util.UUID; -import mage.abilities.common.CantBeCounteredAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -29,7 +29,7 @@ public final class WreakHavoc extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}{G}"); - this.addAbility(new CantBeCounteredAbility()); + this.addAbility(new CantBeCounteredSourceAbility()); this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent(filter)); } diff --git a/Mage.Sets/src/mage/cards/w/WrenchMind.java b/Mage.Sets/src/mage/cards/w/WrenchMind.java index ae5dbe64aa..552f2cfb16 100644 --- a/Mage.Sets/src/mage/cards/w/WrenchMind.java +++ b/Mage.Sets/src/mage/cards/w/WrenchMind.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -9,28 +7,28 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetDiscard; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class WrenchMind extends CardImpl { public WrenchMind(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}{B}"); // Target player discards two cards unless they discard an artifact card. this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new WrenchMindEffect()); - } - public WrenchMind(final WrenchMind card) { + private WrenchMind(final WrenchMind card) { super(card); } @@ -42,12 +40,12 @@ public final class WrenchMind extends CardImpl { class WrenchMindEffect extends OneShotEffect { - public WrenchMindEffect() { + WrenchMindEffect() { super(Outcome.Discard); this.staticText = "Target player discards two cards unless they discard an artifact card"; } - public WrenchMindEffect(final WrenchMindEffect effect) { + private WrenchMindEffect(final WrenchMindEffect effect) { super(effect); } @@ -59,18 +57,19 @@ class WrenchMindEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (targetPlayer != null && !targetPlayer.getHand().isEmpty()) { - TargetDiscard target = new TargetDiscard(targetPlayer.getId()); - targetPlayer.choose(Outcome.Discard, target, source.getSourceId(), game); - Card card = targetPlayer.getHand().get(target.getFirstTarget(), game); - if (card != null) { - targetPlayer.discard(card, source, game); - if (!card.isArtifact() && !targetPlayer.getHand().isEmpty()) { - targetPlayer.discard(1, false, source, game); - } - return true; - } + if (targetPlayer == null || targetPlayer.getHand().isEmpty()) { + return false; } - return false; + if (targetPlayer.getHand().count(StaticFilters.FILTER_CARD_ARTIFACT, game) < 1 + || !targetPlayer.chooseUse(Outcome.Benefit, "Discard an artifact card?", source, game)) { + return !targetPlayer.discard(2, false, source, game).isEmpty(); + } + TargetDiscard target = new TargetDiscard(StaticFilters.FILTER_CARD_ARTIFACT_AN, targetPlayer.getId()); + targetPlayer.choose(Outcome.Discard, target, source.getSourceId(), game); + Card card = targetPlayer.getHand().get(target.getFirstTarget(), game); + if (card != null && targetPlayer.discard(card, source, game)) { + return true; + } + return !targetPlayer.discard(2, false, source, game).isEmpty(); } } diff --git a/Mage.Sets/src/mage/cards/w/WretchedCamel.java b/Mage.Sets/src/mage/cards/w/WretchedCamel.java index 195f091fa0..c5d2d7e2c0 100644 --- a/Mage.Sets/src/mage/cards/w/WretchedCamel.java +++ b/Mage.Sets/src/mage/cards/w/WretchedCamel.java @@ -4,7 +4,7 @@ package mage.cards.w; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.OrCondition; import mage.abilities.condition.common.CardsInControllerGraveCondition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; @@ -42,7 +42,7 @@ public final class WretchedCamel extends CardImpl { // When Wretched Camel dies, if you control a Desert or there is a Desert card in your graveyard, target player discards a card. Ability ability = new ConditionalInterveningIfTriggeredAbility( - new DiesTriggeredAbility(new DiscardTargetEffect(1)), + new DiesSourceTriggeredAbility(new DiscardTargetEffect(1)), new OrCondition( new PermanentsOnTheBattlefieldCondition(new FilterControlledPermanent(filterDesertPermanent)), new CardsInControllerGraveCondition(1, filterDesertCard)), diff --git a/Mage.Sets/src/mage/cards/w/WriteIntoBeing.java b/Mage.Sets/src/mage/cards/w/WriteIntoBeing.java index 0c452d69f6..166847feb5 100644 --- a/Mage.Sets/src/mage/cards/w/WriteIntoBeing.java +++ b/Mage.Sets/src/mage/cards/w/WriteIntoBeing.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -84,12 +83,11 @@ class WriteIntoBeingEffect extends OneShotEffect { new ManifestEffect(1).apply(game, source); if (controller.getLibrary().hasCards()) { Card cardToPutBack = controller.getLibrary().getFromTop(game); - String position = "on top"; if (controller.chooseUse(Outcome.Detriment, "Put " + cardToPutBack.getName() + " on bottom of library?", source, game)) { - cardToPutBack.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); - position = "on bottom"; + controller.putCardsOnBottomOfLibrary(cardToPutBack, game, source, true); + } else { + controller.putCardsOnTopOfLibrary(cardToPutBack, game, source, true); } - game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " puts the other card " + position + " of their library"); } return true; } diff --git a/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java b/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java index 9526069d68..f73a0a7348 100644 --- a/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java +++ b/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java @@ -1,10 +1,8 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.LifelinkAbility; @@ -12,11 +10,12 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.game.permanent.token.Wurm1Token; -import mage.game.permanent.token.Wurm2Token; +import mage.game.permanent.token.WurmWithDeathtouchToken; +import mage.game.permanent.token.WurmWithLifelinkToken; + +import java.util.UUID; /** - * * @author Loki */ public final class WurmcoilEngine extends CardImpl { @@ -32,8 +31,8 @@ public final class WurmcoilEngine extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink. - Ability ability = new DiesTriggeredAbility(new CreateTokenEffect(new Wurm1Token(expansionSetCode)), false); - ability.addEffect(new CreateTokenEffect(new Wurm2Token(expansionSetCode))); + Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new WurmWithDeathtouchToken()), false); + ability.addEffect(new CreateTokenEffect(new WurmWithLifelinkToken())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/x/XyrisTheWrithingStorm.java b/Mage.Sets/src/mage/cards/x/XyrisTheWrithingStorm.java new file mode 100644 index 0000000000..85003d5036 --- /dev/null +++ b/Mage.Sets/src/mage/cards/x/XyrisTheWrithingStorm.java @@ -0,0 +1,129 @@ +package mage.cards.x; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.SnakeToken; +import mage.game.permanent.token.Token; +import mage.players.Player; +import mage.watchers.common.CardsDrawnDuringDrawStepWatcher; + +import java.util.UUID; + +/** + * @author AsterAether + */ +public final class XyrisTheWrithingStorm extends CardImpl { + + public XyrisTheWrithingStorm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.LEVIATHAN); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever an opponent draws a card except the first one they draw in each of their draw steps, create a 1/1 green Snake creature token. + this.addAbility(new XyrisTheWrithingStormDrawAbility(), new CardsDrawnDuringDrawStepWatcher()); + // Whenever Xyris, the Writhing Storm deals combat damage to a player, you and that player each draw that many cards. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new XyrisTheWrithingStormCombatDamageEffect(), false, true)); + } + + private XyrisTheWrithingStorm(final XyrisTheWrithingStorm card) { + super(card); + } + + @Override + public XyrisTheWrithingStorm copy() { + return new XyrisTheWrithingStorm(this); + } +} + +class XyrisTheWrithingStormDrawAbility extends TriggeredAbilityImpl { + + public XyrisTheWrithingStormDrawAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new SnakeToken(), 1), false); + } + + public XyrisTheWrithingStormDrawAbility(final XyrisTheWrithingStormDrawAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DREW_CARD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (game.getPlayer(this.getControllerId()).hasOpponent(event.getPlayerId(), game)) { + if (game.isActivePlayer(event.getPlayerId()) + && game.getPhase().getStep().getType() == PhaseStep.DRAW) { + CardsDrawnDuringDrawStepWatcher watcher = game.getState().getWatcher(CardsDrawnDuringDrawStepWatcher.class); + if (watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) > 1) { + return true; + } + } else { + return true; + } + } + return false; + } + + @Override + public TriggeredAbility copy() { + return new XyrisTheWrithingStormDrawAbility(this); + } + + @Override + public String getRule() { + return "Whenever an opponent draws a card except the first one they draw in each of their draw steps, create a 1/1 green Snake creature token."; + } +} + + +class XyrisTheWrithingStormCombatDamageEffect extends OneShotEffect { + + public XyrisTheWrithingStormCombatDamageEffect() { + super(Outcome.DrawCard); + this.staticText = "you and that player each draw that many cards."; + } + + public XyrisTheWrithingStormCombatDamageEffect(final XyrisTheWrithingStormCombatDamageEffect effect) { + super(effect); + } + + @Override + public XyrisTheWrithingStormCombatDamageEffect copy() { + return new XyrisTheWrithingStormCombatDamageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player sourceController = game.getPlayer(source.getControllerId()); + Player damagedPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); + if (sourceController != null && damagedPlayer != null) { + int amount = (Integer) getValue("damage"); + if (amount > 0) { + sourceController.drawCards(amount, source.getSourceId(), game); + damagedPlayer.drawCards(amount, source.getSourceId(), game); + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java new file mode 100644 index 0000000000..a0f527cdec --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java @@ -0,0 +1,121 @@ +package mage.cards.y; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.counter.DistributeCountersEffect; +import mage.abilities.keyword.PartnerWithAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanentAmount; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class YannikScavengingSentinel extends CardImpl { + + public YannikScavengingSentinel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HYENA); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Partner with Nikara, Lair Scavenger + this.addAbility(new PartnerWithAbility("Nikara, Lair Scavenger")); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // When Yannik, Scavenging Sentinel enters the battlefield, exile another creature you control until Yannik leaves the battlefield. When you do, distribute X +1/+1 counters among any number of target creatures, where X is the exiled creature's power. + this.addAbility(new EntersBattlefieldTriggeredAbility(new YannikScavengingSentinelEffect())); + } + + private YannikScavengingSentinel(final YannikScavengingSentinel card) { + super(card); + } + + @Override + public YannikScavengingSentinel copy() { + return new YannikScavengingSentinel(this); + } +} + +class YannikScavengingSentinelEffect extends OneShotEffect { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another creature you control"); + + static { + filter.add(AnotherPredicate.instance); + } + + YannikScavengingSentinelEffect() { + super(Outcome.Benefit); + staticText = "exile another creature you control until {this} leaves the battlefield. " + + "When you do, distribute X +1/+1 counters among any number of target creatures, " + + "where X is the exiled creature's power."; + } + + private YannikScavengingSentinelEffect(final YannikScavengingSentinelEffect effect) { + super(effect); + } + + @Override + public YannikScavengingSentinelEffect copy() { + return new YannikScavengingSentinelEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + if (player == null || sourcePermanent == null + || game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) < 1) { + return false; + } + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + return false; + } + int power = permanent.getPower().getValue(); + new ExileTargetEffect(CardUtil.getExileZoneId( + game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() + ), permanent.getIdName()).setTargetPointer(new FixedTarget(permanent, game)).apply(game, source); + game.addDelayedTriggeredAbility(new OnLeaveReturnExiledToBattlefieldAbility(), source); + if (game.getState().getZone(permanent.getId()) != Zone.BATTLEFIELD) { + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DistributeCountersEffect( + CounterType.P1P1, power, false, "" + ), false, "distribute X +1/+1 counters among any number of target creatures, " + + "where X is the exiled creature's power" + ); + ability.addTarget(new TargetCreaturePermanentAmount(power)); + game.fireReflexiveTriggeredAbility(ability, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/y/YavimayaElder.java b/Mage.Sets/src/mage/cards/y/YavimayaElder.java index 33687685e8..bc46c0636a 100644 --- a/Mage.Sets/src/mage/cards/y/YavimayaElder.java +++ b/Mage.Sets/src/mage/cards/y/YavimayaElder.java @@ -3,7 +3,7 @@ package mage.cards.y; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.GenericManaCost; @@ -34,7 +34,7 @@ public final class YavimayaElder extends CardImpl { this.toughness = new MageInt(1); // When Yavimaya Elder dies, you may search your library for up to two basic land cards, reveal them, and put them into your hand. If you do, shuffle your library. - this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND), true), true)); + this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND), true), true)); // {2}, Sacrifice Yavimaya Elder: Draw a card. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new GenericManaCost(2)); ability.addCost(new SacrificeSourceCost()); diff --git a/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java b/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java index 6794aa5a3b..7aa8e4e55f 100644 --- a/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java +++ b/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java @@ -99,10 +99,10 @@ class YennettCrypticSovereignEffect extends OneShotEffect { choose not to cast it, you draw a card. Keep in mind that revealing a card doesn’t cause it to change zones. This means that the card you draw will be the card you revealed. */ - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } } else { - player.drawCards(1, game); + player.drawCards(1, source.getSourceId(), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java b/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java new file mode 100644 index 0000000000..aefa96f251 --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java @@ -0,0 +1,166 @@ +package mage.cards.y; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.common.CyclingDiscardCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class YidaroWanderingMonster extends CardImpl { + + public YidaroWanderingMonster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DINOSAUR); + this.subtype.add(SubType.TURTLE); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Cycling {1}{R} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}{R}"))); + + // When you cycle Yidaro, Wandering Monster, shuffle it into your library from your graveyard. If you've cycled a card named Yidaro, Wandering Monster four or more times this game, put it onto the battlefield from your graveyard instead. + this.addAbility(new CycleTriggeredAbility(new YidaroWanderingMonsterEffect()) + .addHint(YidaroWanderingMonsterHint.instance), new YidaroWanderingMonsterWatcher()); + } + + private YidaroWanderingMonster(final YidaroWanderingMonster card) { + super(card); + } + + @Override + public YidaroWanderingMonster copy() { + return new YidaroWanderingMonster(this); + } +} + +class YidaroWanderingMonsterEffect extends OneShotEffect { + + YidaroWanderingMonsterEffect() { + super(Outcome.Benefit); + staticText = "shuffle it into your library from your graveyard. " + + "If you've cycled a card named Yidaro, Wandering Monster four or more times this game, " + + "put it onto the battlefield from your graveyard instead."; + } + + private YidaroWanderingMonsterEffect(final YidaroWanderingMonsterEffect effect) { + super(effect); + } + + @Override + public YidaroWanderingMonsterEffect copy() { + return new YidaroWanderingMonsterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Costs costs = (Costs) this.getValue("cycleCosts"); + if (costs == null) { + return false; + } + MageObjectReference cycledCard = costs + .stream() + .filter(CyclingDiscardCost.class::isInstance) + .map(CyclingDiscardCost.class::cast) + .map(CyclingDiscardCost::getCycledCard) + .findFirst() + .orElse(null); + if (cycledCard == null || game.getState().getZone(cycledCard.getSourceId()) != Zone.GRAVEYARD) { + return false; + } + Card card = cycledCard.getCard(game); + if (card == null) { + return false; + } + YidaroWanderingMonsterWatcher watcher = game.getState().getWatcher(YidaroWanderingMonsterWatcher.class); + if (watcher == null || watcher.getYidaroCount(player.getId()) < 4) { + player.putCardsOnBottomOfLibrary(card, game, source, true); + player.shuffleLibrary(source, game); + } else { + player.moveCards(card, Zone.BATTLEFIELD, source, game); + } + return true; + } +} + +enum YidaroWanderingMonsterHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + Player player = game.getPlayer(ability.getControllerId()); + YidaroWanderingMonsterWatcher watcher = game.getState().getWatcher(YidaroWanderingMonsterWatcher.class); + if (player == null || watcher == null) { + return ""; + } + return player.getName() + " has cycled a card named Yidaro, Wandering Monster " + watcher.getYidaroCount(player.getId()) + " times this game"; + } + + @Override + public Hint copy() { + return instance; + } +} + +class YidaroWanderingMonsterWatcher extends Watcher { + + private final Map countMap = new HashMap(); + + YidaroWanderingMonsterWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ACTIVATED_ABILITY) { + return; + } + StackObject object = game.getStack().getStackObject(event.getSourceId()); + if (object == null || !(object.getStackAbility() instanceof CyclingAbility)) { + return; + } + Card card = game.getCard(object.getSourceId()); + if (card != null && "Yidaro, Wandering Monster".equals(card.getName())) { + countMap.merge(object.getControllerId(), 1, Integer::sum); + } + } + + int getYidaroCount(UUID playerId) { + return countMap.getOrDefault(playerId, 0); + } +} diff --git a/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java b/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java new file mode 100644 index 0000000000..b808b8c616 --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java @@ -0,0 +1,141 @@ +package mage.cards.y; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; +import mage.abilities.keyword.CompanionAbility; +import mage.abilities.keyword.CompanionCondition; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.PermanentCard; +import mage.game.permanent.PermanentMeld; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class YorionSkyNomad extends CardImpl { + + public YorionSkyNomad(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W/U}{W/U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.SERPENT); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Companion — Your starting deck contains at least twenty cards more than the minimum deck size. + this.addAbility(new CompanionAbility(YorionSkyNomadCompanionCondition.instance)); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Yorion enters the battlefield, exile any number of other nonland permanents you own and control. Return those cards to the battlefield at the beginning of the next end step. + this.addAbility(new EntersBattlefieldTriggeredAbility(new YorionSkyNomadEffect())); + } + + private YorionSkyNomad(final YorionSkyNomad card) { + super(card); + } + + @Override + public YorionSkyNomad copy() { + return new YorionSkyNomad(this); + } +} + +enum YorionSkyNomadCompanionCondition implements CompanionCondition { + instance; + + @Override + public String getRule() { + return "Your starting deck contains at least twenty cards more than the minimum deck size."; + } + + @Override + public boolean isLegal(Set deck, int startingSize) { + return deck.size() >= startingSize + 20; + } +} + +class YorionSkyNomadEffect extends OneShotEffect { + + private static final FilterPermanent filter + = new FilterControlledPermanent("other nonland permanents you own and control"); + + static { + filter.add(Predicates.not(CardType.LAND.getPredicate())); + filter.add(TargetController.YOU.getOwnerPredicate()); + filter.add(AnotherPredicate.instance); + } + + YorionSkyNomadEffect() { + super(Outcome.Benefit); + staticText = "exile any number of other nonland permanents you own and control. " + + "Return those cards to the battlefield at the beginning of the next end step."; + } + + private YorionSkyNomadEffect(final YorionSkyNomadEffect effect) { + super(effect); + } + + @Override + public YorionSkyNomadEffect copy() { + return new YorionSkyNomadEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (sourceObject == null || controller == null) { + return false; + } + TargetPermanent target = new TargetPermanent(0, Integer.MAX_VALUE, filter, true); + controller.choose(outcome, target, source.getSourceId(), game); + Set toExile = target.getTargets().stream().map(game::getPermanent).collect(Collectors.toSet()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + controller.moveCardsToExile(toExile, source, game, true, exileId, sourceObject.getIdName()); + + Cards cardsToReturn = new CardsImpl(); + for (Card exiled : toExile) { + if (exiled instanceof PermanentMeld) { + MeldCard meldCard = (MeldCard) ((PermanentCard) exiled).getCard(); + Card topCard = meldCard.getTopHalfCard(); + Card bottomCard = meldCard.getBottomHalfCard(); + if (topCard.getZoneChangeCounter(game) == meldCard.getTopLastZoneChangeCounter()) { + cardsToReturn.add(topCard); + } + if (bottomCard.getZoneChangeCounter(game) == meldCard.getBottomLastZoneChangeCounter()) { + cardsToReturn.add(bottomCard); + } + } else if (exiled.getZoneChangeCounter(game) == game.getState().getZoneChangeCounter(exiled.getId()) - 1) { + cardsToReturn.add(exiled); + } + } + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); + effect.setTargetPointer(new FixedTargets(cardsToReturn, game)); + AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); + game.addDelayedTriggeredAbility(delayedAbility, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java b/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java index 580cbdc595..a0f22942db 100644 --- a/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java +++ b/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java @@ -89,11 +89,11 @@ class YorvoLordOfGarenbrigEffect extends OneShotEffect { return false; } sourcePerm.addCounters(CounterType.P1P1.createInstance(), source, game); - Permanent permanent = ((FixedTarget) targetPointer).getTargetedPermanentOrLKIBattlefield(game); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent == null) { return true; } - game.applyEffects(); + game.getState().processAction(game); if (permanent.getPower().getValue() > sourcePerm.getPower().getValue()) { sourcePerm.addCounters(CounterType.P1P1.createInstance(), source, game); } diff --git a/Mage.Sets/src/mage/cards/y/YoseiTheMorningStar.java b/Mage.Sets/src/mage/cards/y/YoseiTheMorningStar.java index 1eed365c5a..32c1b591a1 100644 --- a/Mage.Sets/src/mage/cards/y/YoseiTheMorningStar.java +++ b/Mage.Sets/src/mage/cards/y/YoseiTheMorningStar.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SkipNextPlayerUntapStepEffect; import mage.abilities.keyword.FlyingAbility; @@ -44,7 +44,7 @@ public final class YoseiTheMorningStar extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Yosei, the Morning Star dies, target player skips their next untap step. Tap up to five target permanents that player controls. - Ability ability = new DiesTriggeredAbility(new SkipNextPlayerUntapStepEffect("target")); + Ability ability = new DiesSourceTriggeredAbility(new SkipNextPlayerUntapStepEffect("target")); ability.addTarget(new TargetPlayer()); ability.addTarget(new YoseiTheMorningStarTarget()); ability.addEffect(new YoseiTheMorningStarTapEffect()); diff --git a/Mage.Sets/src/mage/cards/y/YoungPyromancer.java b/Mage.Sets/src/mage/cards/y/YoungPyromancer.java index d5e647087f..6c5f1f6f24 100644 --- a/Mage.Sets/src/mage/cards/y/YoungPyromancer.java +++ b/Mage.Sets/src/mage/cards/y/YoungPyromancer.java @@ -8,7 +8,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.game.permanent.token.YoungPyromancerElementalToken; +import mage.game.permanent.token.RedElementalToken; import java.util.UUID; @@ -27,7 +27,7 @@ public final class YoungPyromancer extends CardImpl { // Whenever you cast an instant or sorcery spell, create a 1/1 red Elemental creature token. this.addAbility(new SpellCastControllerTriggeredAbility( - new CreateTokenEffect(new YoungPyromancerElementalToken()), + new CreateTokenEffect(new RedElementalToken()), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false )); diff --git a/Mage.Sets/src/mage/cards/y/YouthfulScholar.java b/Mage.Sets/src/mage/cards/y/YouthfulScholar.java index ed3422bd95..dda5ec2eaa 100644 --- a/Mage.Sets/src/mage/cards/y/YouthfulScholar.java +++ b/Mage.Sets/src/mage/cards/y/YouthfulScholar.java @@ -3,7 +3,7 @@ package mage.cards.y; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class YouthfulScholar extends CardImpl { this.toughness = new MageInt(2); // When Youthful Scholar dies, draw two cards. - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(2), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(2), false)); } public YouthfulScholar(final YouthfulScholar card) { diff --git a/Mage.Sets/src/mage/cards/z/ZagothCrystal.java b/Mage.Sets/src/mage/cards/z/ZagothCrystal.java new file mode 100644 index 0000000000..b77d166ddf --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZagothCrystal.java @@ -0,0 +1,39 @@ +package mage.cards.z; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZagothCrystal extends CardImpl { + + public ZagothCrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add {B}, {G}, or {U}. + this.addAbility(new BlackManaAbility()); + this.addAbility(new GreenManaAbility()); + this.addAbility(new BlueManaAbility()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + private ZagothCrystal(final ZagothCrystal card) { + super(card); + } + + @Override + public ZagothCrystal copy() { + return new ZagothCrystal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZagothMamba.java b/Mage.Sets/src/mage/cards/z/ZagothMamba.java new file mode 100644 index 0000000000..e91a88ac18 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZagothMamba.java @@ -0,0 +1,42 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.MutatesSourceTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZagothMamba extends CardImpl { + + public ZagothMamba(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.SNAKE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever this creature mutates, target creature an opponent controls gets -2/-2 until end of turn. + Ability ability = new MutatesSourceTriggeredAbility(new BoostTargetEffect(-2, -2)); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private ZagothMamba(final ZagothMamba card) { + super(card); + } + + @Override + public ZagothMamba copy() { + return new ZagothMamba(this); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZagothTriome.java b/Mage.Sets/src/mage/cards/z/ZagothTriome.java new file mode 100644 index 0000000000..ba11f10e79 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZagothTriome.java @@ -0,0 +1,48 @@ +package mage.cards.z; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZagothTriome extends CardImpl { + + public ZagothTriome(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.SWAMP); + this.subtype.add(SubType.FOREST); + this.subtype.add(SubType.ISLAND); + + // ({T}: Add {B}, {G}, or {U}.) + this.addAbility(new BlackManaAbility()); + this.addAbility(new GreenManaAbility()); + this.addAbility(new BlueManaAbility()); + + // Zagoth Triome enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Cycling {3} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"))); + } + + private ZagothTriome(final ZagothTriome card) { + super(card); + } + + @Override + public ZagothTriome copy() { + return new ZagothTriome(this); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZaxaraTheExemplary.java b/Mage.Sets/src/mage/cards/z/ZaxaraTheExemplary.java new file mode 100644 index 0000000000..a5b097d41d --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZaxaraTheExemplary.java @@ -0,0 +1,146 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.Token; +import mage.game.permanent.token.ZaxaraTheExemplaryHydraToken; +import mage.game.stack.Spell; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author AsterAether + */ +public final class ZaxaraTheExemplary extends CardImpl { + + final static String needPrefix = "_ZaxaraTheExemplary_NeedToken"; + + public ZaxaraTheExemplary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.HYDRA); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // {T}: Add two mana of any one color. + Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(2), new TapSourceCost()); + this.addAbility(ability); + + // Whenever you cast a spell with {X} in its mana cost, create a 0/0 green Hydra creature token, then put X +1/+1 counters on it. + this.addAbility(new ZaxaraTheExemplaryHydraTokenAbility()); + } + + private ZaxaraTheExemplary(final ZaxaraTheExemplary card) { + super(card); + } + + @Override + public ZaxaraTheExemplary copy() { + return new ZaxaraTheExemplary(this); + } +} + +class ZaxaraTheExemplaryHydraTokenAbility extends TriggeredAbilityImpl { + + public ZaxaraTheExemplaryHydraTokenAbility() { + super(Zone.BATTLEFIELD, new ZaxaraTheExemplaryHydraTokenEffect(), false); + } + + public ZaxaraTheExemplaryHydraTokenAbility(final ZaxaraTheExemplaryHydraTokenAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getPlayerId().equals(getControllerId())) { + if (event.getType() == GameEvent.EventType.SPELL_CAST) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null) { + if (spell.getSpellAbility().getManaCostsToPay().containsX()) { + game.getState().setValue(this.getSourceId() + ZaxaraTheExemplary.needPrefix, spell); + return true; + } + } + } + + } + return false; + } + + @Override + public TriggeredAbility copy() { + return new ZaxaraTheExemplaryHydraTokenAbility(this); + } + + @Override + public String getRule() { + return "Whenever you cast a spell with {X} in its mana cost" + super.getRule(); + } +} + +class ZaxaraTheExemplaryHydraTokenEffect extends OneShotEffect { + ZaxaraTheExemplaryHydraTokenEffect() { + super(Outcome.Benefit); + this.staticText = ", create a 0/0 green Hydra creature token, then put X +1/+1 counters on it."; + } + + ZaxaraTheExemplaryHydraTokenEffect(final ZaxaraTheExemplaryHydraTokenEffect effect) { + super(effect); + } + + @Override + public ZaxaraTheExemplaryHydraTokenEffect copy() { + return new ZaxaraTheExemplaryHydraTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); + if (player != null && controller != null) { + Object needObject = game.getState().getValue(source.getSourceId() + ZaxaraTheExemplary.needPrefix); + + // create token + if (needObject instanceof Spell) { + Spell spell = (Spell) needObject; + int xValue = spell.getSpellAbility().getManaCostsToPay().getX(); + + Token hydraToken = new ZaxaraTheExemplaryHydraToken(); + hydraToken.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + + for (UUID tokenId : hydraToken.getLastAddedTokenIds()) { + Permanent permanent = game.getPermanent(tokenId); + if (permanent != null) + permanent.addCounters(CounterType.P1P1.createInstance(xValue), source, game); + } + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/z/ZektarShrineExpedition.java b/Mage.Sets/src/mage/cards/z/ZektarShrineExpedition.java index 49c3e041c7..58e317fe83 100644 --- a/Mage.Sets/src/mage/cards/z/ZektarShrineExpedition.java +++ b/Mage.Sets/src/mage/cards/z/ZektarShrineExpedition.java @@ -1,7 +1,5 @@ - package mage.cards.z; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.LandfallAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -17,10 +15,11 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; -import mage.game.permanent.token.ZektarShrineElementalToken; +import mage.game.permanent.token.RedElementalWithTrampleAndHaste; + +import java.util.UUID; /** - * * @author North */ public final class ZektarShrineExpedition extends CardImpl { @@ -65,7 +64,7 @@ class ZektarShrineExpeditionEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - CreateTokenEffect effect = new CreateTokenEffect(new ZektarShrineElementalToken()); + CreateTokenEffect effect = new CreateTokenEffect(new RedElementalWithTrampleAndHaste()); if (effect.apply(game, source)) { effect.exileTokensCreatedAtNextEndStep(game, source); return true; diff --git a/Mage.Sets/src/mage/cards/z/ZenithFlare.java b/Mage.Sets/src/mage/cards/z/ZenithFlare.java new file mode 100644 index 0000000000..73be511110 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZenithFlare.java @@ -0,0 +1,49 @@ +package mage.cards.z; + +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZenithFlare extends CardImpl { + + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(new AbilityPredicate(CyclingAbility.class)); + } + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(filter); + + public ZenithFlare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}{W}"); + + // Zenith Flare deals X damage to any target and you gain X life, where X is the number of cards with a cycling ability in your graveyard. + this.getSpellAbility().addEffect(new DamageTargetEffect(xValue) + .setText("{this} deals X damage to any target")); + this.getSpellAbility().addEffect(new GainLifeEffect(xValue) + .setText("and you gain X life, where X is the number of cards with a cycling ability in your graveyard")); + this.getSpellAbility().addTarget(new TargetAnyTarget()); + } + + private ZenithFlare(final ZenithFlare card) { + super(card); + } + + @Override + public ZenithFlare copy() { + return new ZenithFlare(this); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZilorthaStrengthIncarnate.java b/Mage.Sets/src/mage/cards/z/ZilorthaStrengthIncarnate.java new file mode 100644 index 0000000000..6fc791ed35 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZilorthaStrengthIncarnate.java @@ -0,0 +1,82 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author htrajan + */ +public final class ZilorthaStrengthIncarnate extends CardImpl { + + public ZilorthaStrengthIncarnate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{G}"); + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(7); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ZilorthaStrengthIncarnateEffect())); + } + + private ZilorthaStrengthIncarnate(ZilorthaStrengthIncarnate card) { + super(card); + } + + @Override + public Card copy() { + return new ZilorthaStrengthIncarnate(this); + } +} + +class ZilorthaStrengthIncarnateEffect extends ContinuousEffectImpl { + + ZilorthaStrengthIncarnateEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Lethal damage dealt to creatures you control is determined by their power rather than their toughness"; + } + + private ZilorthaStrengthIncarnateEffect(ZilorthaStrengthIncarnateEffect effect) { + super(effect); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + // Change the rule + FilterCreaturePermanent filter = StaticFilters.FILTER_PERMANENT_CREATURE.copy(); + filter.add(new ControllerIdPredicate(source.getControllerId())); + game.getState().addPowerInsteadOfToughnessForDamageLethalityFilter(source.getSourceId(), filter); + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.RulesEffects; + } + + @Override + public ContinuousEffect copy() { + return new ZilorthaStrengthIncarnateEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/z/ZirdaTheDawnwaker.java b/Mage.Sets/src/mage/cards/z/ZirdaTheDawnwaker.java new file mode 100644 index 0000000000..f86246c513 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZirdaTheDawnwaker.java @@ -0,0 +1,129 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.MageObject; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.CompanionAbility; +import mage.abilities.keyword.CompanionCondition; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZirdaTheDawnwaker extends CardImpl { + + public ZirdaTheDawnwaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R/W}{R/W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.FOX); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Companion — Each permanent card in your starting deck has an activated ability. + this.addAbility(new CompanionAbility(ZirdaTheDawnwakerCompanionCondition.instance)); + + // Abilities you activate that aren't mana abilities cost {2} less to activate. + // This effect can't reduce the mana in that cost to less than one mana. + this.addAbility(new SimpleStaticAbility(new ZirdaTheDawnwakerEffect())); + + // {1}, {T}: Target creature can't block this turn. + Ability ability = new SimpleActivatedAbility( + new CantBlockTargetEffect(Duration.EndOfTurn), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private ZirdaTheDawnwaker(final ZirdaTheDawnwaker card) { + super(card); + } + + @Override + public ZirdaTheDawnwaker copy() { + return new ZirdaTheDawnwaker(this); + } +} + +enum ZirdaTheDawnwakerCompanionCondition implements CompanionCondition { + instance; + + @Override + public String getRule() { + return "Each permanent card in your starting deck has an activated ability."; + } + + @Override + public boolean isLegal(Set deck, int startingSize) { + return deck + .stream() + .filter(MageObject::isPermanent) + .allMatch(card -> card + .getAbilities() + .stream() + .map(Ability::getAbilityType) + .anyMatch(abilityType -> abilityType == AbilityType.ACTIVATED + || abilityType == AbilityType.MANA) + ); + } +} + +class ZirdaTheDawnwakerEffect extends CostModificationEffectImpl { + + ZirdaTheDawnwakerEffect() { + super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "Abilities you activate that aren't mana abilities cost {2} less to activate. " + + "This effect can't reduce the mana in that cost to less than one mana."; + } + + private ZirdaTheDawnwakerEffect(final ZirdaTheDawnwakerEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + Mana mana = abilityToModify.getManaCostsToPay().getMana(); + int reduceMax = mana.getGeneric(); + if (reduceMax > 0 + && mana.count() == mana.getGeneric()) { + reduceMax--; + } + reduceMax = Math.min(reduceMax, 2); + if (reduceMax <= 0) { + return true; + } + CardUtil.reduceCost(abilityToModify, reduceMax); + return true; + + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify.getAbilityType() == AbilityType.ACTIVATED + && abilityToModify.isControlledBy(source.getControllerId()) + && abilityToModify.getAbilityType() != AbilityType.MANA; // no mana abilities + } + + @Override + public ZirdaTheDawnwakerEffect copy() { + return new ZirdaTheDawnwakerEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java b/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java index cd88d8ad9e..20feb6ecd8 100644 --- a/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java +++ b/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java @@ -60,7 +60,7 @@ class ZirilanOfTheClawEffect extends OneShotEffect { public ZirilanOfTheClawEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "Search your library for a Dragon permanent card and put that card onto the battlefield. Then shuffle your library." - + "That Dragon gains haste until end of turn. Exile it at the beginning of the next end step"; + + " That Dragon gains haste until end of turn. Exile it at the beginning of the next end step"; } public ZirilanOfTheClawEffect(final ZirilanOfTheClawEffect effect) { diff --git a/Mage.Sets/src/mage/cards/z/ZodiacDog.java b/Mage.Sets/src/mage/cards/z/ZodiacDog.java index 9fb27e6f9b..d0e42b72b4 100644 --- a/Mage.Sets/src/mage/cards/z/ZodiacDog.java +++ b/Mage.Sets/src/mage/cards/z/ZodiacDog.java @@ -17,7 +17,7 @@ public final class ZodiacDog extends CardImpl { public ZodiacDog(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); - this.subtype.add(SubType.HOUND); + this.subtype.add(SubType.DOG); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/z/ZodiacDragon.java b/Mage.Sets/src/mage/cards/z/ZodiacDragon.java index e9b40d218c..5a862db733 100644 --- a/Mage.Sets/src/mage/cards/z/ZodiacDragon.java +++ b/Mage.Sets/src/mage/cards/z/ZodiacDragon.java @@ -3,7 +3,7 @@ package mage.cards.z; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class ZodiacDragon extends CardImpl { this.toughness = new MageInt(8); // When Zodiac Dragon is put into your graveyard from the battlefield, you may return it to your hand. - this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect(), true)); + this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect(), true)); } public ZodiacDragon(final ZodiacDragon card) { diff --git a/Mage.Sets/src/mage/cards/z/ZombieApocalypse.java b/Mage.Sets/src/mage/cards/z/ZombieApocalypse.java index 530bd9e7a1..5c06588625 100644 --- a/Mage.Sets/src/mage/cards/z/ZombieApocalypse.java +++ b/Mage.Sets/src/mage/cards/z/ZombieApocalypse.java @@ -66,7 +66,7 @@ class ZombieApocalypseEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { controller.moveCards(controller.getGraveyard().getCards(filterZombie, game), Zone.BATTLEFIELD, source, game, true, false, false, null); - game.applyEffects(); + game.getState().processAction(game); for (Permanent permanent : game.getBattlefield().getActivePermanents( new FilterPermanent(SubType.HUMAN, "Humans"), source.getControllerId(), game)) { permanent.destroy(source.getSourceId(), game, false); diff --git a/Mage.Sets/src/mage/cards/z/ZursWeirding.java b/Mage.Sets/src/mage/cards/z/ZursWeirding.java index df49f02d4c..ea6fe4bbd8 100644 --- a/Mage.Sets/src/mage/cards/z/ZursWeirding.java +++ b/Mage.Sets/src/mage/cards/z/ZursWeirding.java @@ -98,7 +98,7 @@ class ZursWeirdingReplacementEffect extends ReplacementEffectImpl { continue; } Player otherPlayer = game.getPlayer(playerId); - if (otherPlayer.canPayLifeCost() + if (otherPlayer.canPayLifeCost(source) && otherPlayer.getLife() >= 2) { PayLifeCost lifeCost = new PayLifeCost(2); while (otherPlayer.canRespond() diff --git a/Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java b/Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java new file mode 100644 index 0000000000..9cc45d7afa --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java @@ -0,0 +1,186 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.MageItem; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.DevilToken; +import mage.players.Player; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZurzothChaosRider extends CardImpl { + + public ZurzothChaosRider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DEVIL); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever an opponent draws their first card each turn, if it's not their turn, you create a 1/1 red Devil creature token with "When this creature dies, it deals 1 damage to any target." + this.addAbility(new ZurzothChaosRiderDrawAbility()); + + // Whenever one or more Devils you control attack one or more players, you and those players each draw a card, then discard a card at random. + this.addAbility(new ZurzothChaosRiderAttackAbility()); + } + + private ZurzothChaosRider(final ZurzothChaosRider card) { + super(card); + } + + @Override + public ZurzothChaosRider copy() { + return new ZurzothChaosRider(this); + } +} + +class ZurzothChaosRiderDrawAbility extends TriggeredAbilityImpl { + + private final Set playerIds = new HashSet<>(); + + ZurzothChaosRiderDrawAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new DevilToken())); + } + + private ZurzothChaosRiderDrawAbility(final ZurzothChaosRiderDrawAbility ability) { + super(ability); + this.playerIds.addAll(ability.playerIds); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DREW_CARD + || event.getType() == GameEvent.EventType.END_PHASE_POST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + switch (event.getType()) { + case END_PHASE_POST: + playerIds.clear(); + return false; + case DREW_CARD: + if (playerIds.contains(event.getPlayerId())) { + return false; + } + playerIds.add(event.getPlayerId()); + return !game.isActivePlayer(event.getPlayerId()) + && game.getOpponents(this.getControllerId()).contains(event.getPlayerId()); + default: + return false; + } + } + + @Override + public ZurzothChaosRiderDrawAbility copy() { + return new ZurzothChaosRiderDrawAbility(this); + } + + @Override + public String getRule() { + return "Whenever an opponent draws their first card each turn, if it's not their turn, " + + "you create a 1/1 red Devil creature token with \"When this creature dies, it deals 1 damage to any target.\""; + } +} + +class ZurzothChaosRiderAttackAbility extends TriggeredAbilityImpl { + + ZurzothChaosRiderAttackAbility() { + super(Zone.BATTLEFIELD, null); + } + + private ZurzothChaosRiderAttackAbility(final ZurzothChaosRiderAttackAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Set playerIds = new HashSet<>(); + game.getCombat() + .getAttackers() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .filter(permanent -> permanent.hasSubtype(SubType.DEVIL, game)) + .map(MageItem::getId) + .map(game.getCombat()::getDefenderId) + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(MageItem::getId) + .forEach(playerIds::add); + if (playerIds.isEmpty()) { + return false; + } + playerIds.add(getControllerId()); + this.getEffects().clear(); + this.addEffect(new ZurzothChaosRiderEffect(playerIds)); + return true; + } + + @Override + public ZurzothChaosRiderAttackAbility copy() { + return new ZurzothChaosRiderAttackAbility(this); + } + + @Override + public String getRule() { + return "Whenever one or more Devils you control attack one or more players, " + + "you and those players each draw a card, then discard a card at random."; + } +} + +class ZurzothChaosRiderEffect extends OneShotEffect { + + private final Set playerIds = new HashSet<>(); + + ZurzothChaosRiderEffect(Set playerIds) { + super(Outcome.Benefit); + this.playerIds.addAll(playerIds); + } + + private ZurzothChaosRiderEffect(final ZurzothChaosRiderEffect effect) { + super(effect); + this.playerIds.addAll(effect.playerIds); + } + + @Override + public ZurzothChaosRiderEffect copy() { + return new ZurzothChaosRiderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + if (!playerIds.contains(playerId)) { + continue; + } + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + player.drawCards(1, source.getSourceId(), game); + player.discard(1, true, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/Alliances.java b/Mage.Sets/src/mage/sets/Alliances.java index a93161e725..b96c3e42f8 100644 --- a/Mage.Sets/src/mage/sets/Alliances.java +++ b/Mage.Sets/src/mage/sets/Alliances.java @@ -1,220 +1,220 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -public final class Alliances extends ExpansionSet { - - private static final Alliances instance = new Alliances(); - - public static Alliances getInstance() { - return instance; - } - - private Alliances() { - super("Alliances", "ALL", ExpansionSet.buildDate(1996, 6, 10), SetType.EXPANSION); - this.blockName = "Ice Age"; - this.parentSet = IceAge.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 8; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - - cards.add(new SetCardInfo("Aesthir Glider", "116a", Rarity.COMMON, mage.cards.a.AesthirGlider.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Aesthir Glider", "116b", Rarity.COMMON, mage.cards.a.AesthirGlider.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Agent of Stromgald", "64a", Rarity.COMMON, mage.cards.a.AgentOfStromgald.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Agent of Stromgald", "64b", Rarity.COMMON, mage.cards.a.AgentOfStromgald.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Arcane Denial", "22a", Rarity.COMMON, mage.cards.a.ArcaneDenial.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Arcane Denial", "22b", Rarity.COMMON, mage.cards.a.ArcaneDenial.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ashnod's Cylix", 117, Rarity.RARE, mage.cards.a.AshnodsCylix.class)); - cards.add(new SetCardInfo("Astrolabe", "118a", Rarity.COMMON, mage.cards.a.Astrolabe.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Astrolabe", "118b", Rarity.COMMON, mage.cards.a.Astrolabe.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Awesome Presence", "23a", Rarity.COMMON, mage.cards.a.AwesomePresence.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Awesome Presence", "23b", Rarity.COMMON, mage.cards.a.AwesomePresence.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Balduvian Dead", 43, Rarity.UNCOMMON, mage.cards.b.BalduvianDead.class)); - cards.add(new SetCardInfo("Balduvian Horde", 65, Rarity.RARE, mage.cards.b.BalduvianHorde.class)); - cards.add(new SetCardInfo("Balduvian Trading Post", 137, Rarity.RARE, mage.cards.b.BalduvianTradingPost.class)); - cards.add(new SetCardInfo("Balduvian War-Makers", "66a", Rarity.COMMON, mage.cards.b.BalduvianWarMakers.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Balduvian War-Makers", "66b", Rarity.COMMON, mage.cards.b.BalduvianWarMakers.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Benthic Explorers", "24a", Rarity.COMMON, mage.cards.b.BenthicExplorers.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Benthic Explorers", "24b", Rarity.COMMON, mage.cards.b.BenthicExplorers.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Bestial Fury", "67a", Rarity.COMMON, mage.cards.b.BestialFury.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Bestial Fury", "67b", Rarity.COMMON, mage.cards.b.BestialFury.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Bounty of the Hunt", 85, Rarity.UNCOMMON, mage.cards.b.BountyOfTheHunt.class)); - cards.add(new SetCardInfo("Browse", 25, Rarity.UNCOMMON, mage.cards.b.Browse.class)); - cards.add(new SetCardInfo("Burnout", 68, Rarity.UNCOMMON, mage.cards.b.Burnout.class)); - cards.add(new SetCardInfo("Carrier Pigeons", "1a", Rarity.COMMON, mage.cards.c.CarrierPigeons.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Carrier Pigeons", "1b", Rarity.COMMON, mage.cards.c.CarrierPigeons.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Casting of Bones", "44a", Rarity.COMMON, mage.cards.c.CastingOfBones.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Casting of Bones", "44b", Rarity.COMMON, mage.cards.c.CastingOfBones.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Chaos Harlequin", 69, Rarity.RARE, mage.cards.c.ChaosHarlequin.class)); - cards.add(new SetCardInfo("Contagion", 45, Rarity.UNCOMMON, mage.cards.c.Contagion.class)); - cards.add(new SetCardInfo("Deadly Insect", "86a", Rarity.COMMON, mage.cards.d.DeadlyInsect.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Deadly Insect", "86b", Rarity.COMMON, mage.cards.d.DeadlyInsect.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Death Spark", 70, Rarity.UNCOMMON, mage.cards.d.DeathSpark.class)); - cards.add(new SetCardInfo("Diminishing Returns", 26, Rarity.RARE, mage.cards.d.DiminishingReturns.class)); - cards.add(new SetCardInfo("Diseased Vermin", 46, Rarity.UNCOMMON, mage.cards.d.DiseasedVermin.class)); - cards.add(new SetCardInfo("Dystopia", 47, Rarity.RARE, mage.cards.d.Dystopia.class)); - cards.add(new SetCardInfo("Elvish Bard", 87, Rarity.UNCOMMON, mage.cards.e.ElvishBard.class)); - cards.add(new SetCardInfo("Elvish Ranger", "88a", Rarity.COMMON, mage.cards.e.ElvishRanger.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Elvish Ranger", "88b", Rarity.COMMON, mage.cards.e.ElvishRanger.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Elvish Spirit Guide", 89, Rarity.UNCOMMON, mage.cards.e.ElvishSpiritGuide.class)); - cards.add(new SetCardInfo("Energy Arc", 106, Rarity.UNCOMMON, mage.cards.e.EnergyArc.class)); - cards.add(new SetCardInfo("Enslaved Scout", "71a", Rarity.COMMON, mage.cards.e.EnslavedScout.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Enslaved Scout", "71b", Rarity.COMMON, mage.cards.e.EnslavedScout.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Errand of Duty", "2a", Rarity.COMMON, mage.cards.e.ErrandOfDuty.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Errand of Duty", "2b", Rarity.COMMON, mage.cards.e.ErrandOfDuty.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Exile", 3, Rarity.RARE, mage.cards.e.Exile.class)); - cards.add(new SetCardInfo("False Demise", "27a", Rarity.COMMON, mage.cards.f.FalseDemise.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("False Demise", "27b", Rarity.COMMON, mage.cards.f.FalseDemise.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fatal Lore", 48, Rarity.RARE, mage.cards.f.FatalLore.class)); - cards.add(new SetCardInfo("Feast or Famine", "49a", Rarity.COMMON, mage.cards.f.FeastOrFamine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Feast or Famine", "49b", Rarity.COMMON, mage.cards.f.FeastOrFamine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fevered Strength", "50a", Rarity.COMMON, mage.cards.f.FeveredStrength.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fevered Strength", "50b", Rarity.COMMON, mage.cards.f.FeveredStrength.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Floodwater Dam", 119, Rarity.RARE, mage.cards.f.FloodwaterDam.class)); - cards.add(new SetCardInfo("Force of Will", 28, Rarity.UNCOMMON, mage.cards.f.ForceOfWill.class)); - cards.add(new SetCardInfo("Foresight", "29a", Rarity.COMMON, mage.cards.f.Foresight.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Foresight", "29b", Rarity.COMMON, mage.cards.f.Foresight.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fyndhorn Druid", "90a", Rarity.COMMON, mage.cards.f.FyndhornDruid.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fyndhorn Druid", "90b", Rarity.COMMON, mage.cards.f.FyndhornDruid.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gargantuan Gorilla", 91, Rarity.RARE, mage.cards.g.GargantuanGorilla.class)); - cards.add(new SetCardInfo("Gift of the Woods", "92a", Rarity.COMMON, mage.cards.g.GiftOfTheWoods.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gift of the Woods", "92b", Rarity.COMMON, mage.cards.g.GiftOfTheWoods.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Berserkers", "93a", Rarity.COMMON, mage.cards.g.GorillaBerserkers.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Berserkers", "93b", Rarity.COMMON, mage.cards.g.GorillaBerserkers.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Chieftain", "94a", Rarity.COMMON, mage.cards.g.GorillaChieftain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Chieftain", "94b", Rarity.COMMON, mage.cards.g.GorillaChieftain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Shaman", "72a", Rarity.COMMON, mage.cards.g.GorillaShaman.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Shaman", "72b", Rarity.COMMON, mage.cards.g.GorillaShaman.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla War Cry", "73a", Rarity.COMMON, mage.cards.g.GorillaWarCry.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla War Cry", "73b", Rarity.COMMON, mage.cards.g.GorillaWarCry.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Guerrilla Tactics", "74a", Rarity.COMMON, mage.cards.g.GuerrillaTactics.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Guerrilla Tactics", "74b", Rarity.COMMON, mage.cards.g.GuerrillaTactics.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gustha's Scepter", 120, Rarity.RARE, mage.cards.g.GusthasScepter.class)); - cards.add(new SetCardInfo("Hail Storm", 95, Rarity.UNCOMMON, mage.cards.h.HailStorm.class)); - cards.add(new SetCardInfo("Heart of Yavimaya", 138, Rarity.RARE, mage.cards.h.HeartOfYavimaya.class)); - cards.add(new SetCardInfo("Helm of Obedience", 121, Rarity.RARE, mage.cards.h.HelmOfObedience.class)); - cards.add(new SetCardInfo("Inheritance", 4, Rarity.UNCOMMON, mage.cards.i.Inheritance.class)); - cards.add(new SetCardInfo("Insidious Bookworms", "51a", Rarity.COMMON, mage.cards.i.InsidiousBookworms.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Insidious Bookworms", "51b", Rarity.COMMON, mage.cards.i.InsidiousBookworms.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ivory Gargoyle", 5, Rarity.RARE, mage.cards.i.IvoryGargoyle.class)); - cards.add(new SetCardInfo("Juniper Order Advocate", 6, Rarity.UNCOMMON, mage.cards.j.JuniperOrderAdvocate.class)); - cards.add(new SetCardInfo("Kaysa", 96, Rarity.RARE, mage.cards.k.Kaysa.class)); - cards.add(new SetCardInfo("Keeper of Tresserhorn", 52, Rarity.RARE, mage.cards.k.KeeperOfTresserhorn.class)); - cards.add(new SetCardInfo("Kjeldoran Escort", "7a", Rarity.COMMON, mage.cards.k.KjeldoranEscort.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kjeldoran Escort", "7b", Rarity.COMMON, mage.cards.k.KjeldoranEscort.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kjeldoran Pride", "9a", Rarity.COMMON, mage.cards.k.KjeldoranPride.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kjeldoran Pride", "9b", Rarity.COMMON, mage.cards.k.KjeldoranPride.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kjeldoran Home Guard", 8, Rarity.UNCOMMON, mage.cards.k.KjeldoranHomeGuard.class)); - cards.add(new SetCardInfo("Kjeldoran Outpost", 139, Rarity.RARE, mage.cards.k.KjeldoranOutpost.class)); - cards.add(new SetCardInfo("Krovikan Horror", 53, Rarity.RARE, mage.cards.k.KrovikanHorror.class)); - cards.add(new SetCardInfo("Krovikan Plague", 54, Rarity.UNCOMMON, mage.cards.k.KrovikanPlague.class)); - cards.add(new SetCardInfo("Lake of the Dead", 140, Rarity.RARE, mage.cards.l.LakeOfTheDead.class)); - cards.add(new SetCardInfo("Lat-Nam's Legacy", "30a", Rarity.COMMON, mage.cards.l.LatNamsLegacy.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Lat-Nam's Legacy", "30b", Rarity.COMMON, mage.cards.l.LatNamsLegacy.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Library of Lat-Nam", 31, Rarity.RARE, mage.cards.l.LibraryOfLatNam.class)); - cards.add(new SetCardInfo("Lim-Dul's High Guard", "55a", Rarity.COMMON, mage.cards.l.LimDulsHighGuard.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Lim-Dul's High Guard", "55b", Rarity.COMMON, mage.cards.l.LimDulsHighGuard.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Lim-Dul's Paladin", 108, Rarity.UNCOMMON, mage.cards.l.LimDulsPaladin.class)); - cards.add(new SetCardInfo("Lim-Dul's Vault", 107, Rarity.UNCOMMON, mage.cards.l.LimDulsVault.class)); - cards.add(new SetCardInfo("Lodestone Bauble", 122, Rarity.RARE, mage.cards.l.LodestoneBauble.class)); - cards.add(new SetCardInfo("Lord of Tresserhorn", 112, Rarity.RARE, mage.cards.l.LordOfTresserhorn.class)); - cards.add(new SetCardInfo("Martyrdom", "10a", Rarity.COMMON, mage.cards.m.Martyrdom.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Martyrdom", "10b", Rarity.COMMON, mage.cards.m.Martyrdom.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Misfortune", 113, Rarity.RARE, mage.cards.m.Misfortune.class)); - cards.add(new SetCardInfo("Mishra's Groundbreaker", 123, Rarity.UNCOMMON, mage.cards.m.MishrasGroundbreaker.class)); - cards.add(new SetCardInfo("Misinformation", 56, Rarity.UNCOMMON, mage.cards.m.Misinformation.class)); - cards.add(new SetCardInfo("Mystic Compass", 124, Rarity.UNCOMMON, mage.cards.m.MysticCompass.class)); - cards.add(new SetCardInfo("Nature's Blessing", 110, Rarity.UNCOMMON, mage.cards.n.NaturesBlessing.class)); - cards.add(new SetCardInfo("Nature's Chosen", 97, Rarity.UNCOMMON, mage.cards.n.NaturesChosen.class)); - cards.add(new SetCardInfo("Nature's Wrath", 98, Rarity.RARE, mage.cards.n.NaturesWrath.class)); - cards.add(new SetCardInfo("Noble Steeds", "11a", Rarity.COMMON, mage.cards.n.NobleSteeds.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Noble Steeds", "11b", Rarity.COMMON, mage.cards.n.NobleSteeds.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Omen of Fire", 75, Rarity.RARE, mage.cards.o.OmenOfFire.class)); - cards.add(new SetCardInfo("Phantasmal Fiend", "57a", Rarity.COMMON, mage.cards.p.PhantasmalFiend.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phantasmal Fiend", "57b", Rarity.COMMON, mage.cards.p.PhantasmalFiend.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phantasmal Sphere", 32, Rarity.RARE, mage.cards.p.PhantasmalSphere.class)); - cards.add(new SetCardInfo("Phelddagrif", 115, Rarity.RARE, mage.cards.p.Phelddagrif.class)); - cards.add(new SetCardInfo("Phyrexian Boon", "58a", Rarity.COMMON, mage.cards.p.PhyrexianBoon.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phyrexian Boon", "58b", Rarity.COMMON, mage.cards.p.PhyrexianBoon.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phyrexian Devourer", 125, Rarity.RARE, mage.cards.p.PhyrexianDevourer.class)); - cards.add(new SetCardInfo("Phyrexian Portal", 126, Rarity.RARE, mage.cards.p.PhyrexianPortal.class)); - cards.add(new SetCardInfo("Phyrexian War Beast", "127a", Rarity.COMMON, mage.cards.p.PhyrexianWarBeast.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phyrexian War Beast", "127b", Rarity.COMMON, mage.cards.p.PhyrexianWarBeast.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Pillage", 76, Rarity.UNCOMMON, mage.cards.p.Pillage.class)); - cards.add(new SetCardInfo("Pyrokinesis", 78, Rarity.UNCOMMON, mage.cards.p.Pyrokinesis.class)); - cards.add(new SetCardInfo("Reinforcements", "12a", Rarity.COMMON, mage.cards.r.Reinforcements.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Reinforcements", "12b", Rarity.COMMON, mage.cards.r.Reinforcements.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Reprisal", "13a", Rarity.COMMON, mage.cards.r.Reprisal.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Reprisal", "13b", Rarity.COMMON, mage.cards.r.Reprisal.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ritual of the Machine", 59, Rarity.RARE, mage.cards.r.RitualOfTheMachine.class)); - cards.add(new SetCardInfo("Rogue Skycaptain", 79, Rarity.RARE, mage.cards.r.RogueSkycaptain.class)); - cards.add(new SetCardInfo("Royal Decree", 14, Rarity.RARE, mage.cards.r.RoyalDecree.class)); - cards.add(new SetCardInfo("Royal Herbalist", "15a", Rarity.COMMON, mage.cards.r.RoyalHerbalist.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Royal Herbalist", "15b", Rarity.COMMON, mage.cards.r.RoyalHerbalist.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Scarab of the Unseen", 128, Rarity.UNCOMMON, mage.cards.s.ScarabOfTheUnseen.class)); - cards.add(new SetCardInfo("School of the Unseen", 141, Rarity.UNCOMMON, mage.cards.s.SchoolOfTheUnseen.class)); - cards.add(new SetCardInfo("Seasoned Tactician", 17, Rarity.UNCOMMON, mage.cards.s.SeasonedTactician.class)); - cards.add(new SetCardInfo("Sheltered Valley", 142, Rarity.RARE, mage.cards.s.ShelteredValley.class)); - cards.add(new SetCardInfo("Shield Sphere", 129, Rarity.UNCOMMON, mage.cards.s.ShieldSphere.class)); - cards.add(new SetCardInfo("Sol Grail", 130, Rarity.UNCOMMON, mage.cards.s.SolGrail.class)); - cards.add(new SetCardInfo("Soldevi Adnate", "60a", Rarity.COMMON, mage.cards.s.SoldeviAdnate.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Adnate", "60b", Rarity.COMMON, mage.cards.s.SoldeviAdnate.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Digger", 131, Rarity.RARE, mage.cards.s.SoldeviDigger.class)); - cards.add(new SetCardInfo("Soldevi Excavations", 143, Rarity.RARE, mage.cards.s.SoldeviExcavations.class)); - cards.add(new SetCardInfo("Soldevi Heretic", "33a", Rarity.COMMON, mage.cards.s.SoldeviHeretic.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Heretic", "33b", Rarity.COMMON, mage.cards.s.SoldeviHeretic.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Sage", "34a", Rarity.COMMON, mage.cards.s.SoldeviSage.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Sage", "34b", Rarity.COMMON, mage.cards.s.SoldeviSage.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Sentry", "132a", Rarity.COMMON, mage.cards.s.SoldeviSentry.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Sentry", "132b", Rarity.COMMON, mage.cards.s.SoldeviSentry.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Steam Beast", "133a", Rarity.COMMON, mage.cards.s.SoldeviSteamBeast.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Steam Beast", "133b", Rarity.COMMON, mage.cards.s.SoldeviSteamBeast.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldier of Fortune", 80, Rarity.UNCOMMON, mage.cards.s.SoldierOfFortune.class)); - cards.add(new SetCardInfo("Spiny Starfish", 35, Rarity.UNCOMMON, mage.cards.s.SpinyStarfish.class)); - cards.add(new SetCardInfo("Splintering Wind", 99, Rarity.RARE, mage.cards.s.SplinteringWind.class)); - cards.add(new SetCardInfo("Stench of Decay", "61a", Rarity.COMMON, mage.cards.s.StenchOfDecay.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Stench of Decay", "61b", Rarity.COMMON, mage.cards.s.StenchOfDecay.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Storm Cauldron", 134, Rarity.RARE, mage.cards.s.StormCauldron.class)); - cards.add(new SetCardInfo("Storm Crow", "36a", Rarity.COMMON, mage.cards.s.StormCrow.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Storm Crow", "36b", Rarity.COMMON, mage.cards.s.StormCrow.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Storm Elemental", 37, Rarity.UNCOMMON, mage.cards.s.StormElemental.class)); - cards.add(new SetCardInfo("Storm Shaman", "81a", Rarity.COMMON, mage.cards.s.StormShaman.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Storm Shaman", "81b", Rarity.COMMON, mage.cards.s.StormShaman.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Stromgald Spy", 62, Rarity.UNCOMMON, mage.cards.s.StromgaldSpy.class)); - cards.add(new SetCardInfo("Surge of Strength", 109, Rarity.UNCOMMON, mage.cards.s.SurgeOfStrength.class)); - cards.add(new SetCardInfo("Sustaining Spirit", 18, Rarity.RARE, mage.cards.s.SustainingSpirit.class)); - cards.add(new SetCardInfo("Swamp Mosquito", "63a", Rarity.COMMON, mage.cards.s.SwampMosquito.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp Mosquito", "63b", Rarity.COMMON, mage.cards.s.SwampMosquito.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sworn Defender", 19, Rarity.RARE, mage.cards.s.SwornDefender.class)); - cards.add(new SetCardInfo("Thawing Glaciers", 144, Rarity.RARE, mage.cards.t.ThawingGlaciers.class)); - cards.add(new SetCardInfo("Thought Lash", 39, Rarity.RARE, mage.cards.t.ThoughtLash.class)); - cards.add(new SetCardInfo("Tidal Control", 40, Rarity.RARE, mage.cards.t.TidalControl.class)); - cards.add(new SetCardInfo("Tornado", 101, Rarity.RARE, mage.cards.t.Tornado.class)); - cards.add(new SetCardInfo("Unlikely Alliance", 20, Rarity.UNCOMMON, mage.cards.u.UnlikelyAlliance.class)); - cards.add(new SetCardInfo("Urza's Engine", 135, Rarity.UNCOMMON, mage.cards.u.UrzasEngine.class)); - cards.add(new SetCardInfo("Varchild's Crusader", "82a", Rarity.COMMON, mage.cards.v.VarchildsCrusader.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Varchild's Crusader", "82b", Rarity.COMMON, mage.cards.v.VarchildsCrusader.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Varchild's War-Riders", 83, Rarity.RARE, mage.cards.v.VarchildsWarRiders.class)); - cards.add(new SetCardInfo("Veteran's Voice", "84a", Rarity.COMMON, mage.cards.v.VeteransVoice.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Veteran's Voice", "84b", Rarity.COMMON, mage.cards.v.VeteransVoice.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Viscerid Armor", "41a", Rarity.COMMON, mage.cards.v.VisceridArmor.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Viscerid Armor", "41b", Rarity.COMMON, mage.cards.v.VisceridArmor.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Viscerid Drone", 42, Rarity.UNCOMMON, mage.cards.v.VisceridDrone.class)); - cards.add(new SetCardInfo("Wandering Mage", 111, Rarity.RARE, mage.cards.w.WanderingMage.class)); - cards.add(new SetCardInfo("Whip Vine", "103a", Rarity.COMMON, mage.cards.w.WhipVine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Whip Vine", "103b", Rarity.COMMON, mage.cards.w.WhipVine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Whirling Catapult", 136, Rarity.UNCOMMON, mage.cards.w.WhirlingCatapult.class)); - cards.add(new SetCardInfo("Wild Aesthir", "21a", Rarity.COMMON, mage.cards.w.WildAesthir.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Wild Aesthir", "21b", Rarity.COMMON, mage.cards.w.WildAesthir.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Winter's Night", 114, Rarity.RARE, mage.cards.w.WintersNight.class)); - cards.add(new SetCardInfo("Yavimaya Ancients", "104a", Rarity.COMMON, mage.cards.y.YavimayaAncients.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Yavimaya Ancients", "104b", Rarity.COMMON, mage.cards.y.YavimayaAncients.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Yavimaya Ants", 105, Rarity.UNCOMMON, mage.cards.y.YavimayaAnts.class)); - } -} +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +public final class Alliances extends ExpansionSet { + + private static final Alliances instance = new Alliances(); + + public static Alliances getInstance() { + return instance; + } + + private Alliances() { + super("Alliances", "ALL", ExpansionSet.buildDate(1996, 6, 10), SetType.EXPANSION); + this.blockName = "Ice Age"; + this.parentSet = IceAge.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 8; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + + cards.add(new SetCardInfo("Aesthir Glider", "116a", Rarity.COMMON, mage.cards.a.AesthirGlider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aesthir Glider", "116b", Rarity.COMMON, mage.cards.a.AesthirGlider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Agent of Stromgald", "64a", Rarity.COMMON, mage.cards.a.AgentOfStromgald.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Agent of Stromgald", "64b", Rarity.COMMON, mage.cards.a.AgentOfStromgald.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arcane Denial", "22a", Rarity.COMMON, mage.cards.a.ArcaneDenial.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arcane Denial", "22b", Rarity.COMMON, mage.cards.a.ArcaneDenial.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashnod's Cylix", 117, Rarity.RARE, mage.cards.a.AshnodsCylix.class)); + cards.add(new SetCardInfo("Astrolabe", "118a", Rarity.COMMON, mage.cards.a.Astrolabe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Astrolabe", "118b", Rarity.COMMON, mage.cards.a.Astrolabe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Awesome Presence", "23a", Rarity.COMMON, mage.cards.a.AwesomePresence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Awesome Presence", "23b", Rarity.COMMON, mage.cards.a.AwesomePresence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balduvian Dead", 43, Rarity.UNCOMMON, mage.cards.b.BalduvianDead.class)); + cards.add(new SetCardInfo("Balduvian Horde", 65, Rarity.RARE, mage.cards.b.BalduvianHorde.class)); + cards.add(new SetCardInfo("Balduvian Trading Post", 137, Rarity.RARE, mage.cards.b.BalduvianTradingPost.class)); + cards.add(new SetCardInfo("Balduvian War-Makers", "66a", Rarity.COMMON, mage.cards.b.BalduvianWarMakers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balduvian War-Makers", "66b", Rarity.COMMON, mage.cards.b.BalduvianWarMakers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Benthic Explorers", "24a", Rarity.COMMON, mage.cards.b.BenthicExplorers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Benthic Explorers", "24b", Rarity.COMMON, mage.cards.b.BenthicExplorers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bestial Fury", "67a", Rarity.COMMON, mage.cards.b.BestialFury.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bestial Fury", "67b", Rarity.COMMON, mage.cards.b.BestialFury.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bounty of the Hunt", 85, Rarity.UNCOMMON, mage.cards.b.BountyOfTheHunt.class)); + cards.add(new SetCardInfo("Browse", 25, Rarity.UNCOMMON, mage.cards.b.Browse.class)); + cards.add(new SetCardInfo("Burnout", 68, Rarity.UNCOMMON, mage.cards.b.Burnout.class)); + cards.add(new SetCardInfo("Carrier Pigeons", "1a", Rarity.COMMON, mage.cards.c.CarrierPigeons.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Carrier Pigeons", "1b", Rarity.COMMON, mage.cards.c.CarrierPigeons.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Casting of Bones", "44a", Rarity.COMMON, mage.cards.c.CastingOfBones.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Casting of Bones", "44b", Rarity.COMMON, mage.cards.c.CastingOfBones.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chaos Harlequin", 69, Rarity.RARE, mage.cards.c.ChaosHarlequin.class)); + cards.add(new SetCardInfo("Contagion", 45, Rarity.UNCOMMON, mage.cards.c.Contagion.class)); + cards.add(new SetCardInfo("Deadly Insect", "86a", Rarity.COMMON, mage.cards.d.DeadlyInsect.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deadly Insect", "86b", Rarity.COMMON, mage.cards.d.DeadlyInsect.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Death Spark", 70, Rarity.UNCOMMON, mage.cards.d.DeathSpark.class)); + cards.add(new SetCardInfo("Diminishing Returns", 26, Rarity.RARE, mage.cards.d.DiminishingReturns.class)); + cards.add(new SetCardInfo("Diseased Vermin", 46, Rarity.UNCOMMON, mage.cards.d.DiseasedVermin.class)); + cards.add(new SetCardInfo("Dystopia", 47, Rarity.RARE, mage.cards.d.Dystopia.class)); + cards.add(new SetCardInfo("Elvish Bard", 87, Rarity.UNCOMMON, mage.cards.e.ElvishBard.class)); + cards.add(new SetCardInfo("Elvish Ranger", "88a", Rarity.COMMON, mage.cards.e.ElvishRanger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elvish Ranger", "88b", Rarity.COMMON, mage.cards.e.ElvishRanger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elvish Spirit Guide", 89, Rarity.UNCOMMON, mage.cards.e.ElvishSpiritGuide.class)); + cards.add(new SetCardInfo("Energy Arc", 106, Rarity.UNCOMMON, mage.cards.e.EnergyArc.class)); + cards.add(new SetCardInfo("Enslaved Scout", "71a", Rarity.COMMON, mage.cards.e.EnslavedScout.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Enslaved Scout", "71b", Rarity.COMMON, mage.cards.e.EnslavedScout.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Errand of Duty", "2a", Rarity.COMMON, mage.cards.e.ErrandOfDuty.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Errand of Duty", "2b", Rarity.COMMON, mage.cards.e.ErrandOfDuty.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Exile", 3, Rarity.RARE, mage.cards.e.Exile.class)); + cards.add(new SetCardInfo("False Demise", "27a", Rarity.COMMON, mage.cards.f.FalseDemise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("False Demise", "27b", Rarity.COMMON, mage.cards.f.FalseDemise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fatal Lore", 48, Rarity.RARE, mage.cards.f.FatalLore.class)); + cards.add(new SetCardInfo("Feast or Famine", "49a", Rarity.COMMON, mage.cards.f.FeastOrFamine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Feast or Famine", "49b", Rarity.COMMON, mage.cards.f.FeastOrFamine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fevered Strength", "50a", Rarity.COMMON, mage.cards.f.FeveredStrength.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fevered Strength", "50b", Rarity.COMMON, mage.cards.f.FeveredStrength.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Floodwater Dam", 119, Rarity.RARE, mage.cards.f.FloodwaterDam.class)); + cards.add(new SetCardInfo("Force of Will", 28, Rarity.UNCOMMON, mage.cards.f.ForceOfWill.class)); + cards.add(new SetCardInfo("Foresight", "29a", Rarity.COMMON, mage.cards.f.Foresight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Foresight", "29b", Rarity.COMMON, mage.cards.f.Foresight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fyndhorn Druid", "90a", Rarity.COMMON, mage.cards.f.FyndhornDruid.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fyndhorn Druid", "90b", Rarity.COMMON, mage.cards.f.FyndhornDruid.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gargantuan Gorilla", 91, Rarity.RARE, mage.cards.g.GargantuanGorilla.class)); + cards.add(new SetCardInfo("Gift of the Woods", "92a", Rarity.COMMON, mage.cards.g.GiftOfTheWoods.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gift of the Woods", "92b", Rarity.COMMON, mage.cards.g.GiftOfTheWoods.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Berserkers", "93a", Rarity.COMMON, mage.cards.g.GorillaBerserkers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Berserkers", "93b", Rarity.COMMON, mage.cards.g.GorillaBerserkers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Chieftain", "94a", Rarity.COMMON, mage.cards.g.GorillaChieftain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Chieftain", "94b", Rarity.COMMON, mage.cards.g.GorillaChieftain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Shaman", "72a", Rarity.COMMON, mage.cards.g.GorillaShaman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Shaman", "72b", Rarity.COMMON, mage.cards.g.GorillaShaman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla War Cry", "73a", Rarity.COMMON, mage.cards.g.GorillaWarCry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla War Cry", "73b", Rarity.COMMON, mage.cards.g.GorillaWarCry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Guerrilla Tactics", "74a", Rarity.COMMON, mage.cards.g.GuerrillaTactics.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Guerrilla Tactics", "74b", Rarity.COMMON, mage.cards.g.GuerrillaTactics.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gustha's Scepter", 120, Rarity.RARE, mage.cards.g.GusthasScepter.class)); + cards.add(new SetCardInfo("Hail Storm", 95, Rarity.UNCOMMON, mage.cards.h.HailStorm.class)); + cards.add(new SetCardInfo("Heart of Yavimaya", 138, Rarity.RARE, mage.cards.h.HeartOfYavimaya.class)); + cards.add(new SetCardInfo("Helm of Obedience", 121, Rarity.RARE, mage.cards.h.HelmOfObedience.class)); + cards.add(new SetCardInfo("Inheritance", 4, Rarity.UNCOMMON, mage.cards.i.Inheritance.class)); + cards.add(new SetCardInfo("Insidious Bookworms", "51a", Rarity.COMMON, mage.cards.i.InsidiousBookworms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Insidious Bookworms", "51b", Rarity.COMMON, mage.cards.i.InsidiousBookworms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ivory Gargoyle", 5, Rarity.RARE, mage.cards.i.IvoryGargoyle.class)); + cards.add(new SetCardInfo("Juniper Order Advocate", 6, Rarity.UNCOMMON, mage.cards.j.JuniperOrderAdvocate.class)); + cards.add(new SetCardInfo("Kaysa", 96, Rarity.RARE, mage.cards.k.Kaysa.class)); + cards.add(new SetCardInfo("Keeper of Tresserhorn", 52, Rarity.RARE, mage.cards.k.KeeperOfTresserhorn.class)); + cards.add(new SetCardInfo("Kjeldoran Escort", "7a", Rarity.COMMON, mage.cards.k.KjeldoranEscort.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kjeldoran Escort", "7b", Rarity.COMMON, mage.cards.k.KjeldoranEscort.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kjeldoran Pride", "9a", Rarity.COMMON, mage.cards.k.KjeldoranPride.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kjeldoran Pride", "9b", Rarity.COMMON, mage.cards.k.KjeldoranPride.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kjeldoran Home Guard", 8, Rarity.UNCOMMON, mage.cards.k.KjeldoranHomeGuard.class)); + cards.add(new SetCardInfo("Kjeldoran Outpost", 139, Rarity.RARE, mage.cards.k.KjeldoranOutpost.class)); + cards.add(new SetCardInfo("Krovikan Horror", 53, Rarity.RARE, mage.cards.k.KrovikanHorror.class)); + cards.add(new SetCardInfo("Krovikan Plague", 54, Rarity.UNCOMMON, mage.cards.k.KrovikanPlague.class)); + cards.add(new SetCardInfo("Lake of the Dead", 140, Rarity.RARE, mage.cards.l.LakeOfTheDead.class)); + cards.add(new SetCardInfo("Lat-Nam's Legacy", "30a", Rarity.COMMON, mage.cards.l.LatNamsLegacy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lat-Nam's Legacy", "30b", Rarity.COMMON, mage.cards.l.LatNamsLegacy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Library of Lat-Nam", 31, Rarity.RARE, mage.cards.l.LibraryOfLatNam.class)); + cards.add(new SetCardInfo("Lim-Dul's High Guard", "55a", Rarity.COMMON, mage.cards.l.LimDulsHighGuard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lim-Dul's High Guard", "55b", Rarity.COMMON, mage.cards.l.LimDulsHighGuard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lim-Dul's Paladin", 108, Rarity.UNCOMMON, mage.cards.l.LimDulsPaladin.class)); + cards.add(new SetCardInfo("Lim-Dul's Vault", 107, Rarity.UNCOMMON, mage.cards.l.LimDulsVault.class)); + cards.add(new SetCardInfo("Lodestone Bauble", 122, Rarity.RARE, mage.cards.l.LodestoneBauble.class)); + cards.add(new SetCardInfo("Lord of Tresserhorn", 112, Rarity.RARE, mage.cards.l.LordOfTresserhorn.class)); + cards.add(new SetCardInfo("Martyrdom", "10a", Rarity.COMMON, mage.cards.m.Martyrdom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Martyrdom", "10b", Rarity.COMMON, mage.cards.m.Martyrdom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Misfortune", 113, Rarity.RARE, mage.cards.m.Misfortune.class)); + cards.add(new SetCardInfo("Mishra's Groundbreaker", 123, Rarity.UNCOMMON, mage.cards.m.MishrasGroundbreaker.class)); + cards.add(new SetCardInfo("Misinformation", 56, Rarity.UNCOMMON, mage.cards.m.Misinformation.class)); + cards.add(new SetCardInfo("Mystic Compass", 124, Rarity.UNCOMMON, mage.cards.m.MysticCompass.class)); + cards.add(new SetCardInfo("Nature's Blessing", 110, Rarity.UNCOMMON, mage.cards.n.NaturesBlessing.class)); + cards.add(new SetCardInfo("Nature's Chosen", 97, Rarity.UNCOMMON, mage.cards.n.NaturesChosen.class)); + cards.add(new SetCardInfo("Nature's Wrath", 98, Rarity.RARE, mage.cards.n.NaturesWrath.class)); + cards.add(new SetCardInfo("Noble Steeds", "11a", Rarity.COMMON, mage.cards.n.NobleSteeds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Noble Steeds", "11b", Rarity.COMMON, mage.cards.n.NobleSteeds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Omen of Fire", 75, Rarity.RARE, mage.cards.o.OmenOfFire.class)); + cards.add(new SetCardInfo("Phantasmal Fiend", "57a", Rarity.COMMON, mage.cards.p.PhantasmalFiend.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phantasmal Fiend", "57b", Rarity.COMMON, mage.cards.p.PhantasmalFiend.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phantasmal Sphere", 32, Rarity.RARE, mage.cards.p.PhantasmalSphere.class)); + cards.add(new SetCardInfo("Phelddagrif", 115, Rarity.RARE, mage.cards.p.Phelddagrif.class)); + cards.add(new SetCardInfo("Phyrexian Boon", "58a", Rarity.COMMON, mage.cards.p.PhyrexianBoon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phyrexian Boon", "58b", Rarity.COMMON, mage.cards.p.PhyrexianBoon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phyrexian Devourer", 125, Rarity.RARE, mage.cards.p.PhyrexianDevourer.class)); + cards.add(new SetCardInfo("Phyrexian Portal", 126, Rarity.RARE, mage.cards.p.PhyrexianPortal.class)); + cards.add(new SetCardInfo("Phyrexian War Beast", "127a", Rarity.COMMON, mage.cards.p.PhyrexianWarBeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phyrexian War Beast", "127b", Rarity.COMMON, mage.cards.p.PhyrexianWarBeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pillage", 76, Rarity.UNCOMMON, mage.cards.p.Pillage.class)); + cards.add(new SetCardInfo("Pyrokinesis", 78, Rarity.UNCOMMON, mage.cards.p.Pyrokinesis.class)); + cards.add(new SetCardInfo("Reinforcements", "12a", Rarity.COMMON, mage.cards.r.Reinforcements.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reinforcements", "12b", Rarity.COMMON, mage.cards.r.Reinforcements.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reprisal", "13a", Rarity.COMMON, mage.cards.r.Reprisal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reprisal", "13b", Rarity.COMMON, mage.cards.r.Reprisal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ritual of the Machine", 59, Rarity.RARE, mage.cards.r.RitualOfTheMachine.class)); + cards.add(new SetCardInfo("Rogue Skycaptain", 79, Rarity.RARE, mage.cards.r.RogueSkycaptain.class)); + cards.add(new SetCardInfo("Royal Decree", 14, Rarity.RARE, mage.cards.r.RoyalDecree.class)); + cards.add(new SetCardInfo("Royal Herbalist", "15a", Rarity.COMMON, mage.cards.r.RoyalHerbalist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Royal Herbalist", "15b", Rarity.COMMON, mage.cards.r.RoyalHerbalist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scarab of the Unseen", 128, Rarity.UNCOMMON, mage.cards.s.ScarabOfTheUnseen.class)); + cards.add(new SetCardInfo("School of the Unseen", 141, Rarity.UNCOMMON, mage.cards.s.SchoolOfTheUnseen.class)); + cards.add(new SetCardInfo("Seasoned Tactician", 17, Rarity.UNCOMMON, mage.cards.s.SeasonedTactician.class)); + cards.add(new SetCardInfo("Sheltered Valley", 142, Rarity.RARE, mage.cards.s.ShelteredValley.class)); + cards.add(new SetCardInfo("Shield Sphere", 129, Rarity.UNCOMMON, mage.cards.s.ShieldSphere.class)); + cards.add(new SetCardInfo("Sol Grail", 130, Rarity.UNCOMMON, mage.cards.s.SolGrail.class)); + cards.add(new SetCardInfo("Soldevi Adnate", "60a", Rarity.COMMON, mage.cards.s.SoldeviAdnate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Adnate", "60b", Rarity.COMMON, mage.cards.s.SoldeviAdnate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Digger", 131, Rarity.RARE, mage.cards.s.SoldeviDigger.class)); + cards.add(new SetCardInfo("Soldevi Excavations", 143, Rarity.RARE, mage.cards.s.SoldeviExcavations.class)); + cards.add(new SetCardInfo("Soldevi Heretic", "33a", Rarity.COMMON, mage.cards.s.SoldeviHeretic.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Heretic", "33b", Rarity.COMMON, mage.cards.s.SoldeviHeretic.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Sage", "34a", Rarity.COMMON, mage.cards.s.SoldeviSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Sage", "34b", Rarity.COMMON, mage.cards.s.SoldeviSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Sentry", "132a", Rarity.COMMON, mage.cards.s.SoldeviSentry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Sentry", "132b", Rarity.COMMON, mage.cards.s.SoldeviSentry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Steam Beast", "133a", Rarity.COMMON, mage.cards.s.SoldeviSteamBeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Steam Beast", "133b", Rarity.COMMON, mage.cards.s.SoldeviSteamBeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldier of Fortune", 80, Rarity.UNCOMMON, mage.cards.s.SoldierOfFortune.class)); + cards.add(new SetCardInfo("Spiny Starfish", 35, Rarity.UNCOMMON, mage.cards.s.SpinyStarfish.class)); + cards.add(new SetCardInfo("Splintering Wind", 99, Rarity.RARE, mage.cards.s.SplinteringWind.class)); + cards.add(new SetCardInfo("Stench of Decay", "61a", Rarity.COMMON, mage.cards.s.StenchOfDecay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stench of Decay", "61b", Rarity.COMMON, mage.cards.s.StenchOfDecay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm Cauldron", 134, Rarity.RARE, mage.cards.s.StormCauldron.class)); + cards.add(new SetCardInfo("Storm Crow", "36a", Rarity.COMMON, mage.cards.s.StormCrow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm Crow", "36b", Rarity.COMMON, mage.cards.s.StormCrow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm Elemental", 37, Rarity.UNCOMMON, mage.cards.s.StormElemental.class)); + cards.add(new SetCardInfo("Storm Shaman", "81a", Rarity.COMMON, mage.cards.s.StormShaman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm Shaman", "81b", Rarity.COMMON, mage.cards.s.StormShaman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stromgald Spy", 62, Rarity.UNCOMMON, mage.cards.s.StromgaldSpy.class)); + cards.add(new SetCardInfo("Surge of Strength", 109, Rarity.UNCOMMON, mage.cards.s.SurgeOfStrength.class)); + cards.add(new SetCardInfo("Sustaining Spirit", 18, Rarity.RARE, mage.cards.s.SustainingSpirit.class)); + cards.add(new SetCardInfo("Swamp Mosquito", "63a", Rarity.COMMON, mage.cards.s.SwampMosquito.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp Mosquito", "63b", Rarity.COMMON, mage.cards.s.SwampMosquito.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sworn Defender", 19, Rarity.RARE, mage.cards.s.SwornDefender.class)); + cards.add(new SetCardInfo("Thawing Glaciers", 144, Rarity.RARE, mage.cards.t.ThawingGlaciers.class)); + cards.add(new SetCardInfo("Thought Lash", 39, Rarity.RARE, mage.cards.t.ThoughtLash.class)); + cards.add(new SetCardInfo("Tidal Control", 40, Rarity.RARE, mage.cards.t.TidalControl.class)); + cards.add(new SetCardInfo("Tornado", 101, Rarity.RARE, mage.cards.t.Tornado.class)); + cards.add(new SetCardInfo("Unlikely Alliance", 20, Rarity.UNCOMMON, mage.cards.u.UnlikelyAlliance.class)); + cards.add(new SetCardInfo("Urza's Engine", 135, Rarity.UNCOMMON, mage.cards.u.UrzasEngine.class)); + cards.add(new SetCardInfo("Varchild's Crusader", "82a", Rarity.COMMON, mage.cards.v.VarchildsCrusader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Varchild's Crusader", "82b", Rarity.COMMON, mage.cards.v.VarchildsCrusader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Varchild's War-Riders", 83, Rarity.RARE, mage.cards.v.VarchildsWarRiders.class)); + cards.add(new SetCardInfo("Veteran's Voice", "84a", Rarity.COMMON, mage.cards.v.VeteransVoice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Veteran's Voice", "84b", Rarity.COMMON, mage.cards.v.VeteransVoice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Viscerid Armor", "41a", Rarity.COMMON, mage.cards.v.VisceridArmor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Viscerid Armor", "41b", Rarity.COMMON, mage.cards.v.VisceridArmor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Viscerid Drone", 42, Rarity.UNCOMMON, mage.cards.v.VisceridDrone.class)); + cards.add(new SetCardInfo("Wandering Mage", 111, Rarity.RARE, mage.cards.w.WanderingMage.class)); + cards.add(new SetCardInfo("Whip Vine", "103a", Rarity.COMMON, mage.cards.w.WhipVine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Whip Vine", "103b", Rarity.COMMON, mage.cards.w.WhipVine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Whirling Catapult", 136, Rarity.UNCOMMON, mage.cards.w.WhirlingCatapult.class)); + cards.add(new SetCardInfo("Wild Aesthir", "21a", Rarity.COMMON, mage.cards.w.WildAesthir.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wild Aesthir", "21b", Rarity.COMMON, mage.cards.w.WildAesthir.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Winter's Night", 114, Rarity.RARE, mage.cards.w.WintersNight.class)); + cards.add(new SetCardInfo("Yavimaya Ancients", "104a", Rarity.COMMON, mage.cards.y.YavimayaAncients.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yavimaya Ancients", "104b", Rarity.COMMON, mage.cards.y.YavimayaAncients.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yavimaya Ants", 105, Rarity.UNCOMMON, mage.cards.y.YavimayaAnts.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Chronicles.java b/Mage.Sets/src/mage/sets/Chronicles.java index 2d84e37739..abfa44a566 100644 --- a/Mage.Sets/src/mage/sets/Chronicles.java +++ b/Mage.Sets/src/mage/sets/Chronicles.java @@ -1,154 +1,154 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * @author LevelX2 - */ -public final class Chronicles extends ExpansionSet { - - private static final Chronicles instance = new Chronicles(); - - public static Chronicles getInstance() { - return instance; - } - - private Chronicles() { - super("Chronicles", "CHR", ExpansionSet.buildDate(1995, 6, 1), SetType.SUPPLEMENTAL); - this.blockName = "Reprint"; - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 9; - this.numBoosterUncommon = 2; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - - cards.add(new SetCardInfo("Abu Ja'far", 1, Rarity.UNCOMMON, mage.cards.a.AbuJafar.class)); - cards.add(new SetCardInfo("Active Volcano", 43, Rarity.COMMON, mage.cards.a.ActiveVolcano.class)); - cards.add(new SetCardInfo("Akron Legionnaire", 2, Rarity.RARE, mage.cards.a.AkronLegionnaire.class)); - cards.add(new SetCardInfo("Aladdin", 44, Rarity.UNCOMMON, mage.cards.a.Aladdin.class)); - cards.add(new SetCardInfo("Angelic Voices", 3, Rarity.RARE, mage.cards.a.AngelicVoices.class)); - cards.add(new SetCardInfo("Arcades Sabboth", 71, Rarity.RARE, mage.cards.a.ArcadesSabboth.class)); - cards.add(new SetCardInfo("Arena of the Ancients", 91, Rarity.RARE, mage.cards.a.ArenaOfTheAncients.class)); - cards.add(new SetCardInfo("Argothian Pixies", 57, Rarity.COMMON, mage.cards.a.ArgothianPixies.class)); - cards.add(new SetCardInfo("Ashnod's Altar", 92, Rarity.COMMON, mage.cards.a.AshnodsAltar.class)); - cards.add(new SetCardInfo("Ashnod's Transmogrant", 93, Rarity.COMMON, mage.cards.a.AshnodsTransmogrant.class)); - cards.add(new SetCardInfo("Axelrod Gunnarson", 72, Rarity.RARE, mage.cards.a.AxelrodGunnarson.class)); - cards.add(new SetCardInfo("Ayesha Tanaka", 73, Rarity.RARE, mage.cards.a.AyeshaTanaka.class)); - cards.add(new SetCardInfo("Azure Drake", 15, Rarity.UNCOMMON, mage.cards.a.AzureDrake.class)); - cards.add(new SetCardInfo("Banshee", 29, Rarity.UNCOMMON, mage.cards.b.Banshee.class)); - cards.add(new SetCardInfo("Barl's Cage", 94, Rarity.RARE, mage.cards.b.BarlsCage.class)); - cards.add(new SetCardInfo("Beasts of Bogardan", 45, Rarity.UNCOMMON, mage.cards.b.BeastsOfBogardan.class)); - cards.add(new SetCardInfo("Blood Moon", 46, Rarity.RARE, mage.cards.b.BloodMoon.class)); - cards.add(new SetCardInfo("Blood of the Martyr", 4, Rarity.UNCOMMON, mage.cards.b.BloodOfTheMartyr.class)); - cards.add(new SetCardInfo("Bog Rats", 30, Rarity.COMMON, mage.cards.b.BogRats.class)); - cards.add(new SetCardInfo("Book of Rass", 95, Rarity.RARE, mage.cards.b.BookOfRass.class)); - cards.add(new SetCardInfo("Boomerang", 16, Rarity.COMMON, mage.cards.b.Boomerang.class)); - cards.add(new SetCardInfo("Bronze Horse", 96, Rarity.RARE, mage.cards.b.BronzeHorse.class)); - cards.add(new SetCardInfo("Cat Warriors", 58, Rarity.COMMON, mage.cards.c.CatWarriors.class)); - cards.add(new SetCardInfo("Chromium", 74, Rarity.RARE, mage.cards.c.Chromium.class)); - cards.add(new SetCardInfo("City of Brass", 112, Rarity.RARE, mage.cards.c.CityOfBrass.class)); - cards.add(new SetCardInfo("Cocoon", 59, Rarity.UNCOMMON, mage.cards.c.Cocoon.class)); - cards.add(new SetCardInfo("Concordant Crossroads", 60, Rarity.RARE, mage.cards.c.ConcordantCrossroads.class)); - cards.add(new SetCardInfo("Craw Giant", 61, Rarity.UNCOMMON, mage.cards.c.CrawGiant.class)); - cards.add(new SetCardInfo("Cuombajj Witches", 31, Rarity.COMMON, mage.cards.c.CuombajjWitches.class)); - cards.add(new SetCardInfo("Cyclone", 62, Rarity.RARE, mage.cards.c.Cyclone.class)); - cards.add(new SetCardInfo("D'Avenant Archer", 5, Rarity.COMMON, mage.cards.d.DAvenantArcher.class)); - cards.add(new SetCardInfo("Dakkon Blackblade", 75, Rarity.RARE, mage.cards.d.DakkonBlackblade.class)); - cards.add(new SetCardInfo("Dance of Many", 17, Rarity.RARE, mage.cards.d.DanceOfMany.class)); - cards.add(new SetCardInfo("Dandan", 18, Rarity.COMMON, mage.cards.d.Dandan.class)); - cards.add(new SetCardInfo("Divine Offering", 6, Rarity.COMMON, mage.cards.d.DivineOffering.class)); - cards.add(new SetCardInfo("Emerald Dragonfly", 63, Rarity.COMMON, mage.cards.e.EmeraldDragonfly.class)); - cards.add(new SetCardInfo("Enchantment Alteration", 19, Rarity.UNCOMMON, mage.cards.e.EnchantmentAlteration.class)); - cards.add(new SetCardInfo("Erhnam Djinn", 64, Rarity.UNCOMMON, mage.cards.e.ErhnamDjinn.class)); - cards.add(new SetCardInfo("Fallen Angel", 32, Rarity.UNCOMMON, mage.cards.f.FallenAngel.class)); - cards.add(new SetCardInfo("Feldon's Cane", 97, Rarity.COMMON, mage.cards.f.FeldonsCane.class)); - cards.add(new SetCardInfo("Fire Drake", 47, Rarity.UNCOMMON, mage.cards.f.FireDrake.class)); - cards.add(new SetCardInfo("Fishliver Oil", 20, Rarity.COMMON, mage.cards.f.FishliverOil.class)); - cards.add(new SetCardInfo("Flash Flood", 21, Rarity.COMMON, mage.cards.f.FlashFlood.class)); - cards.add(new SetCardInfo("Fountain of Youth", 98, Rarity.COMMON, mage.cards.f.FountainOfYouth.class)); - cards.add(new SetCardInfo("Gabriel Angelfire", 76, Rarity.RARE, mage.cards.g.GabrielAngelfire.class)); - cards.add(new SetCardInfo("Gauntlets of Chaos", 99, Rarity.RARE, mage.cards.g.GauntletsOfChaos.class)); - cards.add(new SetCardInfo("Ghazban Ogre", 65, Rarity.COMMON, mage.cards.g.GhazbanOgre.class)); - cards.add(new SetCardInfo("Giant Slug", 33, Rarity.COMMON, mage.cards.g.GiantSlug.class)); - cards.add(new SetCardInfo("Goblin Artisans", 48, Rarity.UNCOMMON, mage.cards.g.GoblinArtisans.class)); - cards.add(new SetCardInfo("Goblin Digging Team", 49, Rarity.COMMON, mage.cards.g.GoblinDiggingTeam.class)); - cards.add(new SetCardInfo("Goblin Shrine", 50, Rarity.COMMON, mage.cards.g.GoblinShrine.class)); - cards.add(new SetCardInfo("Goblins of the Flarg", 51, Rarity.COMMON, mage.cards.g.GoblinsOfTheFlarg.class)); - cards.add(new SetCardInfo("Hasran Ogress", 34, Rarity.COMMON, mage.cards.h.HasranOgress.class)); - cards.add(new SetCardInfo("Hell's Caretaker", 35, Rarity.RARE, mage.cards.h.HellsCaretaker.class)); - cards.add(new SetCardInfo("Horn of Deafening", 100, Rarity.RARE, mage.cards.h.HornOfDeafening.class)); - cards.add(new SetCardInfo("Indestructible Aura", 7, Rarity.COMMON, mage.cards.i.IndestructibleAura.class)); - cards.add(new SetCardInfo("Ivory Guardians", 8, Rarity.UNCOMMON, mage.cards.i.IvoryGuardians.class)); - cards.add(new SetCardInfo("Jalum Tome", 101, Rarity.RARE, mage.cards.j.JalumTome.class)); - cards.add(new SetCardInfo("Johan", 77, Rarity.RARE, mage.cards.j.Johan.class)); - cards.add(new SetCardInfo("Juxtapose", 22, Rarity.RARE, mage.cards.j.Juxtapose.class)); - cards.add(new SetCardInfo("Keepers of the Faith", 9, Rarity.COMMON, mage.cards.k.KeepersOfTheFaith.class)); - cards.add(new SetCardInfo("Kei Takahashi", 78, Rarity.UNCOMMON, mage.cards.k.KeiTakahashi.class)); - cards.add(new SetCardInfo("Land's Edge", 52, Rarity.RARE, mage.cards.l.LandsEdge.class)); - cards.add(new SetCardInfo("Living Armor", 103, Rarity.COMMON, mage.cards.l.LivingArmor.class)); - cards.add(new SetCardInfo("Marhault Elsdragon", 79, Rarity.UNCOMMON, mage.cards.m.MarhaultElsdragon.class)); - cards.add(new SetCardInfo("Metamorphosis", 66, Rarity.COMMON, mage.cards.m.Metamorphosis.class)); - cards.add(new SetCardInfo("Mountain Yeti", 53, Rarity.COMMON, mage.cards.m.MountainYeti.class)); - cards.add(new SetCardInfo("Nebuchadnezzar", 80, Rarity.RARE, mage.cards.n.Nebuchadnezzar.class)); - cards.add(new SetCardInfo("Nicol Bolas", 81, Rarity.RARE, mage.cards.n.NicolBolas.class)); - cards.add(new SetCardInfo("Obelisk of Undoing", 104, Rarity.RARE, mage.cards.o.ObeliskOfUndoing.class)); - cards.add(new SetCardInfo("Palladia-Mors", 82, Rarity.RARE, mage.cards.p.PalladiaMors.class)); - cards.add(new SetCardInfo("Petra Sphinx", 10, Rarity.RARE, mage.cards.p.PetraSphinx.class)); - cards.add(new SetCardInfo("Primordial Ooze", 54, Rarity.UNCOMMON, mage.cards.p.PrimordialOoze.class)); - cards.add(new SetCardInfo("Puppet Master", 23, Rarity.UNCOMMON, mage.cards.p.PuppetMaster.class)); - cards.add(new SetCardInfo("Rabid Wombat", 67, Rarity.UNCOMMON, mage.cards.r.RabidWombat.class)); - cards.add(new SetCardInfo("Rakalite", 105, Rarity.RARE, mage.cards.r.Rakalite.class)); - cards.add(new SetCardInfo("Recall", 24, Rarity.UNCOMMON, mage.cards.r.Recall.class)); - cards.add(new SetCardInfo("Remove Soul", 25, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); - cards.add(new SetCardInfo("Repentant Blacksmith", 11, Rarity.COMMON, mage.cards.r.RepentantBlacksmith.class)); - cards.add(new SetCardInfo("Revelation", 68, Rarity.RARE, mage.cards.r.Revelation.class)); - cards.add(new SetCardInfo("Rubinia Soulsinger", 83, Rarity.RARE, mage.cards.r.RubiniaSoulsinger.class)); - cards.add(new SetCardInfo("Runesword", 106, Rarity.COMMON, mage.cards.r.Runesword.class)); - cards.add(new SetCardInfo("Safe Haven", 113, Rarity.RARE, mage.cards.s.SafeHaven.class)); - cards.add(new SetCardInfo("Scavenger Folk", 69, Rarity.COMMON, mage.cards.s.ScavengerFolk.class)); - cards.add(new SetCardInfo("Sentinel", 107, Rarity.RARE, mage.cards.s.Sentinel.class)); - cards.add(new SetCardInfo("Serpent Generator", 108, Rarity.RARE, mage.cards.s.SerpentGenerator.class)); - cards.add(new SetCardInfo("Shield Wall", 12, Rarity.UNCOMMON, mage.cards.s.ShieldWall.class)); - cards.add(new SetCardInfo("Shimian Night Stalker", 36, Rarity.UNCOMMON, mage.cards.s.ShimianNightStalker.class)); - cards.add(new SetCardInfo("Sivitri Scarzam", 84, Rarity.UNCOMMON, mage.cards.s.SivitriScarzam.class)); - cards.add(new SetCardInfo("Sol'kanar the Swamp King", 85, Rarity.RARE, mage.cards.s.SolkanarTheSwampKing.class)); - cards.add(new SetCardInfo("Stangg", 86, Rarity.RARE, mage.cards.s.Stangg.class)); - cards.add(new SetCardInfo("Storm Seeker", 70, Rarity.UNCOMMON, mage.cards.s.StormSeeker.class)); - cards.add(new SetCardInfo("Teleport", 26, Rarity.RARE, mage.cards.t.Teleport.class)); - cards.add(new SetCardInfo("The Fallen", 38, Rarity.UNCOMMON, mage.cards.t.TheFallen.class)); - cards.add(new SetCardInfo("The Wretched", 39, Rarity.RARE, mage.cards.t.TheWretched.class)); - cards.add(new SetCardInfo("Tobias Andrion", 87, Rarity.UNCOMMON, mage.cards.t.TobiasAndrion.class)); - cards.add(new SetCardInfo("Tor Wauki", 88, Rarity.UNCOMMON, mage.cards.t.TorWauki.class)); - cards.add(new SetCardInfo("Tormod's Crypt", 109, Rarity.COMMON, mage.cards.t.TormodsCrypt.class)); - cards.add(new SetCardInfo("Transmutation", 40, Rarity.COMMON, mage.cards.t.Transmutation.class)); - cards.add(new SetCardInfo("Triassic Egg", 110, Rarity.RARE, mage.cards.t.TriassicEgg.class)); - cards.add(new SetCardInfo("Urza's Mine", "114a", Rarity.UNCOMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Mine", "114b", Rarity.UNCOMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Mine", "114c", Rarity.UNCOMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Mine", "114d", Rarity.UNCOMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Power Plant", "115a", Rarity.UNCOMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Power Plant", "115b", Rarity.UNCOMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Power Plant", "115c", Rarity.UNCOMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Power Plant", "115d", Rarity.UNCOMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Tower", "116a", Rarity.UNCOMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Tower", "116b", Rarity.UNCOMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Tower", "116c", Rarity.UNCOMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Tower", "116d", Rarity.UNCOMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Vaevictis Asmadi", 89, Rarity.RARE, mage.cards.v.VaevictisAsmadi.class)); - cards.add(new SetCardInfo("Voodoo Doll", 111, Rarity.RARE, mage.cards.v.VoodooDoll.class)); - cards.add(new SetCardInfo("Wall of Heat", 55, Rarity.COMMON, mage.cards.w.WallOfHeat.class)); - cards.add(new SetCardInfo("Wall of Opposition", 56, Rarity.UNCOMMON, mage.cards.w.WallOfOpposition.class)); - cards.add(new SetCardInfo("Wall of Shadows", 41, Rarity.COMMON, mage.cards.w.WallOfShadows.class)); - cards.add(new SetCardInfo("Wall of Vapor", 27, Rarity.COMMON, mage.cards.w.WallOfVapor.class)); - cards.add(new SetCardInfo("Wall of Wonder", 28, Rarity.UNCOMMON, mage.cards.w.WallOfWonder.class)); - cards.add(new SetCardInfo("War Elephant", 13, Rarity.COMMON, mage.cards.w.WarElephant.class)); - cards.add(new SetCardInfo("Witch Hunter", 14, Rarity.UNCOMMON, mage.cards.w.WitchHunter.class)); - cards.add(new SetCardInfo("Xira Arien", 90, Rarity.RARE, mage.cards.x.XiraArien.class)); - cards.add(new SetCardInfo("Yawgmoth Demon", 42, Rarity.RARE, mage.cards.y.YawgmothDemon.class)); - } - -} +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author LevelX2 + */ +public final class Chronicles extends ExpansionSet { + + private static final Chronicles instance = new Chronicles(); + + public static Chronicles getInstance() { + return instance; + } + + private Chronicles() { + super("Chronicles", "CHR", ExpansionSet.buildDate(1995, 6, 1), SetType.SUPPLEMENTAL); + this.blockName = "Reprint"; + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 9; + this.numBoosterUncommon = 2; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + + cards.add(new SetCardInfo("Abu Ja'far", 1, Rarity.UNCOMMON, mage.cards.a.AbuJafar.class)); + cards.add(new SetCardInfo("Active Volcano", 43, Rarity.COMMON, mage.cards.a.ActiveVolcano.class)); + cards.add(new SetCardInfo("Akron Legionnaire", 2, Rarity.RARE, mage.cards.a.AkronLegionnaire.class)); + cards.add(new SetCardInfo("Aladdin", 44, Rarity.UNCOMMON, mage.cards.a.Aladdin.class)); + cards.add(new SetCardInfo("Angelic Voices", 3, Rarity.RARE, mage.cards.a.AngelicVoices.class)); + cards.add(new SetCardInfo("Arcades Sabboth", 71, Rarity.RARE, mage.cards.a.ArcadesSabboth.class)); + cards.add(new SetCardInfo("Arena of the Ancients", 91, Rarity.RARE, mage.cards.a.ArenaOfTheAncients.class)); + cards.add(new SetCardInfo("Argothian Pixies", 57, Rarity.COMMON, mage.cards.a.ArgothianPixies.class)); + cards.add(new SetCardInfo("Ashnod's Altar", 92, Rarity.COMMON, mage.cards.a.AshnodsAltar.class)); + cards.add(new SetCardInfo("Ashnod's Transmogrant", 93, Rarity.COMMON, mage.cards.a.AshnodsTransmogrant.class)); + cards.add(new SetCardInfo("Axelrod Gunnarson", 72, Rarity.RARE, mage.cards.a.AxelrodGunnarson.class)); + cards.add(new SetCardInfo("Ayesha Tanaka", 73, Rarity.RARE, mage.cards.a.AyeshaTanaka.class)); + cards.add(new SetCardInfo("Azure Drake", 15, Rarity.UNCOMMON, mage.cards.a.AzureDrake.class)); + cards.add(new SetCardInfo("Banshee", 29, Rarity.UNCOMMON, mage.cards.b.Banshee.class)); + cards.add(new SetCardInfo("Barl's Cage", 94, Rarity.RARE, mage.cards.b.BarlsCage.class)); + cards.add(new SetCardInfo("Beasts of Bogardan", 45, Rarity.UNCOMMON, mage.cards.b.BeastsOfBogardan.class)); + cards.add(new SetCardInfo("Blood Moon", 46, Rarity.RARE, mage.cards.b.BloodMoon.class)); + cards.add(new SetCardInfo("Blood of the Martyr", 4, Rarity.UNCOMMON, mage.cards.b.BloodOfTheMartyr.class)); + cards.add(new SetCardInfo("Bog Rats", 30, Rarity.COMMON, mage.cards.b.BogRats.class)); + cards.add(new SetCardInfo("Book of Rass", 95, Rarity.RARE, mage.cards.b.BookOfRass.class)); + cards.add(new SetCardInfo("Boomerang", 16, Rarity.COMMON, mage.cards.b.Boomerang.class)); + cards.add(new SetCardInfo("Bronze Horse", 96, Rarity.RARE, mage.cards.b.BronzeHorse.class)); + cards.add(new SetCardInfo("Cat Warriors", 58, Rarity.COMMON, mage.cards.c.CatWarriors.class)); + cards.add(new SetCardInfo("Chromium", 74, Rarity.RARE, mage.cards.c.Chromium.class)); + cards.add(new SetCardInfo("City of Brass", 112, Rarity.RARE, mage.cards.c.CityOfBrass.class)); + cards.add(new SetCardInfo("Cocoon", 59, Rarity.UNCOMMON, mage.cards.c.Cocoon.class)); + cards.add(new SetCardInfo("Concordant Crossroads", 60, Rarity.RARE, mage.cards.c.ConcordantCrossroads.class)); + cards.add(new SetCardInfo("Craw Giant", 61, Rarity.UNCOMMON, mage.cards.c.CrawGiant.class)); + cards.add(new SetCardInfo("Cuombajj Witches", 31, Rarity.COMMON, mage.cards.c.CuombajjWitches.class)); + cards.add(new SetCardInfo("Cyclone", 62, Rarity.RARE, mage.cards.c.Cyclone.class)); + cards.add(new SetCardInfo("D'Avenant Archer", 5, Rarity.COMMON, mage.cards.d.DAvenantArcher.class)); + cards.add(new SetCardInfo("Dakkon Blackblade", 75, Rarity.RARE, mage.cards.d.DakkonBlackblade.class)); + cards.add(new SetCardInfo("Dance of Many", 17, Rarity.RARE, mage.cards.d.DanceOfMany.class)); + cards.add(new SetCardInfo("Dandan", 18, Rarity.COMMON, mage.cards.d.Dandan.class)); + cards.add(new SetCardInfo("Divine Offering", 6, Rarity.COMMON, mage.cards.d.DivineOffering.class)); + cards.add(new SetCardInfo("Emerald Dragonfly", 63, Rarity.COMMON, mage.cards.e.EmeraldDragonfly.class)); + cards.add(new SetCardInfo("Enchantment Alteration", 19, Rarity.UNCOMMON, mage.cards.e.EnchantmentAlteration.class)); + cards.add(new SetCardInfo("Erhnam Djinn", 64, Rarity.UNCOMMON, mage.cards.e.ErhnamDjinn.class)); + cards.add(new SetCardInfo("Fallen Angel", 32, Rarity.UNCOMMON, mage.cards.f.FallenAngel.class)); + cards.add(new SetCardInfo("Feldon's Cane", 97, Rarity.COMMON, mage.cards.f.FeldonsCane.class)); + cards.add(new SetCardInfo("Fire Drake", 47, Rarity.UNCOMMON, mage.cards.f.FireDrake.class)); + cards.add(new SetCardInfo("Fishliver Oil", 20, Rarity.COMMON, mage.cards.f.FishliverOil.class)); + cards.add(new SetCardInfo("Flash Flood", 21, Rarity.COMMON, mage.cards.f.FlashFlood.class)); + cards.add(new SetCardInfo("Fountain of Youth", 98, Rarity.COMMON, mage.cards.f.FountainOfYouth.class)); + cards.add(new SetCardInfo("Gabriel Angelfire", 76, Rarity.RARE, mage.cards.g.GabrielAngelfire.class)); + cards.add(new SetCardInfo("Gauntlets of Chaos", 99, Rarity.RARE, mage.cards.g.GauntletsOfChaos.class)); + cards.add(new SetCardInfo("Ghazban Ogre", 65, Rarity.COMMON, mage.cards.g.GhazbanOgre.class)); + cards.add(new SetCardInfo("Giant Slug", 33, Rarity.COMMON, mage.cards.g.GiantSlug.class)); + cards.add(new SetCardInfo("Goblin Artisans", 48, Rarity.UNCOMMON, mage.cards.g.GoblinArtisans.class)); + cards.add(new SetCardInfo("Goblin Digging Team", 49, Rarity.COMMON, mage.cards.g.GoblinDiggingTeam.class)); + cards.add(new SetCardInfo("Goblin Shrine", 50, Rarity.COMMON, mage.cards.g.GoblinShrine.class)); + cards.add(new SetCardInfo("Goblins of the Flarg", 51, Rarity.COMMON, mage.cards.g.GoblinsOfTheFlarg.class)); + cards.add(new SetCardInfo("Hasran Ogress", 34, Rarity.COMMON, mage.cards.h.HasranOgress.class)); + cards.add(new SetCardInfo("Hell's Caretaker", 35, Rarity.RARE, mage.cards.h.HellsCaretaker.class)); + cards.add(new SetCardInfo("Horn of Deafening", 100, Rarity.RARE, mage.cards.h.HornOfDeafening.class)); + cards.add(new SetCardInfo("Indestructible Aura", 7, Rarity.COMMON, mage.cards.i.IndestructibleAura.class)); + cards.add(new SetCardInfo("Ivory Guardians", 8, Rarity.UNCOMMON, mage.cards.i.IvoryGuardians.class)); + cards.add(new SetCardInfo("Jalum Tome", 101, Rarity.RARE, mage.cards.j.JalumTome.class)); + cards.add(new SetCardInfo("Johan", 77, Rarity.RARE, mage.cards.j.Johan.class)); + cards.add(new SetCardInfo("Juxtapose", 22, Rarity.RARE, mage.cards.j.Juxtapose.class)); + cards.add(new SetCardInfo("Keepers of the Faith", 9, Rarity.COMMON, mage.cards.k.KeepersOfTheFaith.class)); + cards.add(new SetCardInfo("Kei Takahashi", 78, Rarity.UNCOMMON, mage.cards.k.KeiTakahashi.class)); + cards.add(new SetCardInfo("Land's Edge", 52, Rarity.RARE, mage.cards.l.LandsEdge.class)); + cards.add(new SetCardInfo("Living Armor", 103, Rarity.COMMON, mage.cards.l.LivingArmor.class)); + cards.add(new SetCardInfo("Marhault Elsdragon", 79, Rarity.UNCOMMON, mage.cards.m.MarhaultElsdragon.class)); + cards.add(new SetCardInfo("Metamorphosis", 66, Rarity.COMMON, mage.cards.m.Metamorphosis.class)); + cards.add(new SetCardInfo("Mountain Yeti", 53, Rarity.COMMON, mage.cards.m.MountainYeti.class)); + cards.add(new SetCardInfo("Nebuchadnezzar", 80, Rarity.RARE, mage.cards.n.Nebuchadnezzar.class)); + cards.add(new SetCardInfo("Nicol Bolas", 81, Rarity.RARE, mage.cards.n.NicolBolas.class)); + cards.add(new SetCardInfo("Obelisk of Undoing", 104, Rarity.RARE, mage.cards.o.ObeliskOfUndoing.class)); + cards.add(new SetCardInfo("Palladia-Mors", 82, Rarity.RARE, mage.cards.p.PalladiaMors.class)); + cards.add(new SetCardInfo("Petra Sphinx", 10, Rarity.RARE, mage.cards.p.PetraSphinx.class)); + cards.add(new SetCardInfo("Primordial Ooze", 54, Rarity.UNCOMMON, mage.cards.p.PrimordialOoze.class)); + cards.add(new SetCardInfo("Puppet Master", 23, Rarity.UNCOMMON, mage.cards.p.PuppetMaster.class)); + cards.add(new SetCardInfo("Rabid Wombat", 67, Rarity.UNCOMMON, mage.cards.r.RabidWombat.class)); + cards.add(new SetCardInfo("Rakalite", 105, Rarity.RARE, mage.cards.r.Rakalite.class)); + cards.add(new SetCardInfo("Recall", 24, Rarity.UNCOMMON, mage.cards.r.Recall.class)); + cards.add(new SetCardInfo("Remove Soul", 25, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); + cards.add(new SetCardInfo("Repentant Blacksmith", 11, Rarity.COMMON, mage.cards.r.RepentantBlacksmith.class)); + cards.add(new SetCardInfo("Revelation", 68, Rarity.RARE, mage.cards.r.Revelation.class)); + cards.add(new SetCardInfo("Rubinia Soulsinger", 83, Rarity.RARE, mage.cards.r.RubiniaSoulsinger.class)); + cards.add(new SetCardInfo("Runesword", 106, Rarity.COMMON, mage.cards.r.Runesword.class)); + cards.add(new SetCardInfo("Safe Haven", 113, Rarity.RARE, mage.cards.s.SafeHaven.class)); + cards.add(new SetCardInfo("Scavenger Folk", 69, Rarity.COMMON, mage.cards.s.ScavengerFolk.class)); + cards.add(new SetCardInfo("Sentinel", 107, Rarity.RARE, mage.cards.s.Sentinel.class)); + cards.add(new SetCardInfo("Serpent Generator", 108, Rarity.RARE, mage.cards.s.SerpentGenerator.class)); + cards.add(new SetCardInfo("Shield Wall", 12, Rarity.UNCOMMON, mage.cards.s.ShieldWall.class)); + cards.add(new SetCardInfo("Shimian Night Stalker", 36, Rarity.UNCOMMON, mage.cards.s.ShimianNightStalker.class)); + cards.add(new SetCardInfo("Sivitri Scarzam", 84, Rarity.UNCOMMON, mage.cards.s.SivitriScarzam.class)); + cards.add(new SetCardInfo("Sol'kanar the Swamp King", 85, Rarity.RARE, mage.cards.s.SolkanarTheSwampKing.class)); + cards.add(new SetCardInfo("Stangg", 86, Rarity.RARE, mage.cards.s.Stangg.class)); + cards.add(new SetCardInfo("Storm Seeker", 70, Rarity.UNCOMMON, mage.cards.s.StormSeeker.class)); + cards.add(new SetCardInfo("Teleport", 26, Rarity.RARE, mage.cards.t.Teleport.class)); + cards.add(new SetCardInfo("The Fallen", 38, Rarity.UNCOMMON, mage.cards.t.TheFallen.class)); + cards.add(new SetCardInfo("The Wretched", 39, Rarity.RARE, mage.cards.t.TheWretched.class)); + cards.add(new SetCardInfo("Tobias Andrion", 87, Rarity.UNCOMMON, mage.cards.t.TobiasAndrion.class)); + cards.add(new SetCardInfo("Tor Wauki", 88, Rarity.UNCOMMON, mage.cards.t.TorWauki.class)); + cards.add(new SetCardInfo("Tormod's Crypt", 109, Rarity.COMMON, mage.cards.t.TormodsCrypt.class)); + cards.add(new SetCardInfo("Transmutation", 40, Rarity.COMMON, mage.cards.t.Transmutation.class)); + cards.add(new SetCardInfo("Triassic Egg", 110, Rarity.RARE, mage.cards.t.TriassicEgg.class)); + cards.add(new SetCardInfo("Urza's Mine", "114a", Rarity.UNCOMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Mine", "114b", Rarity.UNCOMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Mine", "114c", Rarity.UNCOMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Mine", "114d", Rarity.UNCOMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Power Plant", "115a", Rarity.UNCOMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Power Plant", "115b", Rarity.UNCOMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Power Plant", "115c", Rarity.UNCOMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Power Plant", "115d", Rarity.UNCOMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Tower", "116a", Rarity.UNCOMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Tower", "116b", Rarity.UNCOMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Tower", "116c", Rarity.UNCOMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Tower", "116d", Rarity.UNCOMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vaevictis Asmadi", 89, Rarity.RARE, mage.cards.v.VaevictisAsmadi.class)); + cards.add(new SetCardInfo("Voodoo Doll", 111, Rarity.RARE, mage.cards.v.VoodooDoll.class)); + cards.add(new SetCardInfo("Wall of Heat", 55, Rarity.COMMON, mage.cards.w.WallOfHeat.class)); + cards.add(new SetCardInfo("Wall of Opposition", 56, Rarity.UNCOMMON, mage.cards.w.WallOfOpposition.class)); + cards.add(new SetCardInfo("Wall of Shadows", 41, Rarity.COMMON, mage.cards.w.WallOfShadows.class)); + cards.add(new SetCardInfo("Wall of Vapor", 27, Rarity.COMMON, mage.cards.w.WallOfVapor.class)); + cards.add(new SetCardInfo("Wall of Wonder", 28, Rarity.UNCOMMON, mage.cards.w.WallOfWonder.class)); + cards.add(new SetCardInfo("War Elephant", 13, Rarity.COMMON, mage.cards.w.WarElephant.class)); + cards.add(new SetCardInfo("Witch Hunter", 14, Rarity.UNCOMMON, mage.cards.w.WitchHunter.class)); + cards.add(new SetCardInfo("Xira Arien", 90, Rarity.RARE, mage.cards.x.XiraArien.class)); + cards.add(new SetCardInfo("Yawgmoth Demon", 42, Rarity.RARE, mage.cards.y.YawgmothDemon.class)); + } + +} diff --git a/Mage.Sets/src/mage/sets/Commander2020Edition.java b/Mage.Sets/src/mage/sets/Commander2020Edition.java new file mode 100644 index 0000000000..b42d0becd1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/Commander2020Edition.java @@ -0,0 +1,358 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +import java.util.Arrays; +import java.util.List; + +/** + * @author TheElk801 + */ +public final class Commander2020Edition extends ExpansionSet { + + private static final List mutateNames = Arrays.asList( + "Mindleecher", + "Otrimi, the Ever-Playful", + "Sawtusk Demolisher", + "Souvenir Snatcher" + ); + + private static final Commander2020Edition instance = new Commander2020Edition(); + + public static Commander2020Edition getInstance() { + return instance; + } + + private Commander2020Edition() { + super("Commander 2020 Edition", "C20", ExpansionSet.buildDate(2020, 4, 24), SetType.SUPPLEMENTAL); + this.blockName = "Command Zone"; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Abandoned Sarcophagus", 236, Rarity.RARE, mage.cards.a.AbandonedSarcophagus.class)); + cards.add(new SetCardInfo("Abzan Ascendancy", 198, Rarity.RARE, mage.cards.a.AbzanAscendancy.class)); + cards.add(new SetCardInfo("Abzan Charm", 199, Rarity.UNCOMMON, mage.cards.a.AbzanCharm.class)); + cards.add(new SetCardInfo("Acidic Slime", 165, Rarity.UNCOMMON, mage.cards.a.AcidicSlime.class)); + cards.add(new SetCardInfo("Adriana, Captain of the Guard", 200, Rarity.RARE, mage.cards.a.AdrianaCaptainOfTheGuard.class)); + cards.add(new SetCardInfo("Aerial Responder", 72, Rarity.UNCOMMON, mage.cards.a.AerialResponder.class)); + cards.add(new SetCardInfo("Agitator Ant", 49, Rarity.RARE, mage.cards.a.AgitatorAnt.class)); + cards.add(new SetCardInfo("Ajani Unyielding", 201, Rarity.MYTHIC, mage.cards.a.AjaniUnyielding.class)); + cards.add(new SetCardInfo("Akim, the Soaring Wind", 6, Rarity.MYTHIC, mage.cards.a.AkimTheSoaringWind.class)); + cards.add(new SetCardInfo("Akroma's Vengeance", 74, Rarity.RARE, mage.cards.a.AkromasVengeance.class)); + cards.add(new SetCardInfo("Akroma, Angel of Wrath", 73, Rarity.MYTHIC, mage.cards.a.AkromaAngelOfWrath.class)); + cards.add(new SetCardInfo("Alesha, Who Smiles at Death", 143, Rarity.RARE, mage.cards.a.AleshaWhoSmilesAtDeath.class)); + cards.add(new SetCardInfo("Ambition's Cost", 129, Rarity.UNCOMMON, mage.cards.a.AmbitionsCost.class)); + cards.add(new SetCardInfo("Angel of Finality", 75, Rarity.RARE, mage.cards.a.AngelOfFinality.class)); + cards.add(new SetCardInfo("Animist's Awakening", 166, Rarity.RARE, mage.cards.a.AnimistsAwakening.class)); + cards.add(new SetCardInfo("Arcane Signet", 237, Rarity.COMMON, mage.cards.a.ArcaneSignet.class)); + cards.add(new SetCardInfo("Archon of Valor's Reach", 202, Rarity.RARE, mage.cards.a.ArchonOfValorsReach.class)); + cards.add(new SetCardInfo("Artifact Mutation", 203, Rarity.RARE, mage.cards.a.ArtifactMutation.class)); + cards.add(new SetCardInfo("Ash Barrens", 255, Rarity.COMMON, mage.cards.a.AshBarrens.class)); + cards.add(new SetCardInfo("Astral Drift", 76, Rarity.RARE, mage.cards.a.AstralDrift.class)); + cards.add(new SetCardInfo("Avenging Huntbonder", 22, Rarity.RARE, mage.cards.a.AvengingHuntbonder.class)); + cards.add(new SetCardInfo("Azorius Chancery", 256, Rarity.UNCOMMON, mage.cards.a.AzoriusChancery.class)); + cards.add(new SetCardInfo("Azorius Signet", 238, Rarity.UNCOMMON, mage.cards.a.AzoriusSignet.class)); + cards.add(new SetCardInfo("Banisher Priest", 77, Rarity.UNCOMMON, mage.cards.b.BanisherPriest.class)); + cards.add(new SetCardInfo("Battlefield Forge", 257, Rarity.RARE, mage.cards.b.BattlefieldForge.class)); + cards.add(new SetCardInfo("Beast Whisperer", 167, Rarity.RARE, mage.cards.b.BeastWhisperer.class)); + cards.add(new SetCardInfo("Beast Within", 168, Rarity.UNCOMMON, mage.cards.b.BeastWithin.class)); + cards.add(new SetCardInfo("Blighted Woodland", 258, Rarity.UNCOMMON, mage.cards.b.BlightedWoodland.class)); + cards.add(new SetCardInfo("Bojuka Bog", 259, Rarity.COMMON, mage.cards.b.BojukaBog.class)); + cards.add(new SetCardInfo("Bonder's Ornament", 67, Rarity.COMMON, mage.cards.b.BondersOrnament.class)); + cards.add(new SetCardInfo("Boneyard Mycodrax", 40, Rarity.RARE, mage.cards.b.BoneyardMycodrax.class)); + cards.add(new SetCardInfo("Boros Garrison", 260, Rarity.COMMON, mage.cards.b.BorosGarrison.class)); + cards.add(new SetCardInfo("Boros Signet", 239, Rarity.UNCOMMON, mage.cards.b.BorosSignet.class)); + cards.add(new SetCardInfo("Bounty Agent", 78, Rarity.RARE, mage.cards.b.BountyAgent.class)); + cards.add(new SetCardInfo("Brallin, Skyshark Rider", 4, Rarity.MYTHIC, mage.cards.b.BrallinSkysharkRider.class)); + cards.add(new SetCardInfo("Cairn Wanderer", 130, Rarity.RARE, mage.cards.c.CairnWanderer.class)); + cards.add(new SetCardInfo("Call the Coppercoats", 23, Rarity.RARE, mage.cards.c.CallTheCoppercoats.class)); + cards.add(new SetCardInfo("Canopy Vista", 261, Rarity.RARE, mage.cards.c.CanopyVista.class)); + cards.add(new SetCardInfo("Capricopian", 58, Rarity.RARE, mage.cards.c.Capricopian.class)); + cards.add(new SetCardInfo("Captivating Crew", 144, Rarity.RARE, mage.cards.c.CaptivatingCrew.class)); + cards.add(new SetCardInfo("Cartographer's Hawk", 24, Rarity.RARE, mage.cards.c.CartographersHawk.class)); + cards.add(new SetCardInfo("Cast Out", 79, Rarity.UNCOMMON, mage.cards.c.CastOut.class)); + cards.add(new SetCardInfo("Cataclysmic Gearhulk", 80, Rarity.MYTHIC, mage.cards.c.CataclysmicGearhulk.class)); + cards.add(new SetCardInfo("Cavalry Pegasus", 81, Rarity.COMMON, mage.cards.c.CavalryPegasus.class)); + cards.add(new SetCardInfo("Caves of Koilos", 262, Rarity.RARE, mage.cards.c.CavesOfKoilos.class)); + cards.add(new SetCardInfo("Cazur, Ruthless Stalker", 5, Rarity.MYTHIC, mage.cards.c.CazurRuthlessStalker.class)); + cards.add(new SetCardInfo("Chandra, Flamecaller", 145, Rarity.MYTHIC, mage.cards.c.ChandraFlamecaller.class)); + cards.add(new SetCardInfo("Chaos Warp", 146, Rarity.RARE, mage.cards.c.ChaosWarp.class)); + cards.add(new SetCardInfo("Charmbreaker Devils", 147, Rarity.RARE, mage.cards.c.CharmbreakerDevils.class)); + cards.add(new SetCardInfo("Chemister's Insight", 108, Rarity.UNCOMMON, mage.cards.c.ChemistersInsight.class)); + cards.add(new SetCardInfo("Cinder Glade", 263, Rarity.RARE, mage.cards.c.CinderGlade.class)); + cards.add(new SetCardInfo("Citywide Bust", 82, Rarity.RARE, mage.cards.c.CitywideBust.class)); + cards.add(new SetCardInfo("Cleansing Nova", 83, Rarity.RARE, mage.cards.c.CleansingNova.class)); + cards.add(new SetCardInfo("Cold-Eyed Selkie", 204, Rarity.RARE, mage.cards.c.ColdEyedSelkie.class)); + cards.add(new SetCardInfo("Comet Storm", 148, Rarity.MYTHIC, mage.cards.c.CometStorm.class)); + cards.add(new SetCardInfo("Command Tower", 264, Rarity.COMMON, mage.cards.c.CommandTower.class)); + cards.add(new SetCardInfo("Commander's Sphere", 240, Rarity.COMMON, mage.cards.c.CommandersSphere.class)); + cards.add(new SetCardInfo("Commune with Lava", 149, Rarity.RARE, mage.cards.c.CommuneWithLava.class)); + cards.add(new SetCardInfo("Crackling Doom", 205, Rarity.RARE, mage.cards.c.CracklingDoom.class)); + cards.add(new SetCardInfo("Crackling Drake", 206, Rarity.UNCOMMON, mage.cards.c.CracklingDrake.class)); + cards.add(new SetCardInfo("Crop Rotation", 169, Rarity.COMMON, mage.cards.c.CropRotation.class)); + cards.add(new SetCardInfo("Cryptic Trilobite", 21, Rarity.RARE, mage.cards.c.CrypticTrilobite.class)); + cards.add(new SetCardInfo("Crystalline Resonance", 31, Rarity.RARE, mage.cards.c.CrystallineResonance.class)); + cards.add(new SetCardInfo("Cultivate", 170, Rarity.COMMON, mage.cards.c.Cultivate.class)); + cards.add(new SetCardInfo("Curator of Mysteries", 109, Rarity.RARE, mage.cards.c.CuratorOfMysteries.class)); + cards.add(new SetCardInfo("Curious Herd", 59, Rarity.RARE, mage.cards.c.CuriousHerd.class)); + cards.add(new SetCardInfo("Daring Fiendbonder", 41, Rarity.RARE, mage.cards.d.DaringFiendbonder.class)); + cards.add(new SetCardInfo("Darkwater Catacombs", 265, Rarity.RARE, mage.cards.d.DarkwaterCatacombs.class)); + cards.add(new SetCardInfo("Deadbridge Chant", 207, Rarity.MYTHIC, mage.cards.d.DeadbridgeChant.class)); + cards.add(new SetCardInfo("Deadly Rollick", 42, Rarity.RARE, mage.cards.d.DeadlyRollick.class)); + cards.add(new SetCardInfo("Deadly Tempest", 131, Rarity.RARE, mage.cards.d.DeadlyTempest.class)); + cards.add(new SetCardInfo("Dearly Departed", 84, Rarity.RARE, mage.cards.d.DearlyDeparted.class)); + cards.add(new SetCardInfo("Deathsprout", 208, Rarity.UNCOMMON, mage.cards.d.Deathsprout.class)); + cards.add(new SetCardInfo("Decoy Gambit", 32, Rarity.RARE, mage.cards.d.DecoyGambit.class)); + cards.add(new SetCardInfo("Decree of Justice", 85, Rarity.RARE, mage.cards.d.DecreeOfJustice.class)); + cards.add(new SetCardInfo("Deflecting Swat", 50, Rarity.RARE, mage.cards.d.DeflectingSwat.class)); + cards.add(new SetCardInfo("Descend upon the Sinful", 86, Rarity.MYTHIC, mage.cards.d.DescendUponTheSinful.class)); + cards.add(new SetCardInfo("Desert of the Fervent", 266, Rarity.COMMON, mage.cards.d.DesertOfTheFervent.class)); + cards.add(new SetCardInfo("Desert of the Mindful", 267, Rarity.COMMON, mage.cards.d.DesertOfTheMindful.class)); + cards.add(new SetCardInfo("Desert of the True", 268, Rarity.COMMON, mage.cards.d.DesertOfTheTrue.class)); + cards.add(new SetCardInfo("Desolate Lighthouse", 269, Rarity.RARE, mage.cards.d.DesolateLighthouse.class)); + cards.add(new SetCardInfo("Despark", 209, Rarity.UNCOMMON, mage.cards.d.Despark.class)); + cards.add(new SetCardInfo("Devout Chaplain", 87, Rarity.UNCOMMON, mage.cards.d.DevoutChaplain.class)); + cards.add(new SetCardInfo("Dimir Aqueduct", 270, Rarity.UNCOMMON, mage.cards.d.DimirAqueduct.class)); + cards.add(new SetCardInfo("Disciple of Bolas", 132, Rarity.RARE, mage.cards.d.DiscipleOfBolas.class)); + cards.add(new SetCardInfo("Dismantling Wave", 25, Rarity.RARE, mage.cards.d.DismantlingWave.class)); + cards.add(new SetCardInfo("Djinn Illuminatus", 210, Rarity.RARE, mage.cards.d.DjinnIlluminatus.class)); + cards.add(new SetCardInfo("Drake Haven", 110, Rarity.RARE, mage.cards.d.DrakeHaven.class)); + cards.add(new SetCardInfo("Dredge the Mire", 43, Rarity.RARE, mage.cards.d.DredgeTheMire.class)); + cards.add(new SetCardInfo("Drifting Meadow", 271, Rarity.COMMON, mage.cards.d.DriftingMeadow.class)); + cards.add(new SetCardInfo("Dualcaster Mage", 150, Rarity.RARE, mage.cards.d.DualcasterMage.class)); + cards.add(new SetCardInfo("Duneblast", 211, Rarity.RARE, mage.cards.d.Duneblast.class)); + cards.add(new SetCardInfo("Endless Sands", 272, Rarity.RARE, mage.cards.e.EndlessSands.class)); + cards.add(new SetCardInfo("Eon Frolicker", 33, Rarity.RARE, mage.cards.e.EonFrolicker.class)); + cards.add(new SetCardInfo("Etali, Primal Storm", 151, Rarity.RARE, mage.cards.e.EtaliPrimalStorm.class)); + cards.add(new SetCardInfo("Eternal Dragon", 88, Rarity.RARE, mage.cards.e.EternalDragon.class)); + cards.add(new SetCardInfo("Ethereal Forager", 34, Rarity.RARE, mage.cards.e.EtherealForager.class)); + cards.add(new SetCardInfo("Ever After", 133, Rarity.RARE, mage.cards.e.EverAfter.class)); + cards.add(new SetCardInfo("Evolution Charm", 171, Rarity.COMMON, mage.cards.e.EvolutionCharm.class)); + cards.add(new SetCardInfo("Exotic Orchard", 273, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); + cards.add(new SetCardInfo("Fierce Guardianship", 35, Rarity.RARE, mage.cards.f.FierceGuardianship.class)); + cards.add(new SetCardInfo("Find // Finality", 212, Rarity.RARE, mage.cards.f.FindFinality.class)); + cards.add(new SetCardInfo("Fireflux Squad", 51, Rarity.RARE, mage.cards.f.FirefluxSquad.class)); + cards.add(new SetCardInfo("Flawless Maneuver", 26, Rarity.RARE, mage.cards.f.FlawlessManeuver.class)); + cards.add(new SetCardInfo("Fluctuator", 241, Rarity.RARE, mage.cards.f.Fluctuator.class)); + cards.add(new SetCardInfo("Forgotten Cave", 274, Rarity.UNCOMMON, mage.cards.f.ForgottenCave.class)); + cards.add(new SetCardInfo("Frantic Search", 111, Rarity.COMMON, mage.cards.f.FranticSearch.class)); + cards.add(new SetCardInfo("Frontier Bivouac", 275, Rarity.UNCOMMON, mage.cards.f.FrontierBivouac.class)); + cards.add(new SetCardInfo("Frontier Warmonger", 52, Rarity.RARE, mage.cards.f.FrontierWarmonger.class)); + cards.add(new SetCardInfo("Frontline Medic", 89, Rarity.RARE, mage.cards.f.FrontlineMedic.class)); + cards.add(new SetCardInfo("Fumiko the Lowblood", 152, Rarity.RARE, mage.cards.f.FumikoTheLowblood.class)); + cards.add(new SetCardInfo("Garna, the Bloodflame", 213, Rarity.UNCOMMON, mage.cards.g.GarnaTheBloodflame.class)); + cards.add(new SetCardInfo("Gavi, Nest Warden", 7, Rarity.MYTHIC, mage.cards.g.GaviNestWarden.class)); + cards.add(new SetCardInfo("Gavony Township", 276, Rarity.RARE, mage.cards.g.GavonyTownship.class)); + cards.add(new SetCardInfo("Gaze of Granite", 214, Rarity.RARE, mage.cards.g.GazeOfGranite.class)); + cards.add(new SetCardInfo("Genesis Hydra", 172, Rarity.RARE, mage.cards.g.GenesisHydra.class)); + cards.add(new SetCardInfo("Glademuse", 60, Rarity.RARE, mage.cards.g.Glademuse.class)); + cards.add(new SetCardInfo("Goblin Dark-Dwellers", 153, Rarity.RARE, mage.cards.g.GoblinDarkDwellers.class)); + cards.add(new SetCardInfo("Golgari Rot Farm", 277, Rarity.UNCOMMON, mage.cards.g.GolgariRotFarm.class)); + cards.add(new SetCardInfo("Grim Backwoods", 278, Rarity.RARE, mage.cards.g.GrimBackwoods.class)); + cards.add(new SetCardInfo("Grisly Salvage", 215, Rarity.COMMON, mage.cards.g.GrislySalvage.class)); + cards.add(new SetCardInfo("Growth Spiral", 216, Rarity.COMMON, mage.cards.g.GrowthSpiral.class)); + cards.add(new SetCardInfo("Gruul Turf", 279, Rarity.UNCOMMON, mage.cards.g.GruulTurf.class)); + cards.add(new SetCardInfo("Haldan, Avid Arcanist", 2, Rarity.MYTHIC, mage.cards.h.HaldanAvidArcanist.class)); + cards.add(new SetCardInfo("Halimar Depths", 280, Rarity.COMMON, mage.cards.h.HalimarDepths.class)); + cards.add(new SetCardInfo("Harmonize", 173, Rarity.UNCOMMON, mage.cards.h.Harmonize.class)); + cards.add(new SetCardInfo("Harrow", 174, Rarity.COMMON, mage.cards.h.Harrow.class)); + cards.add(new SetCardInfo("Heirloom Blade", 242, Rarity.UNCOMMON, mage.cards.h.HeirloomBlade.class)); + cards.add(new SetCardInfo("Herald of the Forgotten", 27, Rarity.RARE, mage.cards.h.HeraldOfTheForgotten.class)); + cards.add(new SetCardInfo("Heroes' Bane", 175, Rarity.UNCOMMON, mage.cards.h.HeroesBane.class)); + cards.add(new SetCardInfo("Hieroglyphic Illumination", 112, Rarity.COMMON, mage.cards.h.HieroglyphicIllumination.class)); + cards.add(new SetCardInfo("Hoofprints of the Stag", 90, Rarity.RARE, mage.cards.h.HoofprintsOfTheStag.class)); + cards.add(new SetCardInfo("Hornet Queen", 176, Rarity.RARE, mage.cards.h.HornetQueen.class)); + cards.add(new SetCardInfo("Hostile Desert", 281, Rarity.RARE, mage.cards.h.HostileDesert.class)); + cards.add(new SetCardInfo("Humble Defector", 154, Rarity.UNCOMMON, mage.cards.h.HumbleDefector.class)); + cards.add(new SetCardInfo("Hungering Hydra", 177, Rarity.RARE, mage.cards.h.HungeringHydra.class)); + cards.add(new SetCardInfo("Hunter's Insight", 178, Rarity.UNCOMMON, mage.cards.h.HuntersInsight.class)); + cards.add(new SetCardInfo("Hunting Pack", 179, Rarity.UNCOMMON, mage.cards.h.HuntingPack.class)); + cards.add(new SetCardInfo("Illusory Ambusher", 113, Rarity.UNCOMMON, mage.cards.i.IllusoryAmbusher.class)); + cards.add(new SetCardInfo("Increasing Devotion", 91, Rarity.RARE, mage.cards.i.IncreasingDevotion.class)); + cards.add(new SetCardInfo("Irrigated Farmland", 282, Rarity.RARE, mage.cards.i.IrrigatedFarmland.class)); + cards.add(new SetCardInfo("Isperia, Supreme Judge", 217, Rarity.MYTHIC, mage.cards.i.IsperiaSupremeJudge.class)); + cards.add(new SetCardInfo("Izzet Boilerworks", 283, Rarity.UNCOMMON, mage.cards.i.IzzetBoilerworks.class)); + cards.add(new SetCardInfo("Izzet Signet", 243, Rarity.UNCOMMON, mage.cards.i.IzzetSignet.class)); + cards.add(new SetCardInfo("Jace, Architect of Thought", 114, Rarity.MYTHIC, mage.cards.j.JaceArchitectOfThought.class)); + cards.add(new SetCardInfo("Jirina Kudro", 8, Rarity.MYTHIC, mage.cards.j.JirinaKudro.class)); + cards.add(new SetCardInfo("Kalamax, the Stormsire", 9, Rarity.MYTHIC, mage.cards.k.KalamaxTheStormsire.class)); + cards.add(new SetCardInfo("Kalemne's Captain", 92, Rarity.RARE, mage.cards.k.KalemnesCaptain.class)); + cards.add(new SetCardInfo("Karametra, God of Harvests", 218, Rarity.MYTHIC, mage.cards.k.KarametraGodOfHarvests.class)); + cards.add(new SetCardInfo("Kathril, Aspect Warper", 10, Rarity.MYTHIC, mage.cards.k.KathrilAspectWarper.class)); + cards.add(new SetCardInfo("Kelsien, the Plague", 11, Rarity.MYTHIC, mage.cards.k.KelsienThePlague.class)); + cards.add(new SetCardInfo("Kessig Wolf Run", 284, Rarity.RARE, mage.cards.k.KessigWolfRun.class)); + cards.add(new SetCardInfo("Knight of the White Orchid", 93, Rarity.RARE, mage.cards.k.KnightOfTheWhiteOrchid.class)); + cards.add(new SetCardInfo("Kodama's Reach", 180, Rarity.COMMON, mage.cards.k.KodamasReach.class)); + cards.add(new SetCardInfo("Krosan Grip", 181, Rarity.UNCOMMON, mage.cards.k.KrosanGrip.class)); + cards.add(new SetCardInfo("Krosan Verge", 285, Rarity.UNCOMMON, mage.cards.k.KrosanVerge.class)); + cards.add(new SetCardInfo("Lavabrink Floodgates", 53, Rarity.RARE, mage.cards.l.LavabrinkFloodgates.class)); + cards.add(new SetCardInfo("Lifecrafter's Bestiary", 244, Rarity.RARE, mage.cards.l.LifecraftersBestiary.class)); + cards.add(new SetCardInfo("Lightning Greaves", 245, Rarity.UNCOMMON, mage.cards.l.LightningGreaves.class)); + cards.add(new SetCardInfo("Lightning Rift", 155, Rarity.UNCOMMON, mage.cards.l.LightningRift.class)); + cards.add(new SetCardInfo("Llanowar Wastes", 286, Rarity.RARE, mage.cards.l.LlanowarWastes.class)); + cards.add(new SetCardInfo("Lonely Sandbar", 287, Rarity.UNCOMMON, mage.cards.l.LonelySandbar.class)); + cards.add(new SetCardInfo("Lunar Mystic", 115, Rarity.RARE, mage.cards.l.LunarMystic.class)); + cards.add(new SetCardInfo("Magus of the Disk", 94, Rarity.RARE, mage.cards.m.MagusOfTheDisk.class)); + cards.add(new SetCardInfo("Magus of the Wheel", 156, Rarity.RARE, mage.cards.m.MagusOfTheWheel.class)); + cards.add(new SetCardInfo("Majestic Myriarch", 182, Rarity.MYTHIC, mage.cards.m.MajesticMyriarch.class)); + cards.add(new SetCardInfo("Manascape Refractor", 68, Rarity.RARE, mage.cards.m.ManascapeRefractor.class)); + cards.add(new SetCardInfo("Martial Impetus", 28, Rarity.UNCOMMON, mage.cards.m.MartialImpetus.class)); + cards.add(new SetCardInfo("Masked Admirers", 183, Rarity.RARE, mage.cards.m.MaskedAdmirers.class)); + cards.add(new SetCardInfo("Melek, Izzet Paragon", 220, Rarity.RARE, mage.cards.m.MelekIzzetParagon.class)); + cards.add(new SetCardInfo("Memorial to Folly", 288, Rarity.UNCOMMON, mage.cards.m.MemorialToFolly.class)); + cards.add(new SetCardInfo("Mercurial Chemister", 221, Rarity.RARE, mage.cards.m.MercurialChemister.class)); + cards.add(new SetCardInfo("Migratory Route", 222, Rarity.UNCOMMON, mage.cards.m.MigratoryRoute.class)); + cards.add(new SetCardInfo("Mimic Vat", 246, Rarity.RARE, mage.cards.m.MimicVat.class)); + cards.add(new SetCardInfo("Mind Spring", 116, Rarity.RARE, mage.cards.m.MindSpring.class)); + cards.add(new SetCardInfo("Mindleecher", 44, Rarity.RARE, mage.cards.m.Mindleecher.class)); + cards.add(new SetCardInfo("Molten Echoes", 54, Rarity.RARE, mage.cards.m.MoltenEchoes.class)); + cards.add(new SetCardInfo("Mortuary Mire", 289, Rarity.COMMON, mage.cards.m.MortuaryMire.class)); + cards.add(new SetCardInfo("Mossfire Valley", 290, Rarity.RARE, mage.cards.m.MossfireValley.class)); + cards.add(new SetCardInfo("Mosswort Bridge", 291, Rarity.RARE, mage.cards.m.MosswortBridge.class)); + cards.add(new SetCardInfo("Mulldrifter", 117, Rarity.UNCOMMON, mage.cards.m.Mulldrifter.class)); + cards.add(new SetCardInfo("Murmuring Mystic", 118, Rarity.UNCOMMON, mage.cards.m.MurmuringMystic.class)); + cards.add(new SetCardInfo("Myriad Landscape", 292, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); + cards.add(new SetCardInfo("Mystic Monastery", 293, Rarity.UNCOMMON, mage.cards.m.MysticMonastery.class)); + cards.add(new SetCardInfo("Nahiri, the Harbinger", 223, Rarity.MYTHIC, mage.cards.n.NahiriTheHarbinger.class)); + cards.add(new SetCardInfo("Nascent Metamorph", 36, Rarity.RARE, mage.cards.n.NascentMetamorph.class)); + cards.add(new SetCardInfo("Natural Connection", 184, Rarity.COMMON, mage.cards.n.NaturalConnection.class)); + cards.add(new SetCardInfo("Nesting Grounds", 71, Rarity.RARE, mage.cards.n.NestingGrounds.class)); + cards.add(new SetCardInfo("Netherborn Altar", 45, Rarity.RARE, mage.cards.n.NetherbornAltar.class)); + cards.add(new SetCardInfo("New Perspectives", 119, Rarity.RARE, mage.cards.n.NewPerspectives.class)); + cards.add(new SetCardInfo("Niblis of Frost", 120, Rarity.RARE, mage.cards.n.NiblisOfFrost.class)); + cards.add(new SetCardInfo("Nikara, Lair Scavenger", 3, Rarity.MYTHIC, mage.cards.n.NikaraLairScavenger.class)); + cards.add(new SetCardInfo("Nimble Obstructionist", 121, Rarity.RARE, mage.cards.n.NimbleObstructionist.class)); + cards.add(new SetCardInfo("Nissa, Steward of Elements", 224, Rarity.MYTHIC, mage.cards.n.NissaStewardOfElements.class)); + cards.add(new SetCardInfo("Niv-Mizzet, the Firemind", 225, Rarity.RARE, mage.cards.n.NivMizzetTheFiremind.class)); + cards.add(new SetCardInfo("Nomad Outpost", 294, Rarity.UNCOMMON, mage.cards.n.NomadOutpost.class)); + cards.add(new SetCardInfo("Nyx Weaver", 226, Rarity.UNCOMMON, mage.cards.n.NyxWeaver.class)); + cards.add(new SetCardInfo("Obscuring Haze", 61, Rarity.RARE, mage.cards.o.ObscuringHaze.class)); + cards.add(new SetCardInfo("Odric, Lunarch Marshal", 95, Rarity.RARE, mage.cards.o.OdricLunarchMarshal.class)); + cards.add(new SetCardInfo("Odric, Master Tactician", 96, Rarity.RARE, mage.cards.o.OdricMasterTactician.class)); + cards.add(new SetCardInfo("Opulent Palace", 295, Rarity.UNCOMMON, mage.cards.o.OpulentPalace.class)); + cards.add(new SetCardInfo("Oran-Rief, the Vastwood", 296, Rarity.RARE, mage.cards.o.OranRiefTheVastwood.class)); + cards.add(new SetCardInfo("Orzhov Basilica", 297, Rarity.COMMON, mage.cards.o.OrzhovBasilica.class)); + cards.add(new SetCardInfo("Orzhov Signet", 247, Rarity.UNCOMMON, mage.cards.o.OrzhovSignet.class)); + cards.add(new SetCardInfo("Otrimi, the Ever-Playful", 12, Rarity.MYTHIC, mage.cards.o.OtrimiTheEverPlayful.class)); + cards.add(new SetCardInfo("Outpost Siege", 157, Rarity.RARE, mage.cards.o.OutpostSiege.class)); + cards.add(new SetCardInfo("Painful Truths", 134, Rarity.RARE, mage.cards.p.PainfulTruths.class)); + cards.add(new SetCardInfo("Pako, Arcane Retriever", 13, Rarity.MYTHIC, mage.cards.p.PakoArcaneRetriever.class)); + cards.add(new SetCardInfo("Parasitic Impetus", 46, Rarity.UNCOMMON, mage.cards.p.ParasiticImpetus.class)); + cards.add(new SetCardInfo("Path of Ancestry", 298, Rarity.COMMON, mage.cards.p.PathOfAncestry.class)); + cards.add(new SetCardInfo("Portal Mage", 122, Rarity.RARE, mage.cards.p.PortalMage.class)); + cards.add(new SetCardInfo("Prairie Stream", 299, Rarity.RARE, mage.cards.p.PrairieStream.class)); + cards.add(new SetCardInfo("Predator Ooze", 185, Rarity.RARE, mage.cards.p.PredatorOoze.class)); + cards.add(new SetCardInfo("Predatory Impetus", 62, Rarity.UNCOMMON, mage.cards.p.PredatoryImpetus.class)); + cards.add(new SetCardInfo("Profane Command", 135, Rarity.RARE, mage.cards.p.ProfaneCommand.class)); + cards.add(new SetCardInfo("Propaganda", 123, Rarity.UNCOMMON, mage.cards.p.Propaganda.class)); + cards.add(new SetCardInfo("Prophetic Bolt", 227, Rarity.RARE, mage.cards.p.PropheticBolt.class)); + cards.add(new SetCardInfo("Psychic Impetus", 37, Rarity.UNCOMMON, mage.cards.p.PsychicImpetus.class)); + cards.add(new SetCardInfo("Psychosis Crawler", 248, Rarity.RARE, mage.cards.p.PsychosisCrawler.class)); + cards.add(new SetCardInfo("Putrefy", 228, Rarity.UNCOMMON, mage.cards.p.Putrefy.class)); + cards.add(new SetCardInfo("Rakdos Carnarium", 300, Rarity.COMMON, mage.cards.r.RakdosCarnarium.class)); + cards.add(new SetCardInfo("Rakdos Signet", 249, Rarity.UNCOMMON, mage.cards.r.RakdosSignet.class)); + cards.add(new SetCardInfo("Rashmi, Eternities Crafter", 229, Rarity.MYTHIC, mage.cards.r.RashmiEternitiesCrafter.class)); + cards.add(new SetCardInfo("Ravenous Gigantotherium", 63, Rarity.RARE, mage.cards.r.RavenousGigantotherium.class)); + cards.add(new SetCardInfo("Reclamation Sage", 186, Rarity.UNCOMMON, mage.cards.r.ReclamationSage.class)); + cards.add(new SetCardInfo("Reliquary Tower", 301, Rarity.UNCOMMON, mage.cards.r.ReliquaryTower.class)); + cards.add(new SetCardInfo("Remote Isle", 302, Rarity.COMMON, mage.cards.r.RemoteIsle.class)); + cards.add(new SetCardInfo("Reveillark", 97, Rarity.RARE, mage.cards.r.Reveillark.class)); + cards.add(new SetCardInfo("Riders of Gavony", 98, Rarity.RARE, mage.cards.r.RidersOfGavony.class)); + cards.add(new SetCardInfo("Rogue's Passage", 303, Rarity.UNCOMMON, mage.cards.r.RoguesPassage.class)); + cards.add(new SetCardInfo("Rupture Spire", 304, Rarity.COMMON, mage.cards.r.RuptureSpire.class)); + cards.add(new SetCardInfo("Sakura-Tribe Elder", 187, Rarity.COMMON, mage.cards.s.SakuraTribeElder.class)); + cards.add(new SetCardInfo("Sanctuary Blade", 69, Rarity.RARE, mage.cards.s.SanctuaryBlade.class)); + cards.add(new SetCardInfo("Sandsteppe Citadel", 305, Rarity.UNCOMMON, mage.cards.s.SandsteppeCitadel.class)); + cards.add(new SetCardInfo("Satyr Wayfinder", 188, Rarity.COMMON, mage.cards.s.SatyrWayfinder.class)); + cards.add(new SetCardInfo("Sawtusk Demolisher", 64, Rarity.RARE, mage.cards.s.SawtuskDemolisher.class)); + cards.add(new SetCardInfo("Scavenger Grounds", 306, Rarity.RARE, mage.cards.s.ScavengerGrounds.class)); + cards.add(new SetCardInfo("Secluded Steppe", 307, Rarity.UNCOMMON, mage.cards.s.SecludedSteppe.class)); + cards.add(new SetCardInfo("Selective Adaptation", 65, Rarity.RARE, mage.cards.s.SelectiveAdaptation.class)); + cards.add(new SetCardInfo("Selesnya Sanctuary", 308, Rarity.COMMON, mage.cards.s.SelesnyaSanctuary.class)); + cards.add(new SetCardInfo("Shabraz, the Skyshark", 14, Rarity.MYTHIC, mage.cards.s.ShabrazTheSkyshark.class)); + cards.add(new SetCardInfo("Shadowblood Ridge", 309, Rarity.RARE, mage.cards.s.ShadowbloodRidge.class)); + cards.add(new SetCardInfo("Shared Animosity", 158, Rarity.RARE, mage.cards.s.SharedAnimosity.class)); + cards.add(new SetCardInfo("Shiny Impetus", 55, Rarity.UNCOMMON, mage.cards.s.ShinyImpetus.class)); + cards.add(new SetCardInfo("Shivan Reef", 310, Rarity.RARE, mage.cards.s.ShivanReef.class)); + cards.add(new SetCardInfo("Shriekmaw", 136, Rarity.UNCOMMON, mage.cards.s.Shriekmaw.class)); + cards.add(new SetCardInfo("Silent Arbiter", 250, Rarity.RARE, mage.cards.s.SilentArbiter.class)); + cards.add(new SetCardInfo("Silvar, Devourer of the Free", 15, Rarity.MYTHIC, mage.cards.s.SilvarDevourerOfTheFree.class)); + cards.add(new SetCardInfo("Simic Growth Chamber", 311, Rarity.UNCOMMON, mage.cards.s.SimicGrowthChamber.class)); + cards.add(new SetCardInfo("Skullclamp", 251, Rarity.UNCOMMON, mage.cards.s.Skullclamp.class)); + cards.add(new SetCardInfo("Skullwinder", 189, Rarity.UNCOMMON, mage.cards.s.Skullwinder.class)); + cards.add(new SetCardInfo("Skycloud Expanse", 312, Rarity.RARE, mage.cards.s.SkycloudExpanse.class)); + cards.add(new SetCardInfo("Slice and Dice", 159, Rarity.UNCOMMON, mage.cards.s.SliceAndDice.class)); + cards.add(new SetCardInfo("Slice in Twain", 190, Rarity.UNCOMMON, mage.cards.s.SliceInTwain.class)); + cards.add(new SetCardInfo("Slippery Bogbonder", 66, Rarity.RARE, mage.cards.s.SlipperyBogbonder.class)); + cards.add(new SetCardInfo("Smoldering Crater", 313, Rarity.COMMON, mage.cards.s.SmolderingCrater.class)); + cards.add(new SetCardInfo("Smoldering Marsh", 314, Rarity.RARE, mage.cards.s.SmolderingMarsh.class)); + cards.add(new SetCardInfo("Soaring Seacliff", 315, Rarity.COMMON, mage.cards.s.SoaringSeacliff.class)); + cards.add(new SetCardInfo("Sol Ring", 252, Rarity.UNCOMMON, mage.cards.s.SolRing.class)); + cards.add(new SetCardInfo("Solemn Recruit", 99, Rarity.RARE, mage.cards.s.SolemnRecruit.class)); + cards.add(new SetCardInfo("Solemn Simulacrum", 253, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class)); + cards.add(new SetCardInfo("Soul of Innistrad", 137, Rarity.MYTHIC, mage.cards.s.SoulOfInnistrad.class)); + cards.add(new SetCardInfo("Soulflayer", 138, Rarity.RARE, mage.cards.s.Soulflayer.class)); + cards.add(new SetCardInfo("Souvenir Snatcher", 38, Rarity.RARE, mage.cards.s.SouvenirSnatcher.class)); + cards.add(new SetCardInfo("Species Specialist", 47, Rarity.RARE, mage.cards.s.SpeciesSpecialist.class)); + cards.add(new SetCardInfo("Spellpyre Phoenix", 56, Rarity.RARE, mage.cards.s.SpellpyrePhoenix.class)); + cards.add(new SetCardInfo("Spinerock Knoll", 316, Rarity.RARE, mage.cards.s.SpinerockKnoll.class)); + cards.add(new SetCardInfo("Spirit Cairn", 100, Rarity.UNCOMMON, mage.cards.s.SpiritCairn.class)); + cards.add(new SetCardInfo("Splinterfright", 191, Rarity.RARE, mage.cards.s.Splinterfright.class)); + cards.add(new SetCardInfo("Starstorm", 160, Rarity.RARE, mage.cards.s.Starstorm.class)); + cards.add(new SetCardInfo("Strength of the Tajuru", 192, Rarity.RARE, mage.cards.s.StrengthOfTheTajuru.class)); + cards.add(new SetCardInfo("Sun Titan", 101, Rarity.MYTHIC, mage.cards.s.SunTitan.class)); + cards.add(new SetCardInfo("Sunblast Angel", 102, Rarity.RARE, mage.cards.s.SunblastAngel.class)); + cards.add(new SetCardInfo("Sungrass Prairie", 317, Rarity.RARE, mage.cards.s.SungrassPrairie.class)); + cards.add(new SetCardInfo("Sunken Hollow", 318, Rarity.RARE, mage.cards.s.SunkenHollow.class)); + cards.add(new SetCardInfo("Surly Badgersaur", 57, Rarity.RARE, mage.cards.s.SurlyBadgersaur.class)); + cards.add(new SetCardInfo("Surreal Memoir", 161, Rarity.UNCOMMON, mage.cards.s.SurrealMemoir.class)); + cards.add(new SetCardInfo("Swarm Intelligence", 124, Rarity.RARE, mage.cards.s.SwarmIntelligence.class)); + cards.add(new SetCardInfo("Swiftfoot Boots", 254, Rarity.UNCOMMON, mage.cards.s.SwiftfootBoots.class)); + cards.add(new SetCardInfo("Talrand, Sky Summoner", 125, Rarity.RARE, mage.cards.t.TalrandSkySummoner.class)); + cards.add(new SetCardInfo("Tayam, Luminous Enigma", 16, Rarity.MYTHIC, mage.cards.t.TayamLuminousEnigma.class)); + cards.add(new SetCardInfo("Tectonic Reformation", 162, Rarity.RARE, mage.cards.t.TectonicReformation.class)); + cards.add(new SetCardInfo("Temple of the False God", 319, Rarity.UNCOMMON, mage.cards.t.TempleOfTheFalseGod.class)); + cards.add(new SetCardInfo("Temur Charm", 230, Rarity.UNCOMMON, mage.cards.t.TemurCharm.class)); + cards.add(new SetCardInfo("Terminate", 231, Rarity.UNCOMMON, mage.cards.t.Terminate.class)); + cards.add(new SetCardInfo("Thalia's Lieutenant", 103, Rarity.RARE, mage.cards.t.ThaliasLieutenant.class)); + cards.add(new SetCardInfo("The Locust God", 219, Rarity.MYTHIC, mage.cards.t.TheLocustGod.class)); + cards.add(new SetCardInfo("Thraben Doomsayer", 104, Rarity.RARE, mage.cards.t.ThrabenDoomsayer.class)); + cards.add(new SetCardInfo("Tidal Barracuda", 39, Rarity.RARE, mage.cards.t.TidalBarracuda.class)); + cards.add(new SetCardInfo("Titan Hunter", 48, Rarity.RARE, mage.cards.t.TitanHunter.class)); + cards.add(new SetCardInfo("Titan of Eternal Fire", 163, Rarity.RARE, mage.cards.t.TitanOfEternalFire.class)); + cards.add(new SetCardInfo("Together Forever", 105, Rarity.RARE, mage.cards.t.TogetherForever.class)); + cards.add(new SetCardInfo("Tribute to the Wild", 193, Rarity.UNCOMMON, mage.cards.t.TributeToTheWild.class)); + cards.add(new SetCardInfo("Trygon Predator", 232, Rarity.UNCOMMON, mage.cards.t.TrygonPredator.class)); + cards.add(new SetCardInfo("Trynn, Champion of Freedom", 1, Rarity.MYTHIC, mage.cards.t.TrynnChampionOfFreedom.class)); + cards.add(new SetCardInfo("Twinning Staff", 70, Rarity.RARE, mage.cards.t.TwinningStaff.class)); + cards.add(new SetCardInfo("Ukkima, Stalking Shadow", 17, Rarity.MYTHIC, mage.cards.u.UkkimaStalkingShadow.class)); + cards.add(new SetCardInfo("Unburial Rites", 139, Rarity.UNCOMMON, mage.cards.u.UnburialRites.class)); + cards.add(new SetCardInfo("Unclaimed Territory", 320, Rarity.UNCOMMON, mage.cards.u.UnclaimedTerritory.class)); + cards.add(new SetCardInfo("Unexpectedly Absent", 106, Rarity.RARE, mage.cards.u.UnexpectedlyAbsent.class)); + cards.add(new SetCardInfo("Vampire Nighthawk", 140, Rarity.UNCOMMON, mage.cards.v.VampireNighthawk.class)); + cards.add(new SetCardInfo("Vastwood Hydra", 194, Rarity.RARE, mage.cards.v.VastwoodHydra.class)); + cards.add(new SetCardInfo("Verge Rangers", 29, Rarity.RARE, mage.cards.v.VergeRangers.class)); + cards.add(new SetCardInfo("Vigilante Justice", 164, Rarity.UNCOMMON, mage.cards.v.VigilanteJustice.class)); + cards.add(new SetCardInfo("Villainous Wealth", 233, Rarity.RARE, mage.cards.v.VillainousWealth.class)); + cards.add(new SetCardInfo("Vitality Hunter", 30, Rarity.RARE, mage.cards.v.VitalityHunter.class)); + cards.add(new SetCardInfo("Vizier of Tumbling Sands", 126, Rarity.UNCOMMON, mage.cards.v.VizierOfTumblingSands.class)); + cards.add(new SetCardInfo("Vorapede", 195, Rarity.MYTHIC, mage.cards.v.Vorapede.class)); + cards.add(new SetCardInfo("Whiplash Trap", 127, Rarity.COMMON, mage.cards.w.WhiplashTrap.class)); + cards.add(new SetCardInfo("Wilderness Reclamation", 196, Rarity.UNCOMMON, mage.cards.w.WildernessReclamation.class)); + cards.add(new SetCardInfo("Windbrisk Heights", 321, Rarity.RARE, mage.cards.w.WindbriskHeights.class)); + cards.add(new SetCardInfo("Windfall", 128, Rarity.UNCOMMON, mage.cards.w.Windfall.class)); + cards.add(new SetCardInfo("Wort, the Raidmother", 234, Rarity.RARE, mage.cards.w.WortTheRaidmother.class)); + cards.add(new SetCardInfo("Wydwen, the Biting Gale", 235, Rarity.RARE, mage.cards.w.WydwenTheBitingGale.class)); + cards.add(new SetCardInfo("Xathrid Necromancer", 141, Rarity.RARE, mage.cards.x.XathridNecromancer.class)); + cards.add(new SetCardInfo("Xyris, the Writhing Storm", 18, Rarity.MYTHIC, mage.cards.x.XyrisTheWrithingStorm.class)); + cards.add(new SetCardInfo("Yannik, Scavenging Sentinel", 19, Rarity.MYTHIC, mage.cards.y.YannikScavengingSentinel.class)); + cards.add(new SetCardInfo("Yavimaya Coast", 322, Rarity.RARE, mage.cards.y.YavimayaCoast.class)); + cards.add(new SetCardInfo("Yavimaya Dryad", 197, Rarity.UNCOMMON, mage.cards.y.YavimayaDryad.class)); + cards.add(new SetCardInfo("Zaxara, the Exemplary", 20, Rarity.MYTHIC, mage.cards.z.ZaxaraTheExemplary.class)); + cards.add(new SetCardInfo("Zetalpa, Primal Dawn", 107, Rarity.RARE, mage.cards.z.ZetalpaPrimalDawn.class)); + cards.add(new SetCardInfo("Zulaport Cutthroat", 142, Rarity.UNCOMMON, mage.cards.z.ZulaportCutthroat.class)); + + cards.removeIf(setCardInfo -> mutateNames.contains(setCardInfo.getName())); // remove when mutate is implemented + } +} diff --git a/Mage.Sets/src/mage/sets/CoreSet2019.java b/Mage.Sets/src/mage/sets/CoreSet2019.java index bb7cb7ef76..ec4614f367 100644 --- a/Mage.Sets/src/mage/sets/CoreSet2019.java +++ b/Mage.Sets/src/mage/sets/CoreSet2019.java @@ -1,7 +1,5 @@ package mage.sets; -import java.util.ArrayList; -import java.util.List; import mage.cards.ExpansionSet; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; @@ -10,8 +8,10 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** - * * @author TheElk801 */ public final class CoreSet2019 extends ExpansionSet { @@ -36,7 +36,7 @@ public final class CoreSet2019 extends ExpansionSet { this.ratioBoosterMythic = 8; this.numBoosterDoubleFaced = -1; this.maxCardNumberInBooster = 280; - + // Core 2019 boosters have a 5/12 chance of basic land being replaced // with the common taplands, which DO NOT appear in the common slot. this.ratioBoosterSpecialLand = 12; @@ -380,23 +380,18 @@ public final class CoreSet2019 extends ExpansionSet { return super.getCardsByRarity(rarity); } } - + @Override // the common taplands replacing the basic land - public List getSpecialLand() - { - if (savedSpecialLand.isEmpty()) - { + public List getSpecialLand() { + if (savedSpecialLand.isEmpty()) { CardCriteria criteria = new CardCriteria(); criteria.setCodes(this.code); criteria.rarities(Rarity.COMMON); criteria.types(CardType.LAND); savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); } - + return new ArrayList<>(savedSpecialLand); } - - - } diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java new file mode 100644 index 0000000000..0db411aaa9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/CoreSet2021.java @@ -0,0 +1,479 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.SetType; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author TheElk801 + */ +public final class CoreSet2021 extends ExpansionSet { + + private static final CoreSet2021 instance = new CoreSet2021(); + + public static CoreSet2021 getInstance() { + return instance; + } + + private final List savedSpecialLand = new ArrayList<>(); + + private CoreSet2021() { + super("Core Set 2021", "M21", ExpansionSet.buildDate(2020, 7, 3), SetType.CORE); + this.hasBoosters = true; + this.hasBasicLands = true; + this.numBoosterSpecial = 0; + this.numBoosterLands = 1; + this.numBoosterCommon = 10; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 8; + this.maxCardNumberInBooster = 274; + this.ratioBoosterSpecialLand = 2; + this.ratioBoosterSpecialLandNumerator = 1; + + cards.add(new SetCardInfo("Adherent of Hope", 321, Rarity.COMMON, mage.cards.a.AdherentOfHope.class)); + cards.add(new SetCardInfo("Alchemist's Gift", 87, Rarity.COMMON, mage.cards.a.AlchemistsGift.class)); + cards.add(new SetCardInfo("Alpine Houndmaster", 215, Rarity.UNCOMMON, mage.cards.a.AlpineHoundmaster.class)); + cards.add(new SetCardInfo("Alpine Watchdog", 2, Rarity.COMMON, mage.cards.a.AlpineWatchdog.class)); + cards.add(new SetCardInfo("Angelic Ascension", 3, Rarity.UNCOMMON, mage.cards.a.AngelicAscension.class)); + cards.add(new SetCardInfo("Animal Sanctuary", 242, Rarity.RARE, mage.cards.a.AnimalSanctuary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Animal Sanctuary", 385, Rarity.RARE, mage.cards.a.AnimalSanctuary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anointed Chorister", 4, Rarity.COMMON, mage.cards.a.AnointedChorister.class)); + cards.add(new SetCardInfo("Archfiend's Vessel", 88, Rarity.UNCOMMON, mage.cards.a.ArchfiendsVessel.class)); + cards.add(new SetCardInfo("Aven Gagglemaster", 5, Rarity.UNCOMMON, mage.cards.a.AvenGagglemaster.class)); + cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Azusa, Lost but Seeking", 372, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class)); + cards.add(new SetCardInfo("Baneslayer Angel", 340, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Barrin, Tolarian Archmage", 348, Rarity.RARE, mage.cards.b.BarrinTolarianArchmage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Barrin, Tolarian Archmage", 45, Rarity.RARE, mage.cards.b.BarrinTolarianArchmage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basri Ket", 280, Rarity.MYTHIC, mage.cards.b.BasriKet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basri Ket", 286, Rarity.MYTHIC, mage.cards.b.BasriKet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basri Ket", 7, Rarity.MYTHIC, mage.cards.b.BasriKet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basri's Acolyte", 287, Rarity.COMMON, mage.cards.b.BasrisAcolyte.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basri's Acolyte", 8, Rarity.COMMON, mage.cards.b.BasrisAcolyte.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basri's Aegis", 322, Rarity.RARE, mage.cards.b.BasrisAegis.class)); + cards.add(new SetCardInfo("Basri's Lieutenant", 288, Rarity.RARE, mage.cards.b.BasrisLieutenant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basri's Lieutenant", 9, Rarity.RARE, mage.cards.b.BasrisLieutenant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basri's Solidarity", 10, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basri's Solidarity", 289, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class)); + cards.add(new SetCardInfo("Battle-Rattle Shaman", 130, Rarity.UNCOMMON, mage.cards.b.BattleRattleShaman.class)); + cards.add(new SetCardInfo("Blood Glutton", 90, Rarity.COMMON, mage.cards.b.BloodGlutton.class)); + cards.add(new SetCardInfo("Bloodfell Caves", 243, Rarity.COMMON, mage.cards.b.BloodfellCaves.class)); + cards.add(new SetCardInfo("Blossoming Sands", 244, Rarity.COMMON, mage.cards.b.BlossomingSands.class)); + cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class)); + cards.add(new SetCardInfo("Bone Pit Brute", 132, Rarity.COMMON, mage.cards.b.BonePitBrute.class)); + cards.add(new SetCardInfo("Brash Taunter", 133, Rarity.RARE, mage.cards.b.BrashTaunter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Brash Taunter", 363, Rarity.RARE, mage.cards.b.BrashTaunter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Burlfist Oak", 174, Rarity.UNCOMMON, mage.cards.b.BurlfistOak.class)); + cards.add(new SetCardInfo("Burn Bright", 134, Rarity.COMMON, mage.cards.b.BurnBright.class)); + cards.add(new SetCardInfo("Caged Zombie", 91, Rarity.COMMON, mage.cards.c.CagedZombie.class)); + cards.add(new SetCardInfo("Cancel", 46, Rarity.COMMON, mage.cards.c.Cancel.class)); + cards.add(new SetCardInfo("Canopy Stalker", 175, Rarity.UNCOMMON, mage.cards.c.CanopyStalker.class)); + cards.add(new SetCardInfo("Capture Sphere", 47, Rarity.COMMON, mage.cards.c.CaptureSphere.class)); + cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class)); + cards.add(new SetCardInfo("Celestial Enforcer", 11, Rarity.COMMON, mage.cards.c.CelestialEnforcer.class)); + cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class)); + cards.add(new SetCardInfo("Chandra's Incinerator", 136, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chandra's Incinerator", 302, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chandra's Magmutt", 137, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chandra's Magmutt", 303, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chandra's Pyreling", 138, Rarity.UNCOMMON, mage.cards.c.ChandrasPyreling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chandra's Pyreling", 304, Rarity.UNCOMMON, mage.cards.c.ChandrasPyreling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class)); + cards.add(new SetCardInfo("Chandra, Heart of Fire", 135, Rarity.MYTHIC, mage.cards.c.ChandraHeartOfFire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chandra, Heart of Fire", 283, Rarity.MYTHIC, mage.cards.c.ChandraHeartOfFire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chandra, Heart of Fire", 301, Rarity.MYTHIC, mage.cards.c.ChandraHeartOfFire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chromatic Orrery", 382, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chrome Replicator", 229, Rarity.UNCOMMON, mage.cards.c.ChromeReplicator.class)); + cards.add(new SetCardInfo("Colossal Dreadmaw", 176, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class)); + cards.add(new SetCardInfo("Conclave Mentor", 216, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class)); + cards.add(new SetCardInfo("Concordia Pegasus", 12, Rarity.COMMON, mage.cards.c.ConcordiaPegasus.class)); + cards.add(new SetCardInfo("Conspicuous Snoop", 139, Rarity.RARE, mage.cards.c.ConspicuousSnoop.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Conspicuous Snoop", 364, Rarity.RARE, mage.cards.c.ConspicuousSnoop.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Containment Priest", 13, Rarity.RARE, mage.cards.c.ContainmentPriest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Crash Through", 140, Rarity.COMMON, mage.cards.c.CrashThrough.class)); + cards.add(new SetCardInfo("Crypt Lurker", 93, Rarity.COMMON, mage.cards.c.CryptLurker.class)); + cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cultivate", 317, Rarity.RARE, mage.cards.c.Cultivate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Daybreak Charger", 14, Rarity.COMMON, mage.cards.d.DaybreakCharger.class)); + cards.add(new SetCardInfo("Deathbloom Thallid", 94, Rarity.COMMON, mage.cards.d.DeathbloomThallid.class)); + cards.add(new SetCardInfo("Defiant Strike", 15, Rarity.COMMON, mage.cards.d.DefiantStrike.class)); + cards.add(new SetCardInfo("Demonic Embrace", 356, Rarity.RARE, mage.cards.d.DemonicEmbrace.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Demonic Embrace", 95, Rarity.RARE, mage.cards.d.DemonicEmbrace.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Destructive Tampering", 141, Rarity.COMMON, mage.cards.d.DestructiveTampering.class)); + cards.add(new SetCardInfo("Dire Fleet Warmonger", 217, Rarity.UNCOMMON, mage.cards.d.DireFleetWarmonger.class)); + cards.add(new SetCardInfo("Discontinuity", 349, Rarity.MYTHIC, mage.cards.d.Discontinuity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Discontinuity", 48, Rarity.MYTHIC, mage.cards.d.Discontinuity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dismal Backwater", 245, Rarity.COMMON, mage.cards.d.DismalBackwater.class)); + cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Double Vision", 365, Rarity.RARE, mage.cards.d.DoubleVision.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Drowsing Tyrannodon", 178, Rarity.COMMON, mage.cards.d.DrowsingTyrannodon.class)); + cards.add(new SetCardInfo("Dub", 16, Rarity.COMMON, mage.cards.d.Dub.class)); + cards.add(new SetCardInfo("Duress", 96, Rarity.COMMON, mage.cards.d.Duress.class)); + cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elder Gargaroth", 373, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eliminate", 395, Rarity.UNCOMMON, mage.cards.e.Eliminate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Enthralling Hold", 49, Rarity.UNCOMMON, mage.cards.e.EnthrallingHold.class)); + cards.add(new SetCardInfo("Epitaph Golem", 230, Rarity.UNCOMMON, mage.cards.e.EpitaphGolem.class)); + cards.add(new SetCardInfo("Experimental Overload", 218, Rarity.UNCOMMON, mage.cards.e.ExperimentalOverload.class)); + cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fabled Passage", 386, Rarity.RARE, mage.cards.f.FabledPassage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class)); + cards.add(new SetCardInfo("Falconer Adept", 18, Rarity.UNCOMMON, mage.cards.f.FalconerAdept.class)); + cards.add(new SetCardInfo("Feat of Resistance", 19, Rarity.COMMON, mage.cards.f.FeatOfResistance.class)); + cards.add(new SetCardInfo("Feline Sovereign", 180, Rarity.RARE, mage.cards.f.FelineSovereign.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Feline Sovereign", 374, Rarity.RARE, mage.cards.f.FelineSovereign.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fetid Imp", 98, Rarity.COMMON, mage.cards.f.FetidImp.class)); + cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class)); + cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fiery Emancipation", 366, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Finishing Blow", 99, Rarity.COMMON, mage.cards.f.FinishingBlow.class)); + cards.add(new SetCardInfo("Forest", 272, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 273, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 274, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 313, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forgotten Sentinel", 231, Rarity.COMMON, mage.cards.f.ForgottenSentinel.class)); + cards.add(new SetCardInfo("Frantic Inventory", 394, Rarity.COMMON, mage.cards.f.FranticInventory.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frantic Inventory", 50, Rarity.COMMON, mage.cards.f.FranticInventory.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frost Breath", 51, Rarity.COMMON, mage.cards.f.FrostBreath.class)); + cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class)); + cards.add(new SetCardInfo("Furious Rise", 144, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class)); + cards.add(new SetCardInfo("Furor of the Bitten", 145, Rarity.COMMON, mage.cards.f.FurorOfTheBitten.class)); + cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 367, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gale Swooper", 20, Rarity.COMMON, mage.cards.g.GaleSwooper.class)); + cards.add(new SetCardInfo("Garruk's Gorehorn", 184, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Garruk's Gorehorn", 306, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Garruk's Harbinger", 185, Rarity.RARE, mage.cards.g.GarruksHarbinger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Garruk's Harbinger", 307, Rarity.RARE, mage.cards.g.GarruksHarbinger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Garruk's Uprising", 186, Rarity.UNCOMMON, mage.cards.g.GarruksUprising.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Garruk's Uprising", 308, Rarity.UNCOMMON, mage.cards.g.GarruksUprising.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class)); + cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class)); + cards.add(new SetCardInfo("Garruk, Unleashed", 183, Rarity.MYTHIC, mage.cards.g.GarrukUnleashed.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Garruk, Unleashed", 284, Rarity.MYTHIC, mage.cards.g.GarrukUnleashed.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Garruk, Unleashed", 305, Rarity.MYTHIC, mage.cards.g.GarrukUnleashed.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ghostly Pilferer", 350, Rarity.RARE, mage.cards.g.GhostlyPilferer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ghostly Pilferer", 52, Rarity.RARE, mage.cards.g.GhostlyPilferer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gloom Sower", 100, Rarity.COMMON, mage.cards.g.GloomSower.class)); + cards.add(new SetCardInfo("Glorious Anthem", 21, Rarity.RARE, mage.cards.g.GloriousAnthem.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glorious Anthem", 341, Rarity.RARE, mage.cards.g.GloriousAnthem.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gnarled Sage", 187, Rarity.COMMON, mage.cards.g.GnarledSage.class)); + cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class)); + cards.add(new SetCardInfo("Goblin Wizardry", 148, Rarity.COMMON, mage.cards.g.GoblinWizardry.class)); + cards.add(new SetCardInfo("Goremand", 101, Rarity.UNCOMMON, mage.cards.g.Goremand.class)); + cards.add(new SetCardInfo("Grasp of Darkness", 102, Rarity.COMMON, mage.cards.g.GraspOfDarkness.class)); + cards.add(new SetCardInfo("Griffin Aerie", 22, Rarity.UNCOMMON, mage.cards.g.GriffinAerie.class)); + cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grim Tutor", 315, Rarity.MYTHIC, mage.cards.g.GrimTutor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Havoc Jester", 149, Rarity.UNCOMMON, mage.cards.h.HavocJester.class)); + cards.add(new SetCardInfo("Heartfire Immolator", 150, Rarity.UNCOMMON, mage.cards.h.HeartfireImmolator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Heartfire Immolator", 396, Rarity.UNCOMMON, mage.cards.h.HeartfireImmolator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hellkite Punisher", 151, Rarity.UNCOMMON, mage.cards.h.HellkitePunisher.class)); + cards.add(new SetCardInfo("Heroic Intervention", 188, Rarity.RARE, mage.cards.h.HeroicIntervention.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Heroic Intervention", 375, Rarity.RARE, mage.cards.h.HeroicIntervention.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class)); + cards.add(new SetCardInfo("Hobblefiend", 152, Rarity.COMMON, mage.cards.h.Hobblefiend.class)); + cards.add(new SetCardInfo("Hooded Blightfang", 104, Rarity.RARE, mage.cards.h.HoodedBlightfang.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hooded Blightfang", 357, Rarity.RARE, mage.cards.h.HoodedBlightfang.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hunter's Edge", 189, Rarity.COMMON, mage.cards.h.HuntersEdge.class)); + cards.add(new SetCardInfo("Idol of Endurance", 23, Rarity.RARE, mage.cards.i.IdolOfEndurance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Idol of Endurance", 342, Rarity.RARE, mage.cards.i.IdolOfEndurance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Igneous Cur", 153, Rarity.COMMON, mage.cards.i.IgneousCur.class)); + cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class)); + cards.add(new SetCardInfo("Infernal Scarring", 105, Rarity.COMMON, mage.cards.i.InfernalScarring.class)); + cards.add(new SetCardInfo("Invigorating Surge", 190, Rarity.UNCOMMON, mage.cards.i.InvigoratingSurge.class)); + cards.add(new SetCardInfo("Island", 263, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 264, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 265, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class)); + cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 376, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jungle Hollow", 247, Rarity.COMMON, mage.cards.j.JungleHollow.class)); + cards.add(new SetCardInfo("Kaervek, the Spiteful", 106, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kaervek, the Spiteful", 358, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Keen Glidemaster", 54, Rarity.COMMON, mage.cards.k.KeenGlidemaster.class)); + cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class)); + cards.add(new SetCardInfo("Kinetic Augur", 154, Rarity.UNCOMMON, mage.cards.k.KineticAugur.class)); + cards.add(new SetCardInfo("Kitesail Freebooter", 107, Rarity.UNCOMMON, mage.cards.k.KitesailFreebooter.class)); + cards.add(new SetCardInfo("Leafkin Avenger", 220, Rarity.UNCOMMON, mage.cards.l.LeafkinAvenger.class)); + cards.add(new SetCardInfo("Legion's Judgment", 24, Rarity.COMMON, mage.cards.l.LegionsJudgment.class)); + cards.add(new SetCardInfo("Library Larcenist", 55, Rarity.COMMON, mage.cards.l.LibraryLarcenist.class)); + cards.add(new SetCardInfo("Life Goes On", 192, Rarity.COMMON, mage.cards.l.LifeGoesOn.class)); + cards.add(new SetCardInfo("Light of Promise", 25, Rarity.UNCOMMON, mage.cards.l.LightOfPromise.class)); + cards.add(new SetCardInfo("Liliana's Devotee", 109, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liliana's Devotee", 298, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class)); + cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class)); + cards.add(new SetCardInfo("Liliana's Standard Bearer", 110, Rarity.RARE, mage.cards.l.LilianasStandardBearer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liliana's Standard Bearer", 299, Rarity.RARE, mage.cards.l.LilianasStandardBearer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liliana's Steward", 111, Rarity.COMMON, mage.cards.l.LilianasSteward.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liliana's Steward", 300, Rarity.COMMON, mage.cards.l.LilianasSteward.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class)); + cards.add(new SetCardInfo("Liliana, Waker of the Dead", 108, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liliana, Waker of the Dead", 282, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liliana, Waker of the Dead", 297, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Llanowar Visionary", 397, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lofty Denial", 56, Rarity.COMMON, mage.cards.l.LoftyDenial.class)); + cards.add(new SetCardInfo("Lorescale Coatl", 221, Rarity.UNCOMMON, mage.cards.l.LorescaleCoatl.class)); + cards.add(new SetCardInfo("Makeshift Battalion", 26, Rarity.COMMON, mage.cards.m.MakeshiftBattalion.class)); + cards.add(new SetCardInfo("Malefic Scythe", 112, Rarity.UNCOMMON, mage.cards.m.MaleficScythe.class)); + cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mangara, the Diplomat", 343, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Masked Blackguard", 113, Rarity.COMMON, mage.cards.m.MaskedBlackguard.class)); + cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Massacre Wurm", 316, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mazemind Tome", 383, Rarity.RARE, mage.cards.m.MazemindTome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Meteorite", 233, Rarity.UNCOMMON, mage.cards.m.Meteorite.class)); + cards.add(new SetCardInfo("Mind Rot", 115, Rarity.COMMON, mage.cards.m.MindRot.class)); + cards.add(new SetCardInfo("Miscast", 57, Rarity.UNCOMMON, mage.cards.m.Miscast.class)); + cards.add(new SetCardInfo("Mistral Singer", 58, Rarity.COMMON, mage.cards.m.MistralSinger.class)); + cards.add(new SetCardInfo("Mountain", 269, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 270, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 271, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 312, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class)); + cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Necromentia", 359, Rarity.RARE, mage.cards.n.Necromentia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Niambi, Esteemed Speaker", 222, Rarity.RARE, mage.cards.n.NiambiEsteemedSpeaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Niambi, Esteemed Speaker", 379, Rarity.RARE, mage.cards.n.NiambiEsteemedSpeaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nine Lives", 28, Rarity.RARE, mage.cards.n.NineLives.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nine Lives", 344, Rarity.RARE, mage.cards.n.NineLives.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Obsessive Stitcher", 223, Rarity.UNCOMMON, mage.cards.o.ObsessiveStitcher.class)); + cards.add(new SetCardInfo("Onakke Ogre", 155, Rarity.COMMON, mage.cards.o.OnakkeOgre.class)); + cards.add(new SetCardInfo("Opt", 59, Rarity.COMMON, mage.cards.o.Opt.class)); + cards.add(new SetCardInfo("Ornery Dilophosaur", 194, Rarity.COMMON, mage.cards.o.OrneryDilophosaur.class)); + cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pack Leader", 345, Rarity.RARE, mage.cards.p.PackLeader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pack Leader", 392, Rarity.RARE, mage.cards.p.PackLeader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Palladium Myr", 234, Rarity.UNCOMMON, mage.cards.p.PalladiumMyr.class)); + cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Peer into the Abyss", 360, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pestilent Haze", 118, Rarity.UNCOMMON, mage.cards.p.PestilentHaze.class)); + cards.add(new SetCardInfo("Pitchburn Devils", 156, Rarity.COMMON, mage.cards.p.PitchburnDevils.class)); + cards.add(new SetCardInfo("Plains", 260, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 261, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 262, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 309, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Portcullis Vine", 195, Rarity.COMMON, mage.cards.p.PortcullisVine.class)); + cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class)); + cards.add(new SetCardInfo("Pridemalkin", 196, Rarity.COMMON, mage.cards.p.Pridemalkin.class)); + cards.add(new SetCardInfo("Primal Might", 197, Rarity.RARE, mage.cards.p.PrimalMight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Primal Might", 377, Rarity.RARE, mage.cards.p.PrimalMight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prismite", 235, Rarity.COMMON, mage.cards.p.Prismite.class)); + cards.add(new SetCardInfo("Pursued Whale", 351, Rarity.RARE, mage.cards.p.PursuedWhale.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pursued Whale", 60, Rarity.RARE, mage.cards.p.PursuedWhale.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class)); + cards.add(new SetCardInfo("Radha, Heart of Keld", 224, Rarity.RARE, mage.cards.r.RadhaHeartOfKeld.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Radha, Heart of Keld", 380, Rarity.RARE, mage.cards.r.RadhaHeartOfKeld.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Radiant Fountain", 248, Rarity.COMMON, mage.cards.r.RadiantFountain.class)); + cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class)); + cards.add(new SetCardInfo("Rambunctious Mutt", 30, Rarity.COMMON, mage.cards.r.RambunctiousMutt.class)); + cards.add(new SetCardInfo("Ranger's Guile", 199, Rarity.COMMON, mage.cards.r.RangersGuile.class)); + cards.add(new SetCardInfo("Read the Tides", 62, Rarity.COMMON, mage.cards.r.ReadTheTides.class)); + cards.add(new SetCardInfo("Return to Nature", 200, Rarity.COMMON, mage.cards.r.ReturnToNature.class)); + cards.add(new SetCardInfo("Revitalize", 31, Rarity.COMMON, mage.cards.r.Revitalize.class)); + cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class)); + cards.add(new SetCardInfo("Riddleform", 64, Rarity.UNCOMMON, mage.cards.r.Riddleform.class)); + cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class)); + cards.add(new SetCardInfo("Rise Again", 119, Rarity.COMMON, mage.cards.r.RiseAgain.class)); + cards.add(new SetCardInfo("Roaming Ghostlight", 65, Rarity.COMMON, mage.cards.r.RoamingGhostlight.class)); + cards.add(new SetCardInfo("Rookie Mistake", 66, Rarity.COMMON, mage.cards.r.RookieMistake.class)); + cards.add(new SetCardInfo("Rousing Read", 67, Rarity.COMMON, mage.cards.r.RousingRead.class)); + cards.add(new SetCardInfo("Rugged Highlands", 249, Rarity.COMMON, mage.cards.r.RuggedHighlands.class)); + cards.add(new SetCardInfo("Run Afoul", 201, Rarity.COMMON, mage.cards.r.RunAfoul.class)); + cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Runed Halo", 346, Rarity.RARE, mage.cards.r.RunedHalo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class)); + cards.add(new SetCardInfo("Sanctum of All", 225, Rarity.RARE, mage.cards.s.SanctumOfAll.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sanctum of All", 381, Rarity.RARE, mage.cards.s.SanctumOfAll.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sanctum of Calm Waters", 68, Rarity.UNCOMMON, mage.cards.s.SanctumOfCalmWaters.class)); + cards.add(new SetCardInfo("Sanctum of Fruitful Harvest", 203, Rarity.UNCOMMON, mage.cards.s.SanctumOfFruitfulHarvest.class)); + cards.add(new SetCardInfo("Sanctum of Shattered Heights", 157, Rarity.UNCOMMON, mage.cards.s.SanctumOfShatteredHeights.class)); + cards.add(new SetCardInfo("Sanctum of Stone Fangs", 120, Rarity.UNCOMMON, mage.cards.s.SanctumOfStoneFangs.class)); + cards.add(new SetCardInfo("Sanctum of Tranquil Light", 33, Rarity.UNCOMMON, mage.cards.s.SanctumOfTranquilLight.class)); + cards.add(new SetCardInfo("Sanguine Indulgence", 121, Rarity.COMMON, mage.cards.s.SanguineIndulgence.class)); + cards.add(new SetCardInfo("Scavenging Ooze", 204, Rarity.RARE, mage.cards.s.ScavengingOoze.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scavenging Ooze", 318, Rarity.RARE, mage.cards.s.ScavengingOoze.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scorching Dragonfire", 158, Rarity.COMMON, mage.cards.s.ScorchingDragonfire.class)); + cards.add(new SetCardInfo("Scoured Barrens", 250, Rarity.COMMON, mage.cards.s.ScouredBarrens.class)); + cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class)); + cards.add(new SetCardInfo("Secure the Scene", 35, Rarity.COMMON, mage.cards.s.SecureTheScene.class)); + cards.add(new SetCardInfo("See the Truth", 352, Rarity.RARE, mage.cards.s.SeeTheTruth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Selfless Savior", 36, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Selfless Savior", 393, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Setessan Training", 205, Rarity.COMMON, mage.cards.s.SetessanTraining.class)); + cards.add(new SetCardInfo("Shacklegeist", 353, Rarity.RARE, mage.cards.s.Shacklegeist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shacklegeist", 70, Rarity.RARE, mage.cards.s.Shacklegeist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class)); + cards.add(new SetCardInfo("Shock", 159, Rarity.COMMON, mage.cards.s.Shock.class)); + cards.add(new SetCardInfo("Short Sword", 236, Rarity.COMMON, mage.cards.s.ShortSword.class)); + cards.add(new SetCardInfo("Siege Striker", 37, Rarity.UNCOMMON, mage.cards.s.SiegeStriker.class)); + cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class)); + cards.add(new SetCardInfo("Silent Dart", 237, Rarity.COMMON, mage.cards.s.SilentDart.class)); + cards.add(new SetCardInfo("Silversmote Ghoul", 122, Rarity.UNCOMMON, mage.cards.s.SilversmoteGhoul.class)); + cards.add(new SetCardInfo("Skeleton Archer", 123, Rarity.COMMON, mage.cards.s.SkeletonArcher.class)); + cards.add(new SetCardInfo("Skyscanner", 238, Rarity.COMMON, mage.cards.s.Skyscanner.class)); + cards.add(new SetCardInfo("Skyway Sniper", 206, Rarity.UNCOMMON, mage.cards.s.SkywaySniper.class)); + cards.add(new SetCardInfo("Snarespinner", 207, Rarity.COMMON, mage.cards.s.Snarespinner.class)); + cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Solemn Simulacrum", 319, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soul Sear", 160, Rarity.UNCOMMON, mage.cards.s.SoulSear.class)); + cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sparkhunter Masticore", 384, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Speaker of the Heavens", 347, Rarity.RARE, mage.cards.s.SpeakerOfTheHeavens.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Speaker of the Heavens", 38, Rarity.RARE, mage.cards.s.SpeakerOfTheHeavens.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spellgorger Weird", 161, Rarity.COMMON, mage.cards.s.SpellgorgerWeird.class)); + cards.add(new SetCardInfo("Spined Megalodon", 72, Rarity.COMMON, mage.cards.s.SpinedMegalodon.class)); + cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class)); + cards.add(new SetCardInfo("Sporeweb Weaver", 208, Rarity.RARE, mage.cards.s.SporewebWeaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sporeweb Weaver", 378, Rarity.RARE, mage.cards.s.SporewebWeaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Staunch Shieldmate", 39, Rarity.COMMON, mage.cards.s.StaunchShieldmate.class)); + cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class)); + cards.add(new SetCardInfo("Stormwing Entity", 354, Rarity.RARE, mage.cards.s.StormwingEntity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stormwing Entity", 73, Rarity.RARE, mage.cards.s.StormwingEntity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Subira, Tulzidi Caravanner", 162, Rarity.RARE, mage.cards.s.SubiraTulzidiCaravanner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Subira, Tulzidi Caravanner", 368, Rarity.RARE, mage.cards.s.SubiraTulzidiCaravanner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sublime Epiphany", 355, Rarity.RARE, mage.cards.s.SublimeEpiphany.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sublime Epiphany", 74, Rarity.RARE, mage.cards.s.SublimeEpiphany.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sure Strike", 163, Rarity.COMMON, mage.cards.s.SureStrike.class)); + cards.add(new SetCardInfo("Swamp", 266, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 267, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 268, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 311, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swift Response", 40, Rarity.COMMON, mage.cards.s.SwiftResponse.class)); + cards.add(new SetCardInfo("Swiftwater Cliffs", 251, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class)); + cards.add(new SetCardInfo("Tavern Swindler", 124, Rarity.UNCOMMON, mage.cards.t.TavernSwindler.class)); + cards.add(new SetCardInfo("Teferi's Ageless Insight", 294, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi's Protege", 295, Rarity.COMMON, mage.cards.t.TeferisProtege.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi's Tutelage", 296, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi's Wavecaster", 327, Rarity.RARE, mage.cards.t.TeferisWavecaster.class)); + cards.add(new SetCardInfo("Teferi, Master of Time", 275, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Master of Time", 276, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Master of Time", 277, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Master of Time", 281, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Master of Time", 290, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Master of Time", 291, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Master of Time", 292, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Master of Time", 293, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Timeless Voyager", 324, Rarity.MYTHIC, mage.cards.t.TeferiTimelessVoyager.class)); + cards.add(new SetCardInfo("Tempered Veteran", 41, Rarity.UNCOMMON, mage.cards.t.TemperedVeteran.class)); + cards.add(new SetCardInfo("Temple of Epiphany", 252, Rarity.RARE, mage.cards.t.TempleOfEpiphany.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temple of Epiphany", 387, Rarity.RARE, mage.cards.t.TempleOfEpiphany.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temple of Malady", 253, Rarity.RARE, mage.cards.t.TempleOfMalady.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temple of Malady", 388, Rarity.RARE, mage.cards.t.TempleOfMalady.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temple of Mystery", 254, Rarity.RARE, mage.cards.t.TempleOfMystery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temple of Mystery", 389, Rarity.RARE, mage.cards.t.TempleOfMystery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temple of Silence", 255, Rarity.RARE, mage.cards.t.TempleOfSilence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temple of Silence", 390, Rarity.RARE, mage.cards.t.TempleOfSilence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temple of Triumph", 256, Rarity.RARE, mage.cards.t.TempleOfTriumph.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temple of Triumph", 391, Rarity.RARE, mage.cards.t.TempleOfTriumph.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Terror of the Peaks", 164, Rarity.MYTHIC, mage.cards.t.TerrorOfThePeaks.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Terror of the Peaks", 369, Rarity.MYTHIC, mage.cards.t.TerrorOfThePeaks.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thieves' Guild Enforcer", 125, Rarity.RARE, mage.cards.t.ThievesGuildEnforcer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thieves' Guild Enforcer", 361, Rarity.RARE, mage.cards.t.ThievesGuildEnforcer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thornwood Falls", 257, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class)); + cards.add(new SetCardInfo("Thrashing Brontodon", 209, Rarity.UNCOMMON, mage.cards.t.ThrashingBrontodon.class)); + cards.add(new SetCardInfo("Thrill of Possibility", 165, Rarity.COMMON, mage.cards.t.ThrillOfPossibility.class)); + cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class)); + cards.add(new SetCardInfo("Titanic Growth", 210, Rarity.COMMON, mage.cards.t.TitanicGrowth.class)); + cards.add(new SetCardInfo("Tolarian Kraken", 80, Rarity.UNCOMMON, mage.cards.t.TolarianKraken.class)); + cards.add(new SetCardInfo("Tome Anima", 81, Rarity.COMMON, mage.cards.t.TomeAnima.class)); + cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class)); + cards.add(new SetCardInfo("Track Down", 211, Rarity.COMMON, mage.cards.t.TrackDown.class)); + cards.add(new SetCardInfo("Traitorous Greed", 166, Rarity.UNCOMMON, mage.cards.t.TraitorousGreed.class)); + cards.add(new SetCardInfo("Tranquil Cove", 258, Rarity.COMMON, mage.cards.t.TranquilCove.class)); + cards.add(new SetCardInfo("Transmogrify", 167, Rarity.RARE, mage.cards.t.Transmogrify.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Transmogrify", 370, Rarity.RARE, mage.cards.t.Transmogrify.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class)); + cards.add(new SetCardInfo("Turn to Slag", 168, Rarity.COMMON, mage.cards.t.TurnToSlag.class)); + cards.add(new SetCardInfo("Turret Ogre", 169, Rarity.COMMON, mage.cards.t.TurretOgre.class)); + cards.add(new SetCardInfo("Twinblade Assassins", 226, Rarity.UNCOMMON, mage.cards.t.TwinbladeAssassins.class)); + cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 279, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 285, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Unleash Fury", 170, Rarity.UNCOMMON, mage.cards.u.UnleashFury.class)); + cards.add(new SetCardInfo("Unsubstantiate", 82, Rarity.UNCOMMON, mage.cards.u.Unsubstantiate.class)); + cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class)); + cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class)); + cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 362, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vodalian Arcanist", 83, Rarity.COMMON, mage.cards.v.VodalianArcanist.class)); + cards.add(new SetCardInfo("Volcanic Geyser", 171, Rarity.UNCOMMON, mage.cards.v.VolcanicGeyser.class)); + cards.add(new SetCardInfo("Volcanic Salvo", 172, Rarity.RARE, mage.cards.v.VolcanicSalvo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Volcanic Salvo", 371, Rarity.RARE, mage.cards.v.VolcanicSalvo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vryn Wingmare", 43, Rarity.UNCOMMON, mage.cards.v.VrynWingmare.class)); + cards.add(new SetCardInfo("Waker of Waves", 84, Rarity.UNCOMMON, mage.cards.w.WakerOfWaves.class)); + cards.add(new SetCardInfo("Walking Corpse", 128, Rarity.COMMON, mage.cards.w.WalkingCorpse.class)); + cards.add(new SetCardInfo("Wall of Runes", 85, Rarity.COMMON, mage.cards.w.WallOfRunes.class)); + cards.add(new SetCardInfo("Warded Battlements", 44, Rarity.COMMON, mage.cards.w.WardedBattlements.class)); + cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class)); + cards.add(new SetCardInfo("Watcher of the Spheres", 227, Rarity.UNCOMMON, mage.cards.w.WatcherOfTheSpheres.class)); + cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class)); + cards.add(new SetCardInfo("Wildwood Scourge", 214, Rarity.UNCOMMON, mage.cards.w.WildwoodScourge.class)); + cards.add(new SetCardInfo("Wind-Scarred Crag", 259, Rarity.COMMON, mage.cards.w.WindScarredCrag.class)); + cards.add(new SetCardInfo("Wishcoin Crab", 86, Rarity.COMMON, mage.cards.w.WishcoinCrab.class)); + cards.add(new SetCardInfo("Witch's Cauldron", 129, Rarity.UNCOMMON, mage.cards.w.WitchsCauldron.class)); + } + + + @Override + public List getCardsByRarity(Rarity rarity) { + if (rarity != Rarity.COMMON) { + return super.getCardsByRarity(rarity); + } + List savedCardsInfos = savedCards.get(rarity); + if (savedCardsInfos != null) { + return new ArrayList(savedCardsInfos); + } + CardCriteria criteria = new CardCriteria(); + criteria.setCodes(this.code).notTypes(CardType.LAND); + criteria.rarities(rarity).doubleFaced(false); + savedCardsInfos = CardRepository.instance.findCards(criteria); + if (maxCardNumberInBooster != Integer.MAX_VALUE) { + savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); + } + criteria = new CardCriteria(); + criteria.setCodes(this.code).nameExact("Radiant Fountain"); + savedCardsInfos.addAll(CardRepository.instance.findCards(criteria)); + savedCards.put(rarity, savedCardsInfos); + // Return a copy of the saved cards information, as not to modify the original. + return new ArrayList(savedCardsInfos); + } + + @Override + // the common taplands replacing the basic land + public List getSpecialLand() { + if (savedSpecialLand.isEmpty()) { + CardCriteria criteria = new CardCriteria(); + criteria.setCodes(this.code); + criteria.rarities(Rarity.COMMON); + criteria.types(CardType.LAND); + savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); + savedSpecialLand.removeIf(cardInfo -> "Radiant Fountain".equals(cardInfo.getName())); + } + + return new ArrayList<>(savedSpecialLand); + } +} diff --git a/Mage.Sets/src/mage/sets/Exodus.java b/Mage.Sets/src/mage/sets/Exodus.java index a7d5dcb6b3..072d1e0c9b 100644 --- a/Mage.Sets/src/mage/sets/Exodus.java +++ b/Mage.Sets/src/mage/sets/Exodus.java @@ -1,175 +1,175 @@ - -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author North - */ -public final class Exodus extends ExpansionSet { - - private static final Exodus instance = new Exodus(); - - public static Exodus getInstance() { - return instance; - } - - private Exodus() { - super("Exodus", "EXO", ExpansionSet.buildDate(1998, 6, 15), SetType.EXPANSION); - this.blockName = "Tempest"; - this.parentSet = Tempest.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Aether Tide", 27, Rarity.COMMON, mage.cards.a.AetherTide.class)); - cards.add(new SetCardInfo("Allay", 1, Rarity.COMMON, mage.cards.a.Allay.class)); - cards.add(new SetCardInfo("Anarchist", 79, Rarity.COMMON, mage.cards.a.Anarchist.class)); - cards.add(new SetCardInfo("Angelic Blessing", 2, Rarity.COMMON, mage.cards.a.AngelicBlessing.class)); - cards.add(new SetCardInfo("Avenging Druid", 105, Rarity.COMMON, mage.cards.a.AvengingDruid.class)); - cards.add(new SetCardInfo("Bequeathal", 106, Rarity.COMMON, mage.cards.b.Bequeathal.class)); - cards.add(new SetCardInfo("Carnophage", 53, Rarity.COMMON, mage.cards.c.Carnophage.class)); - cards.add(new SetCardInfo("Cartographer", 107, Rarity.UNCOMMON, mage.cards.c.Cartographer.class)); - cards.add(new SetCardInfo("Cataclysm", 3, Rarity.RARE, mage.cards.c.Cataclysm.class)); - cards.add(new SetCardInfo("Cat Burglar", 54, Rarity.COMMON, mage.cards.c.CatBurglar.class)); - cards.add(new SetCardInfo("Charging Paladin", 4, Rarity.COMMON, mage.cards.c.ChargingPaladin.class)); - cards.add(new SetCardInfo("Cinder Crawler", 80, Rarity.COMMON, mage.cards.c.CinderCrawler.class)); - cards.add(new SetCardInfo("City of Traitors", 143, Rarity.RARE, mage.cards.c.CityOfTraitors.class)); - cards.add(new SetCardInfo("Coat of Arms", 131, Rarity.RARE, mage.cards.c.CoatOfArms.class)); - cards.add(new SetCardInfo("Convalescence", 5, Rarity.RARE, mage.cards.c.Convalescence.class)); - cards.add(new SetCardInfo("Crashing Boars", 108, Rarity.UNCOMMON, mage.cards.c.CrashingBoars.class)); - cards.add(new SetCardInfo("Culling the Weak", 55, Rarity.COMMON, mage.cards.c.CullingTheWeak.class)); - cards.add(new SetCardInfo("Cunning", 28, Rarity.COMMON, mage.cards.c.Cunning.class)); - cards.add(new SetCardInfo("Curiosity", 29, Rarity.UNCOMMON, mage.cards.c.Curiosity.class)); - cards.add(new SetCardInfo("Cursed Flesh", 56, Rarity.COMMON, mage.cards.c.CursedFlesh.class)); - cards.add(new SetCardInfo("Dauthi Cutthroat", 57, Rarity.UNCOMMON, mage.cards.d.DauthiCutthroat.class)); - cards.add(new SetCardInfo("Dauthi Jackal", 58, Rarity.COMMON, mage.cards.d.DauthiJackal.class)); - cards.add(new SetCardInfo("Dauthi Warlord", 59, Rarity.UNCOMMON, mage.cards.d.DauthiWarlord.class)); - cards.add(new SetCardInfo("Death's Duet", 60, Rarity.COMMON, mage.cards.d.DeathsDuet.class)); - cards.add(new SetCardInfo("Dizzying Gaze", 81, Rarity.COMMON, mage.cards.d.DizzyingGaze.class)); - cards.add(new SetCardInfo("Dominating Licid", 30, Rarity.RARE, mage.cards.d.DominatingLicid.class)); - cards.add(new SetCardInfo("Elven Palisade", 109, Rarity.UNCOMMON, mage.cards.e.ElvenPalisade.class)); - cards.add(new SetCardInfo("Elvish Berserker", 110, Rarity.COMMON, mage.cards.e.ElvishBerserker.class)); - cards.add(new SetCardInfo("Entropic Specter", 61, Rarity.RARE, mage.cards.e.EntropicSpecter.class)); - cards.add(new SetCardInfo("Ephemeron", 31, Rarity.RARE, mage.cards.e.Ephemeron.class)); - cards.add(new SetCardInfo("Equilibrium", 32, Rarity.RARE, mage.cards.e.Equilibrium.class)); - cards.add(new SetCardInfo("Erratic Portal", 132, Rarity.RARE, mage.cards.e.ErraticPortal.class)); - cards.add(new SetCardInfo("Ertai, Wizard Adept", 33, Rarity.RARE, mage.cards.e.ErtaiWizardAdept.class)); - cards.add(new SetCardInfo("Exalted Dragon", 6, Rarity.RARE, mage.cards.e.ExaltedDragon.class)); - cards.add(new SetCardInfo("Fade Away", 34, Rarity.COMMON, mage.cards.f.FadeAway.class)); - cards.add(new SetCardInfo("Fighting Chance", 82, Rarity.RARE, mage.cards.f.FightingChance.class)); - cards.add(new SetCardInfo("Flowstone Flood", 83, Rarity.UNCOMMON, mage.cards.f.FlowstoneFlood.class)); - cards.add(new SetCardInfo("Forbid", 35, Rarity.UNCOMMON, mage.cards.f.Forbid.class)); - cards.add(new SetCardInfo("Fugue", 62, Rarity.UNCOMMON, mage.cards.f.Fugue.class)); - cards.add(new SetCardInfo("Furnace Brood", 84, Rarity.COMMON, mage.cards.f.FurnaceBrood.class)); - cards.add(new SetCardInfo("Grollub", 63, Rarity.COMMON, mage.cards.g.Grollub.class)); - cards.add(new SetCardInfo("Hatred", 64, Rarity.RARE, mage.cards.h.Hatred.class)); - cards.add(new SetCardInfo("High Ground", 7, Rarity.UNCOMMON, mage.cards.h.HighGround.class)); - cards.add(new SetCardInfo("Jackalope Herd", 111, Rarity.COMMON, mage.cards.j.JackalopeHerd.class)); - cards.add(new SetCardInfo("Keeper of the Beasts", 112, Rarity.UNCOMMON, mage.cards.k.KeeperOfTheBeasts.class)); - cards.add(new SetCardInfo("Keeper of the Dead", 65, Rarity.UNCOMMON, mage.cards.k.KeeperOfTheDead.class)); - cards.add(new SetCardInfo("Keeper of the Flame", 85, Rarity.UNCOMMON, mage.cards.k.KeeperOfTheFlame.class)); - cards.add(new SetCardInfo("Keeper of the Light", 8, Rarity.UNCOMMON, mage.cards.k.KeeperOfTheLight.class)); - cards.add(new SetCardInfo("Keeper of the Mind", 36, Rarity.UNCOMMON, mage.cards.k.KeeperOfTheMind.class)); - cards.add(new SetCardInfo("Killer Whale", 37, Rarity.UNCOMMON, mage.cards.k.KillerWhale.class)); - cards.add(new SetCardInfo("Kor Chant", 9, Rarity.COMMON, mage.cards.k.KorChant.class)); - cards.add(new SetCardInfo("Limited Resources", 10, Rarity.RARE, mage.cards.l.LimitedResources.class)); - cards.add(new SetCardInfo("Mage il-Vec", 86, Rarity.COMMON, mage.cards.m.MageIlVec.class)); - cards.add(new SetCardInfo("Manabond", 113, Rarity.RARE, mage.cards.m.Manabond.class)); - cards.add(new SetCardInfo("Mana Breach", 38, Rarity.UNCOMMON, mage.cards.m.ManaBreach.class)); - cards.add(new SetCardInfo("Maniacal Rage", 87, Rarity.COMMON, mage.cards.m.ManiacalRage.class)); - cards.add(new SetCardInfo("Medicine Bag", 133, Rarity.UNCOMMON, mage.cards.m.MedicineBag.class)); - cards.add(new SetCardInfo("Memory Crystal", 134, Rarity.RARE, mage.cards.m.MemoryCrystal.class)); - cards.add(new SetCardInfo("Merfolk Looter", 39, Rarity.COMMON, mage.cards.m.MerfolkLooter.class)); - cards.add(new SetCardInfo("Mind Maggots", 66, Rarity.UNCOMMON, mage.cards.m.MindMaggots.class)); - cards.add(new SetCardInfo("Mindless Automaton", 135, Rarity.RARE, mage.cards.m.MindlessAutomaton.class)); - cards.add(new SetCardInfo("Mind Over Matter", 40, Rarity.RARE, mage.cards.m.MindOverMatter.class)); - cards.add(new SetCardInfo("Mirozel", 41, Rarity.UNCOMMON, mage.cards.m.Mirozel.class)); - cards.add(new SetCardInfo("Mirri, Cat Warrior", 114, Rarity.RARE, mage.cards.m.MirriCatWarrior.class)); - cards.add(new SetCardInfo("Mogg Assassin", 88, Rarity.UNCOMMON, mage.cards.m.MoggAssassin.class)); - cards.add(new SetCardInfo("Monstrous Hound", 89, Rarity.RARE, mage.cards.m.MonstrousHound.class)); - cards.add(new SetCardInfo("Nausea", 67, Rarity.COMMON, mage.cards.n.Nausea.class)); - cards.add(new SetCardInfo("Necrologia", 68, Rarity.UNCOMMON, mage.cards.n.Necrologia.class)); - cards.add(new SetCardInfo("Null Brooch", 136, Rarity.RARE, mage.cards.n.NullBrooch.class)); - cards.add(new SetCardInfo("Oath of Druids", 115, Rarity.RARE, mage.cards.o.OathOfDruids.class)); - cards.add(new SetCardInfo("Oath of Ghouls", 69, Rarity.RARE, mage.cards.o.OathOfGhouls.class)); - cards.add(new SetCardInfo("Oath of Lieges", 11, Rarity.RARE, mage.cards.o.OathOfLieges.class)); - cards.add(new SetCardInfo("Oath of Mages", 90, Rarity.RARE, mage.cards.o.OathOfMages.class)); - cards.add(new SetCardInfo("Oath of Scholars", 42, Rarity.RARE, mage.cards.o.OathOfScholars.class)); - cards.add(new SetCardInfo("Ogre Shaman", 91, Rarity.RARE, mage.cards.o.OgreShaman.class)); - cards.add(new SetCardInfo("Onslaught", 92, Rarity.COMMON, mage.cards.o.Onslaught.class)); - cards.add(new SetCardInfo("Paladin en-Vec", 12, Rarity.RARE, mage.cards.p.PaladinEnVec.class)); - cards.add(new SetCardInfo("Pandemonium", 93, Rarity.RARE, mage.cards.p.Pandemonium.class)); - cards.add(new SetCardInfo("Paroxysm", 94, Rarity.UNCOMMON, mage.cards.p.Paroxysm.class)); - cards.add(new SetCardInfo("Peace of Mind", 13, Rarity.UNCOMMON, mage.cards.p.PeaceOfMind.class)); - cards.add(new SetCardInfo("Pegasus Stampede", 14, Rarity.UNCOMMON, mage.cards.p.PegasusStampede.class)); - cards.add(new SetCardInfo("Penance", 15, Rarity.UNCOMMON, mage.cards.p.Penance.class)); - cards.add(new SetCardInfo("Pit Spawn", 70, Rarity.RARE, mage.cards.p.PitSpawn.class)); - cards.add(new SetCardInfo("Plaguebearer", 71, Rarity.RARE, mage.cards.p.Plaguebearer.class)); - cards.add(new SetCardInfo("Plated Rootwalla", 116, Rarity.COMMON, mage.cards.p.PlatedRootwalla.class)); - cards.add(new SetCardInfo("Predatory Hunger", 117, Rarity.COMMON, mage.cards.p.PredatoryHunger.class)); - cards.add(new SetCardInfo("Price of Progress", 95, Rarity.UNCOMMON, mage.cards.p.PriceOfProgress.class)); - cards.add(new SetCardInfo("Pygmy Troll", 118, Rarity.COMMON, mage.cards.p.PygmyTroll.class)); - cards.add(new SetCardInfo("Rabid Wolverines", 119, Rarity.COMMON, mage.cards.r.RabidWolverines.class)); - cards.add(new SetCardInfo("Raging Goblin", 96, Rarity.COMMON, mage.cards.r.RagingGoblin.class)); - cards.add(new SetCardInfo("Ravenous Baboons", 97, Rarity.RARE, mage.cards.r.RavenousBaboons.class)); - cards.add(new SetCardInfo("Reaping the Rewards", 16, Rarity.COMMON, mage.cards.r.ReapingTheRewards.class)); - cards.add(new SetCardInfo("Reckless Ogre", 98, Rarity.COMMON, mage.cards.r.RecklessOgre.class)); - cards.add(new SetCardInfo("Reclaim", 120, Rarity.COMMON, mage.cards.r.Reclaim.class)); - cards.add(new SetCardInfo("Reconnaissance", 17, Rarity.UNCOMMON, mage.cards.r.Reconnaissance.class)); - cards.add(new SetCardInfo("Recurring Nightmare", 72, Rarity.RARE, mage.cards.r.RecurringNightmare.class)); - cards.add(new SetCardInfo("Resuscitate", 121, Rarity.UNCOMMON, mage.cards.r.Resuscitate.class)); - cards.add(new SetCardInfo("Robe of Mirrors", 43, Rarity.COMMON, mage.cards.r.RobeOfMirrors.class)); - cards.add(new SetCardInfo("Rootwater Alligator", 122, Rarity.COMMON, mage.cards.r.RootwaterAlligator.class)); - cards.add(new SetCardInfo("Rootwater Mystic", 44, Rarity.COMMON, mage.cards.r.RootwaterMystic.class)); - cards.add(new SetCardInfo("Sabertooth Wyvern", 99, Rarity.UNCOMMON, mage.cards.s.SabertoothWyvern.class)); - cards.add(new SetCardInfo("Scalding Salamander", 100, Rarity.UNCOMMON, mage.cards.s.ScaldingSalamander.class)); - cards.add(new SetCardInfo("Scare Tactics", 73, Rarity.COMMON, mage.cards.s.ScareTactics.class)); - cards.add(new SetCardInfo("School of Piranha", 45, Rarity.COMMON, mage.cards.s.SchoolOfPiranha.class)); - cards.add(new SetCardInfo("Scrivener", 46, Rarity.UNCOMMON, mage.cards.s.Scrivener.class)); - cards.add(new SetCardInfo("Seismic Assault", 101, Rarity.RARE, mage.cards.s.SeismicAssault.class)); - cards.add(new SetCardInfo("Shackles", 18, Rarity.COMMON, mage.cards.s.Shackles.class)); - cards.add(new SetCardInfo("Shattering Pulse", 102, Rarity.COMMON, mage.cards.s.ShatteringPulse.class)); - cards.add(new SetCardInfo("Shield Mate", 19, Rarity.COMMON, mage.cards.s.ShieldMate.class)); - cards.add(new SetCardInfo("Skyshaper", 137, Rarity.UNCOMMON, mage.cards.s.Skyshaper.class)); - cards.add(new SetCardInfo("Skyshroud Elite", 123, Rarity.UNCOMMON, mage.cards.s.SkyshroudElite.class)); - cards.add(new SetCardInfo("Skyshroud War Beast", 124, Rarity.RARE, mage.cards.s.SkyshroudWarBeast.class)); - cards.add(new SetCardInfo("Slaughter", 74, Rarity.UNCOMMON, mage.cards.s.Slaughter.class)); - cards.add(new SetCardInfo("Soltari Visionary", 20, Rarity.COMMON, mage.cards.s.SoltariVisionary.class)); - cards.add(new SetCardInfo("Song of Serenity", 125, Rarity.UNCOMMON, mage.cards.s.SongOfSerenity.class)); - cards.add(new SetCardInfo("Sonic Burst", 103, Rarity.COMMON, mage.cards.s.SonicBurst.class)); - cards.add(new SetCardInfo("Soul Warden", 21, Rarity.COMMON, mage.cards.s.SoulWarden.class)); - cards.add(new SetCardInfo("Spellbook", 138, Rarity.UNCOMMON, mage.cards.s.Spellbook.class)); - cards.add(new SetCardInfo("Spellshock", 104, Rarity.UNCOMMON, mage.cards.s.Spellshock.class)); - cards.add(new SetCardInfo("Sphere of Resistance", 139, Rarity.RARE, mage.cards.s.SphereOfResistance.class)); - cards.add(new SetCardInfo("Spike Cannibal", 75, Rarity.UNCOMMON, mage.cards.s.SpikeCannibal.class)); - cards.add(new SetCardInfo("Spike Hatcher", 126, Rarity.RARE, mage.cards.s.SpikeHatcher.class)); - cards.add(new SetCardInfo("Spike Rogue", 127, Rarity.UNCOMMON, mage.cards.s.SpikeRogue.class)); - cards.add(new SetCardInfo("Spike Weaver", 128, Rarity.RARE, mage.cards.s.SpikeWeaver.class)); - cards.add(new SetCardInfo("Standing Troops", 22, Rarity.COMMON, mage.cards.s.StandingTroops.class)); - cards.add(new SetCardInfo("Survival of the Fittest", 129, Rarity.RARE, mage.cards.s.SurvivalOfTheFittest.class)); - cards.add(new SetCardInfo("Thalakos Drifters", 47, Rarity.RARE, mage.cards.t.ThalakosDrifters.class)); - cards.add(new SetCardInfo("Thalakos Scout", 48, Rarity.COMMON, mage.cards.t.ThalakosScout.class)); - cards.add(new SetCardInfo("Theft of Dreams", 49, Rarity.COMMON, mage.cards.t.TheftOfDreams.class)); - cards.add(new SetCardInfo("Thopter Squadron", 140, Rarity.RARE, mage.cards.t.ThopterSquadron.class)); - cards.add(new SetCardInfo("Thrull Surgeon", 76, Rarity.COMMON, mage.cards.t.ThrullSurgeon.class)); - cards.add(new SetCardInfo("Transmogrifying Licid", 141, Rarity.UNCOMMON, mage.cards.t.TransmogrifyingLicid.class)); - cards.add(new SetCardInfo("Treasure Hunter", 23, Rarity.UNCOMMON, mage.cards.t.TreasureHunter.class)); - cards.add(new SetCardInfo("Treasure Trove", 50, Rarity.UNCOMMON, mage.cards.t.TreasureTrove.class)); - cards.add(new SetCardInfo("Vampire Hounds", 77, Rarity.COMMON, mage.cards.v.VampireHounds.class)); - cards.add(new SetCardInfo("Volrath's Dungeon", 78, Rarity.RARE, mage.cards.v.VolrathsDungeon.class)); - cards.add(new SetCardInfo("Wall of Nets", 24, Rarity.RARE, mage.cards.w.WallOfNets.class)); - cards.add(new SetCardInfo("Wayward Soul", 51, Rarity.COMMON, mage.cards.w.WaywardSoul.class)); - cards.add(new SetCardInfo("Welkin Hawk", 25, Rarity.COMMON, mage.cards.w.WelkinHawk.class)); - cards.add(new SetCardInfo("Whiptongue Frog", 52, Rarity.COMMON, mage.cards.w.WhiptongueFrog.class)); - cards.add(new SetCardInfo("Wood Elves", 130, Rarity.COMMON, mage.cards.w.WoodElves.class)); - cards.add(new SetCardInfo("Workhorse", 142, Rarity.RARE, mage.cards.w.Workhorse.class)); - cards.add(new SetCardInfo("Zealots en-Dal", 26, Rarity.UNCOMMON, mage.cards.z.ZealotsEnDal.class)); - } -} + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author North + */ +public final class Exodus extends ExpansionSet { + + private static final Exodus instance = new Exodus(); + + public static Exodus getInstance() { + return instance; + } + + private Exodus() { + super("Exodus", "EXO", ExpansionSet.buildDate(1998, 6, 15), SetType.EXPANSION); + this.blockName = "Tempest"; + this.parentSet = Tempest.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Aether Tide", 27, Rarity.COMMON, mage.cards.a.AetherTide.class)); + cards.add(new SetCardInfo("Allay", 1, Rarity.COMMON, mage.cards.a.Allay.class)); + cards.add(new SetCardInfo("Anarchist", 79, Rarity.COMMON, mage.cards.a.Anarchist.class)); + cards.add(new SetCardInfo("Angelic Blessing", 2, Rarity.COMMON, mage.cards.a.AngelicBlessing.class)); + cards.add(new SetCardInfo("Avenging Druid", 105, Rarity.COMMON, mage.cards.a.AvengingDruid.class)); + cards.add(new SetCardInfo("Bequeathal", 106, Rarity.COMMON, mage.cards.b.Bequeathal.class)); + cards.add(new SetCardInfo("Carnophage", 53, Rarity.COMMON, mage.cards.c.Carnophage.class)); + cards.add(new SetCardInfo("Cartographer", 107, Rarity.UNCOMMON, mage.cards.c.Cartographer.class)); + cards.add(new SetCardInfo("Cataclysm", 3, Rarity.RARE, mage.cards.c.Cataclysm.class)); + cards.add(new SetCardInfo("Cat Burglar", 54, Rarity.COMMON, mage.cards.c.CatBurglar.class)); + cards.add(new SetCardInfo("Charging Paladin", 4, Rarity.COMMON, mage.cards.c.ChargingPaladin.class)); + cards.add(new SetCardInfo("Cinder Crawler", 80, Rarity.COMMON, mage.cards.c.CinderCrawler.class)); + cards.add(new SetCardInfo("City of Traitors", 143, Rarity.RARE, mage.cards.c.CityOfTraitors.class)); + cards.add(new SetCardInfo("Coat of Arms", 131, Rarity.RARE, mage.cards.c.CoatOfArms.class)); + cards.add(new SetCardInfo("Convalescence", 5, Rarity.RARE, mage.cards.c.Convalescence.class)); + cards.add(new SetCardInfo("Crashing Boars", 108, Rarity.UNCOMMON, mage.cards.c.CrashingBoars.class)); + cards.add(new SetCardInfo("Culling the Weak", 55, Rarity.COMMON, mage.cards.c.CullingTheWeak.class)); + cards.add(new SetCardInfo("Cunning", 28, Rarity.COMMON, mage.cards.c.Cunning.class)); + cards.add(new SetCardInfo("Curiosity", 29, Rarity.UNCOMMON, mage.cards.c.Curiosity.class)); + cards.add(new SetCardInfo("Cursed Flesh", 56, Rarity.COMMON, mage.cards.c.CursedFlesh.class)); + cards.add(new SetCardInfo("Dauthi Cutthroat", 57, Rarity.UNCOMMON, mage.cards.d.DauthiCutthroat.class)); + cards.add(new SetCardInfo("Dauthi Jackal", 58, Rarity.COMMON, mage.cards.d.DauthiJackal.class)); + cards.add(new SetCardInfo("Dauthi Warlord", 59, Rarity.UNCOMMON, mage.cards.d.DauthiWarlord.class)); + cards.add(new SetCardInfo("Death's Duet", 60, Rarity.COMMON, mage.cards.d.DeathsDuet.class)); + cards.add(new SetCardInfo("Dizzying Gaze", 81, Rarity.COMMON, mage.cards.d.DizzyingGaze.class)); + cards.add(new SetCardInfo("Dominating Licid", 30, Rarity.RARE, mage.cards.d.DominatingLicid.class)); + cards.add(new SetCardInfo("Elven Palisade", 109, Rarity.UNCOMMON, mage.cards.e.ElvenPalisade.class)); + cards.add(new SetCardInfo("Elvish Berserker", 110, Rarity.COMMON, mage.cards.e.ElvishBerserker.class)); + cards.add(new SetCardInfo("Entropic Specter", 61, Rarity.RARE, mage.cards.e.EntropicSpecter.class)); + cards.add(new SetCardInfo("Ephemeron", 31, Rarity.RARE, mage.cards.e.Ephemeron.class)); + cards.add(new SetCardInfo("Equilibrium", 32, Rarity.RARE, mage.cards.e.Equilibrium.class)); + cards.add(new SetCardInfo("Erratic Portal", 132, Rarity.RARE, mage.cards.e.ErraticPortal.class)); + cards.add(new SetCardInfo("Ertai, Wizard Adept", 33, Rarity.RARE, mage.cards.e.ErtaiWizardAdept.class)); + cards.add(new SetCardInfo("Exalted Dragon", 6, Rarity.RARE, mage.cards.e.ExaltedDragon.class)); + cards.add(new SetCardInfo("Fade Away", 34, Rarity.COMMON, mage.cards.f.FadeAway.class)); + cards.add(new SetCardInfo("Fighting Chance", 82, Rarity.RARE, mage.cards.f.FightingChance.class)); + cards.add(new SetCardInfo("Flowstone Flood", 83, Rarity.UNCOMMON, mage.cards.f.FlowstoneFlood.class)); + cards.add(new SetCardInfo("Forbid", 35, Rarity.UNCOMMON, mage.cards.f.Forbid.class)); + cards.add(new SetCardInfo("Fugue", 62, Rarity.UNCOMMON, mage.cards.f.Fugue.class)); + cards.add(new SetCardInfo("Furnace Brood", 84, Rarity.COMMON, mage.cards.f.FurnaceBrood.class)); + cards.add(new SetCardInfo("Grollub", 63, Rarity.COMMON, mage.cards.g.Grollub.class)); + cards.add(new SetCardInfo("Hatred", 64, Rarity.RARE, mage.cards.h.Hatred.class)); + cards.add(new SetCardInfo("High Ground", 7, Rarity.UNCOMMON, mage.cards.h.HighGround.class)); + cards.add(new SetCardInfo("Jackalope Herd", 111, Rarity.COMMON, mage.cards.j.JackalopeHerd.class)); + cards.add(new SetCardInfo("Keeper of the Beasts", 112, Rarity.UNCOMMON, mage.cards.k.KeeperOfTheBeasts.class)); + cards.add(new SetCardInfo("Keeper of the Dead", 65, Rarity.UNCOMMON, mage.cards.k.KeeperOfTheDead.class)); + cards.add(new SetCardInfo("Keeper of the Flame", 85, Rarity.UNCOMMON, mage.cards.k.KeeperOfTheFlame.class)); + cards.add(new SetCardInfo("Keeper of the Light", 8, Rarity.UNCOMMON, mage.cards.k.KeeperOfTheLight.class)); + cards.add(new SetCardInfo("Keeper of the Mind", 36, Rarity.UNCOMMON, mage.cards.k.KeeperOfTheMind.class)); + cards.add(new SetCardInfo("Killer Whale", 37, Rarity.UNCOMMON, mage.cards.k.KillerWhale.class)); + cards.add(new SetCardInfo("Kor Chant", 9, Rarity.COMMON, mage.cards.k.KorChant.class)); + cards.add(new SetCardInfo("Limited Resources", 10, Rarity.RARE, mage.cards.l.LimitedResources.class)); + cards.add(new SetCardInfo("Mage il-Vec", 86, Rarity.COMMON, mage.cards.m.MageIlVec.class)); + cards.add(new SetCardInfo("Manabond", 113, Rarity.RARE, mage.cards.m.Manabond.class)); + cards.add(new SetCardInfo("Mana Breach", 38, Rarity.UNCOMMON, mage.cards.m.ManaBreach.class)); + cards.add(new SetCardInfo("Maniacal Rage", 87, Rarity.COMMON, mage.cards.m.ManiacalRage.class)); + cards.add(new SetCardInfo("Medicine Bag", 133, Rarity.UNCOMMON, mage.cards.m.MedicineBag.class)); + cards.add(new SetCardInfo("Memory Crystal", 134, Rarity.RARE, mage.cards.m.MemoryCrystal.class)); + cards.add(new SetCardInfo("Merfolk Looter", 39, Rarity.COMMON, mage.cards.m.MerfolkLooter.class)); + cards.add(new SetCardInfo("Mind Maggots", 66, Rarity.UNCOMMON, mage.cards.m.MindMaggots.class)); + cards.add(new SetCardInfo("Mindless Automaton", 135, Rarity.RARE, mage.cards.m.MindlessAutomaton.class)); + cards.add(new SetCardInfo("Mind Over Matter", 40, Rarity.RARE, mage.cards.m.MindOverMatter.class)); + cards.add(new SetCardInfo("Mirozel", 41, Rarity.UNCOMMON, mage.cards.m.Mirozel.class)); + cards.add(new SetCardInfo("Mirri, Cat Warrior", 114, Rarity.RARE, mage.cards.m.MirriCatWarrior.class)); + cards.add(new SetCardInfo("Mogg Assassin", 88, Rarity.UNCOMMON, mage.cards.m.MoggAssassin.class)); + cards.add(new SetCardInfo("Monstrous Hound", 89, Rarity.RARE, mage.cards.m.MonstrousHound.class)); + cards.add(new SetCardInfo("Nausea", 67, Rarity.COMMON, mage.cards.n.Nausea.class)); + cards.add(new SetCardInfo("Necrologia", 68, Rarity.UNCOMMON, mage.cards.n.Necrologia.class)); + cards.add(new SetCardInfo("Null Brooch", 136, Rarity.RARE, mage.cards.n.NullBrooch.class)); + cards.add(new SetCardInfo("Oath of Druids", 115, Rarity.RARE, mage.cards.o.OathOfDruids.class)); + cards.add(new SetCardInfo("Oath of Ghouls", 69, Rarity.RARE, mage.cards.o.OathOfGhouls.class)); + cards.add(new SetCardInfo("Oath of Lieges", 11, Rarity.RARE, mage.cards.o.OathOfLieges.class)); + cards.add(new SetCardInfo("Oath of Mages", 90, Rarity.RARE, mage.cards.o.OathOfMages.class)); + cards.add(new SetCardInfo("Oath of Scholars", 42, Rarity.RARE, mage.cards.o.OathOfScholars.class)); + cards.add(new SetCardInfo("Ogre Shaman", 91, Rarity.RARE, mage.cards.o.OgreShaman.class)); + cards.add(new SetCardInfo("Onslaught", 92, Rarity.COMMON, mage.cards.o.Onslaught.class)); + cards.add(new SetCardInfo("Paladin en-Vec", 12, Rarity.RARE, mage.cards.p.PaladinEnVec.class)); + cards.add(new SetCardInfo("Pandemonium", 93, Rarity.RARE, mage.cards.p.Pandemonium.class)); + cards.add(new SetCardInfo("Paroxysm", 94, Rarity.UNCOMMON, mage.cards.p.Paroxysm.class)); + cards.add(new SetCardInfo("Peace of Mind", 13, Rarity.UNCOMMON, mage.cards.p.PeaceOfMind.class)); + cards.add(new SetCardInfo("Pegasus Stampede", 14, Rarity.UNCOMMON, mage.cards.p.PegasusStampede.class)); + cards.add(new SetCardInfo("Penance", 15, Rarity.UNCOMMON, mage.cards.p.Penance.class)); + cards.add(new SetCardInfo("Pit Spawn", 70, Rarity.RARE, mage.cards.p.PitSpawn.class)); + cards.add(new SetCardInfo("Plaguebearer", 71, Rarity.RARE, mage.cards.p.Plaguebearer.class)); + cards.add(new SetCardInfo("Plated Rootwalla", 116, Rarity.COMMON, mage.cards.p.PlatedRootwalla.class)); + cards.add(new SetCardInfo("Predatory Hunger", 117, Rarity.COMMON, mage.cards.p.PredatoryHunger.class)); + cards.add(new SetCardInfo("Price of Progress", 95, Rarity.UNCOMMON, mage.cards.p.PriceOfProgress.class)); + cards.add(new SetCardInfo("Pygmy Troll", 118, Rarity.COMMON, mage.cards.p.PygmyTroll.class)); + cards.add(new SetCardInfo("Rabid Wolverines", 119, Rarity.COMMON, mage.cards.r.RabidWolverines.class)); + cards.add(new SetCardInfo("Raging Goblin", 96, Rarity.COMMON, mage.cards.r.RagingGoblin.class)); + cards.add(new SetCardInfo("Ravenous Baboons", 97, Rarity.RARE, mage.cards.r.RavenousBaboons.class)); + cards.add(new SetCardInfo("Reaping the Rewards", 16, Rarity.COMMON, mage.cards.r.ReapingTheRewards.class)); + cards.add(new SetCardInfo("Reckless Ogre", 98, Rarity.COMMON, mage.cards.r.RecklessOgre.class)); + cards.add(new SetCardInfo("Reclaim", 120, Rarity.COMMON, mage.cards.r.Reclaim.class)); + cards.add(new SetCardInfo("Reconnaissance", 17, Rarity.UNCOMMON, mage.cards.r.Reconnaissance.class)); + cards.add(new SetCardInfo("Recurring Nightmare", 72, Rarity.RARE, mage.cards.r.RecurringNightmare.class)); + cards.add(new SetCardInfo("Resuscitate", 121, Rarity.UNCOMMON, mage.cards.r.Resuscitate.class)); + cards.add(new SetCardInfo("Robe of Mirrors", 43, Rarity.COMMON, mage.cards.r.RobeOfMirrors.class)); + cards.add(new SetCardInfo("Rootwater Alligator", 122, Rarity.COMMON, mage.cards.r.RootwaterAlligator.class)); + cards.add(new SetCardInfo("Rootwater Mystic", 44, Rarity.COMMON, mage.cards.r.RootwaterMystic.class)); + cards.add(new SetCardInfo("Sabertooth Wyvern", 99, Rarity.UNCOMMON, mage.cards.s.SabertoothWyvern.class)); + cards.add(new SetCardInfo("Scalding Salamander", 100, Rarity.UNCOMMON, mage.cards.s.ScaldingSalamander.class)); + cards.add(new SetCardInfo("Scare Tactics", 73, Rarity.COMMON, mage.cards.s.ScareTactics.class)); + cards.add(new SetCardInfo("School of Piranha", 45, Rarity.COMMON, mage.cards.s.SchoolOfPiranha.class)); + cards.add(new SetCardInfo("Scrivener", 46, Rarity.UNCOMMON, mage.cards.s.Scrivener.class)); + cards.add(new SetCardInfo("Seismic Assault", 101, Rarity.RARE, mage.cards.s.SeismicAssault.class)); + cards.add(new SetCardInfo("Shackles", 18, Rarity.COMMON, mage.cards.s.Shackles.class)); + cards.add(new SetCardInfo("Shattering Pulse", 102, Rarity.COMMON, mage.cards.s.ShatteringPulse.class)); + cards.add(new SetCardInfo("Shield Mate", 19, Rarity.COMMON, mage.cards.s.ShieldMate.class)); + cards.add(new SetCardInfo("Skyshaper", 137, Rarity.UNCOMMON, mage.cards.s.Skyshaper.class)); + cards.add(new SetCardInfo("Skyshroud Elite", 123, Rarity.UNCOMMON, mage.cards.s.SkyshroudElite.class)); + cards.add(new SetCardInfo("Skyshroud War Beast", 124, Rarity.RARE, mage.cards.s.SkyshroudWarBeast.class)); + cards.add(new SetCardInfo("Slaughter", 74, Rarity.UNCOMMON, mage.cards.s.Slaughter.class)); + cards.add(new SetCardInfo("Soltari Visionary", 20, Rarity.COMMON, mage.cards.s.SoltariVisionary.class)); + cards.add(new SetCardInfo("Song of Serenity", 125, Rarity.UNCOMMON, mage.cards.s.SongOfSerenity.class)); + cards.add(new SetCardInfo("Sonic Burst", 103, Rarity.COMMON, mage.cards.s.SonicBurst.class)); + cards.add(new SetCardInfo("Soul Warden", 21, Rarity.COMMON, mage.cards.s.SoulWarden.class)); + cards.add(new SetCardInfo("Spellbook", 138, Rarity.UNCOMMON, mage.cards.s.Spellbook.class)); + cards.add(new SetCardInfo("Spellshock", 104, Rarity.UNCOMMON, mage.cards.s.Spellshock.class)); + cards.add(new SetCardInfo("Sphere of Resistance", 139, Rarity.RARE, mage.cards.s.SphereOfResistance.class)); + cards.add(new SetCardInfo("Spike Cannibal", 75, Rarity.UNCOMMON, mage.cards.s.SpikeCannibal.class)); + cards.add(new SetCardInfo("Spike Hatcher", 126, Rarity.RARE, mage.cards.s.SpikeHatcher.class)); + cards.add(new SetCardInfo("Spike Rogue", 127, Rarity.UNCOMMON, mage.cards.s.SpikeRogue.class)); + cards.add(new SetCardInfo("Spike Weaver", 128, Rarity.RARE, mage.cards.s.SpikeWeaver.class)); + cards.add(new SetCardInfo("Standing Troops", 22, Rarity.COMMON, mage.cards.s.StandingTroops.class)); + cards.add(new SetCardInfo("Survival of the Fittest", 129, Rarity.RARE, mage.cards.s.SurvivalOfTheFittest.class)); + cards.add(new SetCardInfo("Thalakos Drifters", 47, Rarity.RARE, mage.cards.t.ThalakosDrifters.class)); + cards.add(new SetCardInfo("Thalakos Scout", 48, Rarity.COMMON, mage.cards.t.ThalakosScout.class)); + cards.add(new SetCardInfo("Theft of Dreams", 49, Rarity.COMMON, mage.cards.t.TheftOfDreams.class)); + cards.add(new SetCardInfo("Thopter Squadron", 140, Rarity.RARE, mage.cards.t.ThopterSquadron.class)); + cards.add(new SetCardInfo("Thrull Surgeon", 76, Rarity.COMMON, mage.cards.t.ThrullSurgeon.class)); + cards.add(new SetCardInfo("Transmogrifying Licid", 141, Rarity.UNCOMMON, mage.cards.t.TransmogrifyingLicid.class)); + cards.add(new SetCardInfo("Treasure Hunter", 23, Rarity.UNCOMMON, mage.cards.t.TreasureHunter.class)); + cards.add(new SetCardInfo("Treasure Trove", 50, Rarity.UNCOMMON, mage.cards.t.TreasureTrove.class)); + cards.add(new SetCardInfo("Vampire Hounds", 77, Rarity.COMMON, mage.cards.v.VampireHounds.class)); + cards.add(new SetCardInfo("Volrath's Dungeon", 78, Rarity.RARE, mage.cards.v.VolrathsDungeon.class)); + cards.add(new SetCardInfo("Wall of Nets", 24, Rarity.RARE, mage.cards.w.WallOfNets.class)); + cards.add(new SetCardInfo("Wayward Soul", 51, Rarity.COMMON, mage.cards.w.WaywardSoul.class)); + cards.add(new SetCardInfo("Welkin Hawk", 25, Rarity.COMMON, mage.cards.w.WelkinHawk.class)); + cards.add(new SetCardInfo("Whiptongue Frog", 52, Rarity.COMMON, mage.cards.w.WhiptongueFrog.class)); + cards.add(new SetCardInfo("Wood Elves", 130, Rarity.COMMON, mage.cards.w.WoodElves.class)); + cards.add(new SetCardInfo("Workhorse", 142, Rarity.RARE, mage.cards.w.Workhorse.class)); + cards.add(new SetCardInfo("Zealots en-Dal", 26, Rarity.UNCOMMON, mage.cards.z.ZealotsEnDal.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/FifthEdition.java b/Mage.Sets/src/mage/sets/FifthEdition.java index 5c5fb5f6df..781bd4c259 100644 --- a/Mage.Sets/src/mage/sets/FifthEdition.java +++ b/Mage.Sets/src/mage/sets/FifthEdition.java @@ -1,468 +1,468 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -public final class FifthEdition extends ExpansionSet { - - private static final FifthEdition instance = new FifthEdition(); - - public static FifthEdition getInstance() { - return instance; - } - - private FifthEdition() { - super("Fifth Edition", "5ED", ExpansionSet.buildDate(1997, 3, 1), SetType.CORE); - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - - cards.add(new SetCardInfo("Abbey Gargoyles", 1, Rarity.UNCOMMON, mage.cards.a.AbbeyGargoyles.class)); - cards.add(new SetCardInfo("Abyssal Specter", 139, Rarity.UNCOMMON, mage.cards.a.AbyssalSpecter.class)); - cards.add(new SetCardInfo("Adarkar Wastes", 410, Rarity.RARE, mage.cards.a.AdarkarWastes.class)); - cards.add(new SetCardInfo("Aether Storm", 70, Rarity.UNCOMMON, mage.cards.a.AetherStorm.class)); - cards.add(new SetCardInfo("Air Elemental", 71, Rarity.UNCOMMON, mage.cards.a.AirElemental.class)); - cards.add(new SetCardInfo("Akron Legionnaire", 2, Rarity.RARE, mage.cards.a.AkronLegionnaire.class)); - cards.add(new SetCardInfo("Alabaster Potion", 3, Rarity.COMMON, mage.cards.a.AlabasterPotion.class)); - cards.add(new SetCardInfo("Aladdin's Ring", 346, Rarity.RARE, mage.cards.a.AladdinsRing.class)); - cards.add(new SetCardInfo("Ambush Party", 208, Rarity.COMMON, mage.cards.a.AmbushParty.class)); - cards.add(new SetCardInfo("Amulet of Kroog", 347, Rarity.COMMON, mage.cards.a.AmuletOfKroog.class)); - cards.add(new SetCardInfo("An-Havva Constable", 277, Rarity.RARE, mage.cards.a.AnHavvaConstable.class)); - cards.add(new SetCardInfo("Angry Mob", 4, Rarity.UNCOMMON, mage.cards.a.AngryMob.class)); - cards.add(new SetCardInfo("Animate Dead", 140, Rarity.UNCOMMON, mage.cards.a.AnimateDead.class)); - cards.add(new SetCardInfo("Animate Wall", 5, Rarity.RARE, mage.cards.a.AnimateWall.class)); - cards.add(new SetCardInfo("Ankh of Mishra", 348, Rarity.RARE, mage.cards.a.AnkhOfMishra.class)); - cards.add(new SetCardInfo("Anti-Magic Aura", 72, Rarity.UNCOMMON, mage.cards.a.AntiMagicAura.class)); - cards.add(new SetCardInfo("Arenson's Aura", 6, Rarity.UNCOMMON, mage.cards.a.ArensonsAura.class)); - cards.add(new SetCardInfo("Armageddon", 7, Rarity.RARE, mage.cards.a.Armageddon.class)); - cards.add(new SetCardInfo("Armor of Faith", 8, Rarity.COMMON, mage.cards.a.ArmorOfFaith.class)); - cards.add(new SetCardInfo("Ashes to Ashes", 141, Rarity.UNCOMMON, mage.cards.a.AshesToAshes.class)); - cards.add(new SetCardInfo("Ashnod's Altar", 349, Rarity.UNCOMMON, mage.cards.a.AshnodsAltar.class)); - cards.add(new SetCardInfo("Ashnod's Transmogrant", 350, Rarity.COMMON, mage.cards.a.AshnodsTransmogrant.class)); - cards.add(new SetCardInfo("Aspect of Wolf", 278, Rarity.RARE, mage.cards.a.AspectOfWolf.class)); - cards.add(new SetCardInfo("Atog", 209, Rarity.UNCOMMON, mage.cards.a.Atog.class)); - cards.add(new SetCardInfo("Aurochs", 279, Rarity.COMMON, mage.cards.a.Aurochs.class)); - cards.add(new SetCardInfo("Aysen Bureaucrats", 9, Rarity.COMMON, mage.cards.a.AysenBureaucrats.class)); - cards.add(new SetCardInfo("Azure Drake", 73, Rarity.UNCOMMON, mage.cards.a.AzureDrake.class)); - cards.add(new SetCardInfo("Bad Moon", 142, Rarity.RARE, mage.cards.b.BadMoon.class)); - cards.add(new SetCardInfo("Ball Lightning", 210, Rarity.RARE, mage.cards.b.BallLightning.class)); - cards.add(new SetCardInfo("Barbed Sextant", 351, Rarity.COMMON, mage.cards.b.BarbedSextant.class)); - cards.add(new SetCardInfo("Barl's Cage", 352, Rarity.RARE, mage.cards.b.BarlsCage.class)); - cards.add(new SetCardInfo("Battering Ram", 353, Rarity.COMMON, mage.cards.b.BatteringRam.class)); - cards.add(new SetCardInfo("Benalish Hero", 10, Rarity.COMMON, mage.cards.b.BenalishHero.class)); - cards.add(new SetCardInfo("Binding Grasp", 74, Rarity.UNCOMMON, mage.cards.b.BindingGrasp.class)); - cards.add(new SetCardInfo("Bird Maiden", 211, Rarity.COMMON, mage.cards.b.BirdMaiden.class)); - cards.add(new SetCardInfo("Birds of Paradise", 280, Rarity.RARE, mage.cards.b.BirdsOfParadise.class)); - cards.add(new SetCardInfo("Black Knight", 143, Rarity.UNCOMMON, mage.cards.b.BlackKnight.class)); - cards.add(new SetCardInfo("Blessed Wine", 11, Rarity.COMMON, mage.cards.b.BlessedWine.class)); - cards.add(new SetCardInfo("Blight", 144, Rarity.UNCOMMON, mage.cards.b.Blight.class)); - cards.add(new SetCardInfo("Blinking Spirit", 12, Rarity.RARE, mage.cards.b.BlinkingSpirit.class)); - cards.add(new SetCardInfo("Blood Lust", 212, Rarity.COMMON, mage.cards.b.BloodLust.class)); - cards.add(new SetCardInfo("Bog Imp", 145, Rarity.COMMON, mage.cards.b.BogImp.class)); - cards.add(new SetCardInfo("Bog Rats", 146, Rarity.COMMON, mage.cards.b.BogRats.class)); - cards.add(new SetCardInfo("Bog Wraith", 147, Rarity.UNCOMMON, mage.cards.b.BogWraith.class)); - cards.add(new SetCardInfo("Boomerang", 75, Rarity.COMMON, mage.cards.b.Boomerang.class)); - cards.add(new SetCardInfo("Bottle of Suleiman", 354, Rarity.RARE, mage.cards.b.BottleOfSuleiman.class)); - cards.add(new SetCardInfo("Bottomless Vault", 411, Rarity.RARE, mage.cards.b.BottomlessVault.class)); - cards.add(new SetCardInfo("Brainstorm", 76, Rarity.COMMON, mage.cards.b.Brainstorm.class)); - cards.add(new SetCardInfo("Brainwash", 13, Rarity.COMMON, mage.cards.b.Brainwash.class)); - cards.add(new SetCardInfo("Brassclaw Orcs", 213, Rarity.COMMON, mage.cards.b.BrassclawOrcs.class)); - cards.add(new SetCardInfo("Breeding Pit", 148, Rarity.UNCOMMON, mage.cards.b.BreedingPit.class)); - cards.add(new SetCardInfo("Broken Visage", 149, Rarity.RARE, mage.cards.b.BrokenVisage.class)); - cards.add(new SetCardInfo("Brothers of Fire", 214, Rarity.COMMON, mage.cards.b.BrothersOfFire.class)); - cards.add(new SetCardInfo("Brushland", 412, Rarity.RARE, mage.cards.b.Brushland.class)); - cards.add(new SetCardInfo("Carapace", 281, Rarity.COMMON, mage.cards.c.Carapace.class)); - cards.add(new SetCardInfo("Caribou Range", 14, Rarity.RARE, mage.cards.c.CaribouRange.class)); - cards.add(new SetCardInfo("Carrion Ants", 150, Rarity.UNCOMMON, mage.cards.c.CarrionAnts.class)); - cards.add(new SetCardInfo("Castle", 15, Rarity.UNCOMMON, mage.cards.c.Castle.class)); - cards.add(new SetCardInfo("Cat Warriors", 282, Rarity.COMMON, mage.cards.c.CatWarriors.class)); - cards.add(new SetCardInfo("Cave People", 215, Rarity.UNCOMMON, mage.cards.c.CavePeople.class)); - cards.add(new SetCardInfo("Chub Toad", 283, Rarity.COMMON, mage.cards.c.ChubToad.class)); - cards.add(new SetCardInfo("Circle of Protection: Artifacts", 16, Rarity.UNCOMMON, mage.cards.c.CircleOfProtectionArtifacts.class)); - cards.add(new SetCardInfo("Circle of Protection: Black", 17, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlack.class)); - cards.add(new SetCardInfo("Circle of Protection: Blue", 18, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlue.class)); - cards.add(new SetCardInfo("Circle of Protection: Green", 19, Rarity.COMMON, mage.cards.c.CircleOfProtectionGreen.class)); - cards.add(new SetCardInfo("Circle of Protection: Red", 20, Rarity.COMMON, mage.cards.c.CircleOfProtectionRed.class)); - cards.add(new SetCardInfo("Circle of Protection: White", 21, Rarity.COMMON, mage.cards.c.CircleOfProtectionWhite.class)); - cards.add(new SetCardInfo("City of Brass", 413, Rarity.RARE, mage.cards.c.CityOfBrass.class)); - cards.add(new SetCardInfo("Clay Statue", 355, Rarity.COMMON, mage.cards.c.ClayStatue.class)); - cards.add(new SetCardInfo("Cloak of Confusion", 151, Rarity.COMMON, mage.cards.c.CloakOfConfusion.class)); - cards.add(new SetCardInfo("Clockwork Beast", 356, Rarity.RARE, mage.cards.c.ClockworkBeast.class)); - cards.add(new SetCardInfo("Clockwork Steed", 357, Rarity.UNCOMMON, mage.cards.c.ClockworkSteed.class)); - cards.add(new SetCardInfo("Cockatrice", 284, Rarity.RARE, mage.cards.c.Cockatrice.class)); - cards.add(new SetCardInfo("Colossus of Sardia", 358, Rarity.RARE, mage.cards.c.ColossusOfSardia.class)); - cards.add(new SetCardInfo("Conquer", 216, Rarity.UNCOMMON, mage.cards.c.Conquer.class)); - cards.add(new SetCardInfo("Coral Helm", 359, Rarity.RARE, mage.cards.c.CoralHelm.class)); - cards.add(new SetCardInfo("Counterspell", 77, Rarity.COMMON, mage.cards.c.Counterspell.class)); - cards.add(new SetCardInfo("Craw Giant", 285, Rarity.UNCOMMON, mage.cards.c.CrawGiant.class)); - cards.add(new SetCardInfo("Craw Wurm", 286, Rarity.COMMON, mage.cards.c.CrawWurm.class)); - cards.add(new SetCardInfo("Crimson Manticore", 217, Rarity.RARE, mage.cards.c.CrimsonManticore.class)); - cards.add(new SetCardInfo("Crown of the Ages", 360, Rarity.RARE, mage.cards.c.CrownOfTheAges.class)); - cards.add(new SetCardInfo("Crumble", 287, Rarity.UNCOMMON, mage.cards.c.Crumble.class)); - cards.add(new SetCardInfo("Crusade", 22, Rarity.RARE, mage.cards.c.Crusade.class)); - cards.add(new SetCardInfo("Crystal Rod", 361, Rarity.UNCOMMON, mage.cards.c.CrystalRod.class)); - cards.add(new SetCardInfo("Cursed Land", 152, Rarity.UNCOMMON, mage.cards.c.CursedLand.class)); - cards.add(new SetCardInfo("D'Avenant Archer", 23, Rarity.COMMON, mage.cards.d.DAvenantArcher.class)); - cards.add(new SetCardInfo("Dance of Many", 78, Rarity.RARE, mage.cards.d.DanceOfMany.class)); - cards.add(new SetCardInfo("Dancing Scimitar", 362, Rarity.RARE, mage.cards.d.DancingScimitar.class)); - cards.add(new SetCardInfo("Dandan", 79, Rarity.COMMON, mage.cards.d.Dandan.class)); - cards.add(new SetCardInfo("Dark Maze", 80, Rarity.COMMON, mage.cards.d.DarkMaze.class)); - cards.add(new SetCardInfo("Dark Ritual", 153, Rarity.COMMON, mage.cards.d.DarkRitual.class)); - cards.add(new SetCardInfo("Death Speakers", 24, Rarity.COMMON, mage.cards.d.DeathSpeakers.class)); - cards.add(new SetCardInfo("Death Ward", 25, Rarity.COMMON, mage.cards.d.DeathWard.class)); - cards.add(new SetCardInfo("Deathgrip", 154, Rarity.UNCOMMON, mage.cards.d.Deathgrip.class)); - cards.add(new SetCardInfo("Deflection", 81, Rarity.RARE, mage.cards.d.Deflection.class)); - cards.add(new SetCardInfo("Derelor", 155, Rarity.RARE, mage.cards.d.Derelor.class)); - cards.add(new SetCardInfo("Desert Twister", 288, Rarity.UNCOMMON, mage.cards.d.DesertTwister.class)); - cards.add(new SetCardInfo("Detonate", 218, Rarity.UNCOMMON, mage.cards.d.Detonate.class)); - cards.add(new SetCardInfo("Diabolic Machine", 363, Rarity.UNCOMMON, mage.cards.d.DiabolicMachine.class)); - cards.add(new SetCardInfo("Dingus Egg", 364, Rarity.RARE, mage.cards.d.DingusEgg.class)); - cards.add(new SetCardInfo("Disenchant", 26, Rarity.COMMON, mage.cards.d.Disenchant.class)); - cards.add(new SetCardInfo("Disintegrate", 219, Rarity.COMMON, mage.cards.d.Disintegrate.class)); - cards.add(new SetCardInfo("Disrupting Scepter", 365, Rarity.RARE, mage.cards.d.DisruptingScepter.class)); - cards.add(new SetCardInfo("Divine Offering", 27, Rarity.COMMON, mage.cards.d.DivineOffering.class)); - cards.add(new SetCardInfo("Divine Transformation", 28, Rarity.UNCOMMON, mage.cards.d.DivineTransformation.class)); - cards.add(new SetCardInfo("Dragon Engine", 366, Rarity.RARE, mage.cards.d.DragonEngine.class)); - cards.add(new SetCardInfo("Drain Life", 156, Rarity.COMMON, mage.cards.d.DrainLife.class)); - cards.add(new SetCardInfo("Drain Power", 82, Rarity.RARE, mage.cards.d.DrainPower.class)); - cards.add(new SetCardInfo("Drudge Skeletons", 157, Rarity.COMMON, mage.cards.d.DrudgeSkeletons.class)); - cards.add(new SetCardInfo("Durkwood Boars", 289, Rarity.COMMON, mage.cards.d.DurkwoodBoars.class)); - cards.add(new SetCardInfo("Dust to Dust", 29, Rarity.UNCOMMON, mage.cards.d.DustToDust.class)); - cards.add(new SetCardInfo("Dwarven Catapult", 220, Rarity.UNCOMMON, mage.cards.d.DwarvenCatapult.class)); - cards.add(new SetCardInfo("Dwarven Hold", 414, Rarity.RARE, mage.cards.d.DwarvenHold.class)); - cards.add(new SetCardInfo("Dwarven Ruins", 415, Rarity.UNCOMMON, mage.cards.d.DwarvenRuins.class)); - cards.add(new SetCardInfo("Dwarven Soldier", 221, Rarity.COMMON, mage.cards.d.DwarvenSoldier.class)); - cards.add(new SetCardInfo("Dwarven Warriors", 222, Rarity.COMMON, mage.cards.d.DwarvenWarriors.class)); - cards.add(new SetCardInfo("Earthquake", 223, Rarity.RARE, mage.cards.e.Earthquake.class)); - cards.add(new SetCardInfo("Ebon Stronghold", 416, Rarity.UNCOMMON, mage.cards.e.EbonStronghold.class)); - cards.add(new SetCardInfo("Elder Druid", 290, Rarity.RARE, mage.cards.e.ElderDruid.class)); - cards.add(new SetCardInfo("Elkin Bottle", 367, Rarity.RARE, mage.cards.e.ElkinBottle.class)); - cards.add(new SetCardInfo("Elven Riders", 291, Rarity.UNCOMMON, mage.cards.e.ElvenRiders.class)); - cards.add(new SetCardInfo("Elvish Archers", 292, Rarity.RARE, mage.cards.e.ElvishArchers.class)); - cards.add(new SetCardInfo("Energy Flux", 83, Rarity.UNCOMMON, mage.cards.e.EnergyFlux.class)); - cards.add(new SetCardInfo("Enervate", 84, Rarity.COMMON, mage.cards.e.Enervate.class)); - cards.add(new SetCardInfo("Erg Raiders", 158, Rarity.COMMON, mage.cards.e.ErgRaiders.class)); - cards.add(new SetCardInfo("Errantry", 224, Rarity.COMMON, mage.cards.e.Errantry.class)); - cards.add(new SetCardInfo("Eternal Warrior", 225, Rarity.COMMON, mage.cards.e.EternalWarrior.class)); - cards.add(new SetCardInfo("Evil Eye of Orms-by-Gore", 159, Rarity.UNCOMMON, mage.cards.e.EvilEyeOfOrmsByGore.class)); - cards.add(new SetCardInfo("Evil Presence", 160, Rarity.UNCOMMON, mage.cards.e.EvilPresence.class)); - cards.add(new SetCardInfo("Eye for an Eye", 30, Rarity.RARE, mage.cards.e.EyeForAnEye.class)); - cards.add(new SetCardInfo("Fallen Angel", 161, Rarity.UNCOMMON, mage.cards.f.FallenAngel.class)); - cards.add(new SetCardInfo("Fear", 162, Rarity.COMMON, mage.cards.f.Fear.class)); - cards.add(new SetCardInfo("Feedback", 85, Rarity.UNCOMMON, mage.cards.f.Feedback.class)); - cards.add(new SetCardInfo("Feldon's Cane", 368, Rarity.UNCOMMON, mage.cards.f.FeldonsCane.class)); - cards.add(new SetCardInfo("Fellwar Stone", 369, Rarity.UNCOMMON, mage.cards.f.FellwarStone.class)); - cards.add(new SetCardInfo("Feroz's Ban", 370, Rarity.RARE, mage.cards.f.FerozsBan.class)); - cards.add(new SetCardInfo("Fire Drake", 226, Rarity.UNCOMMON, mage.cards.f.FireDrake.class)); - cards.add(new SetCardInfo("Fireball", 227, Rarity.COMMON, mage.cards.f.Fireball.class)); - cards.add(new SetCardInfo("Firebreathing", 228, Rarity.COMMON, mage.cards.f.Firebreathing.class)); - cards.add(new SetCardInfo("Flame Spirit", 229, Rarity.UNCOMMON, mage.cards.f.FlameSpirit.class)); - cards.add(new SetCardInfo("Flare", 230, Rarity.COMMON, mage.cards.f.Flare.class)); - cards.add(new SetCardInfo("Flashfires", 231, Rarity.UNCOMMON, mage.cards.f.Flashfires.class)); - cards.add(new SetCardInfo("Flight", 86, Rarity.COMMON, mage.cards.f.Flight.class)); - cards.add(new SetCardInfo("Flood", 87, Rarity.COMMON, mage.cards.f.Flood.class)); - cards.add(new SetCardInfo("Flying Carpet", 371, Rarity.RARE, mage.cards.f.FlyingCarpet.class)); - cards.add(new SetCardInfo("Fog", 293, Rarity.COMMON, mage.cards.f.Fog.class)); - cards.add(new SetCardInfo("Force of Nature", 294, Rarity.RARE, mage.cards.f.ForceOfNature.class)); - cards.add(new SetCardInfo("Force Spike", 88, Rarity.COMMON, mage.cards.f.ForceSpike.class)); - cards.add(new SetCardInfo("Forest", 446, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 447, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 448, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 449, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forget", 89, Rarity.RARE, mage.cards.f.Forget.class)); - cards.add(new SetCardInfo("Fountain of Youth", 372, Rarity.UNCOMMON, mage.cards.f.FountainOfYouth.class)); - cards.add(new SetCardInfo("Foxfire", 295, Rarity.COMMON, mage.cards.f.Foxfire.class)); - cards.add(new SetCardInfo("Frozen Shade", 163, Rarity.COMMON, mage.cards.f.FrozenShade.class)); - cards.add(new SetCardInfo("Funeral March", 164, Rarity.COMMON, mage.cards.f.FuneralMarch.class)); - cards.add(new SetCardInfo("Fungusaur", 296, Rarity.RARE, mage.cards.f.Fungusaur.class)); - cards.add(new SetCardInfo("Fyndhorn Elder", 297, Rarity.UNCOMMON, mage.cards.f.FyndhornElder.class)); - cards.add(new SetCardInfo("Game of Chaos", 232, Rarity.RARE, mage.cards.g.GameOfChaos.class)); - cards.add(new SetCardInfo("Gaseous Form", 90, Rarity.COMMON, mage.cards.g.GaseousForm.class)); - cards.add(new SetCardInfo("Gauntlets of Chaos", 373, Rarity.RARE, mage.cards.g.GauntletsOfChaos.class)); - cards.add(new SetCardInfo("Ghazban Ogre", 298, Rarity.COMMON, mage.cards.g.GhazbanOgre.class)); - cards.add(new SetCardInfo("Giant Growth", 299, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); - cards.add(new SetCardInfo("Giant Spider", 300, Rarity.COMMON, mage.cards.g.GiantSpider.class)); - cards.add(new SetCardInfo("Giant Strength", 233, Rarity.COMMON, mage.cards.g.GiantStrength.class)); - cards.add(new SetCardInfo("Glacial Wall", 91, Rarity.UNCOMMON, mage.cards.g.GlacialWall.class)); - cards.add(new SetCardInfo("Glasses of Urza", 374, Rarity.UNCOMMON, mage.cards.g.GlassesOfUrza.class)); - cards.add(new SetCardInfo("Gloom", 165, Rarity.UNCOMMON, mage.cards.g.Gloom.class)); - cards.add(new SetCardInfo("Goblin Digging Team", 234, Rarity.COMMON, mage.cards.g.GoblinDiggingTeam.class)); - cards.add(new SetCardInfo("Goblin Hero", 235, Rarity.COMMON, mage.cards.g.GoblinHero.class)); - cards.add(new SetCardInfo("Goblin King", 236, Rarity.RARE, mage.cards.g.GoblinKing.class)); - cards.add(new SetCardInfo("Goblin War Drums", 237, Rarity.COMMON, mage.cards.g.GoblinWarDrums.class)); - cards.add(new SetCardInfo("Goblin Warrens", 238, Rarity.RARE, mage.cards.g.GoblinWarrens.class)); - cards.add(new SetCardInfo("Grapeshot Catapult", 375, Rarity.COMMON, mage.cards.g.GrapeshotCatapult.class)); - cards.add(new SetCardInfo("Greater Realm of Preservation", 31, Rarity.UNCOMMON, mage.cards.g.GreaterRealmOfPreservation.class)); - cards.add(new SetCardInfo("Greater Werewolf", 166, Rarity.UNCOMMON, mage.cards.g.GreaterWerewolf.class)); - cards.add(new SetCardInfo("Grizzly Bears", 301, Rarity.COMMON, mage.cards.g.GrizzlyBears.class)); - cards.add(new SetCardInfo("Havenwood Battleground", 417, Rarity.UNCOMMON, mage.cards.h.HavenwoodBattleground.class)); - cards.add(new SetCardInfo("Heal", 32, Rarity.COMMON, mage.cards.h.Heal.class)); - cards.add(new SetCardInfo("Healing Salve", 33, Rarity.COMMON, mage.cards.h.HealingSalve.class)); - cards.add(new SetCardInfo("Hecatomb", 167, Rarity.RARE, mage.cards.h.Hecatomb.class)); - cards.add(new SetCardInfo("Helm of Chatzuk", 376, Rarity.RARE, mage.cards.h.HelmOfChatzuk.class)); - cards.add(new SetCardInfo("Hill Giant", 239, Rarity.COMMON, mage.cards.h.HillGiant.class)); - cards.add(new SetCardInfo("Hollow Trees", 418, Rarity.RARE, mage.cards.h.HollowTrees.class)); - cards.add(new SetCardInfo("Holy Strength", 35, Rarity.COMMON, mage.cards.h.HolyStrength.class)); - cards.add(new SetCardInfo("Homarid Warrior", 92, Rarity.COMMON, mage.cards.h.HomaridWarrior.class)); - cards.add(new SetCardInfo("Howl from Beyond", 168, Rarity.COMMON, mage.cards.h.HowlFromBeyond.class)); - cards.add(new SetCardInfo("Howling Mine", 377, Rarity.RARE, mage.cards.h.HowlingMine.class)); - cards.add(new SetCardInfo("Hungry Mist", 302, Rarity.COMMON, mage.cards.h.HungryMist.class)); - cards.add(new SetCardInfo("Hurkyl's Recall", 93, Rarity.RARE, mage.cards.h.HurkylsRecall.class)); - cards.add(new SetCardInfo("Hurloon Minotaur", 240, Rarity.COMMON, mage.cards.h.HurloonMinotaur.class)); - cards.add(new SetCardInfo("Hurricane", 303, Rarity.UNCOMMON, mage.cards.h.Hurricane.class)); - cards.add(new SetCardInfo("Hydroblast", 94, Rarity.UNCOMMON, mage.cards.h.Hydroblast.class)); - cards.add(new SetCardInfo("Icatian Phalanx", 36, Rarity.UNCOMMON, mage.cards.i.IcatianPhalanx.class)); - cards.add(new SetCardInfo("Icatian Scout", 37, Rarity.COMMON, mage.cards.i.IcatianScout.class)); - cards.add(new SetCardInfo("Icatian Store", 419, Rarity.RARE, mage.cards.i.IcatianStore.class)); - cards.add(new SetCardInfo("Icatian Town", 38, Rarity.RARE, mage.cards.i.IcatianTown.class)); - cards.add(new SetCardInfo("Ice Floe", 420, Rarity.UNCOMMON, mage.cards.i.IceFloe.class)); - cards.add(new SetCardInfo("Imposing Visage", 241, Rarity.COMMON, mage.cards.i.ImposingVisage.class)); - cards.add(new SetCardInfo("Incinerate", 242, Rarity.COMMON, mage.cards.i.Incinerate.class)); - cards.add(new SetCardInfo("Inferno", 243, Rarity.RARE, mage.cards.i.Inferno.class)); - cards.add(new SetCardInfo("Infinite Hourglass", 378, Rarity.RARE, mage.cards.i.InfiniteHourglass.class)); - cards.add(new SetCardInfo("Initiates of the Ebon Hand", 169, Rarity.COMMON, mage.cards.i.InitiatesOfTheEbonHand.class)); - cards.add(new SetCardInfo("Instill Energy", 304, Rarity.UNCOMMON, mage.cards.i.InstillEnergy.class)); - cards.add(new SetCardInfo("Iron Star", 379, Rarity.UNCOMMON, mage.cards.i.IronStar.class)); - cards.add(new SetCardInfo("Ironclaw Curse", 244, Rarity.RARE, mage.cards.i.IronclawCurse.class)); - cards.add(new SetCardInfo("Ironclaw Orcs", 245, Rarity.COMMON, mage.cards.i.IronclawOrcs.class)); - cards.add(new SetCardInfo("Ironroot Treefolk", 305, Rarity.COMMON, mage.cards.i.IronrootTreefolk.class)); - cards.add(new SetCardInfo("Island Sanctuary", 39, Rarity.RARE, mage.cards.i.IslandSanctuary.class)); - cards.add(new SetCardInfo("Island", 434, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 435, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 436, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 437, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ivory Cup", 380, Rarity.UNCOMMON, mage.cards.i.IvoryCup.class)); - cards.add(new SetCardInfo("Ivory Guardians", 40, Rarity.UNCOMMON, mage.cards.i.IvoryGuardians.class)); - cards.add(new SetCardInfo("Jade Monolith", 381, Rarity.RARE, mage.cards.j.JadeMonolith.class)); - cards.add(new SetCardInfo("Jalum Tome", 382, Rarity.RARE, mage.cards.j.JalumTome.class)); - cards.add(new SetCardInfo("Jandor's Saddlebags", 383, Rarity.RARE, mage.cards.j.JandorsSaddlebags.class)); - cards.add(new SetCardInfo("Jayemdae Tome", 384, Rarity.RARE, mage.cards.j.JayemdaeTome.class)); - cards.add(new SetCardInfo("Jester's Cap", 385, Rarity.RARE, mage.cards.j.JestersCap.class)); - cards.add(new SetCardInfo("Johtull Wurm", 306, Rarity.UNCOMMON, mage.cards.j.JohtullWurm.class)); - cards.add(new SetCardInfo("Jokulhaups", 246, Rarity.RARE, mage.cards.j.Jokulhaups.class)); - cards.add(new SetCardInfo("Joven's Tools", 386, Rarity.UNCOMMON, mage.cards.j.JovensTools.class)); - cards.add(new SetCardInfo("Justice", 41, Rarity.UNCOMMON, mage.cards.j.Justice.class)); - cards.add(new SetCardInfo("Juxtapose", 95, Rarity.RARE, mage.cards.j.Juxtapose.class)); - cards.add(new SetCardInfo("Karma", 42, Rarity.UNCOMMON, mage.cards.k.Karma.class)); - cards.add(new SetCardInfo("Karplusan Forest", 421, Rarity.RARE, mage.cards.k.KarplusanForest.class)); - cards.add(new SetCardInfo("Keldon Warlord", 247, Rarity.UNCOMMON, mage.cards.k.KeldonWarlord.class)); - cards.add(new SetCardInfo("Killer Bees", 307, Rarity.UNCOMMON, mage.cards.k.KillerBees.class)); - cards.add(new SetCardInfo("Kismet", 43, Rarity.UNCOMMON, mage.cards.k.Kismet.class)); - cards.add(new SetCardInfo("Kjeldoran Dead", 170, Rarity.COMMON, mage.cards.k.KjeldoranDead.class)); - cards.add(new SetCardInfo("Kjeldoran Royal Guard", 44, Rarity.RARE, mage.cards.k.KjeldoranRoyalGuard.class)); - cards.add(new SetCardInfo("Kjeldoran Skycaptain", 45, Rarity.UNCOMMON, mage.cards.k.KjeldoranSkycaptain.class)); - cards.add(new SetCardInfo("Knight of Stromgald", 171, Rarity.UNCOMMON, mage.cards.k.KnightOfStromgald.class)); - cards.add(new SetCardInfo("Krovikan Fetish", 172, Rarity.COMMON, mage.cards.k.KrovikanFetish.class)); - cards.add(new SetCardInfo("Krovikan Sorcerer", 96, Rarity.COMMON, mage.cards.k.KrovikanSorcerer.class)); - cards.add(new SetCardInfo("Labyrinth Minotaur", 97, Rarity.COMMON, mage.cards.l.LabyrinthMinotaur.class)); - cards.add(new SetCardInfo("Leshrac's Rite", 173, Rarity.UNCOMMON, mage.cards.l.LeshracsRite.class)); - cards.add(new SetCardInfo("Leviathan", 98, Rarity.RARE, mage.cards.l.Leviathan.class)); - cards.add(new SetCardInfo("Ley Druid", 308, Rarity.COMMON, mage.cards.l.LeyDruid.class)); - cards.add(new SetCardInfo("Lhurgoyf", 309, Rarity.RARE, mage.cards.l.Lhurgoyf.class)); - cards.add(new SetCardInfo("Library of Leng", 387, Rarity.UNCOMMON, mage.cards.l.LibraryOfLeng.class)); - cards.add(new SetCardInfo("Lifeforce", 310, Rarity.UNCOMMON, mage.cards.l.Lifeforce.class)); - cards.add(new SetCardInfo("Lifetap", 99, Rarity.UNCOMMON, mage.cards.l.Lifetap.class)); - cards.add(new SetCardInfo("Living Artifact", 311, Rarity.RARE, mage.cards.l.LivingArtifact.class)); - cards.add(new SetCardInfo("Living Lands", 312, Rarity.RARE, mage.cards.l.LivingLands.class)); - cards.add(new SetCardInfo("Llanowar Elves", 313, Rarity.COMMON, mage.cards.l.LlanowarElves.class)); - cards.add(new SetCardInfo("Lord of Atlantis", 100, Rarity.RARE, mage.cards.l.LordOfAtlantis.class)); - cards.add(new SetCardInfo("Lord of the Pit", 174, Rarity.RARE, mage.cards.l.LordOfThePit.class)); - cards.add(new SetCardInfo("Lost Soul", 175, Rarity.COMMON, mage.cards.l.LostSoul.class)); - cards.add(new SetCardInfo("Lure", 314, Rarity.UNCOMMON, mage.cards.l.Lure.class)); - cards.add(new SetCardInfo("Magus of the Unseen", 102, Rarity.RARE, mage.cards.m.MagusOfTheUnseen.class)); - cards.add(new SetCardInfo("Mana Clash", 248, Rarity.RARE, mage.cards.m.ManaClash.class)); - cards.add(new SetCardInfo("Mana Flare", 249, Rarity.RARE, mage.cards.m.ManaFlare.class)); - cards.add(new SetCardInfo("Mana Vault", 388, Rarity.RARE, mage.cards.m.ManaVault.class)); - cards.add(new SetCardInfo("Manabarbs", 250, Rarity.RARE, mage.cards.m.Manabarbs.class)); - cards.add(new SetCardInfo("Marsh Viper", 315, Rarity.COMMON, mage.cards.m.MarshViper.class)); - cards.add(new SetCardInfo("Meekstone", 389, Rarity.RARE, mage.cards.m.Meekstone.class)); - cards.add(new SetCardInfo("Memory Lapse", 103, Rarity.COMMON, mage.cards.m.MemoryLapse.class)); - cards.add(new SetCardInfo("Merfolk of the Pearl Trident", 104, Rarity.COMMON, mage.cards.m.MerfolkOfThePearlTrident.class)); - cards.add(new SetCardInfo("Mesa Falcon", 46, Rarity.COMMON, mage.cards.m.MesaFalcon.class)); - cards.add(new SetCardInfo("Mesa Pegasus", 47, Rarity.COMMON, mage.cards.m.MesaPegasus.class)); - cards.add(new SetCardInfo("Millstone", 390, Rarity.RARE, mage.cards.m.Millstone.class)); - cards.add(new SetCardInfo("Mind Bomb", 105, Rarity.UNCOMMON, mage.cards.m.MindBomb.class)); - cards.add(new SetCardInfo("Mind Ravel", 176, Rarity.COMMON, mage.cards.m.MindRavel.class)); - cards.add(new SetCardInfo("Mind Warp", 177, Rarity.UNCOMMON, mage.cards.m.MindWarp.class)); - cards.add(new SetCardInfo("Mindstab Thrull", 178, Rarity.COMMON, mage.cards.m.MindstabThrull.class)); - cards.add(new SetCardInfo("Mole Worms", 179, Rarity.UNCOMMON, mage.cards.m.MoleWorms.class)); - cards.add(new SetCardInfo("Mons's Goblin Raiders", 251, Rarity.COMMON, mage.cards.m.MonssGoblinRaiders.class)); - cards.add(new SetCardInfo("Mountain Goat", 252, Rarity.COMMON, mage.cards.m.MountainGoat.class)); - cards.add(new SetCardInfo("Mountain", 442, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 443, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 444, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 445, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Murk Dwellers", 180, Rarity.COMMON, mage.cards.m.MurkDwellers.class)); - cards.add(new SetCardInfo("Nature's Lore", 316, Rarity.COMMON, mage.cards.n.NaturesLore.class)); - cards.add(new SetCardInfo("Necrite", 181, Rarity.COMMON, mage.cards.n.Necrite.class)); - cards.add(new SetCardInfo("Necropotence", 182, Rarity.RARE, mage.cards.n.Necropotence.class)); - cards.add(new SetCardInfo("Nether Shadow", 183, Rarity.RARE, mage.cards.n.NetherShadow.class)); - cards.add(new SetCardInfo("Nevinyrral's Disk", 391, Rarity.RARE, mage.cards.n.NevinyrralsDisk.class)); - cards.add(new SetCardInfo("Nightmare", 184, Rarity.RARE, mage.cards.n.Nightmare.class)); - cards.add(new SetCardInfo("Obelisk of Undoing", 392, Rarity.RARE, mage.cards.o.ObeliskOfUndoing.class)); - cards.add(new SetCardInfo("Orcish Artillery", 253, Rarity.UNCOMMON, mage.cards.o.OrcishArtillery.class)); - cards.add(new SetCardInfo("Orcish Captain", 254, Rarity.UNCOMMON, mage.cards.o.OrcishCaptain.class)); - cards.add(new SetCardInfo("Orcish Oriflamme", 257, Rarity.UNCOMMON, mage.cards.o.OrcishOriflamme.class)); - cards.add(new SetCardInfo("Orcish Squatters", 258, Rarity.RARE, mage.cards.o.OrcishSquatters.class)); - cards.add(new SetCardInfo("Order of the Sacred Torch", 48, Rarity.RARE, mage.cards.o.OrderOfTheSacredTorch.class)); - cards.add(new SetCardInfo("Order of the White Shield", 49, Rarity.UNCOMMON, mage.cards.o.OrderOfTheWhiteShield.class)); - cards.add(new SetCardInfo("Orgg", 259, Rarity.RARE, mage.cards.o.Orgg.class)); - cards.add(new SetCardInfo("Ornithopter", 393, Rarity.UNCOMMON, mage.cards.o.Ornithopter.class)); - cards.add(new SetCardInfo("Panic", 260, Rarity.COMMON, mage.cards.p.Panic.class)); - cards.add(new SetCardInfo("Paralyze", 185, Rarity.COMMON, mage.cards.p.Paralyze.class)); - cards.add(new SetCardInfo("Pearled Unicorn", 50, Rarity.COMMON, mage.cards.p.PearledUnicorn.class)); - cards.add(new SetCardInfo("Pentagram of the Ages", 394, Rarity.RARE, mage.cards.p.PentagramOfTheAges.class)); - cards.add(new SetCardInfo("Personal Incarnation", 51, Rarity.RARE, mage.cards.p.PersonalIncarnation.class)); - cards.add(new SetCardInfo("Pestilence", 186, Rarity.COMMON, mage.cards.p.Pestilence.class)); - cards.add(new SetCardInfo("Phantasmal Forces", 106, Rarity.UNCOMMON, mage.cards.p.PhantasmalForces.class)); - cards.add(new SetCardInfo("Phantasmal Terrain", 107, Rarity.COMMON, mage.cards.p.PhantasmalTerrain.class)); - cards.add(new SetCardInfo("Phantom Monster", 108, Rarity.UNCOMMON, mage.cards.p.PhantomMonster.class)); - cards.add(new SetCardInfo("Pikemen", 52, Rarity.COMMON, mage.cards.p.Pikemen.class)); - cards.add(new SetCardInfo("Pirate Ship", 109, Rarity.RARE, mage.cards.p.PirateShip.class)); - cards.add(new SetCardInfo("Pit Scorpion", 187, Rarity.COMMON, mage.cards.p.PitScorpion.class)); - cards.add(new SetCardInfo("Plague Rats", 188, Rarity.COMMON, mage.cards.p.PlagueRats.class)); - cards.add(new SetCardInfo("Plains", 430, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 431, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 432, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 433, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Portent", 110, Rarity.COMMON, mage.cards.p.Portent.class)); - cards.add(new SetCardInfo("Power Sink", 111, Rarity.UNCOMMON, mage.cards.p.PowerSink.class)); - cards.add(new SetCardInfo("Pox", 189, Rarity.RARE, mage.cards.p.Pox.class)); - cards.add(new SetCardInfo("Pradesh Gypsies", 317, Rarity.COMMON, mage.cards.p.PradeshGypsies.class)); - cards.add(new SetCardInfo("Primal Clay", 395, Rarity.RARE, mage.cards.p.PrimalClay.class)); - cards.add(new SetCardInfo("Primal Order", 318, Rarity.RARE, mage.cards.p.PrimalOrder.class)); - cards.add(new SetCardInfo("Primordial Ooze", 261, Rarity.UNCOMMON, mage.cards.p.PrimordialOoze.class)); - cards.add(new SetCardInfo("Prismatic Ward", 53, Rarity.COMMON, mage.cards.p.PrismaticWard.class)); - cards.add(new SetCardInfo("Prodigal Sorcerer", 112, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class)); - cards.add(new SetCardInfo("Psychic Venom", 113, Rarity.COMMON, mage.cards.p.PsychicVenom.class)); - cards.add(new SetCardInfo("Pyroblast", 262, Rarity.UNCOMMON, mage.cards.p.Pyroblast.class)); - cards.add(new SetCardInfo("Pyrotechnics", 263, Rarity.UNCOMMON, mage.cards.p.Pyrotechnics.class)); - cards.add(new SetCardInfo("Rabid Wombat", 319, Rarity.UNCOMMON, mage.cards.r.RabidWombat.class)); - cards.add(new SetCardInfo("Radjan Spirit", 320, Rarity.UNCOMMON, mage.cards.r.RadjanSpirit.class)); - cards.add(new SetCardInfo("Rag Man", 190, Rarity.RARE, mage.cards.r.RagMan.class)); - cards.add(new SetCardInfo("Raise Dead", 191, Rarity.COMMON, mage.cards.r.RaiseDead.class)); - cards.add(new SetCardInfo("Ray of Command", 114, Rarity.COMMON, mage.cards.r.RayOfCommand.class)); - cards.add(new SetCardInfo("Recall", 115, Rarity.RARE, mage.cards.r.Recall.class)); - cards.add(new SetCardInfo("Reef Pirates", 116, Rarity.COMMON, mage.cards.r.ReefPirates.class)); - cards.add(new SetCardInfo("Regeneration", 321, Rarity.COMMON, mage.cards.r.Regeneration.class)); - cards.add(new SetCardInfo("Remove Soul", 117, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); - cards.add(new SetCardInfo("Repentant Blacksmith", 54, Rarity.COMMON, mage.cards.r.RepentantBlacksmith.class)); - cards.add(new SetCardInfo("Reverse Damage", 55, Rarity.RARE, mage.cards.r.ReverseDamage.class)); - cards.add(new SetCardInfo("Righteousness", 56, Rarity.RARE, mage.cards.r.Righteousness.class)); - cards.add(new SetCardInfo("Rod of Ruin", 396, Rarity.UNCOMMON, mage.cards.r.RodOfRuin.class)); - cards.add(new SetCardInfo("Ruins of Trokair", 422, Rarity.UNCOMMON, mage.cards.r.RuinsOfTrokair.class)); - cards.add(new SetCardInfo("Sabretooth Tiger", 264, Rarity.COMMON, mage.cards.s.SabretoothTiger.class)); - cards.add(new SetCardInfo("Samite Healer", 58, Rarity.COMMON, mage.cards.s.SamiteHealer.class)); - cards.add(new SetCardInfo("Sand Silos", 423, Rarity.RARE, mage.cards.s.SandSilos.class)); - cards.add(new SetCardInfo("Scaled Wurm", 322, Rarity.COMMON, mage.cards.s.ScaledWurm.class)); - cards.add(new SetCardInfo("Scathe Zombies", 192, Rarity.COMMON, mage.cards.s.ScatheZombies.class)); - cards.add(new SetCardInfo("Scavenger Folk", 323, Rarity.COMMON, mage.cards.s.ScavengerFolk.class)); - cards.add(new SetCardInfo("Scryb Sprites", 324, Rarity.COMMON, mage.cards.s.ScrybSprites.class)); - cards.add(new SetCardInfo("Sea Serpent", 118, Rarity.COMMON, mage.cards.s.SeaSerpent.class)); - cards.add(new SetCardInfo("Sea Spirit", 119, Rarity.UNCOMMON, mage.cards.s.SeaSpirit.class)); - cards.add(new SetCardInfo("Sea Sprite", 120, Rarity.UNCOMMON, mage.cards.s.SeaSprite.class)); - cards.add(new SetCardInfo("Seasinger", 121, Rarity.UNCOMMON, mage.cards.s.Seasinger.class)); - cards.add(new SetCardInfo("Segovian Leviathan", 122, Rarity.UNCOMMON, mage.cards.s.SegovianLeviathan.class)); - cards.add(new SetCardInfo("Sengir Autocrat", 193, Rarity.RARE, mage.cards.s.SengirAutocrat.class)); - cards.add(new SetCardInfo("Seraph", 59, Rarity.RARE, mage.cards.s.Seraph.class)); - cards.add(new SetCardInfo("Serpent Generator", 397, Rarity.RARE, mage.cards.s.SerpentGenerator.class)); - cards.add(new SetCardInfo("Serra Bestiary", 60, Rarity.UNCOMMON, mage.cards.s.SerraBestiary.class)); - cards.add(new SetCardInfo("Serra Paladin", 61, Rarity.UNCOMMON, mage.cards.s.SerraPaladin.class)); - cards.add(new SetCardInfo("Shanodin Dryads", 325, Rarity.COMMON, mage.cards.s.ShanodinDryads.class)); - cards.add(new SetCardInfo("Shapeshifter", 398, Rarity.UNCOMMON, mage.cards.s.Shapeshifter.class)); - cards.add(new SetCardInfo("Shatter", 265, Rarity.COMMON, mage.cards.s.Shatter.class)); - cards.add(new SetCardInfo("Shatterstorm", 266, Rarity.UNCOMMON, mage.cards.s.Shatterstorm.class)); - cards.add(new SetCardInfo("Shield Bearer", 62, Rarity.COMMON, mage.cards.s.ShieldBearer.class)); - cards.add(new SetCardInfo("Shield Wall", 63, Rarity.COMMON, mage.cards.s.ShieldWall.class)); - cards.add(new SetCardInfo("Shivan Dragon", 267, Rarity.RARE, mage.cards.s.ShivanDragon.class)); - cards.add(new SetCardInfo("Shrink", 326, Rarity.COMMON, mage.cards.s.Shrink.class)); - cards.add(new SetCardInfo("Sibilant Spirit", 123, Rarity.RARE, mage.cards.s.SibilantSpirit.class)); - cards.add(new SetCardInfo("Skull Catapult", 399, Rarity.UNCOMMON, mage.cards.s.SkullCatapult.class)); - cards.add(new SetCardInfo("Smoke", 268, Rarity.RARE, mage.cards.s.Smoke.class)); - cards.add(new SetCardInfo("Sorceress Queen", 194, Rarity.RARE, mage.cards.s.SorceressQueen.class)); - cards.add(new SetCardInfo("Soul Barrier", 125, Rarity.COMMON, mage.cards.s.SoulBarrier.class)); - cards.add(new SetCardInfo("Soul Net", 400, Rarity.UNCOMMON, mage.cards.s.SoulNet.class)); - cards.add(new SetCardInfo("Spell Blast", 126, Rarity.COMMON, mage.cards.s.SpellBlast.class)); - cards.add(new SetCardInfo("Spirit Link", 64, Rarity.UNCOMMON, mage.cards.s.SpiritLink.class)); - cards.add(new SetCardInfo("Stampede", 327, Rarity.RARE, mage.cards.s.Stampede.class)); - cards.add(new SetCardInfo("Stasis", 127, Rarity.RARE, mage.cards.s.Stasis.class)); - cards.add(new SetCardInfo("Steal Artifact", 128, Rarity.UNCOMMON, mage.cards.s.StealArtifact.class)); - cards.add(new SetCardInfo("Stone Giant", 269, Rarity.UNCOMMON, mage.cards.s.StoneGiant.class)); - cards.add(new SetCardInfo("Stone Rain", 270, Rarity.COMMON, mage.cards.s.StoneRain.class)); - cards.add(new SetCardInfo("Stone Spirit", 271, Rarity.UNCOMMON, mage.cards.s.StoneSpirit.class)); - cards.add(new SetCardInfo("Stream of Life", 328, Rarity.COMMON, mage.cards.s.StreamOfLife.class)); - cards.add(new SetCardInfo("Stromgald Cabal", 195, Rarity.RARE, mage.cards.s.StromgaldCabal.class)); - cards.add(new SetCardInfo("Sulfurous Springs", 424, Rarity.RARE, mage.cards.s.SulfurousSprings.class)); - cards.add(new SetCardInfo("Svyelunite Temple", 425, Rarity.UNCOMMON, mage.cards.s.SvyeluniteTemple.class)); - cards.add(new SetCardInfo("Swamp", 438, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 439, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 440, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 441, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sylvan Library", 329, Rarity.RARE, mage.cards.s.SylvanLibrary.class)); - cards.add(new SetCardInfo("Tarpan", 330, Rarity.COMMON, mage.cards.t.Tarpan.class)); - cards.add(new SetCardInfo("Tawnos's Weaponry", 401, Rarity.UNCOMMON, mage.cards.t.TawnossWeaponry.class)); - cards.add(new SetCardInfo("Terror", 196, Rarity.COMMON, mage.cards.t.Terror.class)); - cards.add(new SetCardInfo("The Brute", 272, Rarity.COMMON, mage.cards.t.TheBrute.class)); - cards.add(new SetCardInfo("The Hive", 402, Rarity.RARE, mage.cards.t.TheHive.class)); - cards.add(new SetCardInfo("The Wretched", 197, Rarity.RARE, mage.cards.t.TheWretched.class)); - cards.add(new SetCardInfo("Thicket Basilisk", 331, Rarity.UNCOMMON, mage.cards.t.ThicketBasilisk.class)); - cards.add(new SetCardInfo("Throne of Bone", 403, Rarity.UNCOMMON, mage.cards.t.ThroneOfBone.class)); - cards.add(new SetCardInfo("Thrull Retainer", 198, Rarity.UNCOMMON, mage.cards.t.ThrullRetainer.class)); - cards.add(new SetCardInfo("Time Bomb", 404, Rarity.RARE, mage.cards.t.TimeBomb.class)); - cards.add(new SetCardInfo("Time Elemental", 129, Rarity.RARE, mage.cards.t.TimeElemental.class)); - cards.add(new SetCardInfo("Titania's Song", 332, Rarity.RARE, mage.cards.t.TitaniasSong.class)); - cards.add(new SetCardInfo("Torture", 199, Rarity.COMMON, mage.cards.t.Torture.class)); - cards.add(new SetCardInfo("Touch of Death", 200, Rarity.COMMON, mage.cards.t.TouchOfDeath.class)); - cards.add(new SetCardInfo("Tranquility", 333, Rarity.COMMON, mage.cards.t.Tranquility.class)); - cards.add(new SetCardInfo("Truce", 65, Rarity.RARE, mage.cards.t.Truce.class)); - cards.add(new SetCardInfo("Tsunami", 334, Rarity.UNCOMMON, mage.cards.t.Tsunami.class)); - cards.add(new SetCardInfo("Tundra Wolves", 66, Rarity.COMMON, mage.cards.t.TundraWolves.class)); - cards.add(new SetCardInfo("Twiddle", 130, Rarity.COMMON, mage.cards.t.Twiddle.class)); - cards.add(new SetCardInfo("Underground River", 426, Rarity.RARE, mage.cards.u.UndergroundRiver.class)); - cards.add(new SetCardInfo("Unholy Strength", 201, Rarity.COMMON, mage.cards.u.UnholyStrength.class)); - cards.add(new SetCardInfo("Unstable Mutation", 131, Rarity.COMMON, mage.cards.u.UnstableMutation.class)); - cards.add(new SetCardInfo("Unsummon", 132, Rarity.COMMON, mage.cards.u.Unsummon.class)); - cards.add(new SetCardInfo("Untamed Wilds", 335, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); - cards.add(new SetCardInfo("Updraft", 133, Rarity.COMMON, mage.cards.u.Updraft.class)); - cards.add(new SetCardInfo("Urza's Avenger", 405, Rarity.RARE, mage.cards.u.UrzasAvenger.class)); - cards.add(new SetCardInfo("Urza's Bauble", 406, Rarity.UNCOMMON, mage.cards.u.UrzasBauble.class)); - cards.add(new SetCardInfo("Urza's Mine", 427, Rarity.COMMON, mage.cards.u.UrzasMine.class)); - cards.add(new SetCardInfo("Urza's Power Plant", 428, Rarity.COMMON, mage.cards.u.UrzasPowerPlant.class)); - cards.add(new SetCardInfo("Urza's Tower", 429, Rarity.COMMON, mage.cards.u.UrzasTower.class)); - cards.add(new SetCardInfo("Vampire Bats", 202, Rarity.COMMON, mage.cards.v.VampireBats.class)); - cards.add(new SetCardInfo("Venom", 336, Rarity.COMMON, mage.cards.v.Venom.class)); - cards.add(new SetCardInfo("Verduran Enchantress", 337, Rarity.RARE, mage.cards.v.VerduranEnchantress.class)); - cards.add(new SetCardInfo("Vodalian Soldiers", 134, Rarity.COMMON, mage.cards.v.VodalianSoldiers.class)); - cards.add(new SetCardInfo("Wall of Air", 135, Rarity.UNCOMMON, mage.cards.w.WallOfAir.class)); - cards.add(new SetCardInfo("Wall of Bone", 203, Rarity.UNCOMMON, mage.cards.w.WallOfBone.class)); - cards.add(new SetCardInfo("Wall of Brambles", 338, Rarity.UNCOMMON, mage.cards.w.WallOfBrambles.class)); - cards.add(new SetCardInfo("Wall of Fire", 273, Rarity.UNCOMMON, mage.cards.w.WallOfFire.class)); - cards.add(new SetCardInfo("Wall of Spears", 407, Rarity.COMMON, mage.cards.w.WallOfSpears.class)); - cards.add(new SetCardInfo("Wall of Stone", 274, Rarity.UNCOMMON, mage.cards.w.WallOfStone.class)); - cards.add(new SetCardInfo("Wall of Swords", 67, Rarity.UNCOMMON, mage.cards.w.WallOfSwords.class)); - cards.add(new SetCardInfo("Wanderlust", 339, Rarity.UNCOMMON, mage.cards.w.Wanderlust.class)); - cards.add(new SetCardInfo("War Mammoth", 340, Rarity.COMMON, mage.cards.w.WarMammoth.class)); - cards.add(new SetCardInfo("Warp Artifact", 204, Rarity.RARE, mage.cards.w.WarpArtifact.class)); - cards.add(new SetCardInfo("Weakness", 205, Rarity.COMMON, mage.cards.w.Weakness.class)); - cards.add(new SetCardInfo("Whirling Dervish", 341, Rarity.UNCOMMON, mage.cards.w.WhirlingDervish.class)); - cards.add(new SetCardInfo("White Knight", 68, Rarity.UNCOMMON, mage.cards.w.WhiteKnight.class)); - cards.add(new SetCardInfo("Wild Growth", 342, Rarity.COMMON, mage.cards.w.WildGrowth.class)); - cards.add(new SetCardInfo("Wind Spirit", 136, Rarity.UNCOMMON, mage.cards.w.WindSpirit.class)); - cards.add(new SetCardInfo("Winds of Change", 275, Rarity.RARE, mage.cards.w.WindsOfChange.class)); - cards.add(new SetCardInfo("Winter Blast", 343, Rarity.UNCOMMON, mage.cards.w.WinterBlast.class)); - cards.add(new SetCardInfo("Winter Orb", 408, Rarity.RARE, mage.cards.w.WinterOrb.class)); - cards.add(new SetCardInfo("Wolverine Pack", 344, Rarity.UNCOMMON, mage.cards.w.WolverinePack.class)); - cards.add(new SetCardInfo("Wooden Sphere", 409, Rarity.UNCOMMON, mage.cards.w.WoodenSphere.class)); - cards.add(new SetCardInfo("Word of Blasting", 276, Rarity.UNCOMMON, mage.cards.w.WordOfBlasting.class)); - cards.add(new SetCardInfo("Wrath of God", 69, Rarity.RARE, mage.cards.w.WrathOfGod.class)); - cards.add(new SetCardInfo("Wyluli Wolf", 345, Rarity.RARE, mage.cards.w.WyluliWolf.class)); - cards.add(new SetCardInfo("Xenic Poltergeist", 206, Rarity.RARE, mage.cards.x.XenicPoltergeist.class)); - cards.add(new SetCardInfo("Zephyr Falcon", 137, Rarity.COMMON, mage.cards.z.ZephyrFalcon.class)); - cards.add(new SetCardInfo("Zombie Master", 207, Rarity.RARE, mage.cards.z.ZombieMaster.class)); - cards.add(new SetCardInfo("Zur's Weirding", 138, Rarity.RARE, mage.cards.z.ZursWeirding.class)); - } -} +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +public final class FifthEdition extends ExpansionSet { + + private static final FifthEdition instance = new FifthEdition(); + + public static FifthEdition getInstance() { + return instance; + } + + private FifthEdition() { + super("Fifth Edition", "5ED", ExpansionSet.buildDate(1997, 3, 1), SetType.CORE); + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + + cards.add(new SetCardInfo("Abbey Gargoyles", 1, Rarity.UNCOMMON, mage.cards.a.AbbeyGargoyles.class)); + cards.add(new SetCardInfo("Abyssal Specter", 139, Rarity.UNCOMMON, mage.cards.a.AbyssalSpecter.class)); + cards.add(new SetCardInfo("Adarkar Wastes", 410, Rarity.RARE, mage.cards.a.AdarkarWastes.class)); + cards.add(new SetCardInfo("Aether Storm", 70, Rarity.UNCOMMON, mage.cards.a.AetherStorm.class)); + cards.add(new SetCardInfo("Air Elemental", 71, Rarity.UNCOMMON, mage.cards.a.AirElemental.class)); + cards.add(new SetCardInfo("Akron Legionnaire", 2, Rarity.RARE, mage.cards.a.AkronLegionnaire.class)); + cards.add(new SetCardInfo("Alabaster Potion", 3, Rarity.COMMON, mage.cards.a.AlabasterPotion.class)); + cards.add(new SetCardInfo("Aladdin's Ring", 346, Rarity.RARE, mage.cards.a.AladdinsRing.class)); + cards.add(new SetCardInfo("Ambush Party", 208, Rarity.COMMON, mage.cards.a.AmbushParty.class)); + cards.add(new SetCardInfo("Amulet of Kroog", 347, Rarity.COMMON, mage.cards.a.AmuletOfKroog.class)); + cards.add(new SetCardInfo("An-Havva Constable", 277, Rarity.RARE, mage.cards.a.AnHavvaConstable.class)); + cards.add(new SetCardInfo("Angry Mob", 4, Rarity.UNCOMMON, mage.cards.a.AngryMob.class)); + cards.add(new SetCardInfo("Animate Dead", 140, Rarity.UNCOMMON, mage.cards.a.AnimateDead.class)); + cards.add(new SetCardInfo("Animate Wall", 5, Rarity.RARE, mage.cards.a.AnimateWall.class)); + cards.add(new SetCardInfo("Ankh of Mishra", 348, Rarity.RARE, mage.cards.a.AnkhOfMishra.class)); + cards.add(new SetCardInfo("Anti-Magic Aura", 72, Rarity.UNCOMMON, mage.cards.a.AntiMagicAura.class)); + cards.add(new SetCardInfo("Arenson's Aura", 6, Rarity.UNCOMMON, mage.cards.a.ArensonsAura.class)); + cards.add(new SetCardInfo("Armageddon", 7, Rarity.RARE, mage.cards.a.Armageddon.class)); + cards.add(new SetCardInfo("Armor of Faith", 8, Rarity.COMMON, mage.cards.a.ArmorOfFaith.class)); + cards.add(new SetCardInfo("Ashes to Ashes", 141, Rarity.UNCOMMON, mage.cards.a.AshesToAshes.class)); + cards.add(new SetCardInfo("Ashnod's Altar", 349, Rarity.UNCOMMON, mage.cards.a.AshnodsAltar.class)); + cards.add(new SetCardInfo("Ashnod's Transmogrant", 350, Rarity.COMMON, mage.cards.a.AshnodsTransmogrant.class)); + cards.add(new SetCardInfo("Aspect of Wolf", 278, Rarity.RARE, mage.cards.a.AspectOfWolf.class)); + cards.add(new SetCardInfo("Atog", 209, Rarity.UNCOMMON, mage.cards.a.Atog.class)); + cards.add(new SetCardInfo("Aurochs", 279, Rarity.COMMON, mage.cards.a.Aurochs.class)); + cards.add(new SetCardInfo("Aysen Bureaucrats", 9, Rarity.COMMON, mage.cards.a.AysenBureaucrats.class)); + cards.add(new SetCardInfo("Azure Drake", 73, Rarity.UNCOMMON, mage.cards.a.AzureDrake.class)); + cards.add(new SetCardInfo("Bad Moon", 142, Rarity.RARE, mage.cards.b.BadMoon.class)); + cards.add(new SetCardInfo("Ball Lightning", 210, Rarity.RARE, mage.cards.b.BallLightning.class)); + cards.add(new SetCardInfo("Barbed Sextant", 351, Rarity.COMMON, mage.cards.b.BarbedSextant.class)); + cards.add(new SetCardInfo("Barl's Cage", 352, Rarity.RARE, mage.cards.b.BarlsCage.class)); + cards.add(new SetCardInfo("Battering Ram", 353, Rarity.COMMON, mage.cards.b.BatteringRam.class)); + cards.add(new SetCardInfo("Benalish Hero", 10, Rarity.COMMON, mage.cards.b.BenalishHero.class)); + cards.add(new SetCardInfo("Binding Grasp", 74, Rarity.UNCOMMON, mage.cards.b.BindingGrasp.class)); + cards.add(new SetCardInfo("Bird Maiden", 211, Rarity.COMMON, mage.cards.b.BirdMaiden.class)); + cards.add(new SetCardInfo("Birds of Paradise", 280, Rarity.RARE, mage.cards.b.BirdsOfParadise.class)); + cards.add(new SetCardInfo("Black Knight", 143, Rarity.UNCOMMON, mage.cards.b.BlackKnight.class)); + cards.add(new SetCardInfo("Blessed Wine", 11, Rarity.COMMON, mage.cards.b.BlessedWine.class)); + cards.add(new SetCardInfo("Blight", 144, Rarity.UNCOMMON, mage.cards.b.Blight.class)); + cards.add(new SetCardInfo("Blinking Spirit", 12, Rarity.RARE, mage.cards.b.BlinkingSpirit.class)); + cards.add(new SetCardInfo("Blood Lust", 212, Rarity.COMMON, mage.cards.b.BloodLust.class)); + cards.add(new SetCardInfo("Bog Imp", 145, Rarity.COMMON, mage.cards.b.BogImp.class)); + cards.add(new SetCardInfo("Bog Rats", 146, Rarity.COMMON, mage.cards.b.BogRats.class)); + cards.add(new SetCardInfo("Bog Wraith", 147, Rarity.UNCOMMON, mage.cards.b.BogWraith.class)); + cards.add(new SetCardInfo("Boomerang", 75, Rarity.COMMON, mage.cards.b.Boomerang.class)); + cards.add(new SetCardInfo("Bottle of Suleiman", 354, Rarity.RARE, mage.cards.b.BottleOfSuleiman.class)); + cards.add(new SetCardInfo("Bottomless Vault", 411, Rarity.RARE, mage.cards.b.BottomlessVault.class)); + cards.add(new SetCardInfo("Brainstorm", 76, Rarity.COMMON, mage.cards.b.Brainstorm.class)); + cards.add(new SetCardInfo("Brainwash", 13, Rarity.COMMON, mage.cards.b.Brainwash.class)); + cards.add(new SetCardInfo("Brassclaw Orcs", 213, Rarity.COMMON, mage.cards.b.BrassclawOrcs.class)); + cards.add(new SetCardInfo("Breeding Pit", 148, Rarity.UNCOMMON, mage.cards.b.BreedingPit.class)); + cards.add(new SetCardInfo("Broken Visage", 149, Rarity.RARE, mage.cards.b.BrokenVisage.class)); + cards.add(new SetCardInfo("Brothers of Fire", 214, Rarity.COMMON, mage.cards.b.BrothersOfFire.class)); + cards.add(new SetCardInfo("Brushland", 412, Rarity.RARE, mage.cards.b.Brushland.class)); + cards.add(new SetCardInfo("Carapace", 281, Rarity.COMMON, mage.cards.c.Carapace.class)); + cards.add(new SetCardInfo("Caribou Range", 14, Rarity.RARE, mage.cards.c.CaribouRange.class)); + cards.add(new SetCardInfo("Carrion Ants", 150, Rarity.UNCOMMON, mage.cards.c.CarrionAnts.class)); + cards.add(new SetCardInfo("Castle", 15, Rarity.UNCOMMON, mage.cards.c.Castle.class)); + cards.add(new SetCardInfo("Cat Warriors", 282, Rarity.COMMON, mage.cards.c.CatWarriors.class)); + cards.add(new SetCardInfo("Cave People", 215, Rarity.UNCOMMON, mage.cards.c.CavePeople.class)); + cards.add(new SetCardInfo("Chub Toad", 283, Rarity.COMMON, mage.cards.c.ChubToad.class)); + cards.add(new SetCardInfo("Circle of Protection: Artifacts", 16, Rarity.UNCOMMON, mage.cards.c.CircleOfProtectionArtifacts.class)); + cards.add(new SetCardInfo("Circle of Protection: Black", 17, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlack.class)); + cards.add(new SetCardInfo("Circle of Protection: Blue", 18, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlue.class)); + cards.add(new SetCardInfo("Circle of Protection: Green", 19, Rarity.COMMON, mage.cards.c.CircleOfProtectionGreen.class)); + cards.add(new SetCardInfo("Circle of Protection: Red", 20, Rarity.COMMON, mage.cards.c.CircleOfProtectionRed.class)); + cards.add(new SetCardInfo("Circle of Protection: White", 21, Rarity.COMMON, mage.cards.c.CircleOfProtectionWhite.class)); + cards.add(new SetCardInfo("City of Brass", 413, Rarity.RARE, mage.cards.c.CityOfBrass.class)); + cards.add(new SetCardInfo("Clay Statue", 355, Rarity.COMMON, mage.cards.c.ClayStatue.class)); + cards.add(new SetCardInfo("Cloak of Confusion", 151, Rarity.COMMON, mage.cards.c.CloakOfConfusion.class)); + cards.add(new SetCardInfo("Clockwork Beast", 356, Rarity.RARE, mage.cards.c.ClockworkBeast.class)); + cards.add(new SetCardInfo("Clockwork Steed", 357, Rarity.UNCOMMON, mage.cards.c.ClockworkSteed.class)); + cards.add(new SetCardInfo("Cockatrice", 284, Rarity.RARE, mage.cards.c.Cockatrice.class)); + cards.add(new SetCardInfo("Colossus of Sardia", 358, Rarity.RARE, mage.cards.c.ColossusOfSardia.class)); + cards.add(new SetCardInfo("Conquer", 216, Rarity.UNCOMMON, mage.cards.c.Conquer.class)); + cards.add(new SetCardInfo("Coral Helm", 359, Rarity.RARE, mage.cards.c.CoralHelm.class)); + cards.add(new SetCardInfo("Counterspell", 77, Rarity.COMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Craw Giant", 285, Rarity.UNCOMMON, mage.cards.c.CrawGiant.class)); + cards.add(new SetCardInfo("Craw Wurm", 286, Rarity.COMMON, mage.cards.c.CrawWurm.class)); + cards.add(new SetCardInfo("Crimson Manticore", 217, Rarity.RARE, mage.cards.c.CrimsonManticore.class)); + cards.add(new SetCardInfo("Crown of the Ages", 360, Rarity.RARE, mage.cards.c.CrownOfTheAges.class)); + cards.add(new SetCardInfo("Crumble", 287, Rarity.UNCOMMON, mage.cards.c.Crumble.class)); + cards.add(new SetCardInfo("Crusade", 22, Rarity.RARE, mage.cards.c.Crusade.class)); + cards.add(new SetCardInfo("Crystal Rod", 361, Rarity.UNCOMMON, mage.cards.c.CrystalRod.class)); + cards.add(new SetCardInfo("Cursed Land", 152, Rarity.UNCOMMON, mage.cards.c.CursedLand.class)); + cards.add(new SetCardInfo("D'Avenant Archer", 23, Rarity.COMMON, mage.cards.d.DAvenantArcher.class)); + cards.add(new SetCardInfo("Dance of Many", 78, Rarity.RARE, mage.cards.d.DanceOfMany.class)); + cards.add(new SetCardInfo("Dancing Scimitar", 362, Rarity.RARE, mage.cards.d.DancingScimitar.class)); + cards.add(new SetCardInfo("Dandan", 79, Rarity.COMMON, mage.cards.d.Dandan.class)); + cards.add(new SetCardInfo("Dark Maze", 80, Rarity.COMMON, mage.cards.d.DarkMaze.class)); + cards.add(new SetCardInfo("Dark Ritual", 153, Rarity.COMMON, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Death Speakers", 24, Rarity.COMMON, mage.cards.d.DeathSpeakers.class)); + cards.add(new SetCardInfo("Death Ward", 25, Rarity.COMMON, mage.cards.d.DeathWard.class)); + cards.add(new SetCardInfo("Deathgrip", 154, Rarity.UNCOMMON, mage.cards.d.Deathgrip.class)); + cards.add(new SetCardInfo("Deflection", 81, Rarity.RARE, mage.cards.d.Deflection.class)); + cards.add(new SetCardInfo("Derelor", 155, Rarity.RARE, mage.cards.d.Derelor.class)); + cards.add(new SetCardInfo("Desert Twister", 288, Rarity.UNCOMMON, mage.cards.d.DesertTwister.class)); + cards.add(new SetCardInfo("Detonate", 218, Rarity.UNCOMMON, mage.cards.d.Detonate.class)); + cards.add(new SetCardInfo("Diabolic Machine", 363, Rarity.UNCOMMON, mage.cards.d.DiabolicMachine.class)); + cards.add(new SetCardInfo("Dingus Egg", 364, Rarity.RARE, mage.cards.d.DingusEgg.class)); + cards.add(new SetCardInfo("Disenchant", 26, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Disintegrate", 219, Rarity.COMMON, mage.cards.d.Disintegrate.class)); + cards.add(new SetCardInfo("Disrupting Scepter", 365, Rarity.RARE, mage.cards.d.DisruptingScepter.class)); + cards.add(new SetCardInfo("Divine Offering", 27, Rarity.COMMON, mage.cards.d.DivineOffering.class)); + cards.add(new SetCardInfo("Divine Transformation", 28, Rarity.UNCOMMON, mage.cards.d.DivineTransformation.class)); + cards.add(new SetCardInfo("Dragon Engine", 366, Rarity.RARE, mage.cards.d.DragonEngine.class)); + cards.add(new SetCardInfo("Drain Life", 156, Rarity.COMMON, mage.cards.d.DrainLife.class)); + cards.add(new SetCardInfo("Drain Power", 82, Rarity.RARE, mage.cards.d.DrainPower.class)); + cards.add(new SetCardInfo("Drudge Skeletons", 157, Rarity.COMMON, mage.cards.d.DrudgeSkeletons.class)); + cards.add(new SetCardInfo("Durkwood Boars", 289, Rarity.COMMON, mage.cards.d.DurkwoodBoars.class)); + cards.add(new SetCardInfo("Dust to Dust", 29, Rarity.UNCOMMON, mage.cards.d.DustToDust.class)); + cards.add(new SetCardInfo("Dwarven Catapult", 220, Rarity.UNCOMMON, mage.cards.d.DwarvenCatapult.class)); + cards.add(new SetCardInfo("Dwarven Hold", 414, Rarity.RARE, mage.cards.d.DwarvenHold.class)); + cards.add(new SetCardInfo("Dwarven Ruins", 415, Rarity.UNCOMMON, mage.cards.d.DwarvenRuins.class)); + cards.add(new SetCardInfo("Dwarven Soldier", 221, Rarity.COMMON, mage.cards.d.DwarvenSoldier.class)); + cards.add(new SetCardInfo("Dwarven Warriors", 222, Rarity.COMMON, mage.cards.d.DwarvenWarriors.class)); + cards.add(new SetCardInfo("Earthquake", 223, Rarity.RARE, mage.cards.e.Earthquake.class)); + cards.add(new SetCardInfo("Ebon Stronghold", 416, Rarity.UNCOMMON, mage.cards.e.EbonStronghold.class)); + cards.add(new SetCardInfo("Elder Druid", 290, Rarity.RARE, mage.cards.e.ElderDruid.class)); + cards.add(new SetCardInfo("Elkin Bottle", 367, Rarity.RARE, mage.cards.e.ElkinBottle.class)); + cards.add(new SetCardInfo("Elven Riders", 291, Rarity.UNCOMMON, mage.cards.e.ElvenRiders.class)); + cards.add(new SetCardInfo("Elvish Archers", 292, Rarity.RARE, mage.cards.e.ElvishArchers.class)); + cards.add(new SetCardInfo("Energy Flux", 83, Rarity.UNCOMMON, mage.cards.e.EnergyFlux.class)); + cards.add(new SetCardInfo("Enervate", 84, Rarity.COMMON, mage.cards.e.Enervate.class)); + cards.add(new SetCardInfo("Erg Raiders", 158, Rarity.COMMON, mage.cards.e.ErgRaiders.class)); + cards.add(new SetCardInfo("Errantry", 224, Rarity.COMMON, mage.cards.e.Errantry.class)); + cards.add(new SetCardInfo("Eternal Warrior", 225, Rarity.COMMON, mage.cards.e.EternalWarrior.class)); + cards.add(new SetCardInfo("Evil Eye of Orms-by-Gore", 159, Rarity.UNCOMMON, mage.cards.e.EvilEyeOfOrmsByGore.class)); + cards.add(new SetCardInfo("Evil Presence", 160, Rarity.UNCOMMON, mage.cards.e.EvilPresence.class)); + cards.add(new SetCardInfo("Eye for an Eye", 30, Rarity.RARE, mage.cards.e.EyeForAnEye.class)); + cards.add(new SetCardInfo("Fallen Angel", 161, Rarity.UNCOMMON, mage.cards.f.FallenAngel.class)); + cards.add(new SetCardInfo("Fear", 162, Rarity.COMMON, mage.cards.f.Fear.class)); + cards.add(new SetCardInfo("Feedback", 85, Rarity.UNCOMMON, mage.cards.f.Feedback.class)); + cards.add(new SetCardInfo("Feldon's Cane", 368, Rarity.UNCOMMON, mage.cards.f.FeldonsCane.class)); + cards.add(new SetCardInfo("Fellwar Stone", 369, Rarity.UNCOMMON, mage.cards.f.FellwarStone.class)); + cards.add(new SetCardInfo("Feroz's Ban", 370, Rarity.RARE, mage.cards.f.FerozsBan.class)); + cards.add(new SetCardInfo("Fire Drake", 226, Rarity.UNCOMMON, mage.cards.f.FireDrake.class)); + cards.add(new SetCardInfo("Fireball", 227, Rarity.COMMON, mage.cards.f.Fireball.class)); + cards.add(new SetCardInfo("Firebreathing", 228, Rarity.COMMON, mage.cards.f.Firebreathing.class)); + cards.add(new SetCardInfo("Flame Spirit", 229, Rarity.UNCOMMON, mage.cards.f.FlameSpirit.class)); + cards.add(new SetCardInfo("Flare", 230, Rarity.COMMON, mage.cards.f.Flare.class)); + cards.add(new SetCardInfo("Flashfires", 231, Rarity.UNCOMMON, mage.cards.f.Flashfires.class)); + cards.add(new SetCardInfo("Flight", 86, Rarity.COMMON, mage.cards.f.Flight.class)); + cards.add(new SetCardInfo("Flood", 87, Rarity.COMMON, mage.cards.f.Flood.class)); + cards.add(new SetCardInfo("Flying Carpet", 371, Rarity.RARE, mage.cards.f.FlyingCarpet.class)); + cards.add(new SetCardInfo("Fog", 293, Rarity.COMMON, mage.cards.f.Fog.class)); + cards.add(new SetCardInfo("Force of Nature", 294, Rarity.RARE, mage.cards.f.ForceOfNature.class)); + cards.add(new SetCardInfo("Force Spike", 88, Rarity.COMMON, mage.cards.f.ForceSpike.class)); + cards.add(new SetCardInfo("Forest", 446, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 447, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 448, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 449, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forget", 89, Rarity.RARE, mage.cards.f.Forget.class)); + cards.add(new SetCardInfo("Fountain of Youth", 372, Rarity.UNCOMMON, mage.cards.f.FountainOfYouth.class)); + cards.add(new SetCardInfo("Foxfire", 295, Rarity.COMMON, mage.cards.f.Foxfire.class)); + cards.add(new SetCardInfo("Frozen Shade", 163, Rarity.COMMON, mage.cards.f.FrozenShade.class)); + cards.add(new SetCardInfo("Funeral March", 164, Rarity.COMMON, mage.cards.f.FuneralMarch.class)); + cards.add(new SetCardInfo("Fungusaur", 296, Rarity.RARE, mage.cards.f.Fungusaur.class)); + cards.add(new SetCardInfo("Fyndhorn Elder", 297, Rarity.UNCOMMON, mage.cards.f.FyndhornElder.class)); + cards.add(new SetCardInfo("Game of Chaos", 232, Rarity.RARE, mage.cards.g.GameOfChaos.class)); + cards.add(new SetCardInfo("Gaseous Form", 90, Rarity.COMMON, mage.cards.g.GaseousForm.class)); + cards.add(new SetCardInfo("Gauntlets of Chaos", 373, Rarity.RARE, mage.cards.g.GauntletsOfChaos.class)); + cards.add(new SetCardInfo("Ghazban Ogre", 298, Rarity.COMMON, mage.cards.g.GhazbanOgre.class)); + cards.add(new SetCardInfo("Giant Growth", 299, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); + cards.add(new SetCardInfo("Giant Spider", 300, Rarity.COMMON, mage.cards.g.GiantSpider.class)); + cards.add(new SetCardInfo("Giant Strength", 233, Rarity.COMMON, mage.cards.g.GiantStrength.class)); + cards.add(new SetCardInfo("Glacial Wall", 91, Rarity.UNCOMMON, mage.cards.g.GlacialWall.class)); + cards.add(new SetCardInfo("Glasses of Urza", 374, Rarity.UNCOMMON, mage.cards.g.GlassesOfUrza.class)); + cards.add(new SetCardInfo("Gloom", 165, Rarity.UNCOMMON, mage.cards.g.Gloom.class)); + cards.add(new SetCardInfo("Goblin Digging Team", 234, Rarity.COMMON, mage.cards.g.GoblinDiggingTeam.class)); + cards.add(new SetCardInfo("Goblin Hero", 235, Rarity.COMMON, mage.cards.g.GoblinHero.class)); + cards.add(new SetCardInfo("Goblin King", 236, Rarity.RARE, mage.cards.g.GoblinKing.class)); + cards.add(new SetCardInfo("Goblin War Drums", 237, Rarity.COMMON, mage.cards.g.GoblinWarDrums.class)); + cards.add(new SetCardInfo("Goblin Warrens", 238, Rarity.RARE, mage.cards.g.GoblinWarrens.class)); + cards.add(new SetCardInfo("Grapeshot Catapult", 375, Rarity.COMMON, mage.cards.g.GrapeshotCatapult.class)); + cards.add(new SetCardInfo("Greater Realm of Preservation", 31, Rarity.UNCOMMON, mage.cards.g.GreaterRealmOfPreservation.class)); + cards.add(new SetCardInfo("Greater Werewolf", 166, Rarity.UNCOMMON, mage.cards.g.GreaterWerewolf.class)); + cards.add(new SetCardInfo("Grizzly Bears", 301, Rarity.COMMON, mage.cards.g.GrizzlyBears.class)); + cards.add(new SetCardInfo("Havenwood Battleground", 417, Rarity.UNCOMMON, mage.cards.h.HavenwoodBattleground.class)); + cards.add(new SetCardInfo("Heal", 32, Rarity.COMMON, mage.cards.h.Heal.class)); + cards.add(new SetCardInfo("Healing Salve", 33, Rarity.COMMON, mage.cards.h.HealingSalve.class)); + cards.add(new SetCardInfo("Hecatomb", 167, Rarity.RARE, mage.cards.h.Hecatomb.class)); + cards.add(new SetCardInfo("Helm of Chatzuk", 376, Rarity.RARE, mage.cards.h.HelmOfChatzuk.class)); + cards.add(new SetCardInfo("Hill Giant", 239, Rarity.COMMON, mage.cards.h.HillGiant.class)); + cards.add(new SetCardInfo("Hollow Trees", 418, Rarity.RARE, mage.cards.h.HollowTrees.class)); + cards.add(new SetCardInfo("Holy Strength", 35, Rarity.COMMON, mage.cards.h.HolyStrength.class)); + cards.add(new SetCardInfo("Homarid Warrior", 92, Rarity.COMMON, mage.cards.h.HomaridWarrior.class)); + cards.add(new SetCardInfo("Howl from Beyond", 168, Rarity.COMMON, mage.cards.h.HowlFromBeyond.class)); + cards.add(new SetCardInfo("Howling Mine", 377, Rarity.RARE, mage.cards.h.HowlingMine.class)); + cards.add(new SetCardInfo("Hungry Mist", 302, Rarity.COMMON, mage.cards.h.HungryMist.class)); + cards.add(new SetCardInfo("Hurkyl's Recall", 93, Rarity.RARE, mage.cards.h.HurkylsRecall.class)); + cards.add(new SetCardInfo("Hurloon Minotaur", 240, Rarity.COMMON, mage.cards.h.HurloonMinotaur.class)); + cards.add(new SetCardInfo("Hurricane", 303, Rarity.UNCOMMON, mage.cards.h.Hurricane.class)); + cards.add(new SetCardInfo("Hydroblast", 94, Rarity.UNCOMMON, mage.cards.h.Hydroblast.class)); + cards.add(new SetCardInfo("Icatian Phalanx", 36, Rarity.UNCOMMON, mage.cards.i.IcatianPhalanx.class)); + cards.add(new SetCardInfo("Icatian Scout", 37, Rarity.COMMON, mage.cards.i.IcatianScout.class)); + cards.add(new SetCardInfo("Icatian Store", 419, Rarity.RARE, mage.cards.i.IcatianStore.class)); + cards.add(new SetCardInfo("Icatian Town", 38, Rarity.RARE, mage.cards.i.IcatianTown.class)); + cards.add(new SetCardInfo("Ice Floe", 420, Rarity.UNCOMMON, mage.cards.i.IceFloe.class)); + cards.add(new SetCardInfo("Imposing Visage", 241, Rarity.COMMON, mage.cards.i.ImposingVisage.class)); + cards.add(new SetCardInfo("Incinerate", 242, Rarity.COMMON, mage.cards.i.Incinerate.class)); + cards.add(new SetCardInfo("Inferno", 243, Rarity.RARE, mage.cards.i.Inferno.class)); + cards.add(new SetCardInfo("Infinite Hourglass", 378, Rarity.RARE, mage.cards.i.InfiniteHourglass.class)); + cards.add(new SetCardInfo("Initiates of the Ebon Hand", 169, Rarity.COMMON, mage.cards.i.InitiatesOfTheEbonHand.class)); + cards.add(new SetCardInfo("Instill Energy", 304, Rarity.UNCOMMON, mage.cards.i.InstillEnergy.class)); + cards.add(new SetCardInfo("Iron Star", 379, Rarity.UNCOMMON, mage.cards.i.IronStar.class)); + cards.add(new SetCardInfo("Ironclaw Curse", 244, Rarity.RARE, mage.cards.i.IronclawCurse.class)); + cards.add(new SetCardInfo("Ironclaw Orcs", 245, Rarity.COMMON, mage.cards.i.IronclawOrcs.class)); + cards.add(new SetCardInfo("Ironroot Treefolk", 305, Rarity.COMMON, mage.cards.i.IronrootTreefolk.class)); + cards.add(new SetCardInfo("Island Sanctuary", 39, Rarity.RARE, mage.cards.i.IslandSanctuary.class)); + cards.add(new SetCardInfo("Island", 434, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 435, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 436, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 437, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ivory Cup", 380, Rarity.UNCOMMON, mage.cards.i.IvoryCup.class)); + cards.add(new SetCardInfo("Ivory Guardians", 40, Rarity.UNCOMMON, mage.cards.i.IvoryGuardians.class)); + cards.add(new SetCardInfo("Jade Monolith", 381, Rarity.RARE, mage.cards.j.JadeMonolith.class)); + cards.add(new SetCardInfo("Jalum Tome", 382, Rarity.RARE, mage.cards.j.JalumTome.class)); + cards.add(new SetCardInfo("Jandor's Saddlebags", 383, Rarity.RARE, mage.cards.j.JandorsSaddlebags.class)); + cards.add(new SetCardInfo("Jayemdae Tome", 384, Rarity.RARE, mage.cards.j.JayemdaeTome.class)); + cards.add(new SetCardInfo("Jester's Cap", 385, Rarity.RARE, mage.cards.j.JestersCap.class)); + cards.add(new SetCardInfo("Johtull Wurm", 306, Rarity.UNCOMMON, mage.cards.j.JohtullWurm.class)); + cards.add(new SetCardInfo("Jokulhaups", 246, Rarity.RARE, mage.cards.j.Jokulhaups.class)); + cards.add(new SetCardInfo("Joven's Tools", 386, Rarity.UNCOMMON, mage.cards.j.JovensTools.class)); + cards.add(new SetCardInfo("Justice", 41, Rarity.UNCOMMON, mage.cards.j.Justice.class)); + cards.add(new SetCardInfo("Juxtapose", 95, Rarity.RARE, mage.cards.j.Juxtapose.class)); + cards.add(new SetCardInfo("Karma", 42, Rarity.UNCOMMON, mage.cards.k.Karma.class)); + cards.add(new SetCardInfo("Karplusan Forest", 421, Rarity.RARE, mage.cards.k.KarplusanForest.class)); + cards.add(new SetCardInfo("Keldon Warlord", 247, Rarity.UNCOMMON, mage.cards.k.KeldonWarlord.class)); + cards.add(new SetCardInfo("Killer Bees", 307, Rarity.UNCOMMON, mage.cards.k.KillerBees.class)); + cards.add(new SetCardInfo("Kismet", 43, Rarity.UNCOMMON, mage.cards.k.Kismet.class)); + cards.add(new SetCardInfo("Kjeldoran Dead", 170, Rarity.COMMON, mage.cards.k.KjeldoranDead.class)); + cards.add(new SetCardInfo("Kjeldoran Royal Guard", 44, Rarity.RARE, mage.cards.k.KjeldoranRoyalGuard.class)); + cards.add(new SetCardInfo("Kjeldoran Skycaptain", 45, Rarity.UNCOMMON, mage.cards.k.KjeldoranSkycaptain.class)); + cards.add(new SetCardInfo("Knight of Stromgald", 171, Rarity.UNCOMMON, mage.cards.k.KnightOfStromgald.class)); + cards.add(new SetCardInfo("Krovikan Fetish", 172, Rarity.COMMON, mage.cards.k.KrovikanFetish.class)); + cards.add(new SetCardInfo("Krovikan Sorcerer", 96, Rarity.COMMON, mage.cards.k.KrovikanSorcerer.class)); + cards.add(new SetCardInfo("Labyrinth Minotaur", 97, Rarity.COMMON, mage.cards.l.LabyrinthMinotaur.class)); + cards.add(new SetCardInfo("Leshrac's Rite", 173, Rarity.UNCOMMON, mage.cards.l.LeshracsRite.class)); + cards.add(new SetCardInfo("Leviathan", 98, Rarity.RARE, mage.cards.l.Leviathan.class)); + cards.add(new SetCardInfo("Ley Druid", 308, Rarity.COMMON, mage.cards.l.LeyDruid.class)); + cards.add(new SetCardInfo("Lhurgoyf", 309, Rarity.RARE, mage.cards.l.Lhurgoyf.class)); + cards.add(new SetCardInfo("Library of Leng", 387, Rarity.UNCOMMON, mage.cards.l.LibraryOfLeng.class)); + cards.add(new SetCardInfo("Lifeforce", 310, Rarity.UNCOMMON, mage.cards.l.Lifeforce.class)); + cards.add(new SetCardInfo("Lifetap", 99, Rarity.UNCOMMON, mage.cards.l.Lifetap.class)); + cards.add(new SetCardInfo("Living Artifact", 311, Rarity.RARE, mage.cards.l.LivingArtifact.class)); + cards.add(new SetCardInfo("Living Lands", 312, Rarity.RARE, mage.cards.l.LivingLands.class)); + cards.add(new SetCardInfo("Llanowar Elves", 313, Rarity.COMMON, mage.cards.l.LlanowarElves.class)); + cards.add(new SetCardInfo("Lord of Atlantis", 100, Rarity.RARE, mage.cards.l.LordOfAtlantis.class)); + cards.add(new SetCardInfo("Lord of the Pit", 174, Rarity.RARE, mage.cards.l.LordOfThePit.class)); + cards.add(new SetCardInfo("Lost Soul", 175, Rarity.COMMON, mage.cards.l.LostSoul.class)); + cards.add(new SetCardInfo("Lure", 314, Rarity.UNCOMMON, mage.cards.l.Lure.class)); + cards.add(new SetCardInfo("Magus of the Unseen", 102, Rarity.RARE, mage.cards.m.MagusOfTheUnseen.class)); + cards.add(new SetCardInfo("Mana Clash", 248, Rarity.RARE, mage.cards.m.ManaClash.class)); + cards.add(new SetCardInfo("Mana Flare", 249, Rarity.RARE, mage.cards.m.ManaFlare.class)); + cards.add(new SetCardInfo("Mana Vault", 388, Rarity.RARE, mage.cards.m.ManaVault.class)); + cards.add(new SetCardInfo("Manabarbs", 250, Rarity.RARE, mage.cards.m.Manabarbs.class)); + cards.add(new SetCardInfo("Marsh Viper", 315, Rarity.COMMON, mage.cards.m.MarshViper.class)); + cards.add(new SetCardInfo("Meekstone", 389, Rarity.RARE, mage.cards.m.Meekstone.class)); + cards.add(new SetCardInfo("Memory Lapse", 103, Rarity.COMMON, mage.cards.m.MemoryLapse.class)); + cards.add(new SetCardInfo("Merfolk of the Pearl Trident", 104, Rarity.COMMON, mage.cards.m.MerfolkOfThePearlTrident.class)); + cards.add(new SetCardInfo("Mesa Falcon", 46, Rarity.COMMON, mage.cards.m.MesaFalcon.class)); + cards.add(new SetCardInfo("Mesa Pegasus", 47, Rarity.COMMON, mage.cards.m.MesaPegasus.class)); + cards.add(new SetCardInfo("Millstone", 390, Rarity.RARE, mage.cards.m.Millstone.class)); + cards.add(new SetCardInfo("Mind Bomb", 105, Rarity.UNCOMMON, mage.cards.m.MindBomb.class)); + cards.add(new SetCardInfo("Mind Ravel", 176, Rarity.COMMON, mage.cards.m.MindRavel.class)); + cards.add(new SetCardInfo("Mind Warp", 177, Rarity.UNCOMMON, mage.cards.m.MindWarp.class)); + cards.add(new SetCardInfo("Mindstab Thrull", 178, Rarity.COMMON, mage.cards.m.MindstabThrull.class)); + cards.add(new SetCardInfo("Mole Worms", 179, Rarity.UNCOMMON, mage.cards.m.MoleWorms.class)); + cards.add(new SetCardInfo("Mons's Goblin Raiders", 251, Rarity.COMMON, mage.cards.m.MonssGoblinRaiders.class)); + cards.add(new SetCardInfo("Mountain Goat", 252, Rarity.COMMON, mage.cards.m.MountainGoat.class)); + cards.add(new SetCardInfo("Mountain", 442, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 443, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 444, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 445, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Murk Dwellers", 180, Rarity.COMMON, mage.cards.m.MurkDwellers.class)); + cards.add(new SetCardInfo("Nature's Lore", 316, Rarity.COMMON, mage.cards.n.NaturesLore.class)); + cards.add(new SetCardInfo("Necrite", 181, Rarity.COMMON, mage.cards.n.Necrite.class)); + cards.add(new SetCardInfo("Necropotence", 182, Rarity.RARE, mage.cards.n.Necropotence.class)); + cards.add(new SetCardInfo("Nether Shadow", 183, Rarity.RARE, mage.cards.n.NetherShadow.class)); + cards.add(new SetCardInfo("Nevinyrral's Disk", 391, Rarity.RARE, mage.cards.n.NevinyrralsDisk.class)); + cards.add(new SetCardInfo("Nightmare", 184, Rarity.RARE, mage.cards.n.Nightmare.class)); + cards.add(new SetCardInfo("Obelisk of Undoing", 392, Rarity.RARE, mage.cards.o.ObeliskOfUndoing.class)); + cards.add(new SetCardInfo("Orcish Artillery", 253, Rarity.UNCOMMON, mage.cards.o.OrcishArtillery.class)); + cards.add(new SetCardInfo("Orcish Captain", 254, Rarity.UNCOMMON, mage.cards.o.OrcishCaptain.class)); + cards.add(new SetCardInfo("Orcish Oriflamme", 257, Rarity.UNCOMMON, mage.cards.o.OrcishOriflamme.class)); + cards.add(new SetCardInfo("Orcish Squatters", 258, Rarity.RARE, mage.cards.o.OrcishSquatters.class)); + cards.add(new SetCardInfo("Order of the Sacred Torch", 48, Rarity.RARE, mage.cards.o.OrderOfTheSacredTorch.class)); + cards.add(new SetCardInfo("Order of the White Shield", 49, Rarity.UNCOMMON, mage.cards.o.OrderOfTheWhiteShield.class)); + cards.add(new SetCardInfo("Orgg", 259, Rarity.RARE, mage.cards.o.Orgg.class)); + cards.add(new SetCardInfo("Ornithopter", 393, Rarity.UNCOMMON, mage.cards.o.Ornithopter.class)); + cards.add(new SetCardInfo("Panic", 260, Rarity.COMMON, mage.cards.p.Panic.class)); + cards.add(new SetCardInfo("Paralyze", 185, Rarity.COMMON, mage.cards.p.Paralyze.class)); + cards.add(new SetCardInfo("Pearled Unicorn", 50, Rarity.COMMON, mage.cards.p.PearledUnicorn.class)); + cards.add(new SetCardInfo("Pentagram of the Ages", 394, Rarity.RARE, mage.cards.p.PentagramOfTheAges.class)); + cards.add(new SetCardInfo("Personal Incarnation", 51, Rarity.RARE, mage.cards.p.PersonalIncarnation.class)); + cards.add(new SetCardInfo("Pestilence", 186, Rarity.COMMON, mage.cards.p.Pestilence.class)); + cards.add(new SetCardInfo("Phantasmal Forces", 106, Rarity.UNCOMMON, mage.cards.p.PhantasmalForces.class)); + cards.add(new SetCardInfo("Phantasmal Terrain", 107, Rarity.COMMON, mage.cards.p.PhantasmalTerrain.class)); + cards.add(new SetCardInfo("Phantom Monster", 108, Rarity.UNCOMMON, mage.cards.p.PhantomMonster.class)); + cards.add(new SetCardInfo("Pikemen", 52, Rarity.COMMON, mage.cards.p.Pikemen.class)); + cards.add(new SetCardInfo("Pirate Ship", 109, Rarity.RARE, mage.cards.p.PirateShip.class)); + cards.add(new SetCardInfo("Pit Scorpion", 187, Rarity.COMMON, mage.cards.p.PitScorpion.class)); + cards.add(new SetCardInfo("Plague Rats", 188, Rarity.COMMON, mage.cards.p.PlagueRats.class)); + cards.add(new SetCardInfo("Plains", 430, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 431, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 432, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 433, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Portent", 110, Rarity.COMMON, mage.cards.p.Portent.class)); + cards.add(new SetCardInfo("Power Sink", 111, Rarity.UNCOMMON, mage.cards.p.PowerSink.class)); + cards.add(new SetCardInfo("Pox", 189, Rarity.RARE, mage.cards.p.Pox.class)); + cards.add(new SetCardInfo("Pradesh Gypsies", 317, Rarity.COMMON, mage.cards.p.PradeshGypsies.class)); + cards.add(new SetCardInfo("Primal Clay", 395, Rarity.RARE, mage.cards.p.PrimalClay.class)); + cards.add(new SetCardInfo("Primal Order", 318, Rarity.RARE, mage.cards.p.PrimalOrder.class)); + cards.add(new SetCardInfo("Primordial Ooze", 261, Rarity.UNCOMMON, mage.cards.p.PrimordialOoze.class)); + cards.add(new SetCardInfo("Prismatic Ward", 53, Rarity.COMMON, mage.cards.p.PrismaticWard.class)); + cards.add(new SetCardInfo("Prodigal Sorcerer", 112, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class)); + cards.add(new SetCardInfo("Psychic Venom", 113, Rarity.COMMON, mage.cards.p.PsychicVenom.class)); + cards.add(new SetCardInfo("Pyroblast", 262, Rarity.UNCOMMON, mage.cards.p.Pyroblast.class)); + cards.add(new SetCardInfo("Pyrotechnics", 263, Rarity.UNCOMMON, mage.cards.p.Pyrotechnics.class)); + cards.add(new SetCardInfo("Rabid Wombat", 319, Rarity.UNCOMMON, mage.cards.r.RabidWombat.class)); + cards.add(new SetCardInfo("Radjan Spirit", 320, Rarity.UNCOMMON, mage.cards.r.RadjanSpirit.class)); + cards.add(new SetCardInfo("Rag Man", 190, Rarity.RARE, mage.cards.r.RagMan.class)); + cards.add(new SetCardInfo("Raise Dead", 191, Rarity.COMMON, mage.cards.r.RaiseDead.class)); + cards.add(new SetCardInfo("Ray of Command", 114, Rarity.COMMON, mage.cards.r.RayOfCommand.class)); + cards.add(new SetCardInfo("Recall", 115, Rarity.RARE, mage.cards.r.Recall.class)); + cards.add(new SetCardInfo("Reef Pirates", 116, Rarity.COMMON, mage.cards.r.ReefPirates.class)); + cards.add(new SetCardInfo("Regeneration", 321, Rarity.COMMON, mage.cards.r.Regeneration.class)); + cards.add(new SetCardInfo("Remove Soul", 117, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); + cards.add(new SetCardInfo("Repentant Blacksmith", 54, Rarity.COMMON, mage.cards.r.RepentantBlacksmith.class)); + cards.add(new SetCardInfo("Reverse Damage", 55, Rarity.RARE, mage.cards.r.ReverseDamage.class)); + cards.add(new SetCardInfo("Righteousness", 56, Rarity.RARE, mage.cards.r.Righteousness.class)); + cards.add(new SetCardInfo("Rod of Ruin", 396, Rarity.UNCOMMON, mage.cards.r.RodOfRuin.class)); + cards.add(new SetCardInfo("Ruins of Trokair", 422, Rarity.UNCOMMON, mage.cards.r.RuinsOfTrokair.class)); + cards.add(new SetCardInfo("Sabretooth Tiger", 264, Rarity.COMMON, mage.cards.s.SabretoothTiger.class)); + cards.add(new SetCardInfo("Samite Healer", 58, Rarity.COMMON, mage.cards.s.SamiteHealer.class)); + cards.add(new SetCardInfo("Sand Silos", 423, Rarity.RARE, mage.cards.s.SandSilos.class)); + cards.add(new SetCardInfo("Scaled Wurm", 322, Rarity.COMMON, mage.cards.s.ScaledWurm.class)); + cards.add(new SetCardInfo("Scathe Zombies", 192, Rarity.COMMON, mage.cards.s.ScatheZombies.class)); + cards.add(new SetCardInfo("Scavenger Folk", 323, Rarity.COMMON, mage.cards.s.ScavengerFolk.class)); + cards.add(new SetCardInfo("Scryb Sprites", 324, Rarity.COMMON, mage.cards.s.ScrybSprites.class)); + cards.add(new SetCardInfo("Sea Serpent", 118, Rarity.COMMON, mage.cards.s.SeaSerpent.class)); + cards.add(new SetCardInfo("Sea Spirit", 119, Rarity.UNCOMMON, mage.cards.s.SeaSpirit.class)); + cards.add(new SetCardInfo("Sea Sprite", 120, Rarity.UNCOMMON, mage.cards.s.SeaSprite.class)); + cards.add(new SetCardInfo("Seasinger", 121, Rarity.UNCOMMON, mage.cards.s.Seasinger.class)); + cards.add(new SetCardInfo("Segovian Leviathan", 122, Rarity.UNCOMMON, mage.cards.s.SegovianLeviathan.class)); + cards.add(new SetCardInfo("Sengir Autocrat", 193, Rarity.RARE, mage.cards.s.SengirAutocrat.class)); + cards.add(new SetCardInfo("Seraph", 59, Rarity.RARE, mage.cards.s.Seraph.class)); + cards.add(new SetCardInfo("Serpent Generator", 397, Rarity.RARE, mage.cards.s.SerpentGenerator.class)); + cards.add(new SetCardInfo("Serra Bestiary", 60, Rarity.UNCOMMON, mage.cards.s.SerraBestiary.class)); + cards.add(new SetCardInfo("Serra Paladin", 61, Rarity.UNCOMMON, mage.cards.s.SerraPaladin.class)); + cards.add(new SetCardInfo("Shanodin Dryads", 325, Rarity.COMMON, mage.cards.s.ShanodinDryads.class)); + cards.add(new SetCardInfo("Shapeshifter", 398, Rarity.UNCOMMON, mage.cards.s.Shapeshifter.class)); + cards.add(new SetCardInfo("Shatter", 265, Rarity.COMMON, mage.cards.s.Shatter.class)); + cards.add(new SetCardInfo("Shatterstorm", 266, Rarity.UNCOMMON, mage.cards.s.Shatterstorm.class)); + cards.add(new SetCardInfo("Shield Bearer", 62, Rarity.COMMON, mage.cards.s.ShieldBearer.class)); + cards.add(new SetCardInfo("Shield Wall", 63, Rarity.COMMON, mage.cards.s.ShieldWall.class)); + cards.add(new SetCardInfo("Shivan Dragon", 267, Rarity.RARE, mage.cards.s.ShivanDragon.class)); + cards.add(new SetCardInfo("Shrink", 326, Rarity.COMMON, mage.cards.s.Shrink.class)); + cards.add(new SetCardInfo("Sibilant Spirit", 123, Rarity.RARE, mage.cards.s.SibilantSpirit.class)); + cards.add(new SetCardInfo("Skull Catapult", 399, Rarity.UNCOMMON, mage.cards.s.SkullCatapult.class)); + cards.add(new SetCardInfo("Smoke", 268, Rarity.RARE, mage.cards.s.Smoke.class)); + cards.add(new SetCardInfo("Sorceress Queen", 194, Rarity.RARE, mage.cards.s.SorceressQueen.class)); + cards.add(new SetCardInfo("Soul Barrier", 125, Rarity.COMMON, mage.cards.s.SoulBarrier.class)); + cards.add(new SetCardInfo("Soul Net", 400, Rarity.UNCOMMON, mage.cards.s.SoulNet.class)); + cards.add(new SetCardInfo("Spell Blast", 126, Rarity.COMMON, mage.cards.s.SpellBlast.class)); + cards.add(new SetCardInfo("Spirit Link", 64, Rarity.UNCOMMON, mage.cards.s.SpiritLink.class)); + cards.add(new SetCardInfo("Stampede", 327, Rarity.RARE, mage.cards.s.Stampede.class)); + cards.add(new SetCardInfo("Stasis", 127, Rarity.RARE, mage.cards.s.Stasis.class)); + cards.add(new SetCardInfo("Steal Artifact", 128, Rarity.UNCOMMON, mage.cards.s.StealArtifact.class)); + cards.add(new SetCardInfo("Stone Giant", 269, Rarity.UNCOMMON, mage.cards.s.StoneGiant.class)); + cards.add(new SetCardInfo("Stone Rain", 270, Rarity.COMMON, mage.cards.s.StoneRain.class)); + cards.add(new SetCardInfo("Stone Spirit", 271, Rarity.UNCOMMON, mage.cards.s.StoneSpirit.class)); + cards.add(new SetCardInfo("Stream of Life", 328, Rarity.COMMON, mage.cards.s.StreamOfLife.class)); + cards.add(new SetCardInfo("Stromgald Cabal", 195, Rarity.RARE, mage.cards.s.StromgaldCabal.class)); + cards.add(new SetCardInfo("Sulfurous Springs", 424, Rarity.RARE, mage.cards.s.SulfurousSprings.class)); + cards.add(new SetCardInfo("Svyelunite Temple", 425, Rarity.UNCOMMON, mage.cards.s.SvyeluniteTemple.class)); + cards.add(new SetCardInfo("Swamp", 438, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 439, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 440, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 441, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sylvan Library", 329, Rarity.RARE, mage.cards.s.SylvanLibrary.class)); + cards.add(new SetCardInfo("Tarpan", 330, Rarity.COMMON, mage.cards.t.Tarpan.class)); + cards.add(new SetCardInfo("Tawnos's Weaponry", 401, Rarity.UNCOMMON, mage.cards.t.TawnossWeaponry.class)); + cards.add(new SetCardInfo("Terror", 196, Rarity.COMMON, mage.cards.t.Terror.class)); + cards.add(new SetCardInfo("The Brute", 272, Rarity.COMMON, mage.cards.t.TheBrute.class)); + cards.add(new SetCardInfo("The Hive", 402, Rarity.RARE, mage.cards.t.TheHive.class)); + cards.add(new SetCardInfo("The Wretched", 197, Rarity.RARE, mage.cards.t.TheWretched.class)); + cards.add(new SetCardInfo("Thicket Basilisk", 331, Rarity.UNCOMMON, mage.cards.t.ThicketBasilisk.class)); + cards.add(new SetCardInfo("Throne of Bone", 403, Rarity.UNCOMMON, mage.cards.t.ThroneOfBone.class)); + cards.add(new SetCardInfo("Thrull Retainer", 198, Rarity.UNCOMMON, mage.cards.t.ThrullRetainer.class)); + cards.add(new SetCardInfo("Time Bomb", 404, Rarity.RARE, mage.cards.t.TimeBomb.class)); + cards.add(new SetCardInfo("Time Elemental", 129, Rarity.RARE, mage.cards.t.TimeElemental.class)); + cards.add(new SetCardInfo("Titania's Song", 332, Rarity.RARE, mage.cards.t.TitaniasSong.class)); + cards.add(new SetCardInfo("Torture", 199, Rarity.COMMON, mage.cards.t.Torture.class)); + cards.add(new SetCardInfo("Touch of Death", 200, Rarity.COMMON, mage.cards.t.TouchOfDeath.class)); + cards.add(new SetCardInfo("Tranquility", 333, Rarity.COMMON, mage.cards.t.Tranquility.class)); + cards.add(new SetCardInfo("Truce", 65, Rarity.RARE, mage.cards.t.Truce.class)); + cards.add(new SetCardInfo("Tsunami", 334, Rarity.UNCOMMON, mage.cards.t.Tsunami.class)); + cards.add(new SetCardInfo("Tundra Wolves", 66, Rarity.COMMON, mage.cards.t.TundraWolves.class)); + cards.add(new SetCardInfo("Twiddle", 130, Rarity.COMMON, mage.cards.t.Twiddle.class)); + cards.add(new SetCardInfo("Underground River", 426, Rarity.RARE, mage.cards.u.UndergroundRiver.class)); + cards.add(new SetCardInfo("Unholy Strength", 201, Rarity.COMMON, mage.cards.u.UnholyStrength.class)); + cards.add(new SetCardInfo("Unstable Mutation", 131, Rarity.COMMON, mage.cards.u.UnstableMutation.class)); + cards.add(new SetCardInfo("Unsummon", 132, Rarity.COMMON, mage.cards.u.Unsummon.class)); + cards.add(new SetCardInfo("Untamed Wilds", 335, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); + cards.add(new SetCardInfo("Updraft", 133, Rarity.COMMON, mage.cards.u.Updraft.class)); + cards.add(new SetCardInfo("Urza's Avenger", 405, Rarity.RARE, mage.cards.u.UrzasAvenger.class)); + cards.add(new SetCardInfo("Urza's Bauble", 406, Rarity.UNCOMMON, mage.cards.u.UrzasBauble.class)); + cards.add(new SetCardInfo("Urza's Mine", 427, Rarity.COMMON, mage.cards.u.UrzasMine.class)); + cards.add(new SetCardInfo("Urza's Power Plant", 428, Rarity.COMMON, mage.cards.u.UrzasPowerPlant.class)); + cards.add(new SetCardInfo("Urza's Tower", 429, Rarity.COMMON, mage.cards.u.UrzasTower.class)); + cards.add(new SetCardInfo("Vampire Bats", 202, Rarity.COMMON, mage.cards.v.VampireBats.class)); + cards.add(new SetCardInfo("Venom", 336, Rarity.COMMON, mage.cards.v.Venom.class)); + cards.add(new SetCardInfo("Verduran Enchantress", 337, Rarity.RARE, mage.cards.v.VerduranEnchantress.class)); + cards.add(new SetCardInfo("Vodalian Soldiers", 134, Rarity.COMMON, mage.cards.v.VodalianSoldiers.class)); + cards.add(new SetCardInfo("Wall of Air", 135, Rarity.UNCOMMON, mage.cards.w.WallOfAir.class)); + cards.add(new SetCardInfo("Wall of Bone", 203, Rarity.UNCOMMON, mage.cards.w.WallOfBone.class)); + cards.add(new SetCardInfo("Wall of Brambles", 338, Rarity.UNCOMMON, mage.cards.w.WallOfBrambles.class)); + cards.add(new SetCardInfo("Wall of Fire", 273, Rarity.UNCOMMON, mage.cards.w.WallOfFire.class)); + cards.add(new SetCardInfo("Wall of Spears", 407, Rarity.COMMON, mage.cards.w.WallOfSpears.class)); + cards.add(new SetCardInfo("Wall of Stone", 274, Rarity.UNCOMMON, mage.cards.w.WallOfStone.class)); + cards.add(new SetCardInfo("Wall of Swords", 67, Rarity.UNCOMMON, mage.cards.w.WallOfSwords.class)); + cards.add(new SetCardInfo("Wanderlust", 339, Rarity.UNCOMMON, mage.cards.w.Wanderlust.class)); + cards.add(new SetCardInfo("War Mammoth", 340, Rarity.COMMON, mage.cards.w.WarMammoth.class)); + cards.add(new SetCardInfo("Warp Artifact", 204, Rarity.RARE, mage.cards.w.WarpArtifact.class)); + cards.add(new SetCardInfo("Weakness", 205, Rarity.COMMON, mage.cards.w.Weakness.class)); + cards.add(new SetCardInfo("Whirling Dervish", 341, Rarity.UNCOMMON, mage.cards.w.WhirlingDervish.class)); + cards.add(new SetCardInfo("White Knight", 68, Rarity.UNCOMMON, mage.cards.w.WhiteKnight.class)); + cards.add(new SetCardInfo("Wild Growth", 342, Rarity.COMMON, mage.cards.w.WildGrowth.class)); + cards.add(new SetCardInfo("Wind Spirit", 136, Rarity.UNCOMMON, mage.cards.w.WindSpirit.class)); + cards.add(new SetCardInfo("Winds of Change", 275, Rarity.RARE, mage.cards.w.WindsOfChange.class)); + cards.add(new SetCardInfo("Winter Blast", 343, Rarity.UNCOMMON, mage.cards.w.WinterBlast.class)); + cards.add(new SetCardInfo("Winter Orb", 408, Rarity.RARE, mage.cards.w.WinterOrb.class)); + cards.add(new SetCardInfo("Wolverine Pack", 344, Rarity.UNCOMMON, mage.cards.w.WolverinePack.class)); + cards.add(new SetCardInfo("Wooden Sphere", 409, Rarity.UNCOMMON, mage.cards.w.WoodenSphere.class)); + cards.add(new SetCardInfo("Word of Blasting", 276, Rarity.UNCOMMON, mage.cards.w.WordOfBlasting.class)); + cards.add(new SetCardInfo("Wrath of God", 69, Rarity.RARE, mage.cards.w.WrathOfGod.class)); + cards.add(new SetCardInfo("Wyluli Wolf", 345, Rarity.RARE, mage.cards.w.WyluliWolf.class)); + cards.add(new SetCardInfo("Xenic Poltergeist", 206, Rarity.RARE, mage.cards.x.XenicPoltergeist.class)); + cards.add(new SetCardInfo("Zephyr Falcon", 137, Rarity.COMMON, mage.cards.z.ZephyrFalcon.class)); + cards.add(new SetCardInfo("Zombie Master", 207, Rarity.RARE, mage.cards.z.ZombieMaster.class)); + cards.add(new SetCardInfo("Zur's Weirding", 138, Rarity.RARE, mage.cards.z.ZursWeirding.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/HistoricAnthology1.java b/Mage.Sets/src/mage/sets/HistoricAnthology1.java index 94bed8ae95..1dffb2f824 100644 --- a/Mage.Sets/src/mage/sets/HistoricAnthology1.java +++ b/Mage.Sets/src/mage/sets/HistoricAnthology1.java @@ -6,8 +6,9 @@ import mage.constants.SetType; /** * https://scryfall.com/sets/ha1 + * @author mikalinn777 */ -public class HistoricAnthology1 extends ExpansionSet { +public final class HistoricAnthology1 extends ExpansionSet { private static final HistoricAnthology1 instance = new HistoricAnthology1(); @@ -19,8 +20,7 @@ public class HistoricAnthology1 extends ExpansionSet { super("Historic Anthology 1", "HA1", ExpansionSet.buildDate(2019, 11, 21), SetType.MAGIC_ONLINE); this.hasBoosters = false; this.hasBasicLands = false; - - cards.add(new SetCardInfo("Burning-Tree Emissary", 16, Rarity.UNCOMMON, mage.cards.b.BurningTreeEmissary.class)); + cards.add(new SetCardInfo("Burning Tree Emissary", 16, Rarity.UNCOMMON, mage.cards.b.BurningTreeEmissary.class)); cards.add(new SetCardInfo("Captain Sisay", 17, Rarity.RARE, mage.cards.c.CaptainSisay.class)); cards.add(new SetCardInfo("Cryptbreaker", 6, Rarity.RARE, mage.cards.c.Cryptbreaker.class)); cards.add(new SetCardInfo("Darksteel Reactor", 20, Rarity.RARE, mage.cards.d.DarksteelReactor.class)); diff --git a/Mage.Sets/src/mage/sets/HistoricAnthology2.java b/Mage.Sets/src/mage/sets/HistoricAnthology2.java index ae56ddcaef..0131e95c18 100644 --- a/Mage.Sets/src/mage/sets/HistoricAnthology2.java +++ b/Mage.Sets/src/mage/sets/HistoricAnthology2.java @@ -6,8 +6,9 @@ import mage.constants.SetType; /** * https://scryfall.com/sets/ha2 + * @author mikalinn777 */ -public class HistoricAnthology2 extends ExpansionSet { +public final class HistoricAnthology2 extends ExpansionSet { private static final HistoricAnthology2 instance = new HistoricAnthology2(); @@ -19,7 +20,6 @@ public class HistoricAnthology2 extends ExpansionSet { super("Historic Anthology 2", "HA2", ExpansionSet.buildDate(2020, 3, 12), SetType.MAGIC_ONLINE); this.hasBoosters = false; this.hasBasicLands = false; - cards.add(new SetCardInfo("Ancestral Mask", 13, Rarity.COMMON, mage.cards.a.AncestralMask.class)); cards.add(new SetCardInfo("Barren Moor", 19, Rarity.COMMON, mage.cards.b.BarrenMoor.class)); cards.add(new SetCardInfo("Bojuka Bog", 20, Rarity.COMMON, mage.cards.b.BojukaBog.class)); diff --git a/Mage.Sets/src/mage/sets/HistoricAnthology3.java b/Mage.Sets/src/mage/sets/HistoricAnthology3.java new file mode 100644 index 0000000000..5aa7c331d5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/HistoricAnthology3.java @@ -0,0 +1,55 @@ + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author mikalinn777 + */ +public final class HistoricAnthology3 extends ExpansionSet { + + private static final HistoricAnthology3 instance = new HistoricAnthology3(); + + public static HistoricAnthology3 getInstance() { + return instance; + } + + private HistoricAnthology3() { + super("Historic Anthology 3", "HA3", ExpansionSet.buildDate(2020, 5, 21), SetType.SUPPLEMENTAL); + this.blockName = "Reprint"; + this.hasBoosters = false; + this.hasBasicLands = false; + cards.add(new SetCardInfo("Ancient Ziggurat", 26, Rarity.UNCOMMON, mage.cards.a.AncientZiggurat.class)); + cards.add(new SetCardInfo("Akroma's Memorial", 24, Rarity.MYTHIC, mage.cards.a.AkromasMemorial.class)); + cards.add(new SetCardInfo("Body Double", 6, Rarity.RARE, mage.cards.b.BodyDouble.class)); + cards.add(new SetCardInfo("Chainer's Edict", 10, Rarity.UNCOMMON, mage.cards.c.ChainersEdict.class)); + cards.add(new SetCardInfo("Devil's Play", 15, Rarity.RARE, mage.cards.d.DevilsPlay.class)); + cards.add(new SetCardInfo("Enchantress's Presence", 19, Rarity.RARE, mage.cards.e.EnchantresssPresence.class)); + cards.add(new SetCardInfo("Gempalm Incinerator", 16, Rarity.UNCOMMON, mage.cards.g.GempalmIncinerator.class)); + cards.add(new SetCardInfo("Gempalm Polluter", 11, Rarity.COMMON, mage.cards.g.GempalmPolluter.class)); + cards.add(new SetCardInfo("Honden of Cleansing Fire", 2, Rarity.UNCOMMON, mage.cards.h.HondenOfCleansingFire.class)); + cards.add(new SetCardInfo("Honden of Infinite Rage", 17, Rarity.UNCOMMON, mage.cards.h.HondenOfInfiniteRage.class)); + cards.add(new SetCardInfo("Honden of Life's Web", 20, Rarity.UNCOMMON, mage.cards.h.HondenOfLifesWeb.class)); + cards.add(new SetCardInfo("Honden of Night's Reach", 12, Rarity.UNCOMMON, mage.cards.h.HondenOfNightsReach.class)); + cards.add(new SetCardInfo("Honden of Seeing Winds", 7, Rarity.UNCOMMON, mage.cards.h.HondenOfSeeingWinds.class)); + cards.add(new SetCardInfo("Krosan Tusker", 21, Rarity.COMMON, mage.cards.k.KrosanTusker.class)); + cards.add(new SetCardInfo("Maze's End", 27, Rarity.MYTHIC, mage.cards.m.MazesEnd.class)); + cards.add(new SetCardInfo("Mirari's Wake", 23, Rarity.RARE, mage.cards.m.MirarisWake.class)); + cards.add(new SetCardInfo("Momentary Blink", 3, Rarity.COMMON, mage.cards.m.MomentaryBlink.class)); + cards.add(new SetCardInfo("Phyrexian Obliterator", 13, Rarity.MYTHIC, mage.cards.p.PhyrexianObliterator.class)); + cards.add(new SetCardInfo("Ratchet Bomb", 25, Rarity.RARE, mage.cards.r.RatchetBomb.class)); + cards.add(new SetCardInfo("Roar of the Wurm", 22, Rarity.UNCOMMON, mage.cards.r.RoarOfTheWurm.class)); + cards.add(new SetCardInfo("Silent Departure", 8, Rarity.COMMON, mage.cards.s.SilentDeparture.class)); + cards.add(new SetCardInfo("Swan Song", 9, Rarity.RARE, mage.cards.s.SwanSong.class)); + cards.add(new SetCardInfo("Tectonic Reformation", 18, Rarity.RARE, mage.cards.t.TectonicReformation.class)); + cards.add(new SetCardInfo("Tempered Steel", 4, Rarity.RARE, mage.cards.t.TemperedSteel.class)); + cards.add(new SetCardInfo("Timely Reinforcements", 5, Rarity.UNCOMMON, mage.cards.t.TimelyReinforcements.class)); + cards.add(new SetCardInfo("Ulamog, the Ceaseless Hunger", 1, Rarity.MYTHIC, mage.cards.u.UlamogTheCeaselessHunger.class)); + cards.add(new SetCardInfo("Unburial Rites", 14, Rarity.UNCOMMON, mage.cards.u.UnburialRites.class)); + + + } + +} diff --git a/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java b/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java new file mode 100644 index 0000000000..277192e12b --- /dev/null +++ b/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java @@ -0,0 +1,503 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.SetType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author TheElk801 + */ +public final class IkoriaLairOfBehemoths extends ExpansionSet { + + private static final List mutateNames = Arrays.asList( + "Archipelagore", + "Auspicious Starrix", + "Boneyard Lurker", + "Brokkos, Apex of Forever", + "Cavern Whisperer", + "Chittering Harvester", + "Cloudpiercer", + "Cubwarden", + "Dirge Bat", + "Dreamtail Heron", + "Everquill Phoenix", + "Gemrazer", + "Glowstone Recluse", + "Huntmaster Liger", + "Illuna, Apex of Wishes", + "Insatiable Hemophage", + "Lore Drakkis", + "Majestic Auricorn", + "Migratory Greathorn", + "Necropanther", + "Nethroi, Apex of Death", + "Parcelbeast", + "Porcuparrot", + "Pouncing Shoreshark", + "Regal Leosaur", + "Sea-Dasher Octopus", + "Snapdax, Apex of the Hunt", + "Trumpeting Gnarr", + "Vadrok, Apex of Thunder", + "Vulpikeet" + ); + + private static final IkoriaLairOfBehemoths instance = new IkoriaLairOfBehemoths(); + + public static IkoriaLairOfBehemoths getInstance() { + return instance; + } + + private final List savedSpecialLand = new ArrayList<>(); + + private IkoriaLairOfBehemoths() { + super("Ikoria: Lair of Behemoths", "IKO", ExpansionSet.buildDate(2020, 4, 24), SetType.EXPANSION); + this.blockName = "Ikoria: Lair of Behemoths"; + this.hasBoosters = true; + this.numBoosterLands = 1; + this.numBoosterCommon = 10; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 8; + this.maxCardNumberInBooster = 274; + + // About half of boosters will have a gainland rather than a basic + // Source: https://twitter.com/GavinVerhey/status/1248731315412717568 + this.ratioBoosterSpecialLand = 2; + this.ratioBoosterSpecialLandNumerator = 1; + + cards.add(new SetCardInfo("Adaptive Shimmerer", 1, Rarity.COMMON, mage.cards.a.AdaptiveShimmerer.class)); + cards.add(new SetCardInfo("Adventurous Impulse", 142, Rarity.COMMON, mage.cards.a.AdventurousImpulse.class)); + cards.add(new SetCardInfo("Aegis Turtle", 39, Rarity.COMMON, mage.cards.a.AegisTurtle.class)); + cards.add(new SetCardInfo("Alert Heedbonder", 218, Rarity.UNCOMMON, mage.cards.a.AlertHeedbonder.class)); + cards.add(new SetCardInfo("Almighty Brushwagg", 143, Rarity.COMMON, mage.cards.a.AlmightyBrushwagg.class)); + cards.add(new SetCardInfo("Anticipate", 40, Rarity.COMMON, mage.cards.a.Anticipate.class)); + cards.add(new SetCardInfo("Archipelagore", 283, Rarity.UNCOMMON, mage.cards.a.Archipelagore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Archipelagore", 41, Rarity.UNCOMMON, mage.cards.a.Archipelagore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Auspicious Starrix", 144, Rarity.UNCOMMON, mage.cards.a.AuspiciousStarrix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Auspicious Starrix", 294, Rarity.UNCOMMON, mage.cards.a.AuspiciousStarrix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Avian Oddity", 42, Rarity.UNCOMMON, mage.cards.a.AvianOddity.class)); + cards.add(new SetCardInfo("Back for More", 177, Rarity.UNCOMMON, mage.cards.b.BackForMore.class)); + cards.add(new SetCardInfo("Barrier Breach", 145, Rarity.UNCOMMON, mage.cards.b.BarrierBreach.class)); + cards.add(new SetCardInfo("Bastion of Remembrance", 73, Rarity.UNCOMMON, mage.cards.b.BastionOfRemembrance.class)); + cards.add(new SetCardInfo("Blade Banish", 4, Rarity.COMMON, mage.cards.b.BladeBanish.class)); + cards.add(new SetCardInfo("Blazing Volley", 107, Rarity.COMMON, mage.cards.b.BlazingVolley.class)); + cards.add(new SetCardInfo("Blisterspit Gremlin", 108, Rarity.COMMON, mage.cards.b.BlisterspitGremlin.class)); + cards.add(new SetCardInfo("Blitz Leech", 74, Rarity.COMMON, mage.cards.b.BlitzLeech.class)); + cards.add(new SetCardInfo("Blitz of the Thunder-Raptor", 109, Rarity.UNCOMMON, mage.cards.b.BlitzOfTheThunderRaptor.class)); + cards.add(new SetCardInfo("Blood Curdle", 75, Rarity.COMMON, mage.cards.b.BloodCurdle.class)); + cards.add(new SetCardInfo("Bloodfell Caves", 243, Rarity.COMMON, mage.cards.b.BloodfellCaves.class)); + cards.add(new SetCardInfo("Blossoming Sands", 244, Rarity.COMMON, mage.cards.b.BlossomingSands.class)); + cards.add(new SetCardInfo("Bonders' Enclave", 245, Rarity.RARE, mage.cards.b.BondersEnclave.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bonders' Enclave", 363, Rarity.RARE, mage.cards.b.BondersEnclave.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Boneyard Lurker", 178, Rarity.UNCOMMON, mage.cards.b.BoneyardLurker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Boneyard Lurker", 298, Rarity.UNCOMMON, mage.cards.b.BoneyardLurker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Boon of the Wish-Giver", 43, Rarity.UNCOMMON, mage.cards.b.BoonOfTheWishGiver.class)); + cards.add(new SetCardInfo("Boot Nipper", 76, Rarity.COMMON, mage.cards.b.BootNipper.class)); + cards.add(new SetCardInfo("Bristling Boar", 146, Rarity.COMMON, mage.cards.b.BristlingBoar.class)); + cards.add(new SetCardInfo("Bushmeat Poacher", 77, Rarity.COMMON, mage.cards.b.BushmeatPoacher.class)); + cards.add(new SetCardInfo("Call of the Death-Dweller", 78, Rarity.UNCOMMON, mage.cards.c.CallOfTheDeathDweller.class)); + cards.add(new SetCardInfo("Capture Sphere", 44, Rarity.COMMON, mage.cards.c.CaptureSphere.class)); + cards.add(new SetCardInfo("Cathartic Reunion", 110, Rarity.COMMON, mage.cards.c.CatharticReunion.class)); + cards.add(new SetCardInfo("Cavern Whisperer", 287, Rarity.COMMON, mage.cards.c.CavernWhisperer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cavern Whisperer", 79, Rarity.COMMON, mage.cards.c.CavernWhisperer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Channeled Force", 180, Rarity.UNCOMMON, mage.cards.c.ChanneledForce.class)); + cards.add(new SetCardInfo("Charge of the Forever-Beast", 147, Rarity.UNCOMMON, mage.cards.c.ChargeOfTheForeverBeast.class)); + cards.add(new SetCardInfo("Checkpoint Officer", 5, Rarity.COMMON, mage.cards.c.CheckpointOfficer.class)); + cards.add(new SetCardInfo("Chevill, Bane of Monsters", 181, Rarity.MYTHIC, mage.cards.c.ChevillBaneOfMonsters.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chevill, Bane of Monsters", 330, Rarity.MYTHIC, mage.cards.c.ChevillBaneOfMonsters.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chittering Harvester", 288, Rarity.UNCOMMON, mage.cards.c.ChitteringHarvester.class)); + cards.add(new SetCardInfo("Chittering Harvester", 80, Rarity.UNCOMMON, mage.cards.c.ChitteringHarvester.class)); + cards.add(new SetCardInfo("Clash of Titans", 111, Rarity.UNCOMMON, mage.cards.c.ClashOfTitans.class)); + cards.add(new SetCardInfo("Cloudpiercer", 112, Rarity.COMMON, mage.cards.c.Cloudpiercer.class)); + cards.add(new SetCardInfo("Cloudpiercer", 291, Rarity.COMMON, mage.cards.c.Cloudpiercer.class)); + cards.add(new SetCardInfo("Colossification", 148, Rarity.RARE, mage.cards.c.Colossification.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Colossification", 327, Rarity.RARE, mage.cards.c.Colossification.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Colossification", 364, Rarity.RARE, mage.cards.c.Colossification.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Convolute", 45, Rarity.COMMON, mage.cards.c.Convolute.class)); + cards.add(new SetCardInfo("Coordinated Charge", 6, Rarity.COMMON, mage.cards.c.CoordinatedCharge.class)); + cards.add(new SetCardInfo("Corpse Churn", 81, Rarity.COMMON, mage.cards.c.CorpseChurn.class)); + cards.add(new SetCardInfo("Crystacean", 46, Rarity.COMMON, mage.cards.c.Crystacean.class)); + cards.add(new SetCardInfo("Crystalline Giant", 234, Rarity.RARE, mage.cards.c.CrystallineGiant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Crystalline Giant", 361, Rarity.RARE, mage.cards.c.CrystallineGiant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Crystalline Giant", 387, Rarity.RARE, mage.cards.c.CrystallineGiant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cubwarden", 279, Rarity.RARE, mage.cards.c.Cubwarden.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cubwarden", 7, Rarity.RARE, mage.cards.c.Cubwarden.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cunning Nightbonder", 219, Rarity.UNCOMMON, mage.cards.c.CunningNightbonder.class)); + cards.add(new SetCardInfo("Dark Bargain", 82, Rarity.COMMON, mage.cards.d.DarkBargain.class)); + cards.add(new SetCardInfo("Daysquad Marshal", 8, Rarity.COMMON, mage.cards.d.DaysquadMarshal.class)); + cards.add(new SetCardInfo("Dead Weight", 83, Rarity.COMMON, mage.cards.d.DeadWeight.class)); + cards.add(new SetCardInfo("Death's Oasis", 182, Rarity.RARE, mage.cards.d.DeathsOasis.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Death's Oasis", 331, Rarity.RARE, mage.cards.d.DeathsOasis.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dire Tactics", 183, Rarity.UNCOMMON, mage.cards.d.DireTactics.class)); + cards.add(new SetCardInfo("Dirge Bat", 289, Rarity.RARE, mage.cards.d.DirgeBat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dirge Bat", 386, Rarity.RARE, mage.cards.d.DirgeBat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dirge Bat", 84, Rarity.RARE, mage.cards.d.DirgeBat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dismal Backwater", 246, Rarity.COMMON, mage.cards.d.DismalBackwater.class)); + cards.add(new SetCardInfo("Divine Arrow", 9, Rarity.COMMON, mage.cards.d.DivineArrow.class)); + cards.add(new SetCardInfo("Drannith Healer", 10, Rarity.COMMON, mage.cards.d.DrannithHealer.class)); + cards.add(new SetCardInfo("Drannith Magistrate", 11, Rarity.RARE, mage.cards.d.DrannithMagistrate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Drannith Magistrate", 314, Rarity.RARE, mage.cards.d.DrannithMagistrate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Drannith Stinger", 113, Rarity.COMMON, mage.cards.d.DrannithStinger.class)); + cards.add(new SetCardInfo("Dreamtail Heron", 284, Rarity.COMMON, mage.cards.d.DreamtailHeron.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dreamtail Heron", 47, Rarity.COMMON, mage.cards.d.DreamtailHeron.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Durable Coilbug", 85, Rarity.COMMON, mage.cards.d.DurableCoilbug.class)); + cards.add(new SetCardInfo("Duskfang Mentor", 86, Rarity.UNCOMMON, mage.cards.d.DuskfangMentor.class)); + cards.add(new SetCardInfo("Easy Prey", 87, Rarity.UNCOMMON, mage.cards.e.EasyPrey.class)); + cards.add(new SetCardInfo("Eerie Ultimatum", 184, Rarity.RARE, mage.cards.e.EerieUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eerie Ultimatum", 332, Rarity.RARE, mage.cards.e.EerieUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Emergent Ultimatum", 185, Rarity.RARE, mage.cards.e.EmergentUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Emergent Ultimatum", 333, Rarity.RARE, mage.cards.e.EmergentUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Escape Protocol", 48, Rarity.UNCOMMON, mage.cards.e.EscapeProtocol.class)); + cards.add(new SetCardInfo("Essence Scatter", 49, Rarity.COMMON, mage.cards.e.EssenceScatter.class)); + cards.add(new SetCardInfo("Essence Symbiote", 149, Rarity.COMMON, mage.cards.e.EssenceSymbiote.class)); + cards.add(new SetCardInfo("Everquill Phoenix", 114, Rarity.RARE, mage.cards.e.EverquillPhoenix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Everquill Phoenix", 292, Rarity.RARE, mage.cards.e.EverquillPhoenix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Everquill Phoenix", 374, Rarity.RARE, mage.cards.e.EverquillPhoenix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Evolving Wilds", 247, Rarity.COMMON, mage.cards.e.EvolvingWilds.class)); + cards.add(new SetCardInfo("Excavation Mole", 150, Rarity.COMMON, mage.cards.e.ExcavationMole.class)); + cards.add(new SetCardInfo("Extinction Event", 321, Rarity.RARE, mage.cards.e.ExtinctionEvent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Extinction Event", 88, Rarity.RARE, mage.cards.e.ExtinctionEvent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Exuberant Wolfbear", 151, Rarity.UNCOMMON, mage.cards.e.ExuberantWolfbear.class)); + cards.add(new SetCardInfo("Facet Reader", 50, Rarity.COMMON, mage.cards.f.FacetReader.class)); + cards.add(new SetCardInfo("Farfinder", 2, Rarity.COMMON, mage.cards.f.Farfinder.class)); + cards.add(new SetCardInfo("Ferocious Tigorilla", 115, Rarity.COMMON, mage.cards.f.FerociousTigorilla.class)); + cards.add(new SetCardInfo("Fertilid", 152, Rarity.COMMON, mage.cards.f.Fertilid.class)); + cards.add(new SetCardInfo("Fiend Artisan", 220, Rarity.MYTHIC, mage.cards.f.FiendArtisan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fiend Artisan", 350, Rarity.MYTHIC, mage.cards.f.FiendArtisan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fight as One", 12, Rarity.UNCOMMON, mage.cards.f.FightAsOne.class)); + cards.add(new SetCardInfo("Fire Prophecy", 116, Rarity.COMMON, mage.cards.f.FireProphecy.class)); + cards.add(new SetCardInfo("Flame Spill", 117, Rarity.UNCOMMON, mage.cards.f.FlameSpill.class)); + cards.add(new SetCardInfo("Flourishing Fox", 13, Rarity.UNCOMMON, mage.cards.f.FlourishingFox.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Flourishing Fox", 365, Rarity.UNCOMMON, mage.cards.f.FlourishingFox.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Flycatcher Giraffid", 153, Rarity.COMMON, mage.cards.f.FlycatcherGiraffid.class)); + cards.add(new SetCardInfo("Footfall Crater", 118, Rarity.UNCOMMON, mage.cards.f.FootfallCrater.class)); + cards.add(new SetCardInfo("Forbidden Friendship", 119, Rarity.COMMON, mage.cards.f.ForbiddenFriendship.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forbidden Friendship", 367, Rarity.COMMON, mage.cards.f.ForbiddenFriendship.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 272, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 273, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 274, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frenzied Raptor", 120, Rarity.COMMON, mage.cards.f.FrenziedRaptor.class)); + cards.add(new SetCardInfo("Frillscare Mentor", 121, Rarity.UNCOMMON, mage.cards.f.FrillscareMentor.class)); + cards.add(new SetCardInfo("Frondland Felidar", 186, Rarity.RARE, mage.cards.f.FrondlandFelidar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frondland Felidar", 334, Rarity.RARE, mage.cards.f.FrondlandFelidar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frost Lynx", 51, Rarity.COMMON, mage.cards.f.FrostLynx.class)); + cards.add(new SetCardInfo("Frostveil Ambush", 52, Rarity.COMMON, mage.cards.f.FrostveilAmbush.class)); + cards.add(new SetCardInfo("Fully Grown", 154, Rarity.COMMON, mage.cards.f.FullyGrown.class)); + cards.add(new SetCardInfo("Garrison Cat", 14, Rarity.COMMON, mage.cards.g.GarrisonCat.class)); + cards.add(new SetCardInfo("Gemrazer", 155, Rarity.RARE, mage.cards.g.Gemrazer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gemrazer", 295, Rarity.RARE, mage.cards.g.Gemrazer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gemrazer", 376, Rarity.RARE, mage.cards.g.Gemrazer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("General Kudro of Drannith", 187, Rarity.MYTHIC, mage.cards.g.GeneralKudroOfDrannith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("General Kudro of Drannith", 335, Rarity.MYTHIC, mage.cards.g.GeneralKudroOfDrannith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("General's Enforcer", 188, Rarity.UNCOMMON, mage.cards.g.GeneralsEnforcer.class)); + cards.add(new SetCardInfo("Genesis Ultimatum", 189, Rarity.RARE, mage.cards.g.GenesisUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Genesis Ultimatum", 336, Rarity.RARE, mage.cards.g.GenesisUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glimmerbell", 53, Rarity.COMMON, mage.cards.g.Glimmerbell.class)); + cards.add(new SetCardInfo("Gloom Pangolin", 89, Rarity.COMMON, mage.cards.g.GloomPangolin.class)); + cards.add(new SetCardInfo("Glowstone Recluse", 156, Rarity.UNCOMMON, mage.cards.g.GlowstoneRecluse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glowstone Recluse", 296, Rarity.UNCOMMON, mage.cards.g.GlowstoneRecluse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Go for Blood", 122, Rarity.COMMON, mage.cards.g.GoForBlood.class)); + cards.add(new SetCardInfo("Greater Sandwurm", 157, Rarity.COMMON, mage.cards.g.GreaterSandwurm.class)); + cards.add(new SetCardInfo("Grimdancer", 90, Rarity.UNCOMMON, mage.cards.g.Grimdancer.class)); + cards.add(new SetCardInfo("Gust of Wind", 54, Rarity.COMMON, mage.cards.g.GustOfWind.class)); + cards.add(new SetCardInfo("Gyruda, Doom of Depths", 221, Rarity.RARE, mage.cards.g.GyrudaDoomOfDepths.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gyruda, Doom of Depths", 351, Rarity.RARE, mage.cards.g.GyrudaDoomOfDepths.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gyruda, Doom of Depths", 384, Rarity.RARE, mage.cards.g.GyrudaDoomOfDepths.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hampering Snare", 55, Rarity.COMMON, mage.cards.h.HamperingSnare.class)); + cards.add(new SetCardInfo("Heartless Act", 366, Rarity.UNCOMMON, mage.cards.h.HeartlessAct.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Heartless Act", 91, Rarity.UNCOMMON, mage.cards.h.HeartlessAct.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Heightened Reflexes", 123, Rarity.COMMON, mage.cards.h.HeightenedReflexes.class)); + cards.add(new SetCardInfo("Helica Glider", 15, Rarity.COMMON, mage.cards.h.HelicaGlider.class)); + cards.add(new SetCardInfo("Honey Mammoth", 158, Rarity.COMMON, mage.cards.h.HoneyMammoth.class)); + cards.add(new SetCardInfo("Hornbash Mentor", 159, Rarity.UNCOMMON, mage.cards.h.HornbashMentor.class)); + cards.add(new SetCardInfo("Humble Naturalist", 160, Rarity.COMMON, mage.cards.h.HumbleNaturalist.class)); + cards.add(new SetCardInfo("Hunted Nightmare", 322, Rarity.RARE, mage.cards.h.HuntedNightmare.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hunted Nightmare", 92, Rarity.RARE, mage.cards.h.HuntedNightmare.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Huntmaster Liger", 16, Rarity.UNCOMMON, mage.cards.h.HuntmasterLiger.class)); + cards.add(new SetCardInfo("Huntmaster Liger", 280, Rarity.UNCOMMON, mage.cards.h.HuntmasterLiger.class)); + cards.add(new SetCardInfo("Huntmaster Liger", 370, Rarity.UNCOMMON, mage.cards.h.HuntmasterLiger.class)); + cards.add(new SetCardInfo("Illuna, Apex of Wishes", 190, Rarity.MYTHIC, mage.cards.i.IllunaApexOfWishes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Illuna, Apex of Wishes", 300, Rarity.MYTHIC, mage.cards.i.IllunaApexOfWishes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Illuna, Apex of Wishes", 379, Rarity.MYTHIC, mage.cards.i.IllunaApexOfWishes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Imposing Vantasaur", 17, Rarity.COMMON, mage.cards.i.ImposingVantasaur.class)); + cards.add(new SetCardInfo("Indatha Crystal", 235, Rarity.UNCOMMON, mage.cards.i.IndathaCrystal.class)); + cards.add(new SetCardInfo("Indatha Triome", 248, Rarity.RARE, mage.cards.i.IndathaTriome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Indatha Triome", 309, Rarity.RARE, mage.cards.i.IndathaTriome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Insatiable Hemophage", 290, Rarity.UNCOMMON, mage.cards.i.InsatiableHemophage.class)); + cards.add(new SetCardInfo("Insatiable Hemophage", 93, Rarity.UNCOMMON, mage.cards.i.InsatiableHemophage.class)); + cards.add(new SetCardInfo("Inspired Ultimatum", 191, Rarity.RARE, mage.cards.i.InspiredUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inspired Ultimatum", 337, Rarity.RARE, mage.cards.i.InspiredUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 263, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 264, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 265, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ivy Elemental", 161, Rarity.UNCOMMON, mage.cards.i.IvyElemental.class)); + cards.add(new SetCardInfo("Jegantha, the Wellspring", 222, Rarity.RARE, mage.cards.j.JeganthaTheWellspring.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jegantha, the Wellspring", 352, Rarity.RARE, mage.cards.j.JeganthaTheWellspring.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jubilant Skybonder", 223, Rarity.UNCOMMON, mage.cards.j.JubilantSkybonder.class)); + cards.add(new SetCardInfo("Jungle Hollow", 249, Rarity.COMMON, mage.cards.j.JungleHollow.class)); + cards.add(new SetCardInfo("Kaheera, the Orphanguard", 224, Rarity.RARE, mage.cards.k.KaheeraTheOrphanguard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kaheera, the Orphanguard", 353, Rarity.RARE, mage.cards.k.KaheeraTheOrphanguard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Keensight Mentor", 18, Rarity.UNCOMMON, mage.cards.k.KeensightMentor.class)); + cards.add(new SetCardInfo("Keep Safe", 56, Rarity.COMMON, mage.cards.k.KeepSafe.class)); + cards.add(new SetCardInfo("Keruga, the Macrosage", 225, Rarity.RARE, mage.cards.k.KerugaTheMacrosage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Keruga, the Macrosage", 354, Rarity.RARE, mage.cards.k.KerugaTheMacrosage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ketria Crystal", 236, Rarity.UNCOMMON, mage.cards.k.KetriaCrystal.class)); + cards.add(new SetCardInfo("Ketria Triome", 250, Rarity.RARE, mage.cards.k.KetriaTriome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ketria Triome", 310, Rarity.RARE, mage.cards.k.KetriaTriome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kinnan, Bonder Prodigy", 192, Rarity.MYTHIC, mage.cards.k.KinnanBonderProdigy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kinnan, Bonder Prodigy", 338, Rarity.MYTHIC, mage.cards.k.KinnanBonderProdigy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kogla, the Titan Ape", 162, Rarity.RARE, mage.cards.k.KoglaTheTitanApe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kogla, the Titan Ape", 328, Rarity.RARE, mage.cards.k.KoglaTheTitanApe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Labyrinth Raptor", 193, Rarity.RARE, mage.cards.l.LabyrinthRaptor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Labyrinth Raptor", 339, Rarity.RARE, mage.cards.l.LabyrinthRaptor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lava Serpent", 124, Rarity.COMMON, mage.cards.l.LavaSerpent.class)); + cards.add(new SetCardInfo("Lavabrink Venturer", 19, Rarity.RARE, mage.cards.l.LavabrinkVenturer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lavabrink Venturer", 315, Rarity.RARE, mage.cards.l.LavabrinkVenturer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lead the Stampede", 163, Rarity.UNCOMMON, mage.cards.l.LeadTheStampede.class)); + cards.add(new SetCardInfo("Light of Hope", 20, Rarity.COMMON, mage.cards.l.LightOfHope.class)); + cards.add(new SetCardInfo("Lore Drakkis", 194, Rarity.UNCOMMON, mage.cards.l.LoreDrakkis.class)); + cards.add(new SetCardInfo("Lore Drakkis", 301, Rarity.UNCOMMON, mage.cards.l.LoreDrakkis.class)); + cards.add(new SetCardInfo("Lukka, Coppercoat Outcast", 125, Rarity.MYTHIC, mage.cards.l.LukkaCoppercoatOutcast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lukka, Coppercoat Outcast", 276, Rarity.MYTHIC, mage.cards.l.LukkaCoppercoatOutcast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Luminous Broodmoth", 21, Rarity.MYTHIC, mage.cards.l.LuminousBroodmoth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Luminous Broodmoth", 316, Rarity.MYTHIC, mage.cards.l.LuminousBroodmoth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Luminous Broodmoth", 371, Rarity.MYTHIC, mage.cards.l.LuminousBroodmoth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lurking Deadeye", 94, Rarity.COMMON, mage.cards.l.LurkingDeadeye.class)); + cards.add(new SetCardInfo("Lurrus of the Dream-Den", 226, Rarity.RARE, mage.cards.l.LurrusOfTheDreamDen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lurrus of the Dream-Den", 355, Rarity.RARE, mage.cards.l.LurrusOfTheDreamDen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lutri, the Spellchaser", 227, Rarity.RARE, mage.cards.l.LutriTheSpellchaser.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lutri, the Spellchaser", 356, Rarity.RARE, mage.cards.l.LutriTheSpellchaser.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Majestic Auricorn", 22, Rarity.UNCOMMON, mage.cards.m.MajesticAuricorn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Majestic Auricorn", 281, Rarity.UNCOMMON, mage.cards.m.MajesticAuricorn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Maned Serval", 23, Rarity.COMMON, mage.cards.m.ManedServal.class)); + cards.add(new SetCardInfo("Memory Leak", 95, Rarity.COMMON, mage.cards.m.MemoryLeak.class)); + cards.add(new SetCardInfo("Migration Path", 164, Rarity.UNCOMMON, mage.cards.m.MigrationPath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Migration Path", 368, Rarity.UNCOMMON, mage.cards.m.MigrationPath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Migratory Greathorn", 165, Rarity.COMMON, mage.cards.m.MigratoryGreathorn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Migratory Greathorn", 297, Rarity.COMMON, mage.cards.m.MigratoryGreathorn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Momentum Rumbler", 126, Rarity.UNCOMMON, mage.cards.m.MomentumRumbler.class)); + cards.add(new SetCardInfo("Monstrous Step", 166, Rarity.UNCOMMON, mage.cards.m.MonstrousStep.class)); + cards.add(new SetCardInfo("Mosscoat Goriak", 167, Rarity.COMMON, mage.cards.m.MosscoatGoriak.class)); + cards.add(new SetCardInfo("Mountain", 269, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 270, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 271, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mutual Destruction", 96, Rarity.COMMON, mage.cards.m.MutualDestruction.class)); + cards.add(new SetCardInfo("Mysterious Egg", 3, Rarity.COMMON, mage.cards.m.MysteriousEgg.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mysterious Egg", 385, Rarity.COMMON, mage.cards.m.MysteriousEgg.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mystic Subdual", 57, Rarity.UNCOMMON, mage.cards.m.MysticSubdual.class)); + cards.add(new SetCardInfo("Mythos of Brokkos", 168, Rarity.RARE, mage.cards.m.MythosOfBrokkos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mythos of Brokkos", 329, Rarity.RARE, mage.cards.m.MythosOfBrokkos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mythos of Illuna", 318, Rarity.RARE, mage.cards.m.MythosOfIlluna.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mythos of Illuna", 58, Rarity.RARE, mage.cards.m.MythosOfIlluna.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mythos of Nethroi", 323, Rarity.RARE, mage.cards.m.MythosOfNethroi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mythos of Nethroi", 97, Rarity.RARE, mage.cards.m.MythosOfNethroi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mythos of Snapdax", 24, Rarity.RARE, mage.cards.m.MythosOfSnapdax.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mythos of Snapdax", 317, Rarity.RARE, mage.cards.m.MythosOfSnapdax.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mythos of Vadrok", 127, Rarity.RARE, mage.cards.m.MythosOfVadrok.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mythos of Vadrok", 324, Rarity.RARE, mage.cards.m.MythosOfVadrok.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Narset of the Ancient Way", 195, Rarity.MYTHIC, mage.cards.n.NarsetOfTheAncientWay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Narset of the Ancient Way", 278, Rarity.MYTHIC, mage.cards.n.NarsetOfTheAncientWay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Necropanther", 196, Rarity.UNCOMMON, mage.cards.n.Necropanther.class)); + cards.add(new SetCardInfo("Necropanther", 302, Rarity.UNCOMMON, mage.cards.n.Necropanther.class)); + cards.add(new SetCardInfo("Nethroi, Apex of Death", 197, Rarity.MYTHIC, mage.cards.n.NethroiApexOfDeath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nethroi, Apex of Death", 303, Rarity.MYTHIC, mage.cards.n.NethroiApexOfDeath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nethroi, Apex of Death", 380, Rarity.MYTHIC, mage.cards.n.NethroiApexOfDeath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Neutralize", 59, Rarity.UNCOMMON, mage.cards.n.Neutralize.class)); + cards.add(new SetCardInfo("Nightsquad Commando", 98, Rarity.COMMON, mage.cards.n.NightsquadCommando.class)); + cards.add(new SetCardInfo("Obosh, the Preypiercer", 228, Rarity.RARE, mage.cards.o.OboshThePreypiercer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Obosh, the Preypiercer", 357, Rarity.RARE, mage.cards.o.OboshThePreypiercer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Of One Mind", 60, Rarity.COMMON, mage.cards.o.OfOneMind.class)); + cards.add(new SetCardInfo("Offspring's Revenge", 198, Rarity.RARE, mage.cards.o.OffspringsRevenge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Offspring's Revenge", 340, Rarity.RARE, mage.cards.o.OffspringsRevenge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ominous Seas", 61, Rarity.UNCOMMON, mage.cards.o.OminousSeas.class)); + cards.add(new SetCardInfo("Pacifism", 25, Rarity.COMMON, mage.cards.p.Pacifism.class)); + cards.add(new SetCardInfo("Parcelbeast", 199, Rarity.UNCOMMON, mage.cards.p.Parcelbeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Parcelbeast", 304, Rarity.UNCOMMON, mage.cards.p.Parcelbeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Patagia Tiger", 26, Rarity.COMMON, mage.cards.p.PatagiaTiger.class)); + cards.add(new SetCardInfo("Perimeter Sergeant", 27, Rarity.COMMON, mage.cards.p.PerimeterSergeant.class)); + cards.add(new SetCardInfo("Phase Dolphin", 62, Rarity.COMMON, mage.cards.p.PhaseDolphin.class)); + cards.add(new SetCardInfo("Plains", 260, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 261, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 262, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plummet", 169, Rarity.COMMON, mage.cards.p.Plummet.class)); + cards.add(new SetCardInfo("Pollywog Symbiote", 372, Rarity.UNCOMMON, mage.cards.p.PollywogSymbiote.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pollywog Symbiote", 63, Rarity.UNCOMMON, mage.cards.p.PollywogSymbiote.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Porcuparrot", 128, Rarity.UNCOMMON, mage.cards.p.Porcuparrot.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Porcuparrot", 293, Rarity.UNCOMMON, mage.cards.p.Porcuparrot.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pouncing Shoreshark", 285, Rarity.UNCOMMON, mage.cards.p.PouncingShoreshark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pouncing Shoreshark", 64, Rarity.UNCOMMON, mage.cards.p.PouncingShoreshark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prickly Marmoset", 129, Rarity.COMMON, mage.cards.p.PricklyMarmoset.class)); + cards.add(new SetCardInfo("Primal Empathy", 200, Rarity.UNCOMMON, mage.cards.p.PrimalEmpathy.class)); + cards.add(new SetCardInfo("Proud Wildbonder", 229, Rarity.UNCOMMON, mage.cards.p.ProudWildbonder.class)); + cards.add(new SetCardInfo("Pyroceratops", 130, Rarity.COMMON, mage.cards.p.Pyroceratops.class)); + cards.add(new SetCardInfo("Quartzwood Crasher", 201, Rarity.RARE, mage.cards.q.QuartzwoodCrasher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quartzwood Crasher", 341, Rarity.RARE, mage.cards.q.QuartzwoodCrasher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Raking Claws", 131, Rarity.COMMON, mage.cards.r.RakingClaws.class)); + cards.add(new SetCardInfo("Ram Through", 170, Rarity.COMMON, mage.cards.r.RamThrough.class)); + cards.add(new SetCardInfo("Raugrin Crystal", 238, Rarity.UNCOMMON, mage.cards.r.RaugrinCrystal.class)); + cards.add(new SetCardInfo("Raugrin Triome", 251, Rarity.RARE, mage.cards.r.RaugrinTriome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Raugrin Triome", 311, Rarity.RARE, mage.cards.r.RaugrinTriome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reconnaissance Mission", 65, Rarity.UNCOMMON, mage.cards.r.ReconnaissanceMission.class)); + cards.add(new SetCardInfo("Regal Leosaur", 202, Rarity.UNCOMMON, mage.cards.r.RegalLeosaur.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Regal Leosaur", 305, Rarity.UNCOMMON, mage.cards.r.RegalLeosaur.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reptilian Reflection", 132, Rarity.UNCOMMON, mage.cards.r.ReptilianReflection.class)); + cards.add(new SetCardInfo("Rielle, the Everwise", 203, Rarity.MYTHIC, mage.cards.r.RielleTheEverwise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rielle, the Everwise", 342, Rarity.MYTHIC, mage.cards.r.RielleTheEverwise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rooting Moloch", 133, Rarity.UNCOMMON, mage.cards.r.RootingMoloch.class)); + cards.add(new SetCardInfo("Rugged Highlands", 252, Rarity.COMMON, mage.cards.r.RuggedHighlands.class)); + cards.add(new SetCardInfo("Ruinous Ultimatum", 204, Rarity.RARE, mage.cards.r.RuinousUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ruinous Ultimatum", 343, Rarity.RARE, mage.cards.r.RuinousUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rumbling Rockslide", 134, Rarity.COMMON, mage.cards.r.RumblingRockslide.class)); + cards.add(new SetCardInfo("Sanctuary Lockdown", 28, Rarity.UNCOMMON, mage.cards.s.SanctuaryLockdown.class)); + cards.add(new SetCardInfo("Sanctuary Smasher", 135, Rarity.UNCOMMON, mage.cards.s.SanctuarySmasher.class)); + cards.add(new SetCardInfo("Savai Crystal", 239, Rarity.UNCOMMON, mage.cards.s.SavaiCrystal.class)); + cards.add(new SetCardInfo("Savai Sabertooth", 29, Rarity.COMMON, mage.cards.s.SavaiSabertooth.class)); + cards.add(new SetCardInfo("Savai Thundermane", 205, Rarity.UNCOMMON, mage.cards.s.SavaiThundermane.class)); + cards.add(new SetCardInfo("Savai Triome", 253, Rarity.RARE, mage.cards.s.SavaiTriome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Savai Triome", 312, Rarity.RARE, mage.cards.s.SavaiTriome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scoured Barrens", 254, Rarity.COMMON, mage.cards.s.ScouredBarrens.class)); + cards.add(new SetCardInfo("Sea-Dasher Octopus", 286, Rarity.RARE, mage.cards.s.SeaDasherOctopus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sea-Dasher Octopus", 66, Rarity.RARE, mage.cards.s.SeaDasherOctopus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Serrated Scorpion", 99, Rarity.COMMON, mage.cards.s.SerratedScorpion.class)); + cards.add(new SetCardInfo("Shark Typhoon", 319, Rarity.RARE, mage.cards.s.SharkTyphoon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shark Typhoon", 67, Rarity.RARE, mage.cards.s.SharkTyphoon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shredded Sails", 136, Rarity.COMMON, mage.cards.s.ShreddedSails.class)); + cards.add(new SetCardInfo("Skull Prophet", 206, Rarity.UNCOMMON, mage.cards.s.SkullProphet.class)); + cards.add(new SetCardInfo("Skycat Sovereign", 207, Rarity.RARE, mage.cards.s.SkycatSovereign.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skycat Sovereign", 344, Rarity.RARE, mage.cards.s.SkycatSovereign.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sleeper Dart", 240, Rarity.COMMON, mage.cards.s.SleeperDart.class)); + cards.add(new SetCardInfo("Slitherwisp", 208, Rarity.RARE, mage.cards.s.Slitherwisp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Slitherwisp", 345, Rarity.RARE, mage.cards.s.Slitherwisp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snapdax, Apex of the Hunt", 209, Rarity.MYTHIC, mage.cards.s.SnapdaxApexOfTheHunt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snapdax, Apex of the Hunt", 306, Rarity.MYTHIC, mage.cards.s.SnapdaxApexOfTheHunt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snapdax, Apex of the Hunt", 381, Rarity.MYTHIC, mage.cards.s.SnapdaxApexOfTheHunt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snare Tactician", 30, Rarity.COMMON, mage.cards.s.SnareTactician.class)); + cards.add(new SetCardInfo("Solid Footing", 31, Rarity.COMMON, mage.cards.s.SolidFooting.class)); + cards.add(new SetCardInfo("Song of Creation", 210, Rarity.RARE, mage.cards.s.SongOfCreation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Song of Creation", 346, Rarity.RARE, mage.cards.s.SongOfCreation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sonorous Howlbonder", 230, Rarity.UNCOMMON, mage.cards.s.SonorousHowlbonder.class)); + cards.add(new SetCardInfo("Spelleater Wolverine", 137, Rarity.COMMON, mage.cards.s.SpelleaterWolverine.class)); + cards.add(new SetCardInfo("Splendor Mare", 32, Rarity.UNCOMMON, mage.cards.s.SplendorMare.class)); + cards.add(new SetCardInfo("Spontaneous Flight", 33, Rarity.COMMON, mage.cards.s.SpontaneousFlight.class)); + cards.add(new SetCardInfo("Springjaw Trap", 241, Rarity.COMMON, mage.cards.s.SpringjawTrap.class)); + cards.add(new SetCardInfo("Sprite Dragon", 211, Rarity.UNCOMMON, mage.cards.s.SpriteDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sprite Dragon", 369, Rarity.UNCOMMON, mage.cards.s.SpriteDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sprite Dragon", 382, Rarity.UNCOMMON, mage.cards.s.SpriteDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Startling Development", 68, Rarity.COMMON, mage.cards.s.StartlingDevelopment.class)); + cards.add(new SetCardInfo("Stormwild Capridor", 34, Rarity.UNCOMMON, mage.cards.s.StormwildCapridor.class)); + cards.add(new SetCardInfo("Sudden Spinnerets", 171, Rarity.COMMON, mage.cards.s.SuddenSpinnerets.class)); + cards.add(new SetCardInfo("Suffocating Fumes", 100, Rarity.COMMON, mage.cards.s.SuffocatingFumes.class)); + cards.add(new SetCardInfo("Survivors' Bond", 172, Rarity.COMMON, mage.cards.s.SurvivorsBond.class)); + cards.add(new SetCardInfo("Swallow Whole", 35, Rarity.UNCOMMON, mage.cards.s.SwallowWhole.class)); + cards.add(new SetCardInfo("Swamp", 266, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 267, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 268, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swiftwater Cliffs", 255, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class)); + cards.add(new SetCardInfo("Tentative Connection", 138, Rarity.COMMON, mage.cards.t.TentativeConnection.class)); + cards.add(new SetCardInfo("The Ozolith", 237, Rarity.RARE, mage.cards.t.TheOzolith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Ozolith", 362, Rarity.RARE, mage.cards.t.TheOzolith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thieving Otter", 69, Rarity.COMMON, mage.cards.t.ThievingOtter.class)); + cards.add(new SetCardInfo("Thornwood Falls", 256, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class)); + cards.add(new SetCardInfo("Thwart the Enemy", 173, Rarity.COMMON, mage.cards.t.ThwartTheEnemy.class)); + cards.add(new SetCardInfo("Titanoth Rex", 174, Rarity.UNCOMMON, mage.cards.t.TitanothRex.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Titanoth Rex", 377, Rarity.UNCOMMON, mage.cards.t.TitanothRex.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Titans' Nest", 212, Rarity.RARE, mage.cards.t.TitansNest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Titans' Nest", 347, Rarity.RARE, mage.cards.t.TitansNest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tranquil Cove", 257, Rarity.COMMON, mage.cards.t.TranquilCove.class)); + cards.add(new SetCardInfo("Trumpeting Gnarr", 213, Rarity.UNCOMMON, mage.cards.t.TrumpetingGnarr.class)); + cards.add(new SetCardInfo("Trumpeting Gnarr", 307, Rarity.UNCOMMON, mage.cards.t.TrumpetingGnarr.class)); + cards.add(new SetCardInfo("Umori, the Collector", 231, Rarity.RARE, mage.cards.u.UmoriTheCollector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Umori, the Collector", 358, Rarity.RARE, mage.cards.u.UmoriTheCollector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Unbreakable Bond", 101, Rarity.UNCOMMON, mage.cards.u.UnbreakableBond.class)); + cards.add(new SetCardInfo("Unexpected Fangs", 102, Rarity.COMMON, mage.cards.u.UnexpectedFangs.class)); + cards.add(new SetCardInfo("Unlikely Aid", 103, Rarity.COMMON, mage.cards.u.UnlikelyAid.class)); + cards.add(new SetCardInfo("Unpredictable Cyclone", 139, Rarity.RARE, mage.cards.u.UnpredictableCyclone.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Unpredictable Cyclone", 325, Rarity.RARE, mage.cards.u.UnpredictableCyclone.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vadrok, Apex of Thunder", 214, Rarity.MYTHIC, mage.cards.v.VadrokApexOfThunder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vadrok, Apex of Thunder", 308, Rarity.MYTHIC, mage.cards.v.VadrokApexOfThunder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vadrok, Apex of Thunder", 383, Rarity.MYTHIC, mage.cards.v.VadrokApexOfThunder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Valiant Rescuer", 36, Rarity.UNCOMMON, mage.cards.v.ValiantRescuer.class)); + cards.add(new SetCardInfo("Vivien, Monsters' Advocate", 175, Rarity.MYTHIC, mage.cards.v.VivienMonstersAdvocate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vivien, Monsters' Advocate", 277, Rarity.MYTHIC, mage.cards.v.VivienMonstersAdvocate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Void Beckoner", 104, Rarity.UNCOMMON, mage.cards.v.VoidBeckoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Void Beckoner", 373, Rarity.UNCOMMON, mage.cards.v.VoidBeckoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voracious Greatshark", 320, Rarity.RARE, mage.cards.v.VoraciousGreatshark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voracious Greatshark", 70, Rarity.RARE, mage.cards.v.VoraciousGreatshark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vulpikeet", 282, Rarity.COMMON, mage.cards.v.Vulpikeet.class)); + cards.add(new SetCardInfo("Vulpikeet", 37, Rarity.COMMON, mage.cards.v.Vulpikeet.class)); + cards.add(new SetCardInfo("Weaponize the Monsters", 140, Rarity.UNCOMMON, mage.cards.w.WeaponizeTheMonsters.class)); + cards.add(new SetCardInfo("Whirlwind of Thought", 215, Rarity.RARE, mage.cards.w.WhirlwindOfThought.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Whirlwind of Thought", 348, Rarity.RARE, mage.cards.w.WhirlwindOfThought.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Whisper Squad", 105, Rarity.COMMON, mage.cards.w.WhisperSquad.class)); + cards.add(new SetCardInfo("Will of the All-Hunter", 38, Rarity.UNCOMMON, mage.cards.w.WillOfTheAllHunter.class)); + cards.add(new SetCardInfo("Wilt", 176, Rarity.COMMON, mage.cards.w.Wilt.class)); + cards.add(new SetCardInfo("Wind-Scarred Crag", 258, Rarity.COMMON, mage.cards.w.WindScarredCrag.class)); + cards.add(new SetCardInfo("Wingfold Pteron", 71, Rarity.COMMON, mage.cards.w.WingfoldPteron.class)); + cards.add(new SetCardInfo("Wingspan Mentor", 72, Rarity.UNCOMMON, mage.cards.w.WingspanMentor.class)); + cards.add(new SetCardInfo("Winota, Joiner of Forces", 216, Rarity.MYTHIC, mage.cards.w.WinotaJoinerOfForces.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Winota, Joiner of Forces", 349, Rarity.MYTHIC, mage.cards.w.WinotaJoinerOfForces.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yidaro, Wandering Monster", 141, Rarity.RARE, mage.cards.y.YidaroWanderingMonster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yidaro, Wandering Monster", 326, Rarity.RARE, mage.cards.y.YidaroWanderingMonster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yidaro, Wandering Monster", 375, Rarity.RARE, mage.cards.y.YidaroWanderingMonster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yorion, Sky Nomad", 232, Rarity.RARE, mage.cards.y.YorionSkyNomad.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yorion, Sky Nomad", 359, Rarity.RARE, mage.cards.y.YorionSkyNomad.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zagoth Crystal", 242, Rarity.UNCOMMON, mage.cards.z.ZagothCrystal.class)); + cards.add(new SetCardInfo("Zagoth Mamba", 106, Rarity.UNCOMMON, mage.cards.z.ZagothMamba.class)); + cards.add(new SetCardInfo("Zagoth Triome", 259, Rarity.RARE, mage.cards.z.ZagothTriome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zagoth Triome", 313, Rarity.RARE, mage.cards.z.ZagothTriome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zenith Flare", 217, Rarity.UNCOMMON, mage.cards.z.ZenithFlare.class)); + cards.add(new SetCardInfo("Zilortha, Strength Incarnate", 275, Rarity.MYTHIC, mage.cards.z.ZilorthaStrengthIncarnate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zirda, the Dawnwaker", 233, Rarity.RARE, mage.cards.z.ZirdaTheDawnwaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zirda, the Dawnwaker", 360, Rarity.RARE, mage.cards.z.ZirdaTheDawnwaker.class, NON_FULL_USE_VARIOUS)); + + cards.removeIf(setCardInfo -> mutateNames.contains(setCardInfo.getName())); // remove when mutate is implemented + } + + @Override + public List getCardsByRarity(Rarity rarity) { + if (rarity != Rarity.COMMON) { + return super.getCardsByRarity(rarity); + } + List savedCardsInfos = savedCards.get(rarity); + if (savedCardsInfos != null) { + return new ArrayList(savedCardsInfos); + } + CardCriteria criteria = new CardCriteria(); + criteria.setCodes(this.code).notTypes(CardType.LAND); + criteria.rarities(rarity).doubleFaced(false); + savedCardsInfos = CardRepository.instance.findCards(criteria); + if (maxCardNumberInBooster != Integer.MAX_VALUE) { + savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); + } + criteria = new CardCriteria(); + criteria.setCodes(this.code).nameExact("Evolving Wilds"); + savedCardsInfos.addAll(CardRepository.instance.findCards(criteria)); + savedCards.put(rarity, savedCardsInfos); + // Return a copy of the saved cards information, as not to modify the original. + return new ArrayList(savedCardsInfos); + } + + @Override + // the common taplands replacing the basic land + public List getSpecialLand() { + if (savedSpecialLand.isEmpty()) { + CardCriteria criteria = new CardCriteria(); + criteria.setCodes(this.code); + criteria.rarities(Rarity.COMMON); + criteria.types(CardType.LAND); + savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); + savedSpecialLand.removeIf(cardInfo -> "Evolving Wilds".equals(cardInfo.getName())); + } + + return new ArrayList(savedSpecialLand); + } +} diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java new file mode 100644 index 0000000000..3a6af4c9cf --- /dev/null +++ b/Mage.Sets/src/mage/sets/Jumpstart.java @@ -0,0 +1,519 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class Jumpstart extends ExpansionSet { + + private static final Jumpstart instance = new Jumpstart(); + + public static Jumpstart getInstance() { + return instance; + } + + private Jumpstart() { + super("Jumpstart", "JMP", ExpansionSet.buildDate(2020, 7, 17), SetType.SUPPLEMENTAL); + this.blockName = "Jumpstart"; + this.hasBasicLands = true; + + cards.add(new SetCardInfo("Act of Treason", 289, Rarity.COMMON, mage.cards.a.ActOfTreason.class)); + cards.add(new SetCardInfo("Aegis Turtle", 138, Rarity.COMMON, mage.cards.a.AegisTurtle.class)); + cards.add(new SetCardInfo("Aegis of the Heavens", 79, Rarity.UNCOMMON, mage.cards.a.AegisOfTheHeavens.class)); + cards.add(new SetCardInfo("Aerial Assault", 80, Rarity.COMMON, mage.cards.a.AerialAssault.class)); + cards.add(new SetCardInfo("Aether Spellbomb", 456, Rarity.COMMON, mage.cards.a.AetherSpellbomb.class)); + cards.add(new SetCardInfo("Affa Guard Hound", 81, Rarity.UNCOMMON, mage.cards.a.AffaGuardHound.class)); + cards.add(new SetCardInfo("Affectionate Indrik", 373, Rarity.UNCOMMON, mage.cards.a.AffectionateIndrik.class)); + cards.add(new SetCardInfo("Aggressive Urge", 374, Rarity.COMMON, mage.cards.a.AggressiveUrge.class)); + cards.add(new SetCardInfo("Agonizing Syphon", 199, Rarity.COMMON, mage.cards.a.AgonizingSyphon.class)); + cards.add(new SetCardInfo("Ajani's Chosen", 82, Rarity.RARE, mage.cards.a.AjanisChosen.class)); + cards.add(new SetCardInfo("Alabaster Mage", 83, Rarity.UNCOMMON, mage.cards.a.AlabasterMage.class)); + cards.add(new SetCardInfo("Allosaurus Shepherd", 28, Rarity.MYTHIC, mage.cards.a.AllosaurusShepherd.class)); + cards.add(new SetCardInfo("Alloy Myr", 457, Rarity.COMMON, mage.cards.a.AlloyMyr.class)); + cards.add(new SetCardInfo("Ambassador Oak", 375, Rarity.COMMON, mage.cards.a.AmbassadorOak.class)); + cards.add(new SetCardInfo("Ancestral Statue", 458, Rarity.COMMON, mage.cards.a.AncestralStatue.class)); + cards.add(new SetCardInfo("Angel of Mercy", 84, Rarity.COMMON, mage.cards.a.AngelOfMercy.class)); + cards.add(new SetCardInfo("Angel of the Dire Hour", 85, Rarity.RARE, mage.cards.a.AngelOfTheDireHour.class)); + cards.add(new SetCardInfo("Angelic Arbiter", 86, Rarity.RARE, mage.cards.a.AngelicArbiter.class)); + cards.add(new SetCardInfo("Angelic Edict", 87, Rarity.COMMON, mage.cards.a.AngelicEdict.class)); + cards.add(new SetCardInfo("Angelic Page", 88, Rarity.COMMON, mage.cards.a.AngelicPage.class)); + cards.add(new SetCardInfo("Arbor Armament", 376, Rarity.COMMON, mage.cards.a.ArborArmament.class)); + cards.add(new SetCardInfo("Arcane Encyclopedia", 459, Rarity.UNCOMMON, mage.cards.a.ArcaneEncyclopedia.class)); + cards.add(new SetCardInfo("Archaeomender", 9, Rarity.COMMON, mage.cards.a.Archaeomender.class)); + cards.add(new SetCardInfo("Archon of Justice", 89, Rarity.RARE, mage.cards.a.ArchonOfJustice.class)); + cards.add(new SetCardInfo("Archon of Redemption", 90, Rarity.RARE, mage.cards.a.ArchonOfRedemption.class)); + cards.add(new SetCardInfo("Armorcraft Judge", 377, Rarity.UNCOMMON, mage.cards.a.ArmorcraftJudge.class)); + cards.add(new SetCardInfo("Ashmouth Hound", 290, Rarity.COMMON, mage.cards.a.AshmouthHound.class)); + cards.add(new SetCardInfo("Assassin's Strike", 200, Rarity.UNCOMMON, mage.cards.a.AssassinsStrike.class)); + cards.add(new SetCardInfo("Assault Formation", 378, Rarity.RARE, mage.cards.a.AssaultFormation.class)); + cards.add(new SetCardInfo("Auger Spree", 449, Rarity.COMMON, mage.cards.a.AugerSpree.class)); + cards.add(new SetCardInfo("Awakener Druid", 379, Rarity.UNCOMMON, mage.cards.a.AwakenerDruid.class)); + cards.add(new SetCardInfo("Bake into a Pie", 201, Rarity.COMMON, mage.cards.b.BakeIntoAPie.class)); + cards.add(new SetCardInfo("Ball Lightning", 291, Rarity.RARE, mage.cards.b.BallLightning.class)); + cards.add(new SetCardInfo("Barrage of Expendables", 292, Rarity.UNCOMMON, mage.cards.b.BarrageOfExpendables.class)); + cards.add(new SetCardInfo("Barter in Blood", 202, Rarity.UNCOMMON, mage.cards.b.BarterInBlood.class)); + cards.add(new SetCardInfo("Bathe in Dragonfire", 293, Rarity.COMMON, mage.cards.b.BatheInDragonfire.class)); + cards.add(new SetCardInfo("Battlefield Promotion", 91, Rarity.COMMON, mage.cards.b.BattlefieldPromotion.class)); + cards.add(new SetCardInfo("Battleground Geist", 139, Rarity.UNCOMMON, mage.cards.b.BattlegroundGeist.class)); + cards.add(new SetCardInfo("Beetleback Chief", 294, Rarity.UNCOMMON, mage.cards.b.BeetlebackChief.class)); + cards.add(new SetCardInfo("Befuddle", 140, Rarity.COMMON, mage.cards.b.Befuddle.class)); + cards.add(new SetCardInfo("Belltower Sphinx", 141, Rarity.UNCOMMON, mage.cards.b.BelltowerSphinx.class)); + cards.add(new SetCardInfo("Black Cat", 203, Rarity.COMMON, mage.cards.b.BlackCat.class)); + cards.add(new SetCardInfo("Black Market", 204, Rarity.RARE, mage.cards.b.BlackMarket.class)); + cards.add(new SetCardInfo("Blessed Sanctuary", 1, Rarity.RARE, mage.cards.b.BlessedSanctuary.class)); + cards.add(new SetCardInfo("Blessed Spirits", 92, Rarity.UNCOMMON, mage.cards.b.BlessedSpirits.class)); + cards.add(new SetCardInfo("Blighted Bat", 205, Rarity.COMMON, mage.cards.b.BlightedBat.class)); + cards.add(new SetCardInfo("Blindblast", 295, Rarity.COMMON, mage.cards.b.Blindblast.class)); + cards.add(new SetCardInfo("Blood Artist", 206, Rarity.UNCOMMON, mage.cards.b.BloodArtist.class)); + cards.add(new SetCardInfo("Blood Divination", 207, Rarity.UNCOMMON, mage.cards.b.BloodDivination.class)); + cards.add(new SetCardInfo("Blood Host", 208, Rarity.UNCOMMON, mage.cards.b.BloodHost.class)); + cards.add(new SetCardInfo("Bloodbond Vampire", 209, Rarity.UNCOMMON, mage.cards.b.BloodbondVampire.class)); + cards.add(new SetCardInfo("Bloodhunter Bat", 210, Rarity.COMMON, mage.cards.b.BloodhunterBat.class)); + cards.add(new SetCardInfo("Bloodrage Brawler", 296, Rarity.UNCOMMON, mage.cards.b.BloodrageBrawler.class)); + cards.add(new SetCardInfo("Bloodrock Cyclops", 297, Rarity.COMMON, mage.cards.b.BloodrockCyclops.class)); + cards.add(new SetCardInfo("Bloodshot Trainee", 298, Rarity.UNCOMMON, mage.cards.b.BloodshotTrainee.class)); + cards.add(new SetCardInfo("Bogbrew Witch", 211, Rarity.RARE, mage.cards.b.BogbrewWitch.class)); + cards.add(new SetCardInfo("Boggart Brute", 299, Rarity.COMMON, mage.cards.b.BoggartBrute.class)); + cards.add(new SetCardInfo("Bone Picker", 212, Rarity.UNCOMMON, mage.cards.b.BonePicker.class)); + cards.add(new SetCardInfo("Bone Splinters", 213, Rarity.COMMON, mage.cards.b.BoneSplinters.class)); + cards.add(new SetCardInfo("Borderland Marauder", 300, Rarity.COMMON, mage.cards.b.BorderlandMarauder.class)); + cards.add(new SetCardInfo("Borderland Minotaur", 301, Rarity.COMMON, mage.cards.b.BorderlandMinotaur.class)); + cards.add(new SetCardInfo("Branching Evolution", 29, Rarity.RARE, mage.cards.b.BranchingEvolution.class)); + cards.add(new SetCardInfo("Brightmare", 2, Rarity.UNCOMMON, mage.cards.b.Brightmare.class)); + cards.add(new SetCardInfo("Brindle Shoat", 380, Rarity.UNCOMMON, mage.cards.b.BrindleShoat.class)); + cards.add(new SetCardInfo("Brushstrider", 381, Rarity.UNCOMMON, mage.cards.b.Brushstrider.class)); + cards.add(new SetCardInfo("Bruvac the Grandiloquent", 10, Rarity.MYTHIC, mage.cards.b.BruvacTheGrandiloquent.class)); + cards.add(new SetCardInfo("Bubbling Cauldron", 460, Rarity.UNCOMMON, mage.cards.b.BubblingCauldron.class)); + cards.add(new SetCardInfo("Bulwark Giant", 93, Rarity.COMMON, mage.cards.b.BulwarkGiant.class)); + cards.add(new SetCardInfo("Burglar Rat", 214, Rarity.COMMON, mage.cards.b.BurglarRat.class)); + cards.add(new SetCardInfo("Buried Ruin", 491, Rarity.UNCOMMON, mage.cards.b.BuriedRuin.class)); + cards.add(new SetCardInfo("Cadaver Imp", 215, Rarity.COMMON, mage.cards.c.CadaverImp.class)); + cards.add(new SetCardInfo("Carven Caryatid", 382, Rarity.UNCOMMON, mage.cards.c.CarvenCaryatid.class)); + cards.add(new SetCardInfo("Cathar's Companion", 94, Rarity.COMMON, mage.cards.c.CatharsCompanion.class)); + cards.add(new SetCardInfo("Cathars' Crusade", 95, Rarity.RARE, mage.cards.c.CatharsCrusade.class)); + cards.add(new SetCardInfo("Cauldron Familiar", 216, Rarity.COMMON, mage.cards.c.CauldronFamiliar.class)); + cards.add(new SetCardInfo("Celestial Mantle", 96, Rarity.RARE, mage.cards.c.CelestialMantle.class)); + cards.add(new SetCardInfo("Cemetery Recruitment", 217, Rarity.COMMON, mage.cards.c.CemeteryRecruitment.class)); + cards.add(new SetCardInfo("Chain Lightning", 302, Rarity.UNCOMMON, mage.cards.c.ChainLightning.class)); + cards.add(new SetCardInfo("Chained Brute", 19, Rarity.UNCOMMON, mage.cards.c.ChainedBrute.class)); + cards.add(new SetCardInfo("Chamber Sentry", 461, Rarity.RARE, mage.cards.c.ChamberSentry.class)); + cards.add(new SetCardInfo("Champion of Lambholt", 383, Rarity.RARE, mage.cards.c.ChampionOfLambholt.class)); + cards.add(new SetCardInfo("Charmbreaker Devils", 303, Rarity.RARE, mage.cards.c.CharmbreakerDevils.class)); + cards.add(new SetCardInfo("Chart a Course", 142, Rarity.UNCOMMON, mage.cards.c.ChartACourse.class)); + cards.add(new SetCardInfo("Child of Night", 218, Rarity.COMMON, mage.cards.c.ChildOfNight.class)); + cards.add(new SetCardInfo("Chromatic Sphere", 462, Rarity.COMMON, mage.cards.c.ChromaticSphere.class)); + cards.add(new SetCardInfo("Cinder Elemental", 304, Rarity.UNCOMMON, mage.cards.c.CinderElemental.class)); + cards.add(new SetCardInfo("Cloudreader Sphinx", 143, Rarity.COMMON, mage.cards.c.CloudreaderSphinx.class)); + cards.add(new SetCardInfo("Cloudshift", 97, Rarity.COMMON, mage.cards.c.Cloudshift.class)); + cards.add(new SetCardInfo("Coastal Piracy", 144, Rarity.UNCOMMON, mage.cards.c.CoastalPiracy.class)); + cards.add(new SetCardInfo("Collateral Damage", 305, Rarity.COMMON, mage.cards.c.CollateralDamage.class)); + cards.add(new SetCardInfo("Commune with Dinosaurs", 384, Rarity.COMMON, mage.cards.c.CommuneWithDinosaurs.class)); + cards.add(new SetCardInfo("Corpse Hauler", 219, Rarity.COMMON, mage.cards.c.CorpseHauler.class)); + cards.add(new SetCardInfo("Corpse Traders", 220, Rarity.UNCOMMON, mage.cards.c.CorpseTraders.class)); + cards.add(new SetCardInfo("Corsair Captain", 11, Rarity.RARE, mage.cards.c.CorsairCaptain.class)); + cards.add(new SetCardInfo("Cradle of Vitality", 98, Rarity.RARE, mage.cards.c.CradleOfVitality.class)); + cards.add(new SetCardInfo("Craterhoof Behemoth", 385, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class)); + cards.add(new SetCardInfo("Crookclaw Transmuter", 145, Rarity.COMMON, mage.cards.c.CrookclawTransmuter.class)); + cards.add(new SetCardInfo("Crow of Dark Tidings", 221, Rarity.COMMON, mage.cards.c.CrowOfDarkTidings.class)); + cards.add(new SetCardInfo("Crushing Canopy", 386, Rarity.COMMON, mage.cards.c.CrushingCanopy.class)); + cards.add(new SetCardInfo("Cryptic Serpent", 146, Rarity.UNCOMMON, mage.cards.c.CrypticSerpent.class)); + cards.add(new SetCardInfo("Curiosity", 147, Rarity.UNCOMMON, mage.cards.c.Curiosity.class)); + cards.add(new SetCardInfo("Curious Obsession", 148, Rarity.UNCOMMON, mage.cards.c.CuriousObsession.class)); + cards.add(new SetCardInfo("Dance with Devils", 306, Rarity.UNCOMMON, mage.cards.d.DanceWithDevils.class)); + cards.add(new SetCardInfo("Dauntless Onslaught", 99, Rarity.UNCOMMON, mage.cards.d.DauntlessOnslaught.class)); + cards.add(new SetCardInfo("Dawntreader Elk", 387, Rarity.COMMON, mage.cards.d.DawntreaderElk.class)); + cards.add(new SetCardInfo("Death's Approach", 222, Rarity.COMMON, mage.cards.d.DeathsApproach.class)); + cards.add(new SetCardInfo("Departed Deckhand", 149, Rarity.UNCOMMON, mage.cards.d.DepartedDeckhand.class)); + cards.add(new SetCardInfo("Dinrova Horror", 450, Rarity.UNCOMMON, mage.cards.d.DinrovaHorror.class)); + cards.add(new SetCardInfo("Divine Arrow", 100, Rarity.COMMON, mage.cards.d.DivineArrow.class)); + cards.add(new SetCardInfo("Doublecast", 307, Rarity.UNCOMMON, mage.cards.d.Doublecast.class)); + cards.add(new SetCardInfo("Douse in Gloom", 223, Rarity.COMMON, mage.cards.d.DouseInGloom.class)); + cards.add(new SetCardInfo("Draconic Roar", 308, Rarity.UNCOMMON, mage.cards.d.DraconicRoar.class)); + cards.add(new SetCardInfo("Dragon Fodder", 309, Rarity.COMMON, mage.cards.d.DragonFodder.class)); + cards.add(new SetCardInfo("Dragon Hatchling", 310, Rarity.COMMON, mage.cards.d.DragonHatchling.class)); + cards.add(new SetCardInfo("Dragonloft Idol", 463, Rarity.UNCOMMON, mage.cards.d.DragonloftIdol.class)); + cards.add(new SetCardInfo("Dragonlord's Servant", 311, Rarity.UNCOMMON, mage.cards.d.DragonlordsServant.class)); + cards.add(new SetCardInfo("Dragonspeaker Shaman", 312, Rarity.UNCOMMON, mage.cards.d.DragonspeakerShaman.class)); + cards.add(new SetCardInfo("Drainpipe Vermin", 224, Rarity.COMMON, mage.cards.d.DrainpipeVermin.class)); + cards.add(new SetCardInfo("Drana, Liberator of Malakir", 225, Rarity.MYTHIC, mage.cards.d.DranaLiberatorOfMalakir.class)); + cards.add(new SetCardInfo("Dreamstone Hedron", 464, Rarity.UNCOMMON, mage.cards.d.DreamstoneHedron.class)); + cards.add(new SetCardInfo("Drover of the Mighty", 388, Rarity.UNCOMMON, mage.cards.d.DroverOfTheMighty.class)); + cards.add(new SetCardInfo("Dualcaster Mage", 313, Rarity.RARE, mage.cards.d.DualcasterMage.class)); + cards.add(new SetCardInfo("Duelist's Heritage", 101, Rarity.RARE, mage.cards.d.DuelistsHeritage.class)); + cards.add(new SetCardInfo("Dutiful Attendant", 226, Rarity.COMMON, mage.cards.d.DutifulAttendant.class)); + cards.add(new SetCardInfo("Dwynen's Elite", 389, Rarity.UNCOMMON, mage.cards.d.DwynensElite.class)); + cards.add(new SetCardInfo("Elemental Uprising", 390, Rarity.COMMON, mage.cards.e.ElementalUprising.class)); + cards.add(new SetCardInfo("Elvish Archdruid", 391, Rarity.RARE, mage.cards.e.ElvishArchdruid.class)); + cards.add(new SetCardInfo("Emancipation Angel", 102, Rarity.UNCOMMON, mage.cards.e.EmancipationAngel.class)); + cards.add(new SetCardInfo("Emiel the Blessed", 3, Rarity.MYTHIC, mage.cards.e.EmielTheBlessed.class)); + cards.add(new SetCardInfo("Enlarge", 392, Rarity.UNCOMMON, mage.cards.e.Enlarge.class)); + cards.add(new SetCardInfo("Entomber Exarch", 227, Rarity.UNCOMMON, mage.cards.e.EntomberExarch.class)); + cards.add(new SetCardInfo("Erratic Visionary", 150, Rarity.COMMON, mage.cards.e.ErraticVisionary.class)); + cards.add(new SetCardInfo("Essence Flux", 151, Rarity.UNCOMMON, mage.cards.e.EssenceFlux.class)); + cards.add(new SetCardInfo("Etali, Primal Storm", 314, Rarity.RARE, mage.cards.e.EtaliPrimalStorm.class)); + cards.add(new SetCardInfo("Eternal Taskmaster", 228, Rarity.UNCOMMON, mage.cards.e.EternalTaskmaster.class)); + cards.add(new SetCardInfo("Eternal Thirst", 229, Rarity.COMMON, mage.cards.e.EternalThirst.class)); + cards.add(new SetCardInfo("Exclude", 152, Rarity.UNCOMMON, mage.cards.e.Exclude.class)); + cards.add(new SetCardInfo("Exclusion Mage", 153, Rarity.UNCOMMON, mage.cards.e.ExclusionMage.class)); + cards.add(new SetCardInfo("Exhume", 230, Rarity.UNCOMMON, mage.cards.e.Exhume.class)); + cards.add(new SetCardInfo("Explore", 393, Rarity.COMMON, mage.cards.e.Explore.class)); + cards.add(new SetCardInfo("Exquisite Blood", 231, Rarity.RARE, mage.cards.e.ExquisiteBlood.class)); + cards.add(new SetCardInfo("Fa'adiyah Seer", 394, Rarity.COMMON, mage.cards.f.FaadiyahSeer.class)); + cards.add(new SetCardInfo("Face of Divinity", 103, Rarity.UNCOMMON, mage.cards.f.FaceOfDivinity.class)); + cards.add(new SetCardInfo("Falkenrath Noble", 232, Rarity.UNCOMMON, mage.cards.f.FalkenrathNoble.class)); + cards.add(new SetCardInfo("Fanatical Firebrand", 315, Rarity.COMMON, mage.cards.f.FanaticalFirebrand.class)); + cards.add(new SetCardInfo("Fell Specter", 233, Rarity.UNCOMMON, mage.cards.f.FellSpecter.class)); + cards.add(new SetCardInfo("Feral Hydra", 395, Rarity.UNCOMMON, mage.cards.f.FeralHydra.class)); + cards.add(new SetCardInfo("Feral Invocation", 396, Rarity.COMMON, mage.cards.f.FeralInvocation.class)); + cards.add(new SetCardInfo("Feral Prowler", 397, Rarity.COMMON, mage.cards.f.FeralProwler.class)); + cards.add(new SetCardInfo("Fertilid", 398, Rarity.COMMON, mage.cards.f.Fertilid.class)); + cards.add(new SetCardInfo("Festering Newt", 234, Rarity.COMMON, mage.cards.f.FesteringNewt.class)); + cards.add(new SetCardInfo("Flame Lash", 316, Rarity.COMMON, mage.cards.f.FlameLash.class)); + cards.add(new SetCardInfo("Flames of the Firebrand", 317, Rarity.UNCOMMON, mage.cards.f.FlamesOfTheFirebrand.class)); + cards.add(new SetCardInfo("Flames of the Raze-Boar", 318, Rarity.UNCOMMON, mage.cards.f.FlamesOfTheRazeBoar.class)); + cards.add(new SetCardInfo("Flametongue Kavu", 319, Rarity.UNCOMMON, mage.cards.f.FlametongueKavu.class)); + cards.add(new SetCardInfo("Fling", 320, Rarity.COMMON, mage.cards.f.Fling.class)); + cards.add(new SetCardInfo("Flurry of Horns", 321, Rarity.COMMON, mage.cards.f.FlurryOfHorns.class)); + cards.add(new SetCardInfo("Forced Worship", 104, Rarity.COMMON, mage.cards.f.ForcedWorship.class)); + cards.add(new SetCardInfo("Forest", 70, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 71, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 72, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 73, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 74, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 75, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 76, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 77, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forge Devil", 322, Rarity.COMMON, mage.cards.f.ForgeDevil.class)); + cards.add(new SetCardInfo("Fortify", 105, Rarity.COMMON, mage.cards.f.Fortify.class)); + cards.add(new SetCardInfo("Funeral Rites", 235, Rarity.COMMON, mage.cards.f.FuneralRites.class)); + cards.add(new SetCardInfo("Furnace Whelp", 323, Rarity.UNCOMMON, mage.cards.f.FurnaceWhelp.class)); + cards.add(new SetCardInfo("Fusion Elemental", 451, Rarity.UNCOMMON, mage.cards.f.FusionElemental.class)); + cards.add(new SetCardInfo("Gargoyle Sentinel", 465, Rarity.UNCOMMON, mage.cards.g.GargoyleSentinel.class)); + cards.add(new SetCardInfo("Ghalta, Primal Hunger", 399, Rarity.RARE, mage.cards.g.GhaltaPrimalHunger.class)); + cards.add(new SetCardInfo("Ghirapur Guide", 400, Rarity.UNCOMMON, mage.cards.g.GhirapurGuide.class)); + cards.add(new SetCardInfo("Ghoulcaller Gisa", 236, Rarity.MYTHIC, mage.cards.g.GhoulcallerGisa.class)); + cards.add(new SetCardInfo("Ghoulcaller's Accomplice", 237, Rarity.COMMON, mage.cards.g.GhoulcallersAccomplice.class)); + cards.add(new SetCardInfo("Ghoulraiser", 238, Rarity.COMMON, mage.cards.g.Ghoulraiser.class)); + cards.add(new SetCardInfo("Gifted Aetherborn", 239, Rarity.UNCOMMON, mage.cards.g.GiftedAetherborn.class)); + cards.add(new SetCardInfo("Gingerbrute", 466, Rarity.COMMON, mage.cards.g.Gingerbrute.class)); + cards.add(new SetCardInfo("Gird for Battle", 106, Rarity.UNCOMMON, mage.cards.g.GirdForBattle.class)); + cards.add(new SetCardInfo("Goblin Chieftain", 324, Rarity.RARE, mage.cards.g.GoblinChieftain.class)); + cards.add(new SetCardInfo("Goblin Commando", 325, Rarity.UNCOMMON, mage.cards.g.GoblinCommando.class)); + cards.add(new SetCardInfo("Goblin Goon", 326, Rarity.RARE, mage.cards.g.GoblinGoon.class)); + cards.add(new SetCardInfo("Goblin Instigator", 327, Rarity.COMMON, mage.cards.g.GoblinInstigator.class)); + cards.add(new SetCardInfo("Goblin Lore", 328, Rarity.UNCOMMON, mage.cards.g.GoblinLore.class)); + cards.add(new SetCardInfo("Goblin Rally", 329, Rarity.UNCOMMON, mage.cards.g.GoblinRally.class)); + cards.add(new SetCardInfo("Goblin Shortcutter", 330, Rarity.COMMON, mage.cards.g.GoblinShortcutter.class)); + cards.add(new SetCardInfo("Gonti, Lord of Luxury", 240, Rarity.RARE, mage.cards.g.GontiLordOfLuxury.class)); + cards.add(new SetCardInfo("Grave Bramble", 401, Rarity.COMMON, mage.cards.g.GraveBramble.class)); + cards.add(new SetCardInfo("Gravewaker", 241, Rarity.RARE, mage.cards.g.Gravewaker.class)); + cards.add(new SetCardInfo("Grim Lavamancer", 331, Rarity.RARE, mage.cards.g.GrimLavamancer.class)); + cards.add(new SetCardInfo("Gristle Grinner", 242, Rarity.UNCOMMON, mage.cards.g.GristleGrinner.class)); + cards.add(new SetCardInfo("Guardian Idol", 467, Rarity.UNCOMMON, mage.cards.g.GuardianIdol.class)); + cards.add(new SetCardInfo("Hamletback Goliath", 332, Rarity.RARE, mage.cards.h.HamletbackGoliath.class)); + cards.add(new SetCardInfo("Harvester of Souls", 243, Rarity.RARE, mage.cards.h.HarvesterOfSouls.class)); + cards.add(new SetCardInfo("Healer's Hawk", 107, Rarity.COMMON, mage.cards.h.HealersHawk.class)); + cards.add(new SetCardInfo("Heartfire", 333, Rarity.COMMON, mage.cards.h.Heartfire.class)); + cards.add(new SetCardInfo("Hedron Archive", 468, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class)); + cards.add(new SetCardInfo("Hellrider", 334, Rarity.RARE, mage.cards.h.Hellrider.class)); + cards.add(new SetCardInfo("Herald's Horn", 469, Rarity.UNCOMMON, mage.cards.h.HeraldsHorn.class)); + cards.add(new SetCardInfo("High Sentinels of Arashin", 108, Rarity.RARE, mage.cards.h.HighSentinelsOfArashin.class)); + cards.add(new SetCardInfo("Homing Lightning", 335, Rarity.UNCOMMON, mage.cards.h.HomingLightning.class)); + cards.add(new SetCardInfo("Hungry Flames", 336, Rarity.COMMON, mage.cards.h.HungryFlames.class)); + cards.add(new SetCardInfo("Hunter's Insight", 402, Rarity.UNCOMMON, mage.cards.h.HuntersInsight.class)); + cards.add(new SetCardInfo("Immolating Gyre", 20, Rarity.MYTHIC, mage.cards.i.ImmolatingGyre.class)); + cards.add(new SetCardInfo("Indomitable Will", 109, Rarity.COMMON, mage.cards.i.IndomitableWill.class)); + cards.add(new SetCardInfo("Inferno Hellion", 337, Rarity.UNCOMMON, mage.cards.i.InfernoHellion.class)); + cards.add(new SetCardInfo("Initiate's Companion", 403, Rarity.COMMON, mage.cards.i.InitiatesCompanion.class)); + cards.add(new SetCardInfo("Inniaz, the Gale Force", 12, Rarity.RARE, mage.cards.i.InniazTheGaleForce.class)); + cards.add(new SetCardInfo("Innocent Blood", 244, Rarity.COMMON, mage.cards.i.InnocentBlood.class)); + cards.add(new SetCardInfo("Inspired Charge", 110, Rarity.COMMON, mage.cards.i.InspiredCharge.class)); + cards.add(new SetCardInfo("Inspiring Call", 404, Rarity.UNCOMMON, mage.cards.i.InspiringCall.class)); + cards.add(new SetCardInfo("Inspiring Captain", 111, Rarity.COMMON, mage.cards.i.InspiringCaptain.class)); + cards.add(new SetCardInfo("Inspiring Unicorn", 112, Rarity.UNCOMMON, mage.cards.i.InspiringUnicorn.class)); + cards.add(new SetCardInfo("Ironroot Warlord", 452, Rarity.UNCOMMON, mage.cards.i.IronrootWarlord.class)); + cards.add(new SetCardInfo("Ironshell Beetle", 405, Rarity.COMMON, mage.cards.i.IronshellBeetle.class)); + cards.add(new SetCardInfo("Irresistible Prey", 406, Rarity.UNCOMMON, mage.cards.i.IrresistiblePrey.class)); + cards.add(new SetCardInfo("Isamaru, Hound of Konda", 113, Rarity.RARE, mage.cards.i.IsamaruHoundOfKonda.class)); + cards.add(new SetCardInfo("Island", 46, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 47, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 48, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 49, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 50, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 51, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 52, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 53, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jousting Dummy", 470, Rarity.COMMON, mage.cards.j.JoustingDummy.class)); + cards.add(new SetCardInfo("Juggernaut", 471, Rarity.UNCOMMON, mage.cards.j.Juggernaut.class)); + cards.add(new SetCardInfo("Kalastria Nightwatch", 245, Rarity.COMMON, mage.cards.k.KalastriaNightwatch.class)); + cards.add(new SetCardInfo("Keeper of Fables", 407, Rarity.UNCOMMON, mage.cards.k.KeeperOfFables.class)); + cards.add(new SetCardInfo("Kels, Fight Fixer", 15, Rarity.RARE, mage.cards.k.KelsFightFixer.class)); + cards.add(new SetCardInfo("Kiln Fiend", 338, Rarity.COMMON, mage.cards.k.KilnFiend.class)); + cards.add(new SetCardInfo("Kira, Great Glass-Spinner", 154, Rarity.RARE, mage.cards.k.KiraGreatGlassSpinner.class)); + cards.add(new SetCardInfo("Kitesail Corsair", 155, Rarity.COMMON, mage.cards.k.KitesailCorsair.class)); + cards.add(new SetCardInfo("Knight of the Tusk", 114, Rarity.COMMON, mage.cards.k.KnightOfTheTusk.class)); + cards.add(new SetCardInfo("Knightly Valor", 115, Rarity.COMMON, mage.cards.k.KnightlyValor.class)); + cards.add(new SetCardInfo("Kor Spiritdancer", 116, Rarity.RARE, mage.cards.k.KorSpiritdancer.class)); + cards.add(new SetCardInfo("Krenko, Mob Boss", 339, Rarity.RARE, mage.cards.k.KrenkoMobBoss.class)); + cards.add(new SetCardInfo("Languish", 246, Rarity.RARE, mage.cards.l.Languish.class)); + cards.add(new SetCardInfo("Last Gasp", 247, Rarity.COMMON, mage.cards.l.LastGasp.class)); + cards.add(new SetCardInfo("Lathliss, Dragon Queen", 340, Rarity.RARE, mage.cards.l.LathlissDragonQueen.class)); + cards.add(new SetCardInfo("Launch Party", 248, Rarity.COMMON, mage.cards.l.LaunchParty.class)); + cards.add(new SetCardInfo("Lawless Broker", 249, Rarity.COMMON, mage.cards.l.LawlessBroker.class)); + cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class)); + cards.add(new SetCardInfo("Leaf Gilder", 408, Rarity.COMMON, mage.cards.l.LeafGilder.class)); + cards.add(new SetCardInfo("Leave in the Dust", 156, Rarity.COMMON, mage.cards.l.LeaveInTheDust.class)); + cards.add(new SetCardInfo("Lena, Selfless Champion", 117, Rarity.RARE, mage.cards.l.LenaSelflessChampion.class)); + cards.add(new SetCardInfo("Lifecrafter's Gift", 409, Rarity.UNCOMMON, mage.cards.l.LifecraftersGift.class)); + cards.add(new SetCardInfo("Lightning Axe", 341, Rarity.UNCOMMON, mage.cards.l.LightningAxe.class)); + cards.add(new SetCardInfo("Lightning Bolt", 342, Rarity.UNCOMMON, mage.cards.l.LightningBolt.class)); + cards.add(new SetCardInfo("Lightning Diadem", 343, Rarity.COMMON, mage.cards.l.LightningDiadem.class)); + cards.add(new SetCardInfo("Lightning Elemental", 344, Rarity.COMMON, mage.cards.l.LightningElemental.class)); + cards.add(new SetCardInfo("Lightning Phoenix", 21, Rarity.RARE, mage.cards.l.LightningPhoenix.class)); + cards.add(new SetCardInfo("Lightning Shrieker", 345, Rarity.COMMON, mage.cards.l.LightningShrieker.class)); + cards.add(new SetCardInfo("Lightning Visionary", 22, Rarity.COMMON, mage.cards.l.LightningVisionary.class)); + cards.add(new SetCardInfo("Lightning-Core Excavator", 32, Rarity.COMMON, mage.cards.l.LightningCoreExcavator.class)); + cards.add(new SetCardInfo("Lightwalker", 118, Rarity.COMMON, mage.cards.l.Lightwalker.class)); + cards.add(new SetCardInfo("Liliana's Elite", 250, Rarity.UNCOMMON, mage.cards.l.LilianasElite.class)); + cards.add(new SetCardInfo("Liliana's Reaver", 251, Rarity.RARE, mage.cards.l.LilianasReaver.class)); + cards.add(new SetCardInfo("Linvala, Keeper of Silence", 119, Rarity.MYTHIC, mage.cards.l.LinvalaKeeperOfSilence.class)); + cards.add(new SetCardInfo("Living Lightning", 23, Rarity.UNCOMMON, mage.cards.l.LivingLightning.class)); + cards.add(new SetCardInfo("Long Road Home", 120, Rarity.UNCOMMON, mage.cards.l.LongRoadHome.class)); + cards.add(new SetCardInfo("Lurking Predators", 410, Rarity.RARE, mage.cards.l.LurkingPredators.class)); + cards.add(new SetCardInfo("Macabre Waltz", 252, Rarity.COMMON, mage.cards.m.MacabreWaltz.class)); + cards.add(new SetCardInfo("Maelstrom Archangel", 454, Rarity.MYTHIC, mage.cards.m.MaelstromArchangel.class)); + cards.add(new SetCardInfo("Magma Jet", 346, Rarity.UNCOMMON, mage.cards.m.MagmaJet.class)); + cards.add(new SetCardInfo("Magmaquake", 347, Rarity.RARE, mage.cards.m.Magmaquake.class)); + cards.add(new SetCardInfo("Makeshift Munitions", 348, Rarity.UNCOMMON, mage.cards.m.MakeshiftMunitions.class)); + cards.add(new SetCardInfo("Malakir Familiar", 253, Rarity.UNCOMMON, mage.cards.m.MalakirFamiliar.class)); + cards.add(new SetCardInfo("Mana Geode", 472, Rarity.COMMON, mage.cards.m.ManaGeode.class)); + cards.add(new SetCardInfo("Marauder's Axe", 473, Rarity.COMMON, mage.cards.m.MaraudersAxe.class)); + cards.add(new SetCardInfo("Mark of the Vampire", 254, Rarity.COMMON, mage.cards.m.MarkOfTheVampire.class)); + cards.add(new SetCardInfo("Mausoleum Turnkey", 255, Rarity.UNCOMMON, mage.cards.m.MausoleumTurnkey.class)); + cards.add(new SetCardInfo("Mentor of the Meek", 121, Rarity.RARE, mage.cards.m.MentorOfTheMeek.class)); + cards.add(new SetCardInfo("Mesa Unicorn", 122, Rarity.COMMON, mage.cards.m.MesaUnicorn.class)); + cards.add(new SetCardInfo("Meteor Golem", 474, Rarity.UNCOMMON, mage.cards.m.MeteorGolem.class)); + cards.add(new SetCardInfo("Miasmic Mummy", 256, Rarity.COMMON, mage.cards.m.MiasmicMummy.class)); + cards.add(new SetCardInfo("Mikaeus, the Lunarch", 123, Rarity.MYTHIC, mage.cards.m.MikaeusTheLunarch.class)); + cards.add(new SetCardInfo("Minotaur Skullcleaver", 349, Rarity.COMMON, mage.cards.m.MinotaurSkullcleaver.class)); + cards.add(new SetCardInfo("Minotaur Sureshot", 350, Rarity.COMMON, mage.cards.m.MinotaurSureshot.class)); + cards.add(new SetCardInfo("Mire Triton", 257, Rarity.UNCOMMON, mage.cards.m.MireTriton.class)); + cards.add(new SetCardInfo("Mirrodin's Core", 492, Rarity.UNCOMMON, mage.cards.m.MirrodinsCore.class)); + cards.add(new SetCardInfo("Molten Ravager", 351, Rarity.COMMON, mage.cards.m.MoltenRavager.class)); + cards.add(new SetCardInfo("Moment of Heroism", 124, Rarity.COMMON, mage.cards.m.MomentOfHeroism.class)); + cards.add(new SetCardInfo("Momentous Fall", 411, Rarity.RARE, mage.cards.m.MomentousFall.class)); + cards.add(new SetCardInfo("Mountain", 62, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 63, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 64, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 65, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 66, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 67, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 68, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 69, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mugging", 352, Rarity.COMMON, mage.cards.m.Mugging.class)); + cards.add(new SetCardInfo("Murmuring Phantasm", 157, Rarity.COMMON, mage.cards.m.MurmuringPhantasm.class)); + cards.add(new SetCardInfo("Muxus, Goblin Grandee", 24, Rarity.RARE, mage.cards.m.MuxusGoblinGrandee.class)); + cards.add(new SetCardInfo("Myr Sire", 475, Rarity.COMMON, mage.cards.m.MyrSire.class)); + cards.add(new SetCardInfo("Mystic Archaeologist", 158, Rarity.RARE, mage.cards.m.MysticArchaeologist.class)); + cards.add(new SetCardInfo("Narcolepsy", 159, Rarity.COMMON, mage.cards.n.Narcolepsy.class)); + cards.add(new SetCardInfo("Nature's Way", 412, Rarity.UNCOMMON, mage.cards.n.NaturesWay.class)); + cards.add(new SetCardInfo("Nebelgast Herald", 160, Rarity.UNCOMMON, mage.cards.n.NebelgastHerald.class)); + cards.add(new SetCardInfo("Nessian Hornbeetle", 413, Rarity.UNCOMMON, mage.cards.n.NessianHornbeetle.class)); + cards.add(new SetCardInfo("New Horizons", 414, Rarity.COMMON, mage.cards.n.NewHorizons.class)); + cards.add(new SetCardInfo("Neyith of the Dire Hunt", 30, Rarity.RARE, mage.cards.n.NeyithOfTheDireHunt.class)); + cards.add(new SetCardInfo("Nightshade Stinger", 258, Rarity.COMMON, mage.cards.n.NightshadeStinger.class)); + cards.add(new SetCardInfo("Nocturnal Feeder", 16, Rarity.COMMON, mage.cards.n.NocturnalFeeder.class)); + cards.add(new SetCardInfo("Nyxathid", 259, Rarity.RARE, mage.cards.n.Nyxathid.class)); + cards.add(new SetCardInfo("Octoprophet", 161, Rarity.COMMON, mage.cards.o.Octoprophet.class)); + cards.add(new SetCardInfo("Ogre Slumlord", 260, Rarity.RARE, mage.cards.o.OgreSlumlord.class)); + cards.add(new SetCardInfo("Oneirophage", 162, Rarity.UNCOMMON, mage.cards.o.Oneirophage.class)); + cards.add(new SetCardInfo("Oona's Blackguard", 261, Rarity.UNCOMMON, mage.cards.o.OonasBlackguard.class)); + cards.add(new SetCardInfo("Oracle of Mul Daya", 415, Rarity.RARE, mage.cards.o.OracleOfMulDaya.class)); + cards.add(new SetCardInfo("Orazca Frillback", 416, Rarity.COMMON, mage.cards.o.OrazcaFrillback.class)); + cards.add(new SetCardInfo("Ormos, Archive Keeper", 13, Rarity.RARE, mage.cards.o.OrmosArchiveKeeper.class)); + cards.add(new SetCardInfo("Ornery Goblin", 353, Rarity.COMMON, mage.cards.o.OrneryGoblin.class)); + cards.add(new SetCardInfo("Outnumber", 354, Rarity.COMMON, mage.cards.o.Outnumber.class)); + cards.add(new SetCardInfo("Overgrown Battlement", 417, Rarity.UNCOMMON, mage.cards.o.OvergrownBattlement.class)); + cards.add(new SetCardInfo("Pacifism", 125, Rarity.COMMON, mage.cards.p.Pacifism.class)); + cards.add(new SetCardInfo("Parasitic Implant", 262, Rarity.COMMON, mage.cards.p.ParasiticImplant.class)); + cards.add(new SetCardInfo("Path of Bravery", 126, Rarity.RARE, mage.cards.p.PathOfBravery.class)); + cards.add(new SetCardInfo("Path to Exile", 127, Rarity.UNCOMMON, mage.cards.p.PathToExile.class)); + cards.add(new SetCardInfo("Patron of the Valiant", 128, Rarity.UNCOMMON, mage.cards.p.PatronOfTheValiant.class)); + cards.add(new SetCardInfo("Peel from Reality", 163, Rarity.COMMON, mage.cards.p.PeelFromReality.class)); + cards.add(new SetCardInfo("Penumbra Bobcat", 418, Rarity.COMMON, mage.cards.p.PenumbraBobcat.class)); + cards.add(new SetCardInfo("Perilous Myr", 476, Rarity.UNCOMMON, mage.cards.p.PerilousMyr.class)); + cards.add(new SetCardInfo("Phyrexian Broodlings", 263, Rarity.COMMON, mage.cards.p.PhyrexianBroodlings.class)); + cards.add(new SetCardInfo("Phyrexian Debaser", 264, Rarity.COMMON, mage.cards.p.PhyrexianDebaser.class)); + cards.add(new SetCardInfo("Phyrexian Gargantua", 265, Rarity.UNCOMMON, mage.cards.p.PhyrexianGargantua.class)); + cards.add(new SetCardInfo("Phyrexian Rager", 266, Rarity.COMMON, mage.cards.p.PhyrexianRager.class)); + cards.add(new SetCardInfo("Phyrexian Reclamation", 267, Rarity.UNCOMMON, mage.cards.p.PhyrexianReclamation.class)); + cards.add(new SetCardInfo("Phyrexian Tower", 493, Rarity.RARE, mage.cards.p.PhyrexianTower.class)); + cards.add(new SetCardInfo("Pillar of Flame", 355, Rarity.COMMON, mage.cards.p.PillarOfFlame.class)); + cards.add(new SetCardInfo("Pirate's Cutlass", 477, Rarity.COMMON, mage.cards.p.PiratesCutlass.class)); + cards.add(new SetCardInfo("Plagued Rusalka", 268, Rarity.COMMON, mage.cards.p.PlaguedRusalka.class)); + cards.add(new SetCardInfo("Plains", 38, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 39, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 40, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 41, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 42, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 43, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 44, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 45, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pouncing Cheetah", 419, Rarity.COMMON, mage.cards.p.PouncingCheetah.class)); + cards.add(new SetCardInfo("Prescient Chimera", 164, Rarity.COMMON, mage.cards.p.PrescientChimera.class)); + cards.add(new SetCardInfo("Presence of Gond", 420, Rarity.COMMON, mage.cards.p.PresenceOfGond.class)); + cards.add(new SetCardInfo("Primeval Bounty", 421, Rarity.MYTHIC, mage.cards.p.PrimevalBounty.class)); + cards.add(new SetCardInfo("Primordial Sage", 422, Rarity.RARE, mage.cards.p.PrimordialSage.class)); + cards.add(new SetCardInfo("Prophetic Prism", 478, Rarity.COMMON, mage.cards.p.PropheticPrism.class)); + cards.add(new SetCardInfo("Prosperous Pirates", 165, Rarity.COMMON, mage.cards.p.ProsperousPirates.class)); + cards.add(new SetCardInfo("Pyroclastic Elemental", 356, Rarity.UNCOMMON, mage.cards.p.PyroclasticElemental.class)); + cards.add(new SetCardInfo("Rageblood Shaman", 357, Rarity.RARE, mage.cards.r.RagebloodShaman.class)); + cards.add(new SetCardInfo("Raging Regisaur", 455, Rarity.UNCOMMON, mage.cards.r.RagingRegisaur.class)); + cards.add(new SetCardInfo("Raise the Alarm", 129, Rarity.COMMON, mage.cards.r.RaiseTheAlarm.class)); + cards.add(new SetCardInfo("Rampaging Brontodon", 423, Rarity.RARE, mage.cards.r.RampagingBrontodon.class)); + cards.add(new SetCardInfo("Rapacious Dragon", 358, Rarity.UNCOMMON, mage.cards.r.RapaciousDragon.class)); + cards.add(new SetCardInfo("Rattlechains", 166, Rarity.RARE, mage.cards.r.Rattlechains.class)); + cards.add(new SetCardInfo("Ravenous Baloth", 424, Rarity.RARE, mage.cards.r.RavenousBaloth.class)); + cards.add(new SetCardInfo("Ravenous Chupacabra", 269, Rarity.UNCOMMON, mage.cards.r.RavenousChupacabra.class)); + cards.add(new SetCardInfo("Read the Runes", 167, Rarity.RARE, mage.cards.r.ReadTheRunes.class)); + cards.add(new SetCardInfo("Reanimate", 270, Rarity.RARE, mage.cards.r.Reanimate.class)); + cards.add(new SetCardInfo("Reckless Scholar", 168, Rarity.UNCOMMON, mage.cards.r.RecklessScholar.class)); + cards.add(new SetCardInfo("Release the Dogs", 4, Rarity.UNCOMMON, mage.cards.r.ReleaseTheDogs.class)); + cards.add(new SetCardInfo("Rhox Faithmender", 130, Rarity.RARE, mage.cards.r.RhoxFaithmender.class)); + cards.add(new SetCardInfo("Rhystic Study", 169, Rarity.RARE, mage.cards.r.RhysticStudy.class)); + cards.add(new SetCardInfo("Riddle of Lightning", 359, Rarity.UNCOMMON, mage.cards.r.RiddleOfLightning.class)); + cards.add(new SetCardInfo("Riptide Laboratory", 494, Rarity.RARE, mage.cards.r.RiptideLaboratory.class)); + cards.add(new SetCardInfo("Rise of the Dark Realms", 271, Rarity.MYTHIC, mage.cards.r.RiseOfTheDarkRealms.class)); + cards.add(new SetCardInfo("Rishadan Airship", 170, Rarity.COMMON, mage.cards.r.RishadanAirship.class)); + cards.add(new SetCardInfo("Rishkar, Peema Renegade", 425, Rarity.RARE, mage.cards.r.RishkarPeemaRenegade.class)); + cards.add(new SetCardInfo("Rogue's Gloves", 479, Rarity.UNCOMMON, mage.cards.r.RoguesGloves.class)); + cards.add(new SetCardInfo("Ronom Unicorn", 131, Rarity.COMMON, mage.cards.r.RonomUnicorn.class)); + cards.add(new SetCardInfo("Roving Keep", 480, Rarity.COMMON, mage.cards.r.RovingKeep.class)); + cards.add(new SetCardInfo("Rumbling Baloth", 426, Rarity.COMMON, mage.cards.r.RumblingBaloth.class)); + cards.add(new SetCardInfo("Runed Servitor", 481, Rarity.COMMON, mage.cards.r.RunedServitor.class)); + cards.add(new SetCardInfo("Rupture Spire", 495, Rarity.COMMON, mage.cards.r.RuptureSpire.class)); + cards.add(new SetCardInfo("Sage's Row Savant", 171, Rarity.COMMON, mage.cards.s.SagesRowSavant.class)); + cards.add(new SetCardInfo("Sailor of Means", 172, Rarity.COMMON, mage.cards.s.SailorOfMeans.class)); + cards.add(new SetCardInfo("Sangromancer", 272, Rarity.RARE, mage.cards.s.Sangromancer.class)); + cards.add(new SetCardInfo("Sanitarium Skeleton", 273, Rarity.COMMON, mage.cards.s.SanitariumSkeleton.class)); + cards.add(new SetCardInfo("Sarkhan's Rage", 360, Rarity.COMMON, mage.cards.s.SarkhansRage.class)); + cards.add(new SetCardInfo("Sarkhan's Unsealing", 361, Rarity.RARE, mage.cards.s.SarkhansUnsealing.class)); + cards.add(new SetCardInfo("Savage Stomp", 427, Rarity.UNCOMMON, mage.cards.s.SavageStomp.class)); + cards.add(new SetCardInfo("Scarecrone", 482, Rarity.RARE, mage.cards.s.Scarecrone.class)); + cards.add(new SetCardInfo("Scholar of the Lost Trove", 14, Rarity.RARE, mage.cards.s.ScholarOfTheLostTrove.class)); + cards.add(new SetCardInfo("Scourge of Nel Toth", 274, Rarity.RARE, mage.cards.s.ScourgeOfNelToth.class)); + cards.add(new SetCardInfo("Scroll of Avacyn", 483, Rarity.COMMON, mage.cards.s.ScrollOfAvacyn.class)); + cards.add(new SetCardInfo("Scrounging Bandar", 428, Rarity.COMMON, mage.cards.s.ScroungingBandar.class)); + cards.add(new SetCardInfo("Scuttlemutt", 484, Rarity.UNCOMMON, mage.cards.s.Scuttlemutt.class)); + cards.add(new SetCardInfo("Sea Gate Oracle", 173, Rarity.COMMON, mage.cards.s.SeaGateOracle.class)); + cards.add(new SetCardInfo("Seismic Elemental", 362, Rarity.UNCOMMON, mage.cards.s.SeismicElemental.class)); + cards.add(new SetCardInfo("Selhoff Occultist", 174, Rarity.COMMON, mage.cards.s.SelhoffOccultist.class)); + cards.add(new SetCardInfo("Selvala, Heart of the Wilds", 429, Rarity.MYTHIC, mage.cards.s.SelvalaHeartOfTheWilds.class)); + cards.add(new SetCardInfo("Sengir Vampire", 275, Rarity.UNCOMMON, mage.cards.s.SengirVampire.class)); + cards.add(new SetCardInfo("Serendib Efreet", 175, Rarity.RARE, mage.cards.s.SerendibEfreet.class)); + cards.add(new SetCardInfo("Serra Angel", 132, Rarity.UNCOMMON, mage.cards.s.SerraAngel.class)); + cards.add(new SetCardInfo("Sethron, Hurloon General", 25, Rarity.RARE, mage.cards.s.SethronHurloonGeneral.class)); + cards.add(new SetCardInfo("Settle the Score", 276, Rarity.UNCOMMON, mage.cards.s.SettleTheScore.class)); + cards.add(new SetCardInfo("Shambling Goblin", 277, Rarity.COMMON, mage.cards.s.ShamblingGoblin.class)); + cards.add(new SetCardInfo("Sharding Sphinx", 176, Rarity.RARE, mage.cards.s.ShardingSphinx.class)); + cards.add(new SetCardInfo("Sheoldred, Whispering One", 278, Rarity.MYTHIC, mage.cards.s.SheoldredWhisperingOne.class)); + cards.add(new SetCardInfo("Sigiled Starfish", 177, Rarity.UNCOMMON, mage.cards.s.SigiledStarfish.class)); + cards.add(new SetCardInfo("Signpost Scarecrow", 485, Rarity.COMMON, mage.cards.s.SignpostScarecrow.class)); + cards.add(new SetCardInfo("Silhana Wayfinder", 430, Rarity.UNCOMMON, mage.cards.s.SilhanaWayfinder.class)); + cards.add(new SetCardInfo("Sin Prodder", 363, Rarity.RARE, mage.cards.s.SinProdder.class)); + cards.add(new SetCardInfo("Skittering Surveyor", 486, Rarity.COMMON, mage.cards.s.SkitteringSurveyor.class)); + cards.add(new SetCardInfo("Sky Tether", 133, Rarity.UNCOMMON, mage.cards.s.SkyTether.class)); + cards.add(new SetCardInfo("Slate Street Ruffian", 279, Rarity.COMMON, mage.cards.s.SlateStreetRuffian.class)); + cards.add(new SetCardInfo("Somberwald Stag", 431, Rarity.UNCOMMON, mage.cards.s.SomberwaldStag.class)); + cards.add(new SetCardInfo("Soul Salvage", 280, Rarity.COMMON, mage.cards.s.SoulSalvage.class)); + cards.add(new SetCardInfo("Soul of the Harvest", 432, Rarity.RARE, mage.cards.s.SoulOfTheHarvest.class)); + cards.add(new SetCardInfo("Spectral Sailor", 178, Rarity.UNCOMMON, mage.cards.s.SpectralSailor.class)); + cards.add(new SetCardInfo("Spiteful Prankster", 26, Rarity.UNCOMMON, mage.cards.s.SpitefulPrankster.class)); + cards.add(new SetCardInfo("Spitting Earth", 364, Rarity.COMMON, mage.cards.s.SpittingEarth.class)); + cards.add(new SetCardInfo("Sporemound", 433, Rarity.COMMON, mage.cards.s.Sporemound.class)); + cards.add(new SetCardInfo("Stab Wound", 281, Rarity.UNCOMMON, mage.cards.s.StabWound.class)); + cards.add(new SetCardInfo("Steel-Plume Marshal", 5, Rarity.RARE, mage.cards.s.SteelPlumeMarshal.class)); + cards.add(new SetCardInfo("Stone Haven Pilgrim", 6, Rarity.UNCOMMON, mage.cards.s.StoneHavenPilgrim.class)); + cards.add(new SetCardInfo("Storm Sculptor", 179, Rarity.COMMON, mage.cards.s.StormSculptor.class)); + cards.add(new SetCardInfo("Supply Runners", 7, Rarity.UNCOMMON, mage.cards.s.SupplyRunners.class)); + cards.add(new SetCardInfo("Suspicious Bookcase", 487, Rarity.UNCOMMON, mage.cards.s.SuspiciousBookcase.class)); + cards.add(new SetCardInfo("Swamp", 54, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 55, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 56, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 57, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 58, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 59, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 60, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 61, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swarm of Bloodflies", 282, Rarity.UNCOMMON, mage.cards.s.SwarmOfBloodflies.class)); + cards.add(new SetCardInfo("Sweep Away", 180, Rarity.COMMON, mage.cards.s.SweepAway.class)); + cards.add(new SetCardInfo("Sylvan Brushstrider", 434, Rarity.COMMON, mage.cards.s.SylvanBrushstrider.class)); + cards.add(new SetCardInfo("Sylvan Ranger", 435, Rarity.COMMON, mage.cards.s.SylvanRanger.class)); + cards.add(new SetCardInfo("Take Heart", 134, Rarity.COMMON, mage.cards.t.TakeHeart.class)); + cards.add(new SetCardInfo("Talrand's Invocation", 182, Rarity.UNCOMMON, mage.cards.t.TalrandsInvocation.class)); + cards.add(new SetCardInfo("Talrand, Sky Summoner", 181, Rarity.RARE, mage.cards.t.TalrandSkySummoner.class)); + cards.add(new SetCardInfo("Tandem Tactics", 135, Rarity.COMMON, mage.cards.t.TandemTactics.class)); + cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class)); + cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class)); + cards.add(new SetCardInfo("Terrarion", 488, Rarity.COMMON, mage.cards.t.Terrarion.class)); + cards.add(new SetCardInfo("Thermo-Alchemist", 365, Rarity.COMMON, mage.cards.t.ThermoAlchemist.class)); + cards.add(new SetCardInfo("Thirst for Knowledge", 183, Rarity.UNCOMMON, mage.cards.t.ThirstForKnowledge.class)); + cards.add(new SetCardInfo("Thought Collapse", 184, Rarity.COMMON, mage.cards.t.ThoughtCollapse.class)); + cards.add(new SetCardInfo("Thought Scour", 185, Rarity.COMMON, mage.cards.t.ThoughtScour.class)); + cards.add(new SetCardInfo("Thragtusk", 436, Rarity.RARE, mage.cards.t.Thragtusk.class)); + cards.add(new SetCardInfo("Thriving Bluff", 33, Rarity.COMMON, mage.cards.t.ThrivingBluff.class)); + cards.add(new SetCardInfo("Thriving Grove", 34, Rarity.COMMON, mage.cards.t.ThrivingGrove.class)); + cards.add(new SetCardInfo("Thriving Heath", 35, Rarity.COMMON, mage.cards.t.ThrivingHeath.class)); + cards.add(new SetCardInfo("Thriving Isle", 36, Rarity.COMMON, mage.cards.t.ThrivingIsle.class)); + cards.add(new SetCardInfo("Thriving Moor", 37, Rarity.COMMON, mage.cards.t.ThrivingMoor.class)); + cards.add(new SetCardInfo("Thundering Spineback", 437, Rarity.UNCOMMON, mage.cards.t.ThunderingSpineback.class)); + cards.add(new SetCardInfo("Tibalt's Rager", 366, Rarity.UNCOMMON, mage.cards.t.TibaltsRager.class)); + cards.add(new SetCardInfo("Time to Feed", 438, Rarity.COMMON, mage.cards.t.TimeToFeed.class)); + cards.add(new SetCardInfo("Tinybones, Trinket Thief", 17, Rarity.MYTHIC, mage.cards.t.TinybonesTrinketThief.class)); + cards.add(new SetCardInfo("Tithebearer Giant", 284, Rarity.COMMON, mage.cards.t.TithebearerGiant.class)); + cards.add(new SetCardInfo("Torch Fiend", 367, Rarity.COMMON, mage.cards.t.TorchFiend.class)); + cards.add(new SetCardInfo("Towering Titan", 31, Rarity.MYTHIC, mage.cards.t.ToweringTitan.class)); + cards.add(new SetCardInfo("Towering-Wave Mystic", 186, Rarity.COMMON, mage.cards.t.ToweringWaveMystic.class)); + cards.add(new SetCardInfo("Trusty Retriever", 8, Rarity.COMMON, mage.cards.t.TrustyRetriever.class)); + cards.add(new SetCardInfo("Ulvenwald Hydra", 439, Rarity.MYTHIC, mage.cards.u.UlvenwaldHydra.class)); + cards.add(new SetCardInfo("Unstable Obelisk", 489, Rarity.UNCOMMON, mage.cards.u.UnstableObelisk.class)); + cards.add(new SetCardInfo("Valorous Stance", 136, Rarity.UNCOMMON, mage.cards.v.ValorousStance.class)); + cards.add(new SetCardInfo("Vampire Neonate", 285, Rarity.COMMON, mage.cards.v.VampireNeonate.class)); + cards.add(new SetCardInfo("Vastwood Zendikon", 440, Rarity.COMMON, mage.cards.v.VastwoodZendikon.class)); + cards.add(new SetCardInfo("Vedalken Archmage", 187, Rarity.RARE, mage.cards.v.VedalkenArchmage.class)); + cards.add(new SetCardInfo("Vedalken Entrancer", 188, Rarity.COMMON, mage.cards.v.VedalkenEntrancer.class)); + cards.add(new SetCardInfo("Verdant Embrace", 441, Rarity.RARE, mage.cards.v.VerdantEmbrace.class)); + cards.add(new SetCardInfo("Voice of the Provinces", 137, Rarity.COMMON, mage.cards.v.VoiceOfTheProvinces.class)); + cards.add(new SetCardInfo("Volcanic Fallout", 368, Rarity.UNCOMMON, mage.cards.v.VolcanicFallout.class)); + cards.add(new SetCardInfo("Volley Veteran", 369, Rarity.UNCOMMON, mage.cards.v.VolleyVeteran.class)); + cards.add(new SetCardInfo("Voyage's End", 189, Rarity.COMMON, mage.cards.v.VoyagesEnd.class)); + cards.add(new SetCardInfo("Wailing Ghoul", 286, Rarity.COMMON, mage.cards.w.WailingGhoul.class)); + cards.add(new SetCardInfo("Wall of Blossoms", 442, Rarity.UNCOMMON, mage.cards.w.WallOfBlossoms.class)); + cards.add(new SetCardInfo("Wall of Lost Thoughts", 190, Rarity.UNCOMMON, mage.cards.w.WallOfLostThoughts.class)); + cards.add(new SetCardInfo("Wall of Vines", 443, Rarity.COMMON, mage.cards.w.WallOfVines.class)); + cards.add(new SetCardInfo("Warden of Evos Isle", 191, Rarity.UNCOMMON, mage.cards.w.WardenOfEvosIsle.class)); + cards.add(new SetCardInfo("Warfire Javelineer", 370, Rarity.UNCOMMON, mage.cards.w.WarfireJavelineer.class)); + cards.add(new SetCardInfo("Warmonger's Chariot", 490, Rarity.UNCOMMON, mage.cards.w.WarmongersChariot.class)); + cards.add(new SetCardInfo("Waterknot", 192, Rarity.COMMON, mage.cards.w.Waterknot.class)); + cards.add(new SetCardInfo("Weaver of Lightning", 371, Rarity.UNCOMMON, mage.cards.w.WeaverOfLightning.class)); + cards.add(new SetCardInfo("Whelming Wave", 193, Rarity.RARE, mage.cards.w.WhelmingWave.class)); + cards.add(new SetCardInfo("Wight of Precinct Six", 287, Rarity.COMMON, mage.cards.w.WightOfPrecinctSix.class)); + cards.add(new SetCardInfo("Wildheart Invoker", 444, Rarity.COMMON, mage.cards.w.WildheartInvoker.class)); + cards.add(new SetCardInfo("Wildsize", 445, Rarity.COMMON, mage.cards.w.Wildsize.class)); + cards.add(new SetCardInfo("Windreader Sphinx", 194, Rarity.RARE, mage.cards.w.WindreaderSphinx.class)); + cards.add(new SetCardInfo("Windstorm Drake", 195, Rarity.UNCOMMON, mage.cards.w.WindstormDrake.class)); + cards.add(new SetCardInfo("Winged Words", 196, Rarity.COMMON, mage.cards.w.WingedWords.class)); + cards.add(new SetCardInfo("Wishful Merfolk", 197, Rarity.COMMON, mage.cards.w.WishfulMerfolk.class)); + cards.add(new SetCardInfo("Witch of the Moors", 18, Rarity.RARE, mage.cards.w.WitchOfTheMoors.class)); + cards.add(new SetCardInfo("Wizard's Retort", 198, Rarity.UNCOMMON, mage.cards.w.WizardsRetort.class)); + cards.add(new SetCardInfo("Woodborn Behemoth", 446, Rarity.UNCOMMON, mage.cards.w.WoodbornBehemoth.class)); + cards.add(new SetCardInfo("Wren's Run Vanquisher", 447, Rarity.UNCOMMON, mage.cards.w.WrensRunVanquisher.class)); + cards.add(new SetCardInfo("Young Pyromancer", 372, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class)); + cards.add(new SetCardInfo("Zendikar's Roil", 448, Rarity.UNCOMMON, mage.cards.z.ZendikarsRoil.class)); + cards.add(new SetCardInfo("Zombie Infestation", 288, Rarity.UNCOMMON, mage.cards.z.ZombieInfestation.class)); + cards.add(new SetCardInfo("Zurzoth, Chaos Rider", 27, Rarity.RARE, mage.cards.z.ZurzothChaosRider.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index c62c211ba4..b61739ac0a 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -1,329 +1,329 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author North - */ -public final class Legends extends ExpansionSet { - - private static final Legends instance = new Legends(); - - public static Legends getInstance() { - return instance; - } - - private Legends() { - super("Legends", "LEG", ExpansionSet.buildDate(1994, 6, 1), SetType.EXPANSION); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Abomination", 87, Rarity.UNCOMMON, mage.cards.a.Abomination.class)); - cards.add(new SetCardInfo("Acid Rain", 44, Rarity.RARE, mage.cards.a.AcidRain.class)); - cards.add(new SetCardInfo("Active Volcano", 130, Rarity.COMMON, mage.cards.a.ActiveVolcano.class)); - cards.add(new SetCardInfo("Adun Oakenshield", 216, Rarity.RARE, mage.cards.a.AdunOakenshield.class)); - cards.add(new SetCardInfo("Adventurers' Guildhouse", 300, Rarity.UNCOMMON, mage.cards.a.AdventurersGuildhouse.class)); - cards.add(new SetCardInfo("Aerathi Berserker", 131, Rarity.UNCOMMON, mage.cards.a.AerathiBerserker.class)); - cards.add(new SetCardInfo("Aisling Leprechaun", 173, Rarity.COMMON, mage.cards.a.AislingLeprechaun.class)); - cards.add(new SetCardInfo("Akron Legionnaire", 1, Rarity.RARE, mage.cards.a.AkronLegionnaire.class)); - cards.add(new SetCardInfo("Al-abara's Carpet", 271, Rarity.RARE, mage.cards.a.AlAbarasCarpet.class)); - cards.add(new SetCardInfo("Alabaster Potion", 2, Rarity.COMMON, mage.cards.a.AlabasterPotion.class)); - cards.add(new SetCardInfo("Alchor's Tomb", 272, Rarity.RARE, mage.cards.a.AlchorsTomb.class)); - cards.add(new SetCardInfo("All Hallow's Eve", 88, Rarity.RARE, mage.cards.a.AllHallowsEve.class)); - cards.add(new SetCardInfo("Amrou Kithkin", 3, Rarity.COMMON, mage.cards.a.AmrouKithkin.class)); - cards.add(new SetCardInfo("Angelic Voices", 4, Rarity.RARE, mage.cards.a.AngelicVoices.class)); - cards.add(new SetCardInfo("Angus Mackenzie", 217, Rarity.RARE, mage.cards.a.AngusMackenzie.class)); - cards.add(new SetCardInfo("Anti-Magic Aura", 45, Rarity.COMMON, mage.cards.a.AntiMagicAura.class)); - cards.add(new SetCardInfo("Arboria", 174, Rarity.UNCOMMON, mage.cards.a.Arboria.class)); - cards.add(new SetCardInfo("Arcades Sabboth", 218, Rarity.RARE, mage.cards.a.ArcadesSabboth.class)); - cards.add(new SetCardInfo("Arena of the Ancients", 273, Rarity.RARE, mage.cards.a.ArenaOfTheAncients.class)); - cards.add(new SetCardInfo("Avoid Fate", 175, Rarity.COMMON, mage.cards.a.AvoidFate.class)); - cards.add(new SetCardInfo("Axelrod Gunnarson", 219, Rarity.RARE, mage.cards.a.AxelrodGunnarson.class)); - cards.add(new SetCardInfo("Ayesha Tanaka", 220, Rarity.RARE, mage.cards.a.AyeshaTanaka.class)); - cards.add(new SetCardInfo("Azure Drake", 46, Rarity.UNCOMMON, mage.cards.a.AzureDrake.class)); - cards.add(new SetCardInfo("Backfire", 47, Rarity.UNCOMMON, mage.cards.b.Backfire.class)); - cards.add(new SetCardInfo("Barbary Apes", 176, Rarity.COMMON, mage.cards.b.BarbaryApes.class)); - cards.add(new SetCardInfo("Barktooth Warbeard", 221, Rarity.UNCOMMON, mage.cards.b.BarktoothWarbeard.class)); - cards.add(new SetCardInfo("Bartel Runeaxe", 222, Rarity.RARE, mage.cards.b.BartelRuneaxe.class)); - cards.add(new SetCardInfo("Beasts of Bogardan", 133, Rarity.UNCOMMON, mage.cards.b.BeastsOfBogardan.class)); - cards.add(new SetCardInfo("Black Mana Battery", 274, Rarity.UNCOMMON, mage.cards.b.BlackManaBattery.class)); - cards.add(new SetCardInfo("Blazing Effigy", 134, Rarity.COMMON, mage.cards.b.BlazingEffigy.class)); - cards.add(new SetCardInfo("Blight", 89, Rarity.UNCOMMON, mage.cards.b.Blight.class)); - cards.add(new SetCardInfo("Blood Lust", 135, Rarity.UNCOMMON, mage.cards.b.BloodLust.class)); - cards.add(new SetCardInfo("Blue Mana Battery", 275, Rarity.UNCOMMON, mage.cards.b.BlueManaBattery.class)); - cards.add(new SetCardInfo("Boomerang", 48, Rarity.COMMON, mage.cards.b.Boomerang.class)); - cards.add(new SetCardInfo("Boris Devilboon", 223, Rarity.RARE, mage.cards.b.BorisDevilboon.class)); - cards.add(new SetCardInfo("Brine Hag", 49, Rarity.UNCOMMON, mage.cards.b.BrineHag.class)); - cards.add(new SetCardInfo("Bronze Horse", 276, Rarity.RARE, mage.cards.b.BronzeHorse.class)); - cards.add(new SetCardInfo("Carrion Ants", 90, Rarity.RARE, mage.cards.c.CarrionAnts.class)); - cards.add(new SetCardInfo("Cat Warriors", 177, Rarity.COMMON, mage.cards.c.CatWarriors.class)); - cards.add(new SetCardInfo("Cathedral of Serra", 301, Rarity.UNCOMMON, mage.cards.c.CathedralOfSerra.class)); - cards.add(new SetCardInfo("Caverns of Despair", 136, Rarity.RARE, mage.cards.c.CavernsOfDespair.class)); - cards.add(new SetCardInfo("Chain Lightning", 137, Rarity.COMMON, mage.cards.c.ChainLightning.class)); - cards.add(new SetCardInfo("Chains of Mephistopheles", 91, Rarity.RARE, mage.cards.c.ChainsOfMephistopheles.class)); - cards.add(new SetCardInfo("Chromium", 224, Rarity.RARE, mage.cards.c.Chromium.class)); - cards.add(new SetCardInfo("Cleanse", 5, Rarity.RARE, mage.cards.c.Cleanse.class)); - cards.add(new SetCardInfo("Clergy of the Holy Nimbus", 6, Rarity.COMMON, mage.cards.c.ClergyOfTheHolyNimbus.class)); - cards.add(new SetCardInfo("Concordant Crossroads", 179, Rarity.RARE, mage.cards.c.ConcordantCrossroads.class)); - cards.add(new SetCardInfo("Cocoon", 178, Rarity.UNCOMMON, mage.cards.c.Cocoon.class)); - cards.add(new SetCardInfo("Cosmic Horror", 92, Rarity.RARE, mage.cards.c.CosmicHorror.class)); - cards.add(new SetCardInfo("Craw Giant", 180, Rarity.UNCOMMON, mage.cards.c.CrawGiant.class)); - cards.add(new SetCardInfo("Crevasse", 138, Rarity.UNCOMMON, mage.cards.c.Crevasse.class)); - cards.add(new SetCardInfo("Crimson Kobolds", 139, Rarity.COMMON, mage.cards.c.CrimsonKobolds.class)); - cards.add(new SetCardInfo("Crimson Manticore", 140, Rarity.RARE, mage.cards.c.CrimsonManticore.class)); - cards.add(new SetCardInfo("Crookshank Kobolds", 141, Rarity.COMMON, mage.cards.c.CrookshankKobolds.class)); - cards.add(new SetCardInfo("Cyclopean Mummy", 93, Rarity.COMMON, mage.cards.c.CyclopeanMummy.class)); - cards.add(new SetCardInfo("D'Avenant Archer", 7, Rarity.COMMON, mage.cards.d.DAvenantArcher.class)); - cards.add(new SetCardInfo("Dakkon Blackblade", 225, Rarity.RARE, mage.cards.d.DakkonBlackblade.class)); - cards.add(new SetCardInfo("Darkness", 94, Rarity.COMMON, mage.cards.d.Darkness.class)); - cards.add(new SetCardInfo("Deadfall", 181, Rarity.UNCOMMON, mage.cards.d.Deadfall.class)); - cards.add(new SetCardInfo("Demonic Torment", 95, Rarity.UNCOMMON, mage.cards.d.DemonicTorment.class)); - cards.add(new SetCardInfo("Devouring Deep", 50, Rarity.COMMON, mage.cards.d.DevouringDeep.class)); - cards.add(new SetCardInfo("Disharmony", 142, Rarity.RARE, mage.cards.d.Disharmony.class)); - cards.add(new SetCardInfo("Divine Intervention", 8, Rarity.RARE, mage.cards.d.DivineIntervention.class)); - cards.add(new SetCardInfo("Divine Offering", 9, Rarity.COMMON, mage.cards.d.DivineOffering.class)); - cards.add(new SetCardInfo("Divine Transformation", 10, Rarity.RARE, mage.cards.d.DivineTransformation.class)); - cards.add(new SetCardInfo("Dream Coat", 51, Rarity.UNCOMMON, mage.cards.d.DreamCoat.class)); - cards.add(new SetCardInfo("Durkwood Boars", 182, Rarity.COMMON, mage.cards.d.DurkwoodBoars.class)); - cards.add(new SetCardInfo("Dwarven Song", 143, Rarity.UNCOMMON, mage.cards.d.DwarvenSong.class)); - cards.add(new SetCardInfo("Elder Land Wurm", 11, Rarity.RARE, mage.cards.e.ElderLandWurm.class)); - cards.add(new SetCardInfo("Elder Spawn", 52, Rarity.RARE, mage.cards.e.ElderSpawn.class)); - cards.add(new SetCardInfo("Elven Riders", 183, Rarity.RARE, mage.cards.e.ElvenRiders.class)); - cards.add(new SetCardInfo("Emerald Dragonfly", 184, Rarity.COMMON, mage.cards.e.EmeraldDragonfly.class)); - cards.add(new SetCardInfo("Enchanted Being", 12, Rarity.COMMON, mage.cards.e.EnchantedBeing.class)); - cards.add(new SetCardInfo("Enchantment Alteration", 53, Rarity.COMMON, mage.cards.e.EnchantmentAlteration.class)); - cards.add(new SetCardInfo("Energy Tap", 54, Rarity.COMMON, mage.cards.e.EnergyTap.class)); - cards.add(new SetCardInfo("Eternal Warrior", 144, Rarity.UNCOMMON, mage.cards.e.EternalWarrior.class)); - cards.add(new SetCardInfo("Eureka", 185, Rarity.RARE, mage.cards.e.Eureka.class)); - cards.add(new SetCardInfo("Evil Eye of Orms-by-Gore", 96, Rarity.UNCOMMON, mage.cards.e.EvilEyeOfOrmsByGore.class)); - cards.add(new SetCardInfo("Fallen Angel", 97, Rarity.UNCOMMON, mage.cards.f.FallenAngel.class)); - cards.add(new SetCardInfo("Feint", 146, Rarity.COMMON, mage.cards.f.Feint.class)); - cards.add(new SetCardInfo("Field of Dreams", 55, Rarity.RARE, mage.cards.f.FieldOfDreams.class)); - cards.add(new SetCardInfo("Fire Sprites", 186, Rarity.COMMON, mage.cards.f.FireSprites.class)); - cards.add(new SetCardInfo("Flash Counter", 56, Rarity.COMMON, mage.cards.f.FlashCounter.class)); - cards.add(new SetCardInfo("Flash Flood", 57, Rarity.COMMON, mage.cards.f.FlashFlood.class)); - cards.add(new SetCardInfo("Floral Spuzzem", 187, Rarity.UNCOMMON, mage.cards.f.FloralSpuzzem.class)); - cards.add(new SetCardInfo("Force Spike", 58, Rarity.COMMON, mage.cards.f.ForceSpike.class)); - cards.add(new SetCardInfo("Forethought Amulet", 277, Rarity.RARE, mage.cards.f.ForethoughtAmulet.class)); - cards.add(new SetCardInfo("Fortified Area", 14, Rarity.UNCOMMON, mage.cards.f.FortifiedArea.class)); - cards.add(new SetCardInfo("Frost Giant", 148, Rarity.UNCOMMON, mage.cards.f.FrostGiant.class)); - cards.add(new SetCardInfo("Gabriel Angelfire", 226, Rarity.RARE, mage.cards.g.GabrielAngelfire.class)); - cards.add(new SetCardInfo("Gaseous Form", 59, Rarity.COMMON, mage.cards.g.GaseousForm.class)); - cards.add(new SetCardInfo("Gauntlets of Chaos", 278, Rarity.RARE, mage.cards.g.GauntletsOfChaos.class)); - cards.add(new SetCardInfo("Ghosts of the Damned", 98, Rarity.COMMON, mage.cards.g.GhostsOfTheDamned.class)); - cards.add(new SetCardInfo("Giant Slug", 99, Rarity.COMMON, mage.cards.g.GiantSlug.class)); - cards.add(new SetCardInfo("Giant Strength", 149, Rarity.COMMON, mage.cards.g.GiantStrength.class)); - cards.add(new SetCardInfo("Giant Turtle", 188, Rarity.COMMON, mage.cards.g.GiantTurtle.class)); - cards.add(new SetCardInfo("Glyph of Delusion", 60, Rarity.COMMON, mage.cards.g.GlyphOfDelusion.class)); - cards.add(new SetCardInfo("Glyph of Destruction", 150, Rarity.COMMON, mage.cards.g.GlyphOfDestruction.class)); - cards.add(new SetCardInfo("Glyph of Doom", 100, Rarity.COMMON, mage.cards.g.GlyphOfDoom.class)); - cards.add(new SetCardInfo("Glyph of Life", 15, Rarity.COMMON, mage.cards.g.GlyphOfLife.class)); - cards.add(new SetCardInfo("Glyph of Reincarnation", 189, Rarity.COMMON, mage.cards.g.GlyphOfReincarnation.class)); - cards.add(new SetCardInfo("Gosta Dirk", 227, Rarity.RARE, mage.cards.g.GostaDirk.class)); - cards.add(new SetCardInfo("Gravity Sphere", 151, Rarity.RARE, mage.cards.g.GravitySphere.class)); - cards.add(new SetCardInfo("Great Defender", 16, Rarity.UNCOMMON, mage.cards.g.GreatDefender.class)); - cards.add(new SetCardInfo("Great Wall", 17, Rarity.UNCOMMON, mage.cards.g.GreatWall.class)); - cards.add(new SetCardInfo("Greater Realm of Preservation", 18, Rarity.UNCOMMON, mage.cards.g.GreaterRealmOfPreservation.class)); - cards.add(new SetCardInfo("Greed", 101, Rarity.RARE, mage.cards.g.Greed.class)); - cards.add(new SetCardInfo("Green Mana Battery", 279, Rarity.UNCOMMON, mage.cards.g.GreenManaBattery.class)); - cards.add(new SetCardInfo("Gwendlyn Di Corci", 228, Rarity.RARE, mage.cards.g.GwendlynDiCorci.class)); - cards.add(new SetCardInfo("Halfdane", 229, Rarity.RARE, mage.cards.h.Halfdane.class)); - cards.add(new SetCardInfo("Hammerheim", 302, Rarity.UNCOMMON, mage.cards.h.Hammerheim.class)); - cards.add(new SetCardInfo("Hazezon Tamar", 230, Rarity.RARE, mage.cards.h.HazezonTamar.class)); - cards.add(new SetCardInfo("Headless Horseman", 102, Rarity.COMMON, mage.cards.h.HeadlessHorseman.class)); - cards.add(new SetCardInfo("Heaven's Gate", 19, Rarity.UNCOMMON, mage.cards.h.HeavensGate.class)); - cards.add(new SetCardInfo("Hell Swarm", 103, Rarity.COMMON, mage.cards.h.HellSwarm.class)); - cards.add(new SetCardInfo("Hell's Caretaker", 104, Rarity.RARE, mage.cards.h.HellsCaretaker.class)); - cards.add(new SetCardInfo("Hellfire", 105, Rarity.RARE, mage.cards.h.Hellfire.class)); - cards.add(new SetCardInfo("Holy Day", 20, Rarity.COMMON, mage.cards.h.HolyDay.class)); - cards.add(new SetCardInfo("Horn of Deafening", 280, Rarity.RARE, mage.cards.h.HornOfDeafening.class)); - cards.add(new SetCardInfo("Hornet Cobra", 190, Rarity.COMMON, mage.cards.h.HornetCobra.class)); - cards.add(new SetCardInfo("Horror of Horrors", 106, Rarity.UNCOMMON, mage.cards.h.HorrorOfHorrors.class)); - cards.add(new SetCardInfo("Hunding Gjornersen", 231, Rarity.UNCOMMON, mage.cards.h.HundingGjornersen.class)); - cards.add(new SetCardInfo("Hyperion Blacksmith", 152, Rarity.UNCOMMON, mage.cards.h.HyperionBlacksmith.class)); - cards.add(new SetCardInfo("Ichneumon Druid", 191, Rarity.UNCOMMON, mage.cards.i.IchneumonDruid.class)); - cards.add(new SetCardInfo("Immolation", 153, Rarity.COMMON, mage.cards.i.Immolation.class)); - cards.add(new SetCardInfo("Imprison", 107, Rarity.RARE, mage.cards.i.Imprison.class)); - cards.add(new SetCardInfo("In the Eye of Chaos", 61, Rarity.RARE, mage.cards.i.InTheEyeOfChaos.class)); - cards.add(new SetCardInfo("Indestructible Aura", 21, Rarity.COMMON, mage.cards.i.IndestructibleAura.class)); - cards.add(new SetCardInfo("Infernal Medusa", 108, Rarity.UNCOMMON, mage.cards.i.InfernalMedusa.class)); - cards.add(new SetCardInfo("Infinite Authority", 22, Rarity.RARE, mage.cards.i.InfiniteAuthority.class)); - cards.add(new SetCardInfo("Invoke Prejudice", 62, Rarity.RARE, mage.cards.i.InvokePrejudice.class)); - cards.add(new SetCardInfo("Ivory Guardians", 23, Rarity.UNCOMMON, mage.cards.i.IvoryGuardians.class)); - cards.add(new SetCardInfo("Jacques le Vert", 232, Rarity.RARE, mage.cards.j.JacquesLeVert.class)); - cards.add(new SetCardInfo("Jasmine Boreal", 233, Rarity.UNCOMMON, mage.cards.j.JasmineBoreal.class)); - cards.add(new SetCardInfo("Jedit Ojanen", 234, Rarity.UNCOMMON, mage.cards.j.JeditOjanen.class)); - cards.add(new SetCardInfo("Jerrard of the Closed Fist", 235, Rarity.UNCOMMON, mage.cards.j.JerrardOfTheClosedFist.class)); - cards.add(new SetCardInfo("Johan", 236, Rarity.RARE, mage.cards.j.Johan.class)); - cards.add(new SetCardInfo("Jovial Evil", 109, Rarity.RARE, mage.cards.j.JovialEvil.class)); - cards.add(new SetCardInfo("Juxtapose", 63, Rarity.RARE, mage.cards.j.Juxtapose.class)); - cards.add(new SetCardInfo("Karakas", 303, Rarity.UNCOMMON, mage.cards.k.Karakas.class)); - cards.add(new SetCardInfo("Kasimir the Lone Wolf", 237, Rarity.UNCOMMON, mage.cards.k.KasimirTheLoneWolf.class)); - cards.add(new SetCardInfo("Keepers of the Faith", 24, Rarity.COMMON, mage.cards.k.KeepersOfTheFaith.class)); - cards.add(new SetCardInfo("Kei Takahashi", 238, Rarity.RARE, mage.cards.k.KeiTakahashi.class)); - cards.add(new SetCardInfo("Killer Bees", 192, Rarity.RARE, mage.cards.k.KillerBees.class)); - cards.add(new SetCardInfo("Kismet", 25, Rarity.UNCOMMON, mage.cards.k.Kismet.class)); - cards.add(new SetCardInfo("Knowledge Vault", 281, Rarity.RARE, mage.cards.k.KnowledgeVault.class)); - cards.add(new SetCardInfo("Kobold Drill Sergeant", 154, Rarity.UNCOMMON, mage.cards.k.KoboldDrillSergeant.class)); - cards.add(new SetCardInfo("Kobold Overlord", 155, Rarity.RARE, mage.cards.k.KoboldOverlord.class)); - cards.add(new SetCardInfo("Kobold Taskmaster", 156, Rarity.UNCOMMON, mage.cards.k.KoboldTaskmaster.class)); - cards.add(new SetCardInfo("Kobolds of Kher Keep", 157, Rarity.COMMON, mage.cards.k.KoboldsOfKherKeep.class)); - cards.add(new SetCardInfo("Kry Shield", 282, Rarity.UNCOMMON, mage.cards.k.KryShield.class)); - cards.add(new SetCardInfo("Lady Caleria", 239, Rarity.RARE, mage.cards.l.LadyCaleria.class)); - cards.add(new SetCardInfo("Lady Evangela", 240, Rarity.RARE, mage.cards.l.LadyEvangela.class)); - cards.add(new SetCardInfo("Lady Orca", 241, Rarity.UNCOMMON, mage.cards.l.LadyOrca.class)); - cards.add(new SetCardInfo("Land Equilibrium", 64, Rarity.RARE, mage.cards.l.LandEquilibrium.class)); - cards.add(new SetCardInfo("Land Tax", 26, Rarity.UNCOMMON, mage.cards.l.LandTax.class)); - cards.add(new SetCardInfo("Land's Edge", 158, Rarity.RARE, mage.cards.l.LandsEdge.class)); - cards.add(new SetCardInfo("Lesser Werewolf", 110, Rarity.UNCOMMON, mage.cards.l.LesserWerewolf.class)); - cards.add(new SetCardInfo("Life Chisel", 283, Rarity.UNCOMMON, mage.cards.l.LifeChisel.class)); - cards.add(new SetCardInfo("Life Matrix", 284, Rarity.RARE, mage.cards.l.LifeMatrix.class)); - cards.add(new SetCardInfo("Lifeblood", 27, Rarity.RARE, mage.cards.l.Lifeblood.class)); - cards.add(new SetCardInfo("Living Plane", 193, Rarity.RARE, mage.cards.l.LivingPlane.class)); - cards.add(new SetCardInfo("Livonya Silone", 242, Rarity.RARE, mage.cards.l.LivonyaSilone.class)); - cards.add(new SetCardInfo("Lord Magnus", 243, Rarity.UNCOMMON, mage.cards.l.LordMagnus.class)); - cards.add(new SetCardInfo("Lost Soul", 111, Rarity.COMMON, mage.cards.l.LostSoul.class)); - cards.add(new SetCardInfo("Mana Drain", 65, Rarity.UNCOMMON, mage.cards.m.ManaDrain.class)); - cards.add(new SetCardInfo("Mana Matrix", 285, Rarity.RARE, mage.cards.m.ManaMatrix.class)); - cards.add(new SetCardInfo("Marble Priest", 286, Rarity.UNCOMMON, mage.cards.m.MarblePriest.class)); - cards.add(new SetCardInfo("Marhault Elsdragon", 244, Rarity.UNCOMMON, mage.cards.m.MarhaultElsdragon.class)); - cards.add(new SetCardInfo("Master of the Hunt", 194, Rarity.RARE, mage.cards.m.MasterOfTheHunt.class)); - cards.add(new SetCardInfo("Mirror Universe", 287, Rarity.RARE, mage.cards.m.MirrorUniverse.class)); - cards.add(new SetCardInfo("Moat", 28, Rarity.RARE, mage.cards.m.Moat.class)); - cards.add(new SetCardInfo("Mold Demon", 112, Rarity.RARE, mage.cards.m.MoldDemon.class)); - cards.add(new SetCardInfo("Moss Monster", 195, Rarity.COMMON, mage.cards.m.MossMonster.class)); - cards.add(new SetCardInfo("Mountain Stronghold", 304, Rarity.UNCOMMON, mage.cards.m.MountainStronghold.class)); - cards.add(new SetCardInfo("Mountain Yeti", 159, Rarity.UNCOMMON, mage.cards.m.MountainYeti.class)); - cards.add(new SetCardInfo("Nebuchadnezzar", 245, Rarity.RARE, mage.cards.n.Nebuchadnezzar.class)); - cards.add(new SetCardInfo("Nether Void", 113, Rarity.RARE, mage.cards.n.NetherVoid.class)); - cards.add(new SetCardInfo("Nicol Bolas", 246, Rarity.RARE, mage.cards.n.NicolBolas.class)); - cards.add(new SetCardInfo("Nova Pentacle", 289, Rarity.RARE, mage.cards.n.NovaPentacle.class)); - cards.add(new SetCardInfo("Osai Vultures", 29, Rarity.COMMON, mage.cards.o.OsaiVultures.class)); - cards.add(new SetCardInfo("Palladia-Mors", 247, Rarity.RARE, mage.cards.p.PalladiaMors.class)); - cards.add(new SetCardInfo("Part Water", 66, Rarity.UNCOMMON, mage.cards.p.PartWater.class)); - cards.add(new SetCardInfo("Pavel Maliki", 248, Rarity.UNCOMMON, mage.cards.p.PavelMaliki.class)); - cards.add(new SetCardInfo("Pendelhaven", 305, Rarity.UNCOMMON, mage.cards.p.Pendelhaven.class)); - cards.add(new SetCardInfo("Petra Sphinx", 30, Rarity.RARE, mage.cards.p.PetraSphinx.class)); - cards.add(new SetCardInfo("Pit Scorpion", 114, Rarity.COMMON, mage.cards.p.PitScorpion.class)); - cards.add(new SetCardInfo("Pixie Queen", 196, Rarity.RARE, mage.cards.p.PixieQueen.class)); - cards.add(new SetCardInfo("Planar Gate", 290, Rarity.RARE, mage.cards.p.PlanarGate.class)); - cards.add(new SetCardInfo("Pradesh Gypsies", 197, Rarity.UNCOMMON, mage.cards.p.PradeshGypsies.class)); - cards.add(new SetCardInfo("Presence of the Master", 31, Rarity.UNCOMMON, mage.cards.p.PresenceOfTheMaster.class)); - cards.add(new SetCardInfo("Primordial Ooze", 160, Rarity.UNCOMMON, mage.cards.p.PrimordialOoze.class)); - cards.add(new SetCardInfo("Princess Lucrezia", 249, Rarity.UNCOMMON, mage.cards.p.PrincessLucrezia.class)); - cards.add(new SetCardInfo("Psionic Entity", 67, Rarity.RARE, mage.cards.p.PsionicEntity.class)); - cards.add(new SetCardInfo("Psychic Purge", 68, Rarity.COMMON, mage.cards.p.PsychicPurge.class)); - cards.add(new SetCardInfo("Puppet Master", 69, Rarity.UNCOMMON, mage.cards.p.PuppetMaster.class)); - cards.add(new SetCardInfo("Pyrotechnics", 161, Rarity.COMMON, mage.cards.p.Pyrotechnics.class)); - cards.add(new SetCardInfo("Quagmire", 115, Rarity.UNCOMMON, mage.cards.q.Quagmire.class)); - cards.add(new SetCardInfo("Rabid Wombat", 198, Rarity.UNCOMMON, mage.cards.r.RabidWombat.class)); - cards.add(new SetCardInfo("Radjan Spirit", 199, Rarity.UNCOMMON, mage.cards.r.RadjanSpirit.class)); - cards.add(new SetCardInfo("Raging Bull", 163, Rarity.COMMON, mage.cards.r.RagingBull.class)); - cards.add(new SetCardInfo("Ragnar", 250, Rarity.RARE, mage.cards.r.Ragnar.class)); - cards.add(new SetCardInfo("Ramirez DePietro", 251, Rarity.UNCOMMON, mage.cards.r.RamirezDePietro.class)); - cards.add(new SetCardInfo("Ramses Overdark", 252, Rarity.RARE, mage.cards.r.RamsesOverdark.class)); - cards.add(new SetCardInfo("Rapid Fire", 32, Rarity.RARE, mage.cards.r.RapidFire.class)); - cards.add(new SetCardInfo("Rasputin Dreamweaver", 253, Rarity.RARE, mage.cards.r.RasputinDreamweaver.class)); - cards.add(new SetCardInfo("Recall", 70, Rarity.RARE, mage.cards.r.Recall.class)); - cards.add(new SetCardInfo("Red Mana Battery", 291, Rarity.UNCOMMON, mage.cards.r.RedManaBattery.class)); - cards.add(new SetCardInfo("Reincarnation", 201, Rarity.UNCOMMON, mage.cards.r.Reincarnation.class)); - cards.add(new SetCardInfo("Relic Barrier", 292, Rarity.UNCOMMON, mage.cards.r.RelicBarrier.class)); - cards.add(new SetCardInfo("Relic Bind", 71, Rarity.UNCOMMON, mage.cards.r.RelicBind.class)); - cards.add(new SetCardInfo("Remove Enchantments", 33, Rarity.COMMON, mage.cards.r.RemoveEnchantments.class)); - cards.add(new SetCardInfo("Remove Soul", 72, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); - cards.add(new SetCardInfo("Reset", 73, Rarity.UNCOMMON, mage.cards.r.Reset.class)); - cards.add(new SetCardInfo("Revelation", 202, Rarity.RARE, mage.cards.r.Revelation.class)); - cards.add(new SetCardInfo("Reverberation", 74, Rarity.RARE, mage.cards.r.Reverberation.class)); - cards.add(new SetCardInfo("Righteous Avengers", 34, Rarity.UNCOMMON, mage.cards.r.RighteousAvengers.class)); - cards.add(new SetCardInfo("Ring of Immortals", 293, Rarity.RARE, mage.cards.r.RingOfImmortals.class)); - cards.add(new SetCardInfo("Riven Turnbull", 254, Rarity.UNCOMMON, mage.cards.r.RivenTurnbull.class)); - cards.add(new SetCardInfo("Rohgahh of Kher Keep", 255, Rarity.RARE, mage.cards.r.RohgahhOfKherKeep.class)); - cards.add(new SetCardInfo("Rubinia Soulsinger", 256, Rarity.RARE, mage.cards.r.RubiniaSoulsinger.class)); - cards.add(new SetCardInfo("Rust", 203, Rarity.COMMON, mage.cards.r.Rust.class)); - cards.add(new SetCardInfo("Sea Kings' Blessing", 75, Rarity.UNCOMMON, mage.cards.s.SeaKingsBlessing.class)); - cards.add(new SetCardInfo("Seafarer's Quay", 306, Rarity.UNCOMMON, mage.cards.s.SeafarersQuay.class)); - cards.add(new SetCardInfo("Seeker", 35, Rarity.UNCOMMON, mage.cards.s.Seeker.class)); - cards.add(new SetCardInfo("Segovian Leviathan", 76, Rarity.UNCOMMON, mage.cards.s.SegovianLeviathan.class)); - cards.add(new SetCardInfo("Sentinel", 294, Rarity.RARE, mage.cards.s.Sentinel.class)); - cards.add(new SetCardInfo("Serpent Generator", 295, Rarity.RARE, mage.cards.s.SerpentGenerator.class)); - cards.add(new SetCardInfo("Shelkin Brownie", 204, Rarity.COMMON, mage.cards.s.ShelkinBrownie.class)); - cards.add(new SetCardInfo("Shield Wall", 36, Rarity.UNCOMMON, mage.cards.s.ShieldWall.class)); - cards.add(new SetCardInfo("Shimian Night Stalker", 116, Rarity.UNCOMMON, mage.cards.s.ShimianNightStalker.class)); - cards.add(new SetCardInfo("Sir Shandlar of Eberyn", 257, Rarity.UNCOMMON, mage.cards.s.SirShandlarOfEberyn.class)); - cards.add(new SetCardInfo("Sivitri Scarzam", 258, Rarity.UNCOMMON, mage.cards.s.SivitriScarzam.class)); - cards.add(new SetCardInfo("Sol'kanar the Swamp King", 259, Rarity.RARE, mage.cards.s.SolkanarTheSwampKing.class)); - cards.add(new SetCardInfo("Spectral Cloak", 78, Rarity.UNCOMMON, mage.cards.s.SpectralCloak.class)); - cards.add(new SetCardInfo("Spinal Villain", 164, Rarity.RARE, mage.cards.s.SpinalVillain.class)); - cards.add(new SetCardInfo("Spirit Link", 37, Rarity.UNCOMMON, mage.cards.s.SpiritLink.class)); - cards.add(new SetCardInfo("Spirit Shackle", 117, Rarity.COMMON, mage.cards.s.SpiritShackle.class)); - cards.add(new SetCardInfo("Spiritual Sanctuary", 38, Rarity.RARE, mage.cards.s.SpiritualSanctuary.class)); - cards.add(new SetCardInfo("Stangg", 260, Rarity.RARE, mage.cards.s.Stangg.class)); - cards.add(new SetCardInfo("Storm Seeker", 205, Rarity.UNCOMMON, mage.cards.s.StormSeeker.class)); - cards.add(new SetCardInfo("Storm World", 165, Rarity.RARE, mage.cards.s.StormWorld.class)); - cards.add(new SetCardInfo("Subdue", 206, Rarity.COMMON, mage.cards.s.Subdue.class)); - cards.add(new SetCardInfo("Sunastian Falconer", 261, Rarity.UNCOMMON, mage.cards.s.SunastianFalconer.class)); - cards.add(new SetCardInfo("Sword of the Ages", 296, Rarity.RARE, mage.cards.s.SwordOfTheAges.class)); - cards.add(new SetCardInfo("Sylvan Library", 207, Rarity.UNCOMMON, mage.cards.s.SylvanLibrary.class)); - cards.add(new SetCardInfo("Sylvan Paradise", 208, Rarity.UNCOMMON, mage.cards.s.SylvanParadise.class)); - cards.add(new SetCardInfo("Syphon Soul", 118, Rarity.COMMON, mage.cards.s.SyphonSoul.class)); - cards.add(new SetCardInfo("Telekinesis", 79, Rarity.RARE, mage.cards.t.Telekinesis.class)); - cards.add(new SetCardInfo("Teleport", 80, Rarity.RARE, mage.cards.t.Teleport.class)); - cards.add(new SetCardInfo("Tetsuo Umezawa", 262, Rarity.RARE, mage.cards.t.TetsuoUmezawa.class)); - cards.add(new SetCardInfo("The Abyss", 120, Rarity.RARE, mage.cards.t.TheAbyss.class)); - cards.add(new SetCardInfo("The Brute", 167, Rarity.COMMON, mage.cards.t.TheBrute.class)); - cards.add(new SetCardInfo("The Lady of the Mountain", 263, Rarity.UNCOMMON, mage.cards.t.TheLadyOfTheMountain.class)); - cards.add(new SetCardInfo("The Tabernacle at Pendrell Vale", 307, Rarity.RARE, mage.cards.t.TheTabernacleAtPendrellVale.class)); - cards.add(new SetCardInfo("The Wretched", 121, Rarity.RARE, mage.cards.t.TheWretched.class)); - cards.add(new SetCardInfo("Thunder Spirit", 39, Rarity.RARE, mage.cards.t.ThunderSpirit.class)); - cards.add(new SetCardInfo("Time Elemental", 81, Rarity.RARE, mage.cards.t.TimeElemental.class)); - cards.add(new SetCardInfo("Tobias Andrion", 264, Rarity.UNCOMMON, mage.cards.t.TobiasAndrion.class)); - cards.add(new SetCardInfo("Tolaria", 308, Rarity.UNCOMMON, mage.cards.t.Tolaria.class)); - cards.add(new SetCardInfo("Tor Wauki", 265, Rarity.UNCOMMON, mage.cards.t.TorWauki.class)); - cards.add(new SetCardInfo("Torsten Von Ursus", 266, Rarity.UNCOMMON, mage.cards.t.TorstenVonUrsus.class)); - cards.add(new SetCardInfo("Touch of Darkness", 122, Rarity.UNCOMMON, mage.cards.t.TouchOfDarkness.class)); - cards.add(new SetCardInfo("Transmutation", 123, Rarity.COMMON, mage.cards.t.Transmutation.class)); - cards.add(new SetCardInfo("Triassic Egg", 297, Rarity.RARE, mage.cards.t.TriassicEgg.class)); - cards.add(new SetCardInfo("Tuknir Deathlock", 267, Rarity.RARE, mage.cards.t.TuknirDeathlock.class)); - cards.add(new SetCardInfo("Tundra Wolves", 40, Rarity.COMMON, mage.cards.t.TundraWolves.class)); - cards.add(new SetCardInfo("Typhoon", 209, Rarity.RARE, mage.cards.t.Typhoon.class)); - cards.add(new SetCardInfo("Undertow", 82, Rarity.UNCOMMON, mage.cards.u.Undertow.class)); - cards.add(new SetCardInfo("Unholy Citadel", 309, Rarity.UNCOMMON, mage.cards.u.UnholyCitadel.class)); - cards.add(new SetCardInfo("Underworld Dreams", 124, Rarity.UNCOMMON, mage.cards.u.UnderworldDreams.class)); - cards.add(new SetCardInfo("Untamed Wilds", 210, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); - cards.add(new SetCardInfo("Ur-Drago", 268, Rarity.RARE, mage.cards.u.UrDrago.class)); - cards.add(new SetCardInfo("Urborg", 310, Rarity.UNCOMMON, mage.cards.u.Urborg.class)); - cards.add(new SetCardInfo("Vaevictis Asmadi", 269, Rarity.RARE, mage.cards.v.VaevictisAsmadi.class)); - cards.add(new SetCardInfo("Vampire Bats", 125, Rarity.COMMON, mage.cards.v.VampireBats.class)); - cards.add(new SetCardInfo("Venarian Gold", 83, Rarity.COMMON, mage.cards.v.VenarianGold.class)); - cards.add(new SetCardInfo("Visions", 41, Rarity.UNCOMMON, mage.cards.v.Visions.class)); - cards.add(new SetCardInfo("Voodoo Doll", 298, Rarity.RARE, mage.cards.v.VoodooDoll.class)); - cards.add(new SetCardInfo("Walking Dead", 126, Rarity.COMMON, mage.cards.w.WalkingDead.class)); - cards.add(new SetCardInfo("Wall of Caltrops", 42, Rarity.COMMON, mage.cards.w.WallOfCaltrops.class)); - cards.add(new SetCardInfo("Wall of Dust", 168, Rarity.UNCOMMON, mage.cards.w.WallOfDust.class)); - cards.add(new SetCardInfo("Wall of Earth", 169, Rarity.COMMON, mage.cards.w.WallOfEarth.class)); - cards.add(new SetCardInfo("Wall of Heat", 170, Rarity.COMMON, mage.cards.w.WallOfHeat.class)); - cards.add(new SetCardInfo("Wall of Light", 43, Rarity.UNCOMMON, mage.cards.w.WallOfLight.class)); - cards.add(new SetCardInfo("Wall of Opposition", 171, Rarity.RARE, mage.cards.w.WallOfOpposition.class)); - cards.add(new SetCardInfo("Wall of Putrid Flesh", 127, Rarity.UNCOMMON, mage.cards.w.WallOfPutridFlesh.class)); - cards.add(new SetCardInfo("Wall of Shadows", 128, Rarity.COMMON, mage.cards.w.WallOfShadows.class)); - cards.add(new SetCardInfo("Wall of Tombstones", 129, Rarity.UNCOMMON, mage.cards.w.WallOfTombstones.class)); - cards.add(new SetCardInfo("Wall of Vapor", 84, Rarity.COMMON, mage.cards.w.WallOfVapor.class)); - cards.add(new SetCardInfo("Wall of Wonder", 85, Rarity.UNCOMMON, mage.cards.w.WallOfWonder.class)); - cards.add(new SetCardInfo("Whirling Dervish", 211, Rarity.UNCOMMON, mage.cards.w.WhirlingDervish.class)); - cards.add(new SetCardInfo("White Mana Battery", 299, Rarity.UNCOMMON, mage.cards.w.WhiteManaBattery.class)); - cards.add(new SetCardInfo("Willow Satyr", 212, Rarity.RARE, mage.cards.w.WillowSatyr.class)); - cards.add(new SetCardInfo("Winds of Change", 172, Rarity.UNCOMMON, mage.cards.w.WindsOfChange.class)); - cards.add(new SetCardInfo("Winter Blast", 213, Rarity.RARE, mage.cards.w.WinterBlast.class)); - cards.add(new SetCardInfo("Wolverine Pack", 214, Rarity.COMMON, mage.cards.w.WolverinePack.class)); - cards.add(new SetCardInfo("Wood Elemental", 215, Rarity.RARE, mage.cards.w.WoodElemental.class)); - cards.add(new SetCardInfo("Xira Arien", 270, Rarity.RARE, mage.cards.x.XiraArien.class)); - cards.add(new SetCardInfo("Zephyr Falcon", 86, Rarity.COMMON, mage.cards.z.ZephyrFalcon.class)); - } -} +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author North + */ +public final class Legends extends ExpansionSet { + + private static final Legends instance = new Legends(); + + public static Legends getInstance() { + return instance; + } + + private Legends() { + super("Legends", "LEG", ExpansionSet.buildDate(1994, 6, 1), SetType.EXPANSION); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Abomination", 87, Rarity.UNCOMMON, mage.cards.a.Abomination.class)); + cards.add(new SetCardInfo("Acid Rain", 44, Rarity.RARE, mage.cards.a.AcidRain.class)); + cards.add(new SetCardInfo("Active Volcano", 130, Rarity.COMMON, mage.cards.a.ActiveVolcano.class)); + cards.add(new SetCardInfo("Adun Oakenshield", 216, Rarity.RARE, mage.cards.a.AdunOakenshield.class)); + cards.add(new SetCardInfo("Adventurers' Guildhouse", 300, Rarity.UNCOMMON, mage.cards.a.AdventurersGuildhouse.class)); + cards.add(new SetCardInfo("Aerathi Berserker", 131, Rarity.UNCOMMON, mage.cards.a.AerathiBerserker.class)); + cards.add(new SetCardInfo("Aisling Leprechaun", 173, Rarity.COMMON, mage.cards.a.AislingLeprechaun.class)); + cards.add(new SetCardInfo("Akron Legionnaire", 1, Rarity.RARE, mage.cards.a.AkronLegionnaire.class)); + cards.add(new SetCardInfo("Al-abara's Carpet", 271, Rarity.RARE, mage.cards.a.AlAbarasCarpet.class)); + cards.add(new SetCardInfo("Alabaster Potion", 2, Rarity.COMMON, mage.cards.a.AlabasterPotion.class)); + cards.add(new SetCardInfo("Alchor's Tomb", 272, Rarity.RARE, mage.cards.a.AlchorsTomb.class)); + cards.add(new SetCardInfo("All Hallow's Eve", 88, Rarity.RARE, mage.cards.a.AllHallowsEve.class)); + cards.add(new SetCardInfo("Amrou Kithkin", 3, Rarity.COMMON, mage.cards.a.AmrouKithkin.class)); + cards.add(new SetCardInfo("Angelic Voices", 4, Rarity.RARE, mage.cards.a.AngelicVoices.class)); + cards.add(new SetCardInfo("Angus Mackenzie", 217, Rarity.RARE, mage.cards.a.AngusMackenzie.class)); + cards.add(new SetCardInfo("Anti-Magic Aura", 45, Rarity.COMMON, mage.cards.a.AntiMagicAura.class)); + cards.add(new SetCardInfo("Arboria", 174, Rarity.UNCOMMON, mage.cards.a.Arboria.class)); + cards.add(new SetCardInfo("Arcades Sabboth", 218, Rarity.RARE, mage.cards.a.ArcadesSabboth.class)); + cards.add(new SetCardInfo("Arena of the Ancients", 273, Rarity.RARE, mage.cards.a.ArenaOfTheAncients.class)); + cards.add(new SetCardInfo("Avoid Fate", 175, Rarity.COMMON, mage.cards.a.AvoidFate.class)); + cards.add(new SetCardInfo("Axelrod Gunnarson", 219, Rarity.RARE, mage.cards.a.AxelrodGunnarson.class)); + cards.add(new SetCardInfo("Ayesha Tanaka", 220, Rarity.RARE, mage.cards.a.AyeshaTanaka.class)); + cards.add(new SetCardInfo("Azure Drake", 46, Rarity.UNCOMMON, mage.cards.a.AzureDrake.class)); + cards.add(new SetCardInfo("Backfire", 47, Rarity.UNCOMMON, mage.cards.b.Backfire.class)); + cards.add(new SetCardInfo("Barbary Apes", 176, Rarity.COMMON, mage.cards.b.BarbaryApes.class)); + cards.add(new SetCardInfo("Barktooth Warbeard", 221, Rarity.UNCOMMON, mage.cards.b.BarktoothWarbeard.class)); + cards.add(new SetCardInfo("Bartel Runeaxe", 222, Rarity.RARE, mage.cards.b.BartelRuneaxe.class)); + cards.add(new SetCardInfo("Beasts of Bogardan", 133, Rarity.UNCOMMON, mage.cards.b.BeastsOfBogardan.class)); + cards.add(new SetCardInfo("Black Mana Battery", 274, Rarity.UNCOMMON, mage.cards.b.BlackManaBattery.class)); + cards.add(new SetCardInfo("Blazing Effigy", 134, Rarity.COMMON, mage.cards.b.BlazingEffigy.class)); + cards.add(new SetCardInfo("Blight", 89, Rarity.UNCOMMON, mage.cards.b.Blight.class)); + cards.add(new SetCardInfo("Blood Lust", 135, Rarity.UNCOMMON, mage.cards.b.BloodLust.class)); + cards.add(new SetCardInfo("Blue Mana Battery", 275, Rarity.UNCOMMON, mage.cards.b.BlueManaBattery.class)); + cards.add(new SetCardInfo("Boomerang", 48, Rarity.COMMON, mage.cards.b.Boomerang.class)); + cards.add(new SetCardInfo("Boris Devilboon", 223, Rarity.RARE, mage.cards.b.BorisDevilboon.class)); + cards.add(new SetCardInfo("Brine Hag", 49, Rarity.UNCOMMON, mage.cards.b.BrineHag.class)); + cards.add(new SetCardInfo("Bronze Horse", 276, Rarity.RARE, mage.cards.b.BronzeHorse.class)); + cards.add(new SetCardInfo("Carrion Ants", 90, Rarity.RARE, mage.cards.c.CarrionAnts.class)); + cards.add(new SetCardInfo("Cat Warriors", 177, Rarity.COMMON, mage.cards.c.CatWarriors.class)); + cards.add(new SetCardInfo("Cathedral of Serra", 301, Rarity.UNCOMMON, mage.cards.c.CathedralOfSerra.class)); + cards.add(new SetCardInfo("Caverns of Despair", 136, Rarity.RARE, mage.cards.c.CavernsOfDespair.class)); + cards.add(new SetCardInfo("Chain Lightning", 137, Rarity.COMMON, mage.cards.c.ChainLightning.class)); + cards.add(new SetCardInfo("Chains of Mephistopheles", 91, Rarity.RARE, mage.cards.c.ChainsOfMephistopheles.class)); + cards.add(new SetCardInfo("Chromium", 224, Rarity.RARE, mage.cards.c.Chromium.class)); + cards.add(new SetCardInfo("Cleanse", 5, Rarity.RARE, mage.cards.c.Cleanse.class)); + cards.add(new SetCardInfo("Clergy of the Holy Nimbus", 6, Rarity.COMMON, mage.cards.c.ClergyOfTheHolyNimbus.class)); + cards.add(new SetCardInfo("Concordant Crossroads", 179, Rarity.RARE, mage.cards.c.ConcordantCrossroads.class)); + cards.add(new SetCardInfo("Cocoon", 178, Rarity.UNCOMMON, mage.cards.c.Cocoon.class)); + cards.add(new SetCardInfo("Cosmic Horror", 92, Rarity.RARE, mage.cards.c.CosmicHorror.class)); + cards.add(new SetCardInfo("Craw Giant", 180, Rarity.UNCOMMON, mage.cards.c.CrawGiant.class)); + cards.add(new SetCardInfo("Crevasse", 138, Rarity.UNCOMMON, mage.cards.c.Crevasse.class)); + cards.add(new SetCardInfo("Crimson Kobolds", 139, Rarity.COMMON, mage.cards.c.CrimsonKobolds.class)); + cards.add(new SetCardInfo("Crimson Manticore", 140, Rarity.RARE, mage.cards.c.CrimsonManticore.class)); + cards.add(new SetCardInfo("Crookshank Kobolds", 141, Rarity.COMMON, mage.cards.c.CrookshankKobolds.class)); + cards.add(new SetCardInfo("Cyclopean Mummy", 93, Rarity.COMMON, mage.cards.c.CyclopeanMummy.class)); + cards.add(new SetCardInfo("D'Avenant Archer", 7, Rarity.COMMON, mage.cards.d.DAvenantArcher.class)); + cards.add(new SetCardInfo("Dakkon Blackblade", 225, Rarity.RARE, mage.cards.d.DakkonBlackblade.class)); + cards.add(new SetCardInfo("Darkness", 94, Rarity.COMMON, mage.cards.d.Darkness.class)); + cards.add(new SetCardInfo("Deadfall", 181, Rarity.UNCOMMON, mage.cards.d.Deadfall.class)); + cards.add(new SetCardInfo("Demonic Torment", 95, Rarity.UNCOMMON, mage.cards.d.DemonicTorment.class)); + cards.add(new SetCardInfo("Devouring Deep", 50, Rarity.COMMON, mage.cards.d.DevouringDeep.class)); + cards.add(new SetCardInfo("Disharmony", 142, Rarity.RARE, mage.cards.d.Disharmony.class)); + cards.add(new SetCardInfo("Divine Intervention", 8, Rarity.RARE, mage.cards.d.DivineIntervention.class)); + cards.add(new SetCardInfo("Divine Offering", 9, Rarity.COMMON, mage.cards.d.DivineOffering.class)); + cards.add(new SetCardInfo("Divine Transformation", 10, Rarity.RARE, mage.cards.d.DivineTransformation.class)); + cards.add(new SetCardInfo("Dream Coat", 51, Rarity.UNCOMMON, mage.cards.d.DreamCoat.class)); + cards.add(new SetCardInfo("Durkwood Boars", 182, Rarity.COMMON, mage.cards.d.DurkwoodBoars.class)); + cards.add(new SetCardInfo("Dwarven Song", 143, Rarity.UNCOMMON, mage.cards.d.DwarvenSong.class)); + cards.add(new SetCardInfo("Elder Land Wurm", 11, Rarity.RARE, mage.cards.e.ElderLandWurm.class)); + cards.add(new SetCardInfo("Elder Spawn", 52, Rarity.RARE, mage.cards.e.ElderSpawn.class)); + cards.add(new SetCardInfo("Elven Riders", 183, Rarity.RARE, mage.cards.e.ElvenRiders.class)); + cards.add(new SetCardInfo("Emerald Dragonfly", 184, Rarity.COMMON, mage.cards.e.EmeraldDragonfly.class)); + cards.add(new SetCardInfo("Enchanted Being", 12, Rarity.COMMON, mage.cards.e.EnchantedBeing.class)); + cards.add(new SetCardInfo("Enchantment Alteration", 53, Rarity.COMMON, mage.cards.e.EnchantmentAlteration.class)); + cards.add(new SetCardInfo("Energy Tap", 54, Rarity.COMMON, mage.cards.e.EnergyTap.class)); + cards.add(new SetCardInfo("Eternal Warrior", 144, Rarity.UNCOMMON, mage.cards.e.EternalWarrior.class)); + cards.add(new SetCardInfo("Eureka", 185, Rarity.RARE, mage.cards.e.Eureka.class)); + cards.add(new SetCardInfo("Evil Eye of Orms-by-Gore", 96, Rarity.UNCOMMON, mage.cards.e.EvilEyeOfOrmsByGore.class)); + cards.add(new SetCardInfo("Fallen Angel", 97, Rarity.UNCOMMON, mage.cards.f.FallenAngel.class)); + cards.add(new SetCardInfo("Feint", 146, Rarity.COMMON, mage.cards.f.Feint.class)); + cards.add(new SetCardInfo("Field of Dreams", 55, Rarity.RARE, mage.cards.f.FieldOfDreams.class)); + cards.add(new SetCardInfo("Fire Sprites", 186, Rarity.COMMON, mage.cards.f.FireSprites.class)); + cards.add(new SetCardInfo("Flash Counter", 56, Rarity.COMMON, mage.cards.f.FlashCounter.class)); + cards.add(new SetCardInfo("Flash Flood", 57, Rarity.COMMON, mage.cards.f.FlashFlood.class)); + cards.add(new SetCardInfo("Floral Spuzzem", 187, Rarity.UNCOMMON, mage.cards.f.FloralSpuzzem.class)); + cards.add(new SetCardInfo("Force Spike", 58, Rarity.COMMON, mage.cards.f.ForceSpike.class)); + cards.add(new SetCardInfo("Forethought Amulet", 277, Rarity.RARE, mage.cards.f.ForethoughtAmulet.class)); + cards.add(new SetCardInfo("Fortified Area", 14, Rarity.UNCOMMON, mage.cards.f.FortifiedArea.class)); + cards.add(new SetCardInfo("Frost Giant", 148, Rarity.UNCOMMON, mage.cards.f.FrostGiant.class)); + cards.add(new SetCardInfo("Gabriel Angelfire", 226, Rarity.RARE, mage.cards.g.GabrielAngelfire.class)); + cards.add(new SetCardInfo("Gaseous Form", 59, Rarity.COMMON, mage.cards.g.GaseousForm.class)); + cards.add(new SetCardInfo("Gauntlets of Chaos", 278, Rarity.RARE, mage.cards.g.GauntletsOfChaos.class)); + cards.add(new SetCardInfo("Ghosts of the Damned", 98, Rarity.COMMON, mage.cards.g.GhostsOfTheDamned.class)); + cards.add(new SetCardInfo("Giant Slug", 99, Rarity.COMMON, mage.cards.g.GiantSlug.class)); + cards.add(new SetCardInfo("Giant Strength", 149, Rarity.COMMON, mage.cards.g.GiantStrength.class)); + cards.add(new SetCardInfo("Giant Turtle", 188, Rarity.COMMON, mage.cards.g.GiantTurtle.class)); + cards.add(new SetCardInfo("Glyph of Delusion", 60, Rarity.COMMON, mage.cards.g.GlyphOfDelusion.class)); + cards.add(new SetCardInfo("Glyph of Destruction", 150, Rarity.COMMON, mage.cards.g.GlyphOfDestruction.class)); + cards.add(new SetCardInfo("Glyph of Doom", 100, Rarity.COMMON, mage.cards.g.GlyphOfDoom.class)); + cards.add(new SetCardInfo("Glyph of Life", 15, Rarity.COMMON, mage.cards.g.GlyphOfLife.class)); + cards.add(new SetCardInfo("Glyph of Reincarnation", 189, Rarity.COMMON, mage.cards.g.GlyphOfReincarnation.class)); + cards.add(new SetCardInfo("Gosta Dirk", 227, Rarity.RARE, mage.cards.g.GostaDirk.class)); + cards.add(new SetCardInfo("Gravity Sphere", 151, Rarity.RARE, mage.cards.g.GravitySphere.class)); + cards.add(new SetCardInfo("Great Defender", 16, Rarity.UNCOMMON, mage.cards.g.GreatDefender.class)); + cards.add(new SetCardInfo("Great Wall", 17, Rarity.UNCOMMON, mage.cards.g.GreatWall.class)); + cards.add(new SetCardInfo("Greater Realm of Preservation", 18, Rarity.UNCOMMON, mage.cards.g.GreaterRealmOfPreservation.class)); + cards.add(new SetCardInfo("Greed", 101, Rarity.RARE, mage.cards.g.Greed.class)); + cards.add(new SetCardInfo("Green Mana Battery", 279, Rarity.UNCOMMON, mage.cards.g.GreenManaBattery.class)); + cards.add(new SetCardInfo("Gwendlyn Di Corci", 228, Rarity.RARE, mage.cards.g.GwendlynDiCorci.class)); + cards.add(new SetCardInfo("Halfdane", 229, Rarity.RARE, mage.cards.h.Halfdane.class)); + cards.add(new SetCardInfo("Hammerheim", 302, Rarity.UNCOMMON, mage.cards.h.Hammerheim.class)); + cards.add(new SetCardInfo("Hazezon Tamar", 230, Rarity.RARE, mage.cards.h.HazezonTamar.class)); + cards.add(new SetCardInfo("Headless Horseman", 102, Rarity.COMMON, mage.cards.h.HeadlessHorseman.class)); + cards.add(new SetCardInfo("Heaven's Gate", 19, Rarity.UNCOMMON, mage.cards.h.HeavensGate.class)); + cards.add(new SetCardInfo("Hell Swarm", 103, Rarity.COMMON, mage.cards.h.HellSwarm.class)); + cards.add(new SetCardInfo("Hell's Caretaker", 104, Rarity.RARE, mage.cards.h.HellsCaretaker.class)); + cards.add(new SetCardInfo("Hellfire", 105, Rarity.RARE, mage.cards.h.Hellfire.class)); + cards.add(new SetCardInfo("Holy Day", 20, Rarity.COMMON, mage.cards.h.HolyDay.class)); + cards.add(new SetCardInfo("Horn of Deafening", 280, Rarity.RARE, mage.cards.h.HornOfDeafening.class)); + cards.add(new SetCardInfo("Hornet Cobra", 190, Rarity.COMMON, mage.cards.h.HornetCobra.class)); + cards.add(new SetCardInfo("Horror of Horrors", 106, Rarity.UNCOMMON, mage.cards.h.HorrorOfHorrors.class)); + cards.add(new SetCardInfo("Hunding Gjornersen", 231, Rarity.UNCOMMON, mage.cards.h.HundingGjornersen.class)); + cards.add(new SetCardInfo("Hyperion Blacksmith", 152, Rarity.UNCOMMON, mage.cards.h.HyperionBlacksmith.class)); + cards.add(new SetCardInfo("Ichneumon Druid", 191, Rarity.UNCOMMON, mage.cards.i.IchneumonDruid.class)); + cards.add(new SetCardInfo("Immolation", 153, Rarity.COMMON, mage.cards.i.Immolation.class)); + cards.add(new SetCardInfo("Imprison", 107, Rarity.RARE, mage.cards.i.Imprison.class)); + cards.add(new SetCardInfo("In the Eye of Chaos", 61, Rarity.RARE, mage.cards.i.InTheEyeOfChaos.class)); + cards.add(new SetCardInfo("Indestructible Aura", 21, Rarity.COMMON, mage.cards.i.IndestructibleAura.class)); + cards.add(new SetCardInfo("Infernal Medusa", 108, Rarity.UNCOMMON, mage.cards.i.InfernalMedusa.class)); + cards.add(new SetCardInfo("Infinite Authority", 22, Rarity.RARE, mage.cards.i.InfiniteAuthority.class)); + cards.add(new SetCardInfo("Invoke Prejudice", 62, Rarity.RARE, mage.cards.i.InvokePrejudice.class)); + cards.add(new SetCardInfo("Ivory Guardians", 23, Rarity.UNCOMMON, mage.cards.i.IvoryGuardians.class)); + cards.add(new SetCardInfo("Jacques le Vert", 232, Rarity.RARE, mage.cards.j.JacquesLeVert.class)); + cards.add(new SetCardInfo("Jasmine Boreal", 233, Rarity.UNCOMMON, mage.cards.j.JasmineBoreal.class)); + cards.add(new SetCardInfo("Jedit Ojanen", 234, Rarity.UNCOMMON, mage.cards.j.JeditOjanen.class)); + cards.add(new SetCardInfo("Jerrard of the Closed Fist", 235, Rarity.UNCOMMON, mage.cards.j.JerrardOfTheClosedFist.class)); + cards.add(new SetCardInfo("Johan", 236, Rarity.RARE, mage.cards.j.Johan.class)); + cards.add(new SetCardInfo("Jovial Evil", 109, Rarity.RARE, mage.cards.j.JovialEvil.class)); + cards.add(new SetCardInfo("Juxtapose", 63, Rarity.RARE, mage.cards.j.Juxtapose.class)); + cards.add(new SetCardInfo("Karakas", 303, Rarity.UNCOMMON, mage.cards.k.Karakas.class)); + cards.add(new SetCardInfo("Kasimir the Lone Wolf", 237, Rarity.UNCOMMON, mage.cards.k.KasimirTheLoneWolf.class)); + cards.add(new SetCardInfo("Keepers of the Faith", 24, Rarity.COMMON, mage.cards.k.KeepersOfTheFaith.class)); + cards.add(new SetCardInfo("Kei Takahashi", 238, Rarity.RARE, mage.cards.k.KeiTakahashi.class)); + cards.add(new SetCardInfo("Killer Bees", 192, Rarity.RARE, mage.cards.k.KillerBees.class)); + cards.add(new SetCardInfo("Kismet", 25, Rarity.UNCOMMON, mage.cards.k.Kismet.class)); + cards.add(new SetCardInfo("Knowledge Vault", 281, Rarity.RARE, mage.cards.k.KnowledgeVault.class)); + cards.add(new SetCardInfo("Kobold Drill Sergeant", 154, Rarity.UNCOMMON, mage.cards.k.KoboldDrillSergeant.class)); + cards.add(new SetCardInfo("Kobold Overlord", 155, Rarity.RARE, mage.cards.k.KoboldOverlord.class)); + cards.add(new SetCardInfo("Kobold Taskmaster", 156, Rarity.UNCOMMON, mage.cards.k.KoboldTaskmaster.class)); + cards.add(new SetCardInfo("Kobolds of Kher Keep", 157, Rarity.COMMON, mage.cards.k.KoboldsOfKherKeep.class)); + cards.add(new SetCardInfo("Kry Shield", 282, Rarity.UNCOMMON, mage.cards.k.KryShield.class)); + cards.add(new SetCardInfo("Lady Caleria", 239, Rarity.RARE, mage.cards.l.LadyCaleria.class)); + cards.add(new SetCardInfo("Lady Evangela", 240, Rarity.RARE, mage.cards.l.LadyEvangela.class)); + cards.add(new SetCardInfo("Lady Orca", 241, Rarity.UNCOMMON, mage.cards.l.LadyOrca.class)); + cards.add(new SetCardInfo("Land Equilibrium", 64, Rarity.RARE, mage.cards.l.LandEquilibrium.class)); + cards.add(new SetCardInfo("Land Tax", 26, Rarity.UNCOMMON, mage.cards.l.LandTax.class)); + cards.add(new SetCardInfo("Land's Edge", 158, Rarity.RARE, mage.cards.l.LandsEdge.class)); + cards.add(new SetCardInfo("Lesser Werewolf", 110, Rarity.UNCOMMON, mage.cards.l.LesserWerewolf.class)); + cards.add(new SetCardInfo("Life Chisel", 283, Rarity.UNCOMMON, mage.cards.l.LifeChisel.class)); + cards.add(new SetCardInfo("Life Matrix", 284, Rarity.RARE, mage.cards.l.LifeMatrix.class)); + cards.add(new SetCardInfo("Lifeblood", 27, Rarity.RARE, mage.cards.l.Lifeblood.class)); + cards.add(new SetCardInfo("Living Plane", 193, Rarity.RARE, mage.cards.l.LivingPlane.class)); + cards.add(new SetCardInfo("Livonya Silone", 242, Rarity.RARE, mage.cards.l.LivonyaSilone.class)); + cards.add(new SetCardInfo("Lord Magnus", 243, Rarity.UNCOMMON, mage.cards.l.LordMagnus.class)); + cards.add(new SetCardInfo("Lost Soul", 111, Rarity.COMMON, mage.cards.l.LostSoul.class)); + cards.add(new SetCardInfo("Mana Drain", 65, Rarity.UNCOMMON, mage.cards.m.ManaDrain.class)); + cards.add(new SetCardInfo("Mana Matrix", 285, Rarity.RARE, mage.cards.m.ManaMatrix.class)); + cards.add(new SetCardInfo("Marble Priest", 286, Rarity.UNCOMMON, mage.cards.m.MarblePriest.class)); + cards.add(new SetCardInfo("Marhault Elsdragon", 244, Rarity.UNCOMMON, mage.cards.m.MarhaultElsdragon.class)); + cards.add(new SetCardInfo("Master of the Hunt", 194, Rarity.RARE, mage.cards.m.MasterOfTheHunt.class)); + cards.add(new SetCardInfo("Mirror Universe", 287, Rarity.RARE, mage.cards.m.MirrorUniverse.class)); + cards.add(new SetCardInfo("Moat", 28, Rarity.RARE, mage.cards.m.Moat.class)); + cards.add(new SetCardInfo("Mold Demon", 112, Rarity.RARE, mage.cards.m.MoldDemon.class)); + cards.add(new SetCardInfo("Moss Monster", 195, Rarity.COMMON, mage.cards.m.MossMonster.class)); + cards.add(new SetCardInfo("Mountain Stronghold", 304, Rarity.UNCOMMON, mage.cards.m.MountainStronghold.class)); + cards.add(new SetCardInfo("Mountain Yeti", 159, Rarity.UNCOMMON, mage.cards.m.MountainYeti.class)); + cards.add(new SetCardInfo("Nebuchadnezzar", 245, Rarity.RARE, mage.cards.n.Nebuchadnezzar.class)); + cards.add(new SetCardInfo("Nether Void", 113, Rarity.RARE, mage.cards.n.NetherVoid.class)); + cards.add(new SetCardInfo("Nicol Bolas", 246, Rarity.RARE, mage.cards.n.NicolBolas.class)); + cards.add(new SetCardInfo("Nova Pentacle", 289, Rarity.RARE, mage.cards.n.NovaPentacle.class)); + cards.add(new SetCardInfo("Osai Vultures", 29, Rarity.COMMON, mage.cards.o.OsaiVultures.class)); + cards.add(new SetCardInfo("Palladia-Mors", 247, Rarity.RARE, mage.cards.p.PalladiaMors.class)); + cards.add(new SetCardInfo("Part Water", 66, Rarity.UNCOMMON, mage.cards.p.PartWater.class)); + cards.add(new SetCardInfo("Pavel Maliki", 248, Rarity.UNCOMMON, mage.cards.p.PavelMaliki.class)); + cards.add(new SetCardInfo("Pendelhaven", 305, Rarity.UNCOMMON, mage.cards.p.Pendelhaven.class)); + cards.add(new SetCardInfo("Petra Sphinx", 30, Rarity.RARE, mage.cards.p.PetraSphinx.class)); + cards.add(new SetCardInfo("Pit Scorpion", 114, Rarity.COMMON, mage.cards.p.PitScorpion.class)); + cards.add(new SetCardInfo("Pixie Queen", 196, Rarity.RARE, mage.cards.p.PixieQueen.class)); + cards.add(new SetCardInfo("Planar Gate", 290, Rarity.RARE, mage.cards.p.PlanarGate.class)); + cards.add(new SetCardInfo("Pradesh Gypsies", 197, Rarity.UNCOMMON, mage.cards.p.PradeshGypsies.class)); + cards.add(new SetCardInfo("Presence of the Master", 31, Rarity.UNCOMMON, mage.cards.p.PresenceOfTheMaster.class)); + cards.add(new SetCardInfo("Primordial Ooze", 160, Rarity.UNCOMMON, mage.cards.p.PrimordialOoze.class)); + cards.add(new SetCardInfo("Princess Lucrezia", 249, Rarity.UNCOMMON, mage.cards.p.PrincessLucrezia.class)); + cards.add(new SetCardInfo("Psionic Entity", 67, Rarity.RARE, mage.cards.p.PsionicEntity.class)); + cards.add(new SetCardInfo("Psychic Purge", 68, Rarity.COMMON, mage.cards.p.PsychicPurge.class)); + cards.add(new SetCardInfo("Puppet Master", 69, Rarity.UNCOMMON, mage.cards.p.PuppetMaster.class)); + cards.add(new SetCardInfo("Pyrotechnics", 161, Rarity.COMMON, mage.cards.p.Pyrotechnics.class)); + cards.add(new SetCardInfo("Quagmire", 115, Rarity.UNCOMMON, mage.cards.q.Quagmire.class)); + cards.add(new SetCardInfo("Rabid Wombat", 198, Rarity.UNCOMMON, mage.cards.r.RabidWombat.class)); + cards.add(new SetCardInfo("Radjan Spirit", 199, Rarity.UNCOMMON, mage.cards.r.RadjanSpirit.class)); + cards.add(new SetCardInfo("Raging Bull", 163, Rarity.COMMON, mage.cards.r.RagingBull.class)); + cards.add(new SetCardInfo("Ragnar", 250, Rarity.RARE, mage.cards.r.Ragnar.class)); + cards.add(new SetCardInfo("Ramirez DePietro", 251, Rarity.UNCOMMON, mage.cards.r.RamirezDePietro.class)); + cards.add(new SetCardInfo("Ramses Overdark", 252, Rarity.RARE, mage.cards.r.RamsesOverdark.class)); + cards.add(new SetCardInfo("Rapid Fire", 32, Rarity.RARE, mage.cards.r.RapidFire.class)); + cards.add(new SetCardInfo("Rasputin Dreamweaver", 253, Rarity.RARE, mage.cards.r.RasputinDreamweaver.class)); + cards.add(new SetCardInfo("Recall", 70, Rarity.RARE, mage.cards.r.Recall.class)); + cards.add(new SetCardInfo("Red Mana Battery", 291, Rarity.UNCOMMON, mage.cards.r.RedManaBattery.class)); + cards.add(new SetCardInfo("Reincarnation", 201, Rarity.UNCOMMON, mage.cards.r.Reincarnation.class)); + cards.add(new SetCardInfo("Relic Barrier", 292, Rarity.UNCOMMON, mage.cards.r.RelicBarrier.class)); + cards.add(new SetCardInfo("Relic Bind", 71, Rarity.UNCOMMON, mage.cards.r.RelicBind.class)); + cards.add(new SetCardInfo("Remove Enchantments", 33, Rarity.COMMON, mage.cards.r.RemoveEnchantments.class)); + cards.add(new SetCardInfo("Remove Soul", 72, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); + cards.add(new SetCardInfo("Reset", 73, Rarity.UNCOMMON, mage.cards.r.Reset.class)); + cards.add(new SetCardInfo("Revelation", 202, Rarity.RARE, mage.cards.r.Revelation.class)); + cards.add(new SetCardInfo("Reverberation", 74, Rarity.RARE, mage.cards.r.Reverberation.class)); + cards.add(new SetCardInfo("Righteous Avengers", 34, Rarity.UNCOMMON, mage.cards.r.RighteousAvengers.class)); + cards.add(new SetCardInfo("Ring of Immortals", 293, Rarity.RARE, mage.cards.r.RingOfImmortals.class)); + cards.add(new SetCardInfo("Riven Turnbull", 254, Rarity.UNCOMMON, mage.cards.r.RivenTurnbull.class)); + cards.add(new SetCardInfo("Rohgahh of Kher Keep", 255, Rarity.RARE, mage.cards.r.RohgahhOfKherKeep.class)); + cards.add(new SetCardInfo("Rubinia Soulsinger", 256, Rarity.RARE, mage.cards.r.RubiniaSoulsinger.class)); + cards.add(new SetCardInfo("Rust", 203, Rarity.COMMON, mage.cards.r.Rust.class)); + cards.add(new SetCardInfo("Sea Kings' Blessing", 75, Rarity.UNCOMMON, mage.cards.s.SeaKingsBlessing.class)); + cards.add(new SetCardInfo("Seafarer's Quay", 306, Rarity.UNCOMMON, mage.cards.s.SeafarersQuay.class)); + cards.add(new SetCardInfo("Seeker", 35, Rarity.UNCOMMON, mage.cards.s.Seeker.class)); + cards.add(new SetCardInfo("Segovian Leviathan", 76, Rarity.UNCOMMON, mage.cards.s.SegovianLeviathan.class)); + cards.add(new SetCardInfo("Sentinel", 294, Rarity.RARE, mage.cards.s.Sentinel.class)); + cards.add(new SetCardInfo("Serpent Generator", 295, Rarity.RARE, mage.cards.s.SerpentGenerator.class)); + cards.add(new SetCardInfo("Shelkin Brownie", 204, Rarity.COMMON, mage.cards.s.ShelkinBrownie.class)); + cards.add(new SetCardInfo("Shield Wall", 36, Rarity.UNCOMMON, mage.cards.s.ShieldWall.class)); + cards.add(new SetCardInfo("Shimian Night Stalker", 116, Rarity.UNCOMMON, mage.cards.s.ShimianNightStalker.class)); + cards.add(new SetCardInfo("Sir Shandlar of Eberyn", 257, Rarity.UNCOMMON, mage.cards.s.SirShandlarOfEberyn.class)); + cards.add(new SetCardInfo("Sivitri Scarzam", 258, Rarity.UNCOMMON, mage.cards.s.SivitriScarzam.class)); + cards.add(new SetCardInfo("Sol'kanar the Swamp King", 259, Rarity.RARE, mage.cards.s.SolkanarTheSwampKing.class)); + cards.add(new SetCardInfo("Spectral Cloak", 78, Rarity.UNCOMMON, mage.cards.s.SpectralCloak.class)); + cards.add(new SetCardInfo("Spinal Villain", 164, Rarity.RARE, mage.cards.s.SpinalVillain.class)); + cards.add(new SetCardInfo("Spirit Link", 37, Rarity.UNCOMMON, mage.cards.s.SpiritLink.class)); + cards.add(new SetCardInfo("Spirit Shackle", 117, Rarity.COMMON, mage.cards.s.SpiritShackle.class)); + cards.add(new SetCardInfo("Spiritual Sanctuary", 38, Rarity.RARE, mage.cards.s.SpiritualSanctuary.class)); + cards.add(new SetCardInfo("Stangg", 260, Rarity.RARE, mage.cards.s.Stangg.class)); + cards.add(new SetCardInfo("Storm Seeker", 205, Rarity.UNCOMMON, mage.cards.s.StormSeeker.class)); + cards.add(new SetCardInfo("Storm World", 165, Rarity.RARE, mage.cards.s.StormWorld.class)); + cards.add(new SetCardInfo("Subdue", 206, Rarity.COMMON, mage.cards.s.Subdue.class)); + cards.add(new SetCardInfo("Sunastian Falconer", 261, Rarity.UNCOMMON, mage.cards.s.SunastianFalconer.class)); + cards.add(new SetCardInfo("Sword of the Ages", 296, Rarity.RARE, mage.cards.s.SwordOfTheAges.class)); + cards.add(new SetCardInfo("Sylvan Library", 207, Rarity.UNCOMMON, mage.cards.s.SylvanLibrary.class)); + cards.add(new SetCardInfo("Sylvan Paradise", 208, Rarity.UNCOMMON, mage.cards.s.SylvanParadise.class)); + cards.add(new SetCardInfo("Syphon Soul", 118, Rarity.COMMON, mage.cards.s.SyphonSoul.class)); + cards.add(new SetCardInfo("Telekinesis", 79, Rarity.RARE, mage.cards.t.Telekinesis.class)); + cards.add(new SetCardInfo("Teleport", 80, Rarity.RARE, mage.cards.t.Teleport.class)); + cards.add(new SetCardInfo("Tetsuo Umezawa", 262, Rarity.RARE, mage.cards.t.TetsuoUmezawa.class)); + cards.add(new SetCardInfo("The Abyss", 120, Rarity.RARE, mage.cards.t.TheAbyss.class)); + cards.add(new SetCardInfo("The Brute", 167, Rarity.COMMON, mage.cards.t.TheBrute.class)); + cards.add(new SetCardInfo("The Lady of the Mountain", 263, Rarity.UNCOMMON, mage.cards.t.TheLadyOfTheMountain.class)); + cards.add(new SetCardInfo("The Tabernacle at Pendrell Vale", 307, Rarity.RARE, mage.cards.t.TheTabernacleAtPendrellVale.class)); + cards.add(new SetCardInfo("The Wretched", 121, Rarity.RARE, mage.cards.t.TheWretched.class)); + cards.add(new SetCardInfo("Thunder Spirit", 39, Rarity.RARE, mage.cards.t.ThunderSpirit.class)); + cards.add(new SetCardInfo("Time Elemental", 81, Rarity.RARE, mage.cards.t.TimeElemental.class)); + cards.add(new SetCardInfo("Tobias Andrion", 264, Rarity.UNCOMMON, mage.cards.t.TobiasAndrion.class)); + cards.add(new SetCardInfo("Tolaria", 308, Rarity.UNCOMMON, mage.cards.t.Tolaria.class)); + cards.add(new SetCardInfo("Tor Wauki", 265, Rarity.UNCOMMON, mage.cards.t.TorWauki.class)); + cards.add(new SetCardInfo("Torsten Von Ursus", 266, Rarity.UNCOMMON, mage.cards.t.TorstenVonUrsus.class)); + cards.add(new SetCardInfo("Touch of Darkness", 122, Rarity.UNCOMMON, mage.cards.t.TouchOfDarkness.class)); + cards.add(new SetCardInfo("Transmutation", 123, Rarity.COMMON, mage.cards.t.Transmutation.class)); + cards.add(new SetCardInfo("Triassic Egg", 297, Rarity.RARE, mage.cards.t.TriassicEgg.class)); + cards.add(new SetCardInfo("Tuknir Deathlock", 267, Rarity.RARE, mage.cards.t.TuknirDeathlock.class)); + cards.add(new SetCardInfo("Tundra Wolves", 40, Rarity.COMMON, mage.cards.t.TundraWolves.class)); + cards.add(new SetCardInfo("Typhoon", 209, Rarity.RARE, mage.cards.t.Typhoon.class)); + cards.add(new SetCardInfo("Undertow", 82, Rarity.UNCOMMON, mage.cards.u.Undertow.class)); + cards.add(new SetCardInfo("Unholy Citadel", 309, Rarity.UNCOMMON, mage.cards.u.UnholyCitadel.class)); + cards.add(new SetCardInfo("Underworld Dreams", 124, Rarity.UNCOMMON, mage.cards.u.UnderworldDreams.class)); + cards.add(new SetCardInfo("Untamed Wilds", 210, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); + cards.add(new SetCardInfo("Ur-Drago", 268, Rarity.RARE, mage.cards.u.UrDrago.class)); + cards.add(new SetCardInfo("Urborg", 310, Rarity.UNCOMMON, mage.cards.u.Urborg.class)); + cards.add(new SetCardInfo("Vaevictis Asmadi", 269, Rarity.RARE, mage.cards.v.VaevictisAsmadi.class)); + cards.add(new SetCardInfo("Vampire Bats", 125, Rarity.COMMON, mage.cards.v.VampireBats.class)); + cards.add(new SetCardInfo("Venarian Gold", 83, Rarity.COMMON, mage.cards.v.VenarianGold.class)); + cards.add(new SetCardInfo("Visions", 41, Rarity.UNCOMMON, mage.cards.v.Visions.class)); + cards.add(new SetCardInfo("Voodoo Doll", 298, Rarity.RARE, mage.cards.v.VoodooDoll.class)); + cards.add(new SetCardInfo("Walking Dead", 126, Rarity.COMMON, mage.cards.w.WalkingDead.class)); + cards.add(new SetCardInfo("Wall of Caltrops", 42, Rarity.COMMON, mage.cards.w.WallOfCaltrops.class)); + cards.add(new SetCardInfo("Wall of Dust", 168, Rarity.UNCOMMON, mage.cards.w.WallOfDust.class)); + cards.add(new SetCardInfo("Wall of Earth", 169, Rarity.COMMON, mage.cards.w.WallOfEarth.class)); + cards.add(new SetCardInfo("Wall of Heat", 170, Rarity.COMMON, mage.cards.w.WallOfHeat.class)); + cards.add(new SetCardInfo("Wall of Light", 43, Rarity.UNCOMMON, mage.cards.w.WallOfLight.class)); + cards.add(new SetCardInfo("Wall of Opposition", 171, Rarity.RARE, mage.cards.w.WallOfOpposition.class)); + cards.add(new SetCardInfo("Wall of Putrid Flesh", 127, Rarity.UNCOMMON, mage.cards.w.WallOfPutridFlesh.class)); + cards.add(new SetCardInfo("Wall of Shadows", 128, Rarity.COMMON, mage.cards.w.WallOfShadows.class)); + cards.add(new SetCardInfo("Wall of Tombstones", 129, Rarity.UNCOMMON, mage.cards.w.WallOfTombstones.class)); + cards.add(new SetCardInfo("Wall of Vapor", 84, Rarity.COMMON, mage.cards.w.WallOfVapor.class)); + cards.add(new SetCardInfo("Wall of Wonder", 85, Rarity.UNCOMMON, mage.cards.w.WallOfWonder.class)); + cards.add(new SetCardInfo("Whirling Dervish", 211, Rarity.UNCOMMON, mage.cards.w.WhirlingDervish.class)); + cards.add(new SetCardInfo("White Mana Battery", 299, Rarity.UNCOMMON, mage.cards.w.WhiteManaBattery.class)); + cards.add(new SetCardInfo("Willow Satyr", 212, Rarity.RARE, mage.cards.w.WillowSatyr.class)); + cards.add(new SetCardInfo("Winds of Change", 172, Rarity.UNCOMMON, mage.cards.w.WindsOfChange.class)); + cards.add(new SetCardInfo("Winter Blast", 213, Rarity.RARE, mage.cards.w.WinterBlast.class)); + cards.add(new SetCardInfo("Wolverine Pack", 214, Rarity.COMMON, mage.cards.w.WolverinePack.class)); + cards.add(new SetCardInfo("Wood Elemental", 215, Rarity.RARE, mage.cards.w.WoodElemental.class)); + cards.add(new SetCardInfo("Xira Arien", 270, Rarity.RARE, mage.cards.x.XiraArien.class)); + cards.add(new SetCardInfo("Zephyr Falcon", 86, Rarity.COMMON, mage.cards.z.ZephyrFalcon.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/MastersEditionII.java b/Mage.Sets/src/mage/sets/MastersEditionII.java index 573f0482df..35d1b0cc66 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionII.java +++ b/Mage.Sets/src/mage/sets/MastersEditionII.java @@ -1,265 +1,265 @@ - -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * @author LevelX2 - */ -public final class MastersEditionII extends ExpansionSet { - - private static final MastersEditionII instance = new MastersEditionII(); - - public static MastersEditionII getInstance() { - return instance; - } - - private MastersEditionII() { - super("Masters Edition II", "ME2", ExpansionSet.buildDate(2008, 9, 22), SetType.MAGIC_ONLINE); - this.hasBasicLands = true; - this.hasBoosters = true; - this.numBoosterLands = 1; - this.numBoosterCommon = 10; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - - cards.add(new SetCardInfo("Abbey Gargoyles", 1, Rarity.UNCOMMON, mage.cards.a.AbbeyGargoyles.class)); - cards.add(new SetCardInfo("Adarkar Sentinel", 201, Rarity.COMMON, mage.cards.a.AdarkarSentinel.class)); - cards.add(new SetCardInfo("Aeolipile", 202, Rarity.COMMON, mage.cards.a.Aeolipile.class)); - cards.add(new SetCardInfo("Aether Storm", 39, Rarity.UNCOMMON, mage.cards.a.AetherStorm.class)); - cards.add(new SetCardInfo("Ambush Party", 115, Rarity.COMMON, mage.cards.a.AmbushParty.class)); - cards.add(new SetCardInfo("An-Zerrin Ruins", 117, Rarity.RARE, mage.cards.a.AnZerrinRuins.class)); - cards.add(new SetCardInfo("Anarchy", 116, Rarity.RARE, mage.cards.a.Anarchy.class)); - cards.add(new SetCardInfo("Angel of Fury", 2, Rarity.RARE, mage.cards.a.AngelOfFury.class)); - cards.add(new SetCardInfo("Angel of Light", 3, Rarity.UNCOMMON, mage.cards.a.AngelOfLight.class)); - cards.add(new SetCardInfo("Armor of Faith", 4, Rarity.COMMON, mage.cards.a.ArmorOfFaith.class)); - cards.add(new SetCardInfo("Armor Thrull", 77, Rarity.COMMON, mage.cards.a.ArmorThrull.class)); - cards.add(new SetCardInfo("Armored Griffin", 5, Rarity.COMMON, mage.cards.a.ArmoredGriffin.class)); - cards.add(new SetCardInfo("Ashen Ghoul", 78, Rarity.UNCOMMON, mage.cards.a.AshenGhoul.class)); - cards.add(new SetCardInfo("Ashnod's Cylix", 203, Rarity.RARE, mage.cards.a.AshnodsCylix.class)); - cards.add(new SetCardInfo("Aurochs", 153, Rarity.COMMON, mage.cards.a.Aurochs.class)); - cards.add(new SetCardInfo("Aysen Bureaucrats", 6, Rarity.COMMON, mage.cards.a.AysenBureaucrats.class)); - cards.add(new SetCardInfo("Aysen Crusader", 7, Rarity.UNCOMMON, mage.cards.a.AysenCrusader.class)); - cards.add(new SetCardInfo("Badlands", 225, Rarity.RARE, mage.cards.b.Badlands.class)); - cards.add(new SetCardInfo("Balduvian Conjurer", 40, Rarity.COMMON, mage.cards.b.BalduvianConjurer.class)); - cards.add(new SetCardInfo("Balduvian Dead", 79, Rarity.UNCOMMON, mage.cards.b.BalduvianDead.class)); - cards.add(new SetCardInfo("Balduvian Hydra", 118, Rarity.RARE, mage.cards.b.BalduvianHydra.class)); - cards.add(new SetCardInfo("Balduvian Trading Post", 226, Rarity.RARE, mage.cards.b.BalduvianTradingPost.class)); - cards.add(new SetCardInfo("Barbed Sextant", 204, Rarity.COMMON, mage.cards.b.BarbedSextant.class)); - cards.add(new SetCardInfo("Binding Grasp", 41, Rarity.RARE, mage.cards.b.BindingGrasp.class)); - cards.add(new SetCardInfo("Bounty of the Hunt", 154, Rarity.RARE, mage.cards.b.BountyOfTheHunt.class)); - cards.add(new SetCardInfo("Brainstorm", 42, Rarity.COMMON, mage.cards.b.Brainstorm.class)); - cards.add(new SetCardInfo("Brassclaw Orcs", 119, Rarity.COMMON, mage.cards.b.BrassclawOrcs.class)); - cards.add(new SetCardInfo("Brimstone Dragon", 120, Rarity.RARE, mage.cards.b.BrimstoneDragon.class)); - cards.add(new SetCardInfo("Brine Shaman", 80, Rarity.COMMON, mage.cards.b.BrineShaman.class)); - cards.add(new SetCardInfo("Broken Visage", 81, Rarity.UNCOMMON, mage.cards.b.BrokenVisage.class)); - cards.add(new SetCardInfo("Browse", 43, Rarity.UNCOMMON, mage.cards.b.Browse.class)); - cards.add(new SetCardInfo("Burnout", 121, Rarity.UNCOMMON, mage.cards.b.Burnout.class)); - cards.add(new SetCardInfo("Carapace", 155, Rarity.COMMON, mage.cards.c.Carapace.class)); - cards.add(new SetCardInfo("Caribou Range", 8, Rarity.RARE, mage.cards.c.CaribouRange.class)); - cards.add(new SetCardInfo("Cloak of Confusion", 82, Rarity.COMMON, mage.cards.c.CloakOfConfusion.class)); - cards.add(new SetCardInfo("Clockwork Steed", 205, Rarity.UNCOMMON, mage.cards.c.ClockworkSteed.class)); - cards.add(new SetCardInfo("Combat Medic", 9, Rarity.COMMON, mage.cards.c.CombatMedic.class)); - cards.add(new SetCardInfo("Conquer", 122, Rarity.UNCOMMON, mage.cards.c.Conquer.class)); - cards.add(new SetCardInfo("Counterspell", 44, Rarity.UNCOMMON, mage.cards.c.Counterspell.class)); - cards.add(new SetCardInfo("Dance of the Dead", 83, Rarity.UNCOMMON, mage.cards.d.DanceOfTheDead.class)); - cards.add(new SetCardInfo("Dark Banishing", 84, Rarity.COMMON, mage.cards.d.DarkBanishing.class)); - cards.add(new SetCardInfo("Death Spark", 123, Rarity.COMMON, mage.cards.d.DeathSpark.class)); - cards.add(new SetCardInfo("Deep Spawn", 45, Rarity.RARE, mage.cards.d.DeepSpawn.class)); - cards.add(new SetCardInfo("Demonic Consultation", 85, Rarity.UNCOMMON, mage.cards.d.DemonicConsultation.class)); - cards.add(new SetCardInfo("Despotic Scepter", 206, Rarity.RARE, mage.cards.d.DespoticScepter.class)); - cards.add(new SetCardInfo("Diabolic Vision", 191, Rarity.UNCOMMON, mage.cards.d.DiabolicVision.class)); - cards.add(new SetCardInfo("Disenchant", 10, Rarity.COMMON, mage.cards.d.Disenchant.class)); - cards.add(new SetCardInfo("Dreams of the Dead", 46, Rarity.RARE, mage.cards.d.DreamsOfTheDead.class)); - cards.add(new SetCardInfo("Drift of the Dead", 86, Rarity.COMMON, mage.cards.d.DriftOfTheDead.class)); - cards.add(new SetCardInfo("Dry Spell", 87, Rarity.COMMON, mage.cards.d.DrySpell.class)); - cards.add(new SetCardInfo("Dwarven Ruins", 227, Rarity.UNCOMMON, mage.cards.d.DwarvenRuins.class)); - cards.add(new SetCardInfo("Dystopia", 88, Rarity.RARE, mage.cards.d.Dystopia.class)); - cards.add(new SetCardInfo("Earthlink", 192, Rarity.RARE, mage.cards.e.Earthlink.class)); - cards.add(new SetCardInfo("Ebon Praetor", 89, Rarity.RARE, mage.cards.e.EbonPraetor.class)); - cards.add(new SetCardInfo("Ebon Stronghold", 228, Rarity.UNCOMMON, mage.cards.e.EbonStronghold.class)); - cards.add(new SetCardInfo("Elemental Augury", 193, Rarity.RARE, mage.cards.e.ElementalAugury.class)); - cards.add(new SetCardInfo("Elkin Bottle", 207, Rarity.RARE, mage.cards.e.ElkinBottle.class)); - cards.add(new SetCardInfo("Elven Lyre", 208, Rarity.COMMON, mage.cards.e.ElvenLyre.class)); - cards.add(new SetCardInfo("Elvish Farmer", 156, Rarity.RARE, mage.cards.e.ElvishFarmer.class)); - cards.add(new SetCardInfo("Elvish Hunter", 157, Rarity.COMMON, mage.cards.e.ElvishHunter.class)); - cards.add(new SetCardInfo("Elvish Ranger", 158, Rarity.COMMON, mage.cards.e.ElvishRanger.class)); - cards.add(new SetCardInfo("Elvish Spirit Guide", 159, Rarity.UNCOMMON, mage.cards.e.ElvishSpiritGuide.class)); - cards.add(new SetCardInfo("Energy Storm", 11, Rarity.RARE, mage.cards.e.EnergyStorm.class)); - cards.add(new SetCardInfo("Enervate", 47, Rarity.COMMON, mage.cards.e.Enervate.class)); - cards.add(new SetCardInfo("Errand of Duty", 12, Rarity.UNCOMMON, mage.cards.e.ErrandOfDuty.class)); - cards.add(new SetCardInfo("Errantry", 124, Rarity.COMMON, mage.cards.e.Errantry.class)); - cards.add(new SetCardInfo("Essence Filter", 160, Rarity.UNCOMMON, mage.cards.e.EssenceFilter.class)); - cards.add(new SetCardInfo("Essence Flare", 48, Rarity.COMMON, mage.cards.e.EssenceFlare.class)); - cards.add(new SetCardInfo("Farrel's Mantle", 13, Rarity.UNCOMMON, mage.cards.f.FarrelsMantle.class)); - cards.add(new SetCardInfo("Farrel's Zealot", 14, Rarity.UNCOMMON, mage.cards.f.FarrelsZealot.class)); - cards.add(new SetCardInfo("Feral Thallid", 161, Rarity.COMMON, mage.cards.f.FeralThallid.class)); - cards.add(new SetCardInfo("Fire Dragon", 125, Rarity.RARE, mage.cards.f.FireDragon.class)); - cards.add(new SetCardInfo("Flame Spirit", 126, Rarity.UNCOMMON, mage.cards.f.FlameSpirit.class)); - cards.add(new SetCardInfo("Folk of the Pines", 162, Rarity.COMMON, mage.cards.f.FolkOfThePines.class)); - cards.add(new SetCardInfo("Forbidden Lore", 163, Rarity.UNCOMMON, mage.cards.f.ForbiddenLore.class)); - cards.add(new SetCardInfo("Forgotten Lore", 164, Rarity.UNCOMMON, mage.cards.f.ForgottenLore.class)); - cards.add(new SetCardInfo("Foul Familiar", 90, Rarity.COMMON, mage.cards.f.FoulFamiliar.class)); - cards.add(new SetCardInfo("Fumarole", 194, Rarity.UNCOMMON, mage.cards.f.Fumarole.class)); - cards.add(new SetCardInfo("Funeral March", 91, Rarity.COMMON, mage.cards.f.FuneralMarch.class)); - cards.add(new SetCardInfo("Fungal Bloom", 165, Rarity.RARE, mage.cards.f.FungalBloom.class)); - cards.add(new SetCardInfo("Fyndhorn Pollen", 166, Rarity.RARE, mage.cards.f.FyndhornPollen.class)); - cards.add(new SetCardInfo("Gangrenous Zombies", 92, Rarity.COMMON, mage.cards.g.GangrenousZombies.class)); - cards.add(new SetCardInfo("Giant Growth", 167, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); - cards.add(new SetCardInfo("Giant Trap Door Spider", 195, Rarity.UNCOMMON, mage.cards.g.GiantTrapDoorSpider.class)); - cards.add(new SetCardInfo("Glacial Chasm", 229, Rarity.RARE, mage.cards.g.GlacialChasm.class)); - cards.add(new SetCardInfo("Glacial Crevasses", 127, Rarity.RARE, mage.cards.g.GlacialCrevasses.class)); - cards.add(new SetCardInfo("Gorilla Shaman", 129, Rarity.UNCOMMON, mage.cards.g.GorillaShaman.class)); - cards.add(new SetCardInfo("Grandmother Sengir", 93, Rarity.RARE, mage.cards.g.GrandmotherSengir.class)); - cards.add(new SetCardInfo("Havenwood Battleground", 230, Rarity.UNCOMMON, mage.cards.h.HavenwoodBattleground.class)); - cards.add(new SetCardInfo("Heart of Yavimaya", 231, Rarity.RARE, mage.cards.h.HeartOfYavimaya.class)); - cards.add(new SetCardInfo("Helm of Obedience", 210, Rarity.RARE, mage.cards.h.HelmOfObedience.class)); - cards.add(new SetCardInfo("Icatian Javelineers", 15, Rarity.COMMON, mage.cards.i.IcatianJavelineers.class)); - cards.add(new SetCardInfo("Icatian Phalanx", 16, Rarity.COMMON, mage.cards.i.IcatianPhalanx.class)); - cards.add(new SetCardInfo("Icatian Scout", 17, Rarity.COMMON, mage.cards.i.IcatianScout.class)); - cards.add(new SetCardInfo("Ice Floe", 232, Rarity.UNCOMMON, mage.cards.i.IceFloe.class)); - cards.add(new SetCardInfo("Iceberg", 49, Rarity.UNCOMMON, mage.cards.i.Iceberg.class)); - cards.add(new SetCardInfo("Icequake", 94, Rarity.COMMON, mage.cards.i.Icequake.class)); - cards.add(new SetCardInfo("Icy Prison", 50, Rarity.COMMON, mage.cards.i.IcyPrison.class)); - cards.add(new SetCardInfo("Ihsan's Shade", 95, Rarity.RARE, mage.cards.i.IhsansShade.class)); - cards.add(new SetCardInfo("Imperial Recruiter", 130, Rarity.RARE, mage.cards.i.ImperialRecruiter.class)); - cards.add(new SetCardInfo("Imperial Seal", 96, Rarity.RARE, mage.cards.i.ImperialSeal.class)); - cards.add(new SetCardInfo("Incinerate", 131, Rarity.COMMON, mage.cards.i.Incinerate.class)); - cards.add(new SetCardInfo("Infernal Darkness", 97, Rarity.RARE, mage.cards.i.InfernalDarkness.class)); - cards.add(new SetCardInfo("Inheritance", 18, Rarity.UNCOMMON, mage.cards.i.Inheritance.class)); - cards.add(new SetCardInfo("Ironclaw Orcs", 132, Rarity.COMMON, mage.cards.i.IronclawOrcs.class)); - cards.add(new SetCardInfo("Ivory Gargoyle", 19, Rarity.RARE, mage.cards.i.IvoryGargoyle.class)); - cards.add(new SetCardInfo("Jester's Mask", 211, Rarity.RARE, mage.cards.j.JestersMask.class)); - cards.add(new SetCardInfo("Jeweled Amulet", 212, Rarity.UNCOMMON, mage.cards.j.JeweledAmulet.class)); - cards.add(new SetCardInfo("Johtull Wurm", 168, Rarity.UNCOMMON, mage.cards.j.JohtullWurm.class)); - cards.add(new SetCardInfo("Joven's Ferrets", 169, Rarity.UNCOMMON, mage.cards.j.JovensFerrets.class)); - cards.add(new SetCardInfo("Juniper Order Advocate", 20, Rarity.UNCOMMON, mage.cards.j.JuniperOrderAdvocate.class)); - cards.add(new SetCardInfo("Karplusan Giant", 133, Rarity.UNCOMMON, mage.cards.k.KarplusanGiant.class)); - cards.add(new SetCardInfo("Kaysa", 170, Rarity.RARE, mage.cards.k.Kaysa.class)); - cards.add(new SetCardInfo("Kjeldoran Dead", 98, Rarity.COMMON, mage.cards.k.KjeldoranDead.class)); - cards.add(new SetCardInfo("Kjeldoran Home Guard", 22, Rarity.UNCOMMON, mage.cards.k.KjeldoranHomeGuard.class)); - cards.add(new SetCardInfo("Kjeldoran Outpost", 233, Rarity.RARE, mage.cards.k.KjeldoranOutpost.class)); - cards.add(new SetCardInfo("Kjeldoran Skycaptain", 23, Rarity.COMMON, mage.cards.k.KjeldoranSkycaptain.class)); - cards.add(new SetCardInfo("Knight of Stromgald", 99, Rarity.UNCOMMON, mage.cards.k.KnightOfStromgald.class)); - cards.add(new SetCardInfo("Krovikan Fetish", 100, Rarity.COMMON, mage.cards.k.KrovikanFetish.class)); - cards.add(new SetCardInfo("Krovikan Horror", 101, Rarity.RARE, mage.cards.k.KrovikanHorror.class)); - cards.add(new SetCardInfo("Krovikan Sorcerer", 51, Rarity.COMMON, mage.cards.k.KrovikanSorcerer.class)); - cards.add(new SetCardInfo("Krovikan Vampire", 102, Rarity.UNCOMMON, mage.cards.k.KrovikanVampire.class)); - cards.add(new SetCardInfo("Lat-Nam's Legacy", 52, Rarity.COMMON, mage.cards.l.LatNamsLegacy.class)); - cards.add(new SetCardInfo("Leaping Lizard", 171, Rarity.COMMON, mage.cards.l.LeapingLizard.class)); - cards.add(new SetCardInfo("Lim-Dul's High Guard", 103, Rarity.UNCOMMON, mage.cards.l.LimDulsHighGuard.class)); - cards.add(new SetCardInfo("Lodestone Bauble", 213, Rarity.RARE, mage.cards.l.LodestoneBauble.class)); - cards.add(new SetCardInfo("Lost Order of Jarkeld", 24, Rarity.RARE, mage.cards.l.LostOrderOfJarkeld.class)); - cards.add(new SetCardInfo("Magus of the Unseen", 53, Rarity.RARE, mage.cards.m.MagusOfTheUnseen.class)); - cards.add(new SetCardInfo("Mana Crypt", 214, Rarity.RARE, mage.cards.m.ManaCrypt.class)); - cards.add(new SetCardInfo("Marjhan", 54, Rarity.RARE, mage.cards.m.Marjhan.class)); - cards.add(new SetCardInfo("Mesmeric Trance", 55, Rarity.RARE, mage.cards.m.MesmericTrance.class)); - cards.add(new SetCardInfo("Meteor Shower", 135, Rarity.COMMON, mage.cards.m.MeteorShower.class)); - cards.add(new SetCardInfo("Minion of Leshrac", 104, Rarity.RARE, mage.cards.m.MinionOfLeshrac.class)); - cards.add(new SetCardInfo("Mishra's Groundbreaker", 215, Rarity.UNCOMMON, mage.cards.m.MishrasGroundbreaker.class)); - cards.add(new SetCardInfo("Misinformation", 105, Rarity.UNCOMMON, mage.cards.m.Misinformation.class)); - cards.add(new SetCardInfo("Mudslide", 136, Rarity.RARE, mage.cards.m.Mudslide.class)); - cards.add(new SetCardInfo("Musician", 56, Rarity.RARE, mage.cards.m.Musician.class)); - cards.add(new SetCardInfo("Narwhal", 57, Rarity.UNCOMMON, mage.cards.n.Narwhal.class)); - cards.add(new SetCardInfo("Nature's Blessing", 196, Rarity.UNCOMMON, mage.cards.n.NaturesBlessing.class)); - cards.add(new SetCardInfo("Nature's Wrath", 172, Rarity.RARE, mage.cards.n.NaturesWrath.class)); - cards.add(new SetCardInfo("Necrite", 106, Rarity.COMMON, mage.cards.n.Necrite.class)); - cards.add(new SetCardInfo("Necropotence", 107, Rarity.RARE, mage.cards.n.Necropotence.class)); - cards.add(new SetCardInfo("Night Soil", 173, Rarity.UNCOMMON, mage.cards.n.NightSoil.class)); - cards.add(new SetCardInfo("Orc General", 137, Rarity.UNCOMMON, mage.cards.o.OrcGeneral.class)); - cards.add(new SetCardInfo("Orcish Cannoneers", 138, Rarity.UNCOMMON, mage.cards.o.OrcishCannoneers.class)); - cards.add(new SetCardInfo("Orcish Captain", 139, Rarity.UNCOMMON, mage.cards.o.OrcishCaptain.class)); - cards.add(new SetCardInfo("Orcish Lumberjack", 142, Rarity.COMMON, mage.cards.o.OrcishLumberjack.class)); - cards.add(new SetCardInfo("Orcish Squatters", 143, Rarity.RARE, mage.cards.o.OrcishSquatters.class)); - cards.add(new SetCardInfo("Orcish Veteran", 144, Rarity.COMMON, mage.cards.o.OrcishVeteran.class)); - cards.add(new SetCardInfo("Order of the Sacred Torch", 25, Rarity.RARE, mage.cards.o.OrderOfTheSacredTorch.class)); - cards.add(new SetCardInfo("Order of the White Shield", 26, Rarity.UNCOMMON, mage.cards.o.OrderOfTheWhiteShield.class)); - cards.add(new SetCardInfo("Panic", 145, Rarity.COMMON, mage.cards.p.Panic.class)); - cards.add(new SetCardInfo("Personal Tutor", 58, Rarity.UNCOMMON, mage.cards.p.PersonalTutor.class)); - cards.add(new SetCardInfo("Phantasmal Fiend", 108, Rarity.COMMON, mage.cards.p.PhantasmalFiend.class)); - cards.add(new SetCardInfo("Phantasmal Mount", 59, Rarity.COMMON, mage.cards.p.PhantasmalMount.class)); - cards.add(new SetCardInfo("Phyrexian Devourer", 216, Rarity.UNCOMMON, mage.cards.p.PhyrexianDevourer.class)); - cards.add(new SetCardInfo("Pillage", 146, Rarity.UNCOMMON, mage.cards.p.Pillage.class)); - cards.add(new SetCardInfo("Portent", 60, Rarity.COMMON, mage.cards.p.Portent.class)); - cards.add(new SetCardInfo("Pyrokinesis", 147, Rarity.RARE, mage.cards.p.Pyrokinesis.class)); - cards.add(new SetCardInfo("Ravages of War", 27, Rarity.RARE, mage.cards.r.RavagesOfWar.class)); - cards.add(new SetCardInfo("Ray of Command", 61, Rarity.UNCOMMON, mage.cards.r.RayOfCommand.class)); - cards.add(new SetCardInfo("Red Cliffs Armada", 62, Rarity.COMMON, mage.cards.r.RedCliffsArmada.class)); - cards.add(new SetCardInfo("Reinforcements", 28, Rarity.COMMON, mage.cards.r.Reinforcements.class)); - cards.add(new SetCardInfo("Reprisal", 29, Rarity.COMMON, mage.cards.r.Reprisal.class)); - cards.add(new SetCardInfo("Retribution", 148, Rarity.UNCOMMON, mage.cards.r.Retribution.class)); - cards.add(new SetCardInfo("Righteous Fury", 30, Rarity.RARE, mage.cards.r.RighteousFury.class)); - cards.add(new SetCardInfo("Ritual of Subdual", 174, Rarity.RARE, mage.cards.r.RitualOfSubdual.class)); - cards.add(new SetCardInfo("Ritual of the Machine", 109, Rarity.RARE, mage.cards.r.RitualOfTheMachine.class)); - cards.add(new SetCardInfo("Roterothopter", 218, Rarity.COMMON, mage.cards.r.Roterothopter.class)); - cards.add(new SetCardInfo("Rogue Skycaptain", 149, Rarity.RARE, mage.cards.r.RogueSkycaptain.class)); - cards.add(new SetCardInfo("Royal Decree", 31, Rarity.RARE, mage.cards.r.RoyalDecree.class)); - cards.add(new SetCardInfo("Royal Trooper", 32, Rarity.COMMON, mage.cards.r.RoyalTrooper.class)); - cards.add(new SetCardInfo("Ruins of Trokair", 234, Rarity.UNCOMMON, mage.cards.r.RuinsOfTrokair.class)); - cards.add(new SetCardInfo("Savannah", 235, Rarity.RARE, mage.cards.s.Savannah.class)); - cards.add(new SetCardInfo("Screeching Drake", 63, Rarity.COMMON, mage.cards.s.ScreechingDrake.class)); - cards.add(new SetCardInfo("Sea Drake", 64, Rarity.RARE, mage.cards.s.SeaDrake.class)); - cards.add(new SetCardInfo("Sea Spirit", 65, Rarity.UNCOMMON, mage.cards.s.SeaSpirit.class)); - cards.add(new SetCardInfo("Shield Bearer", 35, Rarity.COMMON, mage.cards.s.ShieldBearer.class)); - cards.add(new SetCardInfo("Shrink", 175, Rarity.COMMON, mage.cards.s.Shrink.class)); - cards.add(new SetCardInfo("Shyft", 66, Rarity.COMMON, mage.cards.s.Shyft.class)); - cards.add(new SetCardInfo("Sibilant Spirit", 67, Rarity.RARE, mage.cards.s.SibilantSpirit.class)); - cards.add(new SetCardInfo("Skeleton Ship", 197, Rarity.RARE, mage.cards.s.SkeletonShip.class)); - cards.add(new SetCardInfo("Skull Catapult", 219, Rarity.UNCOMMON, mage.cards.s.SkullCatapult.class)); - cards.add(new SetCardInfo("Snow Fortress", 220, Rarity.UNCOMMON, mage.cards.s.SnowFortress.class)); - cards.add(new SetCardInfo("Snow-Covered Forest", 245, Rarity.LAND, mage.cards.s.SnowCoveredForest.class)); - cards.add(new SetCardInfo("Snow-Covered Island", 242, Rarity.LAND, mage.cards.s.SnowCoveredIsland.class)); - cards.add(new SetCardInfo("Snow-Covered Mountain", 244, Rarity.LAND, mage.cards.s.SnowCoveredMountain.class)); - cards.add(new SetCardInfo("Snow-Covered Plains", 241, Rarity.LAND, mage.cards.s.SnowCoveredPlains.class)); - cards.add(new SetCardInfo("Snow-Covered Swamp", 243, Rarity.LAND, mage.cards.s.SnowCoveredSwamp.class)); - cards.add(new SetCardInfo("Soldevi Digger", 221, Rarity.UNCOMMON, mage.cards.s.SoldeviDigger.class)); - cards.add(new SetCardInfo("Soldevi Excavations", 236, Rarity.RARE, mage.cards.s.SoldeviExcavations.class)); - cards.add(new SetCardInfo("Soldevi Simulacrum", 222, Rarity.UNCOMMON, mage.cards.s.SoldeviSimulacrum.class)); - cards.add(new SetCardInfo("Songs of the Damned", 110, Rarity.COMMON, mage.cards.s.SongsOfTheDamned.class)); - cards.add(new SetCardInfo("Soul Exchange", 111, Rarity.UNCOMMON, mage.cards.s.SoulExchange.class)); - cards.add(new SetCardInfo("Soul Kiss", 112, Rarity.UNCOMMON, mage.cards.s.SoulKiss.class)); - cards.add(new SetCardInfo("Spore Cloud", 176, Rarity.UNCOMMON, mage.cards.s.SporeCloud.class)); - cards.add(new SetCardInfo("Spore Flower", 177, Rarity.UNCOMMON, mage.cards.s.SporeFlower.class)); - cards.add(new SetCardInfo("Stampede", 178, Rarity.UNCOMMON, mage.cards.s.Stampede.class)); - cards.add(new SetCardInfo("Stone Spirit", 150, Rarity.UNCOMMON, mage.cards.s.StoneSpirit.class)); - cards.add(new SetCardInfo("Stonehands", 151, Rarity.COMMON, mage.cards.s.Stonehands.class)); - cards.add(new SetCardInfo("Storm Spirit", 198, Rarity.RARE, mage.cards.s.StormSpirit.class)); - cards.add(new SetCardInfo("Stromgald Cabal", 113, Rarity.RARE, mage.cards.s.StromgaldCabal.class)); - cards.add(new SetCardInfo("Stunted Growth", 179, Rarity.RARE, mage.cards.s.StuntedGrowth.class)); - cards.add(new SetCardInfo("Sustaining Spirit", 36, Rarity.RARE, mage.cards.s.SustainingSpirit.class)); - cards.add(new SetCardInfo("Svyelunite Temple", 237, Rarity.UNCOMMON, mage.cards.s.SvyeluniteTemple.class)); - cards.add(new SetCardInfo("Swords to Plowshares", 37, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); - cards.add(new SetCardInfo("Taiga", 238, Rarity.RARE, mage.cards.t.Taiga.class)); - cards.add(new SetCardInfo("Temporal Manipulation", 69, Rarity.RARE, mage.cards.t.TemporalManipulation.class)); - cards.add(new SetCardInfo("Thallid Devourer", 181, Rarity.COMMON, mage.cards.t.ThallidDevourer.class)); - cards.add(new SetCardInfo("Thallid", 180, Rarity.COMMON, mage.cards.t.Thallid.class)); - cards.add(new SetCardInfo("Thelonite Druid", 182, Rarity.RARE, mage.cards.t.TheloniteDruid.class)); - cards.add(new SetCardInfo("Thermokarst", 183, Rarity.COMMON, mage.cards.t.Thermokarst.class)); - cards.add(new SetCardInfo("Thought Lash", 70, Rarity.RARE, mage.cards.t.ThoughtLash.class)); - cards.add(new SetCardInfo("Thunder Wall", 71, Rarity.UNCOMMON, mage.cards.t.ThunderWall.class)); - cards.add(new SetCardInfo("Time Bomb", 223, Rarity.RARE, mage.cards.t.TimeBomb.class)); - cards.add(new SetCardInfo("Tinder Wall", 184, Rarity.COMMON, mage.cards.t.TinderWall.class)); - cards.add(new SetCardInfo("Tundra", 239, Rarity.RARE, mage.cards.t.Tundra.class)); - cards.add(new SetCardInfo("Underground Sea", 240, Rarity.RARE, mage.cards.u.UndergroundSea.class)); - cards.add(new SetCardInfo("Varchild's Crusader", 152, Rarity.COMMON, mage.cards.v.VarchildsCrusader.class)); - cards.add(new SetCardInfo("Viscerid Armor", 72, Rarity.COMMON, mage.cards.v.VisceridArmor.class)); - cards.add(new SetCardInfo("Viscerid Drone", 73, Rarity.UNCOMMON, mage.cards.v.VisceridDrone.class)); - cards.add(new SetCardInfo("Wall of Kelp", 74, Rarity.COMMON, mage.cards.w.WallOfKelp.class)); - cards.add(new SetCardInfo("Warning", 38, Rarity.COMMON, mage.cards.w.Warning.class)); - cards.add(new SetCardInfo("Whirling Catapult", 224, Rarity.UNCOMMON, mage.cards.w.WhirlingCatapult.class)); - cards.add(new SetCardInfo("Whiteout", 185, Rarity.COMMON, mage.cards.w.Whiteout.class)); - cards.add(new SetCardInfo("Wiitigo", 186, Rarity.RARE, mage.cards.w.Wiitigo.class)); - cards.add(new SetCardInfo("Wind Spirit", 75, Rarity.UNCOMMON, mage.cards.w.WindSpirit.class)); - cards.add(new SetCardInfo("Wings of Aesthir", 199, Rarity.UNCOMMON, mage.cards.w.WingsOfAesthir.class)); - cards.add(new SetCardInfo("Winter's Night", 200, Rarity.RARE, mage.cards.w.WintersNight.class)); - cards.add(new SetCardInfo("Withering Wisps", 114, Rarity.UNCOMMON, mage.cards.w.WitheringWisps.class)); - cards.add(new SetCardInfo("Wolf Pack", 187, Rarity.RARE, mage.cards.w.WolfPack.class)); - cards.add(new SetCardInfo("Woolly Mammoths", 188, Rarity.COMMON, mage.cards.w.WoollyMammoths.class)); - cards.add(new SetCardInfo("Woolly Spider", 189, Rarity.UNCOMMON, mage.cards.w.WoollySpider.class)); - cards.add(new SetCardInfo("Yavimaya Ancients", 190, Rarity.UNCOMMON, mage.cards.y.YavimayaAncients.class)); - cards.add(new SetCardInfo("Zuran Spellcaster", 76, Rarity.COMMON, mage.cards.z.ZuranSpellcaster.class)); - } -} + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author LevelX2 + */ +public final class MastersEditionII extends ExpansionSet { + + private static final MastersEditionII instance = new MastersEditionII(); + + public static MastersEditionII getInstance() { + return instance; + } + + private MastersEditionII() { + super("Masters Edition II", "ME2", ExpansionSet.buildDate(2008, 9, 22), SetType.MAGIC_ONLINE); + this.hasBasicLands = true; + this.hasBoosters = true; + this.numBoosterLands = 1; + this.numBoosterCommon = 10; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + + cards.add(new SetCardInfo("Abbey Gargoyles", 1, Rarity.UNCOMMON, mage.cards.a.AbbeyGargoyles.class)); + cards.add(new SetCardInfo("Adarkar Sentinel", 201, Rarity.COMMON, mage.cards.a.AdarkarSentinel.class)); + cards.add(new SetCardInfo("Aeolipile", 202, Rarity.COMMON, mage.cards.a.Aeolipile.class)); + cards.add(new SetCardInfo("Aether Storm", 39, Rarity.UNCOMMON, mage.cards.a.AetherStorm.class)); + cards.add(new SetCardInfo("Ambush Party", 115, Rarity.COMMON, mage.cards.a.AmbushParty.class)); + cards.add(new SetCardInfo("An-Zerrin Ruins", 117, Rarity.RARE, mage.cards.a.AnZerrinRuins.class)); + cards.add(new SetCardInfo("Anarchy", 116, Rarity.RARE, mage.cards.a.Anarchy.class)); + cards.add(new SetCardInfo("Angel of Fury", 2, Rarity.RARE, mage.cards.a.AngelOfFury.class)); + cards.add(new SetCardInfo("Angel of Light", 3, Rarity.UNCOMMON, mage.cards.a.AngelOfLight.class)); + cards.add(new SetCardInfo("Armor of Faith", 4, Rarity.COMMON, mage.cards.a.ArmorOfFaith.class)); + cards.add(new SetCardInfo("Armor Thrull", 77, Rarity.COMMON, mage.cards.a.ArmorThrull.class)); + cards.add(new SetCardInfo("Armored Griffin", 5, Rarity.COMMON, mage.cards.a.ArmoredGriffin.class)); + cards.add(new SetCardInfo("Ashen Ghoul", 78, Rarity.UNCOMMON, mage.cards.a.AshenGhoul.class)); + cards.add(new SetCardInfo("Ashnod's Cylix", 203, Rarity.RARE, mage.cards.a.AshnodsCylix.class)); + cards.add(new SetCardInfo("Aurochs", 153, Rarity.COMMON, mage.cards.a.Aurochs.class)); + cards.add(new SetCardInfo("Aysen Bureaucrats", 6, Rarity.COMMON, mage.cards.a.AysenBureaucrats.class)); + cards.add(new SetCardInfo("Aysen Crusader", 7, Rarity.UNCOMMON, mage.cards.a.AysenCrusader.class)); + cards.add(new SetCardInfo("Badlands", 225, Rarity.RARE, mage.cards.b.Badlands.class)); + cards.add(new SetCardInfo("Balduvian Conjurer", 40, Rarity.COMMON, mage.cards.b.BalduvianConjurer.class)); + cards.add(new SetCardInfo("Balduvian Dead", 79, Rarity.UNCOMMON, mage.cards.b.BalduvianDead.class)); + cards.add(new SetCardInfo("Balduvian Hydra", 118, Rarity.RARE, mage.cards.b.BalduvianHydra.class)); + cards.add(new SetCardInfo("Balduvian Trading Post", 226, Rarity.RARE, mage.cards.b.BalduvianTradingPost.class)); + cards.add(new SetCardInfo("Barbed Sextant", 204, Rarity.COMMON, mage.cards.b.BarbedSextant.class)); + cards.add(new SetCardInfo("Binding Grasp", 41, Rarity.RARE, mage.cards.b.BindingGrasp.class)); + cards.add(new SetCardInfo("Bounty of the Hunt", 154, Rarity.RARE, mage.cards.b.BountyOfTheHunt.class)); + cards.add(new SetCardInfo("Brainstorm", 42, Rarity.COMMON, mage.cards.b.Brainstorm.class)); + cards.add(new SetCardInfo("Brassclaw Orcs", 119, Rarity.COMMON, mage.cards.b.BrassclawOrcs.class)); + cards.add(new SetCardInfo("Brimstone Dragon", 120, Rarity.RARE, mage.cards.b.BrimstoneDragon.class)); + cards.add(new SetCardInfo("Brine Shaman", 80, Rarity.COMMON, mage.cards.b.BrineShaman.class)); + cards.add(new SetCardInfo("Broken Visage", 81, Rarity.UNCOMMON, mage.cards.b.BrokenVisage.class)); + cards.add(new SetCardInfo("Browse", 43, Rarity.UNCOMMON, mage.cards.b.Browse.class)); + cards.add(new SetCardInfo("Burnout", 121, Rarity.UNCOMMON, mage.cards.b.Burnout.class)); + cards.add(new SetCardInfo("Carapace", 155, Rarity.COMMON, mage.cards.c.Carapace.class)); + cards.add(new SetCardInfo("Caribou Range", 8, Rarity.RARE, mage.cards.c.CaribouRange.class)); + cards.add(new SetCardInfo("Cloak of Confusion", 82, Rarity.COMMON, mage.cards.c.CloakOfConfusion.class)); + cards.add(new SetCardInfo("Clockwork Steed", 205, Rarity.UNCOMMON, mage.cards.c.ClockworkSteed.class)); + cards.add(new SetCardInfo("Combat Medic", 9, Rarity.COMMON, mage.cards.c.CombatMedic.class)); + cards.add(new SetCardInfo("Conquer", 122, Rarity.UNCOMMON, mage.cards.c.Conquer.class)); + cards.add(new SetCardInfo("Counterspell", 44, Rarity.UNCOMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Dance of the Dead", 83, Rarity.UNCOMMON, mage.cards.d.DanceOfTheDead.class)); + cards.add(new SetCardInfo("Dark Banishing", 84, Rarity.COMMON, mage.cards.d.DarkBanishing.class)); + cards.add(new SetCardInfo("Death Spark", 123, Rarity.COMMON, mage.cards.d.DeathSpark.class)); + cards.add(new SetCardInfo("Deep Spawn", 45, Rarity.RARE, mage.cards.d.DeepSpawn.class)); + cards.add(new SetCardInfo("Demonic Consultation", 85, Rarity.UNCOMMON, mage.cards.d.DemonicConsultation.class)); + cards.add(new SetCardInfo("Despotic Scepter", 206, Rarity.RARE, mage.cards.d.DespoticScepter.class)); + cards.add(new SetCardInfo("Diabolic Vision", 191, Rarity.UNCOMMON, mage.cards.d.DiabolicVision.class)); + cards.add(new SetCardInfo("Disenchant", 10, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Dreams of the Dead", 46, Rarity.RARE, mage.cards.d.DreamsOfTheDead.class)); + cards.add(new SetCardInfo("Drift of the Dead", 86, Rarity.COMMON, mage.cards.d.DriftOfTheDead.class)); + cards.add(new SetCardInfo("Dry Spell", 87, Rarity.COMMON, mage.cards.d.DrySpell.class)); + cards.add(new SetCardInfo("Dwarven Ruins", 227, Rarity.UNCOMMON, mage.cards.d.DwarvenRuins.class)); + cards.add(new SetCardInfo("Dystopia", 88, Rarity.RARE, mage.cards.d.Dystopia.class)); + cards.add(new SetCardInfo("Earthlink", 192, Rarity.RARE, mage.cards.e.Earthlink.class)); + cards.add(new SetCardInfo("Ebon Praetor", 89, Rarity.RARE, mage.cards.e.EbonPraetor.class)); + cards.add(new SetCardInfo("Ebon Stronghold", 228, Rarity.UNCOMMON, mage.cards.e.EbonStronghold.class)); + cards.add(new SetCardInfo("Elemental Augury", 193, Rarity.RARE, mage.cards.e.ElementalAugury.class)); + cards.add(new SetCardInfo("Elkin Bottle", 207, Rarity.RARE, mage.cards.e.ElkinBottle.class)); + cards.add(new SetCardInfo("Elven Lyre", 208, Rarity.COMMON, mage.cards.e.ElvenLyre.class)); + cards.add(new SetCardInfo("Elvish Farmer", 156, Rarity.RARE, mage.cards.e.ElvishFarmer.class)); + cards.add(new SetCardInfo("Elvish Hunter", 157, Rarity.COMMON, mage.cards.e.ElvishHunter.class)); + cards.add(new SetCardInfo("Elvish Ranger", 158, Rarity.COMMON, mage.cards.e.ElvishRanger.class)); + cards.add(new SetCardInfo("Elvish Spirit Guide", 159, Rarity.UNCOMMON, mage.cards.e.ElvishSpiritGuide.class)); + cards.add(new SetCardInfo("Energy Storm", 11, Rarity.RARE, mage.cards.e.EnergyStorm.class)); + cards.add(new SetCardInfo("Enervate", 47, Rarity.COMMON, mage.cards.e.Enervate.class)); + cards.add(new SetCardInfo("Errand of Duty", 12, Rarity.UNCOMMON, mage.cards.e.ErrandOfDuty.class)); + cards.add(new SetCardInfo("Errantry", 124, Rarity.COMMON, mage.cards.e.Errantry.class)); + cards.add(new SetCardInfo("Essence Filter", 160, Rarity.UNCOMMON, mage.cards.e.EssenceFilter.class)); + cards.add(new SetCardInfo("Essence Flare", 48, Rarity.COMMON, mage.cards.e.EssenceFlare.class)); + cards.add(new SetCardInfo("Farrel's Mantle", 13, Rarity.UNCOMMON, mage.cards.f.FarrelsMantle.class)); + cards.add(new SetCardInfo("Farrel's Zealot", 14, Rarity.UNCOMMON, mage.cards.f.FarrelsZealot.class)); + cards.add(new SetCardInfo("Feral Thallid", 161, Rarity.COMMON, mage.cards.f.FeralThallid.class)); + cards.add(new SetCardInfo("Fire Dragon", 125, Rarity.RARE, mage.cards.f.FireDragon.class)); + cards.add(new SetCardInfo("Flame Spirit", 126, Rarity.UNCOMMON, mage.cards.f.FlameSpirit.class)); + cards.add(new SetCardInfo("Folk of the Pines", 162, Rarity.COMMON, mage.cards.f.FolkOfThePines.class)); + cards.add(new SetCardInfo("Forbidden Lore", 163, Rarity.UNCOMMON, mage.cards.f.ForbiddenLore.class)); + cards.add(new SetCardInfo("Forgotten Lore", 164, Rarity.UNCOMMON, mage.cards.f.ForgottenLore.class)); + cards.add(new SetCardInfo("Foul Familiar", 90, Rarity.COMMON, mage.cards.f.FoulFamiliar.class)); + cards.add(new SetCardInfo("Fumarole", 194, Rarity.UNCOMMON, mage.cards.f.Fumarole.class)); + cards.add(new SetCardInfo("Funeral March", 91, Rarity.COMMON, mage.cards.f.FuneralMarch.class)); + cards.add(new SetCardInfo("Fungal Bloom", 165, Rarity.RARE, mage.cards.f.FungalBloom.class)); + cards.add(new SetCardInfo("Fyndhorn Pollen", 166, Rarity.RARE, mage.cards.f.FyndhornPollen.class)); + cards.add(new SetCardInfo("Gangrenous Zombies", 92, Rarity.COMMON, mage.cards.g.GangrenousZombies.class)); + cards.add(new SetCardInfo("Giant Growth", 167, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); + cards.add(new SetCardInfo("Giant Trap Door Spider", 195, Rarity.UNCOMMON, mage.cards.g.GiantTrapDoorSpider.class)); + cards.add(new SetCardInfo("Glacial Chasm", 229, Rarity.RARE, mage.cards.g.GlacialChasm.class)); + cards.add(new SetCardInfo("Glacial Crevasses", 127, Rarity.RARE, mage.cards.g.GlacialCrevasses.class)); + cards.add(new SetCardInfo("Gorilla Shaman", 129, Rarity.UNCOMMON, mage.cards.g.GorillaShaman.class)); + cards.add(new SetCardInfo("Grandmother Sengir", 93, Rarity.RARE, mage.cards.g.GrandmotherSengir.class)); + cards.add(new SetCardInfo("Havenwood Battleground", 230, Rarity.UNCOMMON, mage.cards.h.HavenwoodBattleground.class)); + cards.add(new SetCardInfo("Heart of Yavimaya", 231, Rarity.RARE, mage.cards.h.HeartOfYavimaya.class)); + cards.add(new SetCardInfo("Helm of Obedience", 210, Rarity.RARE, mage.cards.h.HelmOfObedience.class)); + cards.add(new SetCardInfo("Icatian Javelineers", 15, Rarity.COMMON, mage.cards.i.IcatianJavelineers.class)); + cards.add(new SetCardInfo("Icatian Phalanx", 16, Rarity.COMMON, mage.cards.i.IcatianPhalanx.class)); + cards.add(new SetCardInfo("Icatian Scout", 17, Rarity.COMMON, mage.cards.i.IcatianScout.class)); + cards.add(new SetCardInfo("Ice Floe", 232, Rarity.UNCOMMON, mage.cards.i.IceFloe.class)); + cards.add(new SetCardInfo("Iceberg", 49, Rarity.UNCOMMON, mage.cards.i.Iceberg.class)); + cards.add(new SetCardInfo("Icequake", 94, Rarity.COMMON, mage.cards.i.Icequake.class)); + cards.add(new SetCardInfo("Icy Prison", 50, Rarity.COMMON, mage.cards.i.IcyPrison.class)); + cards.add(new SetCardInfo("Ihsan's Shade", 95, Rarity.RARE, mage.cards.i.IhsansShade.class)); + cards.add(new SetCardInfo("Imperial Recruiter", 130, Rarity.RARE, mage.cards.i.ImperialRecruiter.class)); + cards.add(new SetCardInfo("Imperial Seal", 96, Rarity.RARE, mage.cards.i.ImperialSeal.class)); + cards.add(new SetCardInfo("Incinerate", 131, Rarity.COMMON, mage.cards.i.Incinerate.class)); + cards.add(new SetCardInfo("Infernal Darkness", 97, Rarity.RARE, mage.cards.i.InfernalDarkness.class)); + cards.add(new SetCardInfo("Inheritance", 18, Rarity.UNCOMMON, mage.cards.i.Inheritance.class)); + cards.add(new SetCardInfo("Ironclaw Orcs", 132, Rarity.COMMON, mage.cards.i.IronclawOrcs.class)); + cards.add(new SetCardInfo("Ivory Gargoyle", 19, Rarity.RARE, mage.cards.i.IvoryGargoyle.class)); + cards.add(new SetCardInfo("Jester's Mask", 211, Rarity.RARE, mage.cards.j.JestersMask.class)); + cards.add(new SetCardInfo("Jeweled Amulet", 212, Rarity.UNCOMMON, mage.cards.j.JeweledAmulet.class)); + cards.add(new SetCardInfo("Johtull Wurm", 168, Rarity.UNCOMMON, mage.cards.j.JohtullWurm.class)); + cards.add(new SetCardInfo("Joven's Ferrets", 169, Rarity.UNCOMMON, mage.cards.j.JovensFerrets.class)); + cards.add(new SetCardInfo("Juniper Order Advocate", 20, Rarity.UNCOMMON, mage.cards.j.JuniperOrderAdvocate.class)); + cards.add(new SetCardInfo("Karplusan Giant", 133, Rarity.UNCOMMON, mage.cards.k.KarplusanGiant.class)); + cards.add(new SetCardInfo("Kaysa", 170, Rarity.RARE, mage.cards.k.Kaysa.class)); + cards.add(new SetCardInfo("Kjeldoran Dead", 98, Rarity.COMMON, mage.cards.k.KjeldoranDead.class)); + cards.add(new SetCardInfo("Kjeldoran Home Guard", 22, Rarity.UNCOMMON, mage.cards.k.KjeldoranHomeGuard.class)); + cards.add(new SetCardInfo("Kjeldoran Outpost", 233, Rarity.RARE, mage.cards.k.KjeldoranOutpost.class)); + cards.add(new SetCardInfo("Kjeldoran Skycaptain", 23, Rarity.COMMON, mage.cards.k.KjeldoranSkycaptain.class)); + cards.add(new SetCardInfo("Knight of Stromgald", 99, Rarity.UNCOMMON, mage.cards.k.KnightOfStromgald.class)); + cards.add(new SetCardInfo("Krovikan Fetish", 100, Rarity.COMMON, mage.cards.k.KrovikanFetish.class)); + cards.add(new SetCardInfo("Krovikan Horror", 101, Rarity.RARE, mage.cards.k.KrovikanHorror.class)); + cards.add(new SetCardInfo("Krovikan Sorcerer", 51, Rarity.COMMON, mage.cards.k.KrovikanSorcerer.class)); + cards.add(new SetCardInfo("Krovikan Vampire", 102, Rarity.UNCOMMON, mage.cards.k.KrovikanVampire.class)); + cards.add(new SetCardInfo("Lat-Nam's Legacy", 52, Rarity.COMMON, mage.cards.l.LatNamsLegacy.class)); + cards.add(new SetCardInfo("Leaping Lizard", 171, Rarity.COMMON, mage.cards.l.LeapingLizard.class)); + cards.add(new SetCardInfo("Lim-Dul's High Guard", 103, Rarity.UNCOMMON, mage.cards.l.LimDulsHighGuard.class)); + cards.add(new SetCardInfo("Lodestone Bauble", 213, Rarity.RARE, mage.cards.l.LodestoneBauble.class)); + cards.add(new SetCardInfo("Lost Order of Jarkeld", 24, Rarity.RARE, mage.cards.l.LostOrderOfJarkeld.class)); + cards.add(new SetCardInfo("Magus of the Unseen", 53, Rarity.RARE, mage.cards.m.MagusOfTheUnseen.class)); + cards.add(new SetCardInfo("Mana Crypt", 214, Rarity.RARE, mage.cards.m.ManaCrypt.class)); + cards.add(new SetCardInfo("Marjhan", 54, Rarity.RARE, mage.cards.m.Marjhan.class)); + cards.add(new SetCardInfo("Mesmeric Trance", 55, Rarity.RARE, mage.cards.m.MesmericTrance.class)); + cards.add(new SetCardInfo("Meteor Shower", 135, Rarity.COMMON, mage.cards.m.MeteorShower.class)); + cards.add(new SetCardInfo("Minion of Leshrac", 104, Rarity.RARE, mage.cards.m.MinionOfLeshrac.class)); + cards.add(new SetCardInfo("Mishra's Groundbreaker", 215, Rarity.UNCOMMON, mage.cards.m.MishrasGroundbreaker.class)); + cards.add(new SetCardInfo("Misinformation", 105, Rarity.UNCOMMON, mage.cards.m.Misinformation.class)); + cards.add(new SetCardInfo("Mudslide", 136, Rarity.RARE, mage.cards.m.Mudslide.class)); + cards.add(new SetCardInfo("Musician", 56, Rarity.RARE, mage.cards.m.Musician.class)); + cards.add(new SetCardInfo("Narwhal", 57, Rarity.UNCOMMON, mage.cards.n.Narwhal.class)); + cards.add(new SetCardInfo("Nature's Blessing", 196, Rarity.UNCOMMON, mage.cards.n.NaturesBlessing.class)); + cards.add(new SetCardInfo("Nature's Wrath", 172, Rarity.RARE, mage.cards.n.NaturesWrath.class)); + cards.add(new SetCardInfo("Necrite", 106, Rarity.COMMON, mage.cards.n.Necrite.class)); + cards.add(new SetCardInfo("Necropotence", 107, Rarity.RARE, mage.cards.n.Necropotence.class)); + cards.add(new SetCardInfo("Night Soil", 173, Rarity.UNCOMMON, mage.cards.n.NightSoil.class)); + cards.add(new SetCardInfo("Orc General", 137, Rarity.UNCOMMON, mage.cards.o.OrcGeneral.class)); + cards.add(new SetCardInfo("Orcish Cannoneers", 138, Rarity.UNCOMMON, mage.cards.o.OrcishCannoneers.class)); + cards.add(new SetCardInfo("Orcish Captain", 139, Rarity.UNCOMMON, mage.cards.o.OrcishCaptain.class)); + cards.add(new SetCardInfo("Orcish Lumberjack", 142, Rarity.COMMON, mage.cards.o.OrcishLumberjack.class)); + cards.add(new SetCardInfo("Orcish Squatters", 143, Rarity.RARE, mage.cards.o.OrcishSquatters.class)); + cards.add(new SetCardInfo("Orcish Veteran", 144, Rarity.COMMON, mage.cards.o.OrcishVeteran.class)); + cards.add(new SetCardInfo("Order of the Sacred Torch", 25, Rarity.RARE, mage.cards.o.OrderOfTheSacredTorch.class)); + cards.add(new SetCardInfo("Order of the White Shield", 26, Rarity.UNCOMMON, mage.cards.o.OrderOfTheWhiteShield.class)); + cards.add(new SetCardInfo("Panic", 145, Rarity.COMMON, mage.cards.p.Panic.class)); + cards.add(new SetCardInfo("Personal Tutor", 58, Rarity.UNCOMMON, mage.cards.p.PersonalTutor.class)); + cards.add(new SetCardInfo("Phantasmal Fiend", 108, Rarity.COMMON, mage.cards.p.PhantasmalFiend.class)); + cards.add(new SetCardInfo("Phantasmal Mount", 59, Rarity.COMMON, mage.cards.p.PhantasmalMount.class)); + cards.add(new SetCardInfo("Phyrexian Devourer", 216, Rarity.UNCOMMON, mage.cards.p.PhyrexianDevourer.class)); + cards.add(new SetCardInfo("Pillage", 146, Rarity.UNCOMMON, mage.cards.p.Pillage.class)); + cards.add(new SetCardInfo("Portent", 60, Rarity.COMMON, mage.cards.p.Portent.class)); + cards.add(new SetCardInfo("Pyrokinesis", 147, Rarity.RARE, mage.cards.p.Pyrokinesis.class)); + cards.add(new SetCardInfo("Ravages of War", 27, Rarity.RARE, mage.cards.r.RavagesOfWar.class)); + cards.add(new SetCardInfo("Ray of Command", 61, Rarity.UNCOMMON, mage.cards.r.RayOfCommand.class)); + cards.add(new SetCardInfo("Red Cliffs Armada", 62, Rarity.COMMON, mage.cards.r.RedCliffsArmada.class)); + cards.add(new SetCardInfo("Reinforcements", 28, Rarity.COMMON, mage.cards.r.Reinforcements.class)); + cards.add(new SetCardInfo("Reprisal", 29, Rarity.COMMON, mage.cards.r.Reprisal.class)); + cards.add(new SetCardInfo("Retribution", 148, Rarity.UNCOMMON, mage.cards.r.Retribution.class)); + cards.add(new SetCardInfo("Righteous Fury", 30, Rarity.RARE, mage.cards.r.RighteousFury.class)); + cards.add(new SetCardInfo("Ritual of Subdual", 174, Rarity.RARE, mage.cards.r.RitualOfSubdual.class)); + cards.add(new SetCardInfo("Ritual of the Machine", 109, Rarity.RARE, mage.cards.r.RitualOfTheMachine.class)); + cards.add(new SetCardInfo("Roterothopter", 218, Rarity.COMMON, mage.cards.r.Roterothopter.class)); + cards.add(new SetCardInfo("Rogue Skycaptain", 149, Rarity.RARE, mage.cards.r.RogueSkycaptain.class)); + cards.add(new SetCardInfo("Royal Decree", 31, Rarity.RARE, mage.cards.r.RoyalDecree.class)); + cards.add(new SetCardInfo("Royal Trooper", 32, Rarity.COMMON, mage.cards.r.RoyalTrooper.class)); + cards.add(new SetCardInfo("Ruins of Trokair", 234, Rarity.UNCOMMON, mage.cards.r.RuinsOfTrokair.class)); + cards.add(new SetCardInfo("Savannah", 235, Rarity.RARE, mage.cards.s.Savannah.class)); + cards.add(new SetCardInfo("Screeching Drake", 63, Rarity.COMMON, mage.cards.s.ScreechingDrake.class)); + cards.add(new SetCardInfo("Sea Drake", 64, Rarity.RARE, mage.cards.s.SeaDrake.class)); + cards.add(new SetCardInfo("Sea Spirit", 65, Rarity.UNCOMMON, mage.cards.s.SeaSpirit.class)); + cards.add(new SetCardInfo("Shield Bearer", 35, Rarity.COMMON, mage.cards.s.ShieldBearer.class)); + cards.add(new SetCardInfo("Shrink", 175, Rarity.COMMON, mage.cards.s.Shrink.class)); + cards.add(new SetCardInfo("Shyft", 66, Rarity.COMMON, mage.cards.s.Shyft.class)); + cards.add(new SetCardInfo("Sibilant Spirit", 67, Rarity.RARE, mage.cards.s.SibilantSpirit.class)); + cards.add(new SetCardInfo("Skeleton Ship", 197, Rarity.RARE, mage.cards.s.SkeletonShip.class)); + cards.add(new SetCardInfo("Skull Catapult", 219, Rarity.UNCOMMON, mage.cards.s.SkullCatapult.class)); + cards.add(new SetCardInfo("Snow Fortress", 220, Rarity.UNCOMMON, mage.cards.s.SnowFortress.class)); + cards.add(new SetCardInfo("Snow-Covered Forest", 245, Rarity.LAND, mage.cards.s.SnowCoveredForest.class)); + cards.add(new SetCardInfo("Snow-Covered Island", 242, Rarity.LAND, mage.cards.s.SnowCoveredIsland.class)); + cards.add(new SetCardInfo("Snow-Covered Mountain", 244, Rarity.LAND, mage.cards.s.SnowCoveredMountain.class)); + cards.add(new SetCardInfo("Snow-Covered Plains", 241, Rarity.LAND, mage.cards.s.SnowCoveredPlains.class)); + cards.add(new SetCardInfo("Snow-Covered Swamp", 243, Rarity.LAND, mage.cards.s.SnowCoveredSwamp.class)); + cards.add(new SetCardInfo("Soldevi Digger", 221, Rarity.UNCOMMON, mage.cards.s.SoldeviDigger.class)); + cards.add(new SetCardInfo("Soldevi Excavations", 236, Rarity.RARE, mage.cards.s.SoldeviExcavations.class)); + cards.add(new SetCardInfo("Soldevi Simulacrum", 222, Rarity.UNCOMMON, mage.cards.s.SoldeviSimulacrum.class)); + cards.add(new SetCardInfo("Songs of the Damned", 110, Rarity.COMMON, mage.cards.s.SongsOfTheDamned.class)); + cards.add(new SetCardInfo("Soul Exchange", 111, Rarity.UNCOMMON, mage.cards.s.SoulExchange.class)); + cards.add(new SetCardInfo("Soul Kiss", 112, Rarity.UNCOMMON, mage.cards.s.SoulKiss.class)); + cards.add(new SetCardInfo("Spore Cloud", 176, Rarity.UNCOMMON, mage.cards.s.SporeCloud.class)); + cards.add(new SetCardInfo("Spore Flower", 177, Rarity.UNCOMMON, mage.cards.s.SporeFlower.class)); + cards.add(new SetCardInfo("Stampede", 178, Rarity.UNCOMMON, mage.cards.s.Stampede.class)); + cards.add(new SetCardInfo("Stone Spirit", 150, Rarity.UNCOMMON, mage.cards.s.StoneSpirit.class)); + cards.add(new SetCardInfo("Stonehands", 151, Rarity.COMMON, mage.cards.s.Stonehands.class)); + cards.add(new SetCardInfo("Storm Spirit", 198, Rarity.RARE, mage.cards.s.StormSpirit.class)); + cards.add(new SetCardInfo("Stromgald Cabal", 113, Rarity.RARE, mage.cards.s.StromgaldCabal.class)); + cards.add(new SetCardInfo("Stunted Growth", 179, Rarity.RARE, mage.cards.s.StuntedGrowth.class)); + cards.add(new SetCardInfo("Sustaining Spirit", 36, Rarity.RARE, mage.cards.s.SustainingSpirit.class)); + cards.add(new SetCardInfo("Svyelunite Temple", 237, Rarity.UNCOMMON, mage.cards.s.SvyeluniteTemple.class)); + cards.add(new SetCardInfo("Swords to Plowshares", 37, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("Taiga", 238, Rarity.RARE, mage.cards.t.Taiga.class)); + cards.add(new SetCardInfo("Temporal Manipulation", 69, Rarity.RARE, mage.cards.t.TemporalManipulation.class)); + cards.add(new SetCardInfo("Thallid Devourer", 181, Rarity.COMMON, mage.cards.t.ThallidDevourer.class)); + cards.add(new SetCardInfo("Thallid", 180, Rarity.COMMON, mage.cards.t.Thallid.class)); + cards.add(new SetCardInfo("Thelonite Druid", 182, Rarity.RARE, mage.cards.t.TheloniteDruid.class)); + cards.add(new SetCardInfo("Thermokarst", 183, Rarity.COMMON, mage.cards.t.Thermokarst.class)); + cards.add(new SetCardInfo("Thought Lash", 70, Rarity.RARE, mage.cards.t.ThoughtLash.class)); + cards.add(new SetCardInfo("Thunder Wall", 71, Rarity.UNCOMMON, mage.cards.t.ThunderWall.class)); + cards.add(new SetCardInfo("Time Bomb", 223, Rarity.RARE, mage.cards.t.TimeBomb.class)); + cards.add(new SetCardInfo("Tinder Wall", 184, Rarity.COMMON, mage.cards.t.TinderWall.class)); + cards.add(new SetCardInfo("Tundra", 239, Rarity.RARE, mage.cards.t.Tundra.class)); + cards.add(new SetCardInfo("Underground Sea", 240, Rarity.RARE, mage.cards.u.UndergroundSea.class)); + cards.add(new SetCardInfo("Varchild's Crusader", 152, Rarity.COMMON, mage.cards.v.VarchildsCrusader.class)); + cards.add(new SetCardInfo("Viscerid Armor", 72, Rarity.COMMON, mage.cards.v.VisceridArmor.class)); + cards.add(new SetCardInfo("Viscerid Drone", 73, Rarity.UNCOMMON, mage.cards.v.VisceridDrone.class)); + cards.add(new SetCardInfo("Wall of Kelp", 74, Rarity.COMMON, mage.cards.w.WallOfKelp.class)); + cards.add(new SetCardInfo("Warning", 38, Rarity.COMMON, mage.cards.w.Warning.class)); + cards.add(new SetCardInfo("Whirling Catapult", 224, Rarity.UNCOMMON, mage.cards.w.WhirlingCatapult.class)); + cards.add(new SetCardInfo("Whiteout", 185, Rarity.COMMON, mage.cards.w.Whiteout.class)); + cards.add(new SetCardInfo("Wiitigo", 186, Rarity.RARE, mage.cards.w.Wiitigo.class)); + cards.add(new SetCardInfo("Wind Spirit", 75, Rarity.UNCOMMON, mage.cards.w.WindSpirit.class)); + cards.add(new SetCardInfo("Wings of Aesthir", 199, Rarity.UNCOMMON, mage.cards.w.WingsOfAesthir.class)); + cards.add(new SetCardInfo("Winter's Night", 200, Rarity.RARE, mage.cards.w.WintersNight.class)); + cards.add(new SetCardInfo("Withering Wisps", 114, Rarity.UNCOMMON, mage.cards.w.WitheringWisps.class)); + cards.add(new SetCardInfo("Wolf Pack", 187, Rarity.RARE, mage.cards.w.WolfPack.class)); + cards.add(new SetCardInfo("Woolly Mammoths", 188, Rarity.COMMON, mage.cards.w.WoollyMammoths.class)); + cards.add(new SetCardInfo("Woolly Spider", 189, Rarity.UNCOMMON, mage.cards.w.WoollySpider.class)); + cards.add(new SetCardInfo("Yavimaya Ancients", 190, Rarity.UNCOMMON, mage.cards.y.YavimayaAncients.class)); + cards.add(new SetCardInfo("Zuran Spellcaster", 76, Rarity.COMMON, mage.cards.z.ZuranSpellcaster.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/MastersEditionIII.java b/Mage.Sets/src/mage/sets/MastersEditionIII.java index f3ebcd7cd7..45f3a9e970 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIII.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIII.java @@ -1,252 +1,252 @@ - -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * @author LevelX2 - */ -public final class MastersEditionIII extends ExpansionSet { - - private static final MastersEditionIII instance = new MastersEditionIII(); - - public static MastersEditionIII getInstance() { - return instance; - } - - private MastersEditionIII() { - super("Masters Edition III", "ME3", ExpansionSet.buildDate(2009, 9, 7), SetType.MAGIC_ONLINE); - this.hasBasicLands = true; - this.hasBoosters = true; - this.numBoosterLands = 1; - this.numBoosterCommon = 10; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - - cards.add(new SetCardInfo("Active Volcano", 85, Rarity.UNCOMMON, mage.cards.a.ActiveVolcano.class)); - cards.add(new SetCardInfo("Akron Legionnaire", 1, Rarity.RARE, mage.cards.a.AkronLegionnaire.class)); - cards.add(new SetCardInfo("Alabaster Potion", 2, Rarity.UNCOMMON, mage.cards.a.AlabasterPotion.class)); - cards.add(new SetCardInfo("All Hallow's Eve", 57, Rarity.RARE, mage.cards.a.AllHallowsEve.class)); - cards.add(new SetCardInfo("Amrou Kithkin", 3, Rarity.COMMON, mage.cards.a.AmrouKithkin.class)); - cards.add(new SetCardInfo("Anaba Ancestor", 86, Rarity.COMMON, mage.cards.a.AnabaAncestor.class)); - cards.add(new SetCardInfo("Anaba Spirit Crafter", 87, Rarity.COMMON, mage.cards.a.AnabaSpiritCrafter.class)); - cards.add(new SetCardInfo("Angus Mackenzie", 141, Rarity.RARE, mage.cards.a.AngusMackenzie.class)); - cards.add(new SetCardInfo("Arboria", 113, Rarity.RARE, mage.cards.a.Arboria.class)); - cards.add(new SetCardInfo("Arcades Sabboth", 142, Rarity.RARE, mage.cards.a.ArcadesSabboth.class)); - cards.add(new SetCardInfo("Arena of the Ancients", 188, Rarity.RARE, mage.cards.a.ArenaOfTheAncients.class)); - cards.add(new SetCardInfo("Ashes to Ashes", 58, Rarity.UNCOMMON, mage.cards.a.AshesToAshes.class)); - cards.add(new SetCardInfo("Astrolabe", 189, Rarity.COMMON, mage.cards.a.Astrolabe.class)); - cards.add(new SetCardInfo("Axelrod Gunnarson", 143, Rarity.UNCOMMON, mage.cards.a.AxelrodGunnarson.class)); - cards.add(new SetCardInfo("Banshee", 59, Rarity.UNCOMMON, mage.cards.b.Banshee.class)); - cards.add(new SetCardInfo("Barktooth Warbeard", 144, Rarity.COMMON, mage.cards.b.BarktoothWarbeard.class)); - cards.add(new SetCardInfo("Barl's Cage", 190, Rarity.RARE, mage.cards.b.BarlsCage.class)); - cards.add(new SetCardInfo("Bartel Runeaxe", 145, Rarity.UNCOMMON, mage.cards.b.BartelRuneaxe.class)); - cards.add(new SetCardInfo("Bayou", 204, Rarity.RARE, mage.cards.b.Bayou.class)); - cards.add(new SetCardInfo("Bazaar of Baghdad", 205, Rarity.RARE, mage.cards.b.BazaarOfBaghdad.class)); - cards.add(new SetCardInfo("Black Vise", 191, Rarity.RARE, mage.cards.b.BlackVise.class)); - cards.add(new SetCardInfo("Blood Lust", 88, Rarity.COMMON, mage.cards.b.BloodLust.class)); - cards.add(new SetCardInfo("Bone Flute", 192, Rarity.COMMON, mage.cards.b.BoneFlute.class)); - cards.add(new SetCardInfo("Boomerang", 30, Rarity.COMMON, mage.cards.b.Boomerang.class)); - cards.add(new SetCardInfo("Boris Devilboon", 146, Rarity.UNCOMMON, mage.cards.b.BorisDevilboon.class)); - cards.add(new SetCardInfo("Borrowing 100,000 Arrows", 31, Rarity.UNCOMMON, mage.cards.b.Borrowing100000Arrows.class)); - cards.add(new SetCardInfo("Brilliant Plan", 32, Rarity.COMMON, mage.cards.b.BrilliantPlan.class)); - cards.add(new SetCardInfo("Burning of Xinye", 89, Rarity.RARE, mage.cards.b.BurningOfXinye.class)); - cards.add(new SetCardInfo("Call to Arms", 4, Rarity.UNCOMMON, mage.cards.c.CallToArms.class)); - cards.add(new SetCardInfo("Capture of Jingzhou", 33, Rarity.RARE, mage.cards.c.CaptureOfJingzhou.class)); - cards.add(new SetCardInfo("Carrion Ants", 60, Rarity.UNCOMMON, mage.cards.c.CarrionAnts.class)); - cards.add(new SetCardInfo("Chain Lightning", 90, Rarity.COMMON, mage.cards.c.ChainLightning.class)); - cards.add(new SetCardInfo("Chromium", 147, Rarity.RARE, mage.cards.c.Chromium.class)); - cards.add(new SetCardInfo("Cinder Storm", 91, Rarity.UNCOMMON, mage.cards.c.CinderStorm.class)); - cards.add(new SetCardInfo("City of Shadows", 206, Rarity.RARE, mage.cards.c.CityOfShadows.class)); - cards.add(new SetCardInfo("Cleanse", 5, Rarity.RARE, mage.cards.c.Cleanse.class)); - cards.add(new SetCardInfo("Coal Golem", 193, Rarity.COMMON, mage.cards.c.CoalGolem.class)); - cards.add(new SetCardInfo("Concordant Crossroads", 114, Rarity.RARE, mage.cards.c.ConcordantCrossroads.class)); - cards.add(new SetCardInfo("Corrupt Eunuchs", 92, Rarity.UNCOMMON, mage.cards.c.CorruptEunuchs.class)); - cards.add(new SetCardInfo("Cosmic Horror", 61, Rarity.RARE, mage.cards.c.CosmicHorror.class)); - cards.add(new SetCardInfo("Crimson Kobolds", 93, Rarity.COMMON, mage.cards.c.CrimsonKobolds.class)); - cards.add(new SetCardInfo("Crimson Manticore", 94, Rarity.UNCOMMON, mage.cards.c.CrimsonManticore.class)); - cards.add(new SetCardInfo("Dance of Many", 34, Rarity.UNCOMMON, mage.cards.d.DanceOfMany.class)); - cards.add(new SetCardInfo("D'Avenant Archer", 6, Rarity.COMMON, mage.cards.d.DAvenantArcher.class)); - cards.add(new SetCardInfo("Demonic Torment", 62, Rarity.COMMON, mage.cards.d.DemonicTorment.class)); - cards.add(new SetCardInfo("Desert Twister", 115, Rarity.UNCOMMON, mage.cards.d.DesertTwister.class)); - cards.add(new SetCardInfo("Desperate Charge", 63, Rarity.COMMON, mage.cards.d.DesperateCharge.class)); - cards.add(new SetCardInfo("Didgeridoo", 194, Rarity.UNCOMMON, mage.cards.d.Didgeridoo.class)); - cards.add(new SetCardInfo("Disenchant", 7, Rarity.COMMON, mage.cards.d.Disenchant.class)); - cards.add(new SetCardInfo("Disharmony", 95, Rarity.UNCOMMON, mage.cards.d.Disharmony.class)); - cards.add(new SetCardInfo("Divine Intervention", 8, Rarity.RARE, mage.cards.d.DivineIntervention.class)); - cards.add(new SetCardInfo("Dong Zhou, the Tyrant", 96, Rarity.RARE, mage.cards.d.DongZhouTheTyrant.class)); - cards.add(new SetCardInfo("Eightfold Maze", 9, Rarity.UNCOMMON, mage.cards.e.EightfoldMaze.class)); - cards.add(new SetCardInfo("Elves of Deep Shadow", 116, Rarity.COMMON, mage.cards.e.ElvesOfDeepShadow.class)); - cards.add(new SetCardInfo("Evil Presence", 64, Rarity.COMMON, mage.cards.e.EvilPresence.class)); - cards.add(new SetCardInfo("Exorcist", 10, Rarity.UNCOMMON, mage.cards.e.Exorcist.class)); - cards.add(new SetCardInfo("Faerie Noble", 117, Rarity.UNCOMMON, mage.cards.f.FaerieNoble.class)); - cards.add(new SetCardInfo("False Defeat", 11, Rarity.UNCOMMON, mage.cards.f.FalseDefeat.class)); - cards.add(new SetCardInfo("Famine", 65, Rarity.UNCOMMON, mage.cards.f.Famine.class)); - cards.add(new SetCardInfo("Fellwar Stone", 195, Rarity.COMMON, mage.cards.f.FellwarStone.class)); - cards.add(new SetCardInfo("Fevered Strength", 66, Rarity.COMMON, mage.cards.f.FeveredStrength.class)); - cards.add(new SetCardInfo("Fire Ambush", 97, Rarity.COMMON, mage.cards.f.FireAmbush.class)); - cards.add(new SetCardInfo("Fire Drake", 98, Rarity.COMMON, mage.cards.f.FireDrake.class)); - cards.add(new SetCardInfo("Fire Sprites", 118, Rarity.COMMON, mage.cards.f.FireSprites.class)); - cards.add(new SetCardInfo("Flash Flood", 35, Rarity.UNCOMMON, mage.cards.f.FlashFlood.class)); - cards.add(new SetCardInfo("Forced Retreat", 37, Rarity.COMMON, mage.cards.f.ForcedRetreat.class)); - cards.add(new SetCardInfo("Force Spike", 36, Rarity.COMMON, mage.cards.f.ForceSpike.class)); - cards.add(new SetCardInfo("Forest", 228, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 229, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 230, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forked Lightning", 100, Rarity.UNCOMMON, mage.cards.f.ForkedLightning.class)); - cards.add(new SetCardInfo("Freyalise's Winds", 119, Rarity.RARE, mage.cards.f.FreyalisesWinds.class)); - cards.add(new SetCardInfo("Frost Giant", 101, Rarity.UNCOMMON, mage.cards.f.FrostGiant.class)); - cards.add(new SetCardInfo("Gabriel Angelfire", 148, Rarity.RARE, mage.cards.g.GabrielAngelfire.class)); - cards.add(new SetCardInfo("Gaea's Touch", 120, Rarity.UNCOMMON, mage.cards.g.GaeasTouch.class)); - cards.add(new SetCardInfo("Gauntlets of Chaos", 196, Rarity.RARE, mage.cards.g.GauntletsOfChaos.class)); - cards.add(new SetCardInfo("Ghostly Visit", 67, Rarity.COMMON, mage.cards.g.GhostlyVisit.class)); - cards.add(new SetCardInfo("Ghosts of the Damned", 68, Rarity.COMMON, mage.cards.g.GhostsOfTheDamned.class)); - cards.add(new SetCardInfo("Giant Growth", 121, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); - cards.add(new SetCardInfo("Grim Tutor", 69, Rarity.RARE, mage.cards.g.GrimTutor.class)); - cards.add(new SetCardInfo("Guan Yu's 1,000-Li March", 13, Rarity.RARE, mage.cards.g.GuanYus1000LiMarch.class)); - cards.add(new SetCardInfo("Guan Yu, Sainted Warrior", 12, Rarity.UNCOMMON, mage.cards.g.GuanYuSaintedWarrior.class)); - cards.add(new SetCardInfo("Gwendlyn Di Corci", 149, Rarity.RARE, mage.cards.g.GwendlynDiCorci.class)); - cards.add(new SetCardInfo("Halfdane", 150, Rarity.RARE, mage.cards.h.Halfdane.class)); - cards.add(new SetCardInfo("Hammerheim", 207, Rarity.UNCOMMON, mage.cards.h.Hammerheim.class)); - cards.add(new SetCardInfo("Hazezon Tamar", 151, Rarity.RARE, mage.cards.h.HazezonTamar.class)); - cards.add(new SetCardInfo("Heal", 14, Rarity.COMMON, mage.cards.h.Heal.class)); - cards.add(new SetCardInfo("Heavy Fog", 122, Rarity.COMMON, mage.cards.h.HeavyFog.class)); - cards.add(new SetCardInfo("Hellfire", 70, Rarity.RARE, mage.cards.h.Hellfire.class)); - cards.add(new SetCardInfo("Hua Tuo, Honored Physician", 123, Rarity.RARE, mage.cards.h.HuaTuoHonoredPhysician.class)); - cards.add(new SetCardInfo("Hunding Gjornersen", 152, Rarity.UNCOMMON, mage.cards.h.HundingGjornersen.class)); - cards.add(new SetCardInfo("Hunting Cheetah", 124, Rarity.COMMON, mage.cards.h.HuntingCheetah.class)); - cards.add(new SetCardInfo("Hurloon Minotaur", 102, Rarity.COMMON, mage.cards.h.HurloonMinotaur.class)); - cards.add(new SetCardInfo("Immolation", 103, Rarity.COMMON, mage.cards.i.Immolation.class)); - cards.add(new SetCardInfo("Infuse", 38, Rarity.COMMON, mage.cards.i.Infuse.class)); - cards.add(new SetCardInfo("Island", 219, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 220, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 221, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ivory Guardians", 15, Rarity.UNCOMMON, mage.cards.i.IvoryGuardians.class)); - cards.add(new SetCardInfo("Jedit Ojanen", 153, Rarity.COMMON, mage.cards.j.JeditOjanen.class)); - cards.add(new SetCardInfo("Jerrard of the Closed Fist", 154, Rarity.COMMON, mage.cards.j.JerrardOfTheClosedFist.class)); - cards.add(new SetCardInfo("Jungle Lion", 125, Rarity.COMMON, mage.cards.j.JungleLion.class)); - cards.add(new SetCardInfo("Karakas", 208, Rarity.RARE, mage.cards.k.Karakas.class)); - cards.add(new SetCardInfo("Kei Takahashi", 155, Rarity.UNCOMMON, mage.cards.k.KeiTakahashi.class)); - cards.add(new SetCardInfo("Killer Bees", 126, Rarity.UNCOMMON, mage.cards.k.KillerBees.class)); - cards.add(new SetCardInfo("Kjeldoran Frostbeast", 156, Rarity.UNCOMMON, mage.cards.k.KjeldoranFrostbeast.class)); - cards.add(new SetCardInfo("Kobold Drill Sergeant", 104, Rarity.UNCOMMON, mage.cards.k.KoboldDrillSergeant.class)); - cards.add(new SetCardInfo("Kobold Overlord", 105, Rarity.UNCOMMON, mage.cards.k.KoboldOverlord.class)); - cards.add(new SetCardInfo("Kobolds of Kher Keep", 107, Rarity.COMMON, mage.cards.k.KoboldsOfKherKeep.class)); - cards.add(new SetCardInfo("Kobold Taskmaster", 106, Rarity.COMMON, mage.cards.k.KoboldTaskmaster.class)); - cards.add(new SetCardInfo("Kongming, 'Sleeping Dragon'", 16, Rarity.RARE, mage.cards.k.KongmingSleepingDragon.class)); - cards.add(new SetCardInfo("Labyrinth Minotaur", 39, Rarity.COMMON, mage.cards.l.LabyrinthMinotaur.class)); - cards.add(new SetCardInfo("Lady Caleria", 157, Rarity.UNCOMMON, mage.cards.l.LadyCaleria.class)); - cards.add(new SetCardInfo("Lady Evangela", 158, Rarity.UNCOMMON, mage.cards.l.LadyEvangela.class)); - cards.add(new SetCardInfo("Lady Orca", 159, Rarity.COMMON, mage.cards.l.LadyOrca.class)); - cards.add(new SetCardInfo("Land Equilibrium", 40, Rarity.RARE, mage.cards.l.LandEquilibrium.class)); - cards.add(new SetCardInfo("Land Tax", 17, Rarity.RARE, mage.cards.l.LandTax.class)); - cards.add(new SetCardInfo("Life Chisel", 199, Rarity.RARE, mage.cards.l.LifeChisel.class)); - cards.add(new SetCardInfo("Lightning Blow", 18, Rarity.COMMON, mage.cards.l.LightningBlow.class)); - cards.add(new SetCardInfo("Liu Bei, Lord of Shu", 19, Rarity.RARE, mage.cards.l.LiuBeiLordOfShu.class)); - cards.add(new SetCardInfo("Living Plane", 127, Rarity.RARE, mage.cards.l.LivingPlane.class)); - cards.add(new SetCardInfo("Livonya Silone", 160, Rarity.UNCOMMON, mage.cards.l.LivonyaSilone.class)); - cards.add(new SetCardInfo("Loyal Retainers", 20, Rarity.UNCOMMON, mage.cards.l.LoyalRetainers.class)); - cards.add(new SetCardInfo("Lu Bu, Master-at-Arms", 108, Rarity.RARE, mage.cards.l.LuBuMasterAtArms.class)); - cards.add(new SetCardInfo("Lu Meng, Wu General", 41, Rarity.UNCOMMON, mage.cards.l.LuMengWuGeneral.class)); - cards.add(new SetCardInfo("Lu Xun, Scholar General", 42, Rarity.UNCOMMON, mage.cards.l.LuXunScholarGeneral.class)); - cards.add(new SetCardInfo("Mana Drain", 43, Rarity.RARE, mage.cards.m.ManaDrain.class)); - cards.add(new SetCardInfo("Mana Vortex", 44, Rarity.RARE, mage.cards.m.ManaVortex.class)); - cards.add(new SetCardInfo("Marhault Elsdragon", 161, Rarity.UNCOMMON, mage.cards.m.MarhaultElsdragon.class)); - cards.add(new SetCardInfo("Meng Huo, Barbarian King", 128, Rarity.RARE, mage.cards.m.MengHuoBarbarianKing.class)); - cards.add(new SetCardInfo("Meng Huo's Horde", 129, Rarity.COMMON, mage.cards.m.MengHuosHorde.class)); - cards.add(new SetCardInfo("Mind Twist", 72, Rarity.RARE, mage.cards.m.MindTwist.class)); - cards.add(new SetCardInfo("Misfortune's Gain", 21, Rarity.COMMON, mage.cards.m.MisfortunesGain.class)); - cards.add(new SetCardInfo("Mountain", 225, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 226, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 227, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Nebuchadnezzar", 162, Rarity.UNCOMMON, mage.cards.n.Nebuchadnezzar.class)); - cards.add(new SetCardInfo("Nether Void", 73, Rarity.RARE, mage.cards.n.NetherVoid.class)); - cards.add(new SetCardInfo("Nicol Bolas", 163, Rarity.RARE, mage.cards.n.NicolBolas.class)); - cards.add(new SetCardInfo("Nova Pentacle", 200, Rarity.RARE, mage.cards.n.NovaPentacle.class)); - cards.add(new SetCardInfo("Old Man of the Sea", 45, Rarity.RARE, mage.cards.o.OldManOfTheSea.class)); - cards.add(new SetCardInfo("Palladia-Mors", 164, Rarity.RARE, mage.cards.p.PalladiaMors.class)); - cards.add(new SetCardInfo("Pavel Maliki", 165, Rarity.UNCOMMON, mage.cards.p.PavelMaliki.class)); - cards.add(new SetCardInfo("Peach Garden Oath", 22, Rarity.COMMON, mage.cards.p.PeachGardenOath.class)); - cards.add(new SetCardInfo("Plains", 216, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 217, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 218, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plateau", 209, Rarity.RARE, mage.cards.p.Plateau.class)); - cards.add(new SetCardInfo("Princess Lucrezia", 166, Rarity.UNCOMMON, mage.cards.p.PrincessLucrezia.class)); - cards.add(new SetCardInfo("Raging Minotaur", 109, Rarity.COMMON, mage.cards.r.RagingMinotaur.class)); - cards.add(new SetCardInfo("Ragnar", 167, Rarity.UNCOMMON, mage.cards.r.Ragnar.class)); - cards.add(new SetCardInfo("Ramirez DePietro", 168, Rarity.COMMON, mage.cards.r.RamirezDePietro.class)); - cards.add(new SetCardInfo("Ramses Overdark", 169, Rarity.UNCOMMON, mage.cards.r.RamsesOverdark.class)); - cards.add(new SetCardInfo("Rasputin Dreamweaver", 170, Rarity.RARE, mage.cards.r.RasputinDreamweaver.class)); - cards.add(new SetCardInfo("Recall", 46, Rarity.UNCOMMON, mage.cards.r.Recall.class)); - cards.add(new SetCardInfo("Reincarnation", 130, Rarity.UNCOMMON, mage.cards.r.Reincarnation.class)); - cards.add(new SetCardInfo("Remove Soul", 47, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); - cards.add(new SetCardInfo("Reset", 48, Rarity.RARE, mage.cards.r.Reset.class)); - cards.add(new SetCardInfo("Reveka, Wizard Savant", 49, Rarity.UNCOMMON, mage.cards.r.RevekaWizardSavant.class)); - cards.add(new SetCardInfo("Riding the Dilu Horse", 131, Rarity.UNCOMMON, mage.cards.r.RidingTheDiluHorse.class)); - cards.add(new SetCardInfo("Riven Turnbull", 171, Rarity.UNCOMMON, mage.cards.r.RivenTurnbull.class)); - cards.add(new SetCardInfo("Rohgahh of Kher Keep", 172, Rarity.RARE, mage.cards.r.RohgahhOfKherKeep.class)); - cards.add(new SetCardInfo("Rolling Earthquake", 110, Rarity.RARE, mage.cards.r.RollingEarthquake.class)); - cards.add(new SetCardInfo("Rubinia Soulsinger", 173, Rarity.RARE, mage.cards.r.RubiniaSoulsinger.class)); - cards.add(new SetCardInfo("Scrubland", 210, Rarity.RARE, mage.cards.s.Scrubland.class)); - cards.add(new SetCardInfo("Scryb Sprites", 132, Rarity.COMMON, mage.cards.s.ScrybSprites.class)); - cards.add(new SetCardInfo("Shu Cavalry", 23, Rarity.COMMON, mage.cards.s.ShuCavalry.class)); - cards.add(new SetCardInfo("Shu Elite Companions", 24, Rarity.COMMON, mage.cards.s.ShuEliteCompanions.class)); - cards.add(new SetCardInfo("Shu General", 25, Rarity.COMMON, mage.cards.s.ShuGeneral.class)); - cards.add(new SetCardInfo("Shu Soldier-Farmers", 26, Rarity.COMMON, mage.cards.s.ShuSoldierFarmers.class)); - cards.add(new SetCardInfo("Sir Shandlar of Eberyn", 174, Rarity.COMMON, mage.cards.s.SirShandlarOfEberyn.class)); - cards.add(new SetCardInfo("Sivitri Scarzam", 175, Rarity.COMMON, mage.cards.s.SivitriScarzam.class)); - cards.add(new SetCardInfo("Slashing Tiger", 133, Rarity.COMMON, mage.cards.s.SlashingTiger.class)); - cards.add(new SetCardInfo("Sol Grail", 201, Rarity.COMMON, mage.cards.s.SolGrail.class)); - cards.add(new SetCardInfo("Sorrow's Path", 211, Rarity.RARE, mage.cards.s.SorrowsPath.class)); - cards.add(new SetCardInfo("Spectral Shield", 176, Rarity.UNCOMMON, mage.cards.s.SpectralShield.class)); - cards.add(new SetCardInfo("Spirit Shackle", 74, Rarity.COMMON, mage.cards.s.SpiritShackle.class)); - cards.add(new SetCardInfo("Spoils of Victory", 134, Rarity.COMMON, mage.cards.s.SpoilsOfVictory.class)); - cards.add(new SetCardInfo("Stangg", 177, Rarity.UNCOMMON, mage.cards.s.Stangg.class)); - cards.add(new SetCardInfo("Stolen Grain", 75, Rarity.UNCOMMON, mage.cards.s.StolenGrain.class)); - cards.add(new SetCardInfo("Storm World", 111, Rarity.RARE, mage.cards.s.StormWorld.class)); - cards.add(new SetCardInfo("Strategic Planning", 51, Rarity.COMMON, mage.cards.s.StrategicPlanning.class)); - cards.add(new SetCardInfo("Sunastian Falconer", 178, Rarity.UNCOMMON, mage.cards.s.SunastianFalconer.class)); - cards.add(new SetCardInfo("Sun Ce, Young Conquerer", 52, Rarity.UNCOMMON, mage.cards.s.SunCeYoungConquerer.class)); - cards.add(new SetCardInfo("Sun Quan, Lord of Wu", 53, Rarity.RARE, mage.cards.s.SunQuanLordOfWu.class)); - cards.add(new SetCardInfo("Swamp", 222, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 223, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 224, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sword of the Ages", 202, Rarity.RARE, mage.cards.s.SwordOfTheAges.class)); - cards.add(new SetCardInfo("Tetsuo Umezawa", 179, Rarity.RARE, mage.cards.t.TetsuoUmezawa.class)); - cards.add(new SetCardInfo("The Abyss", 77, Rarity.RARE, mage.cards.t.TheAbyss.class)); - cards.add(new SetCardInfo("The Lady of the Mountain", 180, Rarity.COMMON, mage.cards.t.TheLadyOfTheMountain.class)); - cards.add(new SetCardInfo("The Tabernacle at Pendrell Vale", 212, Rarity.RARE, mage.cards.t.TheTabernacleAtPendrellVale.class)); - cards.add(new SetCardInfo("The Wretched", 78, Rarity.UNCOMMON, mage.cards.t.TheWretched.class)); - cards.add(new SetCardInfo("Three Visits", 135, Rarity.COMMON, mage.cards.t.ThreeVisits.class)); - cards.add(new SetCardInfo("Tobias Andrion", 181, Rarity.COMMON, mage.cards.t.TobiasAndrion.class)); - cards.add(new SetCardInfo("Torsten Von Ursus", 183, Rarity.COMMON, mage.cards.t.TorstenVonUrsus.class)); - cards.add(new SetCardInfo("Tor Wauki", 182, Rarity.UNCOMMON, mage.cards.t.TorWauki.class)); - cards.add(new SetCardInfo("Tracker", 136, Rarity.UNCOMMON, mage.cards.t.Tracker.class)); - cards.add(new SetCardInfo("Trip Wire", 137, Rarity.COMMON, mage.cards.t.TripWire.class)); - cards.add(new SetCardInfo("Tropical Island", 213, Rarity.RARE, mage.cards.t.TropicalIsland.class)); - cards.add(new SetCardInfo("Tuknir Deathlock", 184, Rarity.UNCOMMON, mage.cards.t.TuknirDeathlock.class)); - cards.add(new SetCardInfo("Urborg", 214, Rarity.UNCOMMON, mage.cards.u.Urborg.class)); - cards.add(new SetCardInfo("Vaevictis Asmadi", 185, Rarity.RARE, mage.cards.v.VaevictisAsmadi.class)); - cards.add(new SetCardInfo("Volcanic Island", 215, Rarity.RARE, mage.cards.v.VolcanicIsland.class)); - cards.add(new SetCardInfo("Wall of Light", 27, Rarity.COMMON, mage.cards.w.WallOfLight.class)); - cards.add(new SetCardInfo("Wandering Mage", 186, Rarity.RARE, mage.cards.w.WanderingMage.class)); - cards.add(new SetCardInfo("Wei Elite Companions", 79, Rarity.COMMON, mage.cards.w.WeiEliteCompanions.class)); - cards.add(new SetCardInfo("Wei Infantry", 80, Rarity.COMMON, mage.cards.w.WeiInfantry.class)); - cards.add(new SetCardInfo("Wei Night Raiders", 81, Rarity.UNCOMMON, mage.cards.w.WeiNightRaiders.class)); - cards.add(new SetCardInfo("Wei Strike Force", 82, Rarity.COMMON, mage.cards.w.WeiStrikeForce.class)); - cards.add(new SetCardInfo("Willow Priestess", 138, Rarity.UNCOMMON, mage.cards.w.WillowPriestess.class)); - cards.add(new SetCardInfo("Willow Satyr", 139, Rarity.RARE, mage.cards.w.WillowSatyr.class)); - cards.add(new SetCardInfo("Wormwood Treefolk", 140, Rarity.UNCOMMON, mage.cards.w.WormwoodTreefolk.class)); - cards.add(new SetCardInfo("Wu Elite Cavalry", 54, Rarity.COMMON, mage.cards.w.WuEliteCavalry.class)); - cards.add(new SetCardInfo("Wu Longbowman", 55, Rarity.COMMON, mage.cards.w.WuLongbowman.class)); - cards.add(new SetCardInfo("Wu Warship", 56, Rarity.COMMON, mage.cards.w.WuWarship.class)); - cards.add(new SetCardInfo("Xiahou Dun, the One-Eyed", 83, Rarity.UNCOMMON, mage.cards.x.XiahouDunTheOneEyed.class)); - cards.add(new SetCardInfo("Xira Arien", 187, Rarity.RARE, mage.cards.x.XiraArien.class)); - cards.add(new SetCardInfo("Young Wei Recruits", 84, Rarity.COMMON, mage.cards.y.YoungWeiRecruits.class)); - cards.add(new SetCardInfo("Zhang Fei, Fierce Warrior", 28, Rarity.UNCOMMON, mage.cards.z.ZhangFeiFierceWarrior.class)); - cards.add(new SetCardInfo("Zodiac Dragon", 112, Rarity.RARE, mage.cards.z.ZodiacDragon.class)); - } + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author LevelX2 + */ +public final class MastersEditionIII extends ExpansionSet { + + private static final MastersEditionIII instance = new MastersEditionIII(); + + public static MastersEditionIII getInstance() { + return instance; + } + + private MastersEditionIII() { + super("Masters Edition III", "ME3", ExpansionSet.buildDate(2009, 9, 7), SetType.MAGIC_ONLINE); + this.hasBasicLands = true; + this.hasBoosters = true; + this.numBoosterLands = 1; + this.numBoosterCommon = 10; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + + cards.add(new SetCardInfo("Active Volcano", 85, Rarity.UNCOMMON, mage.cards.a.ActiveVolcano.class)); + cards.add(new SetCardInfo("Akron Legionnaire", 1, Rarity.RARE, mage.cards.a.AkronLegionnaire.class)); + cards.add(new SetCardInfo("Alabaster Potion", 2, Rarity.UNCOMMON, mage.cards.a.AlabasterPotion.class)); + cards.add(new SetCardInfo("All Hallow's Eve", 57, Rarity.RARE, mage.cards.a.AllHallowsEve.class)); + cards.add(new SetCardInfo("Amrou Kithkin", 3, Rarity.COMMON, mage.cards.a.AmrouKithkin.class)); + cards.add(new SetCardInfo("Anaba Ancestor", 86, Rarity.COMMON, mage.cards.a.AnabaAncestor.class)); + cards.add(new SetCardInfo("Anaba Spirit Crafter", 87, Rarity.COMMON, mage.cards.a.AnabaSpiritCrafter.class)); + cards.add(new SetCardInfo("Angus Mackenzie", 141, Rarity.RARE, mage.cards.a.AngusMackenzie.class)); + cards.add(new SetCardInfo("Arboria", 113, Rarity.RARE, mage.cards.a.Arboria.class)); + cards.add(new SetCardInfo("Arcades Sabboth", 142, Rarity.RARE, mage.cards.a.ArcadesSabboth.class)); + cards.add(new SetCardInfo("Arena of the Ancients", 188, Rarity.RARE, mage.cards.a.ArenaOfTheAncients.class)); + cards.add(new SetCardInfo("Ashes to Ashes", 58, Rarity.UNCOMMON, mage.cards.a.AshesToAshes.class)); + cards.add(new SetCardInfo("Astrolabe", 189, Rarity.COMMON, mage.cards.a.Astrolabe.class)); + cards.add(new SetCardInfo("Axelrod Gunnarson", 143, Rarity.UNCOMMON, mage.cards.a.AxelrodGunnarson.class)); + cards.add(new SetCardInfo("Banshee", 59, Rarity.UNCOMMON, mage.cards.b.Banshee.class)); + cards.add(new SetCardInfo("Barktooth Warbeard", 144, Rarity.COMMON, mage.cards.b.BarktoothWarbeard.class)); + cards.add(new SetCardInfo("Barl's Cage", 190, Rarity.RARE, mage.cards.b.BarlsCage.class)); + cards.add(new SetCardInfo("Bartel Runeaxe", 145, Rarity.UNCOMMON, mage.cards.b.BartelRuneaxe.class)); + cards.add(new SetCardInfo("Bayou", 204, Rarity.RARE, mage.cards.b.Bayou.class)); + cards.add(new SetCardInfo("Bazaar of Baghdad", 205, Rarity.RARE, mage.cards.b.BazaarOfBaghdad.class)); + cards.add(new SetCardInfo("Black Vise", 191, Rarity.RARE, mage.cards.b.BlackVise.class)); + cards.add(new SetCardInfo("Blood Lust", 88, Rarity.COMMON, mage.cards.b.BloodLust.class)); + cards.add(new SetCardInfo("Bone Flute", 192, Rarity.COMMON, mage.cards.b.BoneFlute.class)); + cards.add(new SetCardInfo("Boomerang", 30, Rarity.COMMON, mage.cards.b.Boomerang.class)); + cards.add(new SetCardInfo("Boris Devilboon", 146, Rarity.UNCOMMON, mage.cards.b.BorisDevilboon.class)); + cards.add(new SetCardInfo("Borrowing 100,000 Arrows", 31, Rarity.UNCOMMON, mage.cards.b.Borrowing100000Arrows.class)); + cards.add(new SetCardInfo("Brilliant Plan", 32, Rarity.COMMON, mage.cards.b.BrilliantPlan.class)); + cards.add(new SetCardInfo("Burning of Xinye", 89, Rarity.RARE, mage.cards.b.BurningOfXinye.class)); + cards.add(new SetCardInfo("Call to Arms", 4, Rarity.UNCOMMON, mage.cards.c.CallToArms.class)); + cards.add(new SetCardInfo("Capture of Jingzhou", 33, Rarity.RARE, mage.cards.c.CaptureOfJingzhou.class)); + cards.add(new SetCardInfo("Carrion Ants", 60, Rarity.UNCOMMON, mage.cards.c.CarrionAnts.class)); + cards.add(new SetCardInfo("Chain Lightning", 90, Rarity.COMMON, mage.cards.c.ChainLightning.class)); + cards.add(new SetCardInfo("Chromium", 147, Rarity.RARE, mage.cards.c.Chromium.class)); + cards.add(new SetCardInfo("Cinder Storm", 91, Rarity.UNCOMMON, mage.cards.c.CinderStorm.class)); + cards.add(new SetCardInfo("City of Shadows", 206, Rarity.RARE, mage.cards.c.CityOfShadows.class)); + cards.add(new SetCardInfo("Cleanse", 5, Rarity.RARE, mage.cards.c.Cleanse.class)); + cards.add(new SetCardInfo("Coal Golem", 193, Rarity.COMMON, mage.cards.c.CoalGolem.class)); + cards.add(new SetCardInfo("Concordant Crossroads", 114, Rarity.RARE, mage.cards.c.ConcordantCrossroads.class)); + cards.add(new SetCardInfo("Corrupt Eunuchs", 92, Rarity.UNCOMMON, mage.cards.c.CorruptEunuchs.class)); + cards.add(new SetCardInfo("Cosmic Horror", 61, Rarity.RARE, mage.cards.c.CosmicHorror.class)); + cards.add(new SetCardInfo("Crimson Kobolds", 93, Rarity.COMMON, mage.cards.c.CrimsonKobolds.class)); + cards.add(new SetCardInfo("Crimson Manticore", 94, Rarity.UNCOMMON, mage.cards.c.CrimsonManticore.class)); + cards.add(new SetCardInfo("Dance of Many", 34, Rarity.UNCOMMON, mage.cards.d.DanceOfMany.class)); + cards.add(new SetCardInfo("D'Avenant Archer", 6, Rarity.COMMON, mage.cards.d.DAvenantArcher.class)); + cards.add(new SetCardInfo("Demonic Torment", 62, Rarity.COMMON, mage.cards.d.DemonicTorment.class)); + cards.add(new SetCardInfo("Desert Twister", 115, Rarity.UNCOMMON, mage.cards.d.DesertTwister.class)); + cards.add(new SetCardInfo("Desperate Charge", 63, Rarity.COMMON, mage.cards.d.DesperateCharge.class)); + cards.add(new SetCardInfo("Didgeridoo", 194, Rarity.UNCOMMON, mage.cards.d.Didgeridoo.class)); + cards.add(new SetCardInfo("Disenchant", 7, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Disharmony", 95, Rarity.UNCOMMON, mage.cards.d.Disharmony.class)); + cards.add(new SetCardInfo("Divine Intervention", 8, Rarity.RARE, mage.cards.d.DivineIntervention.class)); + cards.add(new SetCardInfo("Dong Zhou, the Tyrant", 96, Rarity.RARE, mage.cards.d.DongZhouTheTyrant.class)); + cards.add(new SetCardInfo("Eightfold Maze", 9, Rarity.UNCOMMON, mage.cards.e.EightfoldMaze.class)); + cards.add(new SetCardInfo("Elves of Deep Shadow", 116, Rarity.COMMON, mage.cards.e.ElvesOfDeepShadow.class)); + cards.add(new SetCardInfo("Evil Presence", 64, Rarity.COMMON, mage.cards.e.EvilPresence.class)); + cards.add(new SetCardInfo("Exorcist", 10, Rarity.UNCOMMON, mage.cards.e.Exorcist.class)); + cards.add(new SetCardInfo("Faerie Noble", 117, Rarity.UNCOMMON, mage.cards.f.FaerieNoble.class)); + cards.add(new SetCardInfo("False Defeat", 11, Rarity.UNCOMMON, mage.cards.f.FalseDefeat.class)); + cards.add(new SetCardInfo("Famine", 65, Rarity.UNCOMMON, mage.cards.f.Famine.class)); + cards.add(new SetCardInfo("Fellwar Stone", 195, Rarity.COMMON, mage.cards.f.FellwarStone.class)); + cards.add(new SetCardInfo("Fevered Strength", 66, Rarity.COMMON, mage.cards.f.FeveredStrength.class)); + cards.add(new SetCardInfo("Fire Ambush", 97, Rarity.COMMON, mage.cards.f.FireAmbush.class)); + cards.add(new SetCardInfo("Fire Drake", 98, Rarity.COMMON, mage.cards.f.FireDrake.class)); + cards.add(new SetCardInfo("Fire Sprites", 118, Rarity.COMMON, mage.cards.f.FireSprites.class)); + cards.add(new SetCardInfo("Flash Flood", 35, Rarity.UNCOMMON, mage.cards.f.FlashFlood.class)); + cards.add(new SetCardInfo("Forced Retreat", 37, Rarity.COMMON, mage.cards.f.ForcedRetreat.class)); + cards.add(new SetCardInfo("Force Spike", 36, Rarity.COMMON, mage.cards.f.ForceSpike.class)); + cards.add(new SetCardInfo("Forest", 228, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 229, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 230, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forked Lightning", 100, Rarity.UNCOMMON, mage.cards.f.ForkedLightning.class)); + cards.add(new SetCardInfo("Freyalise's Winds", 119, Rarity.RARE, mage.cards.f.FreyalisesWinds.class)); + cards.add(new SetCardInfo("Frost Giant", 101, Rarity.UNCOMMON, mage.cards.f.FrostGiant.class)); + cards.add(new SetCardInfo("Gabriel Angelfire", 148, Rarity.RARE, mage.cards.g.GabrielAngelfire.class)); + cards.add(new SetCardInfo("Gaea's Touch", 120, Rarity.UNCOMMON, mage.cards.g.GaeasTouch.class)); + cards.add(new SetCardInfo("Gauntlets of Chaos", 196, Rarity.RARE, mage.cards.g.GauntletsOfChaos.class)); + cards.add(new SetCardInfo("Ghostly Visit", 67, Rarity.COMMON, mage.cards.g.GhostlyVisit.class)); + cards.add(new SetCardInfo("Ghosts of the Damned", 68, Rarity.COMMON, mage.cards.g.GhostsOfTheDamned.class)); + cards.add(new SetCardInfo("Giant Growth", 121, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); + cards.add(new SetCardInfo("Grim Tutor", 69, Rarity.RARE, mage.cards.g.GrimTutor.class)); + cards.add(new SetCardInfo("Guan Yu's 1,000-Li March", 13, Rarity.RARE, mage.cards.g.GuanYus1000LiMarch.class)); + cards.add(new SetCardInfo("Guan Yu, Sainted Warrior", 12, Rarity.UNCOMMON, mage.cards.g.GuanYuSaintedWarrior.class)); + cards.add(new SetCardInfo("Gwendlyn Di Corci", 149, Rarity.RARE, mage.cards.g.GwendlynDiCorci.class)); + cards.add(new SetCardInfo("Halfdane", 150, Rarity.RARE, mage.cards.h.Halfdane.class)); + cards.add(new SetCardInfo("Hammerheim", 207, Rarity.UNCOMMON, mage.cards.h.Hammerheim.class)); + cards.add(new SetCardInfo("Hazezon Tamar", 151, Rarity.RARE, mage.cards.h.HazezonTamar.class)); + cards.add(new SetCardInfo("Heal", 14, Rarity.COMMON, mage.cards.h.Heal.class)); + cards.add(new SetCardInfo("Heavy Fog", 122, Rarity.COMMON, mage.cards.h.HeavyFog.class)); + cards.add(new SetCardInfo("Hellfire", 70, Rarity.RARE, mage.cards.h.Hellfire.class)); + cards.add(new SetCardInfo("Hua Tuo, Honored Physician", 123, Rarity.RARE, mage.cards.h.HuaTuoHonoredPhysician.class)); + cards.add(new SetCardInfo("Hunding Gjornersen", 152, Rarity.UNCOMMON, mage.cards.h.HundingGjornersen.class)); + cards.add(new SetCardInfo("Hunting Cheetah", 124, Rarity.COMMON, mage.cards.h.HuntingCheetah.class)); + cards.add(new SetCardInfo("Hurloon Minotaur", 102, Rarity.COMMON, mage.cards.h.HurloonMinotaur.class)); + cards.add(new SetCardInfo("Immolation", 103, Rarity.COMMON, mage.cards.i.Immolation.class)); + cards.add(new SetCardInfo("Infuse", 38, Rarity.COMMON, mage.cards.i.Infuse.class)); + cards.add(new SetCardInfo("Island", 219, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 220, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 221, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ivory Guardians", 15, Rarity.UNCOMMON, mage.cards.i.IvoryGuardians.class)); + cards.add(new SetCardInfo("Jedit Ojanen", 153, Rarity.COMMON, mage.cards.j.JeditOjanen.class)); + cards.add(new SetCardInfo("Jerrard of the Closed Fist", 154, Rarity.COMMON, mage.cards.j.JerrardOfTheClosedFist.class)); + cards.add(new SetCardInfo("Jungle Lion", 125, Rarity.COMMON, mage.cards.j.JungleLion.class)); + cards.add(new SetCardInfo("Karakas", 208, Rarity.RARE, mage.cards.k.Karakas.class)); + cards.add(new SetCardInfo("Kei Takahashi", 155, Rarity.UNCOMMON, mage.cards.k.KeiTakahashi.class)); + cards.add(new SetCardInfo("Killer Bees", 126, Rarity.UNCOMMON, mage.cards.k.KillerBees.class)); + cards.add(new SetCardInfo("Kjeldoran Frostbeast", 156, Rarity.UNCOMMON, mage.cards.k.KjeldoranFrostbeast.class)); + cards.add(new SetCardInfo("Kobold Drill Sergeant", 104, Rarity.UNCOMMON, mage.cards.k.KoboldDrillSergeant.class)); + cards.add(new SetCardInfo("Kobold Overlord", 105, Rarity.UNCOMMON, mage.cards.k.KoboldOverlord.class)); + cards.add(new SetCardInfo("Kobolds of Kher Keep", 107, Rarity.COMMON, mage.cards.k.KoboldsOfKherKeep.class)); + cards.add(new SetCardInfo("Kobold Taskmaster", 106, Rarity.COMMON, mage.cards.k.KoboldTaskmaster.class)); + cards.add(new SetCardInfo("Kongming, 'Sleeping Dragon'", 16, Rarity.RARE, mage.cards.k.KongmingSleepingDragon.class)); + cards.add(new SetCardInfo("Labyrinth Minotaur", 39, Rarity.COMMON, mage.cards.l.LabyrinthMinotaur.class)); + cards.add(new SetCardInfo("Lady Caleria", 157, Rarity.UNCOMMON, mage.cards.l.LadyCaleria.class)); + cards.add(new SetCardInfo("Lady Evangela", 158, Rarity.UNCOMMON, mage.cards.l.LadyEvangela.class)); + cards.add(new SetCardInfo("Lady Orca", 159, Rarity.COMMON, mage.cards.l.LadyOrca.class)); + cards.add(new SetCardInfo("Land Equilibrium", 40, Rarity.RARE, mage.cards.l.LandEquilibrium.class)); + cards.add(new SetCardInfo("Land Tax", 17, Rarity.RARE, mage.cards.l.LandTax.class)); + cards.add(new SetCardInfo("Life Chisel", 199, Rarity.RARE, mage.cards.l.LifeChisel.class)); + cards.add(new SetCardInfo("Lightning Blow", 18, Rarity.COMMON, mage.cards.l.LightningBlow.class)); + cards.add(new SetCardInfo("Liu Bei, Lord of Shu", 19, Rarity.RARE, mage.cards.l.LiuBeiLordOfShu.class)); + cards.add(new SetCardInfo("Living Plane", 127, Rarity.RARE, mage.cards.l.LivingPlane.class)); + cards.add(new SetCardInfo("Livonya Silone", 160, Rarity.UNCOMMON, mage.cards.l.LivonyaSilone.class)); + cards.add(new SetCardInfo("Loyal Retainers", 20, Rarity.UNCOMMON, mage.cards.l.LoyalRetainers.class)); + cards.add(new SetCardInfo("Lu Bu, Master-at-Arms", 108, Rarity.RARE, mage.cards.l.LuBuMasterAtArms.class)); + cards.add(new SetCardInfo("Lu Meng, Wu General", 41, Rarity.UNCOMMON, mage.cards.l.LuMengWuGeneral.class)); + cards.add(new SetCardInfo("Lu Xun, Scholar General", 42, Rarity.UNCOMMON, mage.cards.l.LuXunScholarGeneral.class)); + cards.add(new SetCardInfo("Mana Drain", 43, Rarity.RARE, mage.cards.m.ManaDrain.class)); + cards.add(new SetCardInfo("Mana Vortex", 44, Rarity.RARE, mage.cards.m.ManaVortex.class)); + cards.add(new SetCardInfo("Marhault Elsdragon", 161, Rarity.UNCOMMON, mage.cards.m.MarhaultElsdragon.class)); + cards.add(new SetCardInfo("Meng Huo, Barbarian King", 128, Rarity.RARE, mage.cards.m.MengHuoBarbarianKing.class)); + cards.add(new SetCardInfo("Meng Huo's Horde", 129, Rarity.COMMON, mage.cards.m.MengHuosHorde.class)); + cards.add(new SetCardInfo("Mind Twist", 72, Rarity.RARE, mage.cards.m.MindTwist.class)); + cards.add(new SetCardInfo("Misfortune's Gain", 21, Rarity.COMMON, mage.cards.m.MisfortunesGain.class)); + cards.add(new SetCardInfo("Mountain", 225, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 226, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 227, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nebuchadnezzar", 162, Rarity.UNCOMMON, mage.cards.n.Nebuchadnezzar.class)); + cards.add(new SetCardInfo("Nether Void", 73, Rarity.RARE, mage.cards.n.NetherVoid.class)); + cards.add(new SetCardInfo("Nicol Bolas", 163, Rarity.RARE, mage.cards.n.NicolBolas.class)); + cards.add(new SetCardInfo("Nova Pentacle", 200, Rarity.RARE, mage.cards.n.NovaPentacle.class)); + cards.add(new SetCardInfo("Old Man of the Sea", 45, Rarity.RARE, mage.cards.o.OldManOfTheSea.class)); + cards.add(new SetCardInfo("Palladia-Mors", 164, Rarity.RARE, mage.cards.p.PalladiaMors.class)); + cards.add(new SetCardInfo("Pavel Maliki", 165, Rarity.UNCOMMON, mage.cards.p.PavelMaliki.class)); + cards.add(new SetCardInfo("Peach Garden Oath", 22, Rarity.COMMON, mage.cards.p.PeachGardenOath.class)); + cards.add(new SetCardInfo("Plains", 216, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 217, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 218, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plateau", 209, Rarity.RARE, mage.cards.p.Plateau.class)); + cards.add(new SetCardInfo("Princess Lucrezia", 166, Rarity.UNCOMMON, mage.cards.p.PrincessLucrezia.class)); + cards.add(new SetCardInfo("Raging Minotaur", 109, Rarity.COMMON, mage.cards.r.RagingMinotaur.class)); + cards.add(new SetCardInfo("Ragnar", 167, Rarity.UNCOMMON, mage.cards.r.Ragnar.class)); + cards.add(new SetCardInfo("Ramirez DePietro", 168, Rarity.COMMON, mage.cards.r.RamirezDePietro.class)); + cards.add(new SetCardInfo("Ramses Overdark", 169, Rarity.UNCOMMON, mage.cards.r.RamsesOverdark.class)); + cards.add(new SetCardInfo("Rasputin Dreamweaver", 170, Rarity.RARE, mage.cards.r.RasputinDreamweaver.class)); + cards.add(new SetCardInfo("Recall", 46, Rarity.UNCOMMON, mage.cards.r.Recall.class)); + cards.add(new SetCardInfo("Reincarnation", 130, Rarity.UNCOMMON, mage.cards.r.Reincarnation.class)); + cards.add(new SetCardInfo("Remove Soul", 47, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); + cards.add(new SetCardInfo("Reset", 48, Rarity.RARE, mage.cards.r.Reset.class)); + cards.add(new SetCardInfo("Reveka, Wizard Savant", 49, Rarity.UNCOMMON, mage.cards.r.RevekaWizardSavant.class)); + cards.add(new SetCardInfo("Riding the Dilu Horse", 131, Rarity.UNCOMMON, mage.cards.r.RidingTheDiluHorse.class)); + cards.add(new SetCardInfo("Riven Turnbull", 171, Rarity.UNCOMMON, mage.cards.r.RivenTurnbull.class)); + cards.add(new SetCardInfo("Rohgahh of Kher Keep", 172, Rarity.RARE, mage.cards.r.RohgahhOfKherKeep.class)); + cards.add(new SetCardInfo("Rolling Earthquake", 110, Rarity.RARE, mage.cards.r.RollingEarthquake.class)); + cards.add(new SetCardInfo("Rubinia Soulsinger", 173, Rarity.RARE, mage.cards.r.RubiniaSoulsinger.class)); + cards.add(new SetCardInfo("Scrubland", 210, Rarity.RARE, mage.cards.s.Scrubland.class)); + cards.add(new SetCardInfo("Scryb Sprites", 132, Rarity.COMMON, mage.cards.s.ScrybSprites.class)); + cards.add(new SetCardInfo("Shu Cavalry", 23, Rarity.COMMON, mage.cards.s.ShuCavalry.class)); + cards.add(new SetCardInfo("Shu Elite Companions", 24, Rarity.COMMON, mage.cards.s.ShuEliteCompanions.class)); + cards.add(new SetCardInfo("Shu General", 25, Rarity.COMMON, mage.cards.s.ShuGeneral.class)); + cards.add(new SetCardInfo("Shu Soldier-Farmers", 26, Rarity.COMMON, mage.cards.s.ShuSoldierFarmers.class)); + cards.add(new SetCardInfo("Sir Shandlar of Eberyn", 174, Rarity.COMMON, mage.cards.s.SirShandlarOfEberyn.class)); + cards.add(new SetCardInfo("Sivitri Scarzam", 175, Rarity.COMMON, mage.cards.s.SivitriScarzam.class)); + cards.add(new SetCardInfo("Slashing Tiger", 133, Rarity.COMMON, mage.cards.s.SlashingTiger.class)); + cards.add(new SetCardInfo("Sol Grail", 201, Rarity.COMMON, mage.cards.s.SolGrail.class)); + cards.add(new SetCardInfo("Sorrow's Path", 211, Rarity.RARE, mage.cards.s.SorrowsPath.class)); + cards.add(new SetCardInfo("Spectral Shield", 176, Rarity.UNCOMMON, mage.cards.s.SpectralShield.class)); + cards.add(new SetCardInfo("Spirit Shackle", 74, Rarity.COMMON, mage.cards.s.SpiritShackle.class)); + cards.add(new SetCardInfo("Spoils of Victory", 134, Rarity.COMMON, mage.cards.s.SpoilsOfVictory.class)); + cards.add(new SetCardInfo("Stangg", 177, Rarity.UNCOMMON, mage.cards.s.Stangg.class)); + cards.add(new SetCardInfo("Stolen Grain", 75, Rarity.UNCOMMON, mage.cards.s.StolenGrain.class)); + cards.add(new SetCardInfo("Storm World", 111, Rarity.RARE, mage.cards.s.StormWorld.class)); + cards.add(new SetCardInfo("Strategic Planning", 51, Rarity.COMMON, mage.cards.s.StrategicPlanning.class)); + cards.add(new SetCardInfo("Sunastian Falconer", 178, Rarity.UNCOMMON, mage.cards.s.SunastianFalconer.class)); + cards.add(new SetCardInfo("Sun Ce, Young Conquerer", 52, Rarity.UNCOMMON, mage.cards.s.SunCeYoungConquerer.class)); + cards.add(new SetCardInfo("Sun Quan, Lord of Wu", 53, Rarity.RARE, mage.cards.s.SunQuanLordOfWu.class)); + cards.add(new SetCardInfo("Swamp", 222, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 223, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 224, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sword of the Ages", 202, Rarity.RARE, mage.cards.s.SwordOfTheAges.class)); + cards.add(new SetCardInfo("Tetsuo Umezawa", 179, Rarity.RARE, mage.cards.t.TetsuoUmezawa.class)); + cards.add(new SetCardInfo("The Abyss", 77, Rarity.RARE, mage.cards.t.TheAbyss.class)); + cards.add(new SetCardInfo("The Lady of the Mountain", 180, Rarity.COMMON, mage.cards.t.TheLadyOfTheMountain.class)); + cards.add(new SetCardInfo("The Tabernacle at Pendrell Vale", 212, Rarity.RARE, mage.cards.t.TheTabernacleAtPendrellVale.class)); + cards.add(new SetCardInfo("The Wretched", 78, Rarity.UNCOMMON, mage.cards.t.TheWretched.class)); + cards.add(new SetCardInfo("Three Visits", 135, Rarity.COMMON, mage.cards.t.ThreeVisits.class)); + cards.add(new SetCardInfo("Tobias Andrion", 181, Rarity.COMMON, mage.cards.t.TobiasAndrion.class)); + cards.add(new SetCardInfo("Torsten Von Ursus", 183, Rarity.COMMON, mage.cards.t.TorstenVonUrsus.class)); + cards.add(new SetCardInfo("Tor Wauki", 182, Rarity.UNCOMMON, mage.cards.t.TorWauki.class)); + cards.add(new SetCardInfo("Tracker", 136, Rarity.UNCOMMON, mage.cards.t.Tracker.class)); + cards.add(new SetCardInfo("Trip Wire", 137, Rarity.COMMON, mage.cards.t.TripWire.class)); + cards.add(new SetCardInfo("Tropical Island", 213, Rarity.RARE, mage.cards.t.TropicalIsland.class)); + cards.add(new SetCardInfo("Tuknir Deathlock", 184, Rarity.UNCOMMON, mage.cards.t.TuknirDeathlock.class)); + cards.add(new SetCardInfo("Urborg", 214, Rarity.UNCOMMON, mage.cards.u.Urborg.class)); + cards.add(new SetCardInfo("Vaevictis Asmadi", 185, Rarity.RARE, mage.cards.v.VaevictisAsmadi.class)); + cards.add(new SetCardInfo("Volcanic Island", 215, Rarity.RARE, mage.cards.v.VolcanicIsland.class)); + cards.add(new SetCardInfo("Wall of Light", 27, Rarity.COMMON, mage.cards.w.WallOfLight.class)); + cards.add(new SetCardInfo("Wandering Mage", 186, Rarity.RARE, mage.cards.w.WanderingMage.class)); + cards.add(new SetCardInfo("Wei Elite Companions", 79, Rarity.COMMON, mage.cards.w.WeiEliteCompanions.class)); + cards.add(new SetCardInfo("Wei Infantry", 80, Rarity.COMMON, mage.cards.w.WeiInfantry.class)); + cards.add(new SetCardInfo("Wei Night Raiders", 81, Rarity.UNCOMMON, mage.cards.w.WeiNightRaiders.class)); + cards.add(new SetCardInfo("Wei Strike Force", 82, Rarity.COMMON, mage.cards.w.WeiStrikeForce.class)); + cards.add(new SetCardInfo("Willow Priestess", 138, Rarity.UNCOMMON, mage.cards.w.WillowPriestess.class)); + cards.add(new SetCardInfo("Willow Satyr", 139, Rarity.RARE, mage.cards.w.WillowSatyr.class)); + cards.add(new SetCardInfo("Wormwood Treefolk", 140, Rarity.UNCOMMON, mage.cards.w.WormwoodTreefolk.class)); + cards.add(new SetCardInfo("Wu Elite Cavalry", 54, Rarity.COMMON, mage.cards.w.WuEliteCavalry.class)); + cards.add(new SetCardInfo("Wu Longbowman", 55, Rarity.COMMON, mage.cards.w.WuLongbowman.class)); + cards.add(new SetCardInfo("Wu Warship", 56, Rarity.COMMON, mage.cards.w.WuWarship.class)); + cards.add(new SetCardInfo("Xiahou Dun, the One-Eyed", 83, Rarity.UNCOMMON, mage.cards.x.XiahouDunTheOneEyed.class)); + cards.add(new SetCardInfo("Xira Arien", 187, Rarity.RARE, mage.cards.x.XiraArien.class)); + cards.add(new SetCardInfo("Young Wei Recruits", 84, Rarity.COMMON, mage.cards.y.YoungWeiRecruits.class)); + cards.add(new SetCardInfo("Zhang Fei, Fierce Warrior", 28, Rarity.UNCOMMON, mage.cards.z.ZhangFeiFierceWarrior.class)); + cards.add(new SetCardInfo("Zodiac Dragon", 112, Rarity.RARE, mage.cards.z.ZodiacDragon.class)); + } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 555bfe9b18..9c6fedd163 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -1,320 +1,320 @@ - -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; -import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; -import mage.constants.Rarity; -import mage.constants.SetType; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author LevelX2 - */ -public final class MastersEditionIV extends ExpansionSet { - - private static final MastersEditionIV instance = new MastersEditionIV(); - - public static MastersEditionIV getInstance() { - return instance; - } - - private final List savedSpecialLand = new ArrayList<>(); - - private MastersEditionIV() { - super("Masters Edition IV", "ME4", ExpansionSet.buildDate(2011, 1, 10), SetType.MAGIC_ONLINE); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 1; - this.numBoosterCommon = 10; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - this.ratioBoosterSpecialLand = 1; // replace all basic lands - - cards.add(new SetCardInfo("Acid Rain", 36, Rarity.RARE, mage.cards.a.AcidRain.class)); - cards.add(new SetCardInfo("Aesthir Glider", 176, Rarity.COMMON, mage.cards.a.AesthirGlider.class)); - cards.add(new SetCardInfo("Air Elemental", 37, Rarity.UNCOMMON, mage.cards.a.AirElemental.class)); - cards.add(new SetCardInfo("Al-abara's Carpet", 177, Rarity.RARE, mage.cards.a.AlAbarasCarpet.class)); - cards.add(new SetCardInfo("Alaborn Musketeer", 1, Rarity.COMMON, mage.cards.a.AlabornMusketeer.class)); - cards.add(new SetCardInfo("Alaborn Trooper", 2, Rarity.COMMON, mage.cards.a.AlabornTrooper.class)); - cards.add(new SetCardInfo("Aladdin", 106, Rarity.RARE, mage.cards.a.Aladdin.class)); - cards.add(new SetCardInfo("Alchor's Tomb", 178, Rarity.RARE, mage.cards.a.AlchorsTomb.class)); - cards.add(new SetCardInfo("Ali from Cairo", 107, Rarity.RARE, mage.cards.a.AliFromCairo.class)); - cards.add(new SetCardInfo("Alluring Scent", 141, Rarity.COMMON, mage.cards.a.AlluringScent.class)); - cards.add(new SetCardInfo("Amulet of Kroog", 179, Rarity.COMMON, mage.cards.a.AmuletOfKroog.class)); - cards.add(new SetCardInfo("Angelic Voices", 3, Rarity.UNCOMMON, mage.cards.a.AngelicVoices.class)); - cards.add(new SetCardInfo("Animate Artifact", 38, Rarity.UNCOMMON, mage.cards.a.AnimateArtifact.class)); - cards.add(new SetCardInfo("Argivian Blacksmith", 4, Rarity.UNCOMMON, mage.cards.a.ArgivianBlacksmith.class)); - cards.add(new SetCardInfo("Argothian Pixies", 142, Rarity.COMMON, mage.cards.a.ArgothianPixies.class)); - cards.add(new SetCardInfo("Argothian Treefolk", 143, Rarity.UNCOMMON, mage.cards.a.ArgothianTreefolk.class)); - cards.add(new SetCardInfo("Armageddon Clock", 180, Rarity.RARE, mage.cards.a.ArmageddonClock.class)); - cards.add(new SetCardInfo("Armageddon", 5, Rarity.RARE, mage.cards.a.Armageddon.class)); - cards.add(new SetCardInfo("Artifact Blast", 108, Rarity.COMMON, mage.cards.a.ArtifactBlast.class)); - cards.add(new SetCardInfo("Ashnod's Altar", 181, Rarity.RARE, mage.cards.a.AshnodsAltar.class)); - cards.add(new SetCardInfo("Atog", 109, Rarity.COMMON, mage.cards.a.Atog.class)); - cards.add(new SetCardInfo("Badlands", 241, Rarity.RARE, mage.cards.b.Badlands.class)); - cards.add(new SetCardInfo("Balance", 6, Rarity.RARE, mage.cards.b.Balance.class)); - cards.add(new SetCardInfo("Basalt Monolith", 182, Rarity.UNCOMMON, mage.cards.b.BasaltMonolith.class)); - cards.add(new SetCardInfo("Bayou", 242, Rarity.RARE, mage.cards.b.Bayou.class)); - cards.add(new SetCardInfo("Bee Sting", 144, Rarity.UNCOMMON, mage.cards.b.BeeSting.class)); - cards.add(new SetCardInfo("Bird Maiden", 110, Rarity.COMMON, mage.cards.b.BirdMaiden.class)); - cards.add(new SetCardInfo("Black Knight", 71, Rarity.UNCOMMON, mage.cards.b.BlackKnight.class)); - cards.add(new SetCardInfo("Blaze of Glory", 7, Rarity.UNCOMMON, mage.cards.b.BlazeOfGlory.class)); - cards.add(new SetCardInfo("Blue Elemental Blast", 39, Rarity.UNCOMMON, mage.cards.b.BlueElementalBlast.class)); - cards.add(new SetCardInfo("Book of Rass", 183, Rarity.UNCOMMON, mage.cards.b.BookOfRass.class)); - cards.add(new SetCardInfo("Bottle of Suleiman", 184, Rarity.RARE, mage.cards.b.BottleOfSuleiman.class)); - cards.add(new SetCardInfo("Braingeyser", 40, Rarity.RARE, mage.cards.b.Braingeyser.class)); - cards.add(new SetCardInfo("Brass Man", 185, Rarity.COMMON, mage.cards.b.BrassMan.class)); - cards.add(new SetCardInfo("Candelabra of Tawnos", 187, Rarity.RARE, mage.cards.c.CandelabraOfTawnos.class)); - cards.add(new SetCardInfo("Celestial Sword", 188, Rarity.UNCOMMON, mage.cards.c.CelestialSword.class)); - cards.add(new SetCardInfo("Champion Lancer", 8, Rarity.RARE, mage.cards.c.ChampionLancer.class)); - cards.add(new SetCardInfo("Channel", 145, Rarity.RARE, mage.cards.c.Channel.class)); - cards.add(new SetCardInfo("Citanul Druid", 146, Rarity.COMMON, mage.cards.c.CitanulDruid.class)); - cards.add(new SetCardInfo("City of Brass", 243, Rarity.RARE, mage.cards.c.CityOfBrass.class)); - cards.add(new SetCardInfo("Clay Statue", 189, Rarity.UNCOMMON, mage.cards.c.ClayStatue.class)); - cards.add(new SetCardInfo("Clockwork Avian", 190, Rarity.UNCOMMON, mage.cards.c.ClockworkAvian.class)); - cards.add(new SetCardInfo("Clockwork Gnomes", 191, Rarity.UNCOMMON, mage.cards.c.ClockworkGnomes.class)); - cards.add(new SetCardInfo("Clockwork Swarm", 192, Rarity.COMMON, mage.cards.c.ClockworkSwarm.class)); - cards.add(new SetCardInfo("Cloud Dragon", 41, Rarity.RARE, mage.cards.c.CloudDragon.class)); - cards.add(new SetCardInfo("Cloud Spirit", 42, Rarity.COMMON, mage.cards.c.CloudSpirit.class)); - cards.add(new SetCardInfo("Colossus of Sardia", 193, Rarity.RARE, mage.cards.c.ColossusOfSardia.class)); - cards.add(new SetCardInfo("Control Magic", 43, Rarity.RARE, mage.cards.c.ControlMagic.class)); - cards.add(new SetCardInfo("Conversion", 9, Rarity.RARE, mage.cards.c.Conversion.class)); - cards.add(new SetCardInfo("Copy Artifact", 44, Rarity.RARE, mage.cards.c.CopyArtifact.class)); - cards.add(new SetCardInfo("Coral Helm", 194, Rarity.UNCOMMON, mage.cards.c.CoralHelm.class)); - cards.add(new SetCardInfo("Counterspell", 45, Rarity.COMMON, mage.cards.c.Counterspell.class)); - cards.add(new SetCardInfo("Crumble", 147, Rarity.COMMON, mage.cards.c.Crumble.class)); - cards.add(new SetCardInfo("Cyclone", 148, Rarity.RARE, mage.cards.c.Cyclone.class)); - cards.add(new SetCardInfo("Cyclopean Mummy", 72, Rarity.COMMON, mage.cards.c.CyclopeanMummy.class)); - cards.add(new SetCardInfo("Cyclopean Tomb", 195, Rarity.RARE, mage.cards.c.CyclopeanTomb.class)); - cards.add(new SetCardInfo("Dakmor Plague", 73, Rarity.UNCOMMON, mage.cards.d.DakmorPlague.class)); - cards.add(new SetCardInfo("Dark Ritual", 74, Rarity.COMMON, mage.cards.d.DarkRitual.class)); - cards.add(new SetCardInfo("Deathcoil Wurm", 149, Rarity.RARE, mage.cards.d.DeathcoilWurm.class)); - cards.add(new SetCardInfo("Deathgrip", 75, Rarity.RARE, mage.cards.d.Deathgrip.class)); - cards.add(new SetCardInfo("Demonic Hordes", 76, Rarity.RARE, mage.cards.d.DemonicHordes.class)); - cards.add(new SetCardInfo("Demonic Tutor", 77, Rarity.RARE, mage.cards.d.DemonicTutor.class)); - cards.add(new SetCardInfo("Detonate", 111, Rarity.UNCOMMON, mage.cards.d.Detonate.class)); - cards.add(new SetCardInfo("Devastation", 112, Rarity.RARE, mage.cards.d.Devastation.class)); - cards.add(new SetCardInfo("Diabolic Machine", 196, Rarity.UNCOMMON, mage.cards.d.DiabolicMachine.class)); - cards.add(new SetCardInfo("Divine Offering", 10, Rarity.COMMON, mage.cards.d.DivineOffering.class)); - cards.add(new SetCardInfo("Dragon Engine", 197, Rarity.COMMON, mage.cards.d.DragonEngine.class)); - cards.add(new SetCardInfo("Drain Power", 46, Rarity.RARE, mage.cards.d.DrainPower.class)); - cards.add(new SetCardInfo("Dread Reaper", 78, Rarity.RARE, mage.cards.d.DreadReaper.class)); - cards.add(new SetCardInfo("Dread Wight", 79, Rarity.UNCOMMON, mage.cards.d.DreadWight.class)); - cards.add(new SetCardInfo("Drop of Honey", 150, Rarity.RARE, mage.cards.d.DropOfHoney.class)); - cards.add(new SetCardInfo("Drowned", 47, Rarity.COMMON, mage.cards.d.Drowned.class)); - cards.add(new SetCardInfo("Dust to Dust", 11, Rarity.UNCOMMON, mage.cards.d.DustToDust.class)); - cards.add(new SetCardInfo("Ebon Dragon", 80, Rarity.RARE, mage.cards.e.EbonDragon.class)); - cards.add(new SetCardInfo("Ebony Horse", 198, Rarity.COMMON, mage.cards.e.EbonyHorse.class)); - cards.add(new SetCardInfo("Ebony Rhino", 199, Rarity.COMMON, mage.cards.e.EbonyRhino.class)); - cards.add(new SetCardInfo("Elephant Graveyard", 244, Rarity.UNCOMMON, mage.cards.e.ElephantGraveyard.class)); - cards.add(new SetCardInfo("Elite Cat Warrior", 151, Rarity.COMMON, mage.cards.e.EliteCatWarrior.class)); - cards.add(new SetCardInfo("Energy Flux", 48, Rarity.UNCOMMON, mage.cards.e.EnergyFlux.class)); - cards.add(new SetCardInfo("Eye for an Eye", 12, Rarity.RARE, mage.cards.e.EyeForAnEye.class)); - cards.add(new SetCardInfo("False Summoning", 49, Rarity.COMMON, mage.cards.f.FalseSummoning.class)); - cards.add(new SetCardInfo("Fastbond", 152, Rarity.RARE, mage.cards.f.Fastbond.class)); - cards.add(new SetCardInfo("Fire Imp", 113, Rarity.UNCOMMON, mage.cards.f.FireImp.class)); - cards.add(new SetCardInfo("Fire Tempest", 114, Rarity.RARE, mage.cards.f.FireTempest.class)); - cards.add(new SetCardInfo("Fireball", 115, Rarity.UNCOMMON, mage.cards.f.Fireball.class)); - cards.add(new SetCardInfo("Floodwater Dam", 200, Rarity.RARE, mage.cards.f.FloodwaterDam.class)); - cards.add(new SetCardInfo("Flying Carpet", 201, Rarity.COMMON, mage.cards.f.FlyingCarpet.class)); - cards.add(new SetCardInfo("Fog", 153, Rarity.COMMON, mage.cards.f.Fog.class)); - cards.add(new SetCardInfo("Force of Nature", 154, Rarity.RARE, mage.cards.f.ForceOfNature.class)); - cards.add(new SetCardInfo("Fork", 116, Rarity.RARE, mage.cards.f.Fork.class)); - cards.add(new SetCardInfo("Foul Spirit", 81, Rarity.COMMON, mage.cards.f.FoulSpirit.class)); - cards.add(new SetCardInfo("Gaea's Avenger", 155, Rarity.UNCOMMON, mage.cards.g.GaeasAvenger.class)); - cards.add(new SetCardInfo("Gate to Phyrexia", 82, Rarity.UNCOMMON, mage.cards.g.GateToPhyrexia.class)); - cards.add(new SetCardInfo("Gauntlet of Might", 202, Rarity.RARE, mage.cards.g.GauntletOfMight.class)); - cards.add(new SetCardInfo("Giant Growth", 156, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); - cards.add(new SetCardInfo("Giant Tortoise", 50, Rarity.COMMON, mage.cards.g.GiantTortoise.class)); - cards.add(new SetCardInfo("Glasses of Urza", 203, Rarity.COMMON, mage.cards.g.GlassesOfUrza.class)); - cards.add(new SetCardInfo("Gloom", 83, Rarity.RARE, mage.cards.g.Gloom.class)); - cards.add(new SetCardInfo("Goblin Bully", 117, Rarity.COMMON, mage.cards.g.GoblinBully.class)); - cards.add(new SetCardInfo("Goblin Cavaliers", 118, Rarity.COMMON, mage.cards.g.GoblinCavaliers.class)); - cards.add(new SetCardInfo("Goblin Caves", 119, Rarity.COMMON, mage.cards.g.GoblinCaves.class)); - cards.add(new SetCardInfo("Goblin Firestarter", 120, Rarity.COMMON, mage.cards.g.GoblinFirestarter.class)); - cards.add(new SetCardInfo("Goblin General", 121, Rarity.UNCOMMON, mage.cards.g.GoblinGeneral.class)); - cards.add(new SetCardInfo("Goblin Shrine", 122, Rarity.COMMON, mage.cards.g.GoblinShrine.class)); - cards.add(new SetCardInfo("Goblin Warrens", 123, Rarity.UNCOMMON, mage.cards.g.GoblinWarrens.class)); - cards.add(new SetCardInfo("Gorilla War Cry", 124, Rarity.COMMON, mage.cards.g.GorillaWarCry.class)); - cards.add(new SetCardInfo("Grapeshot Catapult", 204, Rarity.UNCOMMON, mage.cards.g.GrapeshotCatapult.class)); - cards.add(new SetCardInfo("Gravebind", 84, Rarity.COMMON, mage.cards.g.Gravebind.class)); - cards.add(new SetCardInfo("Guardian Beast", 85, Rarity.RARE, mage.cards.g.GuardianBeast.class)); - cards.add(new SetCardInfo("Harsh Justice", 13, Rarity.RARE, mage.cards.h.HarshJustice.class)); - cards.add(new SetCardInfo("Hasran Ogress", 86, Rarity.COMMON, mage.cards.h.HasranOgress.class)); - cards.add(new SetCardInfo("Healing Salve", 14, Rarity.COMMON, mage.cards.h.HealingSalve.class)); - cards.add(new SetCardInfo("Horn of Deafening", 205, Rarity.UNCOMMON, mage.cards.h.HornOfDeafening.class)); - cards.add(new SetCardInfo("Howl from Beyond", 87, Rarity.COMMON, mage.cards.h.HowlFromBeyond.class)); - cards.add(new SetCardInfo("Ice Cauldron", 206, Rarity.RARE, mage.cards.i.IceCauldron.class)); - cards.add(new SetCardInfo("Icy Manipulator", 207, Rarity.UNCOMMON, mage.cards.i.IcyManipulator.class)); - cards.add(new SetCardInfo("In the Eye of Chaos", 51, Rarity.RARE, mage.cards.i.InTheEyeOfChaos.class)); - cards.add(new SetCardInfo("Instill Energy", 157, Rarity.UNCOMMON, mage.cards.i.InstillEnergy.class)); - cards.add(new SetCardInfo("Ironhoof Ox", 158, Rarity.COMMON, mage.cards.i.IronhoofOx.class)); - cards.add(new SetCardInfo("Island Sanctuary", 15, Rarity.RARE, mage.cards.i.IslandSanctuary.class)); - cards.add(new SetCardInfo("Jade Monolith", 208, Rarity.RARE, mage.cards.j.JadeMonolith.class)); - cards.add(new SetCardInfo("Juggernaut", 209, Rarity.UNCOMMON, mage.cards.j.Juggernaut.class)); - cards.add(new SetCardInfo("Junun Efreet", 88, Rarity.UNCOMMON, mage.cards.j.JununEfreet.class)); - cards.add(new SetCardInfo("Just Fate", 16, Rarity.COMMON, mage.cards.j.JustFate.class)); - cards.add(new SetCardInfo("Kismet", 17, Rarity.RARE, mage.cards.k.Kismet.class)); - cards.add(new SetCardInfo("Kormus Bell", 210, Rarity.RARE, mage.cards.k.KormusBell.class)); - cards.add(new SetCardInfo("Kudzu", 159, Rarity.UNCOMMON, mage.cards.k.Kudzu.class)); - cards.add(new SetCardInfo("Last Chance", 125, Rarity.RARE, mage.cards.l.LastChance.class)); - cards.add(new SetCardInfo("Lava Flow", 126, Rarity.COMMON, mage.cards.l.LavaFlow.class)); - cards.add(new SetCardInfo("Leeches", 18, Rarity.RARE, mage.cards.l.Leeches.class)); - cards.add(new SetCardInfo("Library of Alexandria", 245, Rarity.RARE, mage.cards.l.LibraryOfAlexandria.class)); - cards.add(new SetCardInfo("Library of Leng", 211, Rarity.COMMON, mage.cards.l.LibraryOfLeng.class)); - cards.add(new SetCardInfo("Lich", 89, Rarity.RARE, mage.cards.l.Lich.class)); - cards.add(new SetCardInfo("Lifeforce", 160, Rarity.RARE, mage.cards.l.Lifeforce.class)); - cards.add(new SetCardInfo("Lim-Dul's Cohort", 90, Rarity.COMMON, mage.cards.l.LimDulsCohort.class)); - cards.add(new SetCardInfo("Living Lands", 161, Rarity.RARE, mage.cards.l.LivingLands.class)); - cards.add(new SetCardInfo("Living Wall", 212, Rarity.UNCOMMON, mage.cards.l.LivingWall.class)); - cards.add(new SetCardInfo("Mahamoti Djinn", 52, Rarity.RARE, mage.cards.m.MahamotiDjinn.class)); - cards.add(new SetCardInfo("Mana Matrix", 213, Rarity.RARE, mage.cards.m.ManaMatrix.class)); - cards.add(new SetCardInfo("Mana Vault", 214, Rarity.RARE, mage.cards.m.ManaVault.class)); - cards.add(new SetCardInfo("Martyr's Cry", 19, Rarity.RARE, mage.cards.m.MartyrsCry.class)); - cards.add(new SetCardInfo("Martyrs of Korlis", 20, Rarity.UNCOMMON, mage.cards.m.MartyrsOfKorlis.class)); - cards.add(new SetCardInfo("Maze of Ith", 246, Rarity.RARE, mage.cards.m.MazeOfIth.class)); - cards.add(new SetCardInfo("Mightstone", 215, Rarity.COMMON, mage.cards.m.Mightstone.class)); - cards.add(new SetCardInfo("Mijae Djinn", 127, Rarity.RARE, mage.cards.m.MijaeDjinn.class)); - cards.add(new SetCardInfo("Minion of Tevesh Szat", 91, Rarity.RARE, mage.cards.m.MinionOfTeveshSzat.class)); - cards.add(new SetCardInfo("Mishra's Workshop", 247, Rarity.RARE, mage.cards.m.MishrasWorkshop.class)); - cards.add(new SetCardInfo("Mystic Decree", 53, Rarity.UNCOMMON, mage.cards.m.MysticDecree.class)); - cards.add(new SetCardInfo("Naked Singularity", 216, Rarity.RARE, mage.cards.n.NakedSingularity.class)); - cards.add(new SetCardInfo("Oasis", 248, Rarity.COMMON, mage.cards.o.Oasis.class)); - cards.add(new SetCardInfo("Obelisk of Undoing", 217, Rarity.RARE, mage.cards.o.ObeliskOfUndoing.class)); - cards.add(new SetCardInfo("Obsianus Golem", 218, Rarity.COMMON, mage.cards.o.ObsianusGolem.class)); - cards.add(new SetCardInfo("Ogre Taskmaster", 128, Rarity.COMMON, mage.cards.o.OgreTaskmaster.class)); - cards.add(new SetCardInfo("Onulet", 219, Rarity.COMMON, mage.cards.o.Onulet.class)); - cards.add(new SetCardInfo("Orcish Mechanics", 129, Rarity.UNCOMMON, mage.cards.o.OrcishMechanics.class)); - cards.add(new SetCardInfo("Osai Vultures", 21, Rarity.COMMON, mage.cards.o.OsaiVultures.class)); - cards.add(new SetCardInfo("Overwhelming Forces", 92, Rarity.RARE, mage.cards.o.OverwhelmingForces.class)); - cards.add(new SetCardInfo("Owl Familiar", 54, Rarity.COMMON, mage.cards.o.OwlFamiliar.class)); - cards.add(new SetCardInfo("Pentagram of the Ages", 220, Rarity.UNCOMMON, mage.cards.p.PentagramOfTheAges.class)); - cards.add(new SetCardInfo("Personal Incarnation", 22, Rarity.RARE, mage.cards.p.PersonalIncarnation.class)); - cards.add(new SetCardInfo("Phantasmal Forces", 55, Rarity.COMMON, mage.cards.p.PhantasmalForces.class)); - cards.add(new SetCardInfo("Phantasmal Terrain", 56, Rarity.COMMON, mage.cards.p.PhantasmalTerrain.class)); - cards.add(new SetCardInfo("Planar Gate", 221, Rarity.UNCOMMON, mage.cards.p.PlanarGate.class)); - cards.add(new SetCardInfo("Plateau", 249, Rarity.RARE, mage.cards.p.Plateau.class)); - cards.add(new SetCardInfo("Power Artifact", 57, Rarity.RARE, mage.cards.p.PowerArtifact.class)); - cards.add(new SetCardInfo("Primal Clay", 222, Rarity.COMMON, mage.cards.p.PrimalClay.class)); - cards.add(new SetCardInfo("Prodigal Sorcerer", 58, Rarity.UNCOMMON, mage.cards.p.ProdigalSorcerer.class)); - cards.add(new SetCardInfo("Prowling Nightstalker", 93, Rarity.COMMON, mage.cards.p.ProwlingNightstalker.class)); - cards.add(new SetCardInfo("Radjan Spirit", 162, Rarity.UNCOMMON, mage.cards.r.RadjanSpirit.class)); - cards.add(new SetCardInfo("Rain of Daggers", 94, Rarity.RARE, mage.cards.r.RainOfDaggers.class)); - cards.add(new SetCardInfo("Rakalite", 223, Rarity.RARE, mage.cards.r.Rakalite.class)); - cards.add(new SetCardInfo("Reconstruction", 59, Rarity.COMMON, mage.cards.r.Reconstruction.class)); - cards.add(new SetCardInfo("Red Elemental Blast", 131, Rarity.UNCOMMON, mage.cards.r.RedElementalBlast.class)); - cards.add(new SetCardInfo("Regrowth", 163, Rarity.RARE, mage.cards.r.Regrowth.class)); - cards.add(new SetCardInfo("Righteous Charge", 23, Rarity.COMMON, mage.cards.r.RighteousCharge.class)); - cards.add(new SetCardInfo("Ring of Renewal", 224, Rarity.RARE, mage.cards.r.RingOfRenewal.class)); - cards.add(new SetCardInfo("Roc of Kher Ridges", 132, Rarity.UNCOMMON, mage.cards.r.RocOfKherRidges.class)); - cards.add(new SetCardInfo("Rock Hydra", 133, Rarity.RARE, mage.cards.r.RockHydra.class)); - cards.add(new SetCardInfo("Rockslide Ambush", 134, Rarity.COMMON, mage.cards.r.RockslideAmbush.class)); - cards.add(new SetCardInfo("Sandstorm", 164, Rarity.COMMON, mage.cards.s.Sandstorm.class)); - cards.add(new SetCardInfo("Savannah Lions", 24, Rarity.UNCOMMON, mage.cards.s.SavannahLions.class)); - cards.add(new SetCardInfo("Savannah", 250, Rarity.RARE, mage.cards.s.Savannah.class)); - cards.add(new SetCardInfo("Scarecrow", 225, Rarity.UNCOMMON, mage.cards.s.Scarecrow.class)); - cards.add(new SetCardInfo("Scarwood Bandits", 165, Rarity.RARE, mage.cards.s.ScarwoodBandits.class)); - cards.add(new SetCardInfo("Scavenger Folk", 166, Rarity.COMMON, mage.cards.s.ScavengerFolk.class)); - cards.add(new SetCardInfo("Scavenging Ghoul", 95, Rarity.UNCOMMON, mage.cards.s.ScavengingGhoul.class)); - cards.add(new SetCardInfo("Scrubland", 251, Rarity.RARE, mage.cards.s.Scrubland.class)); - cards.add(new SetCardInfo("Sea Serpent", 60, Rarity.COMMON, mage.cards.s.SeaSerpent.class)); - cards.add(new SetCardInfo("Sedge Troll", 135, Rarity.RARE, mage.cards.s.SedgeTroll.class)); - cards.add(new SetCardInfo("Sengir Vampire", 96, Rarity.UNCOMMON, mage.cards.s.SengirVampire.class)); - cards.add(new SetCardInfo("Serendib Djinn", 61, Rarity.RARE, mage.cards.s.SerendibDjinn.class)); - cards.add(new SetCardInfo("Serra Angel", 25, Rarity.UNCOMMON, mage.cards.s.SerraAngel.class)); - cards.add(new SetCardInfo("Serra Aviary", 26, Rarity.UNCOMMON, mage.cards.s.SerraAviary.class)); - cards.add(new SetCardInfo("Serra Bestiary", 27, Rarity.COMMON, mage.cards.s.SerraBestiary.class)); - cards.add(new SetCardInfo("Shapeshifter", 226, Rarity.UNCOMMON, mage.cards.s.Shapeshifter.class)); - cards.add(new SetCardInfo("Shivan Dragon", 136, Rarity.RARE, mage.cards.s.ShivanDragon.class)); - cards.add(new SetCardInfo("Sinkhole", 97, Rarity.RARE, mage.cards.s.Sinkhole.class)); - cards.add(new SetCardInfo("Sleight of Hand", 62, Rarity.COMMON, mage.cards.s.SleightOfHand.class)); - cards.add(new SetCardInfo("Smoke", 137, Rarity.RARE, mage.cards.s.Smoke.class)); - cards.add(new SetCardInfo("Sol Ring", 227, Rarity.RARE, mage.cards.s.SolRing.class)); - cards.add(new SetCardInfo("Soldevi Golem", 228, Rarity.UNCOMMON, mage.cards.s.SoldeviGolem.class)); - cards.add(new SetCardInfo("Soldevi Machinist", 63, Rarity.UNCOMMON, mage.cards.s.SoldeviMachinist.class)); - cards.add(new SetCardInfo("Soul Shred", 98, Rarity.COMMON, mage.cards.s.SoulShred.class)); - cards.add(new SetCardInfo("Southern Elephant", 167, Rarity.COMMON, mage.cards.s.SouthernElephant.class)); - cards.add(new SetCardInfo("Spotted Griffin", 28, Rarity.COMMON, mage.cards.s.SpottedGriffin.class)); - cards.add(new SetCardInfo("Squall", 168, Rarity.UNCOMMON, mage.cards.s.Squall.class)); - cards.add(new SetCardInfo("Staff of Zegon", 229, Rarity.COMMON, mage.cards.s.StaffOfZegon.class)); - cards.add(new SetCardInfo("Stasis", 64, Rarity.RARE, mage.cards.s.Stasis.class)); - cards.add(new SetCardInfo("Steam Catapult", 29, Rarity.RARE, mage.cards.s.SteamCatapult.class)); - cards.add(new SetCardInfo("Strip Mine", 252, Rarity.RARE, mage.cards.s.StripMine.class)); - cards.add(new SetCardInfo("Swords to Plowshares", 30, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); - cards.add(new SetCardInfo("Sylvan Tutor", 169, Rarity.UNCOMMON, mage.cards.s.SylvanTutor.class)); - cards.add(new SetCardInfo("Symbol of Unsummoning", 65, Rarity.COMMON, mage.cards.s.SymbolOfUnsummoning.class)); - cards.add(new SetCardInfo("Tablet of Epityr", 230, Rarity.COMMON, mage.cards.t.TabletOfEpityr.class)); - cards.add(new SetCardInfo("Taiga", 253, Rarity.RARE, mage.cards.t.Taiga.class)); - cards.add(new SetCardInfo("Talas Researcher", 66, Rarity.UNCOMMON, mage.cards.t.TalasResearcher.class)); - cards.add(new SetCardInfo("Tawnos's Wand", 231, Rarity.COMMON, mage.cards.t.TawnossWand.class)); - cards.add(new SetCardInfo("Tawnos's Weaponry", 232, Rarity.UNCOMMON, mage.cards.t.TawnossWeaponry.class)); - cards.add(new SetCardInfo("Temple Acolyte", 31, Rarity.COMMON, mage.cards.t.TempleAcolyte.class)); - cards.add(new SetCardInfo("Terror", 99, Rarity.COMMON, mage.cards.t.Terror.class)); - cards.add(new SetCardInfo("Tetravus", 233, Rarity.RARE, mage.cards.t.Tetravus.class)); - cards.add(new SetCardInfo("Theft of Dreams", 67, Rarity.UNCOMMON, mage.cards.t.TheftOfDreams.class)); - cards.add(new SetCardInfo("Thing from the Deep", 68, Rarity.RARE, mage.cards.t.ThingFromTheDeep.class)); - cards.add(new SetCardInfo("Thunder Dragon", 138, Rarity.RARE, mage.cards.t.ThunderDragon.class)); - cards.add(new SetCardInfo("Time Vault", 234, Rarity.RARE, mage.cards.t.TimeVault.class)); - cards.add(new SetCardInfo("Titania's Song", 170, Rarity.RARE, mage.cards.t.TitaniasSong.class)); - cards.add(new SetCardInfo("Transmute Artifact", 69, Rarity.RARE, mage.cards.t.TransmuteArtifact.class)); - cards.add(new SetCardInfo("Triassic Egg", 235, Rarity.RARE, mage.cards.t.TriassicEgg.class)); - cards.add(new SetCardInfo("Tropical Island", 254, Rarity.RARE, mage.cards.t.TropicalIsland.class)); - cards.add(new SetCardInfo("Tsunami", 171, Rarity.RARE, mage.cards.t.Tsunami.class)); - cards.add(new SetCardInfo("Tundra", 255, Rarity.RARE, mage.cards.t.Tundra.class)); - cards.add(new SetCardInfo("Two-Headed Giant of Foriys", 139, Rarity.UNCOMMON, mage.cards.t.TwoHeadedGiantOfForiys.class)); - cards.add(new SetCardInfo("Underground Sea", 256, Rarity.RARE, mage.cards.u.UndergroundSea.class)); - cards.add(new SetCardInfo("Urza's Chalice", 236, Rarity.COMMON, mage.cards.u.UrzasChalice.class)); - cards.add(new SetCardInfo("Urza's Mine", "257a", Rarity.COMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Mine", "257b", Rarity.COMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Mine", "257c", Rarity.COMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Mine", "257d", Rarity.COMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Miter", 237, Rarity.RARE, mage.cards.u.UrzasMiter.class)); - cards.add(new SetCardInfo("Urza's Power Plant", "258a", Rarity.COMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Power Plant", "258b", Rarity.COMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Power Plant", "258c", Rarity.COMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Power Plant", "258d", Rarity.COMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Tower", "259a", Rarity.COMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Tower", "259b", Rarity.COMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Tower", "259c", Rarity.COMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Urza's Tower", "259d", Rarity.COMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Veteran Bodyguard", 32, Rarity.RARE, mage.cards.v.VeteranBodyguard.class)); - cards.add(new SetCardInfo("Vibrating Sphere", 238, Rarity.RARE, mage.cards.v.VibratingSphere.class)); - cards.add(new SetCardInfo("Volcanic Island", 260, Rarity.RARE, mage.cards.v.VolcanicIsland.class)); - cards.add(new SetCardInfo("War Mammoth", 172, Rarity.COMMON, mage.cards.w.WarMammoth.class)); - cards.add(new SetCardInfo("Warp Artifact", 100, Rarity.COMMON, mage.cards.w.WarpArtifact.class)); - cards.add(new SetCardInfo("Water Elemental", 70, Rarity.UNCOMMON, mage.cards.w.WaterElemental.class)); - cards.add(new SetCardInfo("Weakness", 101, Rarity.COMMON, mage.cards.w.Weakness.class)); - cards.add(new SetCardInfo("Weakstone", 239, Rarity.UNCOMMON, mage.cards.w.Weakstone.class)); - cards.add(new SetCardInfo("Wheel of Fortune", 140, Rarity.RARE, mage.cards.w.WheelOfFortune.class)); - cards.add(new SetCardInfo("Whiptail Wurm", 173, Rarity.UNCOMMON, mage.cards.w.WhiptailWurm.class)); - cards.add(new SetCardInfo("White Knight", 33, Rarity.UNCOMMON, mage.cards.w.WhiteKnight.class)); - cards.add(new SetCardInfo("Wicked Pact", 102, Rarity.UNCOMMON, mage.cards.w.WickedPact.class)); - cards.add(new SetCardInfo("Wild Aesthir", 34, Rarity.COMMON, mage.cards.w.WildAesthir.class)); - cards.add(new SetCardInfo("Wild Griffin", 35, Rarity.COMMON, mage.cards.w.WildGriffin.class)); - cards.add(new SetCardInfo("Wild Ox", 174, Rarity.UNCOMMON, mage.cards.w.WildOx.class)); - cards.add(new SetCardInfo("Wood Elemental", 175, Rarity.RARE, mage.cards.w.WoodElemental.class)); - cards.add(new SetCardInfo("Word of Command", 103, Rarity.RARE, mage.cards.w.WordOfCommand.class)); - cards.add(new SetCardInfo("Xenic Poltergeist", 104, Rarity.UNCOMMON, mage.cards.x.XenicPoltergeist.class)); - cards.add(new SetCardInfo("Yotian Soldier", 240, Rarity.COMMON, mage.cards.y.YotianSoldier.class)); - cards.add(new SetCardInfo("Zombie Master", 105, Rarity.UNCOMMON, mage.cards.z.ZombieMaster.class)); - } - - @Override - public List getSpecialLand() { - // ME4 replace all basic lands with special (1 per booster) - // https://mtg.gamepedia.com/Masters_Edition_IV - - if (savedSpecialLand.isEmpty()) { - savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Mine"))); - savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Power Plant"))); - savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Tower"))); - } - - return new ArrayList<>(savedSpecialLand); - } + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.constants.Rarity; +import mage.constants.SetType; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author LevelX2 + */ +public final class MastersEditionIV extends ExpansionSet { + + private static final MastersEditionIV instance = new MastersEditionIV(); + + public static MastersEditionIV getInstance() { + return instance; + } + + private final List savedSpecialLand = new ArrayList<>(); + + private MastersEditionIV() { + super("Masters Edition IV", "ME4", ExpansionSet.buildDate(2011, 1, 10), SetType.MAGIC_ONLINE); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 1; + this.numBoosterCommon = 10; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + this.ratioBoosterSpecialLand = 1; // replace all basic lands + + cards.add(new SetCardInfo("Acid Rain", 36, Rarity.RARE, mage.cards.a.AcidRain.class)); + cards.add(new SetCardInfo("Aesthir Glider", 176, Rarity.COMMON, mage.cards.a.AesthirGlider.class)); + cards.add(new SetCardInfo("Air Elemental", 37, Rarity.UNCOMMON, mage.cards.a.AirElemental.class)); + cards.add(new SetCardInfo("Al-abara's Carpet", 177, Rarity.RARE, mage.cards.a.AlAbarasCarpet.class)); + cards.add(new SetCardInfo("Alaborn Musketeer", 1, Rarity.COMMON, mage.cards.a.AlabornMusketeer.class)); + cards.add(new SetCardInfo("Alaborn Trooper", 2, Rarity.COMMON, mage.cards.a.AlabornTrooper.class)); + cards.add(new SetCardInfo("Aladdin", 106, Rarity.RARE, mage.cards.a.Aladdin.class)); + cards.add(new SetCardInfo("Alchor's Tomb", 178, Rarity.RARE, mage.cards.a.AlchorsTomb.class)); + cards.add(new SetCardInfo("Ali from Cairo", 107, Rarity.RARE, mage.cards.a.AliFromCairo.class)); + cards.add(new SetCardInfo("Alluring Scent", 141, Rarity.COMMON, mage.cards.a.AlluringScent.class)); + cards.add(new SetCardInfo("Amulet of Kroog", 179, Rarity.COMMON, mage.cards.a.AmuletOfKroog.class)); + cards.add(new SetCardInfo("Angelic Voices", 3, Rarity.UNCOMMON, mage.cards.a.AngelicVoices.class)); + cards.add(new SetCardInfo("Animate Artifact", 38, Rarity.UNCOMMON, mage.cards.a.AnimateArtifact.class)); + cards.add(new SetCardInfo("Argivian Blacksmith", 4, Rarity.UNCOMMON, mage.cards.a.ArgivianBlacksmith.class)); + cards.add(new SetCardInfo("Argothian Pixies", 142, Rarity.COMMON, mage.cards.a.ArgothianPixies.class)); + cards.add(new SetCardInfo("Argothian Treefolk", 143, Rarity.UNCOMMON, mage.cards.a.ArgothianTreefolk.class)); + cards.add(new SetCardInfo("Armageddon Clock", 180, Rarity.RARE, mage.cards.a.ArmageddonClock.class)); + cards.add(new SetCardInfo("Armageddon", 5, Rarity.RARE, mage.cards.a.Armageddon.class)); + cards.add(new SetCardInfo("Artifact Blast", 108, Rarity.COMMON, mage.cards.a.ArtifactBlast.class)); + cards.add(new SetCardInfo("Ashnod's Altar", 181, Rarity.RARE, mage.cards.a.AshnodsAltar.class)); + cards.add(new SetCardInfo("Atog", 109, Rarity.COMMON, mage.cards.a.Atog.class)); + cards.add(new SetCardInfo("Badlands", 241, Rarity.RARE, mage.cards.b.Badlands.class)); + cards.add(new SetCardInfo("Balance", 6, Rarity.RARE, mage.cards.b.Balance.class)); + cards.add(new SetCardInfo("Basalt Monolith", 182, Rarity.UNCOMMON, mage.cards.b.BasaltMonolith.class)); + cards.add(new SetCardInfo("Bayou", 242, Rarity.RARE, mage.cards.b.Bayou.class)); + cards.add(new SetCardInfo("Bee Sting", 144, Rarity.UNCOMMON, mage.cards.b.BeeSting.class)); + cards.add(new SetCardInfo("Bird Maiden", 110, Rarity.COMMON, mage.cards.b.BirdMaiden.class)); + cards.add(new SetCardInfo("Black Knight", 71, Rarity.UNCOMMON, mage.cards.b.BlackKnight.class)); + cards.add(new SetCardInfo("Blaze of Glory", 7, Rarity.UNCOMMON, mage.cards.b.BlazeOfGlory.class)); + cards.add(new SetCardInfo("Blue Elemental Blast", 39, Rarity.UNCOMMON, mage.cards.b.BlueElementalBlast.class)); + cards.add(new SetCardInfo("Book of Rass", 183, Rarity.UNCOMMON, mage.cards.b.BookOfRass.class)); + cards.add(new SetCardInfo("Bottle of Suleiman", 184, Rarity.RARE, mage.cards.b.BottleOfSuleiman.class)); + cards.add(new SetCardInfo("Braingeyser", 40, Rarity.RARE, mage.cards.b.Braingeyser.class)); + cards.add(new SetCardInfo("Brass Man", 185, Rarity.COMMON, mage.cards.b.BrassMan.class)); + cards.add(new SetCardInfo("Candelabra of Tawnos", 187, Rarity.RARE, mage.cards.c.CandelabraOfTawnos.class)); + cards.add(new SetCardInfo("Celestial Sword", 188, Rarity.UNCOMMON, mage.cards.c.CelestialSword.class)); + cards.add(new SetCardInfo("Champion Lancer", 8, Rarity.RARE, mage.cards.c.ChampionLancer.class)); + cards.add(new SetCardInfo("Channel", 145, Rarity.RARE, mage.cards.c.Channel.class)); + cards.add(new SetCardInfo("Citanul Druid", 146, Rarity.COMMON, mage.cards.c.CitanulDruid.class)); + cards.add(new SetCardInfo("City of Brass", 243, Rarity.RARE, mage.cards.c.CityOfBrass.class)); + cards.add(new SetCardInfo("Clay Statue", 189, Rarity.UNCOMMON, mage.cards.c.ClayStatue.class)); + cards.add(new SetCardInfo("Clockwork Avian", 190, Rarity.UNCOMMON, mage.cards.c.ClockworkAvian.class)); + cards.add(new SetCardInfo("Clockwork Gnomes", 191, Rarity.UNCOMMON, mage.cards.c.ClockworkGnomes.class)); + cards.add(new SetCardInfo("Clockwork Swarm", 192, Rarity.COMMON, mage.cards.c.ClockworkSwarm.class)); + cards.add(new SetCardInfo("Cloud Dragon", 41, Rarity.RARE, mage.cards.c.CloudDragon.class)); + cards.add(new SetCardInfo("Cloud Spirit", 42, Rarity.COMMON, mage.cards.c.CloudSpirit.class)); + cards.add(new SetCardInfo("Colossus of Sardia", 193, Rarity.RARE, mage.cards.c.ColossusOfSardia.class)); + cards.add(new SetCardInfo("Control Magic", 43, Rarity.RARE, mage.cards.c.ControlMagic.class)); + cards.add(new SetCardInfo("Conversion", 9, Rarity.RARE, mage.cards.c.Conversion.class)); + cards.add(new SetCardInfo("Copy Artifact", 44, Rarity.RARE, mage.cards.c.CopyArtifact.class)); + cards.add(new SetCardInfo("Coral Helm", 194, Rarity.UNCOMMON, mage.cards.c.CoralHelm.class)); + cards.add(new SetCardInfo("Counterspell", 45, Rarity.COMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Crumble", 147, Rarity.COMMON, mage.cards.c.Crumble.class)); + cards.add(new SetCardInfo("Cyclone", 148, Rarity.RARE, mage.cards.c.Cyclone.class)); + cards.add(new SetCardInfo("Cyclopean Mummy", 72, Rarity.COMMON, mage.cards.c.CyclopeanMummy.class)); + cards.add(new SetCardInfo("Cyclopean Tomb", 195, Rarity.RARE, mage.cards.c.CyclopeanTomb.class)); + cards.add(new SetCardInfo("Dakmor Plague", 73, Rarity.UNCOMMON, mage.cards.d.DakmorPlague.class)); + cards.add(new SetCardInfo("Dark Ritual", 74, Rarity.COMMON, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Deathcoil Wurm", 149, Rarity.RARE, mage.cards.d.DeathcoilWurm.class)); + cards.add(new SetCardInfo("Deathgrip", 75, Rarity.RARE, mage.cards.d.Deathgrip.class)); + cards.add(new SetCardInfo("Demonic Hordes", 76, Rarity.RARE, mage.cards.d.DemonicHordes.class)); + cards.add(new SetCardInfo("Demonic Tutor", 77, Rarity.RARE, mage.cards.d.DemonicTutor.class)); + cards.add(new SetCardInfo("Detonate", 111, Rarity.UNCOMMON, mage.cards.d.Detonate.class)); + cards.add(new SetCardInfo("Devastation", 112, Rarity.RARE, mage.cards.d.Devastation.class)); + cards.add(new SetCardInfo("Diabolic Machine", 196, Rarity.UNCOMMON, mage.cards.d.DiabolicMachine.class)); + cards.add(new SetCardInfo("Divine Offering", 10, Rarity.COMMON, mage.cards.d.DivineOffering.class)); + cards.add(new SetCardInfo("Dragon Engine", 197, Rarity.COMMON, mage.cards.d.DragonEngine.class)); + cards.add(new SetCardInfo("Drain Power", 46, Rarity.RARE, mage.cards.d.DrainPower.class)); + cards.add(new SetCardInfo("Dread Reaper", 78, Rarity.RARE, mage.cards.d.DreadReaper.class)); + cards.add(new SetCardInfo("Dread Wight", 79, Rarity.UNCOMMON, mage.cards.d.DreadWight.class)); + cards.add(new SetCardInfo("Drop of Honey", 150, Rarity.RARE, mage.cards.d.DropOfHoney.class)); + cards.add(new SetCardInfo("Drowned", 47, Rarity.COMMON, mage.cards.d.Drowned.class)); + cards.add(new SetCardInfo("Dust to Dust", 11, Rarity.UNCOMMON, mage.cards.d.DustToDust.class)); + cards.add(new SetCardInfo("Ebon Dragon", 80, Rarity.RARE, mage.cards.e.EbonDragon.class)); + cards.add(new SetCardInfo("Ebony Horse", 198, Rarity.COMMON, mage.cards.e.EbonyHorse.class)); + cards.add(new SetCardInfo("Ebony Rhino", 199, Rarity.COMMON, mage.cards.e.EbonyRhino.class)); + cards.add(new SetCardInfo("Elephant Graveyard", 244, Rarity.UNCOMMON, mage.cards.e.ElephantGraveyard.class)); + cards.add(new SetCardInfo("Elite Cat Warrior", 151, Rarity.COMMON, mage.cards.e.EliteCatWarrior.class)); + cards.add(new SetCardInfo("Energy Flux", 48, Rarity.UNCOMMON, mage.cards.e.EnergyFlux.class)); + cards.add(new SetCardInfo("Eye for an Eye", 12, Rarity.RARE, mage.cards.e.EyeForAnEye.class)); + cards.add(new SetCardInfo("False Summoning", 49, Rarity.COMMON, mage.cards.f.FalseSummoning.class)); + cards.add(new SetCardInfo("Fastbond", 152, Rarity.RARE, mage.cards.f.Fastbond.class)); + cards.add(new SetCardInfo("Fire Imp", 113, Rarity.UNCOMMON, mage.cards.f.FireImp.class)); + cards.add(new SetCardInfo("Fire Tempest", 114, Rarity.RARE, mage.cards.f.FireTempest.class)); + cards.add(new SetCardInfo("Fireball", 115, Rarity.UNCOMMON, mage.cards.f.Fireball.class)); + cards.add(new SetCardInfo("Floodwater Dam", 200, Rarity.RARE, mage.cards.f.FloodwaterDam.class)); + cards.add(new SetCardInfo("Flying Carpet", 201, Rarity.COMMON, mage.cards.f.FlyingCarpet.class)); + cards.add(new SetCardInfo("Fog", 153, Rarity.COMMON, mage.cards.f.Fog.class)); + cards.add(new SetCardInfo("Force of Nature", 154, Rarity.RARE, mage.cards.f.ForceOfNature.class)); + cards.add(new SetCardInfo("Fork", 116, Rarity.RARE, mage.cards.f.Fork.class)); + cards.add(new SetCardInfo("Foul Spirit", 81, Rarity.COMMON, mage.cards.f.FoulSpirit.class)); + cards.add(new SetCardInfo("Gaea's Avenger", 155, Rarity.UNCOMMON, mage.cards.g.GaeasAvenger.class)); + cards.add(new SetCardInfo("Gate to Phyrexia", 82, Rarity.UNCOMMON, mage.cards.g.GateToPhyrexia.class)); + cards.add(new SetCardInfo("Gauntlet of Might", 202, Rarity.RARE, mage.cards.g.GauntletOfMight.class)); + cards.add(new SetCardInfo("Giant Growth", 156, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); + cards.add(new SetCardInfo("Giant Tortoise", 50, Rarity.COMMON, mage.cards.g.GiantTortoise.class)); + cards.add(new SetCardInfo("Glasses of Urza", 203, Rarity.COMMON, mage.cards.g.GlassesOfUrza.class)); + cards.add(new SetCardInfo("Gloom", 83, Rarity.RARE, mage.cards.g.Gloom.class)); + cards.add(new SetCardInfo("Goblin Bully", 117, Rarity.COMMON, mage.cards.g.GoblinBully.class)); + cards.add(new SetCardInfo("Goblin Cavaliers", 118, Rarity.COMMON, mage.cards.g.GoblinCavaliers.class)); + cards.add(new SetCardInfo("Goblin Caves", 119, Rarity.COMMON, mage.cards.g.GoblinCaves.class)); + cards.add(new SetCardInfo("Goblin Firestarter", 120, Rarity.COMMON, mage.cards.g.GoblinFirestarter.class)); + cards.add(new SetCardInfo("Goblin General", 121, Rarity.UNCOMMON, mage.cards.g.GoblinGeneral.class)); + cards.add(new SetCardInfo("Goblin Shrine", 122, Rarity.COMMON, mage.cards.g.GoblinShrine.class)); + cards.add(new SetCardInfo("Goblin Warrens", 123, Rarity.UNCOMMON, mage.cards.g.GoblinWarrens.class)); + cards.add(new SetCardInfo("Gorilla War Cry", 124, Rarity.COMMON, mage.cards.g.GorillaWarCry.class)); + cards.add(new SetCardInfo("Grapeshot Catapult", 204, Rarity.UNCOMMON, mage.cards.g.GrapeshotCatapult.class)); + cards.add(new SetCardInfo("Gravebind", 84, Rarity.COMMON, mage.cards.g.Gravebind.class)); + cards.add(new SetCardInfo("Guardian Beast", 85, Rarity.RARE, mage.cards.g.GuardianBeast.class)); + cards.add(new SetCardInfo("Harsh Justice", 13, Rarity.RARE, mage.cards.h.HarshJustice.class)); + cards.add(new SetCardInfo("Hasran Ogress", 86, Rarity.COMMON, mage.cards.h.HasranOgress.class)); + cards.add(new SetCardInfo("Healing Salve", 14, Rarity.COMMON, mage.cards.h.HealingSalve.class)); + cards.add(new SetCardInfo("Horn of Deafening", 205, Rarity.UNCOMMON, mage.cards.h.HornOfDeafening.class)); + cards.add(new SetCardInfo("Howl from Beyond", 87, Rarity.COMMON, mage.cards.h.HowlFromBeyond.class)); + cards.add(new SetCardInfo("Ice Cauldron", 206, Rarity.RARE, mage.cards.i.IceCauldron.class)); + cards.add(new SetCardInfo("Icy Manipulator", 207, Rarity.UNCOMMON, mage.cards.i.IcyManipulator.class)); + cards.add(new SetCardInfo("In the Eye of Chaos", 51, Rarity.RARE, mage.cards.i.InTheEyeOfChaos.class)); + cards.add(new SetCardInfo("Instill Energy", 157, Rarity.UNCOMMON, mage.cards.i.InstillEnergy.class)); + cards.add(new SetCardInfo("Ironhoof Ox", 158, Rarity.COMMON, mage.cards.i.IronhoofOx.class)); + cards.add(new SetCardInfo("Island Sanctuary", 15, Rarity.RARE, mage.cards.i.IslandSanctuary.class)); + cards.add(new SetCardInfo("Jade Monolith", 208, Rarity.RARE, mage.cards.j.JadeMonolith.class)); + cards.add(new SetCardInfo("Juggernaut", 209, Rarity.UNCOMMON, mage.cards.j.Juggernaut.class)); + cards.add(new SetCardInfo("Junun Efreet", 88, Rarity.UNCOMMON, mage.cards.j.JununEfreet.class)); + cards.add(new SetCardInfo("Just Fate", 16, Rarity.COMMON, mage.cards.j.JustFate.class)); + cards.add(new SetCardInfo("Kismet", 17, Rarity.RARE, mage.cards.k.Kismet.class)); + cards.add(new SetCardInfo("Kormus Bell", 210, Rarity.RARE, mage.cards.k.KormusBell.class)); + cards.add(new SetCardInfo("Kudzu", 159, Rarity.UNCOMMON, mage.cards.k.Kudzu.class)); + cards.add(new SetCardInfo("Last Chance", 125, Rarity.RARE, mage.cards.l.LastChance.class)); + cards.add(new SetCardInfo("Lava Flow", 126, Rarity.COMMON, mage.cards.l.LavaFlow.class)); + cards.add(new SetCardInfo("Leeches", 18, Rarity.RARE, mage.cards.l.Leeches.class)); + cards.add(new SetCardInfo("Library of Alexandria", 245, Rarity.RARE, mage.cards.l.LibraryOfAlexandria.class)); + cards.add(new SetCardInfo("Library of Leng", 211, Rarity.COMMON, mage.cards.l.LibraryOfLeng.class)); + cards.add(new SetCardInfo("Lich", 89, Rarity.RARE, mage.cards.l.Lich.class)); + cards.add(new SetCardInfo("Lifeforce", 160, Rarity.RARE, mage.cards.l.Lifeforce.class)); + cards.add(new SetCardInfo("Lim-Dul's Cohort", 90, Rarity.COMMON, mage.cards.l.LimDulsCohort.class)); + cards.add(new SetCardInfo("Living Lands", 161, Rarity.RARE, mage.cards.l.LivingLands.class)); + cards.add(new SetCardInfo("Living Wall", 212, Rarity.UNCOMMON, mage.cards.l.LivingWall.class)); + cards.add(new SetCardInfo("Mahamoti Djinn", 52, Rarity.RARE, mage.cards.m.MahamotiDjinn.class)); + cards.add(new SetCardInfo("Mana Matrix", 213, Rarity.RARE, mage.cards.m.ManaMatrix.class)); + cards.add(new SetCardInfo("Mana Vault", 214, Rarity.RARE, mage.cards.m.ManaVault.class)); + cards.add(new SetCardInfo("Martyr's Cry", 19, Rarity.RARE, mage.cards.m.MartyrsCry.class)); + cards.add(new SetCardInfo("Martyrs of Korlis", 20, Rarity.UNCOMMON, mage.cards.m.MartyrsOfKorlis.class)); + cards.add(new SetCardInfo("Maze of Ith", 246, Rarity.RARE, mage.cards.m.MazeOfIth.class)); + cards.add(new SetCardInfo("Mightstone", 215, Rarity.COMMON, mage.cards.m.Mightstone.class)); + cards.add(new SetCardInfo("Mijae Djinn", 127, Rarity.RARE, mage.cards.m.MijaeDjinn.class)); + cards.add(new SetCardInfo("Minion of Tevesh Szat", 91, Rarity.RARE, mage.cards.m.MinionOfTeveshSzat.class)); + cards.add(new SetCardInfo("Mishra's Workshop", 247, Rarity.RARE, mage.cards.m.MishrasWorkshop.class)); + cards.add(new SetCardInfo("Mystic Decree", 53, Rarity.UNCOMMON, mage.cards.m.MysticDecree.class)); + cards.add(new SetCardInfo("Naked Singularity", 216, Rarity.RARE, mage.cards.n.NakedSingularity.class)); + cards.add(new SetCardInfo("Oasis", 248, Rarity.COMMON, mage.cards.o.Oasis.class)); + cards.add(new SetCardInfo("Obelisk of Undoing", 217, Rarity.RARE, mage.cards.o.ObeliskOfUndoing.class)); + cards.add(new SetCardInfo("Obsianus Golem", 218, Rarity.COMMON, mage.cards.o.ObsianusGolem.class)); + cards.add(new SetCardInfo("Ogre Taskmaster", 128, Rarity.COMMON, mage.cards.o.OgreTaskmaster.class)); + cards.add(new SetCardInfo("Onulet", 219, Rarity.COMMON, mage.cards.o.Onulet.class)); + cards.add(new SetCardInfo("Orcish Mechanics", 129, Rarity.UNCOMMON, mage.cards.o.OrcishMechanics.class)); + cards.add(new SetCardInfo("Osai Vultures", 21, Rarity.COMMON, mage.cards.o.OsaiVultures.class)); + cards.add(new SetCardInfo("Overwhelming Forces", 92, Rarity.RARE, mage.cards.o.OverwhelmingForces.class)); + cards.add(new SetCardInfo("Owl Familiar", 54, Rarity.COMMON, mage.cards.o.OwlFamiliar.class)); + cards.add(new SetCardInfo("Pentagram of the Ages", 220, Rarity.UNCOMMON, mage.cards.p.PentagramOfTheAges.class)); + cards.add(new SetCardInfo("Personal Incarnation", 22, Rarity.RARE, mage.cards.p.PersonalIncarnation.class)); + cards.add(new SetCardInfo("Phantasmal Forces", 55, Rarity.COMMON, mage.cards.p.PhantasmalForces.class)); + cards.add(new SetCardInfo("Phantasmal Terrain", 56, Rarity.COMMON, mage.cards.p.PhantasmalTerrain.class)); + cards.add(new SetCardInfo("Planar Gate", 221, Rarity.UNCOMMON, mage.cards.p.PlanarGate.class)); + cards.add(new SetCardInfo("Plateau", 249, Rarity.RARE, mage.cards.p.Plateau.class)); + cards.add(new SetCardInfo("Power Artifact", 57, Rarity.RARE, mage.cards.p.PowerArtifact.class)); + cards.add(new SetCardInfo("Primal Clay", 222, Rarity.COMMON, mage.cards.p.PrimalClay.class)); + cards.add(new SetCardInfo("Prodigal Sorcerer", 58, Rarity.UNCOMMON, mage.cards.p.ProdigalSorcerer.class)); + cards.add(new SetCardInfo("Prowling Nightstalker", 93, Rarity.COMMON, mage.cards.p.ProwlingNightstalker.class)); + cards.add(new SetCardInfo("Radjan Spirit", 162, Rarity.UNCOMMON, mage.cards.r.RadjanSpirit.class)); + cards.add(new SetCardInfo("Rain of Daggers", 94, Rarity.RARE, mage.cards.r.RainOfDaggers.class)); + cards.add(new SetCardInfo("Rakalite", 223, Rarity.RARE, mage.cards.r.Rakalite.class)); + cards.add(new SetCardInfo("Reconstruction", 59, Rarity.COMMON, mage.cards.r.Reconstruction.class)); + cards.add(new SetCardInfo("Red Elemental Blast", 131, Rarity.UNCOMMON, mage.cards.r.RedElementalBlast.class)); + cards.add(new SetCardInfo("Regrowth", 163, Rarity.RARE, mage.cards.r.Regrowth.class)); + cards.add(new SetCardInfo("Righteous Charge", 23, Rarity.COMMON, mage.cards.r.RighteousCharge.class)); + cards.add(new SetCardInfo("Ring of Renewal", 224, Rarity.RARE, mage.cards.r.RingOfRenewal.class)); + cards.add(new SetCardInfo("Roc of Kher Ridges", 132, Rarity.UNCOMMON, mage.cards.r.RocOfKherRidges.class)); + cards.add(new SetCardInfo("Rock Hydra", 133, Rarity.RARE, mage.cards.r.RockHydra.class)); + cards.add(new SetCardInfo("Rockslide Ambush", 134, Rarity.COMMON, mage.cards.r.RockslideAmbush.class)); + cards.add(new SetCardInfo("Sandstorm", 164, Rarity.COMMON, mage.cards.s.Sandstorm.class)); + cards.add(new SetCardInfo("Savannah Lions", 24, Rarity.UNCOMMON, mage.cards.s.SavannahLions.class)); + cards.add(new SetCardInfo("Savannah", 250, Rarity.RARE, mage.cards.s.Savannah.class)); + cards.add(new SetCardInfo("Scarecrow", 225, Rarity.UNCOMMON, mage.cards.s.Scarecrow.class)); + cards.add(new SetCardInfo("Scarwood Bandits", 165, Rarity.RARE, mage.cards.s.ScarwoodBandits.class)); + cards.add(new SetCardInfo("Scavenger Folk", 166, Rarity.COMMON, mage.cards.s.ScavengerFolk.class)); + cards.add(new SetCardInfo("Scavenging Ghoul", 95, Rarity.UNCOMMON, mage.cards.s.ScavengingGhoul.class)); + cards.add(new SetCardInfo("Scrubland", 251, Rarity.RARE, mage.cards.s.Scrubland.class)); + cards.add(new SetCardInfo("Sea Serpent", 60, Rarity.COMMON, mage.cards.s.SeaSerpent.class)); + cards.add(new SetCardInfo("Sedge Troll", 135, Rarity.RARE, mage.cards.s.SedgeTroll.class)); + cards.add(new SetCardInfo("Sengir Vampire", 96, Rarity.UNCOMMON, mage.cards.s.SengirVampire.class)); + cards.add(new SetCardInfo("Serendib Djinn", 61, Rarity.RARE, mage.cards.s.SerendibDjinn.class)); + cards.add(new SetCardInfo("Serra Angel", 25, Rarity.UNCOMMON, mage.cards.s.SerraAngel.class)); + cards.add(new SetCardInfo("Serra Aviary", 26, Rarity.UNCOMMON, mage.cards.s.SerraAviary.class)); + cards.add(new SetCardInfo("Serra Bestiary", 27, Rarity.COMMON, mage.cards.s.SerraBestiary.class)); + cards.add(new SetCardInfo("Shapeshifter", 226, Rarity.UNCOMMON, mage.cards.s.Shapeshifter.class)); + cards.add(new SetCardInfo("Shivan Dragon", 136, Rarity.RARE, mage.cards.s.ShivanDragon.class)); + cards.add(new SetCardInfo("Sinkhole", 97, Rarity.RARE, mage.cards.s.Sinkhole.class)); + cards.add(new SetCardInfo("Sleight of Hand", 62, Rarity.COMMON, mage.cards.s.SleightOfHand.class)); + cards.add(new SetCardInfo("Smoke", 137, Rarity.RARE, mage.cards.s.Smoke.class)); + cards.add(new SetCardInfo("Sol Ring", 227, Rarity.RARE, mage.cards.s.SolRing.class)); + cards.add(new SetCardInfo("Soldevi Golem", 228, Rarity.UNCOMMON, mage.cards.s.SoldeviGolem.class)); + cards.add(new SetCardInfo("Soldevi Machinist", 63, Rarity.UNCOMMON, mage.cards.s.SoldeviMachinist.class)); + cards.add(new SetCardInfo("Soul Shred", 98, Rarity.COMMON, mage.cards.s.SoulShred.class)); + cards.add(new SetCardInfo("Southern Elephant", 167, Rarity.COMMON, mage.cards.s.SouthernElephant.class)); + cards.add(new SetCardInfo("Spotted Griffin", 28, Rarity.COMMON, mage.cards.s.SpottedGriffin.class)); + cards.add(new SetCardInfo("Squall", 168, Rarity.UNCOMMON, mage.cards.s.Squall.class)); + cards.add(new SetCardInfo("Staff of Zegon", 229, Rarity.COMMON, mage.cards.s.StaffOfZegon.class)); + cards.add(new SetCardInfo("Stasis", 64, Rarity.RARE, mage.cards.s.Stasis.class)); + cards.add(new SetCardInfo("Steam Catapult", 29, Rarity.RARE, mage.cards.s.SteamCatapult.class)); + cards.add(new SetCardInfo("Strip Mine", 252, Rarity.RARE, mage.cards.s.StripMine.class)); + cards.add(new SetCardInfo("Swords to Plowshares", 30, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("Sylvan Tutor", 169, Rarity.UNCOMMON, mage.cards.s.SylvanTutor.class)); + cards.add(new SetCardInfo("Symbol of Unsummoning", 65, Rarity.COMMON, mage.cards.s.SymbolOfUnsummoning.class)); + cards.add(new SetCardInfo("Tablet of Epityr", 230, Rarity.COMMON, mage.cards.t.TabletOfEpityr.class)); + cards.add(new SetCardInfo("Taiga", 253, Rarity.RARE, mage.cards.t.Taiga.class)); + cards.add(new SetCardInfo("Talas Researcher", 66, Rarity.UNCOMMON, mage.cards.t.TalasResearcher.class)); + cards.add(new SetCardInfo("Tawnos's Wand", 231, Rarity.COMMON, mage.cards.t.TawnossWand.class)); + cards.add(new SetCardInfo("Tawnos's Weaponry", 232, Rarity.UNCOMMON, mage.cards.t.TawnossWeaponry.class)); + cards.add(new SetCardInfo("Temple Acolyte", 31, Rarity.COMMON, mage.cards.t.TempleAcolyte.class)); + cards.add(new SetCardInfo("Terror", 99, Rarity.COMMON, mage.cards.t.Terror.class)); + cards.add(new SetCardInfo("Tetravus", 233, Rarity.RARE, mage.cards.t.Tetravus.class)); + cards.add(new SetCardInfo("Theft of Dreams", 67, Rarity.UNCOMMON, mage.cards.t.TheftOfDreams.class)); + cards.add(new SetCardInfo("Thing from the Deep", 68, Rarity.RARE, mage.cards.t.ThingFromTheDeep.class)); + cards.add(new SetCardInfo("Thunder Dragon", 138, Rarity.RARE, mage.cards.t.ThunderDragon.class)); + cards.add(new SetCardInfo("Time Vault", 234, Rarity.RARE, mage.cards.t.TimeVault.class)); + cards.add(new SetCardInfo("Titania's Song", 170, Rarity.RARE, mage.cards.t.TitaniasSong.class)); + cards.add(new SetCardInfo("Transmute Artifact", 69, Rarity.RARE, mage.cards.t.TransmuteArtifact.class)); + cards.add(new SetCardInfo("Triassic Egg", 235, Rarity.RARE, mage.cards.t.TriassicEgg.class)); + cards.add(new SetCardInfo("Tropical Island", 254, Rarity.RARE, mage.cards.t.TropicalIsland.class)); + cards.add(new SetCardInfo("Tsunami", 171, Rarity.RARE, mage.cards.t.Tsunami.class)); + cards.add(new SetCardInfo("Tundra", 255, Rarity.RARE, mage.cards.t.Tundra.class)); + cards.add(new SetCardInfo("Two-Headed Giant of Foriys", 139, Rarity.UNCOMMON, mage.cards.t.TwoHeadedGiantOfForiys.class)); + cards.add(new SetCardInfo("Underground Sea", 256, Rarity.RARE, mage.cards.u.UndergroundSea.class)); + cards.add(new SetCardInfo("Urza's Chalice", 236, Rarity.COMMON, mage.cards.u.UrzasChalice.class)); + cards.add(new SetCardInfo("Urza's Mine", "257a", Rarity.COMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Mine", "257b", Rarity.COMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Mine", "257c", Rarity.COMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Mine", "257d", Rarity.COMMON, mage.cards.u.UrzasMine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Miter", 237, Rarity.RARE, mage.cards.u.UrzasMiter.class)); + cards.add(new SetCardInfo("Urza's Power Plant", "258a", Rarity.COMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Power Plant", "258b", Rarity.COMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Power Plant", "258c", Rarity.COMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Power Plant", "258d", Rarity.COMMON, mage.cards.u.UrzasPowerPlant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Tower", "259a", Rarity.COMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Tower", "259b", Rarity.COMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Tower", "259c", Rarity.COMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urza's Tower", "259d", Rarity.COMMON, mage.cards.u.UrzasTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Veteran Bodyguard", 32, Rarity.RARE, mage.cards.v.VeteranBodyguard.class)); + cards.add(new SetCardInfo("Vibrating Sphere", 238, Rarity.RARE, mage.cards.v.VibratingSphere.class)); + cards.add(new SetCardInfo("Volcanic Island", 260, Rarity.RARE, mage.cards.v.VolcanicIsland.class)); + cards.add(new SetCardInfo("War Mammoth", 172, Rarity.COMMON, mage.cards.w.WarMammoth.class)); + cards.add(new SetCardInfo("Warp Artifact", 100, Rarity.COMMON, mage.cards.w.WarpArtifact.class)); + cards.add(new SetCardInfo("Water Elemental", 70, Rarity.UNCOMMON, mage.cards.w.WaterElemental.class)); + cards.add(new SetCardInfo("Weakness", 101, Rarity.COMMON, mage.cards.w.Weakness.class)); + cards.add(new SetCardInfo("Weakstone", 239, Rarity.UNCOMMON, mage.cards.w.Weakstone.class)); + cards.add(new SetCardInfo("Wheel of Fortune", 140, Rarity.RARE, mage.cards.w.WheelOfFortune.class)); + cards.add(new SetCardInfo("Whiptail Wurm", 173, Rarity.UNCOMMON, mage.cards.w.WhiptailWurm.class)); + cards.add(new SetCardInfo("White Knight", 33, Rarity.UNCOMMON, mage.cards.w.WhiteKnight.class)); + cards.add(new SetCardInfo("Wicked Pact", 102, Rarity.UNCOMMON, mage.cards.w.WickedPact.class)); + cards.add(new SetCardInfo("Wild Aesthir", 34, Rarity.COMMON, mage.cards.w.WildAesthir.class)); + cards.add(new SetCardInfo("Wild Griffin", 35, Rarity.COMMON, mage.cards.w.WildGriffin.class)); + cards.add(new SetCardInfo("Wild Ox", 174, Rarity.UNCOMMON, mage.cards.w.WildOx.class)); + cards.add(new SetCardInfo("Wood Elemental", 175, Rarity.RARE, mage.cards.w.WoodElemental.class)); + cards.add(new SetCardInfo("Word of Command", 103, Rarity.RARE, mage.cards.w.WordOfCommand.class)); + cards.add(new SetCardInfo("Xenic Poltergeist", 104, Rarity.UNCOMMON, mage.cards.x.XenicPoltergeist.class)); + cards.add(new SetCardInfo("Yotian Soldier", 240, Rarity.COMMON, mage.cards.y.YotianSoldier.class)); + cards.add(new SetCardInfo("Zombie Master", 105, Rarity.UNCOMMON, mage.cards.z.ZombieMaster.class)); + } + + @Override + public List getSpecialLand() { + // ME4 replace all basic lands with special (1 per booster) + // https://mtg.gamepedia.com/Masters_Edition_IV + + if (savedSpecialLand.isEmpty()) { + savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Mine"))); + savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Power Plant"))); + savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Tower"))); + } + + return new ArrayList<>(savedSpecialLand); + } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/MercadianMasques.java b/Mage.Sets/src/mage/sets/MercadianMasques.java index 6e57ee2572..bdd6a8f209 100644 --- a/Mage.Sets/src/mage/sets/MercadianMasques.java +++ b/Mage.Sets/src/mage/sets/MercadianMasques.java @@ -83,6 +83,7 @@ public final class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Collective Unconscious", 236, Rarity.RARE, mage.cards.c.CollectiveUnconscious.class)); cards.add(new SetCardInfo("Common Cause", 13, Rarity.RARE, mage.cards.c.CommonCause.class)); cards.add(new SetCardInfo("Conspiracy", 127, Rarity.RARE, mage.cards.c.Conspiracy.class)); + cards.add(new SetCardInfo("Cornered Market", 14, Rarity.RARE, mage.cards.c.CorneredMarket.class)); cards.add(new SetCardInfo("Corrupt Official", 128, Rarity.RARE, mage.cards.c.CorruptOfficial.class)); cards.add(new SetCardInfo("Counterspell", 69, Rarity.COMMON, mage.cards.c.Counterspell.class)); cards.add(new SetCardInfo("Cowardice", 70, Rarity.RARE, mage.cards.c.Cowardice.class)); diff --git a/Mage.Sets/src/mage/sets/Mirage.java b/Mage.Sets/src/mage/sets/Mirage.java index 9fa3fd6af0..1fdb30ed7b 100644 --- a/Mage.Sets/src/mage/sets/Mirage.java +++ b/Mage.Sets/src/mage/sets/Mirage.java @@ -1,350 +1,350 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * @author North - */ -public final class Mirage extends ExpansionSet { - - private static final Mirage instance = new Mirage(); - - public static Mirage getInstance() { - return instance; - } - - private Mirage() { - super("Mirage", "MIR", ExpansionSet.buildDate(1996, 8, 21), SetType.EXPANSION); - this.blockName = "Mirage"; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - - cards.add(new SetCardInfo("Abyssal Hunter", 103, Rarity.RARE, mage.cards.a.AbyssalHunter.class)); - cards.add(new SetCardInfo("Afiya Grove", 205, Rarity.RARE, mage.cards.a.AfiyaGrove.class)); - cards.add(new SetCardInfo("Afterlife", 1, Rarity.UNCOMMON, mage.cards.a.Afterlife.class)); - cards.add(new SetCardInfo("Agility", 154, Rarity.COMMON, mage.cards.a.Agility.class)); - cards.add(new SetCardInfo("Alarum", 2, Rarity.COMMON, mage.cards.a.Alarum.class)); - cards.add(new SetCardInfo("Aleatory", 155, Rarity.UNCOMMON, mage.cards.a.Aleatory.class)); - cards.add(new SetCardInfo("Amber Prison", 292, Rarity.RARE, mage.cards.a.AmberPrison.class)); - cards.add(new SetCardInfo("Amulet of Unmaking", 293, Rarity.RARE, mage.cards.a.AmuletOfUnmaking.class)); - cards.add(new SetCardInfo("Ancestral Memories", 52, Rarity.RARE, mage.cards.a.AncestralMemories.class)); - cards.add(new SetCardInfo("Armor of Thorns", 206, Rarity.COMMON, mage.cards.a.ArmorOfThorns.class)); - cards.add(new SetCardInfo("Armorer Guildmage", 156, Rarity.COMMON, mage.cards.a.ArmorerGuildmage.class)); - cards.add(new SetCardInfo("Ashen Powder", 104, Rarity.RARE, mage.cards.a.AshenPowder.class)); - cards.add(new SetCardInfo("Asmira, Holy Avenger", 256, Rarity.RARE, mage.cards.a.AsmiraHolyAvenger.class)); - cards.add(new SetCardInfo("Auspicious Ancestor", 3, Rarity.RARE, mage.cards.a.AuspiciousAncestor.class)); - cards.add(new SetCardInfo("Azimaet Drake", 53, Rarity.COMMON, mage.cards.a.AzimaetDrake.class)); - cards.add(new SetCardInfo("Bad River", 324, Rarity.UNCOMMON, mage.cards.b.BadRiver.class)); - cards.add(new SetCardInfo("Barbed Foliage", 207, Rarity.UNCOMMON, mage.cards.b.BarbedFoliage.class)); - cards.add(new SetCardInfo("Barbed-Back Wurm", 105, Rarity.UNCOMMON, mage.cards.b.BarbedBackWurm.class)); - cards.add(new SetCardInfo("Bay Falcon", 54, Rarity.COMMON, mage.cards.b.BayFalcon.class)); - cards.add(new SetCardInfo("Bazaar of Wonders", 55, Rarity.RARE, mage.cards.b.BazaarOfWonders.class)); - cards.add(new SetCardInfo("Benevolent Unicorn", 4, Rarity.COMMON, mage.cards.b.BenevolentUnicorn.class)); - cards.add(new SetCardInfo("Benthic Djinn", 257, Rarity.RARE, mage.cards.b.BenthicDjinn.class)); - cards.add(new SetCardInfo("Binding Agony", 106, Rarity.COMMON, mage.cards.b.BindingAgony.class)); - cards.add(new SetCardInfo("Blighted Shaman", 107, Rarity.UNCOMMON, mage.cards.b.BlightedShaman.class)); - cards.add(new SetCardInfo("Blind Fury", 158, Rarity.UNCOMMON, mage.cards.b.BlindFury.class)); - cards.add(new SetCardInfo("Blinding Light", 5, Rarity.UNCOMMON, mage.cards.b.BlindingLight.class)); - cards.add(new SetCardInfo("Blistering Barrier", 159, Rarity.COMMON, mage.cards.b.BlisteringBarrier.class)); - cards.add(new SetCardInfo("Bone Harvest", 108, Rarity.COMMON, mage.cards.b.BoneHarvest.class)); - cards.add(new SetCardInfo("Bone Mask", 295, Rarity.RARE, mage.cards.b.BoneMask.class)); - cards.add(new SetCardInfo("Boomerang", 56, Rarity.COMMON, mage.cards.b.Boomerang.class)); - cards.add(new SetCardInfo("Breathstealer", 109, Rarity.COMMON, mage.cards.b.Breathstealer.class)); - cards.add(new SetCardInfo("Brushwagg", 208, Rarity.RARE, mage.cards.b.Brushwagg.class)); - cards.add(new SetCardInfo("Builder's Bane", 160, Rarity.COMMON, mage.cards.b.BuildersBane.class)); - cards.add(new SetCardInfo("Burning Palm Efreet", 161, Rarity.UNCOMMON, mage.cards.b.BurningPalmEfreet.class)); - cards.add(new SetCardInfo("Burning Shield Askari", 162, Rarity.COMMON, mage.cards.b.BurningShieldAskari.class)); - cards.add(new SetCardInfo("Cadaverous Bloom", 258, Rarity.RARE, mage.cards.c.CadaverousBloom.class)); - cards.add(new SetCardInfo("Cadaverous Knight", 110, Rarity.COMMON, mage.cards.c.CadaverousKnight.class)); - cards.add(new SetCardInfo("Canopy Dragon", 209, Rarity.RARE, mage.cards.c.CanopyDragon.class)); - cards.add(new SetCardInfo("Carrion", 111, Rarity.RARE, mage.cards.c.Carrion.class)); - cards.add(new SetCardInfo("Catacomb Dragon", 112, Rarity.RARE, mage.cards.c.CatacombDragon.class)); - cards.add(new SetCardInfo("Celestial Dawn", 6, Rarity.RARE, mage.cards.c.CelestialDawn.class)); - cards.add(new SetCardInfo("Cerulean Wyvern", 57, Rarity.UNCOMMON, mage.cards.c.CeruleanWyvern.class)); - cards.add(new SetCardInfo("Chaos Charm", 163, Rarity.COMMON, mage.cards.c.ChaosCharm.class)); - cards.add(new SetCardInfo("Chaosphere", 164, Rarity.RARE, mage.cards.c.Chaosphere.class)); - cards.add(new SetCardInfo("Charcoal Diamond", 296, Rarity.UNCOMMON, mage.cards.c.CharcoalDiamond.class)); - cards.add(new SetCardInfo("Chariot of the Sun", 262, Rarity.UNCOMMON, mage.cards.c.ChariotOfTheSun.class)); - cards.add(new SetCardInfo("Choking Sands", 113, Rarity.COMMON, mage.cards.c.ChokingSands.class)); - cards.add(new SetCardInfo("Cinder Cloud", 165, Rarity.UNCOMMON, mage.cards.c.CinderCloud.class)); - cards.add(new SetCardInfo("Civic Guildmage", 7, Rarity.COMMON, mage.cards.c.CivicGuildmage.class)); - cards.add(new SetCardInfo("Cloak of Invisibility", 58, Rarity.COMMON, mage.cards.c.CloakOfInvisibility.class)); - cards.add(new SetCardInfo("Consuming Ferocity", 166, Rarity.UNCOMMON, mage.cards.c.ConsumingFerocity.class)); - cards.add(new SetCardInfo("Coral Fighters", 59, Rarity.UNCOMMON, mage.cards.c.CoralFighters.class)); - cards.add(new SetCardInfo("Crash of Rhinos", 210, Rarity.COMMON, mage.cards.c.CrashOfRhinos.class)); - cards.add(new SetCardInfo("Crimson Hellkite", 167, Rarity.RARE, mage.cards.c.CrimsonHellkite.class)); +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author North + */ +public final class Mirage extends ExpansionSet { + + private static final Mirage instance = new Mirage(); + + public static Mirage getInstance() { + return instance; + } + + private Mirage() { + super("Mirage", "MIR", ExpansionSet.buildDate(1996, 8, 21), SetType.EXPANSION); + this.blockName = "Mirage"; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + + cards.add(new SetCardInfo("Abyssal Hunter", 103, Rarity.RARE, mage.cards.a.AbyssalHunter.class)); + cards.add(new SetCardInfo("Afiya Grove", 205, Rarity.RARE, mage.cards.a.AfiyaGrove.class)); + cards.add(new SetCardInfo("Afterlife", 1, Rarity.UNCOMMON, mage.cards.a.Afterlife.class)); + cards.add(new SetCardInfo("Agility", 154, Rarity.COMMON, mage.cards.a.Agility.class)); + cards.add(new SetCardInfo("Alarum", 2, Rarity.COMMON, mage.cards.a.Alarum.class)); + cards.add(new SetCardInfo("Aleatory", 155, Rarity.UNCOMMON, mage.cards.a.Aleatory.class)); + cards.add(new SetCardInfo("Amber Prison", 292, Rarity.RARE, mage.cards.a.AmberPrison.class)); + cards.add(new SetCardInfo("Amulet of Unmaking", 293, Rarity.RARE, mage.cards.a.AmuletOfUnmaking.class)); + cards.add(new SetCardInfo("Ancestral Memories", 52, Rarity.RARE, mage.cards.a.AncestralMemories.class)); + cards.add(new SetCardInfo("Armor of Thorns", 206, Rarity.COMMON, mage.cards.a.ArmorOfThorns.class)); + cards.add(new SetCardInfo("Armorer Guildmage", 156, Rarity.COMMON, mage.cards.a.ArmorerGuildmage.class)); + cards.add(new SetCardInfo("Ashen Powder", 104, Rarity.RARE, mage.cards.a.AshenPowder.class)); + cards.add(new SetCardInfo("Asmira, Holy Avenger", 256, Rarity.RARE, mage.cards.a.AsmiraHolyAvenger.class)); + cards.add(new SetCardInfo("Auspicious Ancestor", 3, Rarity.RARE, mage.cards.a.AuspiciousAncestor.class)); + cards.add(new SetCardInfo("Azimaet Drake", 53, Rarity.COMMON, mage.cards.a.AzimaetDrake.class)); + cards.add(new SetCardInfo("Bad River", 324, Rarity.UNCOMMON, mage.cards.b.BadRiver.class)); + cards.add(new SetCardInfo("Barbed Foliage", 207, Rarity.UNCOMMON, mage.cards.b.BarbedFoliage.class)); + cards.add(new SetCardInfo("Barbed-Back Wurm", 105, Rarity.UNCOMMON, mage.cards.b.BarbedBackWurm.class)); + cards.add(new SetCardInfo("Bay Falcon", 54, Rarity.COMMON, mage.cards.b.BayFalcon.class)); + cards.add(new SetCardInfo("Bazaar of Wonders", 55, Rarity.RARE, mage.cards.b.BazaarOfWonders.class)); + cards.add(new SetCardInfo("Benevolent Unicorn", 4, Rarity.COMMON, mage.cards.b.BenevolentUnicorn.class)); + cards.add(new SetCardInfo("Benthic Djinn", 257, Rarity.RARE, mage.cards.b.BenthicDjinn.class)); + cards.add(new SetCardInfo("Binding Agony", 106, Rarity.COMMON, mage.cards.b.BindingAgony.class)); + cards.add(new SetCardInfo("Blighted Shaman", 107, Rarity.UNCOMMON, mage.cards.b.BlightedShaman.class)); + cards.add(new SetCardInfo("Blind Fury", 158, Rarity.UNCOMMON, mage.cards.b.BlindFury.class)); + cards.add(new SetCardInfo("Blinding Light", 5, Rarity.UNCOMMON, mage.cards.b.BlindingLight.class)); + cards.add(new SetCardInfo("Blistering Barrier", 159, Rarity.COMMON, mage.cards.b.BlisteringBarrier.class)); + cards.add(new SetCardInfo("Bone Harvest", 108, Rarity.COMMON, mage.cards.b.BoneHarvest.class)); + cards.add(new SetCardInfo("Bone Mask", 295, Rarity.RARE, mage.cards.b.BoneMask.class)); + cards.add(new SetCardInfo("Boomerang", 56, Rarity.COMMON, mage.cards.b.Boomerang.class)); + cards.add(new SetCardInfo("Breathstealer", 109, Rarity.COMMON, mage.cards.b.Breathstealer.class)); + cards.add(new SetCardInfo("Brushwagg", 208, Rarity.RARE, mage.cards.b.Brushwagg.class)); + cards.add(new SetCardInfo("Builder's Bane", 160, Rarity.COMMON, mage.cards.b.BuildersBane.class)); + cards.add(new SetCardInfo("Burning Palm Efreet", 161, Rarity.UNCOMMON, mage.cards.b.BurningPalmEfreet.class)); + cards.add(new SetCardInfo("Burning Shield Askari", 162, Rarity.COMMON, mage.cards.b.BurningShieldAskari.class)); + cards.add(new SetCardInfo("Cadaverous Bloom", 258, Rarity.RARE, mage.cards.c.CadaverousBloom.class)); + cards.add(new SetCardInfo("Cadaverous Knight", 110, Rarity.COMMON, mage.cards.c.CadaverousKnight.class)); + cards.add(new SetCardInfo("Canopy Dragon", 209, Rarity.RARE, mage.cards.c.CanopyDragon.class)); + cards.add(new SetCardInfo("Carrion", 111, Rarity.RARE, mage.cards.c.Carrion.class)); + cards.add(new SetCardInfo("Catacomb Dragon", 112, Rarity.RARE, mage.cards.c.CatacombDragon.class)); + cards.add(new SetCardInfo("Celestial Dawn", 6, Rarity.RARE, mage.cards.c.CelestialDawn.class)); + cards.add(new SetCardInfo("Cerulean Wyvern", 57, Rarity.UNCOMMON, mage.cards.c.CeruleanWyvern.class)); + cards.add(new SetCardInfo("Chaos Charm", 163, Rarity.COMMON, mage.cards.c.ChaosCharm.class)); + cards.add(new SetCardInfo("Chaosphere", 164, Rarity.RARE, mage.cards.c.Chaosphere.class)); + cards.add(new SetCardInfo("Charcoal Diamond", 296, Rarity.UNCOMMON, mage.cards.c.CharcoalDiamond.class)); + cards.add(new SetCardInfo("Chariot of the Sun", 262, Rarity.UNCOMMON, mage.cards.c.ChariotOfTheSun.class)); + cards.add(new SetCardInfo("Choking Sands", 113, Rarity.COMMON, mage.cards.c.ChokingSands.class)); + cards.add(new SetCardInfo("Cinder Cloud", 165, Rarity.UNCOMMON, mage.cards.c.CinderCloud.class)); + cards.add(new SetCardInfo("Civic Guildmage", 7, Rarity.COMMON, mage.cards.c.CivicGuildmage.class)); + cards.add(new SetCardInfo("Cloak of Invisibility", 58, Rarity.COMMON, mage.cards.c.CloakOfInvisibility.class)); + cards.add(new SetCardInfo("Consuming Ferocity", 166, Rarity.UNCOMMON, mage.cards.c.ConsumingFerocity.class)); + cards.add(new SetCardInfo("Coral Fighters", 59, Rarity.UNCOMMON, mage.cards.c.CoralFighters.class)); + cards.add(new SetCardInfo("Crash of Rhinos", 210, Rarity.COMMON, mage.cards.c.CrashOfRhinos.class)); + cards.add(new SetCardInfo("Crimson Hellkite", 167, Rarity.RARE, mage.cards.c.CrimsonHellkite.class)); cards.add(new SetCardInfo("Crimson Roc", 168, Rarity.UNCOMMON, mage.cards.c.CrimsonRoc.class)); - cards.add(new SetCardInfo("Crypt Cobra", 114, Rarity.UNCOMMON, mage.cards.c.CryptCobra.class)); - cards.add(new SetCardInfo("Crystal Golem", 298, Rarity.UNCOMMON, mage.cards.c.CrystalGolem.class)); - cards.add(new SetCardInfo("Crystal Vein", 325, Rarity.UNCOMMON, mage.cards.c.CrystalVein.class)); - cards.add(new SetCardInfo("Cursed Totem", 299, Rarity.RARE, mage.cards.c.CursedTotem.class)); - cards.add(new SetCardInfo("Daring Apprentice", 60, Rarity.RARE, mage.cards.d.DaringApprentice.class)); - cards.add(new SetCardInfo("Dark Banishing", 115, Rarity.COMMON, mage.cards.d.DarkBanishing.class)); - cards.add(new SetCardInfo("Dark Ritual", 116, Rarity.COMMON, mage.cards.d.DarkRitual.class)); - cards.add(new SetCardInfo("Dazzling Beauty", 8, Rarity.COMMON, mage.cards.d.DazzlingBeauty.class)); - cards.add(new SetCardInfo("Delirium", 260, Rarity.UNCOMMON, mage.cards.d.Delirium.class)); - cards.add(new SetCardInfo("Dirtwater Wraith", 117, Rarity.COMMON, mage.cards.d.DirtwaterWraith.class)); - cards.add(new SetCardInfo("Disempower", 9, Rarity.COMMON, mage.cards.d.Disempower.class)); - cards.add(new SetCardInfo("Disenchant", 10, Rarity.COMMON, mage.cards.d.Disenchant.class)); - cards.add(new SetCardInfo("Dissipate", 61, Rarity.UNCOMMON, mage.cards.d.Dissipate.class)); - cards.add(new SetCardInfo("Divine Offering", 11, Rarity.COMMON, mage.cards.d.DivineOffering.class)); - cards.add(new SetCardInfo("Divine Retribution", 12, Rarity.RARE, mage.cards.d.DivineRetribution.class)); - cards.add(new SetCardInfo("Drain Life", 118, Rarity.COMMON, mage.cards.d.DrainLife.class)); - cards.add(new SetCardInfo("Dread Specter", 119, Rarity.UNCOMMON, mage.cards.d.DreadSpecter.class)); - cards.add(new SetCardInfo("Dream Cache", 62, Rarity.COMMON, mage.cards.d.DreamCache.class)); - cards.add(new SetCardInfo("Dwarven Miner", 169, Rarity.UNCOMMON, mage.cards.d.DwarvenMiner.class)); - cards.add(new SetCardInfo("Dwarven Nomad", 170, Rarity.COMMON, mage.cards.d.DwarvenNomad.class)); - cards.add(new SetCardInfo("Early Harvest", 213, Rarity.RARE, mage.cards.e.EarlyHarvest.class)); - cards.add(new SetCardInfo("Ebony Charm", 120, Rarity.COMMON, mage.cards.e.EbonyCharm.class)); - cards.add(new SetCardInfo("Ekundu Griffin", 13, Rarity.COMMON, mage.cards.e.EkunduGriffin.class)); - cards.add(new SetCardInfo("Elixir of Vitality", 300, Rarity.UNCOMMON, mage.cards.e.ElixirOfVitality.class)); - cards.add(new SetCardInfo("Emberwilde Caliph", 322, Rarity.RARE, mage.cards.e.EmberwildeCaliph.class)); - cards.add(new SetCardInfo("Emberwilde Djinn", 172, Rarity.RARE, mage.cards.e.EmberwildeDjinn.class)); - cards.add(new SetCardInfo("Energy Bolt", 263, Rarity.RARE, mage.cards.e.EnergyBolt.class)); - cards.add(new SetCardInfo("Energy Vortex", 64, Rarity.RARE, mage.cards.e.EnergyVortex.class)); - cards.add(new SetCardInfo("Enfeeblement", 121, Rarity.COMMON, mage.cards.e.Enfeeblement.class)); - cards.add(new SetCardInfo("Enlightened Tutor", 14, Rarity.UNCOMMON, mage.cards.e.EnlightenedTutor.class)); - cards.add(new SetCardInfo("Ersatz Gnomes", 301, Rarity.UNCOMMON, mage.cards.e.ErsatzGnomes.class)); - cards.add(new SetCardInfo("Ether Well", 65, Rarity.UNCOMMON, mage.cards.e.EtherWell.class)); - cards.add(new SetCardInfo("Ethereal Champion", 15, Rarity.RARE, mage.cards.e.EtherealChampion.class)); - cards.add(new SetCardInfo("Fallow Earth", 214, Rarity.UNCOMMON, mage.cards.f.FallowEarth.class)); - cards.add(new SetCardInfo("Favorable Destiny", 16, Rarity.UNCOMMON, mage.cards.f.FavorableDestiny.class)); - cards.add(new SetCardInfo("Femeref Archers", 215, Rarity.UNCOMMON, mage.cards.f.FemerefArchers.class)); - cards.add(new SetCardInfo("Femeref Healer", 17, Rarity.COMMON, mage.cards.f.FemerefHealer.class)); - cards.add(new SetCardInfo("Femeref Knight", 18, Rarity.COMMON, mage.cards.f.FemerefKnight.class)); - cards.add(new SetCardInfo("Femeref Scouts", 19, Rarity.COMMON, mage.cards.f.FemerefScouts.class)); - cards.add(new SetCardInfo("Feral Shadow", 122, Rarity.COMMON, mage.cards.f.FeralShadow.class)); - cards.add(new SetCardInfo("Fetid Horror", 123, Rarity.COMMON, mage.cards.f.FetidHorror.class)); - cards.add(new SetCardInfo("Final Fortune", 173, Rarity.RARE, mage.cards.f.FinalFortune.class)); - cards.add(new SetCardInfo("Fire Diamond", 302, Rarity.UNCOMMON, mage.cards.f.FireDiamond.class)); - cards.add(new SetCardInfo("Firebreathing", 174, Rarity.COMMON, mage.cards.f.Firebreathing.class)); - cards.add(new SetCardInfo("Flame Elemental", 175, Rarity.UNCOMMON, mage.cards.f.FlameElemental.class)); - cards.add(new SetCardInfo("Flare", 176, Rarity.COMMON, mage.cards.f.Flare.class)); - cards.add(new SetCardInfo("Flash", 66, Rarity.RARE, mage.cards.f.Flash.class)); - cards.add(new SetCardInfo("Flood Plain", 326, Rarity.UNCOMMON, mage.cards.f.FloodPlain.class)); - cards.add(new SetCardInfo("Floodgate", 67, Rarity.UNCOMMON, mage.cards.f.Floodgate.class)); - cards.add(new SetCardInfo("Fog", 216, Rarity.COMMON, mage.cards.f.Fog.class)); - cards.add(new SetCardInfo("Foratog", 217, Rarity.UNCOMMON, mage.cards.f.Foratog.class)); - cards.add(new SetCardInfo("Forbidden Crypt", 124, Rarity.RARE, mage.cards.f.ForbiddenCrypt.class)); - cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forsaken Wastes", 125, Rarity.RARE, mage.cards.f.ForsakenWastes.class)); - cards.add(new SetCardInfo("Frenetic Efreet", 264, Rarity.RARE, mage.cards.f.FreneticEfreet.class)); - cards.add(new SetCardInfo("Giant Mantis", 218, Rarity.COMMON, mage.cards.g.GiantMantis.class)); - cards.add(new SetCardInfo("Gibbering Hyenas", 219, Rarity.COMMON, mage.cards.g.GibberingHyenas.class)); - cards.add(new SetCardInfo("Goblin Elite Infantry", 177, Rarity.COMMON, mage.cards.g.GoblinEliteInfantry.class)); - cards.add(new SetCardInfo("Goblin Scouts", 178, Rarity.UNCOMMON, mage.cards.g.GoblinScouts.class)); - cards.add(new SetCardInfo("Goblin Soothsayer", 179, Rarity.UNCOMMON, mage.cards.g.GoblinSoothsayer.class)); - cards.add(new SetCardInfo("Goblin Tinkerer", 180, Rarity.COMMON, mage.cards.g.GoblinTinkerer.class)); - cards.add(new SetCardInfo("Granger Guildmage", 220, Rarity.COMMON, mage.cards.g.GrangerGuildmage.class)); - cards.add(new SetCardInfo("Grasslands", 327, Rarity.UNCOMMON, mage.cards.g.Grasslands.class)); - cards.add(new SetCardInfo("Grave Servitude", 126, Rarity.COMMON, mage.cards.g.GraveServitude.class)); - cards.add(new SetCardInfo("Gravebane Zombie", 127, Rarity.COMMON, mage.cards.g.GravebaneZombie.class)); - cards.add(new SetCardInfo("Grim Feast", 265, Rarity.RARE, mage.cards.g.GrimFeast.class)); - cards.add(new SetCardInfo("Grinning Totem", 303, Rarity.RARE, mage.cards.g.GrinningTotem.class)); - cards.add(new SetCardInfo("Hakim, Loreweaver", 68, Rarity.RARE, mage.cards.h.HakimLoreweaver.class)); - cards.add(new SetCardInfo("Hall of Gemstone", 221, Rarity.RARE, mage.cards.h.HallOfGemstone.class)); - cards.add(new SetCardInfo("Hammer of Bogardan", 181, Rarity.RARE, mage.cards.h.HammerOfBogardan.class)); - cards.add(new SetCardInfo("Harbinger of Night", 128, Rarity.RARE, mage.cards.h.HarbingerOfNight.class)); - cards.add(new SetCardInfo("Harbor Guardian", 266, Rarity.UNCOMMON, mage.cards.h.HarborGuardian.class)); - cards.add(new SetCardInfo("Harmattan Efreet", 69, Rarity.UNCOMMON, mage.cards.h.HarmattanEfreet.class)); - cards.add(new SetCardInfo("Hazerider Drake", 268, Rarity.UNCOMMON, mage.cards.h.HazeriderDrake.class)); - cards.add(new SetCardInfo("Healing Salve", 20, Rarity.COMMON, mage.cards.h.HealingSalve.class)); - cards.add(new SetCardInfo("Hivis of the Scale", 182, Rarity.RARE, mage.cards.h.HivisOfTheScale.class)); - cards.add(new SetCardInfo("Horrible Hordes", 304, Rarity.UNCOMMON, mage.cards.h.HorribleHordes.class)); - cards.add(new SetCardInfo("Igneous Golem", 305, Rarity.UNCOMMON, mage.cards.i.IgneousGolem.class)); - cards.add(new SetCardInfo("Illicit Auction", 183, Rarity.RARE, mage.cards.i.IllicitAuction.class)); - cards.add(new SetCardInfo("Illumination", 21, Rarity.UNCOMMON, mage.cards.i.Illumination.class)); - cards.add(new SetCardInfo("Incinerate", 184, Rarity.COMMON, mage.cards.i.Incinerate.class)); - cards.add(new SetCardInfo("Infernal Contract", 129, Rarity.RARE, mage.cards.i.InfernalContract.class)); - cards.add(new SetCardInfo("Iron Tusk Elephant", 22, Rarity.UNCOMMON, mage.cards.i.IronTuskElephant.class)); - cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ivory Charm", 23, Rarity.COMMON, mage.cards.i.IvoryCharm.class)); - cards.add(new SetCardInfo("Jolrael's Centaur", 222, Rarity.COMMON, mage.cards.j.JolraelsCentaur.class)); - cards.add(new SetCardInfo("Jolt", 70, Rarity.COMMON, mage.cards.j.Jolt.class)); - cards.add(new SetCardInfo("Jungle Patrol", 223, Rarity.RARE, mage.cards.j.JunglePatrol.class)); - cards.add(new SetCardInfo("Jungle Troll", 269, Rarity.UNCOMMON, mage.cards.j.JungleTroll.class)); - cards.add(new SetCardInfo("Jungle Wurm", 224, Rarity.COMMON, mage.cards.j.JungleWurm.class)); - cards.add(new SetCardInfo("Kaervek's Hex", 130, Rarity.UNCOMMON, mage.cards.k.KaerveksHex.class)); - cards.add(new SetCardInfo("Kaervek's Purge", 270, Rarity.UNCOMMON, mage.cards.k.KaerveksPurge.class)); - cards.add(new SetCardInfo("Kaervek's Torch", 185, Rarity.COMMON, mage.cards.k.KaerveksTorch.class)); - cards.add(new SetCardInfo("Karoo Meerkat", 225, Rarity.UNCOMMON, mage.cards.k.KarooMeerkat.class)); - cards.add(new SetCardInfo("Kukemssa Pirates", 71, Rarity.RARE, mage.cards.k.KukemssaPirates.class)); - cards.add(new SetCardInfo("Kukemssa Serpent", 72, Rarity.COMMON, mage.cards.k.KukemssaSerpent.class)); - cards.add(new SetCardInfo("Lead Golem", 306, Rarity.UNCOMMON, mage.cards.l.LeadGolem.class)); - cards.add(new SetCardInfo("Leering Gargoyle", 271, Rarity.RARE, mage.cards.l.LeeringGargoyle.class)); - cards.add(new SetCardInfo("Lightning Reflexes", 186, Rarity.COMMON, mage.cards.l.LightningReflexes.class)); - cards.add(new SetCardInfo("Lion's Eye Diamond", 307, Rarity.RARE, mage.cards.l.LionsEyeDiamond.class)); - cards.add(new SetCardInfo("Locust Swarm", 226, Rarity.UNCOMMON, mage.cards.l.LocustSwarm.class)); - cards.add(new SetCardInfo("Lure of Prey", 227, Rarity.RARE, mage.cards.l.LureOfPrey.class)); - cards.add(new SetCardInfo("Malignant Growth", 272, Rarity.RARE, mage.cards.m.MalignantGrowth.class)); - cards.add(new SetCardInfo("Mana Prism", 308, Rarity.UNCOMMON, mage.cards.m.ManaPrism.class)); - cards.add(new SetCardInfo("Mangara's Tome", 309, Rarity.RARE, mage.cards.m.MangarasTome.class)); - cards.add(new SetCardInfo("Marble Diamond", 310, Rarity.UNCOMMON, mage.cards.m.MarbleDiamond.class)); - cards.add(new SetCardInfo("Maro", 228, Rarity.RARE, mage.cards.m.Maro.class)); - cards.add(new SetCardInfo("Melesse Spirit", 27, Rarity.UNCOMMON, mage.cards.m.MelesseSpirit.class)); - cards.add(new SetCardInfo("Memory Lapse", 74, Rarity.COMMON, mage.cards.m.MemoryLapse.class)); - cards.add(new SetCardInfo("Merfolk Raiders", 75, Rarity.COMMON, mage.cards.m.MerfolkRaiders.class)); - cards.add(new SetCardInfo("Merfolk Seer", 76, Rarity.COMMON, mage.cards.m.MerfolkSeer.class)); - cards.add(new SetCardInfo("Mind Harness", 78, Rarity.UNCOMMON, mage.cards.m.MindHarness.class)); - cards.add(new SetCardInfo("Mire Shade", 131, Rarity.UNCOMMON, mage.cards.m.MireShade.class)); - cards.add(new SetCardInfo("Misers' Cage", 311, Rarity.RARE, mage.cards.m.MisersCage.class)); - cards.add(new SetCardInfo("Mist Dragon", 79, Rarity.RARE, mage.cards.m.MistDragon.class)); - cards.add(new SetCardInfo("Moss Diamond", 312, Rarity.UNCOMMON, mage.cards.m.MossDiamond.class)); - cards.add(new SetCardInfo("Mountain Valley", 328, Rarity.UNCOMMON, mage.cards.m.MountainValley.class)); - cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mtenda Griffin", 28, Rarity.UNCOMMON, mage.cards.m.MtendaGriffin.class)); - cards.add(new SetCardInfo("Mtenda Herder", 29, Rarity.COMMON, mage.cards.m.MtendaHerder.class)); - cards.add(new SetCardInfo("Mtenda Lion", 230, Rarity.COMMON, mage.cards.m.MtendaLion.class)); - cards.add(new SetCardInfo("Mystical Tutor", 80, Rarity.UNCOMMON, mage.cards.m.MysticalTutor.class)); - cards.add(new SetCardInfo("Natural Balance", 231, Rarity.RARE, mage.cards.n.NaturalBalance.class)); - cards.add(new SetCardInfo("Nettletooth Djinn", 232, Rarity.UNCOMMON, mage.cards.n.NettletoothDjinn.class)); - cards.add(new SetCardInfo("Noble Elephant", 30, Rarity.COMMON, mage.cards.n.NobleElephant.class)); - cards.add(new SetCardInfo("Nocturnal Raid", 132, Rarity.UNCOMMON, mage.cards.n.NocturnalRaid.class)); - cards.add(new SetCardInfo("Null Chamber", 31, Rarity.RARE, mage.cards.n.NullChamber.class)); - cards.add(new SetCardInfo("Pacifism", 32, Rarity.COMMON, mage.cards.p.Pacifism.class)); - cards.add(new SetCardInfo("Painful Memories", 133, Rarity.UNCOMMON, mage.cards.p.PainfulMemories.class)); - cards.add(new SetCardInfo("Patagia Golem", 313, Rarity.UNCOMMON, mage.cards.p.PatagiaGolem.class)); - cards.add(new SetCardInfo("Paupers' Cage", 314, Rarity.RARE, mage.cards.p.PaupersCage.class)); - cards.add(new SetCardInfo("Pearl Dragon", 33, Rarity.RARE, mage.cards.p.PearlDragon.class)); - cards.add(new SetCardInfo("Phyrexian Dreadnought", 315, Rarity.RARE, mage.cards.p.PhyrexianDreadnought.class)); - cards.add(new SetCardInfo("Phyrexian Purge", 273, Rarity.RARE, mage.cards.p.PhyrexianPurge.class)); - cards.add(new SetCardInfo("Phyrexian Tribute", 134, Rarity.RARE, mage.cards.p.PhyrexianTribute.class)); - cards.add(new SetCardInfo("Phyrexian Vault", 316, Rarity.UNCOMMON, mage.cards.p.PhyrexianVault.class)); - cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Political Trickery", 81, Rarity.RARE, mage.cards.p.PoliticalTrickery.class)); - cards.add(new SetCardInfo("Polymorph", 82, Rarity.RARE, mage.cards.p.Polymorph.class)); - cards.add(new SetCardInfo("Power Sink", 83, Rarity.COMMON, mage.cards.p.PowerSink.class)); - cards.add(new SetCardInfo("Prismatic Boon", 274, Rarity.UNCOMMON, mage.cards.p.PrismaticBoon.class)); - cards.add(new SetCardInfo("Prismatic Circle", 34, Rarity.COMMON, mage.cards.p.PrismaticCircle.class)); - cards.add(new SetCardInfo("Prismatic Lace", 84, Rarity.RARE, mage.cards.p.PrismaticLace.class)); - cards.add(new SetCardInfo("Psychic Transfer", 85, Rarity.RARE, mage.cards.p.PsychicTransfer.class)); - cards.add(new SetCardInfo("Purgatory", 275, Rarity.RARE, mage.cards.p.Purgatory.class)); - cards.add(new SetCardInfo("Purraj of Urborg", 135, Rarity.RARE, mage.cards.p.PurrajOfUrborg.class)); - cards.add(new SetCardInfo("Pyric Salamander", 187, Rarity.COMMON, mage.cards.p.PyricSalamander.class)); - cards.add(new SetCardInfo("Quirion Elves", 234, Rarity.COMMON, mage.cards.q.QuirionElves.class)); - cards.add(new SetCardInfo("Radiant Essence", 276, Rarity.UNCOMMON, mage.cards.r.RadiantEssence.class)); - cards.add(new SetCardInfo("Raging Spirit", 188, Rarity.COMMON, mage.cards.r.RagingSpirit.class)); - cards.add(new SetCardInfo("Rampant Growth", 235, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); - cards.add(new SetCardInfo("Rashida Scalebane", 35, Rarity.RARE, mage.cards.r.RashidaScalebane.class)); - cards.add(new SetCardInfo("Ravenous Vampire", 136, Rarity.UNCOMMON, mage.cards.r.RavenousVampire.class)); - cards.add(new SetCardInfo("Ray of Command", 86, Rarity.COMMON, mage.cards.r.RayOfCommand.class)); - cards.add(new SetCardInfo("Razor Pendulum", 317, Rarity.RARE, mage.cards.r.RazorPendulum.class)); - cards.add(new SetCardInfo("Reality Ripple", 87, Rarity.COMMON, mage.cards.r.RealityRipple.class)); - cards.add(new SetCardInfo("Reckless Embermage", 189, Rarity.RARE, mage.cards.r.RecklessEmbermage.class)); - cards.add(new SetCardInfo("Regeneration", 236, Rarity.COMMON, mage.cards.r.Regeneration.class)); - cards.add(new SetCardInfo("Reign of Chaos", 190, Rarity.UNCOMMON, mage.cards.r.ReignOfChaos.class)); - cards.add(new SetCardInfo("Reign of Terror", 137, Rarity.UNCOMMON, mage.cards.r.ReignOfTerror.class)); - cards.add(new SetCardInfo("Reparations", 278, Rarity.RARE, mage.cards.r.Reparations.class)); - cards.add(new SetCardInfo("Restless Dead", 138, Rarity.COMMON, mage.cards.r.RestlessDead.class)); - cards.add(new SetCardInfo("Ritual of Steel", 36, Rarity.COMMON, mage.cards.r.RitualOfSteel.class)); - cards.add(new SetCardInfo("Rock Basilisk", 279, Rarity.RARE, mage.cards.r.RockBasilisk.class)); - cards.add(new SetCardInfo("Rocky Tar Pit", 329, Rarity.UNCOMMON, mage.cards.r.RockyTarPit.class)); - cards.add(new SetCardInfo("Roots of Life", 237, Rarity.UNCOMMON, mage.cards.r.RootsOfLife.class)); - cards.add(new SetCardInfo("Sabertooth Cobra", 238, Rarity.COMMON, mage.cards.s.SabertoothCobra.class)); - cards.add(new SetCardInfo("Sacred Mesa", 37, Rarity.RARE, mage.cards.s.SacredMesa.class)); - cards.add(new SetCardInfo("Sandbar Crocodile", 88, Rarity.COMMON, mage.cards.s.SandbarCrocodile.class)); - cards.add(new SetCardInfo("Sandstorm", 239, Rarity.COMMON, mage.cards.s.Sandstorm.class)); - cards.add(new SetCardInfo("Sapphire Charm", 89, Rarity.COMMON, mage.cards.s.SapphireCharm.class)); - cards.add(new SetCardInfo("Savage Twister", 280, Rarity.UNCOMMON, mage.cards.s.SavageTwister.class)); - cards.add(new SetCardInfo("Sawback Manticore", 281, Rarity.RARE, mage.cards.s.SawbackManticore.class)); - cards.add(new SetCardInfo("Sea Scryer", 90, Rarity.COMMON, mage.cards.s.SeaScryer.class)); - cards.add(new SetCardInfo("Searing Spear Askari", 191, Rarity.COMMON, mage.cards.s.SearingSpearAskari.class)); - cards.add(new SetCardInfo("Seedling Charm", 240, Rarity.COMMON, mage.cards.s.SeedlingCharm.class)); - cards.add(new SetCardInfo("Seeds of Innocence", 241, Rarity.RARE, mage.cards.s.SeedsOfInnocence.class)); - cards.add(new SetCardInfo("Serene Heart", 242, Rarity.COMMON, mage.cards.s.SereneHeart.class)); - cards.add(new SetCardInfo("Sewer Rats", 139, Rarity.COMMON, mage.cards.s.SewerRats.class)); - cards.add(new SetCardInfo("Shadow Guildmage", 140, Rarity.COMMON, mage.cards.s.ShadowGuildmage.class)); - cards.add(new SetCardInfo("Shallow Grave", 141, Rarity.RARE, mage.cards.s.ShallowGrave.class)); - cards.add(new SetCardInfo("Shaper Guildmage", 91, Rarity.COMMON, mage.cards.s.ShaperGuildmage.class)); - cards.add(new SetCardInfo("Shauku's Minion", 283, Rarity.UNCOMMON, mage.cards.s.ShaukusMinion.class)); - cards.add(new SetCardInfo("Shauku, Endbringer", 142, Rarity.RARE, mage.cards.s.ShaukuEndbringer.class)); - cards.add(new SetCardInfo("Shimmer", 92, Rarity.RARE, mage.cards.s.Shimmer.class)); - cards.add(new SetCardInfo("Sidar Jabari", 39, Rarity.RARE, mage.cards.s.SidarJabari.class)); - cards.add(new SetCardInfo("Skulking Ghost", 143, Rarity.COMMON, mage.cards.s.SkulkingGhost.class)); - cards.add(new SetCardInfo("Sky Diamond", 319, Rarity.UNCOMMON, mage.cards.s.SkyDiamond.class)); - cards.add(new SetCardInfo("Soar", 93, Rarity.COMMON, mage.cards.s.Soar.class)); - cards.add(new SetCardInfo("Soul Echo", 40, Rarity.RARE, mage.cards.s.SoulEcho.class)); - cards.add(new SetCardInfo("Soul Rend", 144, Rarity.UNCOMMON, mage.cards.s.SoulRend.class)); - cards.add(new SetCardInfo("Soulshriek", 145, Rarity.COMMON, mage.cards.s.Soulshriek.class)); - cards.add(new SetCardInfo("Spectral Guardian", 41, Rarity.RARE, mage.cards.s.SpectralGuardian.class)); - cards.add(new SetCardInfo("Spirit of the Night", 146, Rarity.RARE, mage.cards.s.SpiritOfTheNight.class)); - cards.add(new SetCardInfo("Spitting Earth", 193, Rarity.COMMON, mage.cards.s.SpittingEarth.class)); - cards.add(new SetCardInfo("Stalking Tiger", 243, Rarity.COMMON, mage.cards.s.StalkingTiger.class)); - cards.add(new SetCardInfo("Stone Rain", 194, Rarity.COMMON, mage.cards.s.StoneRain.class)); - cards.add(new SetCardInfo("Stupor", 147, Rarity.UNCOMMON, mage.cards.s.Stupor.class)); - cards.add(new SetCardInfo("Subterranean Spirit", 195, Rarity.RARE, mage.cards.s.SubterraneanSpirit.class)); - cards.add(new SetCardInfo("Sunweb", 42, Rarity.RARE, mage.cards.s.Sunweb.class)); - cards.add(new SetCardInfo("Superior Numbers", 244, Rarity.UNCOMMON, mage.cards.s.SuperiorNumbers.class)); - cards.add(new SetCardInfo("Suq'Ata Firewalker", 94, Rarity.UNCOMMON, mage.cards.s.SuqAtaFirewalker.class)); - cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Talruum Minotaur", 196, Rarity.COMMON, mage.cards.t.TalruumMinotaur.class)); - cards.add(new SetCardInfo("Taniwha", 95, Rarity.RARE, mage.cards.t.Taniwha.class)); - cards.add(new SetCardInfo("Teeka's Dragon", 320, Rarity.RARE, mage.cards.t.TeekasDragon.class)); - cards.add(new SetCardInfo("Teferi's Curse", 96, Rarity.COMMON, mage.cards.t.TeferisCurse.class)); - cards.add(new SetCardInfo("Teferi's Drake", 97, Rarity.COMMON, mage.cards.t.TeferisDrake.class)); - cards.add(new SetCardInfo("Teferi's Isle", 330, Rarity.RARE, mage.cards.t.TeferisIsle.class)); - cards.add(new SetCardInfo("Telim'Tor", 197, Rarity.RARE, mage.cards.t.TelimTor.class)); - cards.add(new SetCardInfo("Telim'Tor's Darts", 321, Rarity.UNCOMMON, mage.cards.t.TelimTorsDarts.class)); - cards.add(new SetCardInfo("Telim'Tor's Edict", 198, Rarity.RARE, mage.cards.t.TelimTorsEdict.class)); - cards.add(new SetCardInfo("Teremko Griffin", 43, Rarity.COMMON, mage.cards.t.TeremkoGriffin.class)); - cards.add(new SetCardInfo("Thirst", 99, Rarity.COMMON, mage.cards.t.Thirst.class)); - cards.add(new SetCardInfo("Tidal Wave", 100, Rarity.UNCOMMON, mage.cards.t.TidalWave.class)); - cards.add(new SetCardInfo("Tombstone Stairwell", 149, Rarity.RARE, mage.cards.t.TombstoneStairwell.class)); - cards.add(new SetCardInfo("Tranquil Domain", 245, Rarity.COMMON, mage.cards.t.TranquilDomain.class)); - cards.add(new SetCardInfo("Tropical Storm", 246, Rarity.UNCOMMON, mage.cards.t.TropicalStorm.class)); - cards.add(new SetCardInfo("Uktabi Faerie", 247, Rarity.COMMON, mage.cards.u.UktabiFaerie.class)); - cards.add(new SetCardInfo("Uktabi Wildcats", 248, Rarity.RARE, mage.cards.u.UktabiWildcats.class)); - cards.add(new SetCardInfo("Unfulfilled Desires", 285, Rarity.RARE, mage.cards.u.UnfulfilledDesires.class)); - cards.add(new SetCardInfo("Unseen Walker", 249, Rarity.UNCOMMON, mage.cards.u.UnseenWalker.class)); - cards.add(new SetCardInfo("Unyaro Bee Sting", 250, Rarity.UNCOMMON, mage.cards.u.UnyaroBeeSting.class)); - cards.add(new SetCardInfo("Unyaro Griffin", 44, Rarity.UNCOMMON, mage.cards.u.UnyaroGriffin.class)); - cards.add(new SetCardInfo("Urborg Panther", 150, Rarity.COMMON, mage.cards.u.UrborgPanther.class)); - cards.add(new SetCardInfo("Vaporous Djinn", 101, Rarity.UNCOMMON, mage.cards.v.VaporousDjinn.class)); - cards.add(new SetCardInfo("Ventifact Bottle", 323, Rarity.RARE, mage.cards.v.VentifactBottle.class)); - cards.add(new SetCardInfo("Viashino Warrior", 200, Rarity.COMMON, mage.cards.v.ViashinoWarrior.class)); - cards.add(new SetCardInfo("Vigilant Martyr", 45, Rarity.UNCOMMON, mage.cards.v.VigilantMartyr.class)); - cards.add(new SetCardInfo("Village Elder", 251, Rarity.COMMON, mage.cards.v.VillageElder.class)); - cards.add(new SetCardInfo("Vitalizing Cascade", 286, Rarity.UNCOMMON, mage.cards.v.VitalizingCascade.class)); - cards.add(new SetCardInfo("Volcanic Dragon", 201, Rarity.RARE, mage.cards.v.VolcanicDragon.class)); - cards.add(new SetCardInfo("Volcanic Geyser", 202, Rarity.UNCOMMON, mage.cards.v.VolcanicGeyser.class)); - cards.add(new SetCardInfo("Waiting in the Weeds", 252, Rarity.RARE, mage.cards.w.WaitingInTheWeeds.class)); - cards.add(new SetCardInfo("Wall of Resistance", 46, Rarity.COMMON, mage.cards.w.WallOfResistance.class)); - cards.add(new SetCardInfo("Wall of Roots", 253, Rarity.COMMON, mage.cards.w.WallOfRoots.class)); - cards.add(new SetCardInfo("Ward of Lights", 47, Rarity.COMMON, mage.cards.w.WardOfLights.class)); - cards.add(new SetCardInfo("Warping Wurm", 287, Rarity.RARE, mage.cards.w.WarpingWurm.class)); - cards.add(new SetCardInfo("Wave Elemental", 102, Rarity.UNCOMMON, mage.cards.w.WaveElemental.class)); - cards.add(new SetCardInfo("Wellspring", 288, Rarity.RARE, mage.cards.w.Wellspring.class)); - cards.add(new SetCardInfo("Wild Elephant", 254, Rarity.COMMON, mage.cards.w.WildElephant.class)); - cards.add(new SetCardInfo("Wildfire Emissary", 203, Rarity.UNCOMMON, mage.cards.w.WildfireEmissary.class)); - cards.add(new SetCardInfo("Windreaper Falcon", 289, Rarity.UNCOMMON, mage.cards.w.WindreaperFalcon.class)); - cards.add(new SetCardInfo("Withering Boon", 152, Rarity.UNCOMMON, mage.cards.w.WitheringBoon.class)); - cards.add(new SetCardInfo("Worldly Tutor", 255, Rarity.UNCOMMON, mage.cards.w.WorldlyTutor.class)); - cards.add(new SetCardInfo("Zebra Unicorn", 290, Rarity.UNCOMMON, mage.cards.z.ZebraUnicorn.class)); - cards.add(new SetCardInfo("Zhalfirin Commander", 49, Rarity.UNCOMMON, mage.cards.z.ZhalfirinCommander.class)); - cards.add(new SetCardInfo("Zhalfirin Knight", 50, Rarity.COMMON, mage.cards.z.ZhalfirinKnight.class)); - cards.add(new SetCardInfo("Zirilan of the Claw", 204, Rarity.RARE, mage.cards.z.ZirilanOfTheClaw.class)); - cards.add(new SetCardInfo("Zombie Mob", 153, Rarity.UNCOMMON, mage.cards.z.ZombieMob.class)); - cards.add(new SetCardInfo("Zuberi, Golden Feather", 51, Rarity.RARE, mage.cards.z.ZuberiGoldenFeather.class)); - } + cards.add(new SetCardInfo("Crypt Cobra", 114, Rarity.UNCOMMON, mage.cards.c.CryptCobra.class)); + cards.add(new SetCardInfo("Crystal Golem", 298, Rarity.UNCOMMON, mage.cards.c.CrystalGolem.class)); + cards.add(new SetCardInfo("Crystal Vein", 325, Rarity.UNCOMMON, mage.cards.c.CrystalVein.class)); + cards.add(new SetCardInfo("Cursed Totem", 299, Rarity.RARE, mage.cards.c.CursedTotem.class)); + cards.add(new SetCardInfo("Daring Apprentice", 60, Rarity.RARE, mage.cards.d.DaringApprentice.class)); + cards.add(new SetCardInfo("Dark Banishing", 115, Rarity.COMMON, mage.cards.d.DarkBanishing.class)); + cards.add(new SetCardInfo("Dark Ritual", 116, Rarity.COMMON, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Dazzling Beauty", 8, Rarity.COMMON, mage.cards.d.DazzlingBeauty.class)); + cards.add(new SetCardInfo("Delirium", 260, Rarity.UNCOMMON, mage.cards.d.Delirium.class)); + cards.add(new SetCardInfo("Dirtwater Wraith", 117, Rarity.COMMON, mage.cards.d.DirtwaterWraith.class)); + cards.add(new SetCardInfo("Disempower", 9, Rarity.COMMON, mage.cards.d.Disempower.class)); + cards.add(new SetCardInfo("Disenchant", 10, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Dissipate", 61, Rarity.UNCOMMON, mage.cards.d.Dissipate.class)); + cards.add(new SetCardInfo("Divine Offering", 11, Rarity.COMMON, mage.cards.d.DivineOffering.class)); + cards.add(new SetCardInfo("Divine Retribution", 12, Rarity.RARE, mage.cards.d.DivineRetribution.class)); + cards.add(new SetCardInfo("Drain Life", 118, Rarity.COMMON, mage.cards.d.DrainLife.class)); + cards.add(new SetCardInfo("Dread Specter", 119, Rarity.UNCOMMON, mage.cards.d.DreadSpecter.class)); + cards.add(new SetCardInfo("Dream Cache", 62, Rarity.COMMON, mage.cards.d.DreamCache.class)); + cards.add(new SetCardInfo("Dwarven Miner", 169, Rarity.UNCOMMON, mage.cards.d.DwarvenMiner.class)); + cards.add(new SetCardInfo("Dwarven Nomad", 170, Rarity.COMMON, mage.cards.d.DwarvenNomad.class)); + cards.add(new SetCardInfo("Early Harvest", 213, Rarity.RARE, mage.cards.e.EarlyHarvest.class)); + cards.add(new SetCardInfo("Ebony Charm", 120, Rarity.COMMON, mage.cards.e.EbonyCharm.class)); + cards.add(new SetCardInfo("Ekundu Griffin", 13, Rarity.COMMON, mage.cards.e.EkunduGriffin.class)); + cards.add(new SetCardInfo("Elixir of Vitality", 300, Rarity.UNCOMMON, mage.cards.e.ElixirOfVitality.class)); + cards.add(new SetCardInfo("Emberwilde Caliph", 322, Rarity.RARE, mage.cards.e.EmberwildeCaliph.class)); + cards.add(new SetCardInfo("Emberwilde Djinn", 172, Rarity.RARE, mage.cards.e.EmberwildeDjinn.class)); + cards.add(new SetCardInfo("Energy Bolt", 263, Rarity.RARE, mage.cards.e.EnergyBolt.class)); + cards.add(new SetCardInfo("Energy Vortex", 64, Rarity.RARE, mage.cards.e.EnergyVortex.class)); + cards.add(new SetCardInfo("Enfeeblement", 121, Rarity.COMMON, mage.cards.e.Enfeeblement.class)); + cards.add(new SetCardInfo("Enlightened Tutor", 14, Rarity.UNCOMMON, mage.cards.e.EnlightenedTutor.class)); + cards.add(new SetCardInfo("Ersatz Gnomes", 301, Rarity.UNCOMMON, mage.cards.e.ErsatzGnomes.class)); + cards.add(new SetCardInfo("Ether Well", 65, Rarity.UNCOMMON, mage.cards.e.EtherWell.class)); + cards.add(new SetCardInfo("Ethereal Champion", 15, Rarity.RARE, mage.cards.e.EtherealChampion.class)); + cards.add(new SetCardInfo("Fallow Earth", 214, Rarity.UNCOMMON, mage.cards.f.FallowEarth.class)); + cards.add(new SetCardInfo("Favorable Destiny", 16, Rarity.UNCOMMON, mage.cards.f.FavorableDestiny.class)); + cards.add(new SetCardInfo("Femeref Archers", 215, Rarity.UNCOMMON, mage.cards.f.FemerefArchers.class)); + cards.add(new SetCardInfo("Femeref Healer", 17, Rarity.COMMON, mage.cards.f.FemerefHealer.class)); + cards.add(new SetCardInfo("Femeref Knight", 18, Rarity.COMMON, mage.cards.f.FemerefKnight.class)); + cards.add(new SetCardInfo("Femeref Scouts", 19, Rarity.COMMON, mage.cards.f.FemerefScouts.class)); + cards.add(new SetCardInfo("Feral Shadow", 122, Rarity.COMMON, mage.cards.f.FeralShadow.class)); + cards.add(new SetCardInfo("Fetid Horror", 123, Rarity.COMMON, mage.cards.f.FetidHorror.class)); + cards.add(new SetCardInfo("Final Fortune", 173, Rarity.RARE, mage.cards.f.FinalFortune.class)); + cards.add(new SetCardInfo("Fire Diamond", 302, Rarity.UNCOMMON, mage.cards.f.FireDiamond.class)); + cards.add(new SetCardInfo("Firebreathing", 174, Rarity.COMMON, mage.cards.f.Firebreathing.class)); + cards.add(new SetCardInfo("Flame Elemental", 175, Rarity.UNCOMMON, mage.cards.f.FlameElemental.class)); + cards.add(new SetCardInfo("Flare", 176, Rarity.COMMON, mage.cards.f.Flare.class)); + cards.add(new SetCardInfo("Flash", 66, Rarity.RARE, mage.cards.f.Flash.class)); + cards.add(new SetCardInfo("Flood Plain", 326, Rarity.UNCOMMON, mage.cards.f.FloodPlain.class)); + cards.add(new SetCardInfo("Floodgate", 67, Rarity.UNCOMMON, mage.cards.f.Floodgate.class)); + cards.add(new SetCardInfo("Fog", 216, Rarity.COMMON, mage.cards.f.Fog.class)); + cards.add(new SetCardInfo("Foratog", 217, Rarity.UNCOMMON, mage.cards.f.Foratog.class)); + cards.add(new SetCardInfo("Forbidden Crypt", 124, Rarity.RARE, mage.cards.f.ForbiddenCrypt.class)); + cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forsaken Wastes", 125, Rarity.RARE, mage.cards.f.ForsakenWastes.class)); + cards.add(new SetCardInfo("Frenetic Efreet", 264, Rarity.RARE, mage.cards.f.FreneticEfreet.class)); + cards.add(new SetCardInfo("Giant Mantis", 218, Rarity.COMMON, mage.cards.g.GiantMantis.class)); + cards.add(new SetCardInfo("Gibbering Hyenas", 219, Rarity.COMMON, mage.cards.g.GibberingHyenas.class)); + cards.add(new SetCardInfo("Goblin Elite Infantry", 177, Rarity.COMMON, mage.cards.g.GoblinEliteInfantry.class)); + cards.add(new SetCardInfo("Goblin Scouts", 178, Rarity.UNCOMMON, mage.cards.g.GoblinScouts.class)); + cards.add(new SetCardInfo("Goblin Soothsayer", 179, Rarity.UNCOMMON, mage.cards.g.GoblinSoothsayer.class)); + cards.add(new SetCardInfo("Goblin Tinkerer", 180, Rarity.COMMON, mage.cards.g.GoblinTinkerer.class)); + cards.add(new SetCardInfo("Granger Guildmage", 220, Rarity.COMMON, mage.cards.g.GrangerGuildmage.class)); + cards.add(new SetCardInfo("Grasslands", 327, Rarity.UNCOMMON, mage.cards.g.Grasslands.class)); + cards.add(new SetCardInfo("Grave Servitude", 126, Rarity.COMMON, mage.cards.g.GraveServitude.class)); + cards.add(new SetCardInfo("Gravebane Zombie", 127, Rarity.COMMON, mage.cards.g.GravebaneZombie.class)); + cards.add(new SetCardInfo("Grim Feast", 265, Rarity.RARE, mage.cards.g.GrimFeast.class)); + cards.add(new SetCardInfo("Grinning Totem", 303, Rarity.RARE, mage.cards.g.GrinningTotem.class)); + cards.add(new SetCardInfo("Hakim, Loreweaver", 68, Rarity.RARE, mage.cards.h.HakimLoreweaver.class)); + cards.add(new SetCardInfo("Hall of Gemstone", 221, Rarity.RARE, mage.cards.h.HallOfGemstone.class)); + cards.add(new SetCardInfo("Hammer of Bogardan", 181, Rarity.RARE, mage.cards.h.HammerOfBogardan.class)); + cards.add(new SetCardInfo("Harbinger of Night", 128, Rarity.RARE, mage.cards.h.HarbingerOfNight.class)); + cards.add(new SetCardInfo("Harbor Guardian", 266, Rarity.UNCOMMON, mage.cards.h.HarborGuardian.class)); + cards.add(new SetCardInfo("Harmattan Efreet", 69, Rarity.UNCOMMON, mage.cards.h.HarmattanEfreet.class)); + cards.add(new SetCardInfo("Hazerider Drake", 268, Rarity.UNCOMMON, mage.cards.h.HazeriderDrake.class)); + cards.add(new SetCardInfo("Healing Salve", 20, Rarity.COMMON, mage.cards.h.HealingSalve.class)); + cards.add(new SetCardInfo("Hivis of the Scale", 182, Rarity.RARE, mage.cards.h.HivisOfTheScale.class)); + cards.add(new SetCardInfo("Horrible Hordes", 304, Rarity.UNCOMMON, mage.cards.h.HorribleHordes.class)); + cards.add(new SetCardInfo("Igneous Golem", 305, Rarity.UNCOMMON, mage.cards.i.IgneousGolem.class)); + cards.add(new SetCardInfo("Illicit Auction", 183, Rarity.RARE, mage.cards.i.IllicitAuction.class)); + cards.add(new SetCardInfo("Illumination", 21, Rarity.UNCOMMON, mage.cards.i.Illumination.class)); + cards.add(new SetCardInfo("Incinerate", 184, Rarity.COMMON, mage.cards.i.Incinerate.class)); + cards.add(new SetCardInfo("Infernal Contract", 129, Rarity.RARE, mage.cards.i.InfernalContract.class)); + cards.add(new SetCardInfo("Iron Tusk Elephant", 22, Rarity.UNCOMMON, mage.cards.i.IronTuskElephant.class)); + cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ivory Charm", 23, Rarity.COMMON, mage.cards.i.IvoryCharm.class)); + cards.add(new SetCardInfo("Jolrael's Centaur", 222, Rarity.COMMON, mage.cards.j.JolraelsCentaur.class)); + cards.add(new SetCardInfo("Jolt", 70, Rarity.COMMON, mage.cards.j.Jolt.class)); + cards.add(new SetCardInfo("Jungle Patrol", 223, Rarity.RARE, mage.cards.j.JunglePatrol.class)); + cards.add(new SetCardInfo("Jungle Troll", 269, Rarity.UNCOMMON, mage.cards.j.JungleTroll.class)); + cards.add(new SetCardInfo("Jungle Wurm", 224, Rarity.COMMON, mage.cards.j.JungleWurm.class)); + cards.add(new SetCardInfo("Kaervek's Hex", 130, Rarity.UNCOMMON, mage.cards.k.KaerveksHex.class)); + cards.add(new SetCardInfo("Kaervek's Purge", 270, Rarity.UNCOMMON, mage.cards.k.KaerveksPurge.class)); + cards.add(new SetCardInfo("Kaervek's Torch", 185, Rarity.COMMON, mage.cards.k.KaerveksTorch.class)); + cards.add(new SetCardInfo("Karoo Meerkat", 225, Rarity.UNCOMMON, mage.cards.k.KarooMeerkat.class)); + cards.add(new SetCardInfo("Kukemssa Pirates", 71, Rarity.RARE, mage.cards.k.KukemssaPirates.class)); + cards.add(new SetCardInfo("Kukemssa Serpent", 72, Rarity.COMMON, mage.cards.k.KukemssaSerpent.class)); + cards.add(new SetCardInfo("Lead Golem", 306, Rarity.UNCOMMON, mage.cards.l.LeadGolem.class)); + cards.add(new SetCardInfo("Leering Gargoyle", 271, Rarity.RARE, mage.cards.l.LeeringGargoyle.class)); + cards.add(new SetCardInfo("Lightning Reflexes", 186, Rarity.COMMON, mage.cards.l.LightningReflexes.class)); + cards.add(new SetCardInfo("Lion's Eye Diamond", 307, Rarity.RARE, mage.cards.l.LionsEyeDiamond.class)); + cards.add(new SetCardInfo("Locust Swarm", 226, Rarity.UNCOMMON, mage.cards.l.LocustSwarm.class)); + cards.add(new SetCardInfo("Lure of Prey", 227, Rarity.RARE, mage.cards.l.LureOfPrey.class)); + cards.add(new SetCardInfo("Malignant Growth", 272, Rarity.RARE, mage.cards.m.MalignantGrowth.class)); + cards.add(new SetCardInfo("Mana Prism", 308, Rarity.UNCOMMON, mage.cards.m.ManaPrism.class)); + cards.add(new SetCardInfo("Mangara's Tome", 309, Rarity.RARE, mage.cards.m.MangarasTome.class)); + cards.add(new SetCardInfo("Marble Diamond", 310, Rarity.UNCOMMON, mage.cards.m.MarbleDiamond.class)); + cards.add(new SetCardInfo("Maro", 228, Rarity.RARE, mage.cards.m.Maro.class)); + cards.add(new SetCardInfo("Melesse Spirit", 27, Rarity.UNCOMMON, mage.cards.m.MelesseSpirit.class)); + cards.add(new SetCardInfo("Memory Lapse", 74, Rarity.COMMON, mage.cards.m.MemoryLapse.class)); + cards.add(new SetCardInfo("Merfolk Raiders", 75, Rarity.COMMON, mage.cards.m.MerfolkRaiders.class)); + cards.add(new SetCardInfo("Merfolk Seer", 76, Rarity.COMMON, mage.cards.m.MerfolkSeer.class)); + cards.add(new SetCardInfo("Mind Harness", 78, Rarity.UNCOMMON, mage.cards.m.MindHarness.class)); + cards.add(new SetCardInfo("Mire Shade", 131, Rarity.UNCOMMON, mage.cards.m.MireShade.class)); + cards.add(new SetCardInfo("Misers' Cage", 311, Rarity.RARE, mage.cards.m.MisersCage.class)); + cards.add(new SetCardInfo("Mist Dragon", 79, Rarity.RARE, mage.cards.m.MistDragon.class)); + cards.add(new SetCardInfo("Moss Diamond", 312, Rarity.UNCOMMON, mage.cards.m.MossDiamond.class)); + cards.add(new SetCardInfo("Mountain Valley", 328, Rarity.UNCOMMON, mage.cards.m.MountainValley.class)); + cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mtenda Griffin", 28, Rarity.UNCOMMON, mage.cards.m.MtendaGriffin.class)); + cards.add(new SetCardInfo("Mtenda Herder", 29, Rarity.COMMON, mage.cards.m.MtendaHerder.class)); + cards.add(new SetCardInfo("Mtenda Lion", 230, Rarity.COMMON, mage.cards.m.MtendaLion.class)); + cards.add(new SetCardInfo("Mystical Tutor", 80, Rarity.UNCOMMON, mage.cards.m.MysticalTutor.class)); + cards.add(new SetCardInfo("Natural Balance", 231, Rarity.RARE, mage.cards.n.NaturalBalance.class)); + cards.add(new SetCardInfo("Nettletooth Djinn", 232, Rarity.UNCOMMON, mage.cards.n.NettletoothDjinn.class)); + cards.add(new SetCardInfo("Noble Elephant", 30, Rarity.COMMON, mage.cards.n.NobleElephant.class)); + cards.add(new SetCardInfo("Nocturnal Raid", 132, Rarity.UNCOMMON, mage.cards.n.NocturnalRaid.class)); + cards.add(new SetCardInfo("Null Chamber", 31, Rarity.RARE, mage.cards.n.NullChamber.class)); + cards.add(new SetCardInfo("Pacifism", 32, Rarity.COMMON, mage.cards.p.Pacifism.class)); + cards.add(new SetCardInfo("Painful Memories", 133, Rarity.UNCOMMON, mage.cards.p.PainfulMemories.class)); + cards.add(new SetCardInfo("Patagia Golem", 313, Rarity.UNCOMMON, mage.cards.p.PatagiaGolem.class)); + cards.add(new SetCardInfo("Paupers' Cage", 314, Rarity.RARE, mage.cards.p.PaupersCage.class)); + cards.add(new SetCardInfo("Pearl Dragon", 33, Rarity.RARE, mage.cards.p.PearlDragon.class)); + cards.add(new SetCardInfo("Phyrexian Dreadnought", 315, Rarity.RARE, mage.cards.p.PhyrexianDreadnought.class)); + cards.add(new SetCardInfo("Phyrexian Purge", 273, Rarity.RARE, mage.cards.p.PhyrexianPurge.class)); + cards.add(new SetCardInfo("Phyrexian Tribute", 134, Rarity.RARE, mage.cards.p.PhyrexianTribute.class)); + cards.add(new SetCardInfo("Phyrexian Vault", 316, Rarity.UNCOMMON, mage.cards.p.PhyrexianVault.class)); + cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Political Trickery", 81, Rarity.RARE, mage.cards.p.PoliticalTrickery.class)); + cards.add(new SetCardInfo("Polymorph", 82, Rarity.RARE, mage.cards.p.Polymorph.class)); + cards.add(new SetCardInfo("Power Sink", 83, Rarity.COMMON, mage.cards.p.PowerSink.class)); + cards.add(new SetCardInfo("Prismatic Boon", 274, Rarity.UNCOMMON, mage.cards.p.PrismaticBoon.class)); + cards.add(new SetCardInfo("Prismatic Circle", 34, Rarity.COMMON, mage.cards.p.PrismaticCircle.class)); + cards.add(new SetCardInfo("Prismatic Lace", 84, Rarity.RARE, mage.cards.p.PrismaticLace.class)); + cards.add(new SetCardInfo("Psychic Transfer", 85, Rarity.RARE, mage.cards.p.PsychicTransfer.class)); + cards.add(new SetCardInfo("Purgatory", 275, Rarity.RARE, mage.cards.p.Purgatory.class)); + cards.add(new SetCardInfo("Purraj of Urborg", 135, Rarity.RARE, mage.cards.p.PurrajOfUrborg.class)); + cards.add(new SetCardInfo("Pyric Salamander", 187, Rarity.COMMON, mage.cards.p.PyricSalamander.class)); + cards.add(new SetCardInfo("Quirion Elves", 234, Rarity.COMMON, mage.cards.q.QuirionElves.class)); + cards.add(new SetCardInfo("Radiant Essence", 276, Rarity.UNCOMMON, mage.cards.r.RadiantEssence.class)); + cards.add(new SetCardInfo("Raging Spirit", 188, Rarity.COMMON, mage.cards.r.RagingSpirit.class)); + cards.add(new SetCardInfo("Rampant Growth", 235, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); + cards.add(new SetCardInfo("Rashida Scalebane", 35, Rarity.RARE, mage.cards.r.RashidaScalebane.class)); + cards.add(new SetCardInfo("Ravenous Vampire", 136, Rarity.UNCOMMON, mage.cards.r.RavenousVampire.class)); + cards.add(new SetCardInfo("Ray of Command", 86, Rarity.COMMON, mage.cards.r.RayOfCommand.class)); + cards.add(new SetCardInfo("Razor Pendulum", 317, Rarity.RARE, mage.cards.r.RazorPendulum.class)); + cards.add(new SetCardInfo("Reality Ripple", 87, Rarity.COMMON, mage.cards.r.RealityRipple.class)); + cards.add(new SetCardInfo("Reckless Embermage", 189, Rarity.RARE, mage.cards.r.RecklessEmbermage.class)); + cards.add(new SetCardInfo("Regeneration", 236, Rarity.COMMON, mage.cards.r.Regeneration.class)); + cards.add(new SetCardInfo("Reign of Chaos", 190, Rarity.UNCOMMON, mage.cards.r.ReignOfChaos.class)); + cards.add(new SetCardInfo("Reign of Terror", 137, Rarity.UNCOMMON, mage.cards.r.ReignOfTerror.class)); + cards.add(new SetCardInfo("Reparations", 278, Rarity.RARE, mage.cards.r.Reparations.class)); + cards.add(new SetCardInfo("Restless Dead", 138, Rarity.COMMON, mage.cards.r.RestlessDead.class)); + cards.add(new SetCardInfo("Ritual of Steel", 36, Rarity.COMMON, mage.cards.r.RitualOfSteel.class)); + cards.add(new SetCardInfo("Rock Basilisk", 279, Rarity.RARE, mage.cards.r.RockBasilisk.class)); + cards.add(new SetCardInfo("Rocky Tar Pit", 329, Rarity.UNCOMMON, mage.cards.r.RockyTarPit.class)); + cards.add(new SetCardInfo("Roots of Life", 237, Rarity.UNCOMMON, mage.cards.r.RootsOfLife.class)); + cards.add(new SetCardInfo("Sabertooth Cobra", 238, Rarity.COMMON, mage.cards.s.SabertoothCobra.class)); + cards.add(new SetCardInfo("Sacred Mesa", 37, Rarity.RARE, mage.cards.s.SacredMesa.class)); + cards.add(new SetCardInfo("Sandbar Crocodile", 88, Rarity.COMMON, mage.cards.s.SandbarCrocodile.class)); + cards.add(new SetCardInfo("Sandstorm", 239, Rarity.COMMON, mage.cards.s.Sandstorm.class)); + cards.add(new SetCardInfo("Sapphire Charm", 89, Rarity.COMMON, mage.cards.s.SapphireCharm.class)); + cards.add(new SetCardInfo("Savage Twister", 280, Rarity.UNCOMMON, mage.cards.s.SavageTwister.class)); + cards.add(new SetCardInfo("Sawback Manticore", 281, Rarity.RARE, mage.cards.s.SawbackManticore.class)); + cards.add(new SetCardInfo("Sea Scryer", 90, Rarity.COMMON, mage.cards.s.SeaScryer.class)); + cards.add(new SetCardInfo("Searing Spear Askari", 191, Rarity.COMMON, mage.cards.s.SearingSpearAskari.class)); + cards.add(new SetCardInfo("Seedling Charm", 240, Rarity.COMMON, mage.cards.s.SeedlingCharm.class)); + cards.add(new SetCardInfo("Seeds of Innocence", 241, Rarity.RARE, mage.cards.s.SeedsOfInnocence.class)); + cards.add(new SetCardInfo("Serene Heart", 242, Rarity.COMMON, mage.cards.s.SereneHeart.class)); + cards.add(new SetCardInfo("Sewer Rats", 139, Rarity.COMMON, mage.cards.s.SewerRats.class)); + cards.add(new SetCardInfo("Shadow Guildmage", 140, Rarity.COMMON, mage.cards.s.ShadowGuildmage.class)); + cards.add(new SetCardInfo("Shallow Grave", 141, Rarity.RARE, mage.cards.s.ShallowGrave.class)); + cards.add(new SetCardInfo("Shaper Guildmage", 91, Rarity.COMMON, mage.cards.s.ShaperGuildmage.class)); + cards.add(new SetCardInfo("Shauku's Minion", 283, Rarity.UNCOMMON, mage.cards.s.ShaukusMinion.class)); + cards.add(new SetCardInfo("Shauku, Endbringer", 142, Rarity.RARE, mage.cards.s.ShaukuEndbringer.class)); + cards.add(new SetCardInfo("Shimmer", 92, Rarity.RARE, mage.cards.s.Shimmer.class)); + cards.add(new SetCardInfo("Sidar Jabari", 39, Rarity.RARE, mage.cards.s.SidarJabari.class)); + cards.add(new SetCardInfo("Skulking Ghost", 143, Rarity.COMMON, mage.cards.s.SkulkingGhost.class)); + cards.add(new SetCardInfo("Sky Diamond", 319, Rarity.UNCOMMON, mage.cards.s.SkyDiamond.class)); + cards.add(new SetCardInfo("Soar", 93, Rarity.COMMON, mage.cards.s.Soar.class)); + cards.add(new SetCardInfo("Soul Echo", 40, Rarity.RARE, mage.cards.s.SoulEcho.class)); + cards.add(new SetCardInfo("Soul Rend", 144, Rarity.UNCOMMON, mage.cards.s.SoulRend.class)); + cards.add(new SetCardInfo("Soulshriek", 145, Rarity.COMMON, mage.cards.s.Soulshriek.class)); + cards.add(new SetCardInfo("Spectral Guardian", 41, Rarity.RARE, mage.cards.s.SpectralGuardian.class)); + cards.add(new SetCardInfo("Spirit of the Night", 146, Rarity.RARE, mage.cards.s.SpiritOfTheNight.class)); + cards.add(new SetCardInfo("Spitting Earth", 193, Rarity.COMMON, mage.cards.s.SpittingEarth.class)); + cards.add(new SetCardInfo("Stalking Tiger", 243, Rarity.COMMON, mage.cards.s.StalkingTiger.class)); + cards.add(new SetCardInfo("Stone Rain", 194, Rarity.COMMON, mage.cards.s.StoneRain.class)); + cards.add(new SetCardInfo("Stupor", 147, Rarity.UNCOMMON, mage.cards.s.Stupor.class)); + cards.add(new SetCardInfo("Subterranean Spirit", 195, Rarity.RARE, mage.cards.s.SubterraneanSpirit.class)); + cards.add(new SetCardInfo("Sunweb", 42, Rarity.RARE, mage.cards.s.Sunweb.class)); + cards.add(new SetCardInfo("Superior Numbers", 244, Rarity.UNCOMMON, mage.cards.s.SuperiorNumbers.class)); + cards.add(new SetCardInfo("Suq'Ata Firewalker", 94, Rarity.UNCOMMON, mage.cards.s.SuqAtaFirewalker.class)); + cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Talruum Minotaur", 196, Rarity.COMMON, mage.cards.t.TalruumMinotaur.class)); + cards.add(new SetCardInfo("Taniwha", 95, Rarity.RARE, mage.cards.t.Taniwha.class)); + cards.add(new SetCardInfo("Teeka's Dragon", 320, Rarity.RARE, mage.cards.t.TeekasDragon.class)); + cards.add(new SetCardInfo("Teferi's Curse", 96, Rarity.COMMON, mage.cards.t.TeferisCurse.class)); + cards.add(new SetCardInfo("Teferi's Drake", 97, Rarity.COMMON, mage.cards.t.TeferisDrake.class)); + cards.add(new SetCardInfo("Teferi's Isle", 330, Rarity.RARE, mage.cards.t.TeferisIsle.class)); + cards.add(new SetCardInfo("Telim'Tor", 197, Rarity.RARE, mage.cards.t.TelimTor.class)); + cards.add(new SetCardInfo("Telim'Tor's Darts", 321, Rarity.UNCOMMON, mage.cards.t.TelimTorsDarts.class)); + cards.add(new SetCardInfo("Telim'Tor's Edict", 198, Rarity.RARE, mage.cards.t.TelimTorsEdict.class)); + cards.add(new SetCardInfo("Teremko Griffin", 43, Rarity.COMMON, mage.cards.t.TeremkoGriffin.class)); + cards.add(new SetCardInfo("Thirst", 99, Rarity.COMMON, mage.cards.t.Thirst.class)); + cards.add(new SetCardInfo("Tidal Wave", 100, Rarity.UNCOMMON, mage.cards.t.TidalWave.class)); + cards.add(new SetCardInfo("Tombstone Stairwell", 149, Rarity.RARE, mage.cards.t.TombstoneStairwell.class)); + cards.add(new SetCardInfo("Tranquil Domain", 245, Rarity.COMMON, mage.cards.t.TranquilDomain.class)); + cards.add(new SetCardInfo("Tropical Storm", 246, Rarity.UNCOMMON, mage.cards.t.TropicalStorm.class)); + cards.add(new SetCardInfo("Uktabi Faerie", 247, Rarity.COMMON, mage.cards.u.UktabiFaerie.class)); + cards.add(new SetCardInfo("Uktabi Wildcats", 248, Rarity.RARE, mage.cards.u.UktabiWildcats.class)); + cards.add(new SetCardInfo("Unfulfilled Desires", 285, Rarity.RARE, mage.cards.u.UnfulfilledDesires.class)); + cards.add(new SetCardInfo("Unseen Walker", 249, Rarity.UNCOMMON, mage.cards.u.UnseenWalker.class)); + cards.add(new SetCardInfo("Unyaro Bee Sting", 250, Rarity.UNCOMMON, mage.cards.u.UnyaroBeeSting.class)); + cards.add(new SetCardInfo("Unyaro Griffin", 44, Rarity.UNCOMMON, mage.cards.u.UnyaroGriffin.class)); + cards.add(new SetCardInfo("Urborg Panther", 150, Rarity.COMMON, mage.cards.u.UrborgPanther.class)); + cards.add(new SetCardInfo("Vaporous Djinn", 101, Rarity.UNCOMMON, mage.cards.v.VaporousDjinn.class)); + cards.add(new SetCardInfo("Ventifact Bottle", 323, Rarity.RARE, mage.cards.v.VentifactBottle.class)); + cards.add(new SetCardInfo("Viashino Warrior", 200, Rarity.COMMON, mage.cards.v.ViashinoWarrior.class)); + cards.add(new SetCardInfo("Vigilant Martyr", 45, Rarity.UNCOMMON, mage.cards.v.VigilantMartyr.class)); + cards.add(new SetCardInfo("Village Elder", 251, Rarity.COMMON, mage.cards.v.VillageElder.class)); + cards.add(new SetCardInfo("Vitalizing Cascade", 286, Rarity.UNCOMMON, mage.cards.v.VitalizingCascade.class)); + cards.add(new SetCardInfo("Volcanic Dragon", 201, Rarity.RARE, mage.cards.v.VolcanicDragon.class)); + cards.add(new SetCardInfo("Volcanic Geyser", 202, Rarity.UNCOMMON, mage.cards.v.VolcanicGeyser.class)); + cards.add(new SetCardInfo("Waiting in the Weeds", 252, Rarity.RARE, mage.cards.w.WaitingInTheWeeds.class)); + cards.add(new SetCardInfo("Wall of Resistance", 46, Rarity.COMMON, mage.cards.w.WallOfResistance.class)); + cards.add(new SetCardInfo("Wall of Roots", 253, Rarity.COMMON, mage.cards.w.WallOfRoots.class)); + cards.add(new SetCardInfo("Ward of Lights", 47, Rarity.COMMON, mage.cards.w.WardOfLights.class)); + cards.add(new SetCardInfo("Warping Wurm", 287, Rarity.RARE, mage.cards.w.WarpingWurm.class)); + cards.add(new SetCardInfo("Wave Elemental", 102, Rarity.UNCOMMON, mage.cards.w.WaveElemental.class)); + cards.add(new SetCardInfo("Wellspring", 288, Rarity.RARE, mage.cards.w.Wellspring.class)); + cards.add(new SetCardInfo("Wild Elephant", 254, Rarity.COMMON, mage.cards.w.WildElephant.class)); + cards.add(new SetCardInfo("Wildfire Emissary", 203, Rarity.UNCOMMON, mage.cards.w.WildfireEmissary.class)); + cards.add(new SetCardInfo("Windreaper Falcon", 289, Rarity.UNCOMMON, mage.cards.w.WindreaperFalcon.class)); + cards.add(new SetCardInfo("Withering Boon", 152, Rarity.UNCOMMON, mage.cards.w.WitheringBoon.class)); + cards.add(new SetCardInfo("Worldly Tutor", 255, Rarity.UNCOMMON, mage.cards.w.WorldlyTutor.class)); + cards.add(new SetCardInfo("Zebra Unicorn", 290, Rarity.UNCOMMON, mage.cards.z.ZebraUnicorn.class)); + cards.add(new SetCardInfo("Zhalfirin Commander", 49, Rarity.UNCOMMON, mage.cards.z.ZhalfirinCommander.class)); + cards.add(new SetCardInfo("Zhalfirin Knight", 50, Rarity.COMMON, mage.cards.z.ZhalfirinKnight.class)); + cards.add(new SetCardInfo("Zirilan of the Claw", 204, Rarity.RARE, mage.cards.z.ZirilanOfTheClaw.class)); + cards.add(new SetCardInfo("Zombie Mob", 153, Rarity.UNCOMMON, mage.cards.z.ZombieMob.class)); + cards.add(new SetCardInfo("Zuberi, Golden Feather", 51, Rarity.RARE, mage.cards.z.ZuberiGoldenFeather.class)); + } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Prophecy.java b/Mage.Sets/src/mage/sets/Prophecy.java index 9ff9dde2a7..fda54f8e3a 100644 --- a/Mage.Sets/src/mage/sets/Prophecy.java +++ b/Mage.Sets/src/mage/sets/Prophecy.java @@ -1,175 +1,175 @@ - -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author North - */ -public final class Prophecy extends ExpansionSet { - - private static final Prophecy instance = new Prophecy(); - - public static Prophecy getInstance() { - return instance; - } - - private Prophecy() { - super("Prophecy", "PCY", ExpansionSet.buildDate(2000, 4, 27), SetType.EXPANSION); - this.blockName = "Masques"; - this.parentSet = MercadianMasques.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Abolish", 1, Rarity.UNCOMMON, mage.cards.a.Abolish.class)); - cards.add(new SetCardInfo("Agent of Shauku", 55, Rarity.COMMON, mage.cards.a.AgentOfShauku.class)); - cards.add(new SetCardInfo("Alexi's Cloak", 29, Rarity.COMMON, mage.cards.a.AlexisCloak.class)); - cards.add(new SetCardInfo("Alexi, Zephyr Mage", 28, Rarity.RARE, mage.cards.a.AlexiZephyrMage.class)); - cards.add(new SetCardInfo("Aura Fracture", 2, Rarity.COMMON, mage.cards.a.AuraFracture.class)); - cards.add(new SetCardInfo("Avatar of Fury", 82, Rarity.RARE, mage.cards.a.AvatarOfFury.class)); - cards.add(new SetCardInfo("Avatar of Hope", 3, Rarity.RARE, mage.cards.a.AvatarOfHope.class)); - cards.add(new SetCardInfo("Avatar of Might", 109, Rarity.RARE, mage.cards.a.AvatarOfMight.class)); - cards.add(new SetCardInfo("Avatar of Will", 30, Rarity.RARE, mage.cards.a.AvatarOfWill.class)); - cards.add(new SetCardInfo("Avatar of Woe", 56, Rarity.RARE, mage.cards.a.AvatarOfWoe.class)); - cards.add(new SetCardInfo("Barbed Field", 83, Rarity.UNCOMMON, mage.cards.b.BarbedField.class)); - cards.add(new SetCardInfo("Blessed Wind", 4, Rarity.RARE, mage.cards.b.BlessedWind.class)); - cards.add(new SetCardInfo("Bog Elemental", 57, Rarity.RARE, mage.cards.b.BogElemental.class)); - cards.add(new SetCardInfo("Bog Glider", 58, Rarity.COMMON, mage.cards.b.BogGlider.class)); - cards.add(new SetCardInfo("Branded Brawlers", 84, Rarity.COMMON, mage.cards.b.BrandedBrawlers.class)); - cards.add(new SetCardInfo("Brutal Suppression", 85, Rarity.UNCOMMON, mage.cards.b.BrutalSuppression.class)); - cards.add(new SetCardInfo("Calming Verse", 110, Rarity.COMMON, mage.cards.c.CalmingVerse.class)); - cards.add(new SetCardInfo("Celestial Convergence", 5, Rarity.RARE, mage.cards.c.CelestialConvergence.class)); - cards.add(new SetCardInfo("Chilling Apparition", 59, Rarity.UNCOMMON, mage.cards.c.ChillingApparition.class)); - cards.add(new SetCardInfo("Chimeric Idol", 136, Rarity.UNCOMMON, mage.cards.c.ChimericIdol.class)); - cards.add(new SetCardInfo("Citadel of Pain", 86, Rarity.UNCOMMON, mage.cards.c.CitadelOfPain.class)); - cards.add(new SetCardInfo("Coastal Hornclaw", 31, Rarity.COMMON, mage.cards.c.CoastalHornclaw.class)); - cards.add(new SetCardInfo("Coffin Puppets", 60, Rarity.RARE, mage.cards.c.CoffinPuppets.class)); - cards.add(new SetCardInfo("Copper-Leaf Angel", 137, Rarity.RARE, mage.cards.c.CopperLeafAngel.class)); - cards.add(new SetCardInfo("Darba", 111, Rarity.UNCOMMON, mage.cards.d.Darba.class)); - cards.add(new SetCardInfo("Death Charmer", 61, Rarity.COMMON, mage.cards.d.DeathCharmer.class)); - cards.add(new SetCardInfo("Denying Wind", 32, Rarity.RARE, mage.cards.d.DenyingWind.class)); - cards.add(new SetCardInfo("Despoil", 62, Rarity.COMMON, mage.cards.d.Despoil.class)); - cards.add(new SetCardInfo("Devastate", 87, Rarity.COMMON, mage.cards.d.Devastate.class)); - cards.add(new SetCardInfo("Diving Griffin", 6, Rarity.COMMON, mage.cards.d.DivingGriffin.class)); - cards.add(new SetCardInfo("Dual Nature", 112, Rarity.RARE, mage.cards.d.DualNature.class)); - cards.add(new SetCardInfo("Elephant Resurgence", 113, Rarity.RARE, mage.cards.e.ElephantResurgence.class)); - cards.add(new SetCardInfo("Endbringer's Revel", 63, Rarity.UNCOMMON, mage.cards.e.EndbringersRevel.class)); - cards.add(new SetCardInfo("Entangler", 7, Rarity.UNCOMMON, mage.cards.e.Entangler.class)); - cards.add(new SetCardInfo("Excavation", 33, Rarity.UNCOMMON, mage.cards.e.Excavation.class)); - cards.add(new SetCardInfo("Excise", 8, Rarity.COMMON, mage.cards.e.Excise.class)); - cards.add(new SetCardInfo("Fault Riders", 88, Rarity.COMMON, mage.cards.f.FaultRiders.class)); - cards.add(new SetCardInfo("Fen Stalker", 64, Rarity.COMMON, mage.cards.f.FenStalker.class)); - cards.add(new SetCardInfo("Fickle Efreet", 89, Rarity.RARE, mage.cards.f.FickleEfreet.class)); - cards.add(new SetCardInfo("Flameshot", 90, Rarity.UNCOMMON, mage.cards.f.Flameshot.class)); - cards.add(new SetCardInfo("Flay", 65, Rarity.COMMON, mage.cards.f.Flay.class)); - cards.add(new SetCardInfo("Flowering Field", 9, Rarity.UNCOMMON, mage.cards.f.FloweringField.class)); - cards.add(new SetCardInfo("Foil", 34, Rarity.UNCOMMON, mage.cards.f.Foil.class)); - cards.add(new SetCardInfo("Forgotten Harvest", 114, Rarity.RARE, mage.cards.f.ForgottenHarvest.class)); - cards.add(new SetCardInfo("Glittering Lion", 10, Rarity.UNCOMMON, mage.cards.g.GlitteringLion.class)); - cards.add(new SetCardInfo("Glittering Lynx", 11, Rarity.COMMON, mage.cards.g.GlitteringLynx.class)); - cards.add(new SetCardInfo("Greel's Caress", 67, Rarity.COMMON, mage.cards.g.GreelsCaress.class)); - cards.add(new SetCardInfo("Greel, Mind Raker", 66, Rarity.RARE, mage.cards.g.GreelMindRaker.class)); - cards.add(new SetCardInfo("Gulf Squid", 35, Rarity.COMMON, mage.cards.g.GulfSquid.class)); - cards.add(new SetCardInfo("Hazy Homunculus", 36, Rarity.COMMON, mage.cards.h.HazyHomunculus.class)); - cards.add(new SetCardInfo("Heightened Awareness", 37, Rarity.RARE, mage.cards.h.HeightenedAwareness.class)); - cards.add(new SetCardInfo("Hollow Warrior", 138, Rarity.UNCOMMON, mage.cards.h.HollowWarrior.class)); - cards.add(new SetCardInfo("Infernal Genesis", 68, Rarity.RARE, mage.cards.i.InfernalGenesis.class)); - cards.add(new SetCardInfo("Inflame", 91, Rarity.COMMON, mage.cards.i.Inflame.class)); - cards.add(new SetCardInfo("Jeweled Spirit", 12, Rarity.RARE, mage.cards.j.JeweledSpirit.class)); - cards.add(new SetCardInfo("Jolrael, Empress of Beasts", 115, Rarity.RARE, mage.cards.j.JolraelEmpressOfBeasts.class)); - cards.add(new SetCardInfo("Jolrael's Favor", 116, Rarity.COMMON, mage.cards.j.JolraelsFavor.class)); - cards.add(new SetCardInfo("Keldon Arsonist", 92, Rarity.UNCOMMON, mage.cards.k.KeldonArsonist.class)); - cards.add(new SetCardInfo("Keldon Battlewagon", 139, Rarity.RARE, mage.cards.k.KeldonBattlewagon.class)); - cards.add(new SetCardInfo("Keldon Berserker", 93, Rarity.COMMON, mage.cards.k.KeldonBerserker.class)); - cards.add(new SetCardInfo("Keldon Firebombers", 94, Rarity.RARE, mage.cards.k.KeldonFirebombers.class)); - cards.add(new SetCardInfo("Latulla, Keldon Overseer", 95, Rarity.RARE, mage.cards.l.LatullaKeldonOverseer.class)); - cards.add(new SetCardInfo("Latulla's Orders", 96, Rarity.COMMON, mage.cards.l.LatullasOrders.class)); - cards.add(new SetCardInfo("Lesser Gargadon", 97, Rarity.UNCOMMON, mage.cards.l.LesserGargadon.class)); - cards.add(new SetCardInfo("Living Terrain", 117, Rarity.UNCOMMON, mage.cards.l.LivingTerrain.class)); - cards.add(new SetCardInfo("Mageta's Boon", 14, Rarity.COMMON, mage.cards.m.MagetasBoon.class)); - cards.add(new SetCardInfo("Mageta the Lion", 13, Rarity.RARE, mage.cards.m.MagetaTheLion.class)); - cards.add(new SetCardInfo("Mana Vapors", 38, Rarity.UNCOMMON, mage.cards.m.ManaVapors.class)); - cards.add(new SetCardInfo("Marsh Boa", 118, Rarity.COMMON, mage.cards.m.MarshBoa.class)); - cards.add(new SetCardInfo("Mercenary Informer", 15, Rarity.RARE, mage.cards.m.MercenaryInformer.class)); - cards.add(new SetCardInfo("Mine Bearer", 16, Rarity.COMMON, mage.cards.m.MineBearer.class)); - cards.add(new SetCardInfo("Mirror Strike", 17, Rarity.UNCOMMON, mage.cards.m.MirrorStrike.class)); - cards.add(new SetCardInfo("Mungha Wurm", 119, Rarity.RARE, mage.cards.m.MunghaWurm.class)); - cards.add(new SetCardInfo("Nakaya Shade", 69, Rarity.UNCOMMON, mage.cards.n.NakayaShade.class)); - cards.add(new SetCardInfo("Noxious Field", 70, Rarity.UNCOMMON, mage.cards.n.NoxiousField.class)); - cards.add(new SetCardInfo("Outbreak", 71, Rarity.UNCOMMON, mage.cards.o.Outbreak.class)); - cards.add(new SetCardInfo("Overburden", 39, Rarity.RARE, mage.cards.o.Overburden.class)); - cards.add(new SetCardInfo("Panic Attack", 98, Rarity.COMMON, mage.cards.p.PanicAttack.class)); - cards.add(new SetCardInfo("Pit Raptor", 72, Rarity.UNCOMMON, mage.cards.p.PitRaptor.class)); - cards.add(new SetCardInfo("Plague Fiend", 73, Rarity.COMMON, mage.cards.p.PlagueFiend.class)); - cards.add(new SetCardInfo("Plague Wind", 74, Rarity.RARE, mage.cards.p.PlagueWind.class)); - cards.add(new SetCardInfo("Psychic Theft", 40, Rarity.RARE, mage.cards.p.PsychicTheft.class)); - cards.add(new SetCardInfo("Pygmy Razorback", 120, Rarity.COMMON, mage.cards.p.PygmyRazorback.class)); - cards.add(new SetCardInfo("Quicksilver Wall", 41, Rarity.UNCOMMON, mage.cards.q.QuicksilverWall.class)); - cards.add(new SetCardInfo("Rebel Informer", 75, Rarity.RARE, mage.cards.r.RebelInformer.class)); - cards.add(new SetCardInfo("Rethink", 42, Rarity.COMMON, mage.cards.r.Rethink.class)); - cards.add(new SetCardInfo("Reveille Squad", 18, Rarity.UNCOMMON, mage.cards.r.ReveilleSquad.class)); - cards.add(new SetCardInfo("Rhystic Cave", 142, Rarity.UNCOMMON, mage.cards.r.RhysticCave.class)); - cards.add(new SetCardInfo("Rhystic Circle", 19, Rarity.COMMON, mage.cards.r.RhysticCircle.class)); - cards.add(new SetCardInfo("Rhystic Deluge", 43, Rarity.COMMON, mage.cards.r.RhysticDeluge.class)); - cards.add(new SetCardInfo("Rhystic Lightning", 99, Rarity.COMMON, mage.cards.r.RhysticLightning.class)); - cards.add(new SetCardInfo("Rhystic Scrying", 44, Rarity.UNCOMMON, mage.cards.r.RhysticScrying.class)); - cards.add(new SetCardInfo("Rhystic Shield", 20, Rarity.COMMON, mage.cards.r.RhysticShield.class)); - cards.add(new SetCardInfo("Rhystic Study", 45, Rarity.COMMON, mage.cards.r.RhysticStudy.class)); - cards.add(new SetCardInfo("Rhystic Syphon", 76, Rarity.UNCOMMON, mage.cards.r.RhysticSyphon.class)); - cards.add(new SetCardInfo("Rhystic Tutor", 77, Rarity.RARE, mage.cards.r.RhysticTutor.class)); - cards.add(new SetCardInfo("Ribbon Snake", 46, Rarity.COMMON, mage.cards.r.RibbonSnake.class)); - cards.add(new SetCardInfo("Rib Cage Spider", 121, Rarity.COMMON, mage.cards.r.RibCageSpider.class)); - cards.add(new SetCardInfo("Ridgeline Rager", 100, Rarity.COMMON, mage.cards.r.RidgelineRager.class)); - cards.add(new SetCardInfo("Root Cage", 122, Rarity.UNCOMMON, mage.cards.r.RootCage.class)); - cards.add(new SetCardInfo("Samite Sanctuary", 21, Rarity.RARE, mage.cards.s.SamiteSanctuary.class)); - cards.add(new SetCardInfo("Scoria Cat", 101, Rarity.UNCOMMON, mage.cards.s.ScoriaCat.class)); - cards.add(new SetCardInfo("Search for Survivors", 102, Rarity.RARE, mage.cards.s.SearchForSurvivors.class)); - cards.add(new SetCardInfo("Searing Wind", 103, Rarity.RARE, mage.cards.s.SearingWind.class)); - cards.add(new SetCardInfo("Sheltering Prayers", 22, Rarity.RARE, mage.cards.s.ShelteringPrayers.class)); - cards.add(new SetCardInfo("Shield Dancer", 23, Rarity.UNCOMMON, mage.cards.s.ShieldDancer.class)); - cards.add(new SetCardInfo("Shrouded Serpent", 47, Rarity.RARE, mage.cards.s.ShroudedSerpent.class)); - cards.add(new SetCardInfo("Silt Crawler", 123, Rarity.COMMON, mage.cards.s.SiltCrawler.class)); - cards.add(new SetCardInfo("Snag", 124, Rarity.UNCOMMON, mage.cards.s.Snag.class)); - cards.add(new SetCardInfo("Soul Charmer", 24, Rarity.COMMON, mage.cards.s.SoulCharmer.class)); - cards.add(new SetCardInfo("Soul Strings", 78, Rarity.COMMON, mage.cards.s.SoulStrings.class)); - cards.add(new SetCardInfo("Spiketail Drake", 48, Rarity.UNCOMMON, mage.cards.s.SpiketailDrake.class)); - cards.add(new SetCardInfo("Spiketail Hatchling", 49, Rarity.COMMON, mage.cards.s.SpiketailHatchling.class)); - cards.add(new SetCardInfo("Spitting Spider", 125, Rarity.UNCOMMON, mage.cards.s.SpittingSpider.class)); - cards.add(new SetCardInfo("Spore Frog", 126, Rarity.COMMON, mage.cards.s.SporeFrog.class)); - cards.add(new SetCardInfo("Spur Grappler", 104, Rarity.COMMON, mage.cards.s.SpurGrappler.class)); - cards.add(new SetCardInfo("Squirrel Wrangler", 127, Rarity.RARE, mage.cards.s.SquirrelWrangler.class)); - cards.add(new SetCardInfo("Steal Strength", 79, Rarity.COMMON, mage.cards.s.StealStrength.class)); - cards.add(new SetCardInfo("Stormwatch Eagle", 50, Rarity.COMMON, mage.cards.s.StormwatchEagle.class)); - cards.add(new SetCardInfo("Sunken Field", 51, Rarity.UNCOMMON, mage.cards.s.SunkenField.class)); - cards.add(new SetCardInfo("Sword Dancer", 25, Rarity.UNCOMMON, mage.cards.s.SwordDancer.class)); - cards.add(new SetCardInfo("Task Mage Assembly", 105, Rarity.RARE, mage.cards.t.TaskMageAssembly.class)); - cards.add(new SetCardInfo("Thresher Beast", 128, Rarity.COMMON, mage.cards.t.ThresherBeast.class)); - cards.add(new SetCardInfo("Thrive", 129, Rarity.COMMON, mage.cards.t.Thrive.class)); - cards.add(new SetCardInfo("Trenching Steed", 26, Rarity.COMMON, mage.cards.t.TrenchingSteed.class)); - cards.add(new SetCardInfo("Troubled Healer", 27, Rarity.COMMON, mage.cards.t.TroubledHealer.class)); - cards.add(new SetCardInfo("Troublesome Spirit", 52, Rarity.RARE, mage.cards.t.TroublesomeSpirit.class)); - cards.add(new SetCardInfo("Verdant Field", 130, Rarity.UNCOMMON, mage.cards.v.VerdantField.class)); - cards.add(new SetCardInfo("Veteran Brawlers", 106, Rarity.RARE, mage.cards.v.VeteranBrawlers.class)); - cards.add(new SetCardInfo("Vintara Elephant", 131, Rarity.COMMON, mage.cards.v.VintaraElephant.class)); - cards.add(new SetCardInfo("Vintara Snapper", 132, Rarity.UNCOMMON, mage.cards.v.VintaraSnapper.class)); - cards.add(new SetCardInfo("Vitalizing Wind", 133, Rarity.RARE, mage.cards.v.VitalizingWind.class)); - cards.add(new SetCardInfo("Wall of Vipers", 80, Rarity.UNCOMMON, mage.cards.w.WallOfVipers.class)); - cards.add(new SetCardInfo("Well of Discovery", 140, Rarity.RARE, mage.cards.w.WellOfDiscovery.class)); - cards.add(new SetCardInfo("Well of Life", 141, Rarity.UNCOMMON, mage.cards.w.WellOfLife.class)); - cards.add(new SetCardInfo("Whip Sergeant", 107, Rarity.UNCOMMON, mage.cards.w.WhipSergeant.class)); - cards.add(new SetCardInfo("Whipstitched Zombie", 81, Rarity.COMMON, mage.cards.w.WhipstitchedZombie.class)); - cards.add(new SetCardInfo("Wild Might", 134, Rarity.COMMON, mage.cards.w.WildMight.class)); - cards.add(new SetCardInfo("Windscouter", 53, Rarity.UNCOMMON, mage.cards.w.Windscouter.class)); - cards.add(new SetCardInfo("Wing Storm", 135, Rarity.UNCOMMON, mage.cards.w.WingStorm.class)); - cards.add(new SetCardInfo("Wintermoon Mesa", 143, Rarity.RARE, mage.cards.w.WintermoonMesa.class)); - cards.add(new SetCardInfo("Withdraw", 54, Rarity.COMMON, mage.cards.w.Withdraw.class)); - cards.add(new SetCardInfo("Zerapa Minotaur", 108, Rarity.COMMON, mage.cards.z.ZerapaMinotaur.class)); - } -} + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author North + */ +public final class Prophecy extends ExpansionSet { + + private static final Prophecy instance = new Prophecy(); + + public static Prophecy getInstance() { + return instance; + } + + private Prophecy() { + super("Prophecy", "PCY", ExpansionSet.buildDate(2000, 4, 27), SetType.EXPANSION); + this.blockName = "Masques"; + this.parentSet = MercadianMasques.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Abolish", 1, Rarity.UNCOMMON, mage.cards.a.Abolish.class)); + cards.add(new SetCardInfo("Agent of Shauku", 55, Rarity.COMMON, mage.cards.a.AgentOfShauku.class)); + cards.add(new SetCardInfo("Alexi's Cloak", 29, Rarity.COMMON, mage.cards.a.AlexisCloak.class)); + cards.add(new SetCardInfo("Alexi, Zephyr Mage", 28, Rarity.RARE, mage.cards.a.AlexiZephyrMage.class)); + cards.add(new SetCardInfo("Aura Fracture", 2, Rarity.COMMON, mage.cards.a.AuraFracture.class)); + cards.add(new SetCardInfo("Avatar of Fury", 82, Rarity.RARE, mage.cards.a.AvatarOfFury.class)); + cards.add(new SetCardInfo("Avatar of Hope", 3, Rarity.RARE, mage.cards.a.AvatarOfHope.class)); + cards.add(new SetCardInfo("Avatar of Might", 109, Rarity.RARE, mage.cards.a.AvatarOfMight.class)); + cards.add(new SetCardInfo("Avatar of Will", 30, Rarity.RARE, mage.cards.a.AvatarOfWill.class)); + cards.add(new SetCardInfo("Avatar of Woe", 56, Rarity.RARE, mage.cards.a.AvatarOfWoe.class)); + cards.add(new SetCardInfo("Barbed Field", 83, Rarity.UNCOMMON, mage.cards.b.BarbedField.class)); + cards.add(new SetCardInfo("Blessed Wind", 4, Rarity.RARE, mage.cards.b.BlessedWind.class)); + cards.add(new SetCardInfo("Bog Elemental", 57, Rarity.RARE, mage.cards.b.BogElemental.class)); + cards.add(new SetCardInfo("Bog Glider", 58, Rarity.COMMON, mage.cards.b.BogGlider.class)); + cards.add(new SetCardInfo("Branded Brawlers", 84, Rarity.COMMON, mage.cards.b.BrandedBrawlers.class)); + cards.add(new SetCardInfo("Brutal Suppression", 85, Rarity.UNCOMMON, mage.cards.b.BrutalSuppression.class)); + cards.add(new SetCardInfo("Calming Verse", 110, Rarity.COMMON, mage.cards.c.CalmingVerse.class)); + cards.add(new SetCardInfo("Celestial Convergence", 5, Rarity.RARE, mage.cards.c.CelestialConvergence.class)); + cards.add(new SetCardInfo("Chilling Apparition", 59, Rarity.UNCOMMON, mage.cards.c.ChillingApparition.class)); + cards.add(new SetCardInfo("Chimeric Idol", 136, Rarity.UNCOMMON, mage.cards.c.ChimericIdol.class)); + cards.add(new SetCardInfo("Citadel of Pain", 86, Rarity.UNCOMMON, mage.cards.c.CitadelOfPain.class)); + cards.add(new SetCardInfo("Coastal Hornclaw", 31, Rarity.COMMON, mage.cards.c.CoastalHornclaw.class)); + cards.add(new SetCardInfo("Coffin Puppets", 60, Rarity.RARE, mage.cards.c.CoffinPuppets.class)); + cards.add(new SetCardInfo("Copper-Leaf Angel", 137, Rarity.RARE, mage.cards.c.CopperLeafAngel.class)); + cards.add(new SetCardInfo("Darba", 111, Rarity.UNCOMMON, mage.cards.d.Darba.class)); + cards.add(new SetCardInfo("Death Charmer", 61, Rarity.COMMON, mage.cards.d.DeathCharmer.class)); + cards.add(new SetCardInfo("Denying Wind", 32, Rarity.RARE, mage.cards.d.DenyingWind.class)); + cards.add(new SetCardInfo("Despoil", 62, Rarity.COMMON, mage.cards.d.Despoil.class)); + cards.add(new SetCardInfo("Devastate", 87, Rarity.COMMON, mage.cards.d.Devastate.class)); + cards.add(new SetCardInfo("Diving Griffin", 6, Rarity.COMMON, mage.cards.d.DivingGriffin.class)); + cards.add(new SetCardInfo("Dual Nature", 112, Rarity.RARE, mage.cards.d.DualNature.class)); + cards.add(new SetCardInfo("Elephant Resurgence", 113, Rarity.RARE, mage.cards.e.ElephantResurgence.class)); + cards.add(new SetCardInfo("Endbringer's Revel", 63, Rarity.UNCOMMON, mage.cards.e.EndbringersRevel.class)); + cards.add(new SetCardInfo("Entangler", 7, Rarity.UNCOMMON, mage.cards.e.Entangler.class)); + cards.add(new SetCardInfo("Excavation", 33, Rarity.UNCOMMON, mage.cards.e.Excavation.class)); + cards.add(new SetCardInfo("Excise", 8, Rarity.COMMON, mage.cards.e.Excise.class)); + cards.add(new SetCardInfo("Fault Riders", 88, Rarity.COMMON, mage.cards.f.FaultRiders.class)); + cards.add(new SetCardInfo("Fen Stalker", 64, Rarity.COMMON, mage.cards.f.FenStalker.class)); + cards.add(new SetCardInfo("Fickle Efreet", 89, Rarity.RARE, mage.cards.f.FickleEfreet.class)); + cards.add(new SetCardInfo("Flameshot", 90, Rarity.UNCOMMON, mage.cards.f.Flameshot.class)); + cards.add(new SetCardInfo("Flay", 65, Rarity.COMMON, mage.cards.f.Flay.class)); + cards.add(new SetCardInfo("Flowering Field", 9, Rarity.UNCOMMON, mage.cards.f.FloweringField.class)); + cards.add(new SetCardInfo("Foil", 34, Rarity.UNCOMMON, mage.cards.f.Foil.class)); + cards.add(new SetCardInfo("Forgotten Harvest", 114, Rarity.RARE, mage.cards.f.ForgottenHarvest.class)); + cards.add(new SetCardInfo("Glittering Lion", 10, Rarity.UNCOMMON, mage.cards.g.GlitteringLion.class)); + cards.add(new SetCardInfo("Glittering Lynx", 11, Rarity.COMMON, mage.cards.g.GlitteringLynx.class)); + cards.add(new SetCardInfo("Greel's Caress", 67, Rarity.COMMON, mage.cards.g.GreelsCaress.class)); + cards.add(new SetCardInfo("Greel, Mind Raker", 66, Rarity.RARE, mage.cards.g.GreelMindRaker.class)); + cards.add(new SetCardInfo("Gulf Squid", 35, Rarity.COMMON, mage.cards.g.GulfSquid.class)); + cards.add(new SetCardInfo("Hazy Homunculus", 36, Rarity.COMMON, mage.cards.h.HazyHomunculus.class)); + cards.add(new SetCardInfo("Heightened Awareness", 37, Rarity.RARE, mage.cards.h.HeightenedAwareness.class)); + cards.add(new SetCardInfo("Hollow Warrior", 138, Rarity.UNCOMMON, mage.cards.h.HollowWarrior.class)); + cards.add(new SetCardInfo("Infernal Genesis", 68, Rarity.RARE, mage.cards.i.InfernalGenesis.class)); + cards.add(new SetCardInfo("Inflame", 91, Rarity.COMMON, mage.cards.i.Inflame.class)); + cards.add(new SetCardInfo("Jeweled Spirit", 12, Rarity.RARE, mage.cards.j.JeweledSpirit.class)); + cards.add(new SetCardInfo("Jolrael, Empress of Beasts", 115, Rarity.RARE, mage.cards.j.JolraelEmpressOfBeasts.class)); + cards.add(new SetCardInfo("Jolrael's Favor", 116, Rarity.COMMON, mage.cards.j.JolraelsFavor.class)); + cards.add(new SetCardInfo("Keldon Arsonist", 92, Rarity.UNCOMMON, mage.cards.k.KeldonArsonist.class)); + cards.add(new SetCardInfo("Keldon Battlewagon", 139, Rarity.RARE, mage.cards.k.KeldonBattlewagon.class)); + cards.add(new SetCardInfo("Keldon Berserker", 93, Rarity.COMMON, mage.cards.k.KeldonBerserker.class)); + cards.add(new SetCardInfo("Keldon Firebombers", 94, Rarity.RARE, mage.cards.k.KeldonFirebombers.class)); + cards.add(new SetCardInfo("Latulla, Keldon Overseer", 95, Rarity.RARE, mage.cards.l.LatullaKeldonOverseer.class)); + cards.add(new SetCardInfo("Latulla's Orders", 96, Rarity.COMMON, mage.cards.l.LatullasOrders.class)); + cards.add(new SetCardInfo("Lesser Gargadon", 97, Rarity.UNCOMMON, mage.cards.l.LesserGargadon.class)); + cards.add(new SetCardInfo("Living Terrain", 117, Rarity.UNCOMMON, mage.cards.l.LivingTerrain.class)); + cards.add(new SetCardInfo("Mageta's Boon", 14, Rarity.COMMON, mage.cards.m.MagetasBoon.class)); + cards.add(new SetCardInfo("Mageta the Lion", 13, Rarity.RARE, mage.cards.m.MagetaTheLion.class)); + cards.add(new SetCardInfo("Mana Vapors", 38, Rarity.UNCOMMON, mage.cards.m.ManaVapors.class)); + cards.add(new SetCardInfo("Marsh Boa", 118, Rarity.COMMON, mage.cards.m.MarshBoa.class)); + cards.add(new SetCardInfo("Mercenary Informer", 15, Rarity.RARE, mage.cards.m.MercenaryInformer.class)); + cards.add(new SetCardInfo("Mine Bearer", 16, Rarity.COMMON, mage.cards.m.MineBearer.class)); + cards.add(new SetCardInfo("Mirror Strike", 17, Rarity.UNCOMMON, mage.cards.m.MirrorStrike.class)); + cards.add(new SetCardInfo("Mungha Wurm", 119, Rarity.RARE, mage.cards.m.MunghaWurm.class)); + cards.add(new SetCardInfo("Nakaya Shade", 69, Rarity.UNCOMMON, mage.cards.n.NakayaShade.class)); + cards.add(new SetCardInfo("Noxious Field", 70, Rarity.UNCOMMON, mage.cards.n.NoxiousField.class)); + cards.add(new SetCardInfo("Outbreak", 71, Rarity.UNCOMMON, mage.cards.o.Outbreak.class)); + cards.add(new SetCardInfo("Overburden", 39, Rarity.RARE, mage.cards.o.Overburden.class)); + cards.add(new SetCardInfo("Panic Attack", 98, Rarity.COMMON, mage.cards.p.PanicAttack.class)); + cards.add(new SetCardInfo("Pit Raptor", 72, Rarity.UNCOMMON, mage.cards.p.PitRaptor.class)); + cards.add(new SetCardInfo("Plague Fiend", 73, Rarity.COMMON, mage.cards.p.PlagueFiend.class)); + cards.add(new SetCardInfo("Plague Wind", 74, Rarity.RARE, mage.cards.p.PlagueWind.class)); + cards.add(new SetCardInfo("Psychic Theft", 40, Rarity.RARE, mage.cards.p.PsychicTheft.class)); + cards.add(new SetCardInfo("Pygmy Razorback", 120, Rarity.COMMON, mage.cards.p.PygmyRazorback.class)); + cards.add(new SetCardInfo("Quicksilver Wall", 41, Rarity.UNCOMMON, mage.cards.q.QuicksilverWall.class)); + cards.add(new SetCardInfo("Rebel Informer", 75, Rarity.RARE, mage.cards.r.RebelInformer.class)); + cards.add(new SetCardInfo("Rethink", 42, Rarity.COMMON, mage.cards.r.Rethink.class)); + cards.add(new SetCardInfo("Reveille Squad", 18, Rarity.UNCOMMON, mage.cards.r.ReveilleSquad.class)); + cards.add(new SetCardInfo("Rhystic Cave", 142, Rarity.UNCOMMON, mage.cards.r.RhysticCave.class)); + cards.add(new SetCardInfo("Rhystic Circle", 19, Rarity.COMMON, mage.cards.r.RhysticCircle.class)); + cards.add(new SetCardInfo("Rhystic Deluge", 43, Rarity.COMMON, mage.cards.r.RhysticDeluge.class)); + cards.add(new SetCardInfo("Rhystic Lightning", 99, Rarity.COMMON, mage.cards.r.RhysticLightning.class)); + cards.add(new SetCardInfo("Rhystic Scrying", 44, Rarity.UNCOMMON, mage.cards.r.RhysticScrying.class)); + cards.add(new SetCardInfo("Rhystic Shield", 20, Rarity.COMMON, mage.cards.r.RhysticShield.class)); + cards.add(new SetCardInfo("Rhystic Study", 45, Rarity.COMMON, mage.cards.r.RhysticStudy.class)); + cards.add(new SetCardInfo("Rhystic Syphon", 76, Rarity.UNCOMMON, mage.cards.r.RhysticSyphon.class)); + cards.add(new SetCardInfo("Rhystic Tutor", 77, Rarity.RARE, mage.cards.r.RhysticTutor.class)); + cards.add(new SetCardInfo("Ribbon Snake", 46, Rarity.COMMON, mage.cards.r.RibbonSnake.class)); + cards.add(new SetCardInfo("Rib Cage Spider", 121, Rarity.COMMON, mage.cards.r.RibCageSpider.class)); + cards.add(new SetCardInfo("Ridgeline Rager", 100, Rarity.COMMON, mage.cards.r.RidgelineRager.class)); + cards.add(new SetCardInfo("Root Cage", 122, Rarity.UNCOMMON, mage.cards.r.RootCage.class)); + cards.add(new SetCardInfo("Samite Sanctuary", 21, Rarity.RARE, mage.cards.s.SamiteSanctuary.class)); + cards.add(new SetCardInfo("Scoria Cat", 101, Rarity.UNCOMMON, mage.cards.s.ScoriaCat.class)); + cards.add(new SetCardInfo("Search for Survivors", 102, Rarity.RARE, mage.cards.s.SearchForSurvivors.class)); + cards.add(new SetCardInfo("Searing Wind", 103, Rarity.RARE, mage.cards.s.SearingWind.class)); + cards.add(new SetCardInfo("Sheltering Prayers", 22, Rarity.RARE, mage.cards.s.ShelteringPrayers.class)); + cards.add(new SetCardInfo("Shield Dancer", 23, Rarity.UNCOMMON, mage.cards.s.ShieldDancer.class)); + cards.add(new SetCardInfo("Shrouded Serpent", 47, Rarity.RARE, mage.cards.s.ShroudedSerpent.class)); + cards.add(new SetCardInfo("Silt Crawler", 123, Rarity.COMMON, mage.cards.s.SiltCrawler.class)); + cards.add(new SetCardInfo("Snag", 124, Rarity.UNCOMMON, mage.cards.s.Snag.class)); + cards.add(new SetCardInfo("Soul Charmer", 24, Rarity.COMMON, mage.cards.s.SoulCharmer.class)); + cards.add(new SetCardInfo("Soul Strings", 78, Rarity.COMMON, mage.cards.s.SoulStrings.class)); + cards.add(new SetCardInfo("Spiketail Drake", 48, Rarity.UNCOMMON, mage.cards.s.SpiketailDrake.class)); + cards.add(new SetCardInfo("Spiketail Hatchling", 49, Rarity.COMMON, mage.cards.s.SpiketailHatchling.class)); + cards.add(new SetCardInfo("Spitting Spider", 125, Rarity.UNCOMMON, mage.cards.s.SpittingSpider.class)); + cards.add(new SetCardInfo("Spore Frog", 126, Rarity.COMMON, mage.cards.s.SporeFrog.class)); + cards.add(new SetCardInfo("Spur Grappler", 104, Rarity.COMMON, mage.cards.s.SpurGrappler.class)); + cards.add(new SetCardInfo("Squirrel Wrangler", 127, Rarity.RARE, mage.cards.s.SquirrelWrangler.class)); + cards.add(new SetCardInfo("Steal Strength", 79, Rarity.COMMON, mage.cards.s.StealStrength.class)); + cards.add(new SetCardInfo("Stormwatch Eagle", 50, Rarity.COMMON, mage.cards.s.StormwatchEagle.class)); + cards.add(new SetCardInfo("Sunken Field", 51, Rarity.UNCOMMON, mage.cards.s.SunkenField.class)); + cards.add(new SetCardInfo("Sword Dancer", 25, Rarity.UNCOMMON, mage.cards.s.SwordDancer.class)); + cards.add(new SetCardInfo("Task Mage Assembly", 105, Rarity.RARE, mage.cards.t.TaskMageAssembly.class)); + cards.add(new SetCardInfo("Thresher Beast", 128, Rarity.COMMON, mage.cards.t.ThresherBeast.class)); + cards.add(new SetCardInfo("Thrive", 129, Rarity.COMMON, mage.cards.t.Thrive.class)); + cards.add(new SetCardInfo("Trenching Steed", 26, Rarity.COMMON, mage.cards.t.TrenchingSteed.class)); + cards.add(new SetCardInfo("Troubled Healer", 27, Rarity.COMMON, mage.cards.t.TroubledHealer.class)); + cards.add(new SetCardInfo("Troublesome Spirit", 52, Rarity.RARE, mage.cards.t.TroublesomeSpirit.class)); + cards.add(new SetCardInfo("Verdant Field", 130, Rarity.UNCOMMON, mage.cards.v.VerdantField.class)); + cards.add(new SetCardInfo("Veteran Brawlers", 106, Rarity.RARE, mage.cards.v.VeteranBrawlers.class)); + cards.add(new SetCardInfo("Vintara Elephant", 131, Rarity.COMMON, mage.cards.v.VintaraElephant.class)); + cards.add(new SetCardInfo("Vintara Snapper", 132, Rarity.UNCOMMON, mage.cards.v.VintaraSnapper.class)); + cards.add(new SetCardInfo("Vitalizing Wind", 133, Rarity.RARE, mage.cards.v.VitalizingWind.class)); + cards.add(new SetCardInfo("Wall of Vipers", 80, Rarity.UNCOMMON, mage.cards.w.WallOfVipers.class)); + cards.add(new SetCardInfo("Well of Discovery", 140, Rarity.RARE, mage.cards.w.WellOfDiscovery.class)); + cards.add(new SetCardInfo("Well of Life", 141, Rarity.UNCOMMON, mage.cards.w.WellOfLife.class)); + cards.add(new SetCardInfo("Whip Sergeant", 107, Rarity.UNCOMMON, mage.cards.w.WhipSergeant.class)); + cards.add(new SetCardInfo("Whipstitched Zombie", 81, Rarity.COMMON, mage.cards.w.WhipstitchedZombie.class)); + cards.add(new SetCardInfo("Wild Might", 134, Rarity.COMMON, mage.cards.w.WildMight.class)); + cards.add(new SetCardInfo("Windscouter", 53, Rarity.UNCOMMON, mage.cards.w.Windscouter.class)); + cards.add(new SetCardInfo("Wing Storm", 135, Rarity.UNCOMMON, mage.cards.w.WingStorm.class)); + cards.add(new SetCardInfo("Wintermoon Mesa", 143, Rarity.RARE, mage.cards.w.WintermoonMesa.class)); + cards.add(new SetCardInfo("Withdraw", 54, Rarity.COMMON, mage.cards.w.Withdraw.class)); + cards.add(new SetCardInfo("Zerapa Minotaur", 108, Rarity.COMMON, mage.cards.z.ZerapaMinotaur.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Starter1999.java b/Mage.Sets/src/mage/sets/Starter1999.java index d41c6b095f..f9146ef2ec 100644 --- a/Mage.Sets/src/mage/sets/Starter1999.java +++ b/Mage.Sets/src/mage/sets/Starter1999.java @@ -1,201 +1,201 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author LevelX2 - */ -public final class Starter1999 extends ExpansionSet { - - private static final Starter1999 instance = new Starter1999(); - - public static Starter1999 getInstance() { - return instance; - } - - private Starter1999() { - super("Starter 1999", "S99", ExpansionSet.buildDate(1999, 7, 1), SetType.SUPPLEMENTAL); - this.blockName = "Beginner"; - this.hasBasicLands = true; - this.hasBoosters = true; - this.numBoosterLands = 2; - this.numBoosterCommon = 9; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Abyssal Horror", 63, Rarity.RARE, mage.cards.a.AbyssalHorror.class)); - cards.add(new SetCardInfo("Air Elemental", 32, Rarity.UNCOMMON, mage.cards.a.AirElemental.class)); - cards.add(new SetCardInfo("Alluring Scent", 124, Rarity.RARE, mage.cards.a.AlluringScent.class)); - cards.add(new SetCardInfo("Ancient Craving", 64, Rarity.RARE, mage.cards.a.AncientCraving.class)); - cards.add(new SetCardInfo("Angelic Blessing", 3, Rarity.COMMON, mage.cards.a.AngelicBlessing.class)); - cards.add(new SetCardInfo("Angel of Light", 1, Rarity.UNCOMMON, mage.cards.a.AngelOfLight.class)); - cards.add(new SetCardInfo("Angel of Mercy", 2, Rarity.UNCOMMON, mage.cards.a.AngelOfMercy.class)); - cards.add(new SetCardInfo("Archangel", 4, Rarity.RARE, mage.cards.a.Archangel.class)); - cards.add(new SetCardInfo("Ardent Militia", 5, Rarity.UNCOMMON, mage.cards.a.ArdentMilitia.class)); - cards.add(new SetCardInfo("Armageddon", 6, Rarity.RARE, mage.cards.a.Armageddon.class)); - cards.add(new SetCardInfo("Barbtooth Wurm", 125, Rarity.COMMON, mage.cards.b.BarbtoothWurm.class)); - cards.add(new SetCardInfo("Bargain", 7, Rarity.UNCOMMON, mage.cards.b.Bargain.class)); - cards.add(new SetCardInfo("Blinding Light", 8, Rarity.RARE, mage.cards.b.BlindingLight.class)); - cards.add(new SetCardInfo("Bog Imp", 65, Rarity.COMMON, mage.cards.b.BogImp.class)); - cards.add(new SetCardInfo("Bog Raiders", 66, Rarity.COMMON, mage.cards.b.BogRaiders.class)); - cards.add(new SetCardInfo("Bog Wraith", 67, Rarity.UNCOMMON, mage.cards.b.BogWraith.class)); - cards.add(new SetCardInfo("Border Guard", 9, Rarity.COMMON, mage.cards.b.BorderGuard.class)); - cards.add(new SetCardInfo("Breath of Life", 10, Rarity.UNCOMMON, mage.cards.b.BreathOfLife.class)); - cards.add(new SetCardInfo("Bull Hippo", 126, Rarity.UNCOMMON, mage.cards.b.BullHippo.class)); - cards.add(new SetCardInfo("Champion Lancer", 11, Rarity.RARE, mage.cards.c.ChampionLancer.class)); - cards.add(new SetCardInfo("Charging Paladin", 12, Rarity.UNCOMMON, mage.cards.c.ChargingPaladin.class)); - cards.add(new SetCardInfo("Chorus of Woe", 68, Rarity.COMMON, mage.cards.c.ChorusOfWoe.class)); - cards.add(new SetCardInfo("Cinder Storm", 93, Rarity.UNCOMMON, mage.cards.c.CinderStorm.class)); - cards.add(new SetCardInfo("Coercion", 69, Rarity.UNCOMMON, mage.cards.c.Coercion.class)); - cards.add(new SetCardInfo("Coral Eel", 33, Rarity.COMMON, mage.cards.c.CoralEel.class)); - cards.add(new SetCardInfo("Counterspell", 34, Rarity.UNCOMMON, mage.cards.c.Counterspell.class)); - cards.add(new SetCardInfo("Dakmor Ghoul", 70, Rarity.UNCOMMON, mage.cards.d.DakmorGhoul.class)); - cards.add(new SetCardInfo("Dakmor Lancer", 71, Rarity.RARE, mage.cards.d.DakmorLancer.class)); - cards.add(new SetCardInfo("Dakmor Plague", 72, Rarity.UNCOMMON, mage.cards.d.DakmorPlague.class)); - cards.add(new SetCardInfo("Dakmor Scorpion", 73, Rarity.COMMON, mage.cards.d.DakmorScorpion.class)); - cards.add(new SetCardInfo("Dakmor Sorceress", 74, Rarity.RARE, mage.cards.d.DakmorSorceress.class)); - cards.add(new SetCardInfo("Dark Offering", 75, Rarity.UNCOMMON, mage.cards.d.DarkOffering.class)); - cards.add(new SetCardInfo("Denizen of the Deep", 35, Rarity.RARE, mage.cards.d.DenizenOfTheDeep.class)); - cards.add(new SetCardInfo("Devastation", 94, Rarity.RARE, mage.cards.d.Devastation.class)); - cards.add(new SetCardInfo("Devoted Hero", 13, Rarity.COMMON, mage.cards.d.DevotedHero.class)); - cards.add(new SetCardInfo("Devout Monk", 14, Rarity.COMMON, mage.cards.d.DevoutMonk.class)); - cards.add(new SetCardInfo("Dread Reaper", 76, Rarity.RARE, mage.cards.d.DreadReaper.class)); - cards.add(new SetCardInfo("Durkwood Boars", 127, Rarity.COMMON, mage.cards.d.DurkwoodBoars.class)); - cards.add(new SetCardInfo("Eager Cadet", 15, Rarity.COMMON, mage.cards.e.EagerCadet.class)); - cards.add(new SetCardInfo("Earth Elemental", 95, Rarity.UNCOMMON, mage.cards.e.EarthElemental.class)); - cards.add(new SetCardInfo("Exhaustion", 36, Rarity.UNCOMMON, mage.cards.e.Exhaustion.class)); - cards.add(new SetCardInfo("Extinguish", 37, Rarity.COMMON, mage.cards.e.Extinguish.class)); - cards.add(new SetCardInfo("Eye Spy", 38, Rarity.UNCOMMON, mage.cards.e.EyeSpy.class)); - cards.add(new SetCardInfo("False Peace", 16, Rarity.UNCOMMON, mage.cards.f.FalsePeace.class)); - cards.add(new SetCardInfo("Feral Shadow", 77, Rarity.COMMON, mage.cards.f.FeralShadow.class)); - cards.add(new SetCardInfo("Fire Elemental", 96, Rarity.UNCOMMON, mage.cards.f.FireElemental.class)); - cards.add(new SetCardInfo("Fire Tempest", 97, Rarity.RARE, mage.cards.f.FireTempest.class)); - cards.add(new SetCardInfo("Foot Soldiers", 17, Rarity.COMMON, mage.cards.f.FootSoldiers.class)); - cards.add(new SetCardInfo("Forest", 170, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 171, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 172, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 173, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gerrard's Wisdom", 18, Rarity.RARE, mage.cards.g.GerrardsWisdom.class)); - cards.add(new SetCardInfo("Giant Octopus", 39, Rarity.COMMON, mage.cards.g.GiantOctopus.class)); - cards.add(new SetCardInfo("Goblin Cavaliers", 98, Rarity.COMMON, mage.cards.g.GoblinCavaliers.class)); - cards.add(new SetCardInfo("Goblin Chariot", 99, Rarity.COMMON, mage.cards.g.GoblinChariot.class)); - cards.add(new SetCardInfo("Goblin Commando", 100, Rarity.UNCOMMON, mage.cards.g.GoblinCommando.class)); - cards.add(new SetCardInfo("Goblin General", 101, Rarity.UNCOMMON, mage.cards.g.GoblinGeneral.class)); - cards.add(new SetCardInfo("Goblin Glider", 102, Rarity.UNCOMMON, mage.cards.g.GoblinGlider.class)); - cards.add(new SetCardInfo("Goblin Hero", 103, Rarity.COMMON, mage.cards.g.GoblinHero.class)); - cards.add(new SetCardInfo("Goblin Lore", 104, Rarity.UNCOMMON, mage.cards.g.GoblinLore.class)); - cards.add(new SetCardInfo("Goblin Mountaineer", 105, Rarity.COMMON, mage.cards.g.GoblinMountaineer.class)); - cards.add(new SetCardInfo("Goblin Settler", 106, Rarity.UNCOMMON, mage.cards.g.GoblinSettler.class)); - cards.add(new SetCardInfo("Gorilla Warrior", 128, Rarity.COMMON, mage.cards.g.GorillaWarrior.class)); - cards.add(new SetCardInfo("Gravedigger", 78, Rarity.UNCOMMON, mage.cards.g.Gravedigger.class)); - cards.add(new SetCardInfo("Grim Tutor", 79, Rarity.RARE, mage.cards.g.GrimTutor.class)); - cards.add(new SetCardInfo("Grizzly Bears", 129, Rarity.COMMON, mage.cards.g.GrizzlyBears.class)); - cards.add(new SetCardInfo("Hand of Death", 80, Rarity.COMMON, mage.cards.h.HandOfDeath.class)); - cards.add(new SetCardInfo("Hollow Dogs", 81, Rarity.COMMON, mage.cards.h.HollowDogs.class)); - cards.add(new SetCardInfo("Howling Fury", 82, Rarity.UNCOMMON, mage.cards.h.HowlingFury.class)); - cards.add(new SetCardInfo("Hulking Goblin", 107, Rarity.COMMON, mage.cards.h.HulkingGoblin.class)); - cards.add(new SetCardInfo("Hulking Ogre", 108, Rarity.UNCOMMON, mage.cards.h.HulkingOgre.class)); - cards.add(new SetCardInfo("Ingenious Thief", 40, Rarity.COMMON, mage.cards.i.IngeniousThief.class)); - cards.add(new SetCardInfo("Island", 158, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 159, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 160, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 161, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Jagged Lightning", 109, Rarity.UNCOMMON, mage.cards.j.JaggedLightning.class)); - cards.add(new SetCardInfo("Knight Errant", 19, Rarity.COMMON, mage.cards.k.KnightErrant.class)); - cards.add(new SetCardInfo("Last Chance", 110, Rarity.RARE, mage.cards.l.LastChance.class)); - cards.add(new SetCardInfo("Lava Axe", 111, Rarity.COMMON, mage.cards.l.LavaAxe.class)); - cards.add(new SetCardInfo("Lone Wolf", 130, Rarity.COMMON, mage.cards.l.LoneWolf.class)); - cards.add(new SetCardInfo("Loyal Sentry", 20, Rarity.RARE, mage.cards.l.LoyalSentry.class)); - cards.add(new SetCardInfo("Lynx", 131, Rarity.UNCOMMON, mage.cards.l.Lynx.class)); - cards.add(new SetCardInfo("Man-o'-War", 41, Rarity.UNCOMMON, mage.cards.m.ManOWar.class)); - cards.add(new SetCardInfo("Merfolk of the Pearl Trident", 42, Rarity.COMMON, mage.cards.m.MerfolkOfThePearlTrident.class)); - cards.add(new SetCardInfo("Mind Rot", 83, Rarity.COMMON, mage.cards.m.MindRot.class)); - cards.add(new SetCardInfo("Mons's Goblin Raiders", 112, Rarity.COMMON, mage.cards.m.MonssGoblinRaiders.class)); - cards.add(new SetCardInfo("Monstrous Growth", 132, Rarity.COMMON, mage.cards.m.MonstrousGrowth.class)); - cards.add(new SetCardInfo("Moon Sprite", 133, Rarity.UNCOMMON, mage.cards.m.MoonSprite.class)); - cards.add(new SetCardInfo("Mountain", 166, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 167, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 168, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 169, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Muck Rats", 84, Rarity.COMMON, mage.cards.m.MuckRats.class)); - cards.add(new SetCardInfo("Natural Spring", 134, Rarity.UNCOMMON, mage.cards.n.NaturalSpring.class)); - cards.add(new SetCardInfo("Nature's Cloak", 135, Rarity.RARE, mage.cards.n.NaturesCloak.class)); - cards.add(new SetCardInfo("Nature's Lore", 136, Rarity.COMMON, mage.cards.n.NaturesLore.class)); - cards.add(new SetCardInfo("Norwood Archers", 137, Rarity.COMMON, mage.cards.n.NorwoodArchers.class)); - cards.add(new SetCardInfo("Norwood Ranger", 138, Rarity.COMMON, mage.cards.n.NorwoodRanger.class)); - cards.add(new SetCardInfo("Ogre Warrior", 113, Rarity.COMMON, mage.cards.o.OgreWarrior.class)); - cards.add(new SetCardInfo("Path of Peace", 21, Rarity.COMMON, mage.cards.p.PathOfPeace.class)); - cards.add(new SetCardInfo("Phantom Warrior", 44, Rarity.RARE, mage.cards.p.PhantomWarrior.class)); - cards.add(new SetCardInfo("Plains", 154, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 155, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 156, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 157, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Pride of Lions", 139, Rarity.UNCOMMON, mage.cards.p.PrideOfLions.class)); - cards.add(new SetCardInfo("Psychic Transfer", 46, Rarity.RARE, mage.cards.p.PsychicTransfer.class)); - cards.add(new SetCardInfo("Raging Goblin", 114, Rarity.COMMON, mage.cards.r.RagingGoblin.class)); - cards.add(new SetCardInfo("Raise Dead", 85, Rarity.COMMON, mage.cards.r.RaiseDead.class)); - cards.add(new SetCardInfo("Ransack", 47, Rarity.RARE, mage.cards.r.Ransack.class)); - cards.add(new SetCardInfo("Ravenous Rats", 86, Rarity.UNCOMMON, mage.cards.r.RavenousRats.class)); - cards.add(new SetCardInfo("Relearn", 48, Rarity.UNCOMMON, mage.cards.r.Relearn.class)); - cards.add(new SetCardInfo("Relentless Assault", 115, Rarity.RARE, mage.cards.r.RelentlessAssault.class)); - cards.add(new SetCardInfo("Remove Soul", 49, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); - cards.add(new SetCardInfo("Renewing Touch", 140, Rarity.UNCOMMON, mage.cards.r.RenewingTouch.class)); - cards.add(new SetCardInfo("Righteous Charge", 22, Rarity.UNCOMMON, mage.cards.r.RighteousCharge.class)); - cards.add(new SetCardInfo("Righteous Fury", 23, Rarity.RARE, mage.cards.r.RighteousFury.class)); - cards.add(new SetCardInfo("Royal Falcon", 24, Rarity.COMMON, mage.cards.r.RoyalFalcon.class)); - cards.add(new SetCardInfo("Royal Trooper", 25, Rarity.UNCOMMON, mage.cards.r.RoyalTrooper.class)); - cards.add(new SetCardInfo("Sacred Nectar", 26, Rarity.COMMON, mage.cards.s.SacredNectar.class)); - cards.add(new SetCardInfo("Scathe Zombies", 87, Rarity.COMMON, mage.cards.s.ScatheZombies.class)); - cards.add(new SetCardInfo("Scorching Spear", 116, Rarity.COMMON, mage.cards.s.ScorchingSpear.class)); - cards.add(new SetCardInfo("Sea Eagle", 50, Rarity.COMMON, mage.cards.s.SeaEagle.class)); - cards.add(new SetCardInfo("Serpent Warrior", 88, Rarity.COMMON, mage.cards.s.SerpentWarrior.class)); - cards.add(new SetCardInfo("Shrieking Specter", 89, Rarity.UNCOMMON, mage.cards.s.ShriekingSpecter.class)); - cards.add(new SetCardInfo("Silverback Ape", 141, Rarity.UNCOMMON, mage.cards.s.SilverbackApe.class)); - cards.add(new SetCardInfo("Sleight of Hand", 51, Rarity.COMMON, mage.cards.s.SleightOfHand.class)); - cards.add(new SetCardInfo("Snapping Drake", 52, Rarity.COMMON, mage.cards.s.SnappingDrake.class)); - cards.add(new SetCardInfo("Soul Feast", 90, Rarity.UNCOMMON, mage.cards.s.SoulFeast.class)); - cards.add(new SetCardInfo("Southern Elephant", 142, Rarity.COMMON, mage.cards.s.SouthernElephant.class)); - cards.add(new SetCardInfo("Spitting Earth", 117, Rarity.UNCOMMON, mage.cards.s.SpittingEarth.class)); - cards.add(new SetCardInfo("Squall", 143, Rarity.COMMON, mage.cards.s.Squall.class)); - cards.add(new SetCardInfo("Steadfastness", 27, Rarity.COMMON, mage.cards.s.Steadfastness.class)); - cards.add(new SetCardInfo("Stone Rain", 118, Rarity.COMMON, mage.cards.s.StoneRain.class)); - cards.add(new SetCardInfo("Storm Crow", 53, Rarity.COMMON, mage.cards.s.StormCrow.class)); - cards.add(new SetCardInfo("Stream of Acid", 91, Rarity.UNCOMMON, mage.cards.s.StreamOfAcid.class)); - cards.add(new SetCardInfo("Summer Bloom", 144, Rarity.RARE, mage.cards.s.SummerBloom.class)); - cards.add(new SetCardInfo("Swamp", 162, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 163, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 164, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 165, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sylvan Basilisk", 145, Rarity.RARE, mage.cards.s.SylvanBasilisk.class)); - cards.add(new SetCardInfo("Sylvan Yeti", 146, Rarity.RARE, mage.cards.s.SylvanYeti.class)); - cards.add(new SetCardInfo("Thorn Elemental", 147, Rarity.RARE, mage.cards.t.ThornElemental.class)); - cards.add(new SetCardInfo("Thunder Dragon", 119, Rarity.RARE, mage.cards.t.ThunderDragon.class)); - cards.add(new SetCardInfo("Tidings", 54, Rarity.UNCOMMON, mage.cards.t.Tidings.class)); - cards.add(new SetCardInfo("Time Ebb", 55, Rarity.COMMON, mage.cards.t.TimeEbb.class)); - cards.add(new SetCardInfo("Time Warp", 56, Rarity.RARE, mage.cards.t.TimeWarp.class)); - cards.add(new SetCardInfo("Touch of Brilliance", 57, Rarity.COMMON, mage.cards.t.TouchOfBrilliance.class)); - cards.add(new SetCardInfo("Trained Orgg", 120, Rarity.RARE, mage.cards.t.TrainedOrgg.class)); - cards.add(new SetCardInfo("Tremor", 121, Rarity.COMMON, mage.cards.t.Tremor.class)); - cards.add(new SetCardInfo("Undo", 58, Rarity.UNCOMMON, mage.cards.u.Undo.class)); - cards.add(new SetCardInfo("Untamed Wilds", 148, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); - cards.add(new SetCardInfo("Venerable Monk", 28, Rarity.COMMON, mage.cards.v.VenerableMonk.class)); - cards.add(new SetCardInfo("Vengeance", 29, Rarity.UNCOMMON, mage.cards.v.Vengeance.class)); - cards.add(new SetCardInfo("Veteran Cavalier", 30, Rarity.UNCOMMON, mage.cards.v.VeteranCavalier.class)); - cards.add(new SetCardInfo("Vizzerdrix", 59, Rarity.RARE, mage.cards.v.Vizzerdrix.class)); - cards.add(new SetCardInfo("Volcanic Dragon", 122, Rarity.RARE, mage.cards.v.VolcanicDragon.class)); - cards.add(new SetCardInfo("Volcanic Hammer", 123, Rarity.COMMON, mage.cards.v.VolcanicHammer.class)); - cards.add(new SetCardInfo("Water Elemental", 60, Rarity.UNCOMMON, mage.cards.w.WaterElemental.class)); - cards.add(new SetCardInfo("Whiptail Wurm", 149, Rarity.UNCOMMON, mage.cards.w.WhiptailWurm.class)); - cards.add(new SetCardInfo("Whirlwind", 150, Rarity.RARE, mage.cards.w.Whirlwind.class)); - cards.add(new SetCardInfo("Wicked Pact", 92, Rarity.RARE, mage.cards.w.WickedPact.class)); - cards.add(new SetCardInfo("Wild Griffin", 31, Rarity.COMMON, mage.cards.w.WildGriffin.class)); - cards.add(new SetCardInfo("Wild Ox", 151, Rarity.UNCOMMON, mage.cards.w.WildOx.class)); - cards.add(new SetCardInfo("Willow Elf", 152, Rarity.COMMON, mage.cards.w.WillowElf.class)); - cards.add(new SetCardInfo("Wind Drake", 61, Rarity.COMMON, mage.cards.w.WindDrake.class)); - cards.add(new SetCardInfo("Wind Sail", 62, Rarity.UNCOMMON, mage.cards.w.WindSail.class)); - cards.add(new SetCardInfo("Wood Elves", 153, Rarity.UNCOMMON, mage.cards.w.WoodElves.class)); - } -} +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author LevelX2 + */ +public final class Starter1999 extends ExpansionSet { + + private static final Starter1999 instance = new Starter1999(); + + public static Starter1999 getInstance() { + return instance; + } + + private Starter1999() { + super("Starter 1999", "S99", ExpansionSet.buildDate(1999, 7, 1), SetType.SUPPLEMENTAL); + this.blockName = "Beginner"; + this.hasBasicLands = true; + this.hasBoosters = true; + this.numBoosterLands = 2; + this.numBoosterCommon = 9; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Abyssal Horror", 63, Rarity.RARE, mage.cards.a.AbyssalHorror.class)); + cards.add(new SetCardInfo("Air Elemental", 32, Rarity.UNCOMMON, mage.cards.a.AirElemental.class)); + cards.add(new SetCardInfo("Alluring Scent", 124, Rarity.RARE, mage.cards.a.AlluringScent.class)); + cards.add(new SetCardInfo("Ancient Craving", 64, Rarity.RARE, mage.cards.a.AncientCraving.class)); + cards.add(new SetCardInfo("Angelic Blessing", 3, Rarity.COMMON, mage.cards.a.AngelicBlessing.class)); + cards.add(new SetCardInfo("Angel of Light", 1, Rarity.UNCOMMON, mage.cards.a.AngelOfLight.class)); + cards.add(new SetCardInfo("Angel of Mercy", 2, Rarity.UNCOMMON, mage.cards.a.AngelOfMercy.class)); + cards.add(new SetCardInfo("Archangel", 4, Rarity.RARE, mage.cards.a.Archangel.class)); + cards.add(new SetCardInfo("Ardent Militia", 5, Rarity.UNCOMMON, mage.cards.a.ArdentMilitia.class)); + cards.add(new SetCardInfo("Armageddon", 6, Rarity.RARE, mage.cards.a.Armageddon.class)); + cards.add(new SetCardInfo("Barbtooth Wurm", 125, Rarity.COMMON, mage.cards.b.BarbtoothWurm.class)); + cards.add(new SetCardInfo("Bargain", 7, Rarity.UNCOMMON, mage.cards.b.Bargain.class)); + cards.add(new SetCardInfo("Blinding Light", 8, Rarity.RARE, mage.cards.b.BlindingLight.class)); + cards.add(new SetCardInfo("Bog Imp", 65, Rarity.COMMON, mage.cards.b.BogImp.class)); + cards.add(new SetCardInfo("Bog Raiders", 66, Rarity.COMMON, mage.cards.b.BogRaiders.class)); + cards.add(new SetCardInfo("Bog Wraith", 67, Rarity.UNCOMMON, mage.cards.b.BogWraith.class)); + cards.add(new SetCardInfo("Border Guard", 9, Rarity.COMMON, mage.cards.b.BorderGuard.class)); + cards.add(new SetCardInfo("Breath of Life", 10, Rarity.UNCOMMON, mage.cards.b.BreathOfLife.class)); + cards.add(new SetCardInfo("Bull Hippo", 126, Rarity.UNCOMMON, mage.cards.b.BullHippo.class)); + cards.add(new SetCardInfo("Champion Lancer", 11, Rarity.RARE, mage.cards.c.ChampionLancer.class)); + cards.add(new SetCardInfo("Charging Paladin", 12, Rarity.UNCOMMON, mage.cards.c.ChargingPaladin.class)); + cards.add(new SetCardInfo("Chorus of Woe", 68, Rarity.COMMON, mage.cards.c.ChorusOfWoe.class)); + cards.add(new SetCardInfo("Cinder Storm", 93, Rarity.UNCOMMON, mage.cards.c.CinderStorm.class)); + cards.add(new SetCardInfo("Coercion", 69, Rarity.UNCOMMON, mage.cards.c.Coercion.class)); + cards.add(new SetCardInfo("Coral Eel", 33, Rarity.COMMON, mage.cards.c.CoralEel.class)); + cards.add(new SetCardInfo("Counterspell", 34, Rarity.UNCOMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Dakmor Ghoul", 70, Rarity.UNCOMMON, mage.cards.d.DakmorGhoul.class)); + cards.add(new SetCardInfo("Dakmor Lancer", 71, Rarity.RARE, mage.cards.d.DakmorLancer.class)); + cards.add(new SetCardInfo("Dakmor Plague", 72, Rarity.UNCOMMON, mage.cards.d.DakmorPlague.class)); + cards.add(new SetCardInfo("Dakmor Scorpion", 73, Rarity.COMMON, mage.cards.d.DakmorScorpion.class)); + cards.add(new SetCardInfo("Dakmor Sorceress", 74, Rarity.RARE, mage.cards.d.DakmorSorceress.class)); + cards.add(new SetCardInfo("Dark Offering", 75, Rarity.UNCOMMON, mage.cards.d.DarkOffering.class)); + cards.add(new SetCardInfo("Denizen of the Deep", 35, Rarity.RARE, mage.cards.d.DenizenOfTheDeep.class)); + cards.add(new SetCardInfo("Devastation", 94, Rarity.RARE, mage.cards.d.Devastation.class)); + cards.add(new SetCardInfo("Devoted Hero", 13, Rarity.COMMON, mage.cards.d.DevotedHero.class)); + cards.add(new SetCardInfo("Devout Monk", 14, Rarity.COMMON, mage.cards.d.DevoutMonk.class)); + cards.add(new SetCardInfo("Dread Reaper", 76, Rarity.RARE, mage.cards.d.DreadReaper.class)); + cards.add(new SetCardInfo("Durkwood Boars", 127, Rarity.COMMON, mage.cards.d.DurkwoodBoars.class)); + cards.add(new SetCardInfo("Eager Cadet", 15, Rarity.COMMON, mage.cards.e.EagerCadet.class)); + cards.add(new SetCardInfo("Earth Elemental", 95, Rarity.UNCOMMON, mage.cards.e.EarthElemental.class)); + cards.add(new SetCardInfo("Exhaustion", 36, Rarity.UNCOMMON, mage.cards.e.Exhaustion.class)); + cards.add(new SetCardInfo("Extinguish", 37, Rarity.COMMON, mage.cards.e.Extinguish.class)); + cards.add(new SetCardInfo("Eye Spy", 38, Rarity.UNCOMMON, mage.cards.e.EyeSpy.class)); + cards.add(new SetCardInfo("False Peace", 16, Rarity.UNCOMMON, mage.cards.f.FalsePeace.class)); + cards.add(new SetCardInfo("Feral Shadow", 77, Rarity.COMMON, mage.cards.f.FeralShadow.class)); + cards.add(new SetCardInfo("Fire Elemental", 96, Rarity.UNCOMMON, mage.cards.f.FireElemental.class)); + cards.add(new SetCardInfo("Fire Tempest", 97, Rarity.RARE, mage.cards.f.FireTempest.class)); + cards.add(new SetCardInfo("Foot Soldiers", 17, Rarity.COMMON, mage.cards.f.FootSoldiers.class)); + cards.add(new SetCardInfo("Forest", 170, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 171, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 172, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 173, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gerrard's Wisdom", 18, Rarity.RARE, mage.cards.g.GerrardsWisdom.class)); + cards.add(new SetCardInfo("Giant Octopus", 39, Rarity.COMMON, mage.cards.g.GiantOctopus.class)); + cards.add(new SetCardInfo("Goblin Cavaliers", 98, Rarity.COMMON, mage.cards.g.GoblinCavaliers.class)); + cards.add(new SetCardInfo("Goblin Chariot", 99, Rarity.COMMON, mage.cards.g.GoblinChariot.class)); + cards.add(new SetCardInfo("Goblin Commando", 100, Rarity.UNCOMMON, mage.cards.g.GoblinCommando.class)); + cards.add(new SetCardInfo("Goblin General", 101, Rarity.UNCOMMON, mage.cards.g.GoblinGeneral.class)); + cards.add(new SetCardInfo("Goblin Glider", 102, Rarity.UNCOMMON, mage.cards.g.GoblinGlider.class)); + cards.add(new SetCardInfo("Goblin Hero", 103, Rarity.COMMON, mage.cards.g.GoblinHero.class)); + cards.add(new SetCardInfo("Goblin Lore", 104, Rarity.UNCOMMON, mage.cards.g.GoblinLore.class)); + cards.add(new SetCardInfo("Goblin Mountaineer", 105, Rarity.COMMON, mage.cards.g.GoblinMountaineer.class)); + cards.add(new SetCardInfo("Goblin Settler", 106, Rarity.UNCOMMON, mage.cards.g.GoblinSettler.class)); + cards.add(new SetCardInfo("Gorilla Warrior", 128, Rarity.COMMON, mage.cards.g.GorillaWarrior.class)); + cards.add(new SetCardInfo("Gravedigger", 78, Rarity.UNCOMMON, mage.cards.g.Gravedigger.class)); + cards.add(new SetCardInfo("Grim Tutor", 79, Rarity.RARE, mage.cards.g.GrimTutor.class)); + cards.add(new SetCardInfo("Grizzly Bears", 129, Rarity.COMMON, mage.cards.g.GrizzlyBears.class)); + cards.add(new SetCardInfo("Hand of Death", 80, Rarity.COMMON, mage.cards.h.HandOfDeath.class)); + cards.add(new SetCardInfo("Hollow Dogs", 81, Rarity.COMMON, mage.cards.h.HollowDogs.class)); + cards.add(new SetCardInfo("Howling Fury", 82, Rarity.UNCOMMON, mage.cards.h.HowlingFury.class)); + cards.add(new SetCardInfo("Hulking Goblin", 107, Rarity.COMMON, mage.cards.h.HulkingGoblin.class)); + cards.add(new SetCardInfo("Hulking Ogre", 108, Rarity.UNCOMMON, mage.cards.h.HulkingOgre.class)); + cards.add(new SetCardInfo("Ingenious Thief", 40, Rarity.COMMON, mage.cards.i.IngeniousThief.class)); + cards.add(new SetCardInfo("Island", 158, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 159, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 160, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 161, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jagged Lightning", 109, Rarity.UNCOMMON, mage.cards.j.JaggedLightning.class)); + cards.add(new SetCardInfo("Knight Errant", 19, Rarity.COMMON, mage.cards.k.KnightErrant.class)); + cards.add(new SetCardInfo("Last Chance", 110, Rarity.RARE, mage.cards.l.LastChance.class)); + cards.add(new SetCardInfo("Lava Axe", 111, Rarity.COMMON, mage.cards.l.LavaAxe.class)); + cards.add(new SetCardInfo("Lone Wolf", 130, Rarity.COMMON, mage.cards.l.LoneWolf.class)); + cards.add(new SetCardInfo("Loyal Sentry", 20, Rarity.RARE, mage.cards.l.LoyalSentry.class)); + cards.add(new SetCardInfo("Lynx", 131, Rarity.UNCOMMON, mage.cards.l.Lynx.class)); + cards.add(new SetCardInfo("Man-o'-War", 41, Rarity.UNCOMMON, mage.cards.m.ManOWar.class)); + cards.add(new SetCardInfo("Merfolk of the Pearl Trident", 42, Rarity.COMMON, mage.cards.m.MerfolkOfThePearlTrident.class)); + cards.add(new SetCardInfo("Mind Rot", 83, Rarity.COMMON, mage.cards.m.MindRot.class)); + cards.add(new SetCardInfo("Mons's Goblin Raiders", 112, Rarity.COMMON, mage.cards.m.MonssGoblinRaiders.class)); + cards.add(new SetCardInfo("Monstrous Growth", 132, Rarity.COMMON, mage.cards.m.MonstrousGrowth.class)); + cards.add(new SetCardInfo("Moon Sprite", 133, Rarity.UNCOMMON, mage.cards.m.MoonSprite.class)); + cards.add(new SetCardInfo("Mountain", 166, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 167, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 168, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 169, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Muck Rats", 84, Rarity.COMMON, mage.cards.m.MuckRats.class)); + cards.add(new SetCardInfo("Natural Spring", 134, Rarity.UNCOMMON, mage.cards.n.NaturalSpring.class)); + cards.add(new SetCardInfo("Nature's Cloak", 135, Rarity.RARE, mage.cards.n.NaturesCloak.class)); + cards.add(new SetCardInfo("Nature's Lore", 136, Rarity.COMMON, mage.cards.n.NaturesLore.class)); + cards.add(new SetCardInfo("Norwood Archers", 137, Rarity.COMMON, mage.cards.n.NorwoodArchers.class)); + cards.add(new SetCardInfo("Norwood Ranger", 138, Rarity.COMMON, mage.cards.n.NorwoodRanger.class)); + cards.add(new SetCardInfo("Ogre Warrior", 113, Rarity.COMMON, mage.cards.o.OgreWarrior.class)); + cards.add(new SetCardInfo("Path of Peace", 21, Rarity.COMMON, mage.cards.p.PathOfPeace.class)); + cards.add(new SetCardInfo("Phantom Warrior", 44, Rarity.RARE, mage.cards.p.PhantomWarrior.class)); + cards.add(new SetCardInfo("Plains", 154, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 155, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 156, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 157, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pride of Lions", 139, Rarity.UNCOMMON, mage.cards.p.PrideOfLions.class)); + cards.add(new SetCardInfo("Psychic Transfer", 46, Rarity.RARE, mage.cards.p.PsychicTransfer.class)); + cards.add(new SetCardInfo("Raging Goblin", 114, Rarity.COMMON, mage.cards.r.RagingGoblin.class)); + cards.add(new SetCardInfo("Raise Dead", 85, Rarity.COMMON, mage.cards.r.RaiseDead.class)); + cards.add(new SetCardInfo("Ransack", 47, Rarity.RARE, mage.cards.r.Ransack.class)); + cards.add(new SetCardInfo("Ravenous Rats", 86, Rarity.UNCOMMON, mage.cards.r.RavenousRats.class)); + cards.add(new SetCardInfo("Relearn", 48, Rarity.UNCOMMON, mage.cards.r.Relearn.class)); + cards.add(new SetCardInfo("Relentless Assault", 115, Rarity.RARE, mage.cards.r.RelentlessAssault.class)); + cards.add(new SetCardInfo("Remove Soul", 49, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); + cards.add(new SetCardInfo("Renewing Touch", 140, Rarity.UNCOMMON, mage.cards.r.RenewingTouch.class)); + cards.add(new SetCardInfo("Righteous Charge", 22, Rarity.UNCOMMON, mage.cards.r.RighteousCharge.class)); + cards.add(new SetCardInfo("Righteous Fury", 23, Rarity.RARE, mage.cards.r.RighteousFury.class)); + cards.add(new SetCardInfo("Royal Falcon", 24, Rarity.COMMON, mage.cards.r.RoyalFalcon.class)); + cards.add(new SetCardInfo("Royal Trooper", 25, Rarity.UNCOMMON, mage.cards.r.RoyalTrooper.class)); + cards.add(new SetCardInfo("Sacred Nectar", 26, Rarity.COMMON, mage.cards.s.SacredNectar.class)); + cards.add(new SetCardInfo("Scathe Zombies", 87, Rarity.COMMON, mage.cards.s.ScatheZombies.class)); + cards.add(new SetCardInfo("Scorching Spear", 116, Rarity.COMMON, mage.cards.s.ScorchingSpear.class)); + cards.add(new SetCardInfo("Sea Eagle", 50, Rarity.COMMON, mage.cards.s.SeaEagle.class)); + cards.add(new SetCardInfo("Serpent Warrior", 88, Rarity.COMMON, mage.cards.s.SerpentWarrior.class)); + cards.add(new SetCardInfo("Shrieking Specter", 89, Rarity.UNCOMMON, mage.cards.s.ShriekingSpecter.class)); + cards.add(new SetCardInfo("Silverback Ape", 141, Rarity.UNCOMMON, mage.cards.s.SilverbackApe.class)); + cards.add(new SetCardInfo("Sleight of Hand", 51, Rarity.COMMON, mage.cards.s.SleightOfHand.class)); + cards.add(new SetCardInfo("Snapping Drake", 52, Rarity.COMMON, mage.cards.s.SnappingDrake.class)); + cards.add(new SetCardInfo("Soul Feast", 90, Rarity.UNCOMMON, mage.cards.s.SoulFeast.class)); + cards.add(new SetCardInfo("Southern Elephant", 142, Rarity.COMMON, mage.cards.s.SouthernElephant.class)); + cards.add(new SetCardInfo("Spitting Earth", 117, Rarity.UNCOMMON, mage.cards.s.SpittingEarth.class)); + cards.add(new SetCardInfo("Squall", 143, Rarity.COMMON, mage.cards.s.Squall.class)); + cards.add(new SetCardInfo("Steadfastness", 27, Rarity.COMMON, mage.cards.s.Steadfastness.class)); + cards.add(new SetCardInfo("Stone Rain", 118, Rarity.COMMON, mage.cards.s.StoneRain.class)); + cards.add(new SetCardInfo("Storm Crow", 53, Rarity.COMMON, mage.cards.s.StormCrow.class)); + cards.add(new SetCardInfo("Stream of Acid", 91, Rarity.UNCOMMON, mage.cards.s.StreamOfAcid.class)); + cards.add(new SetCardInfo("Summer Bloom", 144, Rarity.RARE, mage.cards.s.SummerBloom.class)); + cards.add(new SetCardInfo("Swamp", 162, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 163, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 164, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 165, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sylvan Basilisk", 145, Rarity.RARE, mage.cards.s.SylvanBasilisk.class)); + cards.add(new SetCardInfo("Sylvan Yeti", 146, Rarity.RARE, mage.cards.s.SylvanYeti.class)); + cards.add(new SetCardInfo("Thorn Elemental", 147, Rarity.RARE, mage.cards.t.ThornElemental.class)); + cards.add(new SetCardInfo("Thunder Dragon", 119, Rarity.RARE, mage.cards.t.ThunderDragon.class)); + cards.add(new SetCardInfo("Tidings", 54, Rarity.UNCOMMON, mage.cards.t.Tidings.class)); + cards.add(new SetCardInfo("Time Ebb", 55, Rarity.COMMON, mage.cards.t.TimeEbb.class)); + cards.add(new SetCardInfo("Time Warp", 56, Rarity.RARE, mage.cards.t.TimeWarp.class)); + cards.add(new SetCardInfo("Touch of Brilliance", 57, Rarity.COMMON, mage.cards.t.TouchOfBrilliance.class)); + cards.add(new SetCardInfo("Trained Orgg", 120, Rarity.RARE, mage.cards.t.TrainedOrgg.class)); + cards.add(new SetCardInfo("Tremor", 121, Rarity.COMMON, mage.cards.t.Tremor.class)); + cards.add(new SetCardInfo("Undo", 58, Rarity.UNCOMMON, mage.cards.u.Undo.class)); + cards.add(new SetCardInfo("Untamed Wilds", 148, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); + cards.add(new SetCardInfo("Venerable Monk", 28, Rarity.COMMON, mage.cards.v.VenerableMonk.class)); + cards.add(new SetCardInfo("Vengeance", 29, Rarity.UNCOMMON, mage.cards.v.Vengeance.class)); + cards.add(new SetCardInfo("Veteran Cavalier", 30, Rarity.UNCOMMON, mage.cards.v.VeteranCavalier.class)); + cards.add(new SetCardInfo("Vizzerdrix", 59, Rarity.RARE, mage.cards.v.Vizzerdrix.class)); + cards.add(new SetCardInfo("Volcanic Dragon", 122, Rarity.RARE, mage.cards.v.VolcanicDragon.class)); + cards.add(new SetCardInfo("Volcanic Hammer", 123, Rarity.COMMON, mage.cards.v.VolcanicHammer.class)); + cards.add(new SetCardInfo("Water Elemental", 60, Rarity.UNCOMMON, mage.cards.w.WaterElemental.class)); + cards.add(new SetCardInfo("Whiptail Wurm", 149, Rarity.UNCOMMON, mage.cards.w.WhiptailWurm.class)); + cards.add(new SetCardInfo("Whirlwind", 150, Rarity.RARE, mage.cards.w.Whirlwind.class)); + cards.add(new SetCardInfo("Wicked Pact", 92, Rarity.RARE, mage.cards.w.WickedPact.class)); + cards.add(new SetCardInfo("Wild Griffin", 31, Rarity.COMMON, mage.cards.w.WildGriffin.class)); + cards.add(new SetCardInfo("Wild Ox", 151, Rarity.UNCOMMON, mage.cards.w.WildOx.class)); + cards.add(new SetCardInfo("Willow Elf", 152, Rarity.COMMON, mage.cards.w.WillowElf.class)); + cards.add(new SetCardInfo("Wind Drake", 61, Rarity.COMMON, mage.cards.w.WindDrake.class)); + cards.add(new SetCardInfo("Wind Sail", 62, Rarity.UNCOMMON, mage.cards.w.WindSail.class)); + cards.add(new SetCardInfo("Wood Elves", 153, Rarity.UNCOMMON, mage.cards.w.WoodElves.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Stronghold.java b/Mage.Sets/src/mage/sets/Stronghold.java index 173d3fcf77..644edecce6 100644 --- a/Mage.Sets/src/mage/sets/Stronghold.java +++ b/Mage.Sets/src/mage/sets/Stronghold.java @@ -1,174 +1,174 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * @author North - */ -public final class Stronghold extends ExpansionSet { - - private static final Stronghold instance = new Stronghold(); - - public static Stronghold getInstance() { - return instance; - } - - private Stronghold() { - super("Stronghold", "STH", ExpansionSet.buildDate(1998, 3, 2), SetType.EXPANSION); - this.blockName = "Tempest"; - this.parentSet = Tempest.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - - cards.add(new SetCardInfo("Acidic Sliver", 126, Rarity.UNCOMMON, mage.cards.a.AcidicSliver.class)); - cards.add(new SetCardInfo("Amok", 76, Rarity.RARE, mage.cards.a.Amok.class)); - cards.add(new SetCardInfo("Awakening", 101, Rarity.RARE, mage.cards.a.Awakening.class)); - cards.add(new SetCardInfo("Bandage", 1, Rarity.COMMON, mage.cards.b.Bandage.class)); - cards.add(new SetCardInfo("Bottomless Pit", 51, Rarity.UNCOMMON, mage.cards.b.BottomlessPit.class)); - cards.add(new SetCardInfo("Brush with Death", 52, Rarity.COMMON, mage.cards.b.BrushWithDeath.class)); - cards.add(new SetCardInfo("Bullwhip", 132, Rarity.UNCOMMON, mage.cards.b.Bullwhip.class)); - cards.add(new SetCardInfo("Burgeoning", 102, Rarity.RARE, mage.cards.b.Burgeoning.class)); - cards.add(new SetCardInfo("Calming Licid", 2, Rarity.UNCOMMON, mage.cards.c.CalmingLicid.class)); - cards.add(new SetCardInfo("Cannibalize", 53, Rarity.COMMON, mage.cards.c.Cannibalize.class)); - cards.add(new SetCardInfo("Carnassid", 103, Rarity.RARE, mage.cards.c.Carnassid.class)); - cards.add(new SetCardInfo("Change of Heart", 3, Rarity.COMMON, mage.cards.c.ChangeOfHeart.class)); - cards.add(new SetCardInfo("Cloud Spirit", 26, Rarity.COMMON, mage.cards.c.CloudSpirit.class)); - cards.add(new SetCardInfo("Constant Mists", 104, Rarity.UNCOMMON, mage.cards.c.ConstantMists.class)); - cards.add(new SetCardInfo("Contemplation", 4, Rarity.UNCOMMON, mage.cards.c.Contemplation.class)); - cards.add(new SetCardInfo("Contempt", 27, Rarity.COMMON, mage.cards.c.Contempt.class)); - cards.add(new SetCardInfo("Conviction", 5, Rarity.COMMON, mage.cards.c.Conviction.class)); - cards.add(new SetCardInfo("Convulsing Licid", 77, Rarity.UNCOMMON, mage.cards.c.ConvulsingLicid.class)); - cards.add(new SetCardInfo("Corrupting Licid", 54, Rarity.UNCOMMON, mage.cards.c.CorruptingLicid.class)); - cards.add(new SetCardInfo("Craven Giant", 78, Rarity.COMMON, mage.cards.c.CravenGiant.class)); - cards.add(new SetCardInfo("Crossbow Ambush", 105, Rarity.COMMON, mage.cards.c.CrossbowAmbush.class)); - cards.add(new SetCardInfo("Crovax the Cursed", 55, Rarity.RARE, mage.cards.c.CrovaxTheCursed.class)); - cards.add(new SetCardInfo("Crystalline Sliver", 127, Rarity.UNCOMMON, mage.cards.c.CrystallineSliver.class)); - cards.add(new SetCardInfo("Dauthi Trapper", 56, Rarity.UNCOMMON, mage.cards.d.DauthiTrapper.class)); - cards.add(new SetCardInfo("Death Stroke", 57, Rarity.COMMON, mage.cards.d.DeathStroke.class)); - cards.add(new SetCardInfo("Dream Halls", 28, Rarity.RARE, mage.cards.d.DreamHalls.class)); - cards.add(new SetCardInfo("Dream Prowler", 29, Rarity.COMMON, mage.cards.d.DreamProwler.class)); - cards.add(new SetCardInfo("Duct Crawler", 79, Rarity.COMMON, mage.cards.d.DuctCrawler.class)); - cards.add(new SetCardInfo("Dungeon Shade", 58, Rarity.COMMON, mage.cards.d.DungeonShade.class)); - cards.add(new SetCardInfo("Elven Rite", 106, Rarity.UNCOMMON, mage.cards.e.ElvenRite.class)); - cards.add(new SetCardInfo("Endangered Armodon", 107, Rarity.COMMON, mage.cards.e.EndangeredArmodon.class)); - cards.add(new SetCardInfo("Ensnaring Bridge", 133, Rarity.RARE, mage.cards.e.EnsnaringBridge.class)); - cards.add(new SetCardInfo("Evacuation", 30, Rarity.RARE, mage.cards.e.Evacuation.class)); - cards.add(new SetCardInfo("Fanning the Flames", 80, Rarity.UNCOMMON, mage.cards.f.FanningTheFlames.class)); - cards.add(new SetCardInfo("Flame Wave", 81, Rarity.UNCOMMON, mage.cards.f.FlameWave.class)); - cards.add(new SetCardInfo("Fling", 82, Rarity.COMMON, mage.cards.f.Fling.class)); - cards.add(new SetCardInfo("Flowstone Blade", 83, Rarity.COMMON, mage.cards.f.FlowstoneBlade.class)); - cards.add(new SetCardInfo("Flowstone Hellion", 84, Rarity.UNCOMMON, mage.cards.f.FlowstoneHellion.class)); - cards.add(new SetCardInfo("Flowstone Mauler", 85, Rarity.RARE, mage.cards.f.FlowstoneMauler.class)); - cards.add(new SetCardInfo("Flowstone Shambler", 86, Rarity.COMMON, mage.cards.f.FlowstoneShambler.class)); - cards.add(new SetCardInfo("Foul Imp", 59, Rarity.COMMON, mage.cards.f.FoulImp.class)); - cards.add(new SetCardInfo("Furnace Spirit", 87, Rarity.COMMON, mage.cards.f.FurnaceSpirit.class)); - cards.add(new SetCardInfo("Gliding Licid", 31, Rarity.UNCOMMON, mage.cards.g.GlidingLicid.class)); - cards.add(new SetCardInfo("Grave Pact", 60, Rarity.RARE, mage.cards.g.GravePact.class)); - cards.add(new SetCardInfo("Hammerhead Shark", 32, Rarity.COMMON, mage.cards.h.HammerheadShark.class)); - cards.add(new SetCardInfo("Heartstone", 134, Rarity.UNCOMMON, mage.cards.h.Heartstone.class)); - cards.add(new SetCardInfo("Heat of Battle", 88, Rarity.UNCOMMON, mage.cards.h.HeatOfBattle.class)); - cards.add(new SetCardInfo("Hermit Druid", 108, Rarity.RARE, mage.cards.h.HermitDruid.class)); - cards.add(new SetCardInfo("Hesitation", 33, Rarity.UNCOMMON, mage.cards.h.Hesitation.class)); - cards.add(new SetCardInfo("Hibernation Sliver", 128, Rarity.UNCOMMON, mage.cards.h.HibernationSliver.class)); - cards.add(new SetCardInfo("Hidden Retreat", 6, Rarity.RARE, mage.cards.h.HiddenRetreat.class)); - cards.add(new SetCardInfo("Honor Guard", 7, Rarity.COMMON, mage.cards.h.HonorGuard.class)); - cards.add(new SetCardInfo("Horn of Greed", 135, Rarity.RARE, mage.cards.h.HornOfGreed.class)); - cards.add(new SetCardInfo("Hornet Cannon", 136, Rarity.UNCOMMON, mage.cards.h.HornetCannon.class)); - cards.add(new SetCardInfo("Intruder Alarm", 34, Rarity.RARE, mage.cards.i.IntruderAlarm.class)); - cards.add(new SetCardInfo("Invasion Plans", 89, Rarity.RARE, mage.cards.i.InvasionPlans.class)); - cards.add(new SetCardInfo("Jinxed Ring", 137, Rarity.RARE, mage.cards.j.JinxedRing.class)); - cards.add(new SetCardInfo("Lab Rats", 61, Rarity.COMMON, mage.cards.l.LabRats.class)); - cards.add(new SetCardInfo("Lancers en-Kor", 8, Rarity.UNCOMMON, mage.cards.l.LancersEnKor.class)); - cards.add(new SetCardInfo("Leap", 35, Rarity.COMMON, mage.cards.l.Leap.class)); - cards.add(new SetCardInfo("Lowland Basilisk", 109, Rarity.COMMON, mage.cards.l.LowlandBasilisk.class)); - cards.add(new SetCardInfo("Mana Leak", 36, Rarity.COMMON, mage.cards.m.ManaLeak.class)); - cards.add(new SetCardInfo("Mask of the Mimic", 37, Rarity.UNCOMMON, mage.cards.m.MaskOfTheMimic.class)); - cards.add(new SetCardInfo("Megrim", 62, Rarity.UNCOMMON, mage.cards.m.Megrim.class)); - cards.add(new SetCardInfo("Mind Games", 38, Rarity.COMMON, mage.cards.m.MindGames.class)); - cards.add(new SetCardInfo("Mind Peel", 63, Rarity.UNCOMMON, mage.cards.m.MindPeel.class)); - cards.add(new SetCardInfo("Mindwarper", 64, Rarity.RARE, mage.cards.m.Mindwarper.class)); - cards.add(new SetCardInfo("Mob Justice", 90, Rarity.COMMON, mage.cards.m.MobJustice.class)); - cards.add(new SetCardInfo("Mogg Bombers", 91, Rarity.COMMON, mage.cards.m.MoggBombers.class)); - cards.add(new SetCardInfo("Mogg Flunkies", 92, Rarity.COMMON, mage.cards.m.MoggFlunkies.class)); - cards.add(new SetCardInfo("Mogg Infestation", 93, Rarity.RARE, mage.cards.m.MoggInfestation.class)); - cards.add(new SetCardInfo("Mogg Maniac", 94, Rarity.UNCOMMON, mage.cards.m.MoggManiac.class)); - cards.add(new SetCardInfo("Morgue Thrull", 65, Rarity.COMMON, mage.cards.m.MorgueThrull.class)); - cards.add(new SetCardInfo("Mortuary", 66, Rarity.RARE, mage.cards.m.Mortuary.class)); - cards.add(new SetCardInfo("Mox Diamond", 138, Rarity.RARE, mage.cards.m.MoxDiamond.class)); - cards.add(new SetCardInfo("Mulch", 110, Rarity.COMMON, mage.cards.m.Mulch.class)); - cards.add(new SetCardInfo("Nomads en-Kor", 9, Rarity.COMMON, mage.cards.n.NomadsEnKor.class)); - cards.add(new SetCardInfo("Overgrowth", 111, Rarity.COMMON, mage.cards.o.Overgrowth.class)); - cards.add(new SetCardInfo("Portcullis", 139, Rarity.RARE, mage.cards.p.Portcullis.class)); - cards.add(new SetCardInfo("Primal Rage", 112, Rarity.UNCOMMON, mage.cards.p.PrimalRage.class)); - cards.add(new SetCardInfo("Provoke", 113, Rarity.COMMON, mage.cards.p.Provoke.class)); - cards.add(new SetCardInfo("Pursuit of Knowledge", 10, Rarity.RARE, mage.cards.p.PursuitOfKnowledge.class)); - cards.add(new SetCardInfo("Rabid Rats", 67, Rarity.COMMON, mage.cards.r.RabidRats.class)); - cards.add(new SetCardInfo("Ransack", 39, Rarity.UNCOMMON, mage.cards.r.Ransack.class)); - cards.add(new SetCardInfo("Rebound", 40, Rarity.UNCOMMON, mage.cards.r.Rebound.class)); - cards.add(new SetCardInfo("Reins of Power", 41, Rarity.RARE, mage.cards.r.ReinsOfPower.class)); - cards.add(new SetCardInfo("Revenant", 68, Rarity.RARE, mage.cards.r.Revenant.class)); - cards.add(new SetCardInfo("Rolling Stones", 11, Rarity.RARE, mage.cards.r.RollingStones.class)); - cards.add(new SetCardInfo("Ruination", 95, Rarity.RARE, mage.cards.r.Ruination.class)); - cards.add(new SetCardInfo("Sacred Ground", 12, Rarity.RARE, mage.cards.s.SacredGround.class)); - cards.add(new SetCardInfo("Samite Blessing", 13, Rarity.COMMON, mage.cards.s.SamiteBlessing.class)); - cards.add(new SetCardInfo("Scapegoat", 14, Rarity.UNCOMMON, mage.cards.s.Scapegoat.class)); - cards.add(new SetCardInfo("Seething Anger", 96, Rarity.COMMON, mage.cards.s.SeethingAnger.class)); - cards.add(new SetCardInfo("Serpent Warrior", 69, Rarity.COMMON, mage.cards.s.SerpentWarrior.class)); - cards.add(new SetCardInfo("Shaman en-Kor", 15, Rarity.RARE, mage.cards.s.ShamanEnKor.class)); - cards.add(new SetCardInfo("Shard Phoenix", 97, Rarity.RARE, mage.cards.s.ShardPhoenix.class)); - cards.add(new SetCardInfo("Shifting Wall", 140, Rarity.UNCOMMON, mage.cards.s.ShiftingWall.class)); - cards.add(new SetCardInfo("Shock", 98, Rarity.COMMON, mage.cards.s.Shock.class)); - cards.add(new SetCardInfo("Sift", 42, Rarity.COMMON, mage.cards.s.Sift.class)); - cards.add(new SetCardInfo("Silver Wyvern", 43, Rarity.RARE, mage.cards.s.SilverWyvern.class)); - cards.add(new SetCardInfo("Skeleton Scavengers", 70, Rarity.RARE, mage.cards.s.SkeletonScavengers.class)); - cards.add(new SetCardInfo("Skyshroud Archer", 114, Rarity.COMMON, mage.cards.s.SkyshroudArcher.class)); - cards.add(new SetCardInfo("Skyshroud Falcon", 16, Rarity.COMMON, mage.cards.s.SkyshroudFalcon.class)); - cards.add(new SetCardInfo("Skyshroud Troopers", 115, Rarity.COMMON, mage.cards.s.SkyshroudTroopers.class)); - cards.add(new SetCardInfo("Sliver Queen", 129, Rarity.RARE, mage.cards.s.SliverQueen.class)); - cards.add(new SetCardInfo("Smite", 17, Rarity.COMMON, mage.cards.s.Smite.class)); - cards.add(new SetCardInfo("Soltari Champion", 18, Rarity.RARE, mage.cards.s.SoltariChampion.class)); - cards.add(new SetCardInfo("Spike Breeder", 116, Rarity.RARE, mage.cards.s.SpikeBreeder.class)); - cards.add(new SetCardInfo("Spike Colony", 117, Rarity.COMMON, mage.cards.s.SpikeColony.class)); - cards.add(new SetCardInfo("Spike Feeder", 118, Rarity.UNCOMMON, mage.cards.s.SpikeFeeder.class)); - cards.add(new SetCardInfo("Spike Soldier", 119, Rarity.UNCOMMON, mage.cards.s.SpikeSoldier.class)); - cards.add(new SetCardInfo("Spike Worker", 120, Rarity.COMMON, mage.cards.s.SpikeWorker.class)); - cards.add(new SetCardInfo("Spindrift Drake", 44, Rarity.COMMON, mage.cards.s.SpindriftDrake.class)); - cards.add(new SetCardInfo("Spined Sliver", 130, Rarity.UNCOMMON, mage.cards.s.SpinedSliver.class)); - cards.add(new SetCardInfo("Spined Wurm", 121, Rarity.COMMON, mage.cards.s.SpinedWurm.class)); - cards.add(new SetCardInfo("Spirit en-Kor", 19, Rarity.COMMON, mage.cards.s.SpiritEnKor.class)); - cards.add(new SetCardInfo("Spitting Hydra", 99, Rarity.RARE, mage.cards.s.SpittingHydra.class)); - cards.add(new SetCardInfo("Stronghold Assassin", 71, Rarity.RARE, mage.cards.s.StrongholdAssassin.class)); - cards.add(new SetCardInfo("Stronghold Taskmaster", 72, Rarity.UNCOMMON, mage.cards.s.StrongholdTaskmaster.class)); - cards.add(new SetCardInfo("Sword of the Chosen", 141, Rarity.RARE, mage.cards.s.SwordOfTheChosen.class)); - cards.add(new SetCardInfo("Temper", 20, Rarity.UNCOMMON, mage.cards.t.Temper.class)); - cards.add(new SetCardInfo("Tempting Licid", 122, Rarity.UNCOMMON, mage.cards.t.TemptingLicid.class)); - cards.add(new SetCardInfo("Thalakos Deceiver", 45, Rarity.RARE, mage.cards.t.ThalakosDeceiver.class)); - cards.add(new SetCardInfo("Tidal Surge", 46, Rarity.COMMON, mage.cards.t.TidalSurge.class)); - cards.add(new SetCardInfo("Tidal Warrior", 47, Rarity.COMMON, mage.cards.t.TidalWarrior.class)); - cards.add(new SetCardInfo("Torment", 73, Rarity.COMMON, mage.cards.t.Torment.class)); - cards.add(new SetCardInfo("Tortured Existence", 74, Rarity.COMMON, mage.cards.t.TorturedExistence.class)); - cards.add(new SetCardInfo("Venerable Monk", 21, Rarity.COMMON, mage.cards.v.VenerableMonk.class)); - cards.add(new SetCardInfo("Verdant Touch", 123, Rarity.RARE, mage.cards.v.VerdantTouch.class)); - cards.add(new SetCardInfo("Victual Sliver", 131, Rarity.UNCOMMON, mage.cards.v.VictualSliver.class)); - cards.add(new SetCardInfo("Volrath's Gardens", 124, Rarity.RARE, mage.cards.v.VolrathsGardens.class)); - cards.add(new SetCardInfo("Volrath's Laboratory", 142, Rarity.RARE, mage.cards.v.VolrathsLaboratory.class)); - cards.add(new SetCardInfo("Volrath's Shapeshifter", 48, Rarity.RARE, mage.cards.v.VolrathsShapeshifter.class)); - cards.add(new SetCardInfo("Volrath's Stronghold", 143, Rarity.RARE, mage.cards.v.VolrathsStronghold.class)); - cards.add(new SetCardInfo("Walking Dream", 49, Rarity.UNCOMMON, mage.cards.w.WalkingDream.class)); - cards.add(new SetCardInfo("Wall of Blossoms", 125, Rarity.UNCOMMON, mage.cards.w.WallOfBlossoms.class)); - cards.add(new SetCardInfo("Wall of Essence", 22, Rarity.UNCOMMON, mage.cards.w.WallOfEssence.class)); - cards.add(new SetCardInfo("Wall of Razors", 100, Rarity.UNCOMMON, mage.cards.w.WallOfRazors.class)); - cards.add(new SetCardInfo("Wall of Souls", 75, Rarity.UNCOMMON, mage.cards.w.WallOfSouls.class)); - cards.add(new SetCardInfo("Wall of Tears", 50, Rarity.UNCOMMON, mage.cards.w.WallOfTears.class)); - cards.add(new SetCardInfo("Warrior Angel", 24, Rarity.RARE, mage.cards.w.WarriorAngel.class)); - cards.add(new SetCardInfo("Warrior en-Kor", 23, Rarity.UNCOMMON, mage.cards.w.WarriorEnKor.class)); - cards.add(new SetCardInfo("Youthful Knight", 25, Rarity.COMMON, mage.cards.y.YouthfulKnight.class)); - } -} +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author North + */ +public final class Stronghold extends ExpansionSet { + + private static final Stronghold instance = new Stronghold(); + + public static Stronghold getInstance() { + return instance; + } + + private Stronghold() { + super("Stronghold", "STH", ExpansionSet.buildDate(1998, 3, 2), SetType.EXPANSION); + this.blockName = "Tempest"; + this.parentSet = Tempest.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + + cards.add(new SetCardInfo("Acidic Sliver", 126, Rarity.UNCOMMON, mage.cards.a.AcidicSliver.class)); + cards.add(new SetCardInfo("Amok", 76, Rarity.RARE, mage.cards.a.Amok.class)); + cards.add(new SetCardInfo("Awakening", 101, Rarity.RARE, mage.cards.a.Awakening.class)); + cards.add(new SetCardInfo("Bandage", 1, Rarity.COMMON, mage.cards.b.Bandage.class)); + cards.add(new SetCardInfo("Bottomless Pit", 51, Rarity.UNCOMMON, mage.cards.b.BottomlessPit.class)); + cards.add(new SetCardInfo("Brush with Death", 52, Rarity.COMMON, mage.cards.b.BrushWithDeath.class)); + cards.add(new SetCardInfo("Bullwhip", 132, Rarity.UNCOMMON, mage.cards.b.Bullwhip.class)); + cards.add(new SetCardInfo("Burgeoning", 102, Rarity.RARE, mage.cards.b.Burgeoning.class)); + cards.add(new SetCardInfo("Calming Licid", 2, Rarity.UNCOMMON, mage.cards.c.CalmingLicid.class)); + cards.add(new SetCardInfo("Cannibalize", 53, Rarity.COMMON, mage.cards.c.Cannibalize.class)); + cards.add(new SetCardInfo("Carnassid", 103, Rarity.RARE, mage.cards.c.Carnassid.class)); + cards.add(new SetCardInfo("Change of Heart", 3, Rarity.COMMON, mage.cards.c.ChangeOfHeart.class)); + cards.add(new SetCardInfo("Cloud Spirit", 26, Rarity.COMMON, mage.cards.c.CloudSpirit.class)); + cards.add(new SetCardInfo("Constant Mists", 104, Rarity.UNCOMMON, mage.cards.c.ConstantMists.class)); + cards.add(new SetCardInfo("Contemplation", 4, Rarity.UNCOMMON, mage.cards.c.Contemplation.class)); + cards.add(new SetCardInfo("Contempt", 27, Rarity.COMMON, mage.cards.c.Contempt.class)); + cards.add(new SetCardInfo("Conviction", 5, Rarity.COMMON, mage.cards.c.Conviction.class)); + cards.add(new SetCardInfo("Convulsing Licid", 77, Rarity.UNCOMMON, mage.cards.c.ConvulsingLicid.class)); + cards.add(new SetCardInfo("Corrupting Licid", 54, Rarity.UNCOMMON, mage.cards.c.CorruptingLicid.class)); + cards.add(new SetCardInfo("Craven Giant", 78, Rarity.COMMON, mage.cards.c.CravenGiant.class)); + cards.add(new SetCardInfo("Crossbow Ambush", 105, Rarity.COMMON, mage.cards.c.CrossbowAmbush.class)); + cards.add(new SetCardInfo("Crovax the Cursed", 55, Rarity.RARE, mage.cards.c.CrovaxTheCursed.class)); + cards.add(new SetCardInfo("Crystalline Sliver", 127, Rarity.UNCOMMON, mage.cards.c.CrystallineSliver.class)); + cards.add(new SetCardInfo("Dauthi Trapper", 56, Rarity.UNCOMMON, mage.cards.d.DauthiTrapper.class)); + cards.add(new SetCardInfo("Death Stroke", 57, Rarity.COMMON, mage.cards.d.DeathStroke.class)); + cards.add(new SetCardInfo("Dream Halls", 28, Rarity.RARE, mage.cards.d.DreamHalls.class)); + cards.add(new SetCardInfo("Dream Prowler", 29, Rarity.COMMON, mage.cards.d.DreamProwler.class)); + cards.add(new SetCardInfo("Duct Crawler", 79, Rarity.COMMON, mage.cards.d.DuctCrawler.class)); + cards.add(new SetCardInfo("Dungeon Shade", 58, Rarity.COMMON, mage.cards.d.DungeonShade.class)); + cards.add(new SetCardInfo("Elven Rite", 106, Rarity.UNCOMMON, mage.cards.e.ElvenRite.class)); + cards.add(new SetCardInfo("Endangered Armodon", 107, Rarity.COMMON, mage.cards.e.EndangeredArmodon.class)); + cards.add(new SetCardInfo("Ensnaring Bridge", 133, Rarity.RARE, mage.cards.e.EnsnaringBridge.class)); + cards.add(new SetCardInfo("Evacuation", 30, Rarity.RARE, mage.cards.e.Evacuation.class)); + cards.add(new SetCardInfo("Fanning the Flames", 80, Rarity.UNCOMMON, mage.cards.f.FanningTheFlames.class)); + cards.add(new SetCardInfo("Flame Wave", 81, Rarity.UNCOMMON, mage.cards.f.FlameWave.class)); + cards.add(new SetCardInfo("Fling", 82, Rarity.COMMON, mage.cards.f.Fling.class)); + cards.add(new SetCardInfo("Flowstone Blade", 83, Rarity.COMMON, mage.cards.f.FlowstoneBlade.class)); + cards.add(new SetCardInfo("Flowstone Hellion", 84, Rarity.UNCOMMON, mage.cards.f.FlowstoneHellion.class)); + cards.add(new SetCardInfo("Flowstone Mauler", 85, Rarity.RARE, mage.cards.f.FlowstoneMauler.class)); + cards.add(new SetCardInfo("Flowstone Shambler", 86, Rarity.COMMON, mage.cards.f.FlowstoneShambler.class)); + cards.add(new SetCardInfo("Foul Imp", 59, Rarity.COMMON, mage.cards.f.FoulImp.class)); + cards.add(new SetCardInfo("Furnace Spirit", 87, Rarity.COMMON, mage.cards.f.FurnaceSpirit.class)); + cards.add(new SetCardInfo("Gliding Licid", 31, Rarity.UNCOMMON, mage.cards.g.GlidingLicid.class)); + cards.add(new SetCardInfo("Grave Pact", 60, Rarity.RARE, mage.cards.g.GravePact.class)); + cards.add(new SetCardInfo("Hammerhead Shark", 32, Rarity.COMMON, mage.cards.h.HammerheadShark.class)); + cards.add(new SetCardInfo("Heartstone", 134, Rarity.UNCOMMON, mage.cards.h.Heartstone.class)); + cards.add(new SetCardInfo("Heat of Battle", 88, Rarity.UNCOMMON, mage.cards.h.HeatOfBattle.class)); + cards.add(new SetCardInfo("Hermit Druid", 108, Rarity.RARE, mage.cards.h.HermitDruid.class)); + cards.add(new SetCardInfo("Hesitation", 33, Rarity.UNCOMMON, mage.cards.h.Hesitation.class)); + cards.add(new SetCardInfo("Hibernation Sliver", 128, Rarity.UNCOMMON, mage.cards.h.HibernationSliver.class)); + cards.add(new SetCardInfo("Hidden Retreat", 6, Rarity.RARE, mage.cards.h.HiddenRetreat.class)); + cards.add(new SetCardInfo("Honor Guard", 7, Rarity.COMMON, mage.cards.h.HonorGuard.class)); + cards.add(new SetCardInfo("Horn of Greed", 135, Rarity.RARE, mage.cards.h.HornOfGreed.class)); + cards.add(new SetCardInfo("Hornet Cannon", 136, Rarity.UNCOMMON, mage.cards.h.HornetCannon.class)); + cards.add(new SetCardInfo("Intruder Alarm", 34, Rarity.RARE, mage.cards.i.IntruderAlarm.class)); + cards.add(new SetCardInfo("Invasion Plans", 89, Rarity.RARE, mage.cards.i.InvasionPlans.class)); + cards.add(new SetCardInfo("Jinxed Ring", 137, Rarity.RARE, mage.cards.j.JinxedRing.class)); + cards.add(new SetCardInfo("Lab Rats", 61, Rarity.COMMON, mage.cards.l.LabRats.class)); + cards.add(new SetCardInfo("Lancers en-Kor", 8, Rarity.UNCOMMON, mage.cards.l.LancersEnKor.class)); + cards.add(new SetCardInfo("Leap", 35, Rarity.COMMON, mage.cards.l.Leap.class)); + cards.add(new SetCardInfo("Lowland Basilisk", 109, Rarity.COMMON, mage.cards.l.LowlandBasilisk.class)); + cards.add(new SetCardInfo("Mana Leak", 36, Rarity.COMMON, mage.cards.m.ManaLeak.class)); + cards.add(new SetCardInfo("Mask of the Mimic", 37, Rarity.UNCOMMON, mage.cards.m.MaskOfTheMimic.class)); + cards.add(new SetCardInfo("Megrim", 62, Rarity.UNCOMMON, mage.cards.m.Megrim.class)); + cards.add(new SetCardInfo("Mind Games", 38, Rarity.COMMON, mage.cards.m.MindGames.class)); + cards.add(new SetCardInfo("Mind Peel", 63, Rarity.UNCOMMON, mage.cards.m.MindPeel.class)); + cards.add(new SetCardInfo("Mindwarper", 64, Rarity.RARE, mage.cards.m.Mindwarper.class)); + cards.add(new SetCardInfo("Mob Justice", 90, Rarity.COMMON, mage.cards.m.MobJustice.class)); + cards.add(new SetCardInfo("Mogg Bombers", 91, Rarity.COMMON, mage.cards.m.MoggBombers.class)); + cards.add(new SetCardInfo("Mogg Flunkies", 92, Rarity.COMMON, mage.cards.m.MoggFlunkies.class)); + cards.add(new SetCardInfo("Mogg Infestation", 93, Rarity.RARE, mage.cards.m.MoggInfestation.class)); + cards.add(new SetCardInfo("Mogg Maniac", 94, Rarity.UNCOMMON, mage.cards.m.MoggManiac.class)); + cards.add(new SetCardInfo("Morgue Thrull", 65, Rarity.COMMON, mage.cards.m.MorgueThrull.class)); + cards.add(new SetCardInfo("Mortuary", 66, Rarity.RARE, mage.cards.m.Mortuary.class)); + cards.add(new SetCardInfo("Mox Diamond", 138, Rarity.RARE, mage.cards.m.MoxDiamond.class)); + cards.add(new SetCardInfo("Mulch", 110, Rarity.COMMON, mage.cards.m.Mulch.class)); + cards.add(new SetCardInfo("Nomads en-Kor", 9, Rarity.COMMON, mage.cards.n.NomadsEnKor.class)); + cards.add(new SetCardInfo("Overgrowth", 111, Rarity.COMMON, mage.cards.o.Overgrowth.class)); + cards.add(new SetCardInfo("Portcullis", 139, Rarity.RARE, mage.cards.p.Portcullis.class)); + cards.add(new SetCardInfo("Primal Rage", 112, Rarity.UNCOMMON, mage.cards.p.PrimalRage.class)); + cards.add(new SetCardInfo("Provoke", 113, Rarity.COMMON, mage.cards.p.Provoke.class)); + cards.add(new SetCardInfo("Pursuit of Knowledge", 10, Rarity.RARE, mage.cards.p.PursuitOfKnowledge.class)); + cards.add(new SetCardInfo("Rabid Rats", 67, Rarity.COMMON, mage.cards.r.RabidRats.class)); + cards.add(new SetCardInfo("Ransack", 39, Rarity.UNCOMMON, mage.cards.r.Ransack.class)); + cards.add(new SetCardInfo("Rebound", 40, Rarity.UNCOMMON, mage.cards.r.Rebound.class)); + cards.add(new SetCardInfo("Reins of Power", 41, Rarity.RARE, mage.cards.r.ReinsOfPower.class)); + cards.add(new SetCardInfo("Revenant", 68, Rarity.RARE, mage.cards.r.Revenant.class)); + cards.add(new SetCardInfo("Rolling Stones", 11, Rarity.RARE, mage.cards.r.RollingStones.class)); + cards.add(new SetCardInfo("Ruination", 95, Rarity.RARE, mage.cards.r.Ruination.class)); + cards.add(new SetCardInfo("Sacred Ground", 12, Rarity.RARE, mage.cards.s.SacredGround.class)); + cards.add(new SetCardInfo("Samite Blessing", 13, Rarity.COMMON, mage.cards.s.SamiteBlessing.class)); + cards.add(new SetCardInfo("Scapegoat", 14, Rarity.UNCOMMON, mage.cards.s.Scapegoat.class)); + cards.add(new SetCardInfo("Seething Anger", 96, Rarity.COMMON, mage.cards.s.SeethingAnger.class)); + cards.add(new SetCardInfo("Serpent Warrior", 69, Rarity.COMMON, mage.cards.s.SerpentWarrior.class)); + cards.add(new SetCardInfo("Shaman en-Kor", 15, Rarity.RARE, mage.cards.s.ShamanEnKor.class)); + cards.add(new SetCardInfo("Shard Phoenix", 97, Rarity.RARE, mage.cards.s.ShardPhoenix.class)); + cards.add(new SetCardInfo("Shifting Wall", 140, Rarity.UNCOMMON, mage.cards.s.ShiftingWall.class)); + cards.add(new SetCardInfo("Shock", 98, Rarity.COMMON, mage.cards.s.Shock.class)); + cards.add(new SetCardInfo("Sift", 42, Rarity.COMMON, mage.cards.s.Sift.class)); + cards.add(new SetCardInfo("Silver Wyvern", 43, Rarity.RARE, mage.cards.s.SilverWyvern.class)); + cards.add(new SetCardInfo("Skeleton Scavengers", 70, Rarity.RARE, mage.cards.s.SkeletonScavengers.class)); + cards.add(new SetCardInfo("Skyshroud Archer", 114, Rarity.COMMON, mage.cards.s.SkyshroudArcher.class)); + cards.add(new SetCardInfo("Skyshroud Falcon", 16, Rarity.COMMON, mage.cards.s.SkyshroudFalcon.class)); + cards.add(new SetCardInfo("Skyshroud Troopers", 115, Rarity.COMMON, mage.cards.s.SkyshroudTroopers.class)); + cards.add(new SetCardInfo("Sliver Queen", 129, Rarity.RARE, mage.cards.s.SliverQueen.class)); + cards.add(new SetCardInfo("Smite", 17, Rarity.COMMON, mage.cards.s.Smite.class)); + cards.add(new SetCardInfo("Soltari Champion", 18, Rarity.RARE, mage.cards.s.SoltariChampion.class)); + cards.add(new SetCardInfo("Spike Breeder", 116, Rarity.RARE, mage.cards.s.SpikeBreeder.class)); + cards.add(new SetCardInfo("Spike Colony", 117, Rarity.COMMON, mage.cards.s.SpikeColony.class)); + cards.add(new SetCardInfo("Spike Feeder", 118, Rarity.UNCOMMON, mage.cards.s.SpikeFeeder.class)); + cards.add(new SetCardInfo("Spike Soldier", 119, Rarity.UNCOMMON, mage.cards.s.SpikeSoldier.class)); + cards.add(new SetCardInfo("Spike Worker", 120, Rarity.COMMON, mage.cards.s.SpikeWorker.class)); + cards.add(new SetCardInfo("Spindrift Drake", 44, Rarity.COMMON, mage.cards.s.SpindriftDrake.class)); + cards.add(new SetCardInfo("Spined Sliver", 130, Rarity.UNCOMMON, mage.cards.s.SpinedSliver.class)); + cards.add(new SetCardInfo("Spined Wurm", 121, Rarity.COMMON, mage.cards.s.SpinedWurm.class)); + cards.add(new SetCardInfo("Spirit en-Kor", 19, Rarity.COMMON, mage.cards.s.SpiritEnKor.class)); + cards.add(new SetCardInfo("Spitting Hydra", 99, Rarity.RARE, mage.cards.s.SpittingHydra.class)); + cards.add(new SetCardInfo("Stronghold Assassin", 71, Rarity.RARE, mage.cards.s.StrongholdAssassin.class)); + cards.add(new SetCardInfo("Stronghold Taskmaster", 72, Rarity.UNCOMMON, mage.cards.s.StrongholdTaskmaster.class)); + cards.add(new SetCardInfo("Sword of the Chosen", 141, Rarity.RARE, mage.cards.s.SwordOfTheChosen.class)); + cards.add(new SetCardInfo("Temper", 20, Rarity.UNCOMMON, mage.cards.t.Temper.class)); + cards.add(new SetCardInfo("Tempting Licid", 122, Rarity.UNCOMMON, mage.cards.t.TemptingLicid.class)); + cards.add(new SetCardInfo("Thalakos Deceiver", 45, Rarity.RARE, mage.cards.t.ThalakosDeceiver.class)); + cards.add(new SetCardInfo("Tidal Surge", 46, Rarity.COMMON, mage.cards.t.TidalSurge.class)); + cards.add(new SetCardInfo("Tidal Warrior", 47, Rarity.COMMON, mage.cards.t.TidalWarrior.class)); + cards.add(new SetCardInfo("Torment", 73, Rarity.COMMON, mage.cards.t.Torment.class)); + cards.add(new SetCardInfo("Tortured Existence", 74, Rarity.COMMON, mage.cards.t.TorturedExistence.class)); + cards.add(new SetCardInfo("Venerable Monk", 21, Rarity.COMMON, mage.cards.v.VenerableMonk.class)); + cards.add(new SetCardInfo("Verdant Touch", 123, Rarity.RARE, mage.cards.v.VerdantTouch.class)); + cards.add(new SetCardInfo("Victual Sliver", 131, Rarity.UNCOMMON, mage.cards.v.VictualSliver.class)); + cards.add(new SetCardInfo("Volrath's Gardens", 124, Rarity.RARE, mage.cards.v.VolrathsGardens.class)); + cards.add(new SetCardInfo("Volrath's Laboratory", 142, Rarity.RARE, mage.cards.v.VolrathsLaboratory.class)); + cards.add(new SetCardInfo("Volrath's Shapeshifter", 48, Rarity.RARE, mage.cards.v.VolrathsShapeshifter.class)); + cards.add(new SetCardInfo("Volrath's Stronghold", 143, Rarity.RARE, mage.cards.v.VolrathsStronghold.class)); + cards.add(new SetCardInfo("Walking Dream", 49, Rarity.UNCOMMON, mage.cards.w.WalkingDream.class)); + cards.add(new SetCardInfo("Wall of Blossoms", 125, Rarity.UNCOMMON, mage.cards.w.WallOfBlossoms.class)); + cards.add(new SetCardInfo("Wall of Essence", 22, Rarity.UNCOMMON, mage.cards.w.WallOfEssence.class)); + cards.add(new SetCardInfo("Wall of Razors", 100, Rarity.UNCOMMON, mage.cards.w.WallOfRazors.class)); + cards.add(new SetCardInfo("Wall of Souls", 75, Rarity.UNCOMMON, mage.cards.w.WallOfSouls.class)); + cards.add(new SetCardInfo("Wall of Tears", 50, Rarity.UNCOMMON, mage.cards.w.WallOfTears.class)); + cards.add(new SetCardInfo("Warrior Angel", 24, Rarity.RARE, mage.cards.w.WarriorAngel.class)); + cards.add(new SetCardInfo("Warrior en-Kor", 23, Rarity.UNCOMMON, mage.cards.w.WarriorEnKor.class)); + cards.add(new SetCardInfo("Youthful Knight", 25, Rarity.COMMON, mage.cards.y.YouthfulKnight.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Tempest.java b/Mage.Sets/src/mage/sets/Tempest.java index 5e2ab52b91..67ddcc80a5 100644 --- a/Mage.Sets/src/mage/sets/Tempest.java +++ b/Mage.Sets/src/mage/sets/Tempest.java @@ -1,370 +1,370 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -public final class Tempest extends ExpansionSet { - - private static final Tempest instance = new Tempest(); - - public static Tempest getInstance() { - return instance; - } - - private Tempest() { - super("Tempest", "TMP", ExpansionSet.buildDate(1997, 10, 1), SetType.EXPANSION); - this.blockName = "Tempest"; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Abandon Hope", 107, Rarity.UNCOMMON, mage.cards.a.AbandonHope.class)); - cards.add(new SetCardInfo("Advance Scout", 1, Rarity.COMMON, mage.cards.a.AdvanceScout.class)); - cards.add(new SetCardInfo("Aftershock", 160, Rarity.COMMON, mage.cards.a.Aftershock.class)); - cards.add(new SetCardInfo("Altar of Dementia", 276, Rarity.RARE, mage.cards.a.AltarOfDementia.class)); - cards.add(new SetCardInfo("Aluren", 213, Rarity.RARE, mage.cards.a.Aluren.class)); - cards.add(new SetCardInfo("Ancient Runes", 161, Rarity.UNCOMMON, mage.cards.a.AncientRunes.class)); - cards.add(new SetCardInfo("Ancient Tomb", 315, Rarity.UNCOMMON, mage.cards.a.AncientTomb.class)); - cards.add(new SetCardInfo("Angelic Protector", 2, Rarity.UNCOMMON, mage.cards.a.AngelicProtector.class)); - cards.add(new SetCardInfo("Anoint", 3, Rarity.COMMON, mage.cards.a.Anoint.class)); - cards.add(new SetCardInfo("Apes of Rath", 214, Rarity.UNCOMMON, mage.cards.a.ApesOfRath.class)); - cards.add(new SetCardInfo("Apocalypse", 162, Rarity.RARE, mage.cards.a.Apocalypse.class)); - cards.add(new SetCardInfo("Armor Sliver", 4, Rarity.UNCOMMON, mage.cards.a.ArmorSliver.class)); - cards.add(new SetCardInfo("Armored Pegasus", 5, Rarity.COMMON, mage.cards.a.ArmoredPegasus.class)); - cards.add(new SetCardInfo("Auratog", 6, Rarity.RARE, mage.cards.a.Auratog.class)); - cards.add(new SetCardInfo("Avenging Angel", 7, Rarity.RARE, mage.cards.a.AvengingAngel.class)); - cards.add(new SetCardInfo("Barbed Sliver", 163, Rarity.UNCOMMON, mage.cards.b.BarbedSliver.class)); - cards.add(new SetCardInfo("Bayou Dragonfly", 215, Rarity.COMMON, mage.cards.b.BayouDragonfly.class)); - cards.add(new SetCardInfo("Bellowing Fiend", 108, Rarity.RARE, mage.cards.b.BellowingFiend.class)); - cards.add(new SetCardInfo("Benthic Behemoth", 54, Rarity.RARE, mage.cards.b.BenthicBehemoth.class)); - cards.add(new SetCardInfo("Blood Frenzy", 164, Rarity.COMMON, mage.cards.b.BloodFrenzy.class)); - cards.add(new SetCardInfo("Blood Pet", 109, Rarity.COMMON, mage.cards.b.BloodPet.class)); - cards.add(new SetCardInfo("Boil", 165, Rarity.UNCOMMON, mage.cards.b.Boil.class)); - cards.add(new SetCardInfo("Booby Trap", 277, Rarity.RARE, mage.cards.b.BoobyTrap.class)); - cards.add(new SetCardInfo("Bottle Gnomes", 278, Rarity.UNCOMMON, mage.cards.b.BottleGnomes.class)); - cards.add(new SetCardInfo("Bounty Hunter", 110, Rarity.RARE, mage.cards.b.BountyHunter.class)); - cards.add(new SetCardInfo("Broken Fall", 216, Rarity.COMMON, mage.cards.b.BrokenFall.class)); - cards.add(new SetCardInfo("Caldera Lake", 316, Rarity.RARE, mage.cards.c.CalderaLake.class)); - cards.add(new SetCardInfo("Canopy Spider", 217, Rarity.COMMON, mage.cards.c.CanopySpider.class)); - cards.add(new SetCardInfo("Canyon Drake", 166, Rarity.RARE, mage.cards.c.CanyonDrake.class)); - cards.add(new SetCardInfo("Canyon Wildcat", 167, Rarity.COMMON, mage.cards.c.CanyonWildcat.class)); - cards.add(new SetCardInfo("Capsize", 55, Rarity.COMMON, mage.cards.c.Capsize.class)); - cards.add(new SetCardInfo("Carrionette", 111, Rarity.RARE, mage.cards.c.Carrionette.class)); - cards.add(new SetCardInfo("Chaotic Goo", 168, Rarity.RARE, mage.cards.c.ChaoticGoo.class)); - cards.add(new SetCardInfo("Charging Rhino", 218, Rarity.UNCOMMON, mage.cards.c.ChargingRhino.class)); - cards.add(new SetCardInfo("Chill", 56, Rarity.UNCOMMON, mage.cards.c.Chill.class)); - cards.add(new SetCardInfo("Choke", 219, Rarity.UNCOMMON, mage.cards.c.Choke.class)); - cards.add(new SetCardInfo("Cinder Marsh", 317, Rarity.UNCOMMON, mage.cards.c.CinderMarsh.class)); - cards.add(new SetCardInfo("Circle of Protection: Black", 8, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlack.class)); - cards.add(new SetCardInfo("Circle of Protection: Blue", 9, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlue.class)); - cards.add(new SetCardInfo("Circle of Protection: Green", 10, Rarity.COMMON, mage.cards.c.CircleOfProtectionGreen.class)); - cards.add(new SetCardInfo("Circle of Protection: Red", 11, Rarity.COMMON, mage.cards.c.CircleOfProtectionRed.class)); - cards.add(new SetCardInfo("Circle of Protection: Shadow", 12, Rarity.COMMON, mage.cards.c.CircleOfProtectionShadow.class)); - cards.add(new SetCardInfo("Circle of Protection: White", 13, Rarity.COMMON, mage.cards.c.CircleOfProtectionWhite.class)); - cards.add(new SetCardInfo("Clergy en-Vec", 14, Rarity.COMMON, mage.cards.c.ClergyEnVec.class)); - cards.add(new SetCardInfo("Clot Sliver", 112, Rarity.COMMON, mage.cards.c.ClotSliver.class)); - cards.add(new SetCardInfo("Cloudchaser Eagle", 15, Rarity.COMMON, mage.cards.c.CloudchaserEagle.class)); - cards.add(new SetCardInfo("Coercion", 113, Rarity.COMMON, mage.cards.c.Coercion.class)); - cards.add(new SetCardInfo("Coffin Queen", 114, Rarity.RARE, mage.cards.c.CoffinQueen.class)); - cards.add(new SetCardInfo("Coiled Tinviper", 279, Rarity.COMMON, mage.cards.c.CoiledTinviper.class)); - cards.add(new SetCardInfo("Cold Storage", 280, Rarity.RARE, mage.cards.c.ColdStorage.class)); - cards.add(new SetCardInfo("Commander Greven il-Vec", 115, Rarity.RARE, mage.cards.c.CommanderGrevenIlVec.class)); - cards.add(new SetCardInfo("Corpse Dance", 116, Rarity.RARE, mage.cards.c.CorpseDance.class)); - cards.add(new SetCardInfo("Counterspell", 57, Rarity.COMMON, mage.cards.c.Counterspell.class)); - cards.add(new SetCardInfo("Crazed Armodon", 220, Rarity.RARE, mage.cards.c.CrazedArmodon.class)); - cards.add(new SetCardInfo("Crown of Flames", 169, Rarity.COMMON, mage.cards.c.CrownOfFlames.class)); - cards.add(new SetCardInfo("Cursed Scroll", 281, Rarity.RARE, mage.cards.c.CursedScroll.class)); - cards.add(new SetCardInfo("Dark Banishing", 117, Rarity.COMMON, mage.cards.d.DarkBanishing.class)); - cards.add(new SetCardInfo("Dark Ritual", 118, Rarity.COMMON, mage.cards.d.DarkRitual.class)); - cards.add(new SetCardInfo("Darkling Stalker", 119, Rarity.COMMON, mage.cards.d.DarklingStalker.class)); - cards.add(new SetCardInfo("Dauthi Embrace", 120, Rarity.UNCOMMON, mage.cards.d.DauthiEmbrace.class)); - cards.add(new SetCardInfo("Dauthi Ghoul", 121, Rarity.UNCOMMON, mage.cards.d.DauthiGhoul.class)); - cards.add(new SetCardInfo("Dauthi Horror", 122, Rarity.COMMON, mage.cards.d.DauthiHorror.class)); - cards.add(new SetCardInfo("Dauthi Marauder", 123, Rarity.COMMON, mage.cards.d.DauthiMarauder.class)); - cards.add(new SetCardInfo("Dauthi Mercenary", 124, Rarity.UNCOMMON, mage.cards.d.DauthiMercenary.class)); - cards.add(new SetCardInfo("Dauthi Mindripper", 125, Rarity.UNCOMMON, mage.cards.d.DauthiMindripper.class)); - cards.add(new SetCardInfo("Dauthi Slayer", 126, Rarity.COMMON, mage.cards.d.DauthiSlayer.class)); - cards.add(new SetCardInfo("Deadshot", 170, Rarity.RARE, mage.cards.d.Deadshot.class)); - cards.add(new SetCardInfo("Death Pits of Rath", 127, Rarity.RARE, mage.cards.d.DeathPitsOfRath.class)); - cards.add(new SetCardInfo("Diabolic Edict", 128, Rarity.COMMON, mage.cards.d.DiabolicEdict.class)); - cards.add(new SetCardInfo("Dirtcowl Wurm", 221, Rarity.RARE, mage.cards.d.DirtcowlWurm.class)); - cards.add(new SetCardInfo("Disenchant", 16, Rarity.COMMON, mage.cards.d.Disenchant.class)); - cards.add(new SetCardInfo("Dismiss", 58, Rarity.UNCOMMON, mage.cards.d.Dismiss.class)); - cards.add(new SetCardInfo("Disturbed Burial", 129, Rarity.COMMON, mage.cards.d.DisturbedBurial.class)); - cards.add(new SetCardInfo("Dracoplasm", 266, Rarity.RARE, mage.cards.d.Dracoplasm.class)); - cards.add(new SetCardInfo("Dread of Night", 130, Rarity.UNCOMMON, mage.cards.d.DreadOfNight.class)); - cards.add(new SetCardInfo("Dream Cache", 59, Rarity.COMMON, mage.cards.d.DreamCache.class)); - cards.add(new SetCardInfo("Dregs of Sorrow", 131, Rarity.RARE, mage.cards.d.DregsOfSorrow.class)); - cards.add(new SetCardInfo("Duplicity", 60, Rarity.RARE, mage.cards.d.Duplicity.class)); - cards.add(new SetCardInfo("Earthcraft", 222, Rarity.RARE, mage.cards.e.Earthcraft.class)); - cards.add(new SetCardInfo("Echo Chamber", 282, Rarity.RARE, mage.cards.e.EchoChamber.class)); - cards.add(new SetCardInfo("Eladamri's Vineyard", 223, Rarity.RARE, mage.cards.e.EladamrisVineyard.class)); - cards.add(new SetCardInfo("Eladamri, Lord of Leaves", 224, Rarity.RARE, mage.cards.e.EladamriLordOfLeaves.class)); - cards.add(new SetCardInfo("Elite Javelineer", 17, Rarity.COMMON, mage.cards.e.EliteJavelineer.class)); - cards.add(new SetCardInfo("Elven Warhounds", 225, Rarity.RARE, mage.cards.e.ElvenWarhounds.class)); - cards.add(new SetCardInfo("Elvish Fury", 226, Rarity.COMMON, mage.cards.e.ElvishFury.class)); - cards.add(new SetCardInfo("Emerald Medallion", 283, Rarity.RARE, mage.cards.e.EmeraldMedallion.class)); - cards.add(new SetCardInfo("Emmessi Tome", 284, Rarity.RARE, mage.cards.e.EmmessiTome.class)); - cards.add(new SetCardInfo("Endless Scream", 132, Rarity.COMMON, mage.cards.e.EndlessScream.class)); - cards.add(new SetCardInfo("Energizer", 285, Rarity.RARE, mage.cards.e.Energizer.class)); - cards.add(new SetCardInfo("Enfeeblement", 133, Rarity.COMMON, mage.cards.e.Enfeeblement.class)); - cards.add(new SetCardInfo("Enraging Licid", 171, Rarity.UNCOMMON, mage.cards.e.EnragingLicid.class)); +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +public final class Tempest extends ExpansionSet { + + private static final Tempest instance = new Tempest(); + + public static Tempest getInstance() { + return instance; + } + + private Tempest() { + super("Tempest", "TMP", ExpansionSet.buildDate(1997, 10, 1), SetType.EXPANSION); + this.blockName = "Tempest"; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Abandon Hope", 107, Rarity.UNCOMMON, mage.cards.a.AbandonHope.class)); + cards.add(new SetCardInfo("Advance Scout", 1, Rarity.COMMON, mage.cards.a.AdvanceScout.class)); + cards.add(new SetCardInfo("Aftershock", 160, Rarity.COMMON, mage.cards.a.Aftershock.class)); + cards.add(new SetCardInfo("Altar of Dementia", 276, Rarity.RARE, mage.cards.a.AltarOfDementia.class)); + cards.add(new SetCardInfo("Aluren", 213, Rarity.RARE, mage.cards.a.Aluren.class)); + cards.add(new SetCardInfo("Ancient Runes", 161, Rarity.UNCOMMON, mage.cards.a.AncientRunes.class)); + cards.add(new SetCardInfo("Ancient Tomb", 315, Rarity.UNCOMMON, mage.cards.a.AncientTomb.class)); + cards.add(new SetCardInfo("Angelic Protector", 2, Rarity.UNCOMMON, mage.cards.a.AngelicProtector.class)); + cards.add(new SetCardInfo("Anoint", 3, Rarity.COMMON, mage.cards.a.Anoint.class)); + cards.add(new SetCardInfo("Apes of Rath", 214, Rarity.UNCOMMON, mage.cards.a.ApesOfRath.class)); + cards.add(new SetCardInfo("Apocalypse", 162, Rarity.RARE, mage.cards.a.Apocalypse.class)); + cards.add(new SetCardInfo("Armor Sliver", 4, Rarity.UNCOMMON, mage.cards.a.ArmorSliver.class)); + cards.add(new SetCardInfo("Armored Pegasus", 5, Rarity.COMMON, mage.cards.a.ArmoredPegasus.class)); + cards.add(new SetCardInfo("Auratog", 6, Rarity.RARE, mage.cards.a.Auratog.class)); + cards.add(new SetCardInfo("Avenging Angel", 7, Rarity.RARE, mage.cards.a.AvengingAngel.class)); + cards.add(new SetCardInfo("Barbed Sliver", 163, Rarity.UNCOMMON, mage.cards.b.BarbedSliver.class)); + cards.add(new SetCardInfo("Bayou Dragonfly", 215, Rarity.COMMON, mage.cards.b.BayouDragonfly.class)); + cards.add(new SetCardInfo("Bellowing Fiend", 108, Rarity.RARE, mage.cards.b.BellowingFiend.class)); + cards.add(new SetCardInfo("Benthic Behemoth", 54, Rarity.RARE, mage.cards.b.BenthicBehemoth.class)); + cards.add(new SetCardInfo("Blood Frenzy", 164, Rarity.COMMON, mage.cards.b.BloodFrenzy.class)); + cards.add(new SetCardInfo("Blood Pet", 109, Rarity.COMMON, mage.cards.b.BloodPet.class)); + cards.add(new SetCardInfo("Boil", 165, Rarity.UNCOMMON, mage.cards.b.Boil.class)); + cards.add(new SetCardInfo("Booby Trap", 277, Rarity.RARE, mage.cards.b.BoobyTrap.class)); + cards.add(new SetCardInfo("Bottle Gnomes", 278, Rarity.UNCOMMON, mage.cards.b.BottleGnomes.class)); + cards.add(new SetCardInfo("Bounty Hunter", 110, Rarity.RARE, mage.cards.b.BountyHunter.class)); + cards.add(new SetCardInfo("Broken Fall", 216, Rarity.COMMON, mage.cards.b.BrokenFall.class)); + cards.add(new SetCardInfo("Caldera Lake", 316, Rarity.RARE, mage.cards.c.CalderaLake.class)); + cards.add(new SetCardInfo("Canopy Spider", 217, Rarity.COMMON, mage.cards.c.CanopySpider.class)); + cards.add(new SetCardInfo("Canyon Drake", 166, Rarity.RARE, mage.cards.c.CanyonDrake.class)); + cards.add(new SetCardInfo("Canyon Wildcat", 167, Rarity.COMMON, mage.cards.c.CanyonWildcat.class)); + cards.add(new SetCardInfo("Capsize", 55, Rarity.COMMON, mage.cards.c.Capsize.class)); + cards.add(new SetCardInfo("Carrionette", 111, Rarity.RARE, mage.cards.c.Carrionette.class)); + cards.add(new SetCardInfo("Chaotic Goo", 168, Rarity.RARE, mage.cards.c.ChaoticGoo.class)); + cards.add(new SetCardInfo("Charging Rhino", 218, Rarity.UNCOMMON, mage.cards.c.ChargingRhino.class)); + cards.add(new SetCardInfo("Chill", 56, Rarity.UNCOMMON, mage.cards.c.Chill.class)); + cards.add(new SetCardInfo("Choke", 219, Rarity.UNCOMMON, mage.cards.c.Choke.class)); + cards.add(new SetCardInfo("Cinder Marsh", 317, Rarity.UNCOMMON, mage.cards.c.CinderMarsh.class)); + cards.add(new SetCardInfo("Circle of Protection: Black", 8, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlack.class)); + cards.add(new SetCardInfo("Circle of Protection: Blue", 9, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlue.class)); + cards.add(new SetCardInfo("Circle of Protection: Green", 10, Rarity.COMMON, mage.cards.c.CircleOfProtectionGreen.class)); + cards.add(new SetCardInfo("Circle of Protection: Red", 11, Rarity.COMMON, mage.cards.c.CircleOfProtectionRed.class)); + cards.add(new SetCardInfo("Circle of Protection: Shadow", 12, Rarity.COMMON, mage.cards.c.CircleOfProtectionShadow.class)); + cards.add(new SetCardInfo("Circle of Protection: White", 13, Rarity.COMMON, mage.cards.c.CircleOfProtectionWhite.class)); + cards.add(new SetCardInfo("Clergy en-Vec", 14, Rarity.COMMON, mage.cards.c.ClergyEnVec.class)); + cards.add(new SetCardInfo("Clot Sliver", 112, Rarity.COMMON, mage.cards.c.ClotSliver.class)); + cards.add(new SetCardInfo("Cloudchaser Eagle", 15, Rarity.COMMON, mage.cards.c.CloudchaserEagle.class)); + cards.add(new SetCardInfo("Coercion", 113, Rarity.COMMON, mage.cards.c.Coercion.class)); + cards.add(new SetCardInfo("Coffin Queen", 114, Rarity.RARE, mage.cards.c.CoffinQueen.class)); + cards.add(new SetCardInfo("Coiled Tinviper", 279, Rarity.COMMON, mage.cards.c.CoiledTinviper.class)); + cards.add(new SetCardInfo("Cold Storage", 280, Rarity.RARE, mage.cards.c.ColdStorage.class)); + cards.add(new SetCardInfo("Commander Greven il-Vec", 115, Rarity.RARE, mage.cards.c.CommanderGrevenIlVec.class)); + cards.add(new SetCardInfo("Corpse Dance", 116, Rarity.RARE, mage.cards.c.CorpseDance.class)); + cards.add(new SetCardInfo("Counterspell", 57, Rarity.COMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Crazed Armodon", 220, Rarity.RARE, mage.cards.c.CrazedArmodon.class)); + cards.add(new SetCardInfo("Crown of Flames", 169, Rarity.COMMON, mage.cards.c.CrownOfFlames.class)); + cards.add(new SetCardInfo("Cursed Scroll", 281, Rarity.RARE, mage.cards.c.CursedScroll.class)); + cards.add(new SetCardInfo("Dark Banishing", 117, Rarity.COMMON, mage.cards.d.DarkBanishing.class)); + cards.add(new SetCardInfo("Dark Ritual", 118, Rarity.COMMON, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Darkling Stalker", 119, Rarity.COMMON, mage.cards.d.DarklingStalker.class)); + cards.add(new SetCardInfo("Dauthi Embrace", 120, Rarity.UNCOMMON, mage.cards.d.DauthiEmbrace.class)); + cards.add(new SetCardInfo("Dauthi Ghoul", 121, Rarity.UNCOMMON, mage.cards.d.DauthiGhoul.class)); + cards.add(new SetCardInfo("Dauthi Horror", 122, Rarity.COMMON, mage.cards.d.DauthiHorror.class)); + cards.add(new SetCardInfo("Dauthi Marauder", 123, Rarity.COMMON, mage.cards.d.DauthiMarauder.class)); + cards.add(new SetCardInfo("Dauthi Mercenary", 124, Rarity.UNCOMMON, mage.cards.d.DauthiMercenary.class)); + cards.add(new SetCardInfo("Dauthi Mindripper", 125, Rarity.UNCOMMON, mage.cards.d.DauthiMindripper.class)); + cards.add(new SetCardInfo("Dauthi Slayer", 126, Rarity.COMMON, mage.cards.d.DauthiSlayer.class)); + cards.add(new SetCardInfo("Deadshot", 170, Rarity.RARE, mage.cards.d.Deadshot.class)); + cards.add(new SetCardInfo("Death Pits of Rath", 127, Rarity.RARE, mage.cards.d.DeathPitsOfRath.class)); + cards.add(new SetCardInfo("Diabolic Edict", 128, Rarity.COMMON, mage.cards.d.DiabolicEdict.class)); + cards.add(new SetCardInfo("Dirtcowl Wurm", 221, Rarity.RARE, mage.cards.d.DirtcowlWurm.class)); + cards.add(new SetCardInfo("Disenchant", 16, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Dismiss", 58, Rarity.UNCOMMON, mage.cards.d.Dismiss.class)); + cards.add(new SetCardInfo("Disturbed Burial", 129, Rarity.COMMON, mage.cards.d.DisturbedBurial.class)); + cards.add(new SetCardInfo("Dracoplasm", 266, Rarity.RARE, mage.cards.d.Dracoplasm.class)); + cards.add(new SetCardInfo("Dread of Night", 130, Rarity.UNCOMMON, mage.cards.d.DreadOfNight.class)); + cards.add(new SetCardInfo("Dream Cache", 59, Rarity.COMMON, mage.cards.d.DreamCache.class)); + cards.add(new SetCardInfo("Dregs of Sorrow", 131, Rarity.RARE, mage.cards.d.DregsOfSorrow.class)); + cards.add(new SetCardInfo("Duplicity", 60, Rarity.RARE, mage.cards.d.Duplicity.class)); + cards.add(new SetCardInfo("Earthcraft", 222, Rarity.RARE, mage.cards.e.Earthcraft.class)); + cards.add(new SetCardInfo("Echo Chamber", 282, Rarity.RARE, mage.cards.e.EchoChamber.class)); + cards.add(new SetCardInfo("Eladamri's Vineyard", 223, Rarity.RARE, mage.cards.e.EladamrisVineyard.class)); + cards.add(new SetCardInfo("Eladamri, Lord of Leaves", 224, Rarity.RARE, mage.cards.e.EladamriLordOfLeaves.class)); + cards.add(new SetCardInfo("Elite Javelineer", 17, Rarity.COMMON, mage.cards.e.EliteJavelineer.class)); + cards.add(new SetCardInfo("Elven Warhounds", 225, Rarity.RARE, mage.cards.e.ElvenWarhounds.class)); + cards.add(new SetCardInfo("Elvish Fury", 226, Rarity.COMMON, mage.cards.e.ElvishFury.class)); + cards.add(new SetCardInfo("Emerald Medallion", 283, Rarity.RARE, mage.cards.e.EmeraldMedallion.class)); + cards.add(new SetCardInfo("Emmessi Tome", 284, Rarity.RARE, mage.cards.e.EmmessiTome.class)); + cards.add(new SetCardInfo("Endless Scream", 132, Rarity.COMMON, mage.cards.e.EndlessScream.class)); + cards.add(new SetCardInfo("Energizer", 285, Rarity.RARE, mage.cards.e.Energizer.class)); + cards.add(new SetCardInfo("Enfeeblement", 133, Rarity.COMMON, mage.cards.e.Enfeeblement.class)); + cards.add(new SetCardInfo("Enraging Licid", 171, Rarity.UNCOMMON, mage.cards.e.EnragingLicid.class)); cards.add(new SetCardInfo("Escaped Shapeshifter", 62, Rarity.RARE, mage.cards.e.EscapedShapeshifter.class)); - cards.add(new SetCardInfo("Essence Bottle", 286, Rarity.UNCOMMON, mage.cards.e.EssenceBottle.class)); - cards.add(new SetCardInfo("Evincar's Justice", 134, Rarity.COMMON, mage.cards.e.EvincarsJustice.class)); - cards.add(new SetCardInfo("Excavator", 287, Rarity.UNCOMMON, mage.cards.e.Excavator.class)); - cards.add(new SetCardInfo("Extinction", 135, Rarity.RARE, mage.cards.e.Extinction.class)); - cards.add(new SetCardInfo("Fevered Convulsions", 136, Rarity.RARE, mage.cards.f.FeveredConvulsions.class)); - cards.add(new SetCardInfo("Field of Souls", 18, Rarity.RARE, mage.cards.f.FieldOfSouls.class)); - cards.add(new SetCardInfo("Fighting Drake", 63, Rarity.UNCOMMON, mage.cards.f.FightingDrake.class)); - cards.add(new SetCardInfo("Firefly", 172, Rarity.UNCOMMON, mage.cards.f.Firefly.class)); - cards.add(new SetCardInfo("Fireslinger", 173, Rarity.COMMON, mage.cards.f.Fireslinger.class)); - cards.add(new SetCardInfo("Flailing Drake", 227, Rarity.UNCOMMON, mage.cards.f.FlailingDrake.class)); - cards.add(new SetCardInfo("Flickering Ward", 19, Rarity.UNCOMMON, mage.cards.f.FlickeringWard.class)); - cards.add(new SetCardInfo("Flowstone Giant", 174, Rarity.COMMON, mage.cards.f.FlowstoneGiant.class)); - cards.add(new SetCardInfo("Flowstone Salamander", 175, Rarity.UNCOMMON, mage.cards.f.FlowstoneSalamander.class)); - cards.add(new SetCardInfo("Flowstone Sculpture", 288, Rarity.RARE, mage.cards.f.FlowstoneSculpture.class)); - cards.add(new SetCardInfo("Flowstone Wyvern", 176, Rarity.RARE, mage.cards.f.FlowstoneWyvern.class)); - cards.add(new SetCardInfo("Fool's Tome", 289, Rarity.RARE, mage.cards.f.FoolsTome.class)); - cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Frog Tongue", 228, Rarity.COMMON, mage.cards.f.FrogTongue.class)); - cards.add(new SetCardInfo("Fugitive Druid", 229, Rarity.RARE, mage.cards.f.FugitiveDruid.class)); - cards.add(new SetCardInfo("Furnace of Rath", 177, Rarity.RARE, mage.cards.f.FurnaceOfRath.class)); - cards.add(new SetCardInfo("Fylamarid", 64, Rarity.UNCOMMON, mage.cards.f.Fylamarid.class)); - cards.add(new SetCardInfo("Gallantry", 20, Rarity.UNCOMMON, mage.cards.g.Gallantry.class)); - cards.add(new SetCardInfo("Gaseous Form", 65, Rarity.COMMON, mage.cards.g.GaseousForm.class)); - cards.add(new SetCardInfo("Gerrard's Battle Cry", 21, Rarity.RARE, mage.cards.g.GerrardsBattleCry.class)); - cards.add(new SetCardInfo("Ghost Town", 318, Rarity.UNCOMMON, mage.cards.g.GhostTown.class)); - cards.add(new SetCardInfo("Giant Crab", 66, Rarity.COMMON, mage.cards.g.GiantCrab.class)); - cards.add(new SetCardInfo("Giant Strength", 178, Rarity.COMMON, mage.cards.g.GiantStrength.class)); - cards.add(new SetCardInfo("Goblin Bombardment", 179, Rarity.UNCOMMON, mage.cards.g.GoblinBombardment.class)); - cards.add(new SetCardInfo("Gravedigger", 137, Rarity.COMMON, mage.cards.g.Gravedigger.class)); - cards.add(new SetCardInfo("Grindstone", 290, Rarity.RARE, mage.cards.g.Grindstone.class)); - cards.add(new SetCardInfo("Hand to Hand", 180, Rarity.RARE, mage.cards.h.HandToHand.class)); - cards.add(new SetCardInfo("Hanna's Custody", 22, Rarity.RARE, mage.cards.h.HannasCustody.class)); - cards.add(new SetCardInfo("Harrow", 230, Rarity.UNCOMMON, mage.cards.h.Harrow.class)); - cards.add(new SetCardInfo("Havoc", 181, Rarity.UNCOMMON, mage.cards.h.Havoc.class)); - cards.add(new SetCardInfo("Heart Sliver", 182, Rarity.COMMON, mage.cards.h.HeartSliver.class)); - cards.add(new SetCardInfo("Heartwood Dryad", 231, Rarity.COMMON, mage.cards.h.HeartwoodDryad.class)); - cards.add(new SetCardInfo("Heartwood Giant", 232, Rarity.RARE, mage.cards.h.HeartwoodGiant.class)); - cards.add(new SetCardInfo("Heartwood Treefolk", 233, Rarity.UNCOMMON, mage.cards.h.HeartwoodTreefolk.class)); - cards.add(new SetCardInfo("Helm of Possession", 291, Rarity.RARE, mage.cards.h.HelmOfPossession.class)); - cards.add(new SetCardInfo("Hero's Resolve", 23, Rarity.COMMON, mage.cards.h.HerosResolve.class)); - cards.add(new SetCardInfo("Horned Sliver", 234, Rarity.UNCOMMON, mage.cards.h.HornedSliver.class)); - cards.add(new SetCardInfo("Horned Turtle", 67, Rarity.COMMON, mage.cards.h.HornedTurtle.class)); - cards.add(new SetCardInfo("Humility", 24, Rarity.RARE, mage.cards.h.Humility.class)); - cards.add(new SetCardInfo("Imps' Taunt", 138, Rarity.UNCOMMON, mage.cards.i.ImpsTaunt.class)); - cards.add(new SetCardInfo("Insight", 68, Rarity.UNCOMMON, mage.cards.i.Insight.class)); - cards.add(new SetCardInfo("Interdict", 69, Rarity.UNCOMMON, mage.cards.i.Interdict.class)); - cards.add(new SetCardInfo("Intuition", 70, Rarity.RARE, mage.cards.i.Intuition.class)); - cards.add(new SetCardInfo("Invulnerability", 25, Rarity.UNCOMMON, mage.cards.i.Invulnerability.class)); - cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Jackal Pup", 183, Rarity.UNCOMMON, mage.cards.j.JackalPup.class)); - cards.add(new SetCardInfo("Jet Medallion", 292, Rarity.RARE, mage.cards.j.JetMedallion.class)); - cards.add(new SetCardInfo("Jinxed Idol", 293, Rarity.RARE, mage.cards.j.JinxedIdol.class)); - cards.add(new SetCardInfo("Kezzerdrix", 139, Rarity.RARE, mage.cards.k.Kezzerdrix.class)); - cards.add(new SetCardInfo("Kindle", 184, Rarity.COMMON, mage.cards.k.Kindle.class)); - cards.add(new SetCardInfo("Knight of Dawn", 26, Rarity.UNCOMMON, mage.cards.k.KnightOfDawn.class)); - cards.add(new SetCardInfo("Knight of Dusk", 140, Rarity.UNCOMMON, mage.cards.k.KnightOfDusk.class)); - cards.add(new SetCardInfo("Krakilin", 235, Rarity.UNCOMMON, mage.cards.k.Krakilin.class)); - cards.add(new SetCardInfo("Leeching Licid", 141, Rarity.UNCOMMON, mage.cards.l.LeechingLicid.class)); - cards.add(new SetCardInfo("Legacy's Allure", 71, Rarity.UNCOMMON, mage.cards.l.LegacysAllure.class)); - cards.add(new SetCardInfo("Legerdemain", 72, Rarity.UNCOMMON, mage.cards.l.Legerdemain.class)); - cards.add(new SetCardInfo("Light of Day", 27, Rarity.UNCOMMON, mage.cards.l.LightOfDay.class)); - cards.add(new SetCardInfo("Lightning Blast", 185, Rarity.COMMON, mage.cards.l.LightningBlast.class)); - cards.add(new SetCardInfo("Lightning Elemental", 186, Rarity.COMMON, mage.cards.l.LightningElemental.class)); - cards.add(new SetCardInfo("Living Death", 142, Rarity.RARE, mage.cards.l.LivingDeath.class)); - cards.add(new SetCardInfo("Lobotomy", 267, Rarity.UNCOMMON, mage.cards.l.Lobotomy.class)); - cards.add(new SetCardInfo("Lotus Petal", 294, Rarity.COMMON, mage.cards.l.LotusPetal.class)); - cards.add(new SetCardInfo("Lowland Giant", 187, Rarity.COMMON, mage.cards.l.LowlandGiant.class)); - cards.add(new SetCardInfo("Maddening Imp", 143, Rarity.RARE, mage.cards.m.MaddeningImp.class)); - cards.add(new SetCardInfo("Magmasaur", 188, Rarity.RARE, mage.cards.m.Magmasaur.class)); - cards.add(new SetCardInfo("Mana Severance", 73, Rarity.RARE, mage.cards.m.ManaSeverance.class)); - cards.add(new SetCardInfo("Manakin", 296, Rarity.COMMON, mage.cards.m.Manakin.class)); - cards.add(new SetCardInfo("Manta Riders", 74, Rarity.COMMON, mage.cards.m.MantaRiders.class)); - cards.add(new SetCardInfo("Marble Titan", 28, Rarity.RARE, mage.cards.m.MarbleTitan.class)); - cards.add(new SetCardInfo("Marsh Lurker", 144, Rarity.COMMON, mage.cards.m.MarshLurker.class)); - cards.add(new SetCardInfo("Master Decoy", 29, Rarity.COMMON, mage.cards.m.MasterDecoy.class)); - cards.add(new SetCardInfo("Mawcor", 75, Rarity.RARE, mage.cards.m.Mawcor.class)); - cards.add(new SetCardInfo("Maze of Shadows", 319, Rarity.UNCOMMON, mage.cards.m.MazeOfShadows.class)); - cards.add(new SetCardInfo("Meditate", 76, Rarity.RARE, mage.cards.m.Meditate.class)); - cards.add(new SetCardInfo("Metallic Sliver", 297, Rarity.COMMON, mage.cards.m.MetallicSliver.class)); - cards.add(new SetCardInfo("Mindwhip Sliver", 145, Rarity.UNCOMMON, mage.cards.m.MindwhipSliver.class)); - cards.add(new SetCardInfo("Minion of the Wastes", 146, Rarity.RARE, mage.cards.m.MinionOfTheWastes.class)); - cards.add(new SetCardInfo("Mirri's Guile", 236, Rarity.RARE, mage.cards.m.MirrisGuile.class)); - cards.add(new SetCardInfo("Mnemonic Sliver", 77, Rarity.UNCOMMON, mage.cards.m.MnemonicSliver.class)); - cards.add(new SetCardInfo("Mogg Cannon", 298, Rarity.UNCOMMON, mage.cards.m.MoggCannon.class)); - cards.add(new SetCardInfo("Mogg Conscripts", 189, Rarity.COMMON, mage.cards.m.MoggConscripts.class)); - cards.add(new SetCardInfo("Mogg Fanatic", 190, Rarity.COMMON, mage.cards.m.MoggFanatic.class)); - cards.add(new SetCardInfo("Mogg Hollows", 320, Rarity.UNCOMMON, mage.cards.m.MoggHollows.class)); - cards.add(new SetCardInfo("Mogg Raider", 191, Rarity.COMMON, mage.cards.m.MoggRaider.class)); - cards.add(new SetCardInfo("Mogg Squad", 192, Rarity.UNCOMMON, mage.cards.m.MoggSquad.class)); - cards.add(new SetCardInfo("Mongrel Pack", 237, Rarity.RARE, mage.cards.m.MongrelPack.class)); - cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mounted Archers", 30, Rarity.COMMON, mage.cards.m.MountedArchers.class)); - cards.add(new SetCardInfo("Muscle Sliver", 238, Rarity.COMMON, mage.cards.m.MuscleSliver.class)); - cards.add(new SetCardInfo("Natural Spring", 239, Rarity.COMMON, mage.cards.n.NaturalSpring.class)); - cards.add(new SetCardInfo("Nature's Revolt", 240, Rarity.RARE, mage.cards.n.NaturesRevolt.class)); - cards.add(new SetCardInfo("Needle Storm", 241, Rarity.UNCOMMON, mage.cards.n.NeedleStorm.class)); - cards.add(new SetCardInfo("Nurturing Licid", 242, Rarity.UNCOMMON, mage.cards.n.NurturingLicid.class)); - cards.add(new SetCardInfo("Opportunist", 194, Rarity.UNCOMMON, mage.cards.o.Opportunist.class)); - cards.add(new SetCardInfo("Oracle en-Vec", 31, Rarity.RARE, mage.cards.o.OracleEnVec.class)); - cards.add(new SetCardInfo("Orim's Prayer", 32, Rarity.UNCOMMON, mage.cards.o.OrimsPrayer.class)); - cards.add(new SetCardInfo("Orim, Samite Healer", 33, Rarity.RARE, mage.cards.o.OrimSamiteHealer.class)); - cards.add(new SetCardInfo("Overrun", 243, Rarity.UNCOMMON, mage.cards.o.Overrun.class)); - cards.add(new SetCardInfo("Pacifism", 34, Rarity.COMMON, mage.cards.p.Pacifism.class)); - cards.add(new SetCardInfo("Pallimud", 195, Rarity.RARE, mage.cards.p.Pallimud.class)); - cards.add(new SetCardInfo("Patchwork Gnomes", 299, Rarity.UNCOMMON, mage.cards.p.PatchworkGnomes.class)); - cards.add(new SetCardInfo("Pearl Medallion", 300, Rarity.RARE, mage.cards.p.PearlMedallion.class)); - cards.add(new SetCardInfo("Pegasus Refuge", 35, Rarity.RARE, mage.cards.p.PegasusRefuge.class)); - cards.add(new SetCardInfo("Perish", 147, Rarity.UNCOMMON, mage.cards.p.Perish.class)); - cards.add(new SetCardInfo("Phyrexian Grimoire", 301, Rarity.RARE, mage.cards.p.PhyrexianGrimoire.class)); - cards.add(new SetCardInfo("Phyrexian Hulk", 302, Rarity.UNCOMMON, mage.cards.p.PhyrexianHulk.class)); - cards.add(new SetCardInfo("Pincher Beetles", 244, Rarity.COMMON, mage.cards.p.PincherBeetles.class)); - cards.add(new SetCardInfo("Pine Barrens", 321, Rarity.RARE, mage.cards.p.PineBarrens.class)); - cards.add(new SetCardInfo("Pit Imp", 148, Rarity.COMMON, mage.cards.p.PitImp.class)); - cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Power Sink", 78, Rarity.COMMON, mage.cards.p.PowerSink.class)); - cards.add(new SetCardInfo("Precognition", 79, Rarity.RARE, mage.cards.p.Precognition.class)); - cards.add(new SetCardInfo("Propaganda", 80, Rarity.UNCOMMON, mage.cards.p.Propaganda.class)); - cards.add(new SetCardInfo("Puppet Strings", 304, Rarity.UNCOMMON, mage.cards.p.PuppetStrings.class)); - cards.add(new SetCardInfo("Quickening Licid", 36, Rarity.UNCOMMON, mage.cards.q.QuickeningLicid.class)); - cards.add(new SetCardInfo("Rain of Tears", 149, Rarity.UNCOMMON, mage.cards.r.RainOfTears.class)); - cards.add(new SetCardInfo("Rampant Growth", 245, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); - cards.add(new SetCardInfo("Ranger en-Vec", 268, Rarity.UNCOMMON, mage.cards.r.RangerEnVec.class)); - cards.add(new SetCardInfo("Rathi Dragon", 196, Rarity.RARE, mage.cards.r.RathiDragon.class)); - cards.add(new SetCardInfo("Rats of Rath", 150, Rarity.COMMON, mage.cards.r.RatsOfRath.class)); - cards.add(new SetCardInfo("Reality Anchor", 246, Rarity.COMMON, mage.cards.r.RealityAnchor.class)); - cards.add(new SetCardInfo("Reanimate", 151, Rarity.UNCOMMON, mage.cards.r.Reanimate.class)); - cards.add(new SetCardInfo("Reap", 247, Rarity.UNCOMMON, mage.cards.r.Reap.class)); - cards.add(new SetCardInfo("Reckless Spite", 152, Rarity.UNCOMMON, mage.cards.r.RecklessSpite.class)); - cards.add(new SetCardInfo("Recycle", 248, Rarity.RARE, mage.cards.r.Recycle.class)); - cards.add(new SetCardInfo("Reflecting Pool", 322, Rarity.RARE, mage.cards.r.ReflectingPool.class)); - cards.add(new SetCardInfo("Renegade Warlord", 197, Rarity.UNCOMMON, mage.cards.r.RenegadeWarlord.class)); - cards.add(new SetCardInfo("Repentance", 37, Rarity.UNCOMMON, mage.cards.r.Repentance.class)); - cards.add(new SetCardInfo("Respite", 249, Rarity.COMMON, mage.cards.r.Respite.class)); - cards.add(new SetCardInfo("Rolling Thunder", 198, Rarity.COMMON, mage.cards.r.RollingThunder.class)); - cards.add(new SetCardInfo("Root Maze", 250, Rarity.RARE, mage.cards.r.RootMaze.class)); - cards.add(new SetCardInfo("Rootbreaker Wurm", 251, Rarity.COMMON, mage.cards.r.RootbreakerWurm.class)); - cards.add(new SetCardInfo("Rootwalla", 252, Rarity.COMMON, mage.cards.r.Rootwalla.class)); - cards.add(new SetCardInfo("Rootwater Depths", 323, Rarity.UNCOMMON, mage.cards.r.RootwaterDepths.class)); - cards.add(new SetCardInfo("Rootwater Diver", 81, Rarity.UNCOMMON, mage.cards.r.RootwaterDiver.class)); - cards.add(new SetCardInfo("Rootwater Hunter", 82, Rarity.COMMON, mage.cards.r.RootwaterHunter.class)); - cards.add(new SetCardInfo("Rootwater Matriarch", 83, Rarity.RARE, mage.cards.r.RootwaterMatriarch.class)); - cards.add(new SetCardInfo("Rootwater Shaman", 84, Rarity.RARE, mage.cards.r.RootwaterShaman.class)); - cards.add(new SetCardInfo("Ruby Medallion", 305, Rarity.RARE, mage.cards.r.RubyMedallion.class)); - cards.add(new SetCardInfo("Sacred Guide", 38, Rarity.RARE, mage.cards.s.SacredGuide.class)); - cards.add(new SetCardInfo("Sadistic Glee", 153, Rarity.COMMON, mage.cards.s.SadisticGlee.class)); - cards.add(new SetCardInfo("Safeguard", 39, Rarity.RARE, mage.cards.s.Safeguard.class)); - cards.add(new SetCardInfo("Salt Flats", 324, Rarity.RARE, mage.cards.s.SaltFlats.class)); - cards.add(new SetCardInfo("Sandstone Warrior", 199, Rarity.COMMON, mage.cards.s.SandstoneWarrior.class)); - cards.add(new SetCardInfo("Sapphire Medallion", 306, Rarity.RARE, mage.cards.s.SapphireMedallion.class)); - cards.add(new SetCardInfo("Sarcomancy", 154, Rarity.RARE, mage.cards.s.Sarcomancy.class)); - cards.add(new SetCardInfo("Scabland", 325, Rarity.RARE, mage.cards.s.Scabland.class)); - cards.add(new SetCardInfo("Scalding Tongs", 307, Rarity.RARE, mage.cards.s.ScaldingTongs.class)); - cards.add(new SetCardInfo("Scorched Earth", 200, Rarity.RARE, mage.cards.s.ScorchedEarth.class)); - cards.add(new SetCardInfo("Scragnoth", 253, Rarity.UNCOMMON, mage.cards.s.Scragnoth.class)); - cards.add(new SetCardInfo("Screeching Harpy", 155, Rarity.UNCOMMON, mage.cards.s.ScreechingHarpy.class)); - cards.add(new SetCardInfo("Scroll Rack", 308, Rarity.RARE, mage.cards.s.ScrollRack.class)); - cards.add(new SetCardInfo("Sea Monster", 85, Rarity.COMMON, mage.cards.s.SeaMonster.class)); - cards.add(new SetCardInfo("Searing Touch", 201, Rarity.UNCOMMON, mage.cards.s.SearingTouch.class)); - cards.add(new SetCardInfo("Seeker of Skybreak", 254, Rarity.COMMON, mage.cards.s.SeekerOfSkybreak.class)); - cards.add(new SetCardInfo("Segmented Wurm", 269, Rarity.UNCOMMON, mage.cards.s.SegmentedWurm.class)); - cards.add(new SetCardInfo("Selenia, Dark Angel", 270, Rarity.RARE, mage.cards.s.SeleniaDarkAngel.class)); - cards.add(new SetCardInfo("Serene Offering", 40, Rarity.UNCOMMON, mage.cards.s.SereneOffering.class)); - cards.add(new SetCardInfo("Servant of Volrath", 156, Rarity.COMMON, mage.cards.s.ServantOfVolrath.class)); - cards.add(new SetCardInfo("Shadow Rift", 86, Rarity.COMMON, mage.cards.s.ShadowRift.class)); - cards.add(new SetCardInfo("Shadowstorm", 202, Rarity.UNCOMMON, mage.cards.s.Shadowstorm.class)); - cards.add(new SetCardInfo("Shatter", 203, Rarity.COMMON, mage.cards.s.Shatter.class)); - cards.add(new SetCardInfo("Shimmering Wings", 87, Rarity.COMMON, mage.cards.s.ShimmeringWings.class)); - cards.add(new SetCardInfo("Shocker", 204, Rarity.RARE, mage.cards.s.Shocker.class)); - cards.add(new SetCardInfo("Sky Spirit", 271, Rarity.UNCOMMON, mage.cards.s.SkySpirit.class)); - cards.add(new SetCardInfo("Skyshroud Condor", 88, Rarity.UNCOMMON, mage.cards.s.SkyshroudCondor.class)); - cards.add(new SetCardInfo("Skyshroud Elf", 255, Rarity.COMMON, mage.cards.s.SkyshroudElf.class)); - cards.add(new SetCardInfo("Skyshroud Forest", 326, Rarity.RARE, mage.cards.s.SkyshroudForest.class)); - cards.add(new SetCardInfo("Skyshroud Ranger", 256, Rarity.COMMON, mage.cards.s.SkyshroudRanger.class)); - cards.add(new SetCardInfo("Skyshroud Troll", 257, Rarity.COMMON, mage.cards.s.SkyshroudTroll.class)); - cards.add(new SetCardInfo("Skyshroud Vampire", 157, Rarity.UNCOMMON, mage.cards.s.SkyshroudVampire.class)); - cards.add(new SetCardInfo("Soltari Crusader", 41, Rarity.UNCOMMON, mage.cards.s.SoltariCrusader.class)); - cards.add(new SetCardInfo("Soltari Emissary", 42, Rarity.RARE, mage.cards.s.SoltariEmissary.class)); - cards.add(new SetCardInfo("Soltari Foot Soldier", 43, Rarity.COMMON, mage.cards.s.SoltariFootSoldier.class)); - cards.add(new SetCardInfo("Soltari Guerrillas", 272, Rarity.RARE, mage.cards.s.SoltariGuerrillas.class)); - cards.add(new SetCardInfo("Soltari Lancer", 44, Rarity.COMMON, mage.cards.s.SoltariLancer.class)); - cards.add(new SetCardInfo("Soltari Monk", 45, Rarity.UNCOMMON, mage.cards.s.SoltariMonk.class)); - cards.add(new SetCardInfo("Soltari Priest", 46, Rarity.UNCOMMON, mage.cards.s.SoltariPriest.class)); - cards.add(new SetCardInfo("Soltari Trooper", 47, Rarity.COMMON, mage.cards.s.SoltariTrooper.class)); - cards.add(new SetCardInfo("Souldrinker", 158, Rarity.UNCOMMON, mage.cards.s.Souldrinker.class)); - cards.add(new SetCardInfo("Spell Blast", 89, Rarity.COMMON, mage.cards.s.SpellBlast.class)); - cards.add(new SetCardInfo("Spike Drone", 258, Rarity.COMMON, mage.cards.s.SpikeDrone.class)); - cards.add(new SetCardInfo("Spinal Graft", 159, Rarity.COMMON, mage.cards.s.SpinalGraft.class)); - cards.add(new SetCardInfo("Spirit Mirror", 48, Rarity.RARE, mage.cards.s.SpiritMirror.class)); - cards.add(new SetCardInfo("Spontaneous Combustion", 273, Rarity.UNCOMMON, mage.cards.s.SpontaneousCombustion.class)); - cards.add(new SetCardInfo("Squee's Toy", 309, Rarity.COMMON, mage.cards.s.SqueesToy.class)); - cards.add(new SetCardInfo("Stalking Stones", 327, Rarity.UNCOMMON, mage.cards.s.StalkingStones.class)); - cards.add(new SetCardInfo("Starke of Rath", 205, Rarity.RARE, mage.cards.s.StarkeOfRath.class)); - cards.add(new SetCardInfo("Static Orb", 310, Rarity.RARE, mage.cards.s.StaticOrb.class)); - cards.add(new SetCardInfo("Staunch Defenders", 49, Rarity.UNCOMMON, mage.cards.s.StaunchDefenders.class)); - cards.add(new SetCardInfo("Steal Enchantment", 90, Rarity.UNCOMMON, mage.cards.s.StealEnchantment.class)); - cards.add(new SetCardInfo("Stinging Licid", 91, Rarity.UNCOMMON, mage.cards.s.StingingLicid.class)); - cards.add(new SetCardInfo("Stone Rain", 206, Rarity.COMMON, mage.cards.s.StoneRain.class)); - cards.add(new SetCardInfo("Storm Front", 259, Rarity.UNCOMMON, mage.cards.s.StormFront.class)); - cards.add(new SetCardInfo("Stun", 207, Rarity.COMMON, mage.cards.s.Stun.class)); - cards.add(new SetCardInfo("Sudden Impact", 208, Rarity.UNCOMMON, mage.cards.s.SuddenImpact.class)); - cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Tahngarth's Rage", 209, Rarity.UNCOMMON, mage.cards.t.TahngarthsRage.class)); - cards.add(new SetCardInfo("Talon Sliver", 50, Rarity.COMMON, mage.cards.t.TalonSliver.class)); - cards.add(new SetCardInfo("Telethopter", 311, Rarity.UNCOMMON, mage.cards.t.Telethopter.class)); - cards.add(new SetCardInfo("Thalakos Dreamsower", 92, Rarity.UNCOMMON, mage.cards.t.ThalakosDreamsower.class)); - cards.add(new SetCardInfo("Thalakos Lowlands", 328, Rarity.UNCOMMON, mage.cards.t.ThalakosLowlands.class)); - cards.add(new SetCardInfo("Thalakos Mistfolk", 93, Rarity.COMMON, mage.cards.t.ThalakosMistfolk.class)); - cards.add(new SetCardInfo("Thalakos Seer", 94, Rarity.COMMON, mage.cards.t.ThalakosSeer.class)); - cards.add(new SetCardInfo("Thalakos Sentry", 95, Rarity.COMMON, mage.cards.t.ThalakosSentry.class)); - cards.add(new SetCardInfo("Thumbscrews", 312, Rarity.RARE, mage.cards.t.Thumbscrews.class)); - cards.add(new SetCardInfo("Time Ebb", 96, Rarity.COMMON, mage.cards.t.TimeEbb.class)); - cards.add(new SetCardInfo("Time Warp", 97, Rarity.RARE, mage.cards.t.TimeWarp.class)); - cards.add(new SetCardInfo("Tooth and Claw", 210, Rarity.RARE, mage.cards.t.ToothAndClaw.class)); - cards.add(new SetCardInfo("Torture Chamber", 313, Rarity.RARE, mage.cards.t.TortureChamber.class)); - cards.add(new SetCardInfo("Tradewind Rider", 98, Rarity.RARE, mage.cards.t.TradewindRider.class)); - cards.add(new SetCardInfo("Trained Armodon", 260, Rarity.COMMON, mage.cards.t.TrainedArmodon.class)); - cards.add(new SetCardInfo("Tranquility", 261, Rarity.COMMON, mage.cards.t.Tranquility.class)); - cards.add(new SetCardInfo("Trumpeting Armodon", 262, Rarity.UNCOMMON, mage.cards.t.TrumpetingArmodon.class)); - cards.add(new SetCardInfo("Twitch", 99, Rarity.COMMON, mage.cards.t.Twitch.class)); - cards.add(new SetCardInfo("Unstable Shapeshifter", 100, Rarity.RARE, mage.cards.u.UnstableShapeshifter.class)); - cards.add(new SetCardInfo("Vec Townships", 329, Rarity.UNCOMMON, mage.cards.v.VecTownships.class)); - cards.add(new SetCardInfo("Verdant Force", 263, Rarity.RARE, mage.cards.v.VerdantForce.class)); - cards.add(new SetCardInfo("Verdigris", 264, Rarity.UNCOMMON, mage.cards.v.Verdigris.class)); - cards.add(new SetCardInfo("Vhati il-Dal", 274, Rarity.RARE, mage.cards.v.VhatiIlDal.class)); - cards.add(new SetCardInfo("Volrath's Curse", 101, Rarity.COMMON, mage.cards.v.VolrathsCurse.class)); - cards.add(new SetCardInfo("Wall of Diffusion", 211, Rarity.COMMON, mage.cards.w.WallOfDiffusion.class)); - cards.add(new SetCardInfo("Warmth", 51, Rarity.UNCOMMON, mage.cards.w.Warmth.class)); - cards.add(new SetCardInfo("Wasteland", 330, Rarity.UNCOMMON, mage.cards.w.Wasteland.class)); - cards.add(new SetCardInfo("Watchdog", 314, Rarity.UNCOMMON, mage.cards.w.Watchdog.class)); - cards.add(new SetCardInfo("Whispers of the Muse", 103, Rarity.UNCOMMON, mage.cards.w.WhispersOfTheMuse.class)); - cards.add(new SetCardInfo("Wild Wurm", 212, Rarity.UNCOMMON, mage.cards.w.WildWurm.class)); - cards.add(new SetCardInfo("Wind Dancer", 104, Rarity.UNCOMMON, mage.cards.w.WindDancer.class)); - cards.add(new SetCardInfo("Wind Drake", 105, Rarity.COMMON, mage.cards.w.WindDrake.class)); - cards.add(new SetCardInfo("Winds of Rath", 52, Rarity.RARE, mage.cards.w.WindsOfRath.class)); - cards.add(new SetCardInfo("Winged Sliver", 106, Rarity.COMMON, mage.cards.w.WingedSliver.class)); - cards.add(new SetCardInfo("Winter's Grasp", 265, Rarity.UNCOMMON, mage.cards.w.WintersGrasp.class)); - cards.add(new SetCardInfo("Wood Sage", 275, Rarity.RARE, mage.cards.w.WoodSage.class)); - cards.add(new SetCardInfo("Worthy Cause", 53, Rarity.UNCOMMON, mage.cards.w.WorthyCause.class)); - } -} + cards.add(new SetCardInfo("Essence Bottle", 286, Rarity.UNCOMMON, mage.cards.e.EssenceBottle.class)); + cards.add(new SetCardInfo("Evincar's Justice", 134, Rarity.COMMON, mage.cards.e.EvincarsJustice.class)); + cards.add(new SetCardInfo("Excavator", 287, Rarity.UNCOMMON, mage.cards.e.Excavator.class)); + cards.add(new SetCardInfo("Extinction", 135, Rarity.RARE, mage.cards.e.Extinction.class)); + cards.add(new SetCardInfo("Fevered Convulsions", 136, Rarity.RARE, mage.cards.f.FeveredConvulsions.class)); + cards.add(new SetCardInfo("Field of Souls", 18, Rarity.RARE, mage.cards.f.FieldOfSouls.class)); + cards.add(new SetCardInfo("Fighting Drake", 63, Rarity.UNCOMMON, mage.cards.f.FightingDrake.class)); + cards.add(new SetCardInfo("Firefly", 172, Rarity.UNCOMMON, mage.cards.f.Firefly.class)); + cards.add(new SetCardInfo("Fireslinger", 173, Rarity.COMMON, mage.cards.f.Fireslinger.class)); + cards.add(new SetCardInfo("Flailing Drake", 227, Rarity.UNCOMMON, mage.cards.f.FlailingDrake.class)); + cards.add(new SetCardInfo("Flickering Ward", 19, Rarity.UNCOMMON, mage.cards.f.FlickeringWard.class)); + cards.add(new SetCardInfo("Flowstone Giant", 174, Rarity.COMMON, mage.cards.f.FlowstoneGiant.class)); + cards.add(new SetCardInfo("Flowstone Salamander", 175, Rarity.UNCOMMON, mage.cards.f.FlowstoneSalamander.class)); + cards.add(new SetCardInfo("Flowstone Sculpture", 288, Rarity.RARE, mage.cards.f.FlowstoneSculpture.class)); + cards.add(new SetCardInfo("Flowstone Wyvern", 176, Rarity.RARE, mage.cards.f.FlowstoneWyvern.class)); + cards.add(new SetCardInfo("Fool's Tome", 289, Rarity.RARE, mage.cards.f.FoolsTome.class)); + cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frog Tongue", 228, Rarity.COMMON, mage.cards.f.FrogTongue.class)); + cards.add(new SetCardInfo("Fugitive Druid", 229, Rarity.RARE, mage.cards.f.FugitiveDruid.class)); + cards.add(new SetCardInfo("Furnace of Rath", 177, Rarity.RARE, mage.cards.f.FurnaceOfRath.class)); + cards.add(new SetCardInfo("Fylamarid", 64, Rarity.UNCOMMON, mage.cards.f.Fylamarid.class)); + cards.add(new SetCardInfo("Gallantry", 20, Rarity.UNCOMMON, mage.cards.g.Gallantry.class)); + cards.add(new SetCardInfo("Gaseous Form", 65, Rarity.COMMON, mage.cards.g.GaseousForm.class)); + cards.add(new SetCardInfo("Gerrard's Battle Cry", 21, Rarity.RARE, mage.cards.g.GerrardsBattleCry.class)); + cards.add(new SetCardInfo("Ghost Town", 318, Rarity.UNCOMMON, mage.cards.g.GhostTown.class)); + cards.add(new SetCardInfo("Giant Crab", 66, Rarity.COMMON, mage.cards.g.GiantCrab.class)); + cards.add(new SetCardInfo("Giant Strength", 178, Rarity.COMMON, mage.cards.g.GiantStrength.class)); + cards.add(new SetCardInfo("Goblin Bombardment", 179, Rarity.UNCOMMON, mage.cards.g.GoblinBombardment.class)); + cards.add(new SetCardInfo("Gravedigger", 137, Rarity.COMMON, mage.cards.g.Gravedigger.class)); + cards.add(new SetCardInfo("Grindstone", 290, Rarity.RARE, mage.cards.g.Grindstone.class)); + cards.add(new SetCardInfo("Hand to Hand", 180, Rarity.RARE, mage.cards.h.HandToHand.class)); + cards.add(new SetCardInfo("Hanna's Custody", 22, Rarity.RARE, mage.cards.h.HannasCustody.class)); + cards.add(new SetCardInfo("Harrow", 230, Rarity.UNCOMMON, mage.cards.h.Harrow.class)); + cards.add(new SetCardInfo("Havoc", 181, Rarity.UNCOMMON, mage.cards.h.Havoc.class)); + cards.add(new SetCardInfo("Heart Sliver", 182, Rarity.COMMON, mage.cards.h.HeartSliver.class)); + cards.add(new SetCardInfo("Heartwood Dryad", 231, Rarity.COMMON, mage.cards.h.HeartwoodDryad.class)); + cards.add(new SetCardInfo("Heartwood Giant", 232, Rarity.RARE, mage.cards.h.HeartwoodGiant.class)); + cards.add(new SetCardInfo("Heartwood Treefolk", 233, Rarity.UNCOMMON, mage.cards.h.HeartwoodTreefolk.class)); + cards.add(new SetCardInfo("Helm of Possession", 291, Rarity.RARE, mage.cards.h.HelmOfPossession.class)); + cards.add(new SetCardInfo("Hero's Resolve", 23, Rarity.COMMON, mage.cards.h.HerosResolve.class)); + cards.add(new SetCardInfo("Horned Sliver", 234, Rarity.UNCOMMON, mage.cards.h.HornedSliver.class)); + cards.add(new SetCardInfo("Horned Turtle", 67, Rarity.COMMON, mage.cards.h.HornedTurtle.class)); + cards.add(new SetCardInfo("Humility", 24, Rarity.RARE, mage.cards.h.Humility.class)); + cards.add(new SetCardInfo("Imps' Taunt", 138, Rarity.UNCOMMON, mage.cards.i.ImpsTaunt.class)); + cards.add(new SetCardInfo("Insight", 68, Rarity.UNCOMMON, mage.cards.i.Insight.class)); + cards.add(new SetCardInfo("Interdict", 69, Rarity.UNCOMMON, mage.cards.i.Interdict.class)); + cards.add(new SetCardInfo("Intuition", 70, Rarity.RARE, mage.cards.i.Intuition.class)); + cards.add(new SetCardInfo("Invulnerability", 25, Rarity.UNCOMMON, mage.cards.i.Invulnerability.class)); + cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jackal Pup", 183, Rarity.UNCOMMON, mage.cards.j.JackalPup.class)); + cards.add(new SetCardInfo("Jet Medallion", 292, Rarity.RARE, mage.cards.j.JetMedallion.class)); + cards.add(new SetCardInfo("Jinxed Idol", 293, Rarity.RARE, mage.cards.j.JinxedIdol.class)); + cards.add(new SetCardInfo("Kezzerdrix", 139, Rarity.RARE, mage.cards.k.Kezzerdrix.class)); + cards.add(new SetCardInfo("Kindle", 184, Rarity.COMMON, mage.cards.k.Kindle.class)); + cards.add(new SetCardInfo("Knight of Dawn", 26, Rarity.UNCOMMON, mage.cards.k.KnightOfDawn.class)); + cards.add(new SetCardInfo("Knight of Dusk", 140, Rarity.UNCOMMON, mage.cards.k.KnightOfDusk.class)); + cards.add(new SetCardInfo("Krakilin", 235, Rarity.UNCOMMON, mage.cards.k.Krakilin.class)); + cards.add(new SetCardInfo("Leeching Licid", 141, Rarity.UNCOMMON, mage.cards.l.LeechingLicid.class)); + cards.add(new SetCardInfo("Legacy's Allure", 71, Rarity.UNCOMMON, mage.cards.l.LegacysAllure.class)); + cards.add(new SetCardInfo("Legerdemain", 72, Rarity.UNCOMMON, mage.cards.l.Legerdemain.class)); + cards.add(new SetCardInfo("Light of Day", 27, Rarity.UNCOMMON, mage.cards.l.LightOfDay.class)); + cards.add(new SetCardInfo("Lightning Blast", 185, Rarity.COMMON, mage.cards.l.LightningBlast.class)); + cards.add(new SetCardInfo("Lightning Elemental", 186, Rarity.COMMON, mage.cards.l.LightningElemental.class)); + cards.add(new SetCardInfo("Living Death", 142, Rarity.RARE, mage.cards.l.LivingDeath.class)); + cards.add(new SetCardInfo("Lobotomy", 267, Rarity.UNCOMMON, mage.cards.l.Lobotomy.class)); + cards.add(new SetCardInfo("Lotus Petal", 294, Rarity.COMMON, mage.cards.l.LotusPetal.class)); + cards.add(new SetCardInfo("Lowland Giant", 187, Rarity.COMMON, mage.cards.l.LowlandGiant.class)); + cards.add(new SetCardInfo("Maddening Imp", 143, Rarity.RARE, mage.cards.m.MaddeningImp.class)); + cards.add(new SetCardInfo("Magmasaur", 188, Rarity.RARE, mage.cards.m.Magmasaur.class)); + cards.add(new SetCardInfo("Mana Severance", 73, Rarity.RARE, mage.cards.m.ManaSeverance.class)); + cards.add(new SetCardInfo("Manakin", 296, Rarity.COMMON, mage.cards.m.Manakin.class)); + cards.add(new SetCardInfo("Manta Riders", 74, Rarity.COMMON, mage.cards.m.MantaRiders.class)); + cards.add(new SetCardInfo("Marble Titan", 28, Rarity.RARE, mage.cards.m.MarbleTitan.class)); + cards.add(new SetCardInfo("Marsh Lurker", 144, Rarity.COMMON, mage.cards.m.MarshLurker.class)); + cards.add(new SetCardInfo("Master Decoy", 29, Rarity.COMMON, mage.cards.m.MasterDecoy.class)); + cards.add(new SetCardInfo("Mawcor", 75, Rarity.RARE, mage.cards.m.Mawcor.class)); + cards.add(new SetCardInfo("Maze of Shadows", 319, Rarity.UNCOMMON, mage.cards.m.MazeOfShadows.class)); + cards.add(new SetCardInfo("Meditate", 76, Rarity.RARE, mage.cards.m.Meditate.class)); + cards.add(new SetCardInfo("Metallic Sliver", 297, Rarity.COMMON, mage.cards.m.MetallicSliver.class)); + cards.add(new SetCardInfo("Mindwhip Sliver", 145, Rarity.UNCOMMON, mage.cards.m.MindwhipSliver.class)); + cards.add(new SetCardInfo("Minion of the Wastes", 146, Rarity.RARE, mage.cards.m.MinionOfTheWastes.class)); + cards.add(new SetCardInfo("Mirri's Guile", 236, Rarity.RARE, mage.cards.m.MirrisGuile.class)); + cards.add(new SetCardInfo("Mnemonic Sliver", 77, Rarity.UNCOMMON, mage.cards.m.MnemonicSliver.class)); + cards.add(new SetCardInfo("Mogg Cannon", 298, Rarity.UNCOMMON, mage.cards.m.MoggCannon.class)); + cards.add(new SetCardInfo("Mogg Conscripts", 189, Rarity.COMMON, mage.cards.m.MoggConscripts.class)); + cards.add(new SetCardInfo("Mogg Fanatic", 190, Rarity.COMMON, mage.cards.m.MoggFanatic.class)); + cards.add(new SetCardInfo("Mogg Hollows", 320, Rarity.UNCOMMON, mage.cards.m.MoggHollows.class)); + cards.add(new SetCardInfo("Mogg Raider", 191, Rarity.COMMON, mage.cards.m.MoggRaider.class)); + cards.add(new SetCardInfo("Mogg Squad", 192, Rarity.UNCOMMON, mage.cards.m.MoggSquad.class)); + cards.add(new SetCardInfo("Mongrel Pack", 237, Rarity.RARE, mage.cards.m.MongrelPack.class)); + cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mounted Archers", 30, Rarity.COMMON, mage.cards.m.MountedArchers.class)); + cards.add(new SetCardInfo("Muscle Sliver", 238, Rarity.COMMON, mage.cards.m.MuscleSliver.class)); + cards.add(new SetCardInfo("Natural Spring", 239, Rarity.COMMON, mage.cards.n.NaturalSpring.class)); + cards.add(new SetCardInfo("Nature's Revolt", 240, Rarity.RARE, mage.cards.n.NaturesRevolt.class)); + cards.add(new SetCardInfo("Needle Storm", 241, Rarity.UNCOMMON, mage.cards.n.NeedleStorm.class)); + cards.add(new SetCardInfo("Nurturing Licid", 242, Rarity.UNCOMMON, mage.cards.n.NurturingLicid.class)); + cards.add(new SetCardInfo("Opportunist", 194, Rarity.UNCOMMON, mage.cards.o.Opportunist.class)); + cards.add(new SetCardInfo("Oracle en-Vec", 31, Rarity.RARE, mage.cards.o.OracleEnVec.class)); + cards.add(new SetCardInfo("Orim's Prayer", 32, Rarity.UNCOMMON, mage.cards.o.OrimsPrayer.class)); + cards.add(new SetCardInfo("Orim, Samite Healer", 33, Rarity.RARE, mage.cards.o.OrimSamiteHealer.class)); + cards.add(new SetCardInfo("Overrun", 243, Rarity.UNCOMMON, mage.cards.o.Overrun.class)); + cards.add(new SetCardInfo("Pacifism", 34, Rarity.COMMON, mage.cards.p.Pacifism.class)); + cards.add(new SetCardInfo("Pallimud", 195, Rarity.RARE, mage.cards.p.Pallimud.class)); + cards.add(new SetCardInfo("Patchwork Gnomes", 299, Rarity.UNCOMMON, mage.cards.p.PatchworkGnomes.class)); + cards.add(new SetCardInfo("Pearl Medallion", 300, Rarity.RARE, mage.cards.p.PearlMedallion.class)); + cards.add(new SetCardInfo("Pegasus Refuge", 35, Rarity.RARE, mage.cards.p.PegasusRefuge.class)); + cards.add(new SetCardInfo("Perish", 147, Rarity.UNCOMMON, mage.cards.p.Perish.class)); + cards.add(new SetCardInfo("Phyrexian Grimoire", 301, Rarity.RARE, mage.cards.p.PhyrexianGrimoire.class)); + cards.add(new SetCardInfo("Phyrexian Hulk", 302, Rarity.UNCOMMON, mage.cards.p.PhyrexianHulk.class)); + cards.add(new SetCardInfo("Pincher Beetles", 244, Rarity.COMMON, mage.cards.p.PincherBeetles.class)); + cards.add(new SetCardInfo("Pine Barrens", 321, Rarity.RARE, mage.cards.p.PineBarrens.class)); + cards.add(new SetCardInfo("Pit Imp", 148, Rarity.COMMON, mage.cards.p.PitImp.class)); + cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Power Sink", 78, Rarity.COMMON, mage.cards.p.PowerSink.class)); + cards.add(new SetCardInfo("Precognition", 79, Rarity.RARE, mage.cards.p.Precognition.class)); + cards.add(new SetCardInfo("Propaganda", 80, Rarity.UNCOMMON, mage.cards.p.Propaganda.class)); + cards.add(new SetCardInfo("Puppet Strings", 304, Rarity.UNCOMMON, mage.cards.p.PuppetStrings.class)); + cards.add(new SetCardInfo("Quickening Licid", 36, Rarity.UNCOMMON, mage.cards.q.QuickeningLicid.class)); + cards.add(new SetCardInfo("Rain of Tears", 149, Rarity.UNCOMMON, mage.cards.r.RainOfTears.class)); + cards.add(new SetCardInfo("Rampant Growth", 245, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); + cards.add(new SetCardInfo("Ranger en-Vec", 268, Rarity.UNCOMMON, mage.cards.r.RangerEnVec.class)); + cards.add(new SetCardInfo("Rathi Dragon", 196, Rarity.RARE, mage.cards.r.RathiDragon.class)); + cards.add(new SetCardInfo("Rats of Rath", 150, Rarity.COMMON, mage.cards.r.RatsOfRath.class)); + cards.add(new SetCardInfo("Reality Anchor", 246, Rarity.COMMON, mage.cards.r.RealityAnchor.class)); + cards.add(new SetCardInfo("Reanimate", 151, Rarity.UNCOMMON, mage.cards.r.Reanimate.class)); + cards.add(new SetCardInfo("Reap", 247, Rarity.UNCOMMON, mage.cards.r.Reap.class)); + cards.add(new SetCardInfo("Reckless Spite", 152, Rarity.UNCOMMON, mage.cards.r.RecklessSpite.class)); + cards.add(new SetCardInfo("Recycle", 248, Rarity.RARE, mage.cards.r.Recycle.class)); + cards.add(new SetCardInfo("Reflecting Pool", 322, Rarity.RARE, mage.cards.r.ReflectingPool.class)); + cards.add(new SetCardInfo("Renegade Warlord", 197, Rarity.UNCOMMON, mage.cards.r.RenegadeWarlord.class)); + cards.add(new SetCardInfo("Repentance", 37, Rarity.UNCOMMON, mage.cards.r.Repentance.class)); + cards.add(new SetCardInfo("Respite", 249, Rarity.COMMON, mage.cards.r.Respite.class)); + cards.add(new SetCardInfo("Rolling Thunder", 198, Rarity.COMMON, mage.cards.r.RollingThunder.class)); + cards.add(new SetCardInfo("Root Maze", 250, Rarity.RARE, mage.cards.r.RootMaze.class)); + cards.add(new SetCardInfo("Rootbreaker Wurm", 251, Rarity.COMMON, mage.cards.r.RootbreakerWurm.class)); + cards.add(new SetCardInfo("Rootwalla", 252, Rarity.COMMON, mage.cards.r.Rootwalla.class)); + cards.add(new SetCardInfo("Rootwater Depths", 323, Rarity.UNCOMMON, mage.cards.r.RootwaterDepths.class)); + cards.add(new SetCardInfo("Rootwater Diver", 81, Rarity.UNCOMMON, mage.cards.r.RootwaterDiver.class)); + cards.add(new SetCardInfo("Rootwater Hunter", 82, Rarity.COMMON, mage.cards.r.RootwaterHunter.class)); + cards.add(new SetCardInfo("Rootwater Matriarch", 83, Rarity.RARE, mage.cards.r.RootwaterMatriarch.class)); + cards.add(new SetCardInfo("Rootwater Shaman", 84, Rarity.RARE, mage.cards.r.RootwaterShaman.class)); + cards.add(new SetCardInfo("Ruby Medallion", 305, Rarity.RARE, mage.cards.r.RubyMedallion.class)); + cards.add(new SetCardInfo("Sacred Guide", 38, Rarity.RARE, mage.cards.s.SacredGuide.class)); + cards.add(new SetCardInfo("Sadistic Glee", 153, Rarity.COMMON, mage.cards.s.SadisticGlee.class)); + cards.add(new SetCardInfo("Safeguard", 39, Rarity.RARE, mage.cards.s.Safeguard.class)); + cards.add(new SetCardInfo("Salt Flats", 324, Rarity.RARE, mage.cards.s.SaltFlats.class)); + cards.add(new SetCardInfo("Sandstone Warrior", 199, Rarity.COMMON, mage.cards.s.SandstoneWarrior.class)); + cards.add(new SetCardInfo("Sapphire Medallion", 306, Rarity.RARE, mage.cards.s.SapphireMedallion.class)); + cards.add(new SetCardInfo("Sarcomancy", 154, Rarity.RARE, mage.cards.s.Sarcomancy.class)); + cards.add(new SetCardInfo("Scabland", 325, Rarity.RARE, mage.cards.s.Scabland.class)); + cards.add(new SetCardInfo("Scalding Tongs", 307, Rarity.RARE, mage.cards.s.ScaldingTongs.class)); + cards.add(new SetCardInfo("Scorched Earth", 200, Rarity.RARE, mage.cards.s.ScorchedEarth.class)); + cards.add(new SetCardInfo("Scragnoth", 253, Rarity.UNCOMMON, mage.cards.s.Scragnoth.class)); + cards.add(new SetCardInfo("Screeching Harpy", 155, Rarity.UNCOMMON, mage.cards.s.ScreechingHarpy.class)); + cards.add(new SetCardInfo("Scroll Rack", 308, Rarity.RARE, mage.cards.s.ScrollRack.class)); + cards.add(new SetCardInfo("Sea Monster", 85, Rarity.COMMON, mage.cards.s.SeaMonster.class)); + cards.add(new SetCardInfo("Searing Touch", 201, Rarity.UNCOMMON, mage.cards.s.SearingTouch.class)); + cards.add(new SetCardInfo("Seeker of Skybreak", 254, Rarity.COMMON, mage.cards.s.SeekerOfSkybreak.class)); + cards.add(new SetCardInfo("Segmented Wurm", 269, Rarity.UNCOMMON, mage.cards.s.SegmentedWurm.class)); + cards.add(new SetCardInfo("Selenia, Dark Angel", 270, Rarity.RARE, mage.cards.s.SeleniaDarkAngel.class)); + cards.add(new SetCardInfo("Serene Offering", 40, Rarity.UNCOMMON, mage.cards.s.SereneOffering.class)); + cards.add(new SetCardInfo("Servant of Volrath", 156, Rarity.COMMON, mage.cards.s.ServantOfVolrath.class)); + cards.add(new SetCardInfo("Shadow Rift", 86, Rarity.COMMON, mage.cards.s.ShadowRift.class)); + cards.add(new SetCardInfo("Shadowstorm", 202, Rarity.UNCOMMON, mage.cards.s.Shadowstorm.class)); + cards.add(new SetCardInfo("Shatter", 203, Rarity.COMMON, mage.cards.s.Shatter.class)); + cards.add(new SetCardInfo("Shimmering Wings", 87, Rarity.COMMON, mage.cards.s.ShimmeringWings.class)); + cards.add(new SetCardInfo("Shocker", 204, Rarity.RARE, mage.cards.s.Shocker.class)); + cards.add(new SetCardInfo("Sky Spirit", 271, Rarity.UNCOMMON, mage.cards.s.SkySpirit.class)); + cards.add(new SetCardInfo("Skyshroud Condor", 88, Rarity.UNCOMMON, mage.cards.s.SkyshroudCondor.class)); + cards.add(new SetCardInfo("Skyshroud Elf", 255, Rarity.COMMON, mage.cards.s.SkyshroudElf.class)); + cards.add(new SetCardInfo("Skyshroud Forest", 326, Rarity.RARE, mage.cards.s.SkyshroudForest.class)); + cards.add(new SetCardInfo("Skyshroud Ranger", 256, Rarity.COMMON, mage.cards.s.SkyshroudRanger.class)); + cards.add(new SetCardInfo("Skyshroud Troll", 257, Rarity.COMMON, mage.cards.s.SkyshroudTroll.class)); + cards.add(new SetCardInfo("Skyshroud Vampire", 157, Rarity.UNCOMMON, mage.cards.s.SkyshroudVampire.class)); + cards.add(new SetCardInfo("Soltari Crusader", 41, Rarity.UNCOMMON, mage.cards.s.SoltariCrusader.class)); + cards.add(new SetCardInfo("Soltari Emissary", 42, Rarity.RARE, mage.cards.s.SoltariEmissary.class)); + cards.add(new SetCardInfo("Soltari Foot Soldier", 43, Rarity.COMMON, mage.cards.s.SoltariFootSoldier.class)); + cards.add(new SetCardInfo("Soltari Guerrillas", 272, Rarity.RARE, mage.cards.s.SoltariGuerrillas.class)); + cards.add(new SetCardInfo("Soltari Lancer", 44, Rarity.COMMON, mage.cards.s.SoltariLancer.class)); + cards.add(new SetCardInfo("Soltari Monk", 45, Rarity.UNCOMMON, mage.cards.s.SoltariMonk.class)); + cards.add(new SetCardInfo("Soltari Priest", 46, Rarity.UNCOMMON, mage.cards.s.SoltariPriest.class)); + cards.add(new SetCardInfo("Soltari Trooper", 47, Rarity.COMMON, mage.cards.s.SoltariTrooper.class)); + cards.add(new SetCardInfo("Souldrinker", 158, Rarity.UNCOMMON, mage.cards.s.Souldrinker.class)); + cards.add(new SetCardInfo("Spell Blast", 89, Rarity.COMMON, mage.cards.s.SpellBlast.class)); + cards.add(new SetCardInfo("Spike Drone", 258, Rarity.COMMON, mage.cards.s.SpikeDrone.class)); + cards.add(new SetCardInfo("Spinal Graft", 159, Rarity.COMMON, mage.cards.s.SpinalGraft.class)); + cards.add(new SetCardInfo("Spirit Mirror", 48, Rarity.RARE, mage.cards.s.SpiritMirror.class)); + cards.add(new SetCardInfo("Spontaneous Combustion", 273, Rarity.UNCOMMON, mage.cards.s.SpontaneousCombustion.class)); + cards.add(new SetCardInfo("Squee's Toy", 309, Rarity.COMMON, mage.cards.s.SqueesToy.class)); + cards.add(new SetCardInfo("Stalking Stones", 327, Rarity.UNCOMMON, mage.cards.s.StalkingStones.class)); + cards.add(new SetCardInfo("Starke of Rath", 205, Rarity.RARE, mage.cards.s.StarkeOfRath.class)); + cards.add(new SetCardInfo("Static Orb", 310, Rarity.RARE, mage.cards.s.StaticOrb.class)); + cards.add(new SetCardInfo("Staunch Defenders", 49, Rarity.UNCOMMON, mage.cards.s.StaunchDefenders.class)); + cards.add(new SetCardInfo("Steal Enchantment", 90, Rarity.UNCOMMON, mage.cards.s.StealEnchantment.class)); + cards.add(new SetCardInfo("Stinging Licid", 91, Rarity.UNCOMMON, mage.cards.s.StingingLicid.class)); + cards.add(new SetCardInfo("Stone Rain", 206, Rarity.COMMON, mage.cards.s.StoneRain.class)); + cards.add(new SetCardInfo("Storm Front", 259, Rarity.UNCOMMON, mage.cards.s.StormFront.class)); + cards.add(new SetCardInfo("Stun", 207, Rarity.COMMON, mage.cards.s.Stun.class)); + cards.add(new SetCardInfo("Sudden Impact", 208, Rarity.UNCOMMON, mage.cards.s.SuddenImpact.class)); + cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tahngarth's Rage", 209, Rarity.UNCOMMON, mage.cards.t.TahngarthsRage.class)); + cards.add(new SetCardInfo("Talon Sliver", 50, Rarity.COMMON, mage.cards.t.TalonSliver.class)); + cards.add(new SetCardInfo("Telethopter", 311, Rarity.UNCOMMON, mage.cards.t.Telethopter.class)); + cards.add(new SetCardInfo("Thalakos Dreamsower", 92, Rarity.UNCOMMON, mage.cards.t.ThalakosDreamsower.class)); + cards.add(new SetCardInfo("Thalakos Lowlands", 328, Rarity.UNCOMMON, mage.cards.t.ThalakosLowlands.class)); + cards.add(new SetCardInfo("Thalakos Mistfolk", 93, Rarity.COMMON, mage.cards.t.ThalakosMistfolk.class)); + cards.add(new SetCardInfo("Thalakos Seer", 94, Rarity.COMMON, mage.cards.t.ThalakosSeer.class)); + cards.add(new SetCardInfo("Thalakos Sentry", 95, Rarity.COMMON, mage.cards.t.ThalakosSentry.class)); + cards.add(new SetCardInfo("Thumbscrews", 312, Rarity.RARE, mage.cards.t.Thumbscrews.class)); + cards.add(new SetCardInfo("Time Ebb", 96, Rarity.COMMON, mage.cards.t.TimeEbb.class)); + cards.add(new SetCardInfo("Time Warp", 97, Rarity.RARE, mage.cards.t.TimeWarp.class)); + cards.add(new SetCardInfo("Tooth and Claw", 210, Rarity.RARE, mage.cards.t.ToothAndClaw.class)); + cards.add(new SetCardInfo("Torture Chamber", 313, Rarity.RARE, mage.cards.t.TortureChamber.class)); + cards.add(new SetCardInfo("Tradewind Rider", 98, Rarity.RARE, mage.cards.t.TradewindRider.class)); + cards.add(new SetCardInfo("Trained Armodon", 260, Rarity.COMMON, mage.cards.t.TrainedArmodon.class)); + cards.add(new SetCardInfo("Tranquility", 261, Rarity.COMMON, mage.cards.t.Tranquility.class)); + cards.add(new SetCardInfo("Trumpeting Armodon", 262, Rarity.UNCOMMON, mage.cards.t.TrumpetingArmodon.class)); + cards.add(new SetCardInfo("Twitch", 99, Rarity.COMMON, mage.cards.t.Twitch.class)); + cards.add(new SetCardInfo("Unstable Shapeshifter", 100, Rarity.RARE, mage.cards.u.UnstableShapeshifter.class)); + cards.add(new SetCardInfo("Vec Townships", 329, Rarity.UNCOMMON, mage.cards.v.VecTownships.class)); + cards.add(new SetCardInfo("Verdant Force", 263, Rarity.RARE, mage.cards.v.VerdantForce.class)); + cards.add(new SetCardInfo("Verdigris", 264, Rarity.UNCOMMON, mage.cards.v.Verdigris.class)); + cards.add(new SetCardInfo("Vhati il-Dal", 274, Rarity.RARE, mage.cards.v.VhatiIlDal.class)); + cards.add(new SetCardInfo("Volrath's Curse", 101, Rarity.COMMON, mage.cards.v.VolrathsCurse.class)); + cards.add(new SetCardInfo("Wall of Diffusion", 211, Rarity.COMMON, mage.cards.w.WallOfDiffusion.class)); + cards.add(new SetCardInfo("Warmth", 51, Rarity.UNCOMMON, mage.cards.w.Warmth.class)); + cards.add(new SetCardInfo("Wasteland", 330, Rarity.UNCOMMON, mage.cards.w.Wasteland.class)); + cards.add(new SetCardInfo("Watchdog", 314, Rarity.UNCOMMON, mage.cards.w.Watchdog.class)); + cards.add(new SetCardInfo("Whispers of the Muse", 103, Rarity.UNCOMMON, mage.cards.w.WhispersOfTheMuse.class)); + cards.add(new SetCardInfo("Wild Wurm", 212, Rarity.UNCOMMON, mage.cards.w.WildWurm.class)); + cards.add(new SetCardInfo("Wind Dancer", 104, Rarity.UNCOMMON, mage.cards.w.WindDancer.class)); + cards.add(new SetCardInfo("Wind Drake", 105, Rarity.COMMON, mage.cards.w.WindDrake.class)); + cards.add(new SetCardInfo("Winds of Rath", 52, Rarity.RARE, mage.cards.w.WindsOfRath.class)); + cards.add(new SetCardInfo("Winged Sliver", 106, Rarity.COMMON, mage.cards.w.WingedSliver.class)); + cards.add(new SetCardInfo("Winter's Grasp", 265, Rarity.UNCOMMON, mage.cards.w.WintersGrasp.class)); + cards.add(new SetCardInfo("Wood Sage", 275, Rarity.RARE, mage.cards.w.WoodSage.class)); + cards.add(new SetCardInfo("Worthy Cause", 53, Rarity.UNCOMMON, mage.cards.w.WorthyCause.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Torment.java b/Mage.Sets/src/mage/sets/Torment.java index 2680b1ba0c..6ede6aff0a 100644 --- a/Mage.Sets/src/mage/sets/Torment.java +++ b/Mage.Sets/src/mage/sets/Torment.java @@ -1,170 +1,170 @@ - -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author North - */ -public final class Torment extends ExpansionSet { - - private static final Torment instance = new Torment(); - - public static Torment getInstance() { - return instance; - } - - private Torment() { - super("Torment", "TOR", ExpansionSet.buildDate(2002, 1, 26), SetType.EXPANSION); - this.blockName = "Odyssey"; - this.parentSet = Odyssey.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Accelerate", 90, Rarity.COMMON, mage.cards.a.Accelerate.class)); - cards.add(new SetCardInfo("Acorn Harvest", 118, Rarity.COMMON, mage.cards.a.AcornHarvest.class)); - cards.add(new SetCardInfo("Ambassador Laquatus", 23, Rarity.RARE, mage.cards.a.AmbassadorLaquatus.class)); - cards.add(new SetCardInfo("Angel of Retribution", 1, Rarity.RARE, mage.cards.a.AngelOfRetribution.class)); - cards.add(new SetCardInfo("Anurid Scavenger", 119, Rarity.UNCOMMON, mage.cards.a.AnuridScavenger.class)); - cards.add(new SetCardInfo("Aquamoeba", 24, Rarity.COMMON, mage.cards.a.Aquamoeba.class)); - cards.add(new SetCardInfo("Arrogant Wurm", 120, Rarity.UNCOMMON, mage.cards.a.ArrogantWurm.class)); - cards.add(new SetCardInfo("Aven Trooper", 2, Rarity.COMMON, mage.cards.a.AvenTrooper.class)); - cards.add(new SetCardInfo("Balshan Collaborator", 25, Rarity.UNCOMMON, mage.cards.b.BalshanCollaborator.class)); - cards.add(new SetCardInfo("Balthor the Stout", 91, Rarity.RARE, mage.cards.b.BalthorTheStout.class)); - cards.add(new SetCardInfo("Barbarian Outcast", 92, Rarity.COMMON, mage.cards.b.BarbarianOutcast.class)); - cards.add(new SetCardInfo("Basking Rootwalla", 121, Rarity.COMMON, mage.cards.b.BaskingRootwalla.class)); - cards.add(new SetCardInfo("Boneshard Slasher", 50, Rarity.UNCOMMON, mage.cards.b.BoneshardSlasher.class)); - cards.add(new SetCardInfo("Breakthrough", 26, Rarity.UNCOMMON, mage.cards.b.Breakthrough.class)); - cards.add(new SetCardInfo("Cabal Coffers", 139, Rarity.UNCOMMON, mage.cards.c.CabalCoffers.class)); - cards.add(new SetCardInfo("Cabal Ritual", 51, Rarity.COMMON, mage.cards.c.CabalRitual.class)); - cards.add(new SetCardInfo("Cabal Surgeon", 52, Rarity.COMMON, mage.cards.c.CabalSurgeon.class)); - cards.add(new SetCardInfo("Cabal Torturer", 53, Rarity.COMMON, mage.cards.c.CabalTorturer.class)); - cards.add(new SetCardInfo("Centaur Chieftain", 122, Rarity.UNCOMMON, mage.cards.c.CentaurChieftain.class)); - cards.add(new SetCardInfo("Centaur Veteran", 123, Rarity.COMMON, mage.cards.c.CentaurVeteran.class)); - cards.add(new SetCardInfo("Cephalid Aristocrat", 27, Rarity.COMMON, mage.cards.c.CephalidAristocrat.class)); - cards.add(new SetCardInfo("Cephalid Illusionist", 28, Rarity.UNCOMMON, mage.cards.c.CephalidIllusionist.class)); - cards.add(new SetCardInfo("Cephalid Sage", 29, Rarity.UNCOMMON, mage.cards.c.CephalidSage.class)); - cards.add(new SetCardInfo("Cephalid Snitch", 30, Rarity.COMMON, mage.cards.c.CephalidSnitch.class)); - cards.add(new SetCardInfo("Cephalid Vandal", 31, Rarity.RARE, mage.cards.c.CephalidVandal.class)); - cards.add(new SetCardInfo("Chainer, Dementia Master", 56, Rarity.RARE, mage.cards.c.ChainerDementiaMaster.class)); - cards.add(new SetCardInfo("Chainer's Edict", 57, Rarity.UNCOMMON, mage.cards.c.ChainersEdict.class)); - cards.add(new SetCardInfo("Churning Eddy", 32, Rarity.COMMON, mage.cards.c.ChurningEddy.class)); - cards.add(new SetCardInfo("Circular Logic", 33, Rarity.UNCOMMON, mage.cards.c.CircularLogic.class)); - cards.add(new SetCardInfo("Cleansing Meditation", 3, Rarity.UNCOMMON, mage.cards.c.CleansingMeditation.class)); - cards.add(new SetCardInfo("Compulsion", 34, Rarity.UNCOMMON, mage.cards.c.Compulsion.class)); - cards.add(new SetCardInfo("Coral Net", 35, Rarity.COMMON, mage.cards.c.CoralNet.class)); - cards.add(new SetCardInfo("Crackling Club", 93, Rarity.COMMON, mage.cards.c.CracklingClub.class)); - cards.add(new SetCardInfo("Crazed Firecat", 94, Rarity.UNCOMMON, mage.cards.c.CrazedFirecat.class)); - cards.add(new SetCardInfo("Crippling Fatigue", 58, Rarity.COMMON, mage.cards.c.CripplingFatigue.class)); - cards.add(new SetCardInfo("Dawn of the Dead", 59, Rarity.RARE, mage.cards.d.DawnOfTheDead.class)); - cards.add(new SetCardInfo("Deep Analysis", 36, Rarity.COMMON, mage.cards.d.DeepAnalysis.class)); - cards.add(new SetCardInfo("Devastating Dreams", 95, Rarity.RARE, mage.cards.d.DevastatingDreams.class)); - cards.add(new SetCardInfo("Dwell on the Past", 124, Rarity.UNCOMMON, mage.cards.d.DwellOnThePast.class)); - cards.add(new SetCardInfo("Enslaved Dwarf", 96, Rarity.COMMON, mage.cards.e.EnslavedDwarf.class)); - cards.add(new SetCardInfo("Equal Treatment", 4, Rarity.UNCOMMON, mage.cards.e.EqualTreatment.class)); - cards.add(new SetCardInfo("Faceless Butcher", 60, Rarity.COMMON, mage.cards.f.FacelessButcher.class)); - cards.add(new SetCardInfo("False Memories", 37, Rarity.RARE, mage.cards.f.FalseMemories.class)); - cards.add(new SetCardInfo("Far Wanderings", 125, Rarity.COMMON, mage.cards.f.FarWanderings.class)); - cards.add(new SetCardInfo("Fiery Temper", 97, Rarity.COMMON, mage.cards.f.FieryTemper.class)); - cards.add(new SetCardInfo("Flash of Defiance", 99, Rarity.COMMON, mage.cards.f.FlashOfDefiance.class)); - cards.add(new SetCardInfo("Frantic Purification", 6, Rarity.COMMON, mage.cards.f.FranticPurification.class)); - cards.add(new SetCardInfo("Ghostly Wings", 38, Rarity.COMMON, mage.cards.g.GhostlyWings.class)); - cards.add(new SetCardInfo("Gloomdrifter", 61, Rarity.UNCOMMON, mage.cards.g.Gloomdrifter.class)); - cards.add(new SetCardInfo("Gravegouger", 62, Rarity.COMMON, mage.cards.g.Gravegouger.class)); - cards.add(new SetCardInfo("Grim Lavamancer", 100, Rarity.RARE, mage.cards.g.GrimLavamancer.class)); - cards.add(new SetCardInfo("Grotesque Hybrid", 63, Rarity.UNCOMMON, mage.cards.g.GrotesqueHybrid.class)); - cards.add(new SetCardInfo("Gurzigost", 126, Rarity.RARE, mage.cards.g.Gurzigost.class)); - cards.add(new SetCardInfo("Hell-Bent Raider", 101, Rarity.RARE, mage.cards.h.HellBentRaider.class)); - cards.add(new SetCardInfo("Hydromorph Guardian", 39, Rarity.COMMON, mage.cards.h.HydromorphGuardian.class)); - cards.add(new SetCardInfo("Hydromorph Gull", 40, Rarity.UNCOMMON, mage.cards.h.HydromorphGull.class)); - cards.add(new SetCardInfo("Hypochondria", 7, Rarity.UNCOMMON, mage.cards.h.Hypochondria.class)); - cards.add(new SetCardInfo("Hypnox", 64, Rarity.RARE, mage.cards.h.Hypnox.class)); - cards.add(new SetCardInfo("Ichorid", 65, Rarity.RARE, mage.cards.i.Ichorid.class)); - cards.add(new SetCardInfo("Insidious Dreams", 66, Rarity.RARE, mage.cards.i.InsidiousDreams.class)); - cards.add(new SetCardInfo("Insist", 127, Rarity.RARE, mage.cards.i.Insist.class)); - cards.add(new SetCardInfo("Invigorating Falls", 128, Rarity.COMMON, mage.cards.i.InvigoratingFalls.class)); - cards.add(new SetCardInfo("Kamahl's Sledge", 102, Rarity.COMMON, mage.cards.k.KamahlsSledge.class)); - cards.add(new SetCardInfo("Krosan Constrictor", 129, Rarity.COMMON, mage.cards.k.KrosanConstrictor.class)); - cards.add(new SetCardInfo("Krosan Restorer", 130, Rarity.COMMON, mage.cards.k.KrosanRestorer.class)); - cards.add(new SetCardInfo("Laquatus's Champion", 67, Rarity.RARE, mage.cards.l.LaquatussChampion.class)); - cards.add(new SetCardInfo("Last Laugh", 68, Rarity.RARE, mage.cards.l.LastLaugh.class)); - cards.add(new SetCardInfo("Liquify", 41, Rarity.COMMON, mage.cards.l.Liquify.class)); - cards.add(new SetCardInfo("Llawan, Cephalid Empress", 42, Rarity.RARE, mage.cards.l.LlawanCephalidEmpress.class)); - cards.add(new SetCardInfo("Longhorn Firebeast", 103, Rarity.COMMON, mage.cards.l.LonghornFirebeast.class)); - cards.add(new SetCardInfo("Major Teroh", 8, Rarity.RARE, mage.cards.m.MajorTeroh.class)); - cards.add(new SetCardInfo("Mesmeric Fiend", 69, Rarity.COMMON, mage.cards.m.MesmericFiend.class)); - cards.add(new SetCardInfo("Militant Monk", 9, Rarity.COMMON, mage.cards.m.MilitantMonk.class)); - cards.add(new SetCardInfo("Mind Sludge", 70, Rarity.UNCOMMON, mage.cards.m.MindSludge.class)); - cards.add(new SetCardInfo("Morningtide", 10, Rarity.RARE, mage.cards.m.Morningtide.class)); - cards.add(new SetCardInfo("Mortal Combat", 71, Rarity.RARE, mage.cards.m.MortalCombat.class)); - cards.add(new SetCardInfo("Mortiphobia", 72, Rarity.UNCOMMON, mage.cards.m.Mortiphobia.class)); - cards.add(new SetCardInfo("Mutilate", 73, Rarity.RARE, mage.cards.m.Mutilate.class)); - cards.add(new SetCardInfo("Mystic Familiar", 11, Rarity.COMMON, mage.cards.m.MysticFamiliar.class)); - cards.add(new SetCardInfo("Nantuko Blightcutter", 131, Rarity.RARE, mage.cards.n.NantukoBlightcutter.class)); - cards.add(new SetCardInfo("Nantuko Calmer", 132, Rarity.COMMON, mage.cards.n.NantukoCalmer.class)); - cards.add(new SetCardInfo("Nantuko Cultivator", 133, Rarity.RARE, mage.cards.n.NantukoCultivator.class)); - cards.add(new SetCardInfo("Nantuko Shade", 74, Rarity.RARE, mage.cards.n.NantukoShade.class)); - cards.add(new SetCardInfo("Narcissism", 134, Rarity.UNCOMMON, mage.cards.n.Narcissism.class)); - cards.add(new SetCardInfo("Nostalgic Dreams", 135, Rarity.RARE, mage.cards.n.NostalgicDreams.class)); - cards.add(new SetCardInfo("Obsessive Search", 43, Rarity.COMMON, mage.cards.o.ObsessiveSearch.class)); - cards.add(new SetCardInfo("Organ Grinder", 75, Rarity.COMMON, mage.cards.o.OrganGrinder.class)); - cards.add(new SetCardInfo("Overmaster", 104, Rarity.RARE, mage.cards.o.Overmaster.class)); - cards.add(new SetCardInfo("Parallel Evolution", 136, Rarity.RARE, mage.cards.p.ParallelEvolution.class)); - cards.add(new SetCardInfo("Pardic Arsonist", 105, Rarity.UNCOMMON, mage.cards.p.PardicArsonist.class)); - cards.add(new SetCardInfo("Pardic Collaborator", 106, Rarity.UNCOMMON, mage.cards.p.PardicCollaborator.class)); - cards.add(new SetCardInfo("Pardic Lancer", 107, Rarity.COMMON, mage.cards.p.PardicLancer.class)); - cards.add(new SetCardInfo("Pay No Heed", 12, Rarity.COMMON, mage.cards.p.PayNoHeed.class)); - cards.add(new SetCardInfo("Petradon", 108, Rarity.RARE, mage.cards.p.Petradon.class)); - cards.add(new SetCardInfo("Petravark", 109, Rarity.COMMON, mage.cards.p.Petravark.class)); - cards.add(new SetCardInfo("Pitchstone Wall", 110, Rarity.UNCOMMON, mage.cards.p.PitchstoneWall.class)); - cards.add(new SetCardInfo("Plagiarize", 44, Rarity.RARE, mage.cards.p.Plagiarize.class)); - cards.add(new SetCardInfo("Possessed Aven", 45, Rarity.RARE, mage.cards.p.PossessedAven.class)); - cards.add(new SetCardInfo("Possessed Barbarian", 111, Rarity.RARE, mage.cards.p.PossessedBarbarian.class)); - cards.add(new SetCardInfo("Possessed Centaur", 137, Rarity.RARE, mage.cards.p.PossessedCentaur.class)); - cards.add(new SetCardInfo("Possessed Nomad", 13, Rarity.RARE, mage.cards.p.PossessedNomad.class)); - cards.add(new SetCardInfo("Psychotic Haze", 76, Rarity.COMMON, mage.cards.p.PsychoticHaze.class)); - cards.add(new SetCardInfo("Putrid Imp", 77, Rarity.COMMON, mage.cards.p.PutridImp.class)); - cards.add(new SetCardInfo("Pyromania", 112, Rarity.UNCOMMON, mage.cards.p.Pyromania.class)); - cards.add(new SetCardInfo("Radiate", 113, Rarity.RARE, mage.cards.r.Radiate.class)); - cards.add(new SetCardInfo("Rancid Earth", 78, Rarity.COMMON, mage.cards.r.RancidEarth.class)); - cards.add(new SetCardInfo("Reborn Hero", 14, Rarity.RARE, mage.cards.r.RebornHero.class)); - cards.add(new SetCardInfo("Restless Dreams", 79, Rarity.COMMON, mage.cards.r.RestlessDreams.class)); - cards.add(new SetCardInfo("Retraced Image", 46, Rarity.RARE, mage.cards.r.RetracedImage.class)); - cards.add(new SetCardInfo("Sengir Vampire", 80, Rarity.RARE, mage.cards.s.SengirVampire.class)); - cards.add(new SetCardInfo("Seton's Scout", 138, Rarity.UNCOMMON, mage.cards.s.SetonsScout.class)); - cards.add(new SetCardInfo("Shade's Form", 81, Rarity.COMMON, mage.cards.s.ShadesForm.class)); - cards.add(new SetCardInfo("Shambling Swarm", 82, Rarity.RARE, mage.cards.s.ShamblingSwarm.class)); - cards.add(new SetCardInfo("Sickening Dreams", 83, Rarity.UNCOMMON, mage.cards.s.SickeningDreams.class)); - cards.add(new SetCardInfo("Skullscorch", 114, Rarity.RARE, mage.cards.s.Skullscorch.class)); - cards.add(new SetCardInfo("Skywing Aven", 47, Rarity.COMMON, mage.cards.s.SkywingAven.class)); - cards.add(new SetCardInfo("Slithery Stalker", 84, Rarity.UNCOMMON, mage.cards.s.SlitheryStalker.class)); - cards.add(new SetCardInfo("Sonic Seizure", 115, Rarity.COMMON, mage.cards.s.SonicSeizure.class)); - cards.add(new SetCardInfo("Soul Scourge", 85, Rarity.COMMON, mage.cards.s.SoulScourge.class)); + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author North + */ +public final class Torment extends ExpansionSet { + + private static final Torment instance = new Torment(); + + public static Torment getInstance() { + return instance; + } + + private Torment() { + super("Torment", "TOR", ExpansionSet.buildDate(2002, 1, 26), SetType.EXPANSION); + this.blockName = "Odyssey"; + this.parentSet = Odyssey.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Accelerate", 90, Rarity.COMMON, mage.cards.a.Accelerate.class)); + cards.add(new SetCardInfo("Acorn Harvest", 118, Rarity.COMMON, mage.cards.a.AcornHarvest.class)); + cards.add(new SetCardInfo("Ambassador Laquatus", 23, Rarity.RARE, mage.cards.a.AmbassadorLaquatus.class)); + cards.add(new SetCardInfo("Angel of Retribution", 1, Rarity.RARE, mage.cards.a.AngelOfRetribution.class)); + cards.add(new SetCardInfo("Anurid Scavenger", 119, Rarity.UNCOMMON, mage.cards.a.AnuridScavenger.class)); + cards.add(new SetCardInfo("Aquamoeba", 24, Rarity.COMMON, mage.cards.a.Aquamoeba.class)); + cards.add(new SetCardInfo("Arrogant Wurm", 120, Rarity.UNCOMMON, mage.cards.a.ArrogantWurm.class)); + cards.add(new SetCardInfo("Aven Trooper", 2, Rarity.COMMON, mage.cards.a.AvenTrooper.class)); + cards.add(new SetCardInfo("Balshan Collaborator", 25, Rarity.UNCOMMON, mage.cards.b.BalshanCollaborator.class)); + cards.add(new SetCardInfo("Balthor the Stout", 91, Rarity.RARE, mage.cards.b.BalthorTheStout.class)); + cards.add(new SetCardInfo("Barbarian Outcast", 92, Rarity.COMMON, mage.cards.b.BarbarianOutcast.class)); + cards.add(new SetCardInfo("Basking Rootwalla", 121, Rarity.COMMON, mage.cards.b.BaskingRootwalla.class)); + cards.add(new SetCardInfo("Boneshard Slasher", 50, Rarity.UNCOMMON, mage.cards.b.BoneshardSlasher.class)); + cards.add(new SetCardInfo("Breakthrough", 26, Rarity.UNCOMMON, mage.cards.b.Breakthrough.class)); + cards.add(new SetCardInfo("Cabal Coffers", 139, Rarity.UNCOMMON, mage.cards.c.CabalCoffers.class)); + cards.add(new SetCardInfo("Cabal Ritual", 51, Rarity.COMMON, mage.cards.c.CabalRitual.class)); + cards.add(new SetCardInfo("Cabal Surgeon", 52, Rarity.COMMON, mage.cards.c.CabalSurgeon.class)); + cards.add(new SetCardInfo("Cabal Torturer", 53, Rarity.COMMON, mage.cards.c.CabalTorturer.class)); + cards.add(new SetCardInfo("Centaur Chieftain", 122, Rarity.UNCOMMON, mage.cards.c.CentaurChieftain.class)); + cards.add(new SetCardInfo("Centaur Veteran", 123, Rarity.COMMON, mage.cards.c.CentaurVeteran.class)); + cards.add(new SetCardInfo("Cephalid Aristocrat", 27, Rarity.COMMON, mage.cards.c.CephalidAristocrat.class)); + cards.add(new SetCardInfo("Cephalid Illusionist", 28, Rarity.UNCOMMON, mage.cards.c.CephalidIllusionist.class)); + cards.add(new SetCardInfo("Cephalid Sage", 29, Rarity.UNCOMMON, mage.cards.c.CephalidSage.class)); + cards.add(new SetCardInfo("Cephalid Snitch", 30, Rarity.COMMON, mage.cards.c.CephalidSnitch.class)); + cards.add(new SetCardInfo("Cephalid Vandal", 31, Rarity.RARE, mage.cards.c.CephalidVandal.class)); + cards.add(new SetCardInfo("Chainer, Dementia Master", 56, Rarity.RARE, mage.cards.c.ChainerDementiaMaster.class)); + cards.add(new SetCardInfo("Chainer's Edict", 57, Rarity.UNCOMMON, mage.cards.c.ChainersEdict.class)); + cards.add(new SetCardInfo("Churning Eddy", 32, Rarity.COMMON, mage.cards.c.ChurningEddy.class)); + cards.add(new SetCardInfo("Circular Logic", 33, Rarity.UNCOMMON, mage.cards.c.CircularLogic.class)); + cards.add(new SetCardInfo("Cleansing Meditation", 3, Rarity.UNCOMMON, mage.cards.c.CleansingMeditation.class)); + cards.add(new SetCardInfo("Compulsion", 34, Rarity.UNCOMMON, mage.cards.c.Compulsion.class)); + cards.add(new SetCardInfo("Coral Net", 35, Rarity.COMMON, mage.cards.c.CoralNet.class)); + cards.add(new SetCardInfo("Crackling Club", 93, Rarity.COMMON, mage.cards.c.CracklingClub.class)); + cards.add(new SetCardInfo("Crazed Firecat", 94, Rarity.UNCOMMON, mage.cards.c.CrazedFirecat.class)); + cards.add(new SetCardInfo("Crippling Fatigue", 58, Rarity.COMMON, mage.cards.c.CripplingFatigue.class)); + cards.add(new SetCardInfo("Dawn of the Dead", 59, Rarity.RARE, mage.cards.d.DawnOfTheDead.class)); + cards.add(new SetCardInfo("Deep Analysis", 36, Rarity.COMMON, mage.cards.d.DeepAnalysis.class)); + cards.add(new SetCardInfo("Devastating Dreams", 95, Rarity.RARE, mage.cards.d.DevastatingDreams.class)); + cards.add(new SetCardInfo("Dwell on the Past", 124, Rarity.UNCOMMON, mage.cards.d.DwellOnThePast.class)); + cards.add(new SetCardInfo("Enslaved Dwarf", 96, Rarity.COMMON, mage.cards.e.EnslavedDwarf.class)); + cards.add(new SetCardInfo("Equal Treatment", 4, Rarity.UNCOMMON, mage.cards.e.EqualTreatment.class)); + cards.add(new SetCardInfo("Faceless Butcher", 60, Rarity.COMMON, mage.cards.f.FacelessButcher.class)); + cards.add(new SetCardInfo("False Memories", 37, Rarity.RARE, mage.cards.f.FalseMemories.class)); + cards.add(new SetCardInfo("Far Wanderings", 125, Rarity.COMMON, mage.cards.f.FarWanderings.class)); + cards.add(new SetCardInfo("Fiery Temper", 97, Rarity.COMMON, mage.cards.f.FieryTemper.class)); + cards.add(new SetCardInfo("Flash of Defiance", 99, Rarity.COMMON, mage.cards.f.FlashOfDefiance.class)); + cards.add(new SetCardInfo("Frantic Purification", 6, Rarity.COMMON, mage.cards.f.FranticPurification.class)); + cards.add(new SetCardInfo("Ghostly Wings", 38, Rarity.COMMON, mage.cards.g.GhostlyWings.class)); + cards.add(new SetCardInfo("Gloomdrifter", 61, Rarity.UNCOMMON, mage.cards.g.Gloomdrifter.class)); + cards.add(new SetCardInfo("Gravegouger", 62, Rarity.COMMON, mage.cards.g.Gravegouger.class)); + cards.add(new SetCardInfo("Grim Lavamancer", 100, Rarity.RARE, mage.cards.g.GrimLavamancer.class)); + cards.add(new SetCardInfo("Grotesque Hybrid", 63, Rarity.UNCOMMON, mage.cards.g.GrotesqueHybrid.class)); + cards.add(new SetCardInfo("Gurzigost", 126, Rarity.RARE, mage.cards.g.Gurzigost.class)); + cards.add(new SetCardInfo("Hell-Bent Raider", 101, Rarity.RARE, mage.cards.h.HellBentRaider.class)); + cards.add(new SetCardInfo("Hydromorph Guardian", 39, Rarity.COMMON, mage.cards.h.HydromorphGuardian.class)); + cards.add(new SetCardInfo("Hydromorph Gull", 40, Rarity.UNCOMMON, mage.cards.h.HydromorphGull.class)); + cards.add(new SetCardInfo("Hypochondria", 7, Rarity.UNCOMMON, mage.cards.h.Hypochondria.class)); + cards.add(new SetCardInfo("Hypnox", 64, Rarity.RARE, mage.cards.h.Hypnox.class)); + cards.add(new SetCardInfo("Ichorid", 65, Rarity.RARE, mage.cards.i.Ichorid.class)); + cards.add(new SetCardInfo("Insidious Dreams", 66, Rarity.RARE, mage.cards.i.InsidiousDreams.class)); + cards.add(new SetCardInfo("Insist", 127, Rarity.RARE, mage.cards.i.Insist.class)); + cards.add(new SetCardInfo("Invigorating Falls", 128, Rarity.COMMON, mage.cards.i.InvigoratingFalls.class)); + cards.add(new SetCardInfo("Kamahl's Sledge", 102, Rarity.COMMON, mage.cards.k.KamahlsSledge.class)); + cards.add(new SetCardInfo("Krosan Constrictor", 129, Rarity.COMMON, mage.cards.k.KrosanConstrictor.class)); + cards.add(new SetCardInfo("Krosan Restorer", 130, Rarity.COMMON, mage.cards.k.KrosanRestorer.class)); + cards.add(new SetCardInfo("Laquatus's Champion", 67, Rarity.RARE, mage.cards.l.LaquatussChampion.class)); + cards.add(new SetCardInfo("Last Laugh", 68, Rarity.RARE, mage.cards.l.LastLaugh.class)); + cards.add(new SetCardInfo("Liquify", 41, Rarity.COMMON, mage.cards.l.Liquify.class)); + cards.add(new SetCardInfo("Llawan, Cephalid Empress", 42, Rarity.RARE, mage.cards.l.LlawanCephalidEmpress.class)); + cards.add(new SetCardInfo("Longhorn Firebeast", 103, Rarity.COMMON, mage.cards.l.LonghornFirebeast.class)); + cards.add(new SetCardInfo("Major Teroh", 8, Rarity.RARE, mage.cards.m.MajorTeroh.class)); + cards.add(new SetCardInfo("Mesmeric Fiend", 69, Rarity.COMMON, mage.cards.m.MesmericFiend.class)); + cards.add(new SetCardInfo("Militant Monk", 9, Rarity.COMMON, mage.cards.m.MilitantMonk.class)); + cards.add(new SetCardInfo("Mind Sludge", 70, Rarity.UNCOMMON, mage.cards.m.MindSludge.class)); + cards.add(new SetCardInfo("Morningtide", 10, Rarity.RARE, mage.cards.m.Morningtide.class)); + cards.add(new SetCardInfo("Mortal Combat", 71, Rarity.RARE, mage.cards.m.MortalCombat.class)); + cards.add(new SetCardInfo("Mortiphobia", 72, Rarity.UNCOMMON, mage.cards.m.Mortiphobia.class)); + cards.add(new SetCardInfo("Mutilate", 73, Rarity.RARE, mage.cards.m.Mutilate.class)); + cards.add(new SetCardInfo("Mystic Familiar", 11, Rarity.COMMON, mage.cards.m.MysticFamiliar.class)); + cards.add(new SetCardInfo("Nantuko Blightcutter", 131, Rarity.RARE, mage.cards.n.NantukoBlightcutter.class)); + cards.add(new SetCardInfo("Nantuko Calmer", 132, Rarity.COMMON, mage.cards.n.NantukoCalmer.class)); + cards.add(new SetCardInfo("Nantuko Cultivator", 133, Rarity.RARE, mage.cards.n.NantukoCultivator.class)); + cards.add(new SetCardInfo("Nantuko Shade", 74, Rarity.RARE, mage.cards.n.NantukoShade.class)); + cards.add(new SetCardInfo("Narcissism", 134, Rarity.UNCOMMON, mage.cards.n.Narcissism.class)); + cards.add(new SetCardInfo("Nostalgic Dreams", 135, Rarity.RARE, mage.cards.n.NostalgicDreams.class)); + cards.add(new SetCardInfo("Obsessive Search", 43, Rarity.COMMON, mage.cards.o.ObsessiveSearch.class)); + cards.add(new SetCardInfo("Organ Grinder", 75, Rarity.COMMON, mage.cards.o.OrganGrinder.class)); + cards.add(new SetCardInfo("Overmaster", 104, Rarity.RARE, mage.cards.o.Overmaster.class)); + cards.add(new SetCardInfo("Parallel Evolution", 136, Rarity.RARE, mage.cards.p.ParallelEvolution.class)); + cards.add(new SetCardInfo("Pardic Arsonist", 105, Rarity.UNCOMMON, mage.cards.p.PardicArsonist.class)); + cards.add(new SetCardInfo("Pardic Collaborator", 106, Rarity.UNCOMMON, mage.cards.p.PardicCollaborator.class)); + cards.add(new SetCardInfo("Pardic Lancer", 107, Rarity.COMMON, mage.cards.p.PardicLancer.class)); + cards.add(new SetCardInfo("Pay No Heed", 12, Rarity.COMMON, mage.cards.p.PayNoHeed.class)); + cards.add(new SetCardInfo("Petradon", 108, Rarity.RARE, mage.cards.p.Petradon.class)); + cards.add(new SetCardInfo("Petravark", 109, Rarity.COMMON, mage.cards.p.Petravark.class)); + cards.add(new SetCardInfo("Pitchstone Wall", 110, Rarity.UNCOMMON, mage.cards.p.PitchstoneWall.class)); + cards.add(new SetCardInfo("Plagiarize", 44, Rarity.RARE, mage.cards.p.Plagiarize.class)); + cards.add(new SetCardInfo("Possessed Aven", 45, Rarity.RARE, mage.cards.p.PossessedAven.class)); + cards.add(new SetCardInfo("Possessed Barbarian", 111, Rarity.RARE, mage.cards.p.PossessedBarbarian.class)); + cards.add(new SetCardInfo("Possessed Centaur", 137, Rarity.RARE, mage.cards.p.PossessedCentaur.class)); + cards.add(new SetCardInfo("Possessed Nomad", 13, Rarity.RARE, mage.cards.p.PossessedNomad.class)); + cards.add(new SetCardInfo("Psychotic Haze", 76, Rarity.COMMON, mage.cards.p.PsychoticHaze.class)); + cards.add(new SetCardInfo("Putrid Imp", 77, Rarity.COMMON, mage.cards.p.PutridImp.class)); + cards.add(new SetCardInfo("Pyromania", 112, Rarity.UNCOMMON, mage.cards.p.Pyromania.class)); + cards.add(new SetCardInfo("Radiate", 113, Rarity.RARE, mage.cards.r.Radiate.class)); + cards.add(new SetCardInfo("Rancid Earth", 78, Rarity.COMMON, mage.cards.r.RancidEarth.class)); + cards.add(new SetCardInfo("Reborn Hero", 14, Rarity.RARE, mage.cards.r.RebornHero.class)); + cards.add(new SetCardInfo("Restless Dreams", 79, Rarity.COMMON, mage.cards.r.RestlessDreams.class)); + cards.add(new SetCardInfo("Retraced Image", 46, Rarity.RARE, mage.cards.r.RetracedImage.class)); + cards.add(new SetCardInfo("Sengir Vampire", 80, Rarity.RARE, mage.cards.s.SengirVampire.class)); + cards.add(new SetCardInfo("Seton's Scout", 138, Rarity.UNCOMMON, mage.cards.s.SetonsScout.class)); + cards.add(new SetCardInfo("Shade's Form", 81, Rarity.COMMON, mage.cards.s.ShadesForm.class)); + cards.add(new SetCardInfo("Shambling Swarm", 82, Rarity.RARE, mage.cards.s.ShamblingSwarm.class)); + cards.add(new SetCardInfo("Sickening Dreams", 83, Rarity.UNCOMMON, mage.cards.s.SickeningDreams.class)); + cards.add(new SetCardInfo("Skullscorch", 114, Rarity.RARE, mage.cards.s.Skullscorch.class)); + cards.add(new SetCardInfo("Skywing Aven", 47, Rarity.COMMON, mage.cards.s.SkywingAven.class)); + cards.add(new SetCardInfo("Slithery Stalker", 84, Rarity.UNCOMMON, mage.cards.s.SlitheryStalker.class)); + cards.add(new SetCardInfo("Sonic Seizure", 115, Rarity.COMMON, mage.cards.s.SonicSeizure.class)); + cards.add(new SetCardInfo("Soul Scourge", 85, Rarity.COMMON, mage.cards.s.SoulScourge.class)); cards.add(new SetCardInfo("Spirit Flare", 15, Rarity.COMMON, mage.cards.s.SpiritFlare.class)); - cards.add(new SetCardInfo("Stern Judge", 16, Rarity.UNCOMMON, mage.cards.s.SternJudge.class)); - cards.add(new SetCardInfo("Strength of Isolation", 17, Rarity.UNCOMMON, mage.cards.s.StrengthOfIsolation.class)); - cards.add(new SetCardInfo("Strength of Lunacy", 86, Rarity.UNCOMMON, mage.cards.s.StrengthOfLunacy.class)); - cards.add(new SetCardInfo("Stupefying Touch", 48, Rarity.UNCOMMON, mage.cards.s.StupefyingTouch.class)); - cards.add(new SetCardInfo("Tainted Field", 140, Rarity.UNCOMMON, mage.cards.t.TaintedField.class)); - cards.add(new SetCardInfo("Tainted Isle", 141, Rarity.UNCOMMON, mage.cards.t.TaintedIsle.class)); - cards.add(new SetCardInfo("Tainted Peak", 142, Rarity.UNCOMMON, mage.cards.t.TaintedPeak.class)); - cards.add(new SetCardInfo("Tainted Wood", 143, Rarity.UNCOMMON, mage.cards.t.TaintedWood.class)); - cards.add(new SetCardInfo("Temporary Insanity", 116, Rarity.UNCOMMON, mage.cards.t.TemporaryInsanity.class)); - cards.add(new SetCardInfo("Teroh's Faithful", 18, Rarity.COMMON, mage.cards.t.TerohsFaithful.class)); - cards.add(new SetCardInfo("Teroh's Vanguard", 19, Rarity.UNCOMMON, mage.cards.t.TerohsVanguard.class)); - cards.add(new SetCardInfo("Transcendence", 20, Rarity.RARE, mage.cards.t.Transcendence.class)); - cards.add(new SetCardInfo("Turbulent Dreams", 49, Rarity.RARE, mage.cards.t.TurbulentDreams.class)); - cards.add(new SetCardInfo("Unhinge", 87, Rarity.COMMON, mage.cards.u.Unhinge.class)); - cards.add(new SetCardInfo("Vengeful Dreams", 21, Rarity.RARE, mage.cards.v.VengefulDreams.class)); - cards.add(new SetCardInfo("Violent Eruption", 117, Rarity.UNCOMMON, mage.cards.v.ViolentEruption.class)); - cards.add(new SetCardInfo("Waste Away", 88, Rarity.COMMON, mage.cards.w.WasteAway.class)); - cards.add(new SetCardInfo("Zombie Trailblazer", 89, Rarity.UNCOMMON, mage.cards.z.ZombieTrailblazer.class)); - } -} + cards.add(new SetCardInfo("Stern Judge", 16, Rarity.UNCOMMON, mage.cards.s.SternJudge.class)); + cards.add(new SetCardInfo("Strength of Isolation", 17, Rarity.UNCOMMON, mage.cards.s.StrengthOfIsolation.class)); + cards.add(new SetCardInfo("Strength of Lunacy", 86, Rarity.UNCOMMON, mage.cards.s.StrengthOfLunacy.class)); + cards.add(new SetCardInfo("Stupefying Touch", 48, Rarity.UNCOMMON, mage.cards.s.StupefyingTouch.class)); + cards.add(new SetCardInfo("Tainted Field", 140, Rarity.UNCOMMON, mage.cards.t.TaintedField.class)); + cards.add(new SetCardInfo("Tainted Isle", 141, Rarity.UNCOMMON, mage.cards.t.TaintedIsle.class)); + cards.add(new SetCardInfo("Tainted Peak", 142, Rarity.UNCOMMON, mage.cards.t.TaintedPeak.class)); + cards.add(new SetCardInfo("Tainted Wood", 143, Rarity.UNCOMMON, mage.cards.t.TaintedWood.class)); + cards.add(new SetCardInfo("Temporary Insanity", 116, Rarity.UNCOMMON, mage.cards.t.TemporaryInsanity.class)); + cards.add(new SetCardInfo("Teroh's Faithful", 18, Rarity.COMMON, mage.cards.t.TerohsFaithful.class)); + cards.add(new SetCardInfo("Teroh's Vanguard", 19, Rarity.UNCOMMON, mage.cards.t.TerohsVanguard.class)); + cards.add(new SetCardInfo("Transcendence", 20, Rarity.RARE, mage.cards.t.Transcendence.class)); + cards.add(new SetCardInfo("Turbulent Dreams", 49, Rarity.RARE, mage.cards.t.TurbulentDreams.class)); + cards.add(new SetCardInfo("Unhinge", 87, Rarity.COMMON, mage.cards.u.Unhinge.class)); + cards.add(new SetCardInfo("Vengeful Dreams", 21, Rarity.RARE, mage.cards.v.VengefulDreams.class)); + cards.add(new SetCardInfo("Violent Eruption", 117, Rarity.UNCOMMON, mage.cards.v.ViolentEruption.class)); + cards.add(new SetCardInfo("Waste Away", 88, Rarity.COMMON, mage.cards.w.WasteAway.class)); + cards.add(new SetCardInfo("Zombie Trailblazer", 89, Rarity.UNCOMMON, mage.cards.z.ZombieTrailblazer.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 5b874239b1..4417dcb857 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -72,6 +72,7 @@ public final class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Steamflogger Boss", 93, Rarity.RARE, mage.cards.s.SteamfloggerBoss.class)); cards.add(new SetCardInfo("Steel Squirrel", 162, Rarity.UNCOMMON, mage.cards.s.SteelSquirrel.class)); cards.add(new SetCardInfo("Summon the Pack", 74, Rarity.MYTHIC, mage.cards.s.SummonThePack.class)); + cards.add(new SetCardInfo("Super-Duper Death Ray", 97, Rarity.UNCOMMON, mage.cards.s.SuperDuperDeathRay.class)); cards.add(new SetCardInfo("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UST_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Sword of Dungeons & Dragons", 163, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); cards.add(new SetCardInfo("Target Minotaur", "98a", Rarity.COMMON, mage.cards.t.TargetMinotaur.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/UrzasDestiny.java b/Mage.Sets/src/mage/sets/UrzasDestiny.java index aae8ca309c..93a6023a2e 100644 --- a/Mage.Sets/src/mage/sets/UrzasDestiny.java +++ b/Mage.Sets/src/mage/sets/UrzasDestiny.java @@ -1,175 +1,175 @@ - -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author Backfir3 - */ -public final class UrzasDestiny extends ExpansionSet { - - private static final UrzasDestiny instance = new UrzasDestiny(); - - public static UrzasDestiny getInstance() { - return instance; - } - - private UrzasDestiny() { - super("Urza's Destiny", "UDS", ExpansionSet.buildDate(1999, 6, 7), SetType.EXPANSION); - this.blockName = "Urza"; - this.parentSet = UrzasSaga.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Academy Rector", 1, Rarity.RARE, mage.cards.a.AcademyRector.class)); - cards.add(new SetCardInfo("Aether Sting", 76, Rarity.UNCOMMON, mage.cards.a.AetherSting.class)); - cards.add(new SetCardInfo("Ancient Silverback", 101, Rarity.RARE, mage.cards.a.AncientSilverback.class)); - cards.add(new SetCardInfo("Apprentice Necromancer", 51, Rarity.RARE, mage.cards.a.ApprenticeNecromancer.class)); - cards.add(new SetCardInfo("Archery Training", 2, Rarity.UNCOMMON, mage.cards.a.ArcheryTraining.class)); - cards.add(new SetCardInfo("Attrition", 52, Rarity.RARE, mage.cards.a.Attrition.class)); - cards.add(new SetCardInfo("Aura Thief", 26, Rarity.RARE, mage.cards.a.AuraThief.class)); - cards.add(new SetCardInfo("Blizzard Elemental", 27, Rarity.RARE, mage.cards.b.BlizzardElemental.class)); - cards.add(new SetCardInfo("Bloodshot Cyclops", 77, Rarity.RARE, mage.cards.b.BloodshotCyclops.class)); - cards.add(new SetCardInfo("Body Snatcher", 53, Rarity.RARE, mage.cards.b.BodySnatcher.class)); - cards.add(new SetCardInfo("Braidwood Cup", 126, Rarity.UNCOMMON, mage.cards.b.BraidwoodCup.class)); - cards.add(new SetCardInfo("Braidwood Sextant", 127, Rarity.UNCOMMON, mage.cards.b.BraidwoodSextant.class)); - cards.add(new SetCardInfo("Brass Secretary", 128, Rarity.UNCOMMON, mage.cards.b.BrassSecretary.class)); - cards.add(new SetCardInfo("Brine Seer", 28, Rarity.UNCOMMON, mage.cards.b.BrineSeer.class)); - cards.add(new SetCardInfo("Bubbling Beebles", 29, Rarity.COMMON, mage.cards.b.BubblingBeebles.class)); - cards.add(new SetCardInfo("Bubbling Muck", 54, Rarity.COMMON, mage.cards.b.BubblingMuck.class)); - cards.add(new SetCardInfo("Caltrops", 129, Rarity.UNCOMMON, mage.cards.c.Caltrops.class)); - cards.add(new SetCardInfo("Capashen Knight", 3, Rarity.COMMON, mage.cards.c.CapashenKnight.class)); - cards.add(new SetCardInfo("Capashen Standard", 4, Rarity.COMMON, mage.cards.c.CapashenStandard.class)); - cards.add(new SetCardInfo("Capashen Templar", 5, Rarity.COMMON, mage.cards.c.CapashenTemplar.class)); - cards.add(new SetCardInfo("Carnival of Souls", 55, Rarity.RARE, mage.cards.c.CarnivalOfSouls.class)); - cards.add(new SetCardInfo("Chime of Night", 56, Rarity.COMMON, mage.cards.c.ChimeOfNight.class)); - cards.add(new SetCardInfo("Cinder Seer", 78, Rarity.UNCOMMON, mage.cards.c.CinderSeer.class)); - cards.add(new SetCardInfo("Colos Yearling", 79, Rarity.COMMON, mage.cards.c.ColosYearling.class)); - cards.add(new SetCardInfo("Compost", 102, Rarity.UNCOMMON, mage.cards.c.Compost.class)); - cards.add(new SetCardInfo("Covetous Dragon", 80, Rarity.RARE, mage.cards.c.CovetousDragon.class)); - cards.add(new SetCardInfo("Disappear", 30, Rarity.UNCOMMON, mage.cards.d.Disappear.class)); - cards.add(new SetCardInfo("Disease Carriers", 57, Rarity.COMMON, mage.cards.d.DiseaseCarriers.class)); - cards.add(new SetCardInfo("Donate", 31, Rarity.RARE, mage.cards.d.Donate.class)); - cards.add(new SetCardInfo("Dying Wail", 58, Rarity.COMMON, mage.cards.d.DyingWail.class)); - cards.add(new SetCardInfo("Elvish Lookout", 103, Rarity.COMMON, mage.cards.e.ElvishLookout.class)); - cards.add(new SetCardInfo("Elvish Piper", 104, Rarity.RARE, mage.cards.e.ElvishPiper.class)); - cards.add(new SetCardInfo("Emperor Crocodile", 105, Rarity.RARE, mage.cards.e.EmperorCrocodile.class)); - cards.add(new SetCardInfo("Encroach", 59, Rarity.UNCOMMON, mage.cards.e.Encroach.class)); - cards.add(new SetCardInfo("Eradicate", 60, Rarity.UNCOMMON, mage.cards.e.Eradicate.class)); - cards.add(new SetCardInfo("Extruder", 130, Rarity.UNCOMMON, mage.cards.e.Extruder.class)); - cards.add(new SetCardInfo("False Prophet", 6, Rarity.RARE, mage.cards.f.FalseProphet.class)); - cards.add(new SetCardInfo("Fatigue", 32, Rarity.COMMON, mage.cards.f.Fatigue.class)); - cards.add(new SetCardInfo("Fend Off", 7, Rarity.COMMON, mage.cards.f.FendOff.class)); - cards.add(new SetCardInfo("Festering Wound", 61, Rarity.UNCOMMON, mage.cards.f.FesteringWound.class)); - cards.add(new SetCardInfo("Field Surgeon", 8, Rarity.COMMON, mage.cards.f.FieldSurgeon.class)); - cards.add(new SetCardInfo("Flame Jet", 81, Rarity.COMMON, mage.cards.f.FlameJet.class)); - cards.add(new SetCardInfo("Fledgling Osprey", 33, Rarity.COMMON, mage.cards.f.FledglingOsprey.class)); - cards.add(new SetCardInfo("Flicker", 9, Rarity.RARE, mage.cards.f.Flicker.class)); - cards.add(new SetCardInfo("Fodder Cannon", 131, Rarity.UNCOMMON, mage.cards.f.FodderCannon.class)); - cards.add(new SetCardInfo("Gamekeeper", 106, Rarity.UNCOMMON, mage.cards.g.Gamekeeper.class)); - cards.add(new SetCardInfo("Goblin Berserker", 82, Rarity.UNCOMMON, mage.cards.g.GoblinBerserker.class)); - cards.add(new SetCardInfo("Goblin Festival", 83, Rarity.RARE, mage.cards.g.GoblinFestival.class)); - cards.add(new SetCardInfo("Goblin Gardener", 84, Rarity.COMMON, mage.cards.g.GoblinGardener.class)); - cards.add(new SetCardInfo("Goblin Marshal", 85, Rarity.RARE, mage.cards.g.GoblinMarshal.class)); - cards.add(new SetCardInfo("Goblin Masons", 86, Rarity.COMMON, mage.cards.g.GoblinMasons.class)); - cards.add(new SetCardInfo("Goliath Beetle", 107, Rarity.COMMON, mage.cards.g.GoliathBeetle.class)); - cards.add(new SetCardInfo("Heart Warden", 108, Rarity.COMMON, mage.cards.h.HeartWarden.class)); - cards.add(new SetCardInfo("Hulking Ogre", 87, Rarity.COMMON, mage.cards.h.HulkingOgre.class)); - cards.add(new SetCardInfo("Hunting Moa", 109, Rarity.UNCOMMON, mage.cards.h.HuntingMoa.class)); - cards.add(new SetCardInfo("Illuminated Wings", 34, Rarity.COMMON, mage.cards.i.IlluminatedWings.class)); - cards.add(new SetCardInfo("Impatience", 88, Rarity.RARE, mage.cards.i.Impatience.class)); - cards.add(new SetCardInfo("Incendiary", 89, Rarity.UNCOMMON, mage.cards.i.Incendiary.class)); - cards.add(new SetCardInfo("Iridescent Drake", 35, Rarity.UNCOMMON, mage.cards.i.IridescentDrake.class)); - cards.add(new SetCardInfo("Ivy Seer", 110, Rarity.UNCOMMON, mage.cards.i.IvySeer.class)); - cards.add(new SetCardInfo("Jasmine Seer", 10, Rarity.UNCOMMON, mage.cards.j.JasmineSeer.class)); - cards.add(new SetCardInfo("Junk Diver", 132, Rarity.RARE, mage.cards.j.JunkDiver.class)); - cards.add(new SetCardInfo("Keldon Champion", 90, Rarity.UNCOMMON, mage.cards.k.KeldonChampion.class)); - cards.add(new SetCardInfo("Keldon Vandals", 91, Rarity.COMMON, mage.cards.k.KeldonVandals.class)); - cards.add(new SetCardInfo("Kingfisher", 36, Rarity.COMMON, mage.cards.k.Kingfisher.class)); - cards.add(new SetCardInfo("Landslide", 92, Rarity.UNCOMMON, mage.cards.l.Landslide.class)); - cards.add(new SetCardInfo("Lurking Jackals", 62, Rarity.UNCOMMON, mage.cards.l.LurkingJackals.class)); - cards.add(new SetCardInfo("Magnify", 111, Rarity.COMMON, mage.cards.m.Magnify.class)); - cards.add(new SetCardInfo("Mantis Engine", 133, Rarity.UNCOMMON, mage.cards.m.MantisEngine.class)); - cards.add(new SetCardInfo("Marker Beetles", 112, Rarity.COMMON, mage.cards.m.MarkerBeetles.class)); - cards.add(new SetCardInfo("Mark of Fury", 93, Rarity.COMMON, mage.cards.m.MarkOfFury.class)); - cards.add(new SetCardInfo("Mask of Law and Grace", 11, Rarity.COMMON, mage.cards.m.MaskOfLawAndGrace.class)); - cards.add(new SetCardInfo("Master Healer", 12, Rarity.RARE, mage.cards.m.MasterHealer.class)); - cards.add(new SetCardInfo("Masticore", 134, Rarity.RARE, mage.cards.m.Masticore.class)); - cards.add(new SetCardInfo("Mental Discipline", 37, Rarity.COMMON, mage.cards.m.MentalDiscipline.class)); - cards.add(new SetCardInfo("Metalworker", 135, Rarity.RARE, mage.cards.m.Metalworker.class)); - cards.add(new SetCardInfo("Metathran Elite", 38, Rarity.UNCOMMON, mage.cards.m.MetathranElite.class)); - cards.add(new SetCardInfo("Metathran Soldier", 39, Rarity.COMMON, mage.cards.m.MetathranSoldier.class)); - cards.add(new SetCardInfo("Momentum", 113, Rarity.UNCOMMON, mage.cards.m.Momentum.class)); - cards.add(new SetCardInfo("Multani's Decree", 114, Rarity.COMMON, mage.cards.m.MultanisDecree.class)); - cards.add(new SetCardInfo("Nightshade Seer", 63, Rarity.UNCOMMON, mage.cards.n.NightshadeSeer.class)); - cards.add(new SetCardInfo("Opalescence", 13, Rarity.RARE, mage.cards.o.Opalescence.class)); - cards.add(new SetCardInfo("Opposition", 40, Rarity.RARE, mage.cards.o.Opposition.class)); - cards.add(new SetCardInfo("Pattern of Rebirth", 115, Rarity.RARE, mage.cards.p.PatternOfRebirth.class)); - cards.add(new SetCardInfo("Phyrexian Monitor", 64, Rarity.COMMON, mage.cards.p.PhyrexianMonitor.class)); - cards.add(new SetCardInfo("Phyrexian Negator", 65, Rarity.RARE, mage.cards.p.PhyrexianNegator.class)); - cards.add(new SetCardInfo("Plague Dogs", 66, Rarity.UNCOMMON, mage.cards.p.PlagueDogs.class)); - cards.add(new SetCardInfo("Plated Spider", 116, Rarity.COMMON, mage.cards.p.PlatedSpider.class)); - cards.add(new SetCardInfo("Plow Under", 117, Rarity.RARE, mage.cards.p.PlowUnder.class)); - cards.add(new SetCardInfo("Powder Keg", 136, Rarity.RARE, mage.cards.p.PowderKeg.class)); - cards.add(new SetCardInfo("Private Research", 41, Rarity.UNCOMMON, mage.cards.p.PrivateResearch.class)); - cards.add(new SetCardInfo("Quash", 42, Rarity.UNCOMMON, mage.cards.q.Quash.class)); - cards.add(new SetCardInfo("Rapid Decay", 67, Rarity.RARE, mage.cards.r.RapidDecay.class)); - cards.add(new SetCardInfo("Ravenous Rats", 68, Rarity.COMMON, mage.cards.r.RavenousRats.class)); - cards.add(new SetCardInfo("Rayne, Academy Chancellor", 43, Rarity.RARE, mage.cards.r.RayneAcademyChancellor.class)); - cards.add(new SetCardInfo("Reckless Abandon", 94, Rarity.COMMON, mage.cards.r.RecklessAbandon.class)); - cards.add(new SetCardInfo("Reliquary Monk", 14, Rarity.COMMON, mage.cards.r.ReliquaryMonk.class)); - cards.add(new SetCardInfo("Repercussion", 95, Rarity.RARE, mage.cards.r.Repercussion.class)); - cards.add(new SetCardInfo("Replenish", 15, Rarity.RARE, mage.cards.r.Replenish.class)); - cards.add(new SetCardInfo("Rescue", 44, Rarity.COMMON, mage.cards.r.Rescue.class)); - cards.add(new SetCardInfo("Rofellos's Gift", 119, Rarity.COMMON, mage.cards.r.RofellossGift.class)); - cards.add(new SetCardInfo("Rofellos, Llanowar Emissary", 118, Rarity.RARE, mage.cards.r.RofellosLlanowarEmissary.class)); - cards.add(new SetCardInfo("Sanctimony", 16, Rarity.UNCOMMON, mage.cards.s.Sanctimony.class)); - cards.add(new SetCardInfo("Scent of Brine", 45, Rarity.COMMON, mage.cards.s.ScentOfBrine.class)); - cards.add(new SetCardInfo("Scent of Cinder", 96, Rarity.COMMON, mage.cards.s.ScentOfCinder.class)); - cards.add(new SetCardInfo("Scent of Ivy", 120, Rarity.COMMON, mage.cards.s.ScentOfIvy.class)); - cards.add(new SetCardInfo("Scent of Jasmine", 17, Rarity.COMMON, mage.cards.s.ScentOfJasmine.class)); - cards.add(new SetCardInfo("Scent of Nightshade", 69, Rarity.COMMON, mage.cards.s.ScentOfNightshade.class)); - cards.add(new SetCardInfo("Scour", 18, Rarity.UNCOMMON, mage.cards.s.Scour.class)); - cards.add(new SetCardInfo("Scrying Glass", 137, Rarity.RARE, mage.cards.s.ScryingGlass.class)); - cards.add(new SetCardInfo("Serra Advocate", 19, Rarity.UNCOMMON, mage.cards.s.SerraAdvocate.class)); - cards.add(new SetCardInfo("Sigil of Sleep", 46, Rarity.COMMON, mage.cards.s.SigilOfSleep.class)); - cards.add(new SetCardInfo("Skittering Horror", 70, Rarity.COMMON, mage.cards.s.SkitteringHorror.class)); - cards.add(new SetCardInfo("Slinking Skirge", 71, Rarity.COMMON, mage.cards.s.SlinkingSkirge.class)); - cards.add(new SetCardInfo("Solidarity", 20, Rarity.COMMON, mage.cards.s.Solidarity.class)); - cards.add(new SetCardInfo("Soul Feast", 72, Rarity.UNCOMMON, mage.cards.s.SoulFeast.class)); - cards.add(new SetCardInfo("Sowing Salt", 97, Rarity.UNCOMMON, mage.cards.s.SowingSalt.class)); - cards.add(new SetCardInfo("Splinter", 121, Rarity.UNCOMMON, mage.cards.s.Splinter.class)); - cards.add(new SetCardInfo("Squirming Mass", 73, Rarity.COMMON, mage.cards.s.SquirmingMass.class)); - cards.add(new SetCardInfo("Storage Matrix", 138, Rarity.RARE, mage.cards.s.StorageMatrix.class)); - cards.add(new SetCardInfo("Taunting Elf", 122, Rarity.COMMON, mage.cards.t.TauntingElf.class)); - cards.add(new SetCardInfo("Telepathic Spies", 47, Rarity.COMMON, mage.cards.t.TelepathicSpies.class)); - cards.add(new SetCardInfo("Temporal Adept", 48, Rarity.RARE, mage.cards.t.TemporalAdept.class)); - cards.add(new SetCardInfo("Tethered Griffin", 21, Rarity.RARE, mage.cards.t.TetheredGriffin.class)); - cards.add(new SetCardInfo("Thieving Magpie", 49, Rarity.UNCOMMON, mage.cards.t.ThievingMagpie.class)); - cards.add(new SetCardInfo("Thorn Elemental", 123, Rarity.RARE, mage.cards.t.ThornElemental.class)); - cards.add(new SetCardInfo("Thran Dynamo", 139, Rarity.UNCOMMON, mage.cards.t.ThranDynamo.class)); - cards.add(new SetCardInfo("Thran Foundry", 140, Rarity.UNCOMMON, mage.cards.t.ThranFoundry.class)); - cards.add(new SetCardInfo("Thran Golem", 141, Rarity.RARE, mage.cards.t.ThranGolem.class)); - cards.add(new SetCardInfo("Tormented Angel", 22, Rarity.COMMON, mage.cards.t.TormentedAngel.class)); - cards.add(new SetCardInfo("Treachery", 50, Rarity.RARE, mage.cards.t.Treachery.class)); - cards.add(new SetCardInfo("Trumpet Blast", 98, Rarity.COMMON, mage.cards.t.TrumpetBlast.class)); - cards.add(new SetCardInfo("Twisted Experiment", 74, Rarity.COMMON, mage.cards.t.TwistedExperiment.class)); - cards.add(new SetCardInfo("Urza's Incubator", 142, Rarity.RARE, mage.cards.u.UrzasIncubator.class)); - cards.add(new SetCardInfo("Voice of Duty", 23, Rarity.UNCOMMON, mage.cards.v.VoiceOfDuty.class)); - cards.add(new SetCardInfo("Voice of Reason", 24, Rarity.UNCOMMON, mage.cards.v.VoiceOfReason.class)); - cards.add(new SetCardInfo("Wake of Destruction", 99, Rarity.RARE, mage.cards.w.WakeOfDestruction.class)); - cards.add(new SetCardInfo("Wall of Glare", 25, Rarity.COMMON, mage.cards.w.WallOfGlare.class)); - cards.add(new SetCardInfo("Wild Colos", 100, Rarity.COMMON, mage.cards.w.WildColos.class)); - cards.add(new SetCardInfo("Yavimaya Elder", 124, Rarity.COMMON, mage.cards.y.YavimayaElder.class)); - cards.add(new SetCardInfo("Yavimaya Enchantress", 125, Rarity.UNCOMMON, mage.cards.y.YavimayaEnchantress.class)); - cards.add(new SetCardInfo("Yavimaya Hollow", 143, Rarity.RARE, mage.cards.y.YavimayaHollow.class)); - cards.add(new SetCardInfo("Yawgmoth's Bargain", 75, Rarity.RARE, mage.cards.y.YawgmothsBargain.class)); - } -} + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author Backfir3 + */ +public final class UrzasDestiny extends ExpansionSet { + + private static final UrzasDestiny instance = new UrzasDestiny(); + + public static UrzasDestiny getInstance() { + return instance; + } + + private UrzasDestiny() { + super("Urza's Destiny", "UDS", ExpansionSet.buildDate(1999, 6, 7), SetType.EXPANSION); + this.blockName = "Urza"; + this.parentSet = UrzasSaga.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Academy Rector", 1, Rarity.RARE, mage.cards.a.AcademyRector.class)); + cards.add(new SetCardInfo("Aether Sting", 76, Rarity.UNCOMMON, mage.cards.a.AetherSting.class)); + cards.add(new SetCardInfo("Ancient Silverback", 101, Rarity.RARE, mage.cards.a.AncientSilverback.class)); + cards.add(new SetCardInfo("Apprentice Necromancer", 51, Rarity.RARE, mage.cards.a.ApprenticeNecromancer.class)); + cards.add(new SetCardInfo("Archery Training", 2, Rarity.UNCOMMON, mage.cards.a.ArcheryTraining.class)); + cards.add(new SetCardInfo("Attrition", 52, Rarity.RARE, mage.cards.a.Attrition.class)); + cards.add(new SetCardInfo("Aura Thief", 26, Rarity.RARE, mage.cards.a.AuraThief.class)); + cards.add(new SetCardInfo("Blizzard Elemental", 27, Rarity.RARE, mage.cards.b.BlizzardElemental.class)); + cards.add(new SetCardInfo("Bloodshot Cyclops", 77, Rarity.RARE, mage.cards.b.BloodshotCyclops.class)); + cards.add(new SetCardInfo("Body Snatcher", 53, Rarity.RARE, mage.cards.b.BodySnatcher.class)); + cards.add(new SetCardInfo("Braidwood Cup", 126, Rarity.UNCOMMON, mage.cards.b.BraidwoodCup.class)); + cards.add(new SetCardInfo("Braidwood Sextant", 127, Rarity.UNCOMMON, mage.cards.b.BraidwoodSextant.class)); + cards.add(new SetCardInfo("Brass Secretary", 128, Rarity.UNCOMMON, mage.cards.b.BrassSecretary.class)); + cards.add(new SetCardInfo("Brine Seer", 28, Rarity.UNCOMMON, mage.cards.b.BrineSeer.class)); + cards.add(new SetCardInfo("Bubbling Beebles", 29, Rarity.COMMON, mage.cards.b.BubblingBeebles.class)); + cards.add(new SetCardInfo("Bubbling Muck", 54, Rarity.COMMON, mage.cards.b.BubblingMuck.class)); + cards.add(new SetCardInfo("Caltrops", 129, Rarity.UNCOMMON, mage.cards.c.Caltrops.class)); + cards.add(new SetCardInfo("Capashen Knight", 3, Rarity.COMMON, mage.cards.c.CapashenKnight.class)); + cards.add(new SetCardInfo("Capashen Standard", 4, Rarity.COMMON, mage.cards.c.CapashenStandard.class)); + cards.add(new SetCardInfo("Capashen Templar", 5, Rarity.COMMON, mage.cards.c.CapashenTemplar.class)); + cards.add(new SetCardInfo("Carnival of Souls", 55, Rarity.RARE, mage.cards.c.CarnivalOfSouls.class)); + cards.add(new SetCardInfo("Chime of Night", 56, Rarity.COMMON, mage.cards.c.ChimeOfNight.class)); + cards.add(new SetCardInfo("Cinder Seer", 78, Rarity.UNCOMMON, mage.cards.c.CinderSeer.class)); + cards.add(new SetCardInfo("Colos Yearling", 79, Rarity.COMMON, mage.cards.c.ColosYearling.class)); + cards.add(new SetCardInfo("Compost", 102, Rarity.UNCOMMON, mage.cards.c.Compost.class)); + cards.add(new SetCardInfo("Covetous Dragon", 80, Rarity.RARE, mage.cards.c.CovetousDragon.class)); + cards.add(new SetCardInfo("Disappear", 30, Rarity.UNCOMMON, mage.cards.d.Disappear.class)); + cards.add(new SetCardInfo("Disease Carriers", 57, Rarity.COMMON, mage.cards.d.DiseaseCarriers.class)); + cards.add(new SetCardInfo("Donate", 31, Rarity.RARE, mage.cards.d.Donate.class)); + cards.add(new SetCardInfo("Dying Wail", 58, Rarity.COMMON, mage.cards.d.DyingWail.class)); + cards.add(new SetCardInfo("Elvish Lookout", 103, Rarity.COMMON, mage.cards.e.ElvishLookout.class)); + cards.add(new SetCardInfo("Elvish Piper", 104, Rarity.RARE, mage.cards.e.ElvishPiper.class)); + cards.add(new SetCardInfo("Emperor Crocodile", 105, Rarity.RARE, mage.cards.e.EmperorCrocodile.class)); + cards.add(new SetCardInfo("Encroach", 59, Rarity.UNCOMMON, mage.cards.e.Encroach.class)); + cards.add(new SetCardInfo("Eradicate", 60, Rarity.UNCOMMON, mage.cards.e.Eradicate.class)); + cards.add(new SetCardInfo("Extruder", 130, Rarity.UNCOMMON, mage.cards.e.Extruder.class)); + cards.add(new SetCardInfo("False Prophet", 6, Rarity.RARE, mage.cards.f.FalseProphet.class)); + cards.add(new SetCardInfo("Fatigue", 32, Rarity.COMMON, mage.cards.f.Fatigue.class)); + cards.add(new SetCardInfo("Fend Off", 7, Rarity.COMMON, mage.cards.f.FendOff.class)); + cards.add(new SetCardInfo("Festering Wound", 61, Rarity.UNCOMMON, mage.cards.f.FesteringWound.class)); + cards.add(new SetCardInfo("Field Surgeon", 8, Rarity.COMMON, mage.cards.f.FieldSurgeon.class)); + cards.add(new SetCardInfo("Flame Jet", 81, Rarity.COMMON, mage.cards.f.FlameJet.class)); + cards.add(new SetCardInfo("Fledgling Osprey", 33, Rarity.COMMON, mage.cards.f.FledglingOsprey.class)); + cards.add(new SetCardInfo("Flicker", 9, Rarity.RARE, mage.cards.f.Flicker.class)); + cards.add(new SetCardInfo("Fodder Cannon", 131, Rarity.UNCOMMON, mage.cards.f.FodderCannon.class)); + cards.add(new SetCardInfo("Gamekeeper", 106, Rarity.UNCOMMON, mage.cards.g.Gamekeeper.class)); + cards.add(new SetCardInfo("Goblin Berserker", 82, Rarity.UNCOMMON, mage.cards.g.GoblinBerserker.class)); + cards.add(new SetCardInfo("Goblin Festival", 83, Rarity.RARE, mage.cards.g.GoblinFestival.class)); + cards.add(new SetCardInfo("Goblin Gardener", 84, Rarity.COMMON, mage.cards.g.GoblinGardener.class)); + cards.add(new SetCardInfo("Goblin Marshal", 85, Rarity.RARE, mage.cards.g.GoblinMarshal.class)); + cards.add(new SetCardInfo("Goblin Masons", 86, Rarity.COMMON, mage.cards.g.GoblinMasons.class)); + cards.add(new SetCardInfo("Goliath Beetle", 107, Rarity.COMMON, mage.cards.g.GoliathBeetle.class)); + cards.add(new SetCardInfo("Heart Warden", 108, Rarity.COMMON, mage.cards.h.HeartWarden.class)); + cards.add(new SetCardInfo("Hulking Ogre", 87, Rarity.COMMON, mage.cards.h.HulkingOgre.class)); + cards.add(new SetCardInfo("Hunting Moa", 109, Rarity.UNCOMMON, mage.cards.h.HuntingMoa.class)); + cards.add(new SetCardInfo("Illuminated Wings", 34, Rarity.COMMON, mage.cards.i.IlluminatedWings.class)); + cards.add(new SetCardInfo("Impatience", 88, Rarity.RARE, mage.cards.i.Impatience.class)); + cards.add(new SetCardInfo("Incendiary", 89, Rarity.UNCOMMON, mage.cards.i.Incendiary.class)); + cards.add(new SetCardInfo("Iridescent Drake", 35, Rarity.UNCOMMON, mage.cards.i.IridescentDrake.class)); + cards.add(new SetCardInfo("Ivy Seer", 110, Rarity.UNCOMMON, mage.cards.i.IvySeer.class)); + cards.add(new SetCardInfo("Jasmine Seer", 10, Rarity.UNCOMMON, mage.cards.j.JasmineSeer.class)); + cards.add(new SetCardInfo("Junk Diver", 132, Rarity.RARE, mage.cards.j.JunkDiver.class)); + cards.add(new SetCardInfo("Keldon Champion", 90, Rarity.UNCOMMON, mage.cards.k.KeldonChampion.class)); + cards.add(new SetCardInfo("Keldon Vandals", 91, Rarity.COMMON, mage.cards.k.KeldonVandals.class)); + cards.add(new SetCardInfo("Kingfisher", 36, Rarity.COMMON, mage.cards.k.Kingfisher.class)); + cards.add(new SetCardInfo("Landslide", 92, Rarity.UNCOMMON, mage.cards.l.Landslide.class)); + cards.add(new SetCardInfo("Lurking Jackals", 62, Rarity.UNCOMMON, mage.cards.l.LurkingJackals.class)); + cards.add(new SetCardInfo("Magnify", 111, Rarity.COMMON, mage.cards.m.Magnify.class)); + cards.add(new SetCardInfo("Mantis Engine", 133, Rarity.UNCOMMON, mage.cards.m.MantisEngine.class)); + cards.add(new SetCardInfo("Marker Beetles", 112, Rarity.COMMON, mage.cards.m.MarkerBeetles.class)); + cards.add(new SetCardInfo("Mark of Fury", 93, Rarity.COMMON, mage.cards.m.MarkOfFury.class)); + cards.add(new SetCardInfo("Mask of Law and Grace", 11, Rarity.COMMON, mage.cards.m.MaskOfLawAndGrace.class)); + cards.add(new SetCardInfo("Master Healer", 12, Rarity.RARE, mage.cards.m.MasterHealer.class)); + cards.add(new SetCardInfo("Masticore", 134, Rarity.RARE, mage.cards.m.Masticore.class)); + cards.add(new SetCardInfo("Mental Discipline", 37, Rarity.COMMON, mage.cards.m.MentalDiscipline.class)); + cards.add(new SetCardInfo("Metalworker", 135, Rarity.RARE, mage.cards.m.Metalworker.class)); + cards.add(new SetCardInfo("Metathran Elite", 38, Rarity.UNCOMMON, mage.cards.m.MetathranElite.class)); + cards.add(new SetCardInfo("Metathran Soldier", 39, Rarity.COMMON, mage.cards.m.MetathranSoldier.class)); + cards.add(new SetCardInfo("Momentum", 113, Rarity.UNCOMMON, mage.cards.m.Momentum.class)); + cards.add(new SetCardInfo("Multani's Decree", 114, Rarity.COMMON, mage.cards.m.MultanisDecree.class)); + cards.add(new SetCardInfo("Nightshade Seer", 63, Rarity.UNCOMMON, mage.cards.n.NightshadeSeer.class)); + cards.add(new SetCardInfo("Opalescence", 13, Rarity.RARE, mage.cards.o.Opalescence.class)); + cards.add(new SetCardInfo("Opposition", 40, Rarity.RARE, mage.cards.o.Opposition.class)); + cards.add(new SetCardInfo("Pattern of Rebirth", 115, Rarity.RARE, mage.cards.p.PatternOfRebirth.class)); + cards.add(new SetCardInfo("Phyrexian Monitor", 64, Rarity.COMMON, mage.cards.p.PhyrexianMonitor.class)); + cards.add(new SetCardInfo("Phyrexian Negator", 65, Rarity.RARE, mage.cards.p.PhyrexianNegator.class)); + cards.add(new SetCardInfo("Plague Dogs", 66, Rarity.UNCOMMON, mage.cards.p.PlagueDogs.class)); + cards.add(new SetCardInfo("Plated Spider", 116, Rarity.COMMON, mage.cards.p.PlatedSpider.class)); + cards.add(new SetCardInfo("Plow Under", 117, Rarity.RARE, mage.cards.p.PlowUnder.class)); + cards.add(new SetCardInfo("Powder Keg", 136, Rarity.RARE, mage.cards.p.PowderKeg.class)); + cards.add(new SetCardInfo("Private Research", 41, Rarity.UNCOMMON, mage.cards.p.PrivateResearch.class)); + cards.add(new SetCardInfo("Quash", 42, Rarity.UNCOMMON, mage.cards.q.Quash.class)); + cards.add(new SetCardInfo("Rapid Decay", 67, Rarity.RARE, mage.cards.r.RapidDecay.class)); + cards.add(new SetCardInfo("Ravenous Rats", 68, Rarity.COMMON, mage.cards.r.RavenousRats.class)); + cards.add(new SetCardInfo("Rayne, Academy Chancellor", 43, Rarity.RARE, mage.cards.r.RayneAcademyChancellor.class)); + cards.add(new SetCardInfo("Reckless Abandon", 94, Rarity.COMMON, mage.cards.r.RecklessAbandon.class)); + cards.add(new SetCardInfo("Reliquary Monk", 14, Rarity.COMMON, mage.cards.r.ReliquaryMonk.class)); + cards.add(new SetCardInfo("Repercussion", 95, Rarity.RARE, mage.cards.r.Repercussion.class)); + cards.add(new SetCardInfo("Replenish", 15, Rarity.RARE, mage.cards.r.Replenish.class)); + cards.add(new SetCardInfo("Rescue", 44, Rarity.COMMON, mage.cards.r.Rescue.class)); + cards.add(new SetCardInfo("Rofellos's Gift", 119, Rarity.COMMON, mage.cards.r.RofellossGift.class)); + cards.add(new SetCardInfo("Rofellos, Llanowar Emissary", 118, Rarity.RARE, mage.cards.r.RofellosLlanowarEmissary.class)); + cards.add(new SetCardInfo("Sanctimony", 16, Rarity.UNCOMMON, mage.cards.s.Sanctimony.class)); + cards.add(new SetCardInfo("Scent of Brine", 45, Rarity.COMMON, mage.cards.s.ScentOfBrine.class)); + cards.add(new SetCardInfo("Scent of Cinder", 96, Rarity.COMMON, mage.cards.s.ScentOfCinder.class)); + cards.add(new SetCardInfo("Scent of Ivy", 120, Rarity.COMMON, mage.cards.s.ScentOfIvy.class)); + cards.add(new SetCardInfo("Scent of Jasmine", 17, Rarity.COMMON, mage.cards.s.ScentOfJasmine.class)); + cards.add(new SetCardInfo("Scent of Nightshade", 69, Rarity.COMMON, mage.cards.s.ScentOfNightshade.class)); + cards.add(new SetCardInfo("Scour", 18, Rarity.UNCOMMON, mage.cards.s.Scour.class)); + cards.add(new SetCardInfo("Scrying Glass", 137, Rarity.RARE, mage.cards.s.ScryingGlass.class)); + cards.add(new SetCardInfo("Serra Advocate", 19, Rarity.UNCOMMON, mage.cards.s.SerraAdvocate.class)); + cards.add(new SetCardInfo("Sigil of Sleep", 46, Rarity.COMMON, mage.cards.s.SigilOfSleep.class)); + cards.add(new SetCardInfo("Skittering Horror", 70, Rarity.COMMON, mage.cards.s.SkitteringHorror.class)); + cards.add(new SetCardInfo("Slinking Skirge", 71, Rarity.COMMON, mage.cards.s.SlinkingSkirge.class)); + cards.add(new SetCardInfo("Solidarity", 20, Rarity.COMMON, mage.cards.s.Solidarity.class)); + cards.add(new SetCardInfo("Soul Feast", 72, Rarity.UNCOMMON, mage.cards.s.SoulFeast.class)); + cards.add(new SetCardInfo("Sowing Salt", 97, Rarity.UNCOMMON, mage.cards.s.SowingSalt.class)); + cards.add(new SetCardInfo("Splinter", 121, Rarity.UNCOMMON, mage.cards.s.Splinter.class)); + cards.add(new SetCardInfo("Squirming Mass", 73, Rarity.COMMON, mage.cards.s.SquirmingMass.class)); + cards.add(new SetCardInfo("Storage Matrix", 138, Rarity.RARE, mage.cards.s.StorageMatrix.class)); + cards.add(new SetCardInfo("Taunting Elf", 122, Rarity.COMMON, mage.cards.t.TauntingElf.class)); + cards.add(new SetCardInfo("Telepathic Spies", 47, Rarity.COMMON, mage.cards.t.TelepathicSpies.class)); + cards.add(new SetCardInfo("Temporal Adept", 48, Rarity.RARE, mage.cards.t.TemporalAdept.class)); + cards.add(new SetCardInfo("Tethered Griffin", 21, Rarity.RARE, mage.cards.t.TetheredGriffin.class)); + cards.add(new SetCardInfo("Thieving Magpie", 49, Rarity.UNCOMMON, mage.cards.t.ThievingMagpie.class)); + cards.add(new SetCardInfo("Thorn Elemental", 123, Rarity.RARE, mage.cards.t.ThornElemental.class)); + cards.add(new SetCardInfo("Thran Dynamo", 139, Rarity.UNCOMMON, mage.cards.t.ThranDynamo.class)); + cards.add(new SetCardInfo("Thran Foundry", 140, Rarity.UNCOMMON, mage.cards.t.ThranFoundry.class)); + cards.add(new SetCardInfo("Thran Golem", 141, Rarity.RARE, mage.cards.t.ThranGolem.class)); + cards.add(new SetCardInfo("Tormented Angel", 22, Rarity.COMMON, mage.cards.t.TormentedAngel.class)); + cards.add(new SetCardInfo("Treachery", 50, Rarity.RARE, mage.cards.t.Treachery.class)); + cards.add(new SetCardInfo("Trumpet Blast", 98, Rarity.COMMON, mage.cards.t.TrumpetBlast.class)); + cards.add(new SetCardInfo("Twisted Experiment", 74, Rarity.COMMON, mage.cards.t.TwistedExperiment.class)); + cards.add(new SetCardInfo("Urza's Incubator", 142, Rarity.RARE, mage.cards.u.UrzasIncubator.class)); + cards.add(new SetCardInfo("Voice of Duty", 23, Rarity.UNCOMMON, mage.cards.v.VoiceOfDuty.class)); + cards.add(new SetCardInfo("Voice of Reason", 24, Rarity.UNCOMMON, mage.cards.v.VoiceOfReason.class)); + cards.add(new SetCardInfo("Wake of Destruction", 99, Rarity.RARE, mage.cards.w.WakeOfDestruction.class)); + cards.add(new SetCardInfo("Wall of Glare", 25, Rarity.COMMON, mage.cards.w.WallOfGlare.class)); + cards.add(new SetCardInfo("Wild Colos", 100, Rarity.COMMON, mage.cards.w.WildColos.class)); + cards.add(new SetCardInfo("Yavimaya Elder", 124, Rarity.COMMON, mage.cards.y.YavimayaElder.class)); + cards.add(new SetCardInfo("Yavimaya Enchantress", 125, Rarity.UNCOMMON, mage.cards.y.YavimayaEnchantress.class)); + cards.add(new SetCardInfo("Yavimaya Hollow", 143, Rarity.RARE, mage.cards.y.YavimayaHollow.class)); + cards.add(new SetCardInfo("Yawgmoth's Bargain", 75, Rarity.RARE, mage.cards.y.YawgmothsBargain.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/UrzasLegacy.java b/Mage.Sets/src/mage/sets/UrzasLegacy.java index 431d2eb3f8..922af28dc0 100644 --- a/Mage.Sets/src/mage/sets/UrzasLegacy.java +++ b/Mage.Sets/src/mage/sets/UrzasLegacy.java @@ -1,175 +1,175 @@ - -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author noxx - */ -public final class UrzasLegacy extends ExpansionSet { - - private static final UrzasLegacy instance = new UrzasLegacy(); - - public static UrzasLegacy getInstance() { - return instance; - } - - private UrzasLegacy() { - super("Urza's Legacy", "ULG", ExpansionSet.buildDate(1999, 2, 15), SetType.EXPANSION); - this.blockName = "Urza"; - this.parentSet = UrzasSaga.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("About Face", 73, Rarity.COMMON, mage.cards.a.AboutFace.class)); - cards.add(new SetCardInfo("Angel's Trumpet", 121, Rarity.UNCOMMON, mage.cards.a.AngelsTrumpet.class)); - cards.add(new SetCardInfo("Angelic Curator", 1, Rarity.COMMON, mage.cards.a.AngelicCurator.class)); - cards.add(new SetCardInfo("Anthroplasm", 25, Rarity.RARE, mage.cards.a.Anthroplasm.class)); - cards.add(new SetCardInfo("Archivist", 26, Rarity.RARE, mage.cards.a.Archivist.class)); - cards.add(new SetCardInfo("Aura Flux", 27, Rarity.COMMON, mage.cards.a.AuraFlux.class)); - cards.add(new SetCardInfo("Avalanche Riders", 74, Rarity.UNCOMMON, mage.cards.a.AvalancheRiders.class)); - cards.add(new SetCardInfo("Beast of Burden", 122, Rarity.RARE, mage.cards.b.BeastOfBurden.class)); - cards.add(new SetCardInfo("Blessed Reversal", 2, Rarity.RARE, mage.cards.b.BlessedReversal.class)); - cards.add(new SetCardInfo("Bloated Toad", 97, Rarity.UNCOMMON, mage.cards.b.BloatedToad.class)); - cards.add(new SetCardInfo("Bone Shredder", 49, Rarity.UNCOMMON, mage.cards.b.BoneShredder.class)); - cards.add(new SetCardInfo("Bouncing Beebles", 28, Rarity.COMMON, mage.cards.b.BouncingBeebles.class)); - cards.add(new SetCardInfo("Brink of Madness", 50, Rarity.RARE, mage.cards.b.BrinkOfMadness.class)); - cards.add(new SetCardInfo("Burst of Energy", 3, Rarity.COMMON, mage.cards.b.BurstOfEnergy.class)); - cards.add(new SetCardInfo("Cessation", 4, Rarity.COMMON, mage.cards.c.Cessation.class)); - cards.add(new SetCardInfo("Cloud of Faeries", 29, Rarity.COMMON, mage.cards.c.CloudOfFaeries.class)); - cards.add(new SetCardInfo("Crawlspace", 123, Rarity.RARE, mage.cards.c.Crawlspace.class)); - cards.add(new SetCardInfo("Crop Rotation", 98, Rarity.COMMON, mage.cards.c.CropRotation.class)); - cards.add(new SetCardInfo("Damping Engine", 124, Rarity.RARE, mage.cards.d.DampingEngine.class)); - cards.add(new SetCardInfo("Darkwatch Elves", 99, Rarity.UNCOMMON, mage.cards.d.DarkwatchElves.class)); - cards.add(new SetCardInfo("Defender of Chaos", 75, Rarity.COMMON, mage.cards.d.DefenderOfChaos.class)); - cards.add(new SetCardInfo("Defender of Law", 5, Rarity.COMMON, mage.cards.d.DefenderOfLaw.class)); - cards.add(new SetCardInfo("Defense Grid", 125, Rarity.RARE, mage.cards.d.DefenseGrid.class)); - cards.add(new SetCardInfo("Defense of the Heart", 100, Rarity.RARE, mage.cards.d.DefenseOfTheHeart.class)); - cards.add(new SetCardInfo("Delusions of Mediocrity", 30, Rarity.RARE, mage.cards.d.DelusionsOfMediocrity.class)); - cards.add(new SetCardInfo("Deranged Hermit", 101, Rarity.RARE, mage.cards.d.DerangedHermit.class)); - cards.add(new SetCardInfo("Devout Harpist", 6, Rarity.COMMON, mage.cards.d.DevoutHarpist.class)); - cards.add(new SetCardInfo("Engineered Plague", 51, Rarity.UNCOMMON, mage.cards.e.EngineeredPlague.class)); - cards.add(new SetCardInfo("Erase", 7, Rarity.COMMON, mage.cards.e.Erase.class)); - cards.add(new SetCardInfo("Eviscerator", 52, Rarity.RARE, mage.cards.e.Eviscerator.class)); - cards.add(new SetCardInfo("Expendable Troops", 8, Rarity.COMMON, mage.cards.e.ExpendableTroops.class)); - cards.add(new SetCardInfo("Faerie Conclave", 139, Rarity.UNCOMMON, mage.cards.f.FaerieConclave.class)); - cards.add(new SetCardInfo("Fleeting Image", 31, Rarity.RARE, mage.cards.f.FleetingImage.class)); - cards.add(new SetCardInfo("Fog of Gnats", 53, Rarity.COMMON, mage.cards.f.FogOfGnats.class)); - cards.add(new SetCardInfo("Forbidding Watchtower", 140, Rarity.UNCOMMON, mage.cards.f.ForbiddingWatchtower.class)); - cards.add(new SetCardInfo("Frantic Search", 32, Rarity.COMMON, mage.cards.f.FranticSearch.class)); - cards.add(new SetCardInfo("Gang of Elk", 102, Rarity.UNCOMMON, mage.cards.g.GangOfElk.class)); - cards.add(new SetCardInfo("Ghitu Encampment", 141, Rarity.UNCOMMON, mage.cards.g.GhituEncampment.class)); - cards.add(new SetCardInfo("Ghitu Fire-Eater", 76, Rarity.UNCOMMON, mage.cards.g.GhituFireEater.class)); - cards.add(new SetCardInfo("Ghitu Slinger", 77, Rarity.COMMON, mage.cards.g.GhituSlinger.class)); - cards.add(new SetCardInfo("Ghitu War Cry", 78, Rarity.UNCOMMON, mage.cards.g.GhituWarCry.class)); - cards.add(new SetCardInfo("Giant Cockroach", 54, Rarity.COMMON, mage.cards.g.GiantCockroach.class)); - cards.add(new SetCardInfo("Goblin Medics", 79, Rarity.COMMON, mage.cards.g.GoblinMedics.class)); - cards.add(new SetCardInfo("Goblin Welder", 80, Rarity.RARE, mage.cards.g.GoblinWelder.class)); - cards.add(new SetCardInfo("Granite Grip", 81, Rarity.COMMON, mage.cards.g.GraniteGrip.class)); - cards.add(new SetCardInfo("Grim Monolith", 126, Rarity.RARE, mage.cards.g.GrimMonolith.class)); - cards.add(new SetCardInfo("Harmonic Convergence", 103, Rarity.UNCOMMON, mage.cards.h.HarmonicConvergence.class)); - cards.add(new SetCardInfo("Hidden Gibbons", 104, Rarity.RARE, mage.cards.h.HiddenGibbons.class)); - cards.add(new SetCardInfo("Hope and Glory", 9, Rarity.UNCOMMON, mage.cards.h.HopeAndGlory.class)); - cards.add(new SetCardInfo("Impending Disaster", 82, Rarity.RARE, mage.cards.i.ImpendingDisaster.class)); - cards.add(new SetCardInfo("Intervene", 33, Rarity.COMMON, mage.cards.i.Intervene.class)); - cards.add(new SetCardInfo("Iron Maiden", 127, Rarity.RARE, mage.cards.i.IronMaiden.class)); - cards.add(new SetCardInfo("Iron Will", 10, Rarity.COMMON, mage.cards.i.IronWill.class)); - cards.add(new SetCardInfo("Jhoira's Toolbox", 128, Rarity.UNCOMMON, mage.cards.j.JhoirasToolbox.class)); - cards.add(new SetCardInfo("Karmic Guide", 11, Rarity.RARE, mage.cards.k.KarmicGuide.class)); - cards.add(new SetCardInfo("King Crab", 34, Rarity.UNCOMMON, mage.cards.k.KingCrab.class)); - cards.add(new SetCardInfo("Knighthood", 12, Rarity.UNCOMMON, mage.cards.k.Knighthood.class)); - cards.add(new SetCardInfo("Last-Ditch Effort", 83, Rarity.UNCOMMON, mage.cards.l.LastDitchEffort.class)); - cards.add(new SetCardInfo("Lava Axe", 84, Rarity.COMMON, mage.cards.l.LavaAxe.class)); - cards.add(new SetCardInfo("Levitation", 35, Rarity.UNCOMMON, mage.cards.l.Levitation.class)); - cards.add(new SetCardInfo("Lone Wolf", 105, Rarity.UNCOMMON, mage.cards.l.LoneWolf.class)); - cards.add(new SetCardInfo("Lurking Skirge", 55, Rarity.RARE, mage.cards.l.LurkingSkirge.class)); - cards.add(new SetCardInfo("Martyr's Cause", 13, Rarity.UNCOMMON, mage.cards.m.MartyrsCause.class)); - cards.add(new SetCardInfo("Memory Jar", 129, Rarity.RARE, mage.cards.m.MemoryJar.class)); - cards.add(new SetCardInfo("Might of Oaks", 106, Rarity.RARE, mage.cards.m.MightOfOaks.class)); - cards.add(new SetCardInfo("Miscalculation", 36, Rarity.COMMON, mage.cards.m.Miscalculation.class)); - cards.add(new SetCardInfo("Molten Hydra", 85, Rarity.RARE, mage.cards.m.MoltenHydra.class)); - cards.add(new SetCardInfo("Mother of Runes", 14, Rarity.UNCOMMON, mage.cards.m.MotherOfRunes.class)); - cards.add(new SetCardInfo("Multani's Acolyte", 108, Rarity.COMMON, mage.cards.m.MultanisAcolyte.class)); - cards.add(new SetCardInfo("Multani's Presence", 109, Rarity.UNCOMMON, mage.cards.m.MultanisPresence.class)); - cards.add(new SetCardInfo("Multani, Maro-Sorcerer", 107, Rarity.RARE, mage.cards.m.MultaniMaroSorcerer.class)); - cards.add(new SetCardInfo("No Mercy", 56, Rarity.RARE, mage.cards.n.NoMercy.class)); - cards.add(new SetCardInfo("Opal Avenger", 15, Rarity.RARE, mage.cards.o.OpalAvenger.class)); - cards.add(new SetCardInfo("Opal Champion", 16, Rarity.COMMON, mage.cards.o.OpalChampion.class)); - cards.add(new SetCardInfo("Opportunity", 37, Rarity.UNCOMMON, mage.cards.o.Opportunity.class)); - cards.add(new SetCardInfo("Ostracize", 57, Rarity.COMMON, mage.cards.o.Ostracize.class)); - cards.add(new SetCardInfo("Palinchron", 38, Rarity.RARE, mage.cards.p.Palinchron.class)); - cards.add(new SetCardInfo("Parch", 86, Rarity.COMMON, mage.cards.p.Parch.class)); - cards.add(new SetCardInfo("Peace and Quiet", 17, Rarity.UNCOMMON, mage.cards.p.PeaceAndQuiet.class)); - cards.add(new SetCardInfo("Phyrexian Broodlings", 58, Rarity.COMMON, mage.cards.p.PhyrexianBroodlings.class)); - cards.add(new SetCardInfo("Phyrexian Debaser", 59, Rarity.COMMON, mage.cards.p.PhyrexianDebaser.class)); - cards.add(new SetCardInfo("Phyrexian Defiler", 60, Rarity.UNCOMMON, mage.cards.p.PhyrexianDefiler.class)); - cards.add(new SetCardInfo("Phyrexian Denouncer", 61, Rarity.COMMON, mage.cards.p.PhyrexianDenouncer.class)); - cards.add(new SetCardInfo("Phyrexian Plaguelord", 62, Rarity.RARE, mage.cards.p.PhyrexianPlaguelord.class)); - cards.add(new SetCardInfo("Phyrexian Reclamation", 63, Rarity.UNCOMMON, mage.cards.p.PhyrexianReclamation.class)); - cards.add(new SetCardInfo("Plague Beetle", 64, Rarity.COMMON, mage.cards.p.PlagueBeetle.class)); - cards.add(new SetCardInfo("Planar Collapse", 18, Rarity.RARE, mage.cards.p.PlanarCollapse.class)); - cards.add(new SetCardInfo("Purify", 19, Rarity.RARE, mage.cards.p.Purify.class)); - cards.add(new SetCardInfo("Pygmy Pyrosaur", 87, Rarity.COMMON, mage.cards.p.PygmyPyrosaur.class)); - cards.add(new SetCardInfo("Pyromancy", 88, Rarity.RARE, mage.cards.p.Pyromancy.class)); - cards.add(new SetCardInfo("Quicksilver Amulet", 130, Rarity.RARE, mage.cards.q.QuicksilverAmulet.class)); - cards.add(new SetCardInfo("Rack and Ruin", 89, Rarity.UNCOMMON, mage.cards.r.RackAndRuin.class)); - cards.add(new SetCardInfo("Radiant's Dragoons", 21, Rarity.UNCOMMON, mage.cards.r.RadiantsDragoons.class)); - cards.add(new SetCardInfo("Radiant's Judgment", 22, Rarity.COMMON, mage.cards.r.RadiantsJudgment.class)); - cards.add(new SetCardInfo("Radiant, Archangel", 20, Rarity.RARE, mage.cards.r.RadiantArchangel.class)); - cards.add(new SetCardInfo("Rancor", 110, Rarity.COMMON, mage.cards.r.Rancor.class)); - cards.add(new SetCardInfo("Rank and File", 65, Rarity.UNCOMMON, mage.cards.r.RankAndFile.class)); - cards.add(new SetCardInfo("Raven Familiar", 39, Rarity.UNCOMMON, mage.cards.r.RavenFamiliar.class)); - cards.add(new SetCardInfo("Rebuild", 40, Rarity.UNCOMMON, mage.cards.r.Rebuild.class)); - cards.add(new SetCardInfo("Repopulate", 111, Rarity.COMMON, mage.cards.r.Repopulate.class)); - cards.add(new SetCardInfo("Ring of Gix", 131, Rarity.RARE, mage.cards.r.RingOfGix.class)); - cards.add(new SetCardInfo("Rivalry", 90, Rarity.RARE, mage.cards.r.Rivalry.class)); - cards.add(new SetCardInfo("Scrapheap", 132, Rarity.RARE, mage.cards.s.Scrapheap.class)); - cards.add(new SetCardInfo("Second Chance", 41, Rarity.RARE, mage.cards.s.SecondChance.class)); - cards.add(new SetCardInfo("Shivan Phoenix", 91, Rarity.RARE, mage.cards.s.ShivanPhoenix.class)); - cards.add(new SetCardInfo("Sick and Tired", 66, Rarity.COMMON, mage.cards.s.SickAndTired.class)); - cards.add(new SetCardInfo("Silk Net", 112, Rarity.COMMON, mage.cards.s.SilkNet.class)); - cards.add(new SetCardInfo("Simian Grunts", 113, Rarity.COMMON, mage.cards.s.SimianGrunts.class)); - cards.add(new SetCardInfo("Sleeper's Guile", 67, Rarity.COMMON, mage.cards.s.SleepersGuile.class)); - cards.add(new SetCardInfo("Slow Motion", 42, Rarity.COMMON, mage.cards.s.SlowMotion.class)); - cards.add(new SetCardInfo("Sluggishness", 92, Rarity.COMMON, mage.cards.s.Sluggishness.class)); - cards.add(new SetCardInfo("Snap", 43, Rarity.COMMON, mage.cards.s.Snap.class)); - cards.add(new SetCardInfo("Spawning Pool", 142, Rarity.UNCOMMON, mage.cards.s.SpawningPool.class)); - cards.add(new SetCardInfo("Subversion", 68, Rarity.RARE, mage.cards.s.Subversion.class)); - cards.add(new SetCardInfo("Sustainer of the Realm", 23, Rarity.UNCOMMON, mage.cards.s.SustainerOfTheRealm.class)); - cards.add(new SetCardInfo("Swat", 69, Rarity.COMMON, mage.cards.s.Swat.class)); - cards.add(new SetCardInfo("Tethered Skirge", 70, Rarity.UNCOMMON, mage.cards.t.TetheredSkirge.class)); - cards.add(new SetCardInfo("Thornwind Faeries", 44, Rarity.COMMON, mage.cards.t.ThornwindFaeries.class)); - cards.add(new SetCardInfo("Thran Lens", 133, Rarity.RARE, mage.cards.t.ThranLens.class)); - cards.add(new SetCardInfo("Thran War Machine", 134, Rarity.UNCOMMON, mage.cards.t.ThranWarMachine.class)); - cards.add(new SetCardInfo("Thran Weaponry", 135, Rarity.RARE, mage.cards.t.ThranWeaponry.class)); - cards.add(new SetCardInfo("Ticking Gnomes", 136, Rarity.UNCOMMON, mage.cards.t.TickingGnomes.class)); - cards.add(new SetCardInfo("Tinker", 45, Rarity.UNCOMMON, mage.cards.t.Tinker.class)); - cards.add(new SetCardInfo("Tragic Poet", 24, Rarity.COMMON, mage.cards.t.TragicPoet.class)); - cards.add(new SetCardInfo("Treacherous Link", 71, Rarity.UNCOMMON, mage.cards.t.TreacherousLink.class)); - cards.add(new SetCardInfo("Treefolk Mystic", 114, Rarity.COMMON, mage.cards.t.TreefolkMystic.class)); - cards.add(new SetCardInfo("Treetop Village", 143, Rarity.UNCOMMON, mage.cards.t.TreetopVillage.class)); - cards.add(new SetCardInfo("Unearth", 72, Rarity.COMMON, mage.cards.u.Unearth.class)); - cards.add(new SetCardInfo("Urza's Blueprints", 137, Rarity.RARE, mage.cards.u.UrzasBlueprints.class)); - cards.add(new SetCardInfo("Viashino Bey", 93, Rarity.COMMON, mage.cards.v.ViashinoBey.class)); - cards.add(new SetCardInfo("Viashino Cutthroat", 94, Rarity.UNCOMMON, mage.cards.v.ViashinoCutthroat.class)); - cards.add(new SetCardInfo("Viashino Heretic", 95, Rarity.UNCOMMON, mage.cards.v.ViashinoHeretic.class)); - cards.add(new SetCardInfo("Viashino Sandscout", 96, Rarity.COMMON, mage.cards.v.ViashinoSandscout.class)); - cards.add(new SetCardInfo("Vigilant Drake", 46, Rarity.COMMON, mage.cards.v.VigilantDrake.class)); - cards.add(new SetCardInfo("Walking Sponge", 47, Rarity.UNCOMMON, mage.cards.w.WalkingSponge.class)); - cards.add(new SetCardInfo("Weatherseed Elf", 115, Rarity.COMMON, mage.cards.w.WeatherseedElf.class)); - cards.add(new SetCardInfo("Weatherseed Faeries", 48, Rarity.COMMON, mage.cards.w.WeatherseedFaeries.class)); - cards.add(new SetCardInfo("Weatherseed Treefolk", 116, Rarity.RARE, mage.cards.w.WeatherseedTreefolk.class)); - cards.add(new SetCardInfo("Wheel of Torture", 138, Rarity.RARE, mage.cards.w.WheelOfTorture.class)); - cards.add(new SetCardInfo("Wing Snare", 117, Rarity.UNCOMMON, mage.cards.w.WingSnare.class)); - cards.add(new SetCardInfo("Yavimaya Granger", 118, Rarity.COMMON, mage.cards.y.YavimayaGranger.class)); - cards.add(new SetCardInfo("Yavimaya Scion", 119, Rarity.COMMON, mage.cards.y.YavimayaScion.class)); - cards.add(new SetCardInfo("Yavimaya Wurm", 120, Rarity.COMMON, mage.cards.y.YavimayaWurm.class)); - } -} + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author noxx + */ +public final class UrzasLegacy extends ExpansionSet { + + private static final UrzasLegacy instance = new UrzasLegacy(); + + public static UrzasLegacy getInstance() { + return instance; + } + + private UrzasLegacy() { + super("Urza's Legacy", "ULG", ExpansionSet.buildDate(1999, 2, 15), SetType.EXPANSION); + this.blockName = "Urza"; + this.parentSet = UrzasSaga.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("About Face", 73, Rarity.COMMON, mage.cards.a.AboutFace.class)); + cards.add(new SetCardInfo("Angel's Trumpet", 121, Rarity.UNCOMMON, mage.cards.a.AngelsTrumpet.class)); + cards.add(new SetCardInfo("Angelic Curator", 1, Rarity.COMMON, mage.cards.a.AngelicCurator.class)); + cards.add(new SetCardInfo("Anthroplasm", 25, Rarity.RARE, mage.cards.a.Anthroplasm.class)); + cards.add(new SetCardInfo("Archivist", 26, Rarity.RARE, mage.cards.a.Archivist.class)); + cards.add(new SetCardInfo("Aura Flux", 27, Rarity.COMMON, mage.cards.a.AuraFlux.class)); + cards.add(new SetCardInfo("Avalanche Riders", 74, Rarity.UNCOMMON, mage.cards.a.AvalancheRiders.class)); + cards.add(new SetCardInfo("Beast of Burden", 122, Rarity.RARE, mage.cards.b.BeastOfBurden.class)); + cards.add(new SetCardInfo("Blessed Reversal", 2, Rarity.RARE, mage.cards.b.BlessedReversal.class)); + cards.add(new SetCardInfo("Bloated Toad", 97, Rarity.UNCOMMON, mage.cards.b.BloatedToad.class)); + cards.add(new SetCardInfo("Bone Shredder", 49, Rarity.UNCOMMON, mage.cards.b.BoneShredder.class)); + cards.add(new SetCardInfo("Bouncing Beebles", 28, Rarity.COMMON, mage.cards.b.BouncingBeebles.class)); + cards.add(new SetCardInfo("Brink of Madness", 50, Rarity.RARE, mage.cards.b.BrinkOfMadness.class)); + cards.add(new SetCardInfo("Burst of Energy", 3, Rarity.COMMON, mage.cards.b.BurstOfEnergy.class)); + cards.add(new SetCardInfo("Cessation", 4, Rarity.COMMON, mage.cards.c.Cessation.class)); + cards.add(new SetCardInfo("Cloud of Faeries", 29, Rarity.COMMON, mage.cards.c.CloudOfFaeries.class)); + cards.add(new SetCardInfo("Crawlspace", 123, Rarity.RARE, mage.cards.c.Crawlspace.class)); + cards.add(new SetCardInfo("Crop Rotation", 98, Rarity.COMMON, mage.cards.c.CropRotation.class)); + cards.add(new SetCardInfo("Damping Engine", 124, Rarity.RARE, mage.cards.d.DampingEngine.class)); + cards.add(new SetCardInfo("Darkwatch Elves", 99, Rarity.UNCOMMON, mage.cards.d.DarkwatchElves.class)); + cards.add(new SetCardInfo("Defender of Chaos", 75, Rarity.COMMON, mage.cards.d.DefenderOfChaos.class)); + cards.add(new SetCardInfo("Defender of Law", 5, Rarity.COMMON, mage.cards.d.DefenderOfLaw.class)); + cards.add(new SetCardInfo("Defense Grid", 125, Rarity.RARE, mage.cards.d.DefenseGrid.class)); + cards.add(new SetCardInfo("Defense of the Heart", 100, Rarity.RARE, mage.cards.d.DefenseOfTheHeart.class)); + cards.add(new SetCardInfo("Delusions of Mediocrity", 30, Rarity.RARE, mage.cards.d.DelusionsOfMediocrity.class)); + cards.add(new SetCardInfo("Deranged Hermit", 101, Rarity.RARE, mage.cards.d.DerangedHermit.class)); + cards.add(new SetCardInfo("Devout Harpist", 6, Rarity.COMMON, mage.cards.d.DevoutHarpist.class)); + cards.add(new SetCardInfo("Engineered Plague", 51, Rarity.UNCOMMON, mage.cards.e.EngineeredPlague.class)); + cards.add(new SetCardInfo("Erase", 7, Rarity.COMMON, mage.cards.e.Erase.class)); + cards.add(new SetCardInfo("Eviscerator", 52, Rarity.RARE, mage.cards.e.Eviscerator.class)); + cards.add(new SetCardInfo("Expendable Troops", 8, Rarity.COMMON, mage.cards.e.ExpendableTroops.class)); + cards.add(new SetCardInfo("Faerie Conclave", 139, Rarity.UNCOMMON, mage.cards.f.FaerieConclave.class)); + cards.add(new SetCardInfo("Fleeting Image", 31, Rarity.RARE, mage.cards.f.FleetingImage.class)); + cards.add(new SetCardInfo("Fog of Gnats", 53, Rarity.COMMON, mage.cards.f.FogOfGnats.class)); + cards.add(new SetCardInfo("Forbidding Watchtower", 140, Rarity.UNCOMMON, mage.cards.f.ForbiddingWatchtower.class)); + cards.add(new SetCardInfo("Frantic Search", 32, Rarity.COMMON, mage.cards.f.FranticSearch.class)); + cards.add(new SetCardInfo("Gang of Elk", 102, Rarity.UNCOMMON, mage.cards.g.GangOfElk.class)); + cards.add(new SetCardInfo("Ghitu Encampment", 141, Rarity.UNCOMMON, mage.cards.g.GhituEncampment.class)); + cards.add(new SetCardInfo("Ghitu Fire-Eater", 76, Rarity.UNCOMMON, mage.cards.g.GhituFireEater.class)); + cards.add(new SetCardInfo("Ghitu Slinger", 77, Rarity.COMMON, mage.cards.g.GhituSlinger.class)); + cards.add(new SetCardInfo("Ghitu War Cry", 78, Rarity.UNCOMMON, mage.cards.g.GhituWarCry.class)); + cards.add(new SetCardInfo("Giant Cockroach", 54, Rarity.COMMON, mage.cards.g.GiantCockroach.class)); + cards.add(new SetCardInfo("Goblin Medics", 79, Rarity.COMMON, mage.cards.g.GoblinMedics.class)); + cards.add(new SetCardInfo("Goblin Welder", 80, Rarity.RARE, mage.cards.g.GoblinWelder.class)); + cards.add(new SetCardInfo("Granite Grip", 81, Rarity.COMMON, mage.cards.g.GraniteGrip.class)); + cards.add(new SetCardInfo("Grim Monolith", 126, Rarity.RARE, mage.cards.g.GrimMonolith.class)); + cards.add(new SetCardInfo("Harmonic Convergence", 103, Rarity.UNCOMMON, mage.cards.h.HarmonicConvergence.class)); + cards.add(new SetCardInfo("Hidden Gibbons", 104, Rarity.RARE, mage.cards.h.HiddenGibbons.class)); + cards.add(new SetCardInfo("Hope and Glory", 9, Rarity.UNCOMMON, mage.cards.h.HopeAndGlory.class)); + cards.add(new SetCardInfo("Impending Disaster", 82, Rarity.RARE, mage.cards.i.ImpendingDisaster.class)); + cards.add(new SetCardInfo("Intervene", 33, Rarity.COMMON, mage.cards.i.Intervene.class)); + cards.add(new SetCardInfo("Iron Maiden", 127, Rarity.RARE, mage.cards.i.IronMaiden.class)); + cards.add(new SetCardInfo("Iron Will", 10, Rarity.COMMON, mage.cards.i.IronWill.class)); + cards.add(new SetCardInfo("Jhoira's Toolbox", 128, Rarity.UNCOMMON, mage.cards.j.JhoirasToolbox.class)); + cards.add(new SetCardInfo("Karmic Guide", 11, Rarity.RARE, mage.cards.k.KarmicGuide.class)); + cards.add(new SetCardInfo("King Crab", 34, Rarity.UNCOMMON, mage.cards.k.KingCrab.class)); + cards.add(new SetCardInfo("Knighthood", 12, Rarity.UNCOMMON, mage.cards.k.Knighthood.class)); + cards.add(new SetCardInfo("Last-Ditch Effort", 83, Rarity.UNCOMMON, mage.cards.l.LastDitchEffort.class)); + cards.add(new SetCardInfo("Lava Axe", 84, Rarity.COMMON, mage.cards.l.LavaAxe.class)); + cards.add(new SetCardInfo("Levitation", 35, Rarity.UNCOMMON, mage.cards.l.Levitation.class)); + cards.add(new SetCardInfo("Lone Wolf", 105, Rarity.UNCOMMON, mage.cards.l.LoneWolf.class)); + cards.add(new SetCardInfo("Lurking Skirge", 55, Rarity.RARE, mage.cards.l.LurkingSkirge.class)); + cards.add(new SetCardInfo("Martyr's Cause", 13, Rarity.UNCOMMON, mage.cards.m.MartyrsCause.class)); + cards.add(new SetCardInfo("Memory Jar", 129, Rarity.RARE, mage.cards.m.MemoryJar.class)); + cards.add(new SetCardInfo("Might of Oaks", 106, Rarity.RARE, mage.cards.m.MightOfOaks.class)); + cards.add(new SetCardInfo("Miscalculation", 36, Rarity.COMMON, mage.cards.m.Miscalculation.class)); + cards.add(new SetCardInfo("Molten Hydra", 85, Rarity.RARE, mage.cards.m.MoltenHydra.class)); + cards.add(new SetCardInfo("Mother of Runes", 14, Rarity.UNCOMMON, mage.cards.m.MotherOfRunes.class)); + cards.add(new SetCardInfo("Multani's Acolyte", 108, Rarity.COMMON, mage.cards.m.MultanisAcolyte.class)); + cards.add(new SetCardInfo("Multani's Presence", 109, Rarity.UNCOMMON, mage.cards.m.MultanisPresence.class)); + cards.add(new SetCardInfo("Multani, Maro-Sorcerer", 107, Rarity.RARE, mage.cards.m.MultaniMaroSorcerer.class)); + cards.add(new SetCardInfo("No Mercy", 56, Rarity.RARE, mage.cards.n.NoMercy.class)); + cards.add(new SetCardInfo("Opal Avenger", 15, Rarity.RARE, mage.cards.o.OpalAvenger.class)); + cards.add(new SetCardInfo("Opal Champion", 16, Rarity.COMMON, mage.cards.o.OpalChampion.class)); + cards.add(new SetCardInfo("Opportunity", 37, Rarity.UNCOMMON, mage.cards.o.Opportunity.class)); + cards.add(new SetCardInfo("Ostracize", 57, Rarity.COMMON, mage.cards.o.Ostracize.class)); + cards.add(new SetCardInfo("Palinchron", 38, Rarity.RARE, mage.cards.p.Palinchron.class)); + cards.add(new SetCardInfo("Parch", 86, Rarity.COMMON, mage.cards.p.Parch.class)); + cards.add(new SetCardInfo("Peace and Quiet", 17, Rarity.UNCOMMON, mage.cards.p.PeaceAndQuiet.class)); + cards.add(new SetCardInfo("Phyrexian Broodlings", 58, Rarity.COMMON, mage.cards.p.PhyrexianBroodlings.class)); + cards.add(new SetCardInfo("Phyrexian Debaser", 59, Rarity.COMMON, mage.cards.p.PhyrexianDebaser.class)); + cards.add(new SetCardInfo("Phyrexian Defiler", 60, Rarity.UNCOMMON, mage.cards.p.PhyrexianDefiler.class)); + cards.add(new SetCardInfo("Phyrexian Denouncer", 61, Rarity.COMMON, mage.cards.p.PhyrexianDenouncer.class)); + cards.add(new SetCardInfo("Phyrexian Plaguelord", 62, Rarity.RARE, mage.cards.p.PhyrexianPlaguelord.class)); + cards.add(new SetCardInfo("Phyrexian Reclamation", 63, Rarity.UNCOMMON, mage.cards.p.PhyrexianReclamation.class)); + cards.add(new SetCardInfo("Plague Beetle", 64, Rarity.COMMON, mage.cards.p.PlagueBeetle.class)); + cards.add(new SetCardInfo("Planar Collapse", 18, Rarity.RARE, mage.cards.p.PlanarCollapse.class)); + cards.add(new SetCardInfo("Purify", 19, Rarity.RARE, mage.cards.p.Purify.class)); + cards.add(new SetCardInfo("Pygmy Pyrosaur", 87, Rarity.COMMON, mage.cards.p.PygmyPyrosaur.class)); + cards.add(new SetCardInfo("Pyromancy", 88, Rarity.RARE, mage.cards.p.Pyromancy.class)); + cards.add(new SetCardInfo("Quicksilver Amulet", 130, Rarity.RARE, mage.cards.q.QuicksilverAmulet.class)); + cards.add(new SetCardInfo("Rack and Ruin", 89, Rarity.UNCOMMON, mage.cards.r.RackAndRuin.class)); + cards.add(new SetCardInfo("Radiant's Dragoons", 21, Rarity.UNCOMMON, mage.cards.r.RadiantsDragoons.class)); + cards.add(new SetCardInfo("Radiant's Judgment", 22, Rarity.COMMON, mage.cards.r.RadiantsJudgment.class)); + cards.add(new SetCardInfo("Radiant, Archangel", 20, Rarity.RARE, mage.cards.r.RadiantArchangel.class)); + cards.add(new SetCardInfo("Rancor", 110, Rarity.COMMON, mage.cards.r.Rancor.class)); + cards.add(new SetCardInfo("Rank and File", 65, Rarity.UNCOMMON, mage.cards.r.RankAndFile.class)); + cards.add(new SetCardInfo("Raven Familiar", 39, Rarity.UNCOMMON, mage.cards.r.RavenFamiliar.class)); + cards.add(new SetCardInfo("Rebuild", 40, Rarity.UNCOMMON, mage.cards.r.Rebuild.class)); + cards.add(new SetCardInfo("Repopulate", 111, Rarity.COMMON, mage.cards.r.Repopulate.class)); + cards.add(new SetCardInfo("Ring of Gix", 131, Rarity.RARE, mage.cards.r.RingOfGix.class)); + cards.add(new SetCardInfo("Rivalry", 90, Rarity.RARE, mage.cards.r.Rivalry.class)); + cards.add(new SetCardInfo("Scrapheap", 132, Rarity.RARE, mage.cards.s.Scrapheap.class)); + cards.add(new SetCardInfo("Second Chance", 41, Rarity.RARE, mage.cards.s.SecondChance.class)); + cards.add(new SetCardInfo("Shivan Phoenix", 91, Rarity.RARE, mage.cards.s.ShivanPhoenix.class)); + cards.add(new SetCardInfo("Sick and Tired", 66, Rarity.COMMON, mage.cards.s.SickAndTired.class)); + cards.add(new SetCardInfo("Silk Net", 112, Rarity.COMMON, mage.cards.s.SilkNet.class)); + cards.add(new SetCardInfo("Simian Grunts", 113, Rarity.COMMON, mage.cards.s.SimianGrunts.class)); + cards.add(new SetCardInfo("Sleeper's Guile", 67, Rarity.COMMON, mage.cards.s.SleepersGuile.class)); + cards.add(new SetCardInfo("Slow Motion", 42, Rarity.COMMON, mage.cards.s.SlowMotion.class)); + cards.add(new SetCardInfo("Sluggishness", 92, Rarity.COMMON, mage.cards.s.Sluggishness.class)); + cards.add(new SetCardInfo("Snap", 43, Rarity.COMMON, mage.cards.s.Snap.class)); + cards.add(new SetCardInfo("Spawning Pool", 142, Rarity.UNCOMMON, mage.cards.s.SpawningPool.class)); + cards.add(new SetCardInfo("Subversion", 68, Rarity.RARE, mage.cards.s.Subversion.class)); + cards.add(new SetCardInfo("Sustainer of the Realm", 23, Rarity.UNCOMMON, mage.cards.s.SustainerOfTheRealm.class)); + cards.add(new SetCardInfo("Swat", 69, Rarity.COMMON, mage.cards.s.Swat.class)); + cards.add(new SetCardInfo("Tethered Skirge", 70, Rarity.UNCOMMON, mage.cards.t.TetheredSkirge.class)); + cards.add(new SetCardInfo("Thornwind Faeries", 44, Rarity.COMMON, mage.cards.t.ThornwindFaeries.class)); + cards.add(new SetCardInfo("Thran Lens", 133, Rarity.RARE, mage.cards.t.ThranLens.class)); + cards.add(new SetCardInfo("Thran War Machine", 134, Rarity.UNCOMMON, mage.cards.t.ThranWarMachine.class)); + cards.add(new SetCardInfo("Thran Weaponry", 135, Rarity.RARE, mage.cards.t.ThranWeaponry.class)); + cards.add(new SetCardInfo("Ticking Gnomes", 136, Rarity.UNCOMMON, mage.cards.t.TickingGnomes.class)); + cards.add(new SetCardInfo("Tinker", 45, Rarity.UNCOMMON, mage.cards.t.Tinker.class)); + cards.add(new SetCardInfo("Tragic Poet", 24, Rarity.COMMON, mage.cards.t.TragicPoet.class)); + cards.add(new SetCardInfo("Treacherous Link", 71, Rarity.UNCOMMON, mage.cards.t.TreacherousLink.class)); + cards.add(new SetCardInfo("Treefolk Mystic", 114, Rarity.COMMON, mage.cards.t.TreefolkMystic.class)); + cards.add(new SetCardInfo("Treetop Village", 143, Rarity.UNCOMMON, mage.cards.t.TreetopVillage.class)); + cards.add(new SetCardInfo("Unearth", 72, Rarity.COMMON, mage.cards.u.Unearth.class)); + cards.add(new SetCardInfo("Urza's Blueprints", 137, Rarity.RARE, mage.cards.u.UrzasBlueprints.class)); + cards.add(new SetCardInfo("Viashino Bey", 93, Rarity.COMMON, mage.cards.v.ViashinoBey.class)); + cards.add(new SetCardInfo("Viashino Cutthroat", 94, Rarity.UNCOMMON, mage.cards.v.ViashinoCutthroat.class)); + cards.add(new SetCardInfo("Viashino Heretic", 95, Rarity.UNCOMMON, mage.cards.v.ViashinoHeretic.class)); + cards.add(new SetCardInfo("Viashino Sandscout", 96, Rarity.COMMON, mage.cards.v.ViashinoSandscout.class)); + cards.add(new SetCardInfo("Vigilant Drake", 46, Rarity.COMMON, mage.cards.v.VigilantDrake.class)); + cards.add(new SetCardInfo("Walking Sponge", 47, Rarity.UNCOMMON, mage.cards.w.WalkingSponge.class)); + cards.add(new SetCardInfo("Weatherseed Elf", 115, Rarity.COMMON, mage.cards.w.WeatherseedElf.class)); + cards.add(new SetCardInfo("Weatherseed Faeries", 48, Rarity.COMMON, mage.cards.w.WeatherseedFaeries.class)); + cards.add(new SetCardInfo("Weatherseed Treefolk", 116, Rarity.RARE, mage.cards.w.WeatherseedTreefolk.class)); + cards.add(new SetCardInfo("Wheel of Torture", 138, Rarity.RARE, mage.cards.w.WheelOfTorture.class)); + cards.add(new SetCardInfo("Wing Snare", 117, Rarity.UNCOMMON, mage.cards.w.WingSnare.class)); + cards.add(new SetCardInfo("Yavimaya Granger", 118, Rarity.COMMON, mage.cards.y.YavimayaGranger.class)); + cards.add(new SetCardInfo("Yavimaya Scion", 119, Rarity.COMMON, mage.cards.y.YavimayaScion.class)); + cards.add(new SetCardInfo("Yavimaya Wurm", 120, Rarity.COMMON, mage.cards.y.YavimayaWurm.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/UrzasSaga.java b/Mage.Sets/src/mage/sets/UrzasSaga.java index ab0632ed8e..8a7ff4755d 100644 --- a/Mage.Sets/src/mage/sets/UrzasSaga.java +++ b/Mage.Sets/src/mage/sets/UrzasSaga.java @@ -1,379 +1,379 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author Backfir3 - */ -public final class UrzasSaga extends ExpansionSet { - - private static final UrzasSaga instance = new UrzasSaga(); - - public static UrzasSaga getInstance() { - return instance; - } - - private UrzasSaga() { - super("Urza's Saga", "USG", ExpansionSet.buildDate(1998, 10, 12), SetType.EXPANSION); - this.blockName = "Urza"; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Absolute Grace", 1, Rarity.UNCOMMON, mage.cards.a.AbsoluteGrace.class)); - cards.add(new SetCardInfo("Absolute Law", 2, Rarity.UNCOMMON, mage.cards.a.AbsoluteLaw.class)); - cards.add(new SetCardInfo("Abundance", 229, Rarity.RARE, mage.cards.a.Abundance.class)); - cards.add(new SetCardInfo("Abyssal Horror", 115, Rarity.RARE, mage.cards.a.AbyssalHorror.class)); - cards.add(new SetCardInfo("Academy Researchers", 58, Rarity.UNCOMMON, mage.cards.a.AcademyResearchers.class)); - cards.add(new SetCardInfo("Acidic Soil", 172, Rarity.UNCOMMON, mage.cards.a.AcidicSoil.class)); - cards.add(new SetCardInfo("Acridian", 230, Rarity.COMMON, mage.cards.a.Acridian.class)); - cards.add(new SetCardInfo("Albino Troll", 231, Rarity.UNCOMMON, mage.cards.a.AlbinoTroll.class)); - cards.add(new SetCardInfo("Anaconda", 232, Rarity.UNCOMMON, mage.cards.a.Anaconda.class)); - cards.add(new SetCardInfo("Angelic Chorus", 3, Rarity.RARE, mage.cards.a.AngelicChorus.class)); - cards.add(new SetCardInfo("Angelic Page", 4, Rarity.COMMON, mage.cards.a.AngelicPage.class)); - cards.add(new SetCardInfo("Annul", 59, Rarity.COMMON, mage.cards.a.Annul.class)); - cards.add(new SetCardInfo("Antagonism", 173, Rarity.RARE, mage.cards.a.Antagonism.class)); - cards.add(new SetCardInfo("Arcane Laboratory", 60, Rarity.UNCOMMON, mage.cards.a.ArcaneLaboratory.class)); - cards.add(new SetCardInfo("Arc Lightning", 174, Rarity.COMMON, mage.cards.a.ArcLightning.class)); - cards.add(new SetCardInfo("Argothian Elder", 233, Rarity.UNCOMMON, mage.cards.a.ArgothianElder.class)); - cards.add(new SetCardInfo("Argothian Enchantress", 234, Rarity.RARE, mage.cards.a.ArgothianEnchantress.class)); - cards.add(new SetCardInfo("Argothian Swine", 235, Rarity.COMMON, mage.cards.a.ArgothianSwine.class)); - cards.add(new SetCardInfo("Argothian Wurm", 236, Rarity.RARE, mage.cards.a.ArgothianWurm.class)); - cards.add(new SetCardInfo("Attunement", 61, Rarity.RARE, mage.cards.a.Attunement.class)); - cards.add(new SetCardInfo("Back to Basics", 62, Rarity.RARE, mage.cards.b.BackToBasics.class)); - cards.add(new SetCardInfo("Barrin, Master Wizard", 63, Rarity.RARE, mage.cards.b.BarrinMasterWizard.class)); - cards.add(new SetCardInfo("Barrin's Codex", 286, Rarity.RARE, mage.cards.b.BarrinsCodex.class)); - cards.add(new SetCardInfo("Bedlam", 175, Rarity.RARE, mage.cards.b.Bedlam.class)); - cards.add(new SetCardInfo("Befoul", 116, Rarity.COMMON, mage.cards.b.Befoul.class)); - cards.add(new SetCardInfo("Bereavement", 117, Rarity.UNCOMMON, mage.cards.b.Bereavement.class)); - cards.add(new SetCardInfo("Blanchwood Armor", 237, Rarity.UNCOMMON, mage.cards.b.BlanchwoodArmor.class)); - cards.add(new SetCardInfo("Blanchwood Treefolk", 238, Rarity.COMMON, mage.cards.b.BlanchwoodTreefolk.class)); - cards.add(new SetCardInfo("Blasted Landscape", 319, Rarity.UNCOMMON, mage.cards.b.BlastedLandscape.class)); - cards.add(new SetCardInfo("Blood Vassal", 118, Rarity.COMMON, mage.cards.b.BloodVassal.class)); - cards.add(new SetCardInfo("Bog Raiders", 119, Rarity.COMMON, mage.cards.b.BogRaiders.class)); - cards.add(new SetCardInfo("Brand", 176, Rarity.RARE, mage.cards.b.Brand.class)); - cards.add(new SetCardInfo("Bravado", 177, Rarity.COMMON, mage.cards.b.Bravado.class)); - cards.add(new SetCardInfo("Breach", 120, Rarity.COMMON, mage.cards.b.Breach.class)); - cards.add(new SetCardInfo("Brilliant Halo", 5, Rarity.COMMON, mage.cards.b.BrilliantHalo.class)); - cards.add(new SetCardInfo("Bull Hippo", 239, Rarity.UNCOMMON, mage.cards.b.BullHippo.class)); - cards.add(new SetCardInfo("Bulwark", 178, Rarity.RARE, mage.cards.b.Bulwark.class)); - cards.add(new SetCardInfo("Cackling Fiend", 121, Rarity.COMMON, mage.cards.c.CacklingFiend.class)); - cards.add(new SetCardInfo("Carpet of Flowers", 240, Rarity.UNCOMMON, mage.cards.c.CarpetOfFlowers.class)); - cards.add(new SetCardInfo("Carrion Beetles", 122, Rarity.COMMON, mage.cards.c.CarrionBeetles.class)); - cards.add(new SetCardInfo("Catalog", 64, Rarity.COMMON, mage.cards.c.Catalog.class)); - cards.add(new SetCardInfo("Catastrophe", 6, Rarity.RARE, mage.cards.c.Catastrophe.class)); - cards.add(new SetCardInfo("Cathodion", 287, Rarity.UNCOMMON, mage.cards.c.Cathodion.class)); - cards.add(new SetCardInfo("Cave Tiger", 241, Rarity.COMMON, mage.cards.c.CaveTiger.class)); - cards.add(new SetCardInfo("Child of Gaea", 242, Rarity.RARE, mage.cards.c.ChildOfGaea.class)); - cards.add(new SetCardInfo("Chimeric Staff", 288, Rarity.RARE, mage.cards.c.ChimericStaff.class)); - cards.add(new SetCardInfo("Citanul Centaurs", 243, Rarity.RARE, mage.cards.c.CitanulCentaurs.class)); - cards.add(new SetCardInfo("Citanul Flute", 289, Rarity.RARE, mage.cards.c.CitanulFlute.class)); - cards.add(new SetCardInfo("Citanul Hierophants", 244, Rarity.RARE, mage.cards.c.CitanulHierophants.class)); - cards.add(new SetCardInfo("Claws of Gix", 290, Rarity.UNCOMMON, mage.cards.c.ClawsOfGix.class)); - cards.add(new SetCardInfo("Clear", 7, Rarity.UNCOMMON, mage.cards.c.Clear.class)); - cards.add(new SetCardInfo("Cloak of Mists", 65, Rarity.COMMON, mage.cards.c.CloakOfMists.class)); - cards.add(new SetCardInfo("Confiscate", 66, Rarity.UNCOMMON, mage.cards.c.Confiscate.class)); - cards.add(new SetCardInfo("Congregate", 8, Rarity.COMMON, mage.cards.c.Congregate.class)); - cards.add(new SetCardInfo("Contamination", 123, Rarity.RARE, mage.cards.c.Contamination.class)); - cards.add(new SetCardInfo("Copper Gnomes", 291, Rarity.RARE, mage.cards.c.CopperGnomes.class)); - cards.add(new SetCardInfo("Coral Merfolk", 67, Rarity.COMMON, mage.cards.c.CoralMerfolk.class)); - cards.add(new SetCardInfo("Corrupt", 124, Rarity.COMMON, mage.cards.c.Corrupt.class)); - cards.add(new SetCardInfo("Cradle Guard", 245, Rarity.UNCOMMON, mage.cards.c.CradleGuard.class)); - cards.add(new SetCardInfo("Crater Hellion", 179, Rarity.RARE, mage.cards.c.CraterHellion.class)); - cards.add(new SetCardInfo("Crazed Skirge", 125, Rarity.UNCOMMON, mage.cards.c.CrazedSkirge.class)); - cards.add(new SetCardInfo("Crosswinds", 246, Rarity.UNCOMMON, mage.cards.c.Crosswinds.class)); - cards.add(new SetCardInfo("Crystal Chimes", 292, Rarity.UNCOMMON, mage.cards.c.CrystalChimes.class)); - cards.add(new SetCardInfo("Curfew", 68, Rarity.COMMON, mage.cards.c.Curfew.class)); - cards.add(new SetCardInfo("Darkest Hour", 128, Rarity.RARE, mage.cards.d.DarkestHour.class)); - cards.add(new SetCardInfo("Dark Hatchling", 126, Rarity.RARE, mage.cards.d.DarkHatchling.class)); - cards.add(new SetCardInfo("Dark Ritual", 127, Rarity.COMMON, mage.cards.d.DarkRitual.class)); - cards.add(new SetCardInfo("Defensive Formation", 9, Rarity.UNCOMMON, mage.cards.d.DefensiveFormation.class)); - cards.add(new SetCardInfo("Despondency", 129, Rarity.COMMON, mage.cards.d.Despondency.class)); - cards.add(new SetCardInfo("Destructive Urge", 180, Rarity.UNCOMMON, mage.cards.d.DestructiveUrge.class)); - cards.add(new SetCardInfo("Diabolic Servitude", 130, Rarity.UNCOMMON, mage.cards.d.DiabolicServitude.class)); - cards.add(new SetCardInfo("Disciple of Grace", 10, Rarity.COMMON, mage.cards.d.DiscipleOfGrace.class)); - cards.add(new SetCardInfo("Disciple of Law", 11, Rarity.COMMON, mage.cards.d.DiscipleOfLaw.class)); - cards.add(new SetCardInfo("Discordant Dirge", 131, Rarity.RARE, mage.cards.d.DiscordantDirge.class)); - cards.add(new SetCardInfo("Disenchant", 12, Rarity.COMMON, mage.cards.d.Disenchant.class)); - cards.add(new SetCardInfo("Disorder", 181, Rarity.UNCOMMON, mage.cards.d.Disorder.class)); - cards.add(new SetCardInfo("Disruptive Student", 69, Rarity.COMMON, mage.cards.d.DisruptiveStudent.class)); - cards.add(new SetCardInfo("Douse", 70, Rarity.UNCOMMON, mage.cards.d.Douse.class)); - cards.add(new SetCardInfo("Dragon Blood", 293, Rarity.UNCOMMON, mage.cards.d.DragonBlood.class)); - cards.add(new SetCardInfo("Drifting Djinn", 71, Rarity.RARE, mage.cards.d.DriftingDjinn.class)); - cards.add(new SetCardInfo("Drifting Meadow", 320, Rarity.COMMON, mage.cards.d.DriftingMeadow.class)); - cards.add(new SetCardInfo("Dromosaur", 182, Rarity.COMMON, mage.cards.d.Dromosaur.class)); - cards.add(new SetCardInfo("Duress", 132, Rarity.COMMON, mage.cards.d.Duress.class)); - cards.add(new SetCardInfo("Eastern Paladin", 133, Rarity.RARE, mage.cards.e.EasternPaladin.class)); - cards.add(new SetCardInfo("Electryte", 183, Rarity.RARE, mage.cards.e.Electryte.class)); - cards.add(new SetCardInfo("Elite Archers", 13, Rarity.RARE, mage.cards.e.EliteArchers.class)); - cards.add(new SetCardInfo("Elvish Herder", 247, Rarity.COMMON, mage.cards.e.ElvishHerder.class)); - cards.add(new SetCardInfo("Elvish Lyrist", 248, Rarity.COMMON, mage.cards.e.ElvishLyrist.class)); - cards.add(new SetCardInfo("Enchantment Alteration", 72, Rarity.UNCOMMON, mage.cards.e.EnchantmentAlteration.class)); - cards.add(new SetCardInfo("Endless Wurm", 249, Rarity.RARE, mage.cards.e.EndlessWurm.class)); - cards.add(new SetCardInfo("Endoskeleton", 294, Rarity.UNCOMMON, mage.cards.e.Endoskeleton.class)); - cards.add(new SetCardInfo("Energy Field", 73, Rarity.RARE, mage.cards.e.EnergyField.class)); - cards.add(new SetCardInfo("Exhaustion", 74, Rarity.UNCOMMON, mage.cards.e.Exhaustion.class)); - cards.add(new SetCardInfo("Exhume", 134, Rarity.COMMON, mage.cards.e.Exhume.class)); - cards.add(new SetCardInfo("Exploration", 250, Rarity.RARE, mage.cards.e.Exploration.class)); - cards.add(new SetCardInfo("Expunge", 135, Rarity.COMMON, mage.cards.e.Expunge.class)); - cards.add(new SetCardInfo("Faith Healer", 14, Rarity.RARE, mage.cards.f.FaithHealer.class)); - cards.add(new SetCardInfo("Falter", 184, Rarity.COMMON, mage.cards.f.Falter.class)); - cards.add(new SetCardInfo("Fault Line", 185, Rarity.RARE, mage.cards.f.FaultLine.class)); - cards.add(new SetCardInfo("Fecundity", 251, Rarity.UNCOMMON, mage.cards.f.Fecundity.class)); - cards.add(new SetCardInfo("Fertile Ground", 252, Rarity.COMMON, mage.cards.f.FertileGround.class)); - cards.add(new SetCardInfo("Fiery Mantle", 186, Rarity.COMMON, mage.cards.f.FieryMantle.class)); - cards.add(new SetCardInfo("Fire Ants", 187, Rarity.UNCOMMON, mage.cards.f.FireAnts.class)); - cards.add(new SetCardInfo("Flesh Reaver", 136, Rarity.UNCOMMON, mage.cards.f.FleshReaver.class)); - cards.add(new SetCardInfo("Fluctuator", 295, Rarity.RARE, mage.cards.f.Fluctuator.class)); - cards.add(new SetCardInfo("Fog Bank", 75, Rarity.UNCOMMON, mage.cards.f.FogBank.class)); - cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fortitude", 253, Rarity.COMMON, mage.cards.f.Fortitude.class)); - cards.add(new SetCardInfo("Gaea's Bounty", 254, Rarity.COMMON, mage.cards.g.GaeasBounty.class)); - cards.add(new SetCardInfo("Gaea's Cradle", 321, Rarity.RARE, mage.cards.g.GaeasCradle.class)); - cards.add(new SetCardInfo("Gaea's Embrace", 255, Rarity.UNCOMMON, mage.cards.g.GaeasEmbrace.class)); - cards.add(new SetCardInfo("Gamble", 188, Rarity.RARE, mage.cards.g.Gamble.class)); - cards.add(new SetCardInfo("Gilded Drake", 76, Rarity.RARE, mage.cards.g.GildedDrake.class)); - cards.add(new SetCardInfo("Glorious Anthem", 15, Rarity.RARE, mage.cards.g.GloriousAnthem.class)); - cards.add(new SetCardInfo("Goblin Cadets", 189, Rarity.UNCOMMON, mage.cards.g.GoblinCadets.class)); - cards.add(new SetCardInfo("Goblin Lackey", 190, Rarity.UNCOMMON, mage.cards.g.GoblinLackey.class)); - cards.add(new SetCardInfo("Goblin Matron", 191, Rarity.COMMON, mage.cards.g.GoblinMatron.class)); - cards.add(new SetCardInfo("Goblin Offensive", 192, Rarity.UNCOMMON, mage.cards.g.GoblinOffensive.class)); - cards.add(new SetCardInfo("Goblin Patrol", 193, Rarity.COMMON, mage.cards.g.GoblinPatrol.class)); - cards.add(new SetCardInfo("Goblin Raider", 194, Rarity.COMMON, mage.cards.g.GoblinRaider.class)); - cards.add(new SetCardInfo("Goblin Spelunkers", 195, Rarity.COMMON, mage.cards.g.GoblinSpelunkers.class)); - cards.add(new SetCardInfo("Goblin War Buggy", 196, Rarity.COMMON, mage.cards.g.GoblinWarBuggy.class)); - cards.add(new SetCardInfo("Gorilla Warrior", 256, Rarity.COMMON, mage.cards.g.GorillaWarrior.class)); - cards.add(new SetCardInfo("Grafted Skullcap", 296, Rarity.RARE, mage.cards.g.GraftedSkullcap.class)); - cards.add(new SetCardInfo("Greater Good", 257, Rarity.RARE, mage.cards.g.GreaterGood.class)); - cards.add(new SetCardInfo("Great Whale", 77, Rarity.RARE, mage.cards.g.GreatWhale.class)); - cards.add(new SetCardInfo("Greener Pastures", 258, Rarity.RARE, mage.cards.g.GreenerPastures.class)); - cards.add(new SetCardInfo("Guma", 197, Rarity.UNCOMMON, mage.cards.g.Guma.class)); - cards.add(new SetCardInfo("Hawkeater Moth", 259, Rarity.UNCOMMON, mage.cards.h.HawkeaterMoth.class)); - cards.add(new SetCardInfo("Headlong Rush", 198, Rarity.COMMON, mage.cards.h.HeadlongRush.class)); - cards.add(new SetCardInfo("Healing Salve", 16, Rarity.COMMON, mage.cards.h.HealingSalve.class)); - cards.add(new SetCardInfo("Heat Ray", 199, Rarity.COMMON, mage.cards.h.HeatRay.class)); - cards.add(new SetCardInfo("Herald of Serra", 17, Rarity.RARE, mage.cards.h.HeraldOfSerra.class)); - cards.add(new SetCardInfo("Hermetic Study", 78, Rarity.COMMON, mage.cards.h.HermeticStudy.class)); - cards.add(new SetCardInfo("Hibernation", 79, Rarity.UNCOMMON, mage.cards.h.Hibernation.class)); - cards.add(new SetCardInfo("Hidden Ancients", 260, Rarity.UNCOMMON, mage.cards.h.HiddenAncients.class)); - cards.add(new SetCardInfo("Hidden Guerrillas", 261, Rarity.UNCOMMON, mage.cards.h.HiddenGuerrillas.class)); - cards.add(new SetCardInfo("Hidden Herd", 262, Rarity.RARE, mage.cards.h.HiddenHerd.class)); - cards.add(new SetCardInfo("Hidden Predators", 263, Rarity.RARE, mage.cards.h.HiddenPredators.class)); - cards.add(new SetCardInfo("Hidden Spider", 264, Rarity.COMMON, mage.cards.h.HiddenSpider.class)); - cards.add(new SetCardInfo("Hidden Stag", 265, Rarity.RARE, mage.cards.h.HiddenStag.class)); - cards.add(new SetCardInfo("Hollow Dogs", 137, Rarity.COMMON, mage.cards.h.HollowDogs.class)); - cards.add(new SetCardInfo("Hopping Automaton", 297, Rarity.UNCOMMON, mage.cards.h.HoppingAutomaton.class)); - cards.add(new SetCardInfo("Horseshoe Crab", 80, Rarity.COMMON, mage.cards.h.HorseshoeCrab.class)); - cards.add(new SetCardInfo("Humble", 18, Rarity.UNCOMMON, mage.cards.h.Humble.class)); - cards.add(new SetCardInfo("Hush", 266, Rarity.COMMON, mage.cards.h.Hush.class)); - cards.add(new SetCardInfo("Ill-Gotten Gains", 138, Rarity.RARE, mage.cards.i.IllGottenGains.class)); - cards.add(new SetCardInfo("Imaginary Pet", 81, Rarity.RARE, mage.cards.i.ImaginaryPet.class)); - cards.add(new SetCardInfo("Intrepid Hero", 19, Rarity.RARE, mage.cards.i.IntrepidHero.class)); - cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Jagged Lightning", 200, Rarity.UNCOMMON, mage.cards.j.JaggedLightning.class)); - cards.add(new SetCardInfo("Karn, Silver Golem", 298, Rarity.RARE, mage.cards.k.KarnSilverGolem.class)); - cards.add(new SetCardInfo("Launch", 82, Rarity.COMMON, mage.cards.l.Launch.class)); - cards.add(new SetCardInfo("Lay Waste", 201, Rarity.COMMON, mage.cards.l.LayWaste.class)); - cards.add(new SetCardInfo("Lifeline", 299, Rarity.RARE, mage.cards.l.Lifeline.class)); - cards.add(new SetCardInfo("Lightning Dragon", 202, Rarity.RARE, mage.cards.l.LightningDragon.class)); - cards.add(new SetCardInfo("Lilting Refrain", 83, Rarity.UNCOMMON, mage.cards.l.LiltingRefrain.class)); - cards.add(new SetCardInfo("Lingering Mirage", 84, Rarity.UNCOMMON, mage.cards.l.LingeringMirage.class)); - cards.add(new SetCardInfo("Looming Shade", 139, Rarity.COMMON, mage.cards.l.LoomingShade.class)); - cards.add(new SetCardInfo("Lotus Blossom", 300, Rarity.RARE, mage.cards.l.LotusBlossom.class)); - cards.add(new SetCardInfo("Lull", 267, Rarity.COMMON, mage.cards.l.Lull.class)); - cards.add(new SetCardInfo("Lurking Evil", 140, Rarity.RARE, mage.cards.l.LurkingEvil.class)); - cards.add(new SetCardInfo("Mana Leech", 141, Rarity.UNCOMMON, mage.cards.m.ManaLeech.class)); - cards.add(new SetCardInfo("Meltdown", 203, Rarity.UNCOMMON, mage.cards.m.Meltdown.class)); - cards.add(new SetCardInfo("Metrognome", 301, Rarity.RARE, mage.cards.m.Metrognome.class)); - cards.add(new SetCardInfo("Midsummer Revel", 268, Rarity.RARE, mage.cards.m.MidsummerRevel.class)); - cards.add(new SetCardInfo("Mishra's Helix", 302, Rarity.RARE, mage.cards.m.MishrasHelix.class)); - cards.add(new SetCardInfo("Mobile Fort", 303, Rarity.UNCOMMON, mage.cards.m.MobileFort.class)); - cards.add(new SetCardInfo("Monk Idealist", 20, Rarity.UNCOMMON, mage.cards.m.MonkIdealist.class)); - cards.add(new SetCardInfo("Monk Realist", 21, Rarity.COMMON, mage.cards.m.MonkRealist.class)); - cards.add(new SetCardInfo("Morphling", 85, Rarity.RARE, mage.cards.m.Morphling.class)); - cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("No Rest for the Wicked", 142, Rarity.UNCOMMON, mage.cards.n.NoRestForTheWicked.class)); - cards.add(new SetCardInfo("Noetic Scales", 304, Rarity.RARE, mage.cards.n.NoeticScales.class)); - cards.add(new SetCardInfo("Okk", 204, Rarity.RARE, mage.cards.o.Okk.class)); - cards.add(new SetCardInfo("Opal Acrolith", 22, Rarity.UNCOMMON, mage.cards.o.OpalAcrolith.class)); - cards.add(new SetCardInfo("Opal Archangel", 23, Rarity.RARE, mage.cards.o.OpalArchangel.class)); - cards.add(new SetCardInfo("Opal Caryatid", 24, Rarity.COMMON, mage.cards.o.OpalCaryatid.class)); - cards.add(new SetCardInfo("Opal Gargoyle", 25, Rarity.COMMON, mage.cards.o.OpalGargoyle.class)); - cards.add(new SetCardInfo("Opal Titan", 26, Rarity.RARE, mage.cards.o.OpalTitan.class)); - cards.add(new SetCardInfo("Oppression", 143, Rarity.RARE, mage.cards.o.Oppression.class)); - cards.add(new SetCardInfo("Order of Yawgmoth", 144, Rarity.UNCOMMON, mage.cards.o.OrderOfYawgmoth.class)); - cards.add(new SetCardInfo("Outmaneuver", 205, Rarity.UNCOMMON, mage.cards.o.Outmaneuver.class)); - cards.add(new SetCardInfo("Pacifism", 27, Rarity.COMMON, mage.cards.p.Pacifism.class)); - cards.add(new SetCardInfo("Parasitic Bond", 145, Rarity.UNCOMMON, mage.cards.p.ParasiticBond.class)); - cards.add(new SetCardInfo("Pariah", 28, Rarity.RARE, mage.cards.p.Pariah.class)); - cards.add(new SetCardInfo("Path of Peace", 29, Rarity.COMMON, mage.cards.p.PathOfPeace.class)); - cards.add(new SetCardInfo("Pegasus Charger", 30, Rarity.COMMON, mage.cards.p.PegasusCharger.class)); - cards.add(new SetCardInfo("Pendrell Drake", 86, Rarity.COMMON, mage.cards.p.PendrellDrake.class)); - cards.add(new SetCardInfo("Pendrell Flux", 87, Rarity.COMMON, mage.cards.p.PendrellFlux.class)); - cards.add(new SetCardInfo("Peregrine Drake", 88, Rarity.UNCOMMON, mage.cards.p.PeregrineDrake.class)); - cards.add(new SetCardInfo("Persecute", 146, Rarity.RARE, mage.cards.p.Persecute.class)); - cards.add(new SetCardInfo("Pestilence", 147, Rarity.COMMON, mage.cards.p.Pestilence.class)); - cards.add(new SetCardInfo("Phyrexian Colossus", 305, Rarity.RARE, mage.cards.p.PhyrexianColossus.class)); - cards.add(new SetCardInfo("Phyrexian Ghoul", 148, Rarity.COMMON, mage.cards.p.PhyrexianGhoul.class)); - cards.add(new SetCardInfo("Phyrexian Processor", 306, Rarity.RARE, mage.cards.p.PhyrexianProcessor.class)); - cards.add(new SetCardInfo("Phyrexian Tower", 322, Rarity.RARE, mage.cards.p.PhyrexianTower.class)); - cards.add(new SetCardInfo("Pit Trap", 307, Rarity.UNCOMMON, mage.cards.p.PitTrap.class)); - cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Planar Birth", 31, Rarity.RARE, mage.cards.p.PlanarBirth.class)); - cards.add(new SetCardInfo("Planar Void", 149, Rarity.UNCOMMON, mage.cards.p.PlanarVoid.class)); - cards.add(new SetCardInfo("Polluted Mire", 323, Rarity.COMMON, mage.cards.p.PollutedMire.class)); - cards.add(new SetCardInfo("Pouncing Jaguar", 269, Rarity.COMMON, mage.cards.p.PouncingJaguar.class)); - cards.add(new SetCardInfo("Power Sink", 89, Rarity.COMMON, mage.cards.p.PowerSink.class)); - cards.add(new SetCardInfo("Power Taint", 90, Rarity.COMMON, mage.cards.p.PowerTaint.class)); - cards.add(new SetCardInfo("Presence of the Master", 32, Rarity.UNCOMMON, mage.cards.p.PresenceOfTheMaster.class)); - cards.add(new SetCardInfo("Priest of Gix", 150, Rarity.UNCOMMON, mage.cards.p.PriestOfGix.class)); - cards.add(new SetCardInfo("Priest of Titania", 270, Rarity.COMMON, mage.cards.p.PriestOfTitania.class)); - cards.add(new SetCardInfo("Purging Scythe", 308, Rarity.RARE, mage.cards.p.PurgingScythe.class)); - cards.add(new SetCardInfo("Rain of Filth", 151, Rarity.UNCOMMON, mage.cards.r.RainOfFilth.class)); - cards.add(new SetCardInfo("Rain of Salt", 206, Rarity.UNCOMMON, mage.cards.r.RainOfSalt.class)); - cards.add(new SetCardInfo("Ravenous Skirge", 152, Rarity.COMMON, mage.cards.r.RavenousSkirge.class)); - cards.add(new SetCardInfo("Raze", 207, Rarity.COMMON, mage.cards.r.Raze.class)); - cards.add(new SetCardInfo("Recantation", 91, Rarity.RARE, mage.cards.r.Recantation.class)); - cards.add(new SetCardInfo("Reclusive Wight", 153, Rarity.UNCOMMON, mage.cards.r.ReclusiveWight.class)); - cards.add(new SetCardInfo("Redeem", 33, Rarity.UNCOMMON, mage.cards.r.Redeem.class)); - cards.add(new SetCardInfo("Reflexes", 208, Rarity.COMMON, mage.cards.r.Reflexes.class)); - cards.add(new SetCardInfo("Rejuvenate", 271, Rarity.COMMON, mage.cards.r.Rejuvenate.class)); - cards.add(new SetCardInfo("Remembrance", 34, Rarity.RARE, mage.cards.r.Remembrance.class)); - cards.add(new SetCardInfo("Remote Isle", 324, Rarity.COMMON, mage.cards.r.RemoteIsle.class)); - cards.add(new SetCardInfo("Reprocess", 154, Rarity.RARE, mage.cards.r.Reprocess.class)); - cards.add(new SetCardInfo("Rescind", 92, Rarity.COMMON, mage.cards.r.Rescind.class)); - cards.add(new SetCardInfo("Retaliation", 272, Rarity.UNCOMMON, mage.cards.r.Retaliation.class)); - cards.add(new SetCardInfo("Retromancer", 209, Rarity.COMMON, mage.cards.r.Retromancer.class)); - cards.add(new SetCardInfo("Rewind", 93, Rarity.COMMON, mage.cards.r.Rewind.class)); - cards.add(new SetCardInfo("Rumbling Crescendo", 210, Rarity.RARE, mage.cards.r.RumblingCrescendo.class)); - cards.add(new SetCardInfo("Rune of Protection: Artifacts", 35, Rarity.UNCOMMON, mage.cards.r.RuneOfProtectionArtifacts.class)); - cards.add(new SetCardInfo("Rune of Protection: Black", 36, Rarity.COMMON, mage.cards.r.RuneOfProtectionBlack.class)); - cards.add(new SetCardInfo("Rune of Protection: Blue", 37, Rarity.COMMON, mage.cards.r.RuneOfProtectionBlue.class)); - cards.add(new SetCardInfo("Rune of Protection: Green", 38, Rarity.COMMON, mage.cards.r.RuneOfProtectionGreen.class)); - cards.add(new SetCardInfo("Rune of Protection: Lands", 39, Rarity.RARE, mage.cards.r.RuneOfProtectionLands.class)); - cards.add(new SetCardInfo("Rune of Protection: Red", 40, Rarity.COMMON, mage.cards.r.RuneOfProtectionRed.class)); - cards.add(new SetCardInfo("Rune of Protection: White", 41, Rarity.COMMON, mage.cards.r.RuneOfProtectionWhite.class)); - cards.add(new SetCardInfo("Sanctum Custodian", 42, Rarity.COMMON, mage.cards.s.SanctumCustodian.class)); - cards.add(new SetCardInfo("Sanctum Guardian", 43, Rarity.UNCOMMON, mage.cards.s.SanctumGuardian.class)); - cards.add(new SetCardInfo("Sandbar Merfolk", 94, Rarity.COMMON, mage.cards.s.SandbarMerfolk.class)); - cards.add(new SetCardInfo("Sandbar Serpent", 95, Rarity.UNCOMMON, mage.cards.s.SandbarSerpent.class)); - cards.add(new SetCardInfo("Sanguine Guard", 155, Rarity.UNCOMMON, mage.cards.s.SanguineGuard.class)); - cards.add(new SetCardInfo("Scald", 211, Rarity.UNCOMMON, mage.cards.s.Scald.class)); - cards.add(new SetCardInfo("Scoria Wurm", 212, Rarity.RARE, mage.cards.s.ScoriaWurm.class)); - cards.add(new SetCardInfo("Scrap", 213, Rarity.COMMON, mage.cards.s.Scrap.class)); - cards.add(new SetCardInfo("Seasoned Marshal", 44, Rarity.UNCOMMON, mage.cards.s.SeasonedMarshal.class)); - cards.add(new SetCardInfo("Serra Avatar", 45, Rarity.RARE, mage.cards.s.SerraAvatar.class)); - cards.add(new SetCardInfo("Serra's Embrace", 47, Rarity.UNCOMMON, mage.cards.s.SerrasEmbrace.class)); - cards.add(new SetCardInfo("Serra's Hymn", 48, Rarity.UNCOMMON, mage.cards.s.SerrasHymn.class)); - cards.add(new SetCardInfo("Serra's Liturgy", 49, Rarity.RARE, mage.cards.s.SerrasLiturgy.class)); - cards.add(new SetCardInfo("Serra's Sanctum", 325, Rarity.RARE, mage.cards.s.SerrasSanctum.class)); - cards.add(new SetCardInfo("Serra Zealot", 46, Rarity.COMMON, mage.cards.s.SerraZealot.class)); - cards.add(new SetCardInfo("Shimmering Barrier", 50, Rarity.UNCOMMON, mage.cards.s.ShimmeringBarrier.class)); - cards.add(new SetCardInfo("Shivan Gorge", 326, Rarity.RARE, mage.cards.s.ShivanGorge.class)); - cards.add(new SetCardInfo("Shivan Hellkite", 214, Rarity.RARE, mage.cards.s.ShivanHellkite.class)); - cards.add(new SetCardInfo("Shivan Raptor", 215, Rarity.UNCOMMON, mage.cards.s.ShivanRaptor.class)); - cards.add(new SetCardInfo("Shiv's Embrace", 216, Rarity.UNCOMMON, mage.cards.s.ShivsEmbrace.class)); - cards.add(new SetCardInfo("Show and Tell", 96, Rarity.RARE, mage.cards.s.ShowAndTell.class)); - cards.add(new SetCardInfo("Shower of Sparks", 217, Rarity.COMMON, mage.cards.s.ShowerOfSparks.class)); - cards.add(new SetCardInfo("Sicken", 156, Rarity.COMMON, mage.cards.s.Sicken.class)); - cards.add(new SetCardInfo("Silent Attendant", 51, Rarity.COMMON, mage.cards.s.SilentAttendant.class)); - cards.add(new SetCardInfo("Skirge Familiar", 157, Rarity.UNCOMMON, mage.cards.s.SkirgeFamiliar.class)); - cards.add(new SetCardInfo("Skittering Skirge", 158, Rarity.COMMON, mage.cards.s.SkitteringSkirge.class)); - cards.add(new SetCardInfo("Sleeper Agent", 159, Rarity.RARE, mage.cards.s.SleeperAgent.class)); - cards.add(new SetCardInfo("Slippery Karst", 327, Rarity.COMMON, mage.cards.s.SlipperyKarst.class)); - cards.add(new SetCardInfo("Smokestack", 309, Rarity.RARE, mage.cards.s.Smokestack.class)); - cards.add(new SetCardInfo("Smoldering Crater", 328, Rarity.COMMON, mage.cards.s.SmolderingCrater.class)); - cards.add(new SetCardInfo("Sneak Attack", 218, Rarity.RARE, mage.cards.s.SneakAttack.class)); - cards.add(new SetCardInfo("Somnophore", 97, Rarity.RARE, mage.cards.s.Somnophore.class)); - cards.add(new SetCardInfo("Songstitcher", 52, Rarity.UNCOMMON, mage.cards.s.Songstitcher.class)); - cards.add(new SetCardInfo("Soul Sculptor", 53, Rarity.RARE, mage.cards.s.SoulSculptor.class)); - cards.add(new SetCardInfo("Spined Fluke", 160, Rarity.UNCOMMON, mage.cards.s.SpinedFluke.class)); - cards.add(new SetCardInfo("Spire Owl", 98, Rarity.COMMON, mage.cards.s.SpireOwl.class)); - cards.add(new SetCardInfo("Sporogenesis", 273, Rarity.RARE, mage.cards.s.Sporogenesis.class)); - cards.add(new SetCardInfo("Spreading Algae", 274, Rarity.UNCOMMON, mage.cards.s.SpreadingAlgae.class)); - cards.add(new SetCardInfo("Steam Blast", 219, Rarity.UNCOMMON, mage.cards.s.SteamBlast.class)); - cards.add(new SetCardInfo("Stern Proctor", 99, Rarity.UNCOMMON, mage.cards.s.SternProctor.class)); - cards.add(new SetCardInfo("Stroke of Genius", 100, Rarity.RARE, mage.cards.s.StrokeOfGenius.class)); - cards.add(new SetCardInfo("Sulfuric Vapors", 220, Rarity.RARE, mage.cards.s.SulfuricVapors.class)); - cards.add(new SetCardInfo("Sunder", 101, Rarity.RARE, mage.cards.s.Sunder.class)); - cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Symbiosis", 275, Rarity.COMMON, mage.cards.s.Symbiosis.class)); - cards.add(new SetCardInfo("Tainted Aether", 161, Rarity.RARE, mage.cards.t.TaintedAether.class)); - cards.add(new SetCardInfo("Telepathy", 102, Rarity.UNCOMMON, mage.cards.t.Telepathy.class)); - cards.add(new SetCardInfo("Temporal Aperture", 310, Rarity.RARE, mage.cards.t.TemporalAperture.class)); - cards.add(new SetCardInfo("Thran Quarry", 329, Rarity.RARE, mage.cards.t.ThranQuarry.class)); - cards.add(new SetCardInfo("Thran Turbine", 311, Rarity.UNCOMMON, mage.cards.t.ThranTurbine.class)); - cards.add(new SetCardInfo("Thundering Giant", 221, Rarity.UNCOMMON, mage.cards.t.ThunderingGiant.class)); - cards.add(new SetCardInfo("Time Spiral", 103, Rarity.RARE, mage.cards.t.TimeSpiral.class)); - cards.add(new SetCardInfo("Titania's Boon", 276, Rarity.UNCOMMON, mage.cards.t.TitaniasBoon.class)); - cards.add(new SetCardInfo("Titania's Chosen", 277, Rarity.UNCOMMON, mage.cards.t.TitaniasChosen.class)); - cards.add(new SetCardInfo("Tolarian Academy", 330, Rarity.RARE, mage.cards.t.TolarianAcademy.class)); - cards.add(new SetCardInfo("Tolarian Winds", 104, Rarity.COMMON, mage.cards.t.TolarianWinds.class)); - cards.add(new SetCardInfo("Torch Song", 222, Rarity.UNCOMMON, mage.cards.t.TorchSong.class)); - cards.add(new SetCardInfo("Treefolk Seedlings", 278, Rarity.UNCOMMON, mage.cards.t.TreefolkSeedlings.class)); - cards.add(new SetCardInfo("Treetop Rangers", 279, Rarity.COMMON, mage.cards.t.TreetopRangers.class)); - cards.add(new SetCardInfo("Turnabout", 105, Rarity.UNCOMMON, mage.cards.t.Turnabout.class)); - cards.add(new SetCardInfo("Umbilicus", 312, Rarity.RARE, mage.cards.u.Umbilicus.class)); - cards.add(new SetCardInfo("Unnerve", 162, Rarity.COMMON, mage.cards.u.Unnerve.class)); - cards.add(new SetCardInfo("Unworthy Dead", 163, Rarity.COMMON, mage.cards.u.UnworthyDead.class)); - cards.add(new SetCardInfo("Urza's Armor", 313, Rarity.UNCOMMON, mage.cards.u.UrzasArmor.class)); - cards.add(new SetCardInfo("Vampiric Embrace", 164, Rarity.UNCOMMON, mage.cards.v.VampiricEmbrace.class)); - cards.add(new SetCardInfo("Vebulid", 165, Rarity.RARE, mage.cards.v.Vebulid.class)); - cards.add(new SetCardInfo("Veil of Birds", 106, Rarity.COMMON, mage.cards.v.VeilOfBirds.class)); - cards.add(new SetCardInfo("Veiled Apparition", 107, Rarity.UNCOMMON, mage.cards.v.VeiledApparition.class)); - cards.add(new SetCardInfo("Veiled Crocodile", 108, Rarity.RARE, mage.cards.v.VeiledCrocodile.class)); - cards.add(new SetCardInfo("Veiled Sentry", 109, Rarity.UNCOMMON, mage.cards.v.VeiledSentry.class)); - cards.add(new SetCardInfo("Veiled Serpent", 110, Rarity.COMMON, mage.cards.v.VeiledSerpent.class)); - cards.add(new SetCardInfo("Venomous Fangs", 280, Rarity.COMMON, mage.cards.v.VenomousFangs.class)); - cards.add(new SetCardInfo("Vernal Bloom", 281, Rarity.RARE, mage.cards.v.VernalBloom.class)); - cards.add(new SetCardInfo("Viashino Outrider", 223, Rarity.COMMON, mage.cards.v.ViashinoOutrider.class)); - cards.add(new SetCardInfo("Viashino Runner", 224, Rarity.COMMON, mage.cards.v.ViashinoRunner.class)); - cards.add(new SetCardInfo("Viashino Sandswimmer", 225, Rarity.RARE, mage.cards.v.ViashinoSandswimmer.class)); - cards.add(new SetCardInfo("Viashino Weaponsmith", 226, Rarity.COMMON, mage.cards.v.ViashinoWeaponsmith.class)); - cards.add(new SetCardInfo("Victimize", 166, Rarity.UNCOMMON, mage.cards.v.Victimize.class)); - cards.add(new SetCardInfo("Vile Requiem", 167, Rarity.UNCOMMON, mage.cards.v.VileRequiem.class)); - cards.add(new SetCardInfo("Voice of Grace", 54, Rarity.UNCOMMON, mage.cards.v.VoiceOfGrace.class)); - cards.add(new SetCardInfo("Voice of Law", 55, Rarity.UNCOMMON, mage.cards.v.VoiceOfLaw.class)); - cards.add(new SetCardInfo("Voltaic Key", 314, Rarity.UNCOMMON, mage.cards.v.VoltaicKey.class)); - cards.add(new SetCardInfo("Vug Lizard", 227, Rarity.UNCOMMON, mage.cards.v.VugLizard.class)); - cards.add(new SetCardInfo("War Dance", 282, Rarity.UNCOMMON, mage.cards.w.WarDance.class)); - cards.add(new SetCardInfo("Wall of Junk", 315, Rarity.UNCOMMON, mage.cards.w.WallOfJunk.class)); - cards.add(new SetCardInfo("Waylay", 56, Rarity.UNCOMMON, mage.cards.w.Waylay.class)); - cards.add(new SetCardInfo("Western Paladin", 168, Rarity.RARE, mage.cards.w.WesternPaladin.class)); - cards.add(new SetCardInfo("Whetstone", 316, Rarity.RARE, mage.cards.w.Whetstone.class)); - cards.add(new SetCardInfo("Whirlwind", 283, Rarity.RARE, mage.cards.w.Whirlwind.class)); - cards.add(new SetCardInfo("Wild Dogs", 284, Rarity.COMMON, mage.cards.w.WildDogs.class)); - cards.add(new SetCardInfo("Wildfire", 228, Rarity.RARE, mage.cards.w.Wildfire.class)); - cards.add(new SetCardInfo("Windfall", 111, Rarity.UNCOMMON, mage.cards.w.Windfall.class)); - cards.add(new SetCardInfo("Winding Wurm", 285, Rarity.COMMON, mage.cards.w.WindingWurm.class)); - cards.add(new SetCardInfo("Wirecat", 317, Rarity.UNCOMMON, mage.cards.w.Wirecat.class)); - cards.add(new SetCardInfo("Witch Engine", 169, Rarity.RARE, mage.cards.w.WitchEngine.class)); - cards.add(new SetCardInfo("Wizard Mentor", 112, Rarity.COMMON, mage.cards.w.WizardMentor.class)); - cards.add(new SetCardInfo("Worn Powerstone", 318, Rarity.UNCOMMON, mage.cards.w.WornPowerstone.class)); - cards.add(new SetCardInfo("Worship", 57, Rarity.RARE, mage.cards.w.Worship.class)); - cards.add(new SetCardInfo("Yawgmoth's Edict", 170, Rarity.UNCOMMON, mage.cards.y.YawgmothsEdict.class)); - cards.add(new SetCardInfo("Yawgmoth's Will", 171, Rarity.RARE, mage.cards.y.YawgmothsWill.class)); - cards.add(new SetCardInfo("Zephid", 113, Rarity.RARE, mage.cards.z.Zephid.class)); - cards.add(new SetCardInfo("Zephid's Embrace", 114, Rarity.UNCOMMON, mage.cards.z.ZephidsEmbrace.class)); - } -} +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author Backfir3 + */ +public final class UrzasSaga extends ExpansionSet { + + private static final UrzasSaga instance = new UrzasSaga(); + + public static UrzasSaga getInstance() { + return instance; + } + + private UrzasSaga() { + super("Urza's Saga", "USG", ExpansionSet.buildDate(1998, 10, 12), SetType.EXPANSION); + this.blockName = "Urza"; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Absolute Grace", 1, Rarity.UNCOMMON, mage.cards.a.AbsoluteGrace.class)); + cards.add(new SetCardInfo("Absolute Law", 2, Rarity.UNCOMMON, mage.cards.a.AbsoluteLaw.class)); + cards.add(new SetCardInfo("Abundance", 229, Rarity.RARE, mage.cards.a.Abundance.class)); + cards.add(new SetCardInfo("Abyssal Horror", 115, Rarity.RARE, mage.cards.a.AbyssalHorror.class)); + cards.add(new SetCardInfo("Academy Researchers", 58, Rarity.UNCOMMON, mage.cards.a.AcademyResearchers.class)); + cards.add(new SetCardInfo("Acidic Soil", 172, Rarity.UNCOMMON, mage.cards.a.AcidicSoil.class)); + cards.add(new SetCardInfo("Acridian", 230, Rarity.COMMON, mage.cards.a.Acridian.class)); + cards.add(new SetCardInfo("Albino Troll", 231, Rarity.UNCOMMON, mage.cards.a.AlbinoTroll.class)); + cards.add(new SetCardInfo("Anaconda", 232, Rarity.UNCOMMON, mage.cards.a.Anaconda.class)); + cards.add(new SetCardInfo("Angelic Chorus", 3, Rarity.RARE, mage.cards.a.AngelicChorus.class)); + cards.add(new SetCardInfo("Angelic Page", 4, Rarity.COMMON, mage.cards.a.AngelicPage.class)); + cards.add(new SetCardInfo("Annul", 59, Rarity.COMMON, mage.cards.a.Annul.class)); + cards.add(new SetCardInfo("Antagonism", 173, Rarity.RARE, mage.cards.a.Antagonism.class)); + cards.add(new SetCardInfo("Arcane Laboratory", 60, Rarity.UNCOMMON, mage.cards.a.ArcaneLaboratory.class)); + cards.add(new SetCardInfo("Arc Lightning", 174, Rarity.COMMON, mage.cards.a.ArcLightning.class)); + cards.add(new SetCardInfo("Argothian Elder", 233, Rarity.UNCOMMON, mage.cards.a.ArgothianElder.class)); + cards.add(new SetCardInfo("Argothian Enchantress", 234, Rarity.RARE, mage.cards.a.ArgothianEnchantress.class)); + cards.add(new SetCardInfo("Argothian Swine", 235, Rarity.COMMON, mage.cards.a.ArgothianSwine.class)); + cards.add(new SetCardInfo("Argothian Wurm", 236, Rarity.RARE, mage.cards.a.ArgothianWurm.class)); + cards.add(new SetCardInfo("Attunement", 61, Rarity.RARE, mage.cards.a.Attunement.class)); + cards.add(new SetCardInfo("Back to Basics", 62, Rarity.RARE, mage.cards.b.BackToBasics.class)); + cards.add(new SetCardInfo("Barrin, Master Wizard", 63, Rarity.RARE, mage.cards.b.BarrinMasterWizard.class)); + cards.add(new SetCardInfo("Barrin's Codex", 286, Rarity.RARE, mage.cards.b.BarrinsCodex.class)); + cards.add(new SetCardInfo("Bedlam", 175, Rarity.RARE, mage.cards.b.Bedlam.class)); + cards.add(new SetCardInfo("Befoul", 116, Rarity.COMMON, mage.cards.b.Befoul.class)); + cards.add(new SetCardInfo("Bereavement", 117, Rarity.UNCOMMON, mage.cards.b.Bereavement.class)); + cards.add(new SetCardInfo("Blanchwood Armor", 237, Rarity.UNCOMMON, mage.cards.b.BlanchwoodArmor.class)); + cards.add(new SetCardInfo("Blanchwood Treefolk", 238, Rarity.COMMON, mage.cards.b.BlanchwoodTreefolk.class)); + cards.add(new SetCardInfo("Blasted Landscape", 319, Rarity.UNCOMMON, mage.cards.b.BlastedLandscape.class)); + cards.add(new SetCardInfo("Blood Vassal", 118, Rarity.COMMON, mage.cards.b.BloodVassal.class)); + cards.add(new SetCardInfo("Bog Raiders", 119, Rarity.COMMON, mage.cards.b.BogRaiders.class)); + cards.add(new SetCardInfo("Brand", 176, Rarity.RARE, mage.cards.b.Brand.class)); + cards.add(new SetCardInfo("Bravado", 177, Rarity.COMMON, mage.cards.b.Bravado.class)); + cards.add(new SetCardInfo("Breach", 120, Rarity.COMMON, mage.cards.b.Breach.class)); + cards.add(new SetCardInfo("Brilliant Halo", 5, Rarity.COMMON, mage.cards.b.BrilliantHalo.class)); + cards.add(new SetCardInfo("Bull Hippo", 239, Rarity.UNCOMMON, mage.cards.b.BullHippo.class)); + cards.add(new SetCardInfo("Bulwark", 178, Rarity.RARE, mage.cards.b.Bulwark.class)); + cards.add(new SetCardInfo("Cackling Fiend", 121, Rarity.COMMON, mage.cards.c.CacklingFiend.class)); + cards.add(new SetCardInfo("Carpet of Flowers", 240, Rarity.UNCOMMON, mage.cards.c.CarpetOfFlowers.class)); + cards.add(new SetCardInfo("Carrion Beetles", 122, Rarity.COMMON, mage.cards.c.CarrionBeetles.class)); + cards.add(new SetCardInfo("Catalog", 64, Rarity.COMMON, mage.cards.c.Catalog.class)); + cards.add(new SetCardInfo("Catastrophe", 6, Rarity.RARE, mage.cards.c.Catastrophe.class)); + cards.add(new SetCardInfo("Cathodion", 287, Rarity.UNCOMMON, mage.cards.c.Cathodion.class)); + cards.add(new SetCardInfo("Cave Tiger", 241, Rarity.COMMON, mage.cards.c.CaveTiger.class)); + cards.add(new SetCardInfo("Child of Gaea", 242, Rarity.RARE, mage.cards.c.ChildOfGaea.class)); + cards.add(new SetCardInfo("Chimeric Staff", 288, Rarity.RARE, mage.cards.c.ChimericStaff.class)); + cards.add(new SetCardInfo("Citanul Centaurs", 243, Rarity.RARE, mage.cards.c.CitanulCentaurs.class)); + cards.add(new SetCardInfo("Citanul Flute", 289, Rarity.RARE, mage.cards.c.CitanulFlute.class)); + cards.add(new SetCardInfo("Citanul Hierophants", 244, Rarity.RARE, mage.cards.c.CitanulHierophants.class)); + cards.add(new SetCardInfo("Claws of Gix", 290, Rarity.UNCOMMON, mage.cards.c.ClawsOfGix.class)); + cards.add(new SetCardInfo("Clear", 7, Rarity.UNCOMMON, mage.cards.c.Clear.class)); + cards.add(new SetCardInfo("Cloak of Mists", 65, Rarity.COMMON, mage.cards.c.CloakOfMists.class)); + cards.add(new SetCardInfo("Confiscate", 66, Rarity.UNCOMMON, mage.cards.c.Confiscate.class)); + cards.add(new SetCardInfo("Congregate", 8, Rarity.COMMON, mage.cards.c.Congregate.class)); + cards.add(new SetCardInfo("Contamination", 123, Rarity.RARE, mage.cards.c.Contamination.class)); + cards.add(new SetCardInfo("Copper Gnomes", 291, Rarity.RARE, mage.cards.c.CopperGnomes.class)); + cards.add(new SetCardInfo("Coral Merfolk", 67, Rarity.COMMON, mage.cards.c.CoralMerfolk.class)); + cards.add(new SetCardInfo("Corrupt", 124, Rarity.COMMON, mage.cards.c.Corrupt.class)); + cards.add(new SetCardInfo("Cradle Guard", 245, Rarity.UNCOMMON, mage.cards.c.CradleGuard.class)); + cards.add(new SetCardInfo("Crater Hellion", 179, Rarity.RARE, mage.cards.c.CraterHellion.class)); + cards.add(new SetCardInfo("Crazed Skirge", 125, Rarity.UNCOMMON, mage.cards.c.CrazedSkirge.class)); + cards.add(new SetCardInfo("Crosswinds", 246, Rarity.UNCOMMON, mage.cards.c.Crosswinds.class)); + cards.add(new SetCardInfo("Crystal Chimes", 292, Rarity.UNCOMMON, mage.cards.c.CrystalChimes.class)); + cards.add(new SetCardInfo("Curfew", 68, Rarity.COMMON, mage.cards.c.Curfew.class)); + cards.add(new SetCardInfo("Darkest Hour", 128, Rarity.RARE, mage.cards.d.DarkestHour.class)); + cards.add(new SetCardInfo("Dark Hatchling", 126, Rarity.RARE, mage.cards.d.DarkHatchling.class)); + cards.add(new SetCardInfo("Dark Ritual", 127, Rarity.COMMON, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Defensive Formation", 9, Rarity.UNCOMMON, mage.cards.d.DefensiveFormation.class)); + cards.add(new SetCardInfo("Despondency", 129, Rarity.COMMON, mage.cards.d.Despondency.class)); + cards.add(new SetCardInfo("Destructive Urge", 180, Rarity.UNCOMMON, mage.cards.d.DestructiveUrge.class)); + cards.add(new SetCardInfo("Diabolic Servitude", 130, Rarity.UNCOMMON, mage.cards.d.DiabolicServitude.class)); + cards.add(new SetCardInfo("Disciple of Grace", 10, Rarity.COMMON, mage.cards.d.DiscipleOfGrace.class)); + cards.add(new SetCardInfo("Disciple of Law", 11, Rarity.COMMON, mage.cards.d.DiscipleOfLaw.class)); + cards.add(new SetCardInfo("Discordant Dirge", 131, Rarity.RARE, mage.cards.d.DiscordantDirge.class)); + cards.add(new SetCardInfo("Disenchant", 12, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Disorder", 181, Rarity.UNCOMMON, mage.cards.d.Disorder.class)); + cards.add(new SetCardInfo("Disruptive Student", 69, Rarity.COMMON, mage.cards.d.DisruptiveStudent.class)); + cards.add(new SetCardInfo("Douse", 70, Rarity.UNCOMMON, mage.cards.d.Douse.class)); + cards.add(new SetCardInfo("Dragon Blood", 293, Rarity.UNCOMMON, mage.cards.d.DragonBlood.class)); + cards.add(new SetCardInfo("Drifting Djinn", 71, Rarity.RARE, mage.cards.d.DriftingDjinn.class)); + cards.add(new SetCardInfo("Drifting Meadow", 320, Rarity.COMMON, mage.cards.d.DriftingMeadow.class)); + cards.add(new SetCardInfo("Dromosaur", 182, Rarity.COMMON, mage.cards.d.Dromosaur.class)); + cards.add(new SetCardInfo("Duress", 132, Rarity.COMMON, mage.cards.d.Duress.class)); + cards.add(new SetCardInfo("Eastern Paladin", 133, Rarity.RARE, mage.cards.e.EasternPaladin.class)); + cards.add(new SetCardInfo("Electryte", 183, Rarity.RARE, mage.cards.e.Electryte.class)); + cards.add(new SetCardInfo("Elite Archers", 13, Rarity.RARE, mage.cards.e.EliteArchers.class)); + cards.add(new SetCardInfo("Elvish Herder", 247, Rarity.COMMON, mage.cards.e.ElvishHerder.class)); + cards.add(new SetCardInfo("Elvish Lyrist", 248, Rarity.COMMON, mage.cards.e.ElvishLyrist.class)); + cards.add(new SetCardInfo("Enchantment Alteration", 72, Rarity.UNCOMMON, mage.cards.e.EnchantmentAlteration.class)); + cards.add(new SetCardInfo("Endless Wurm", 249, Rarity.RARE, mage.cards.e.EndlessWurm.class)); + cards.add(new SetCardInfo("Endoskeleton", 294, Rarity.UNCOMMON, mage.cards.e.Endoskeleton.class)); + cards.add(new SetCardInfo("Energy Field", 73, Rarity.RARE, mage.cards.e.EnergyField.class)); + cards.add(new SetCardInfo("Exhaustion", 74, Rarity.UNCOMMON, mage.cards.e.Exhaustion.class)); + cards.add(new SetCardInfo("Exhume", 134, Rarity.COMMON, mage.cards.e.Exhume.class)); + cards.add(new SetCardInfo("Exploration", 250, Rarity.RARE, mage.cards.e.Exploration.class)); + cards.add(new SetCardInfo("Expunge", 135, Rarity.COMMON, mage.cards.e.Expunge.class)); + cards.add(new SetCardInfo("Faith Healer", 14, Rarity.RARE, mage.cards.f.FaithHealer.class)); + cards.add(new SetCardInfo("Falter", 184, Rarity.COMMON, mage.cards.f.Falter.class)); + cards.add(new SetCardInfo("Fault Line", 185, Rarity.RARE, mage.cards.f.FaultLine.class)); + cards.add(new SetCardInfo("Fecundity", 251, Rarity.UNCOMMON, mage.cards.f.Fecundity.class)); + cards.add(new SetCardInfo("Fertile Ground", 252, Rarity.COMMON, mage.cards.f.FertileGround.class)); + cards.add(new SetCardInfo("Fiery Mantle", 186, Rarity.COMMON, mage.cards.f.FieryMantle.class)); + cards.add(new SetCardInfo("Fire Ants", 187, Rarity.UNCOMMON, mage.cards.f.FireAnts.class)); + cards.add(new SetCardInfo("Flesh Reaver", 136, Rarity.UNCOMMON, mage.cards.f.FleshReaver.class)); + cards.add(new SetCardInfo("Fluctuator", 295, Rarity.RARE, mage.cards.f.Fluctuator.class)); + cards.add(new SetCardInfo("Fog Bank", 75, Rarity.UNCOMMON, mage.cards.f.FogBank.class)); + cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fortitude", 253, Rarity.COMMON, mage.cards.f.Fortitude.class)); + cards.add(new SetCardInfo("Gaea's Bounty", 254, Rarity.COMMON, mage.cards.g.GaeasBounty.class)); + cards.add(new SetCardInfo("Gaea's Cradle", 321, Rarity.RARE, mage.cards.g.GaeasCradle.class)); + cards.add(new SetCardInfo("Gaea's Embrace", 255, Rarity.UNCOMMON, mage.cards.g.GaeasEmbrace.class)); + cards.add(new SetCardInfo("Gamble", 188, Rarity.RARE, mage.cards.g.Gamble.class)); + cards.add(new SetCardInfo("Gilded Drake", 76, Rarity.RARE, mage.cards.g.GildedDrake.class)); + cards.add(new SetCardInfo("Glorious Anthem", 15, Rarity.RARE, mage.cards.g.GloriousAnthem.class)); + cards.add(new SetCardInfo("Goblin Cadets", 189, Rarity.UNCOMMON, mage.cards.g.GoblinCadets.class)); + cards.add(new SetCardInfo("Goblin Lackey", 190, Rarity.UNCOMMON, mage.cards.g.GoblinLackey.class)); + cards.add(new SetCardInfo("Goblin Matron", 191, Rarity.COMMON, mage.cards.g.GoblinMatron.class)); + cards.add(new SetCardInfo("Goblin Offensive", 192, Rarity.UNCOMMON, mage.cards.g.GoblinOffensive.class)); + cards.add(new SetCardInfo("Goblin Patrol", 193, Rarity.COMMON, mage.cards.g.GoblinPatrol.class)); + cards.add(new SetCardInfo("Goblin Raider", 194, Rarity.COMMON, mage.cards.g.GoblinRaider.class)); + cards.add(new SetCardInfo("Goblin Spelunkers", 195, Rarity.COMMON, mage.cards.g.GoblinSpelunkers.class)); + cards.add(new SetCardInfo("Goblin War Buggy", 196, Rarity.COMMON, mage.cards.g.GoblinWarBuggy.class)); + cards.add(new SetCardInfo("Gorilla Warrior", 256, Rarity.COMMON, mage.cards.g.GorillaWarrior.class)); + cards.add(new SetCardInfo("Grafted Skullcap", 296, Rarity.RARE, mage.cards.g.GraftedSkullcap.class)); + cards.add(new SetCardInfo("Greater Good", 257, Rarity.RARE, mage.cards.g.GreaterGood.class)); + cards.add(new SetCardInfo("Great Whale", 77, Rarity.RARE, mage.cards.g.GreatWhale.class)); + cards.add(new SetCardInfo("Greener Pastures", 258, Rarity.RARE, mage.cards.g.GreenerPastures.class)); + cards.add(new SetCardInfo("Guma", 197, Rarity.UNCOMMON, mage.cards.g.Guma.class)); + cards.add(new SetCardInfo("Hawkeater Moth", 259, Rarity.UNCOMMON, mage.cards.h.HawkeaterMoth.class)); + cards.add(new SetCardInfo("Headlong Rush", 198, Rarity.COMMON, mage.cards.h.HeadlongRush.class)); + cards.add(new SetCardInfo("Healing Salve", 16, Rarity.COMMON, mage.cards.h.HealingSalve.class)); + cards.add(new SetCardInfo("Heat Ray", 199, Rarity.COMMON, mage.cards.h.HeatRay.class)); + cards.add(new SetCardInfo("Herald of Serra", 17, Rarity.RARE, mage.cards.h.HeraldOfSerra.class)); + cards.add(new SetCardInfo("Hermetic Study", 78, Rarity.COMMON, mage.cards.h.HermeticStudy.class)); + cards.add(new SetCardInfo("Hibernation", 79, Rarity.UNCOMMON, mage.cards.h.Hibernation.class)); + cards.add(new SetCardInfo("Hidden Ancients", 260, Rarity.UNCOMMON, mage.cards.h.HiddenAncients.class)); + cards.add(new SetCardInfo("Hidden Guerrillas", 261, Rarity.UNCOMMON, mage.cards.h.HiddenGuerrillas.class)); + cards.add(new SetCardInfo("Hidden Herd", 262, Rarity.RARE, mage.cards.h.HiddenHerd.class)); + cards.add(new SetCardInfo("Hidden Predators", 263, Rarity.RARE, mage.cards.h.HiddenPredators.class)); + cards.add(new SetCardInfo("Hidden Spider", 264, Rarity.COMMON, mage.cards.h.HiddenSpider.class)); + cards.add(new SetCardInfo("Hidden Stag", 265, Rarity.RARE, mage.cards.h.HiddenStag.class)); + cards.add(new SetCardInfo("Hollow Dogs", 137, Rarity.COMMON, mage.cards.h.HollowDogs.class)); + cards.add(new SetCardInfo("Hopping Automaton", 297, Rarity.UNCOMMON, mage.cards.h.HoppingAutomaton.class)); + cards.add(new SetCardInfo("Horseshoe Crab", 80, Rarity.COMMON, mage.cards.h.HorseshoeCrab.class)); + cards.add(new SetCardInfo("Humble", 18, Rarity.UNCOMMON, mage.cards.h.Humble.class)); + cards.add(new SetCardInfo("Hush", 266, Rarity.COMMON, mage.cards.h.Hush.class)); + cards.add(new SetCardInfo("Ill-Gotten Gains", 138, Rarity.RARE, mage.cards.i.IllGottenGains.class)); + cards.add(new SetCardInfo("Imaginary Pet", 81, Rarity.RARE, mage.cards.i.ImaginaryPet.class)); + cards.add(new SetCardInfo("Intrepid Hero", 19, Rarity.RARE, mage.cards.i.IntrepidHero.class)); + cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jagged Lightning", 200, Rarity.UNCOMMON, mage.cards.j.JaggedLightning.class)); + cards.add(new SetCardInfo("Karn, Silver Golem", 298, Rarity.RARE, mage.cards.k.KarnSilverGolem.class)); + cards.add(new SetCardInfo("Launch", 82, Rarity.COMMON, mage.cards.l.Launch.class)); + cards.add(new SetCardInfo("Lay Waste", 201, Rarity.COMMON, mage.cards.l.LayWaste.class)); + cards.add(new SetCardInfo("Lifeline", 299, Rarity.RARE, mage.cards.l.Lifeline.class)); + cards.add(new SetCardInfo("Lightning Dragon", 202, Rarity.RARE, mage.cards.l.LightningDragon.class)); + cards.add(new SetCardInfo("Lilting Refrain", 83, Rarity.UNCOMMON, mage.cards.l.LiltingRefrain.class)); + cards.add(new SetCardInfo("Lingering Mirage", 84, Rarity.UNCOMMON, mage.cards.l.LingeringMirage.class)); + cards.add(new SetCardInfo("Looming Shade", 139, Rarity.COMMON, mage.cards.l.LoomingShade.class)); + cards.add(new SetCardInfo("Lotus Blossom", 300, Rarity.RARE, mage.cards.l.LotusBlossom.class)); + cards.add(new SetCardInfo("Lull", 267, Rarity.COMMON, mage.cards.l.Lull.class)); + cards.add(new SetCardInfo("Lurking Evil", 140, Rarity.RARE, mage.cards.l.LurkingEvil.class)); + cards.add(new SetCardInfo("Mana Leech", 141, Rarity.UNCOMMON, mage.cards.m.ManaLeech.class)); + cards.add(new SetCardInfo("Meltdown", 203, Rarity.UNCOMMON, mage.cards.m.Meltdown.class)); + cards.add(new SetCardInfo("Metrognome", 301, Rarity.RARE, mage.cards.m.Metrognome.class)); + cards.add(new SetCardInfo("Midsummer Revel", 268, Rarity.RARE, mage.cards.m.MidsummerRevel.class)); + cards.add(new SetCardInfo("Mishra's Helix", 302, Rarity.RARE, mage.cards.m.MishrasHelix.class)); + cards.add(new SetCardInfo("Mobile Fort", 303, Rarity.UNCOMMON, mage.cards.m.MobileFort.class)); + cards.add(new SetCardInfo("Monk Idealist", 20, Rarity.UNCOMMON, mage.cards.m.MonkIdealist.class)); + cards.add(new SetCardInfo("Monk Realist", 21, Rarity.COMMON, mage.cards.m.MonkRealist.class)); + cards.add(new SetCardInfo("Morphling", 85, Rarity.RARE, mage.cards.m.Morphling.class)); + cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("No Rest for the Wicked", 142, Rarity.UNCOMMON, mage.cards.n.NoRestForTheWicked.class)); + cards.add(new SetCardInfo("Noetic Scales", 304, Rarity.RARE, mage.cards.n.NoeticScales.class)); + cards.add(new SetCardInfo("Okk", 204, Rarity.RARE, mage.cards.o.Okk.class)); + cards.add(new SetCardInfo("Opal Acrolith", 22, Rarity.UNCOMMON, mage.cards.o.OpalAcrolith.class)); + cards.add(new SetCardInfo("Opal Archangel", 23, Rarity.RARE, mage.cards.o.OpalArchangel.class)); + cards.add(new SetCardInfo("Opal Caryatid", 24, Rarity.COMMON, mage.cards.o.OpalCaryatid.class)); + cards.add(new SetCardInfo("Opal Gargoyle", 25, Rarity.COMMON, mage.cards.o.OpalGargoyle.class)); + cards.add(new SetCardInfo("Opal Titan", 26, Rarity.RARE, mage.cards.o.OpalTitan.class)); + cards.add(new SetCardInfo("Oppression", 143, Rarity.RARE, mage.cards.o.Oppression.class)); + cards.add(new SetCardInfo("Order of Yawgmoth", 144, Rarity.UNCOMMON, mage.cards.o.OrderOfYawgmoth.class)); + cards.add(new SetCardInfo("Outmaneuver", 205, Rarity.UNCOMMON, mage.cards.o.Outmaneuver.class)); + cards.add(new SetCardInfo("Pacifism", 27, Rarity.COMMON, mage.cards.p.Pacifism.class)); + cards.add(new SetCardInfo("Parasitic Bond", 145, Rarity.UNCOMMON, mage.cards.p.ParasiticBond.class)); + cards.add(new SetCardInfo("Pariah", 28, Rarity.RARE, mage.cards.p.Pariah.class)); + cards.add(new SetCardInfo("Path of Peace", 29, Rarity.COMMON, mage.cards.p.PathOfPeace.class)); + cards.add(new SetCardInfo("Pegasus Charger", 30, Rarity.COMMON, mage.cards.p.PegasusCharger.class)); + cards.add(new SetCardInfo("Pendrell Drake", 86, Rarity.COMMON, mage.cards.p.PendrellDrake.class)); + cards.add(new SetCardInfo("Pendrell Flux", 87, Rarity.COMMON, mage.cards.p.PendrellFlux.class)); + cards.add(new SetCardInfo("Peregrine Drake", 88, Rarity.UNCOMMON, mage.cards.p.PeregrineDrake.class)); + cards.add(new SetCardInfo("Persecute", 146, Rarity.RARE, mage.cards.p.Persecute.class)); + cards.add(new SetCardInfo("Pestilence", 147, Rarity.COMMON, mage.cards.p.Pestilence.class)); + cards.add(new SetCardInfo("Phyrexian Colossus", 305, Rarity.RARE, mage.cards.p.PhyrexianColossus.class)); + cards.add(new SetCardInfo("Phyrexian Ghoul", 148, Rarity.COMMON, mage.cards.p.PhyrexianGhoul.class)); + cards.add(new SetCardInfo("Phyrexian Processor", 306, Rarity.RARE, mage.cards.p.PhyrexianProcessor.class)); + cards.add(new SetCardInfo("Phyrexian Tower", 322, Rarity.RARE, mage.cards.p.PhyrexianTower.class)); + cards.add(new SetCardInfo("Pit Trap", 307, Rarity.UNCOMMON, mage.cards.p.PitTrap.class)); + cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Planar Birth", 31, Rarity.RARE, mage.cards.p.PlanarBirth.class)); + cards.add(new SetCardInfo("Planar Void", 149, Rarity.UNCOMMON, mage.cards.p.PlanarVoid.class)); + cards.add(new SetCardInfo("Polluted Mire", 323, Rarity.COMMON, mage.cards.p.PollutedMire.class)); + cards.add(new SetCardInfo("Pouncing Jaguar", 269, Rarity.COMMON, mage.cards.p.PouncingJaguar.class)); + cards.add(new SetCardInfo("Power Sink", 89, Rarity.COMMON, mage.cards.p.PowerSink.class)); + cards.add(new SetCardInfo("Power Taint", 90, Rarity.COMMON, mage.cards.p.PowerTaint.class)); + cards.add(new SetCardInfo("Presence of the Master", 32, Rarity.UNCOMMON, mage.cards.p.PresenceOfTheMaster.class)); + cards.add(new SetCardInfo("Priest of Gix", 150, Rarity.UNCOMMON, mage.cards.p.PriestOfGix.class)); + cards.add(new SetCardInfo("Priest of Titania", 270, Rarity.COMMON, mage.cards.p.PriestOfTitania.class)); + cards.add(new SetCardInfo("Purging Scythe", 308, Rarity.RARE, mage.cards.p.PurgingScythe.class)); + cards.add(new SetCardInfo("Rain of Filth", 151, Rarity.UNCOMMON, mage.cards.r.RainOfFilth.class)); + cards.add(new SetCardInfo("Rain of Salt", 206, Rarity.UNCOMMON, mage.cards.r.RainOfSalt.class)); + cards.add(new SetCardInfo("Ravenous Skirge", 152, Rarity.COMMON, mage.cards.r.RavenousSkirge.class)); + cards.add(new SetCardInfo("Raze", 207, Rarity.COMMON, mage.cards.r.Raze.class)); + cards.add(new SetCardInfo("Recantation", 91, Rarity.RARE, mage.cards.r.Recantation.class)); + cards.add(new SetCardInfo("Reclusive Wight", 153, Rarity.UNCOMMON, mage.cards.r.ReclusiveWight.class)); + cards.add(new SetCardInfo("Redeem", 33, Rarity.UNCOMMON, mage.cards.r.Redeem.class)); + cards.add(new SetCardInfo("Reflexes", 208, Rarity.COMMON, mage.cards.r.Reflexes.class)); + cards.add(new SetCardInfo("Rejuvenate", 271, Rarity.COMMON, mage.cards.r.Rejuvenate.class)); + cards.add(new SetCardInfo("Remembrance", 34, Rarity.RARE, mage.cards.r.Remembrance.class)); + cards.add(new SetCardInfo("Remote Isle", 324, Rarity.COMMON, mage.cards.r.RemoteIsle.class)); + cards.add(new SetCardInfo("Reprocess", 154, Rarity.RARE, mage.cards.r.Reprocess.class)); + cards.add(new SetCardInfo("Rescind", 92, Rarity.COMMON, mage.cards.r.Rescind.class)); + cards.add(new SetCardInfo("Retaliation", 272, Rarity.UNCOMMON, mage.cards.r.Retaliation.class)); + cards.add(new SetCardInfo("Retromancer", 209, Rarity.COMMON, mage.cards.r.Retromancer.class)); + cards.add(new SetCardInfo("Rewind", 93, Rarity.COMMON, mage.cards.r.Rewind.class)); + cards.add(new SetCardInfo("Rumbling Crescendo", 210, Rarity.RARE, mage.cards.r.RumblingCrescendo.class)); + cards.add(new SetCardInfo("Rune of Protection: Artifacts", 35, Rarity.UNCOMMON, mage.cards.r.RuneOfProtectionArtifacts.class)); + cards.add(new SetCardInfo("Rune of Protection: Black", 36, Rarity.COMMON, mage.cards.r.RuneOfProtectionBlack.class)); + cards.add(new SetCardInfo("Rune of Protection: Blue", 37, Rarity.COMMON, mage.cards.r.RuneOfProtectionBlue.class)); + cards.add(new SetCardInfo("Rune of Protection: Green", 38, Rarity.COMMON, mage.cards.r.RuneOfProtectionGreen.class)); + cards.add(new SetCardInfo("Rune of Protection: Lands", 39, Rarity.RARE, mage.cards.r.RuneOfProtectionLands.class)); + cards.add(new SetCardInfo("Rune of Protection: Red", 40, Rarity.COMMON, mage.cards.r.RuneOfProtectionRed.class)); + cards.add(new SetCardInfo("Rune of Protection: White", 41, Rarity.COMMON, mage.cards.r.RuneOfProtectionWhite.class)); + cards.add(new SetCardInfo("Sanctum Custodian", 42, Rarity.COMMON, mage.cards.s.SanctumCustodian.class)); + cards.add(new SetCardInfo("Sanctum Guardian", 43, Rarity.UNCOMMON, mage.cards.s.SanctumGuardian.class)); + cards.add(new SetCardInfo("Sandbar Merfolk", 94, Rarity.COMMON, mage.cards.s.SandbarMerfolk.class)); + cards.add(new SetCardInfo("Sandbar Serpent", 95, Rarity.UNCOMMON, mage.cards.s.SandbarSerpent.class)); + cards.add(new SetCardInfo("Sanguine Guard", 155, Rarity.UNCOMMON, mage.cards.s.SanguineGuard.class)); + cards.add(new SetCardInfo("Scald", 211, Rarity.UNCOMMON, mage.cards.s.Scald.class)); + cards.add(new SetCardInfo("Scoria Wurm", 212, Rarity.RARE, mage.cards.s.ScoriaWurm.class)); + cards.add(new SetCardInfo("Scrap", 213, Rarity.COMMON, mage.cards.s.Scrap.class)); + cards.add(new SetCardInfo("Seasoned Marshal", 44, Rarity.UNCOMMON, mage.cards.s.SeasonedMarshal.class)); + cards.add(new SetCardInfo("Serra Avatar", 45, Rarity.RARE, mage.cards.s.SerraAvatar.class)); + cards.add(new SetCardInfo("Serra's Embrace", 47, Rarity.UNCOMMON, mage.cards.s.SerrasEmbrace.class)); + cards.add(new SetCardInfo("Serra's Hymn", 48, Rarity.UNCOMMON, mage.cards.s.SerrasHymn.class)); + cards.add(new SetCardInfo("Serra's Liturgy", 49, Rarity.RARE, mage.cards.s.SerrasLiturgy.class)); + cards.add(new SetCardInfo("Serra's Sanctum", 325, Rarity.RARE, mage.cards.s.SerrasSanctum.class)); + cards.add(new SetCardInfo("Serra Zealot", 46, Rarity.COMMON, mage.cards.s.SerraZealot.class)); + cards.add(new SetCardInfo("Shimmering Barrier", 50, Rarity.UNCOMMON, mage.cards.s.ShimmeringBarrier.class)); + cards.add(new SetCardInfo("Shivan Gorge", 326, Rarity.RARE, mage.cards.s.ShivanGorge.class)); + cards.add(new SetCardInfo("Shivan Hellkite", 214, Rarity.RARE, mage.cards.s.ShivanHellkite.class)); + cards.add(new SetCardInfo("Shivan Raptor", 215, Rarity.UNCOMMON, mage.cards.s.ShivanRaptor.class)); + cards.add(new SetCardInfo("Shiv's Embrace", 216, Rarity.UNCOMMON, mage.cards.s.ShivsEmbrace.class)); + cards.add(new SetCardInfo("Show and Tell", 96, Rarity.RARE, mage.cards.s.ShowAndTell.class)); + cards.add(new SetCardInfo("Shower of Sparks", 217, Rarity.COMMON, mage.cards.s.ShowerOfSparks.class)); + cards.add(new SetCardInfo("Sicken", 156, Rarity.COMMON, mage.cards.s.Sicken.class)); + cards.add(new SetCardInfo("Silent Attendant", 51, Rarity.COMMON, mage.cards.s.SilentAttendant.class)); + cards.add(new SetCardInfo("Skirge Familiar", 157, Rarity.UNCOMMON, mage.cards.s.SkirgeFamiliar.class)); + cards.add(new SetCardInfo("Skittering Skirge", 158, Rarity.COMMON, mage.cards.s.SkitteringSkirge.class)); + cards.add(new SetCardInfo("Sleeper Agent", 159, Rarity.RARE, mage.cards.s.SleeperAgent.class)); + cards.add(new SetCardInfo("Slippery Karst", 327, Rarity.COMMON, mage.cards.s.SlipperyKarst.class)); + cards.add(new SetCardInfo("Smokestack", 309, Rarity.RARE, mage.cards.s.Smokestack.class)); + cards.add(new SetCardInfo("Smoldering Crater", 328, Rarity.COMMON, mage.cards.s.SmolderingCrater.class)); + cards.add(new SetCardInfo("Sneak Attack", 218, Rarity.RARE, mage.cards.s.SneakAttack.class)); + cards.add(new SetCardInfo("Somnophore", 97, Rarity.RARE, mage.cards.s.Somnophore.class)); + cards.add(new SetCardInfo("Songstitcher", 52, Rarity.UNCOMMON, mage.cards.s.Songstitcher.class)); + cards.add(new SetCardInfo("Soul Sculptor", 53, Rarity.RARE, mage.cards.s.SoulSculptor.class)); + cards.add(new SetCardInfo("Spined Fluke", 160, Rarity.UNCOMMON, mage.cards.s.SpinedFluke.class)); + cards.add(new SetCardInfo("Spire Owl", 98, Rarity.COMMON, mage.cards.s.SpireOwl.class)); + cards.add(new SetCardInfo("Sporogenesis", 273, Rarity.RARE, mage.cards.s.Sporogenesis.class)); + cards.add(new SetCardInfo("Spreading Algae", 274, Rarity.UNCOMMON, mage.cards.s.SpreadingAlgae.class)); + cards.add(new SetCardInfo("Steam Blast", 219, Rarity.UNCOMMON, mage.cards.s.SteamBlast.class)); + cards.add(new SetCardInfo("Stern Proctor", 99, Rarity.UNCOMMON, mage.cards.s.SternProctor.class)); + cards.add(new SetCardInfo("Stroke of Genius", 100, Rarity.RARE, mage.cards.s.StrokeOfGenius.class)); + cards.add(new SetCardInfo("Sulfuric Vapors", 220, Rarity.RARE, mage.cards.s.SulfuricVapors.class)); + cards.add(new SetCardInfo("Sunder", 101, Rarity.RARE, mage.cards.s.Sunder.class)); + cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Symbiosis", 275, Rarity.COMMON, mage.cards.s.Symbiosis.class)); + cards.add(new SetCardInfo("Tainted Aether", 161, Rarity.RARE, mage.cards.t.TaintedAether.class)); + cards.add(new SetCardInfo("Telepathy", 102, Rarity.UNCOMMON, mage.cards.t.Telepathy.class)); + cards.add(new SetCardInfo("Temporal Aperture", 310, Rarity.RARE, mage.cards.t.TemporalAperture.class)); + cards.add(new SetCardInfo("Thran Quarry", 329, Rarity.RARE, mage.cards.t.ThranQuarry.class)); + cards.add(new SetCardInfo("Thran Turbine", 311, Rarity.UNCOMMON, mage.cards.t.ThranTurbine.class)); + cards.add(new SetCardInfo("Thundering Giant", 221, Rarity.UNCOMMON, mage.cards.t.ThunderingGiant.class)); + cards.add(new SetCardInfo("Time Spiral", 103, Rarity.RARE, mage.cards.t.TimeSpiral.class)); + cards.add(new SetCardInfo("Titania's Boon", 276, Rarity.UNCOMMON, mage.cards.t.TitaniasBoon.class)); + cards.add(new SetCardInfo("Titania's Chosen", 277, Rarity.UNCOMMON, mage.cards.t.TitaniasChosen.class)); + cards.add(new SetCardInfo("Tolarian Academy", 330, Rarity.RARE, mage.cards.t.TolarianAcademy.class)); + cards.add(new SetCardInfo("Tolarian Winds", 104, Rarity.COMMON, mage.cards.t.TolarianWinds.class)); + cards.add(new SetCardInfo("Torch Song", 222, Rarity.UNCOMMON, mage.cards.t.TorchSong.class)); + cards.add(new SetCardInfo("Treefolk Seedlings", 278, Rarity.UNCOMMON, mage.cards.t.TreefolkSeedlings.class)); + cards.add(new SetCardInfo("Treetop Rangers", 279, Rarity.COMMON, mage.cards.t.TreetopRangers.class)); + cards.add(new SetCardInfo("Turnabout", 105, Rarity.UNCOMMON, mage.cards.t.Turnabout.class)); + cards.add(new SetCardInfo("Umbilicus", 312, Rarity.RARE, mage.cards.u.Umbilicus.class)); + cards.add(new SetCardInfo("Unnerve", 162, Rarity.COMMON, mage.cards.u.Unnerve.class)); + cards.add(new SetCardInfo("Unworthy Dead", 163, Rarity.COMMON, mage.cards.u.UnworthyDead.class)); + cards.add(new SetCardInfo("Urza's Armor", 313, Rarity.UNCOMMON, mage.cards.u.UrzasArmor.class)); + cards.add(new SetCardInfo("Vampiric Embrace", 164, Rarity.UNCOMMON, mage.cards.v.VampiricEmbrace.class)); + cards.add(new SetCardInfo("Vebulid", 165, Rarity.RARE, mage.cards.v.Vebulid.class)); + cards.add(new SetCardInfo("Veil of Birds", 106, Rarity.COMMON, mage.cards.v.VeilOfBirds.class)); + cards.add(new SetCardInfo("Veiled Apparition", 107, Rarity.UNCOMMON, mage.cards.v.VeiledApparition.class)); + cards.add(new SetCardInfo("Veiled Crocodile", 108, Rarity.RARE, mage.cards.v.VeiledCrocodile.class)); + cards.add(new SetCardInfo("Veiled Sentry", 109, Rarity.UNCOMMON, mage.cards.v.VeiledSentry.class)); + cards.add(new SetCardInfo("Veiled Serpent", 110, Rarity.COMMON, mage.cards.v.VeiledSerpent.class)); + cards.add(new SetCardInfo("Venomous Fangs", 280, Rarity.COMMON, mage.cards.v.VenomousFangs.class)); + cards.add(new SetCardInfo("Vernal Bloom", 281, Rarity.RARE, mage.cards.v.VernalBloom.class)); + cards.add(new SetCardInfo("Viashino Outrider", 223, Rarity.COMMON, mage.cards.v.ViashinoOutrider.class)); + cards.add(new SetCardInfo("Viashino Runner", 224, Rarity.COMMON, mage.cards.v.ViashinoRunner.class)); + cards.add(new SetCardInfo("Viashino Sandswimmer", 225, Rarity.RARE, mage.cards.v.ViashinoSandswimmer.class)); + cards.add(new SetCardInfo("Viashino Weaponsmith", 226, Rarity.COMMON, mage.cards.v.ViashinoWeaponsmith.class)); + cards.add(new SetCardInfo("Victimize", 166, Rarity.UNCOMMON, mage.cards.v.Victimize.class)); + cards.add(new SetCardInfo("Vile Requiem", 167, Rarity.UNCOMMON, mage.cards.v.VileRequiem.class)); + cards.add(new SetCardInfo("Voice of Grace", 54, Rarity.UNCOMMON, mage.cards.v.VoiceOfGrace.class)); + cards.add(new SetCardInfo("Voice of Law", 55, Rarity.UNCOMMON, mage.cards.v.VoiceOfLaw.class)); + cards.add(new SetCardInfo("Voltaic Key", 314, Rarity.UNCOMMON, mage.cards.v.VoltaicKey.class)); + cards.add(new SetCardInfo("Vug Lizard", 227, Rarity.UNCOMMON, mage.cards.v.VugLizard.class)); + cards.add(new SetCardInfo("War Dance", 282, Rarity.UNCOMMON, mage.cards.w.WarDance.class)); + cards.add(new SetCardInfo("Wall of Junk", 315, Rarity.UNCOMMON, mage.cards.w.WallOfJunk.class)); + cards.add(new SetCardInfo("Waylay", 56, Rarity.UNCOMMON, mage.cards.w.Waylay.class)); + cards.add(new SetCardInfo("Western Paladin", 168, Rarity.RARE, mage.cards.w.WesternPaladin.class)); + cards.add(new SetCardInfo("Whetstone", 316, Rarity.RARE, mage.cards.w.Whetstone.class)); + cards.add(new SetCardInfo("Whirlwind", 283, Rarity.RARE, mage.cards.w.Whirlwind.class)); + cards.add(new SetCardInfo("Wild Dogs", 284, Rarity.COMMON, mage.cards.w.WildDogs.class)); + cards.add(new SetCardInfo("Wildfire", 228, Rarity.RARE, mage.cards.w.Wildfire.class)); + cards.add(new SetCardInfo("Windfall", 111, Rarity.UNCOMMON, mage.cards.w.Windfall.class)); + cards.add(new SetCardInfo("Winding Wurm", 285, Rarity.COMMON, mage.cards.w.WindingWurm.class)); + cards.add(new SetCardInfo("Wirecat", 317, Rarity.UNCOMMON, mage.cards.w.Wirecat.class)); + cards.add(new SetCardInfo("Witch Engine", 169, Rarity.RARE, mage.cards.w.WitchEngine.class)); + cards.add(new SetCardInfo("Wizard Mentor", 112, Rarity.COMMON, mage.cards.w.WizardMentor.class)); + cards.add(new SetCardInfo("Worn Powerstone", 318, Rarity.UNCOMMON, mage.cards.w.WornPowerstone.class)); + cards.add(new SetCardInfo("Worship", 57, Rarity.RARE, mage.cards.w.Worship.class)); + cards.add(new SetCardInfo("Yawgmoth's Edict", 170, Rarity.UNCOMMON, mage.cards.y.YawgmothsEdict.class)); + cards.add(new SetCardInfo("Yawgmoth's Will", 171, Rarity.RARE, mage.cards.y.YawgmothsWill.class)); + cards.add(new SetCardInfo("Zephid", 113, Rarity.RARE, mage.cards.z.Zephid.class)); + cards.add(new SetCardInfo("Zephid's Embrace", 114, Rarity.UNCOMMON, mage.cards.z.ZephidsEmbrace.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Visions.java b/Mage.Sets/src/mage/sets/Visions.java index 31852388b5..57c609947e 100644 --- a/Mage.Sets/src/mage/sets/Visions.java +++ b/Mage.Sets/src/mage/sets/Visions.java @@ -1,192 +1,192 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author North - */ -public final class Visions extends ExpansionSet { - - private static final Visions instance = new Visions(); - - public static Visions getInstance() { - return instance; - } - - private Visions() { - super("Visions", "VIS", ExpansionSet.buildDate(1997, 1, 11), SetType.EXPANSION); - this.blockName = "Mirage"; - this.parentSet = Mirage.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Aku Djinn", 51, Rarity.RARE, mage.cards.a.AkuDjinn.class)); - cards.add(new SetCardInfo("Anvil of Bogardan", 141, Rarity.RARE, mage.cards.a.AnvilOfBogardan.class)); - cards.add(new SetCardInfo("Archangel", 1, Rarity.RARE, mage.cards.a.Archangel.class)); - cards.add(new SetCardInfo("Army Ants", 126, Rarity.UNCOMMON, mage.cards.a.ArmyAnts.class)); - cards.add(new SetCardInfo("Betrayal", 26, Rarity.COMMON, mage.cards.b.Betrayal.class)); - cards.add(new SetCardInfo("Blanket of Night", 52, Rarity.UNCOMMON, mage.cards.b.BlanketOfNight.class)); - cards.add(new SetCardInfo("Bogardan Phoenix", 76, Rarity.RARE, mage.cards.b.BogardanPhoenix.class)); - cards.add(new SetCardInfo("Brass-Talon Chimera", 142, Rarity.UNCOMMON, mage.cards.b.BrassTalonChimera.class)); - cards.add(new SetCardInfo("Breathstealer's Crypt", 127, Rarity.RARE, mage.cards.b.BreathstealersCrypt.class)); - cards.add(new SetCardInfo("Breezekeeper", 27, Rarity.COMMON, mage.cards.b.Breezekeeper.class)); - cards.add(new SetCardInfo("Brood of Cockroaches", 53, Rarity.UNCOMMON, mage.cards.b.BroodOfCockroaches.class)); - cards.add(new SetCardInfo("Bull Elephant", 101, Rarity.COMMON, mage.cards.b.BullElephant.class)); - cards.add(new SetCardInfo("Chronatog", 28, Rarity.RARE, mage.cards.c.Chronatog.class)); - cards.add(new SetCardInfo("City of Solitude", 102, Rarity.RARE, mage.cards.c.CityOfSolitude.class)); - cards.add(new SetCardInfo("Cloud Elemental", 29, Rarity.COMMON, mage.cards.c.CloudElemental.class)); - cards.add(new SetCardInfo("Coercion", 54, Rarity.COMMON, mage.cards.c.Coercion.class)); - cards.add(new SetCardInfo("Coral Atoll", 160, Rarity.UNCOMMON, mage.cards.c.CoralAtoll.class)); - cards.add(new SetCardInfo("Corrosion", 128, Rarity.RARE, mage.cards.c.Corrosion.class)); - cards.add(new SetCardInfo("Creeping Mold", 103, Rarity.UNCOMMON, mage.cards.c.CreepingMold.class)); - cards.add(new SetCardInfo("Crypt Rats", 55, Rarity.COMMON, mage.cards.c.CryptRats.class)); - cards.add(new SetCardInfo("Daraja Griffin", 2, Rarity.UNCOMMON, mage.cards.d.DarajaGriffin.class)); - cards.add(new SetCardInfo("Dark Privilege", 56, Rarity.COMMON, mage.cards.d.DarkPrivilege.class)); - cards.add(new SetCardInfo("Death Watch", 57, Rarity.COMMON, mage.cards.d.DeathWatch.class)); - cards.add(new SetCardInfo("Desertion", 30, Rarity.RARE, mage.cards.d.Desertion.class)); - cards.add(new SetCardInfo("Desolation", 58, Rarity.UNCOMMON, mage.cards.d.Desolation.class)); - cards.add(new SetCardInfo("Diamond Kaleidoscope", 143, Rarity.RARE, mage.cards.d.DiamondKaleidoscope.class)); - cards.add(new SetCardInfo("Dormant Volcano", 161, Rarity.UNCOMMON, mage.cards.d.DormantVolcano.class)); - cards.add(new SetCardInfo("Dragon Mask", 144, Rarity.UNCOMMON, mage.cards.d.DragonMask.class)); - cards.add(new SetCardInfo("Dream Tides", 31, Rarity.UNCOMMON, mage.cards.d.DreamTides.class)); - cards.add(new SetCardInfo("Dwarven Vigilantes", 77, Rarity.COMMON, mage.cards.d.DwarvenVigilantes.class)); - cards.add(new SetCardInfo("Elephant Grass", 104, Rarity.UNCOMMON, mage.cards.e.ElephantGrass.class)); - cards.add(new SetCardInfo("Elkin Lair", 78, Rarity.RARE, mage.cards.e.ElkinLair.class)); - cards.add(new SetCardInfo("Elven Cache", 105, Rarity.COMMON, mage.cards.e.ElvenCache.class)); - cards.add(new SetCardInfo("Emerald Charm", 106, Rarity.COMMON, mage.cards.e.EmeraldCharm.class)); - cards.add(new SetCardInfo("Equipoise", 3, Rarity.RARE, mage.cards.e.Equipoise.class)); - cards.add(new SetCardInfo("Everglades", 162, Rarity.UNCOMMON, mage.cards.e.Everglades.class)); - cards.add(new SetCardInfo("Eye of Singularity", 4, Rarity.RARE, mage.cards.e.EyeOfSingularity.class)); - cards.add(new SetCardInfo("Fallen Askari", 59, Rarity.COMMON, mage.cards.f.FallenAskari.class)); - cards.add(new SetCardInfo("Femeref Enchantress", 129, Rarity.RARE, mage.cards.f.FemerefEnchantress.class)); - cards.add(new SetCardInfo("Feral Instinct", 107, Rarity.COMMON, mage.cards.f.FeralInstinct.class)); - cards.add(new SetCardInfo("Fireblast", 79, Rarity.COMMON, mage.cards.f.Fireblast.class)); - cards.add(new SetCardInfo("Firestorm Hellkite", 130, Rarity.RARE, mage.cards.f.FirestormHellkite.class)); - cards.add(new SetCardInfo("Flooded Shoreline", 32, Rarity.RARE, mage.cards.f.FloodedShoreline.class)); - cards.add(new SetCardInfo("Foreshadow", 33, Rarity.UNCOMMON, mage.cards.f.Foreshadow.class)); - cards.add(new SetCardInfo("Freewind Falcon", 5, Rarity.COMMON, mage.cards.f.FreewindFalcon.class)); - cards.add(new SetCardInfo("Funeral Charm", 61, Rarity.COMMON, mage.cards.f.FuneralCharm.class)); - cards.add(new SetCardInfo("Giant Caterpillar", 108, Rarity.COMMON, mage.cards.g.GiantCaterpillar.class)); - cards.add(new SetCardInfo("Goblin Recruiter", 80, Rarity.UNCOMMON, mage.cards.g.GoblinRecruiter.class)); - cards.add(new SetCardInfo("Goblin Swine-Rider", 81, Rarity.COMMON, mage.cards.g.GoblinSwineRider.class)); - cards.add(new SetCardInfo("Gossamer Chains", 6, Rarity.COMMON, mage.cards.g.GossamerChains.class)); - cards.add(new SetCardInfo("Griffin Canyon", 163, Rarity.RARE, mage.cards.g.GriffinCanyon.class)); - cards.add(new SetCardInfo("Guiding Spirit", 131, Rarity.RARE, mage.cards.g.GuidingSpirit.class)); - cards.add(new SetCardInfo("Hearth Charm", 82, Rarity.COMMON, mage.cards.h.HearthCharm.class)); - cards.add(new SetCardInfo("Helm of Awakening", 145, Rarity.UNCOMMON, mage.cards.h.HelmOfAwakening.class)); - cards.add(new SetCardInfo("Honorable Passage", 7, Rarity.UNCOMMON, mage.cards.h.HonorablePassage.class)); - cards.add(new SetCardInfo("Hope Charm", 8, Rarity.COMMON, mage.cards.h.HopeCharm.class)); - cards.add(new SetCardInfo("Hulking Cyclops", 84, Rarity.UNCOMMON, mage.cards.h.HulkingCyclops.class)); - cards.add(new SetCardInfo("Impulse", 34, Rarity.COMMON, mage.cards.i.Impulse.class)); - cards.add(new SetCardInfo("Infantry Veteran", 9, Rarity.COMMON, mage.cards.i.InfantryVeteran.class)); - cards.add(new SetCardInfo("Infernal Harvest", 62, Rarity.COMMON, mage.cards.i.InfernalHarvest.class)); - cards.add(new SetCardInfo("Inspiration", 35, Rarity.COMMON, mage.cards.i.Inspiration.class)); - cards.add(new SetCardInfo("Iron-Heart Chimera", 146, Rarity.UNCOMMON, mage.cards.i.IronHeartChimera.class)); - cards.add(new SetCardInfo("Jamuraan Lion", 10, Rarity.COMMON, mage.cards.j.JamuraanLion.class)); - cards.add(new SetCardInfo("Juju Bubble", 147, Rarity.UNCOMMON, mage.cards.j.JujuBubble.class)); - cards.add(new SetCardInfo("Jungle Basin", 164, Rarity.UNCOMMON, mage.cards.j.JungleBasin.class)); - cards.add(new SetCardInfo("Kaervek's Spite", 63, Rarity.RARE, mage.cards.k.KaerveksSpite.class)); - cards.add(new SetCardInfo("Karoo", 165, Rarity.UNCOMMON, mage.cards.k.Karoo.class)); - cards.add(new SetCardInfo("Katabatic Winds", 109, Rarity.RARE, mage.cards.k.KatabaticWinds.class)); - cards.add(new SetCardInfo("Keeper of Kookus", 85, Rarity.COMMON, mage.cards.k.KeeperOfKookus.class)); - cards.add(new SetCardInfo("King Cheetah", 110, Rarity.COMMON, mage.cards.k.KingCheetah.class)); - cards.add(new SetCardInfo("Knight of the Mists", 36, Rarity.COMMON, mage.cards.k.KnightOfTheMists.class)); - cards.add(new SetCardInfo("Knight of Valor", 11, Rarity.COMMON, mage.cards.k.KnightOfValor.class)); - cards.add(new SetCardInfo("Kookus", 86, Rarity.RARE, mage.cards.k.Kookus.class)); - cards.add(new SetCardInfo("Kyscu Drake", 111, Rarity.RARE, mage.cards.k.KyscuDrake.class)); - cards.add(new SetCardInfo("Lead-Belly Chimera", 148, Rarity.UNCOMMON, mage.cards.l.LeadBellyChimera.class)); - cards.add(new SetCardInfo("Lichenthrope", 112, Rarity.RARE, mage.cards.l.Lichenthrope.class)); - cards.add(new SetCardInfo("Lightning Cloud", 87, Rarity.RARE, mage.cards.l.LightningCloud.class)); - cards.add(new SetCardInfo("Longbow Archer", 12, Rarity.UNCOMMON, mage.cards.l.LongbowArcher.class)); - cards.add(new SetCardInfo("Magma Mine", 149, Rarity.UNCOMMON, mage.cards.m.MagmaMine.class)); - cards.add(new SetCardInfo("Man-o'-War", 37, Rarity.COMMON, mage.cards.m.ManOWar.class)); - cards.add(new SetCardInfo("Miraculous Recovery", 13, Rarity.UNCOMMON, mage.cards.m.MiraculousRecovery.class)); - cards.add(new SetCardInfo("Mob Mentality", 88, Rarity.UNCOMMON, mage.cards.m.MobMentality.class)); - cards.add(new SetCardInfo("Mortal Wound", 113, Rarity.COMMON, mage.cards.m.MortalWound.class)); - cards.add(new SetCardInfo("Mundungu", 132, Rarity.UNCOMMON, mage.cards.m.Mundungu.class)); - cards.add(new SetCardInfo("Mystic Veil", 38, Rarity.COMMON, mage.cards.m.MysticVeil.class)); - cards.add(new SetCardInfo("Natural Order", 114, Rarity.RARE, mage.cards.n.NaturalOrder.class)); - cards.add(new SetCardInfo("Necromancy", 64, Rarity.UNCOMMON, mage.cards.n.Necromancy.class)); - cards.add(new SetCardInfo("Necrosavant", 65, Rarity.RARE, mage.cards.n.Necrosavant.class)); - cards.add(new SetCardInfo("Nekrataal", 66, Rarity.UNCOMMON, mage.cards.n.Nekrataal.class)); - cards.add(new SetCardInfo("Ovinomancer", 39, Rarity.UNCOMMON, mage.cards.o.Ovinomancer.class)); - cards.add(new SetCardInfo("Panther Warriors", 115, Rarity.COMMON, mage.cards.p.PantherWarriors.class)); - cards.add(new SetCardInfo("Parapet", 14, Rarity.COMMON, mage.cards.p.Parapet.class)); - cards.add(new SetCardInfo("Peace Talks", 15, Rarity.UNCOMMON, mage.cards.p.PeaceTalks.class)); - cards.add(new SetCardInfo("Phyrexian Marauder", 151, Rarity.RARE, mage.cards.p.PhyrexianMarauder.class)); - cards.add(new SetCardInfo("Phyrexian Walker", 152, Rarity.COMMON, mage.cards.p.PhyrexianWalker.class)); - cards.add(new SetCardInfo("Pillar Tombs of Aku", 67, Rarity.RARE, mage.cards.p.PillarTombsOfAku.class)); - cards.add(new SetCardInfo("Prosperity", 40, Rarity.UNCOMMON, mage.cards.p.Prosperity.class)); - cards.add(new SetCardInfo("Python", 68, Rarity.COMMON, mage.cards.p.Python.class)); - cards.add(new SetCardInfo("Quicksand", 166, Rarity.UNCOMMON, mage.cards.q.Quicksand.class)); - cards.add(new SetCardInfo("Quirion Druid", 116, Rarity.RARE, mage.cards.q.QuirionDruid.class)); - cards.add(new SetCardInfo("Quirion Ranger", 117, Rarity.COMMON, mage.cards.q.QuirionRanger.class)); - cards.add(new SetCardInfo("Raging Gorilla", 90, Rarity.COMMON, mage.cards.r.RagingGorilla.class)); - cards.add(new SetCardInfo("Rainbow Efreet", 41, Rarity.RARE, mage.cards.r.RainbowEfreet.class)); - cards.add(new SetCardInfo("Relentless Assault", 91, Rarity.RARE, mage.cards.r.RelentlessAssault.class)); - cards.add(new SetCardInfo("Relic Ward", 16, Rarity.UNCOMMON, mage.cards.r.RelicWard.class)); - cards.add(new SetCardInfo("Remedy", 17, Rarity.COMMON, mage.cards.r.Remedy.class)); - cards.add(new SetCardInfo("Resistance Fighter", 18, Rarity.COMMON, mage.cards.r.ResistanceFighter.class)); - cards.add(new SetCardInfo("Retribution of the Meek", 19, Rarity.RARE, mage.cards.r.RetributionOfTheMeek.class)); - cards.add(new SetCardInfo("Righteous Aura", 20, Rarity.COMMON, mage.cards.r.RighteousAura.class)); - cards.add(new SetCardInfo("Righteous War", 134, Rarity.RARE, mage.cards.r.RighteousWar.class)); - cards.add(new SetCardInfo("River Boa", 118, Rarity.COMMON, mage.cards.r.RiverBoa.class)); - cards.add(new SetCardInfo("Rock Slide", 92, Rarity.COMMON, mage.cards.r.RockSlide.class)); - cards.add(new SetCardInfo("Rowen", 119, Rarity.RARE, mage.cards.r.Rowen.class)); - cards.add(new SetCardInfo("Sands of Time", 153, Rarity.RARE, mage.cards.s.SandsOfTime.class)); - cards.add(new SetCardInfo("Scalebane's Elite", 135, Rarity.UNCOMMON, mage.cards.s.ScalebanesElite.class)); - cards.add(new SetCardInfo("Shimmering Efreet", 42, Rarity.UNCOMMON, mage.cards.s.ShimmeringEfreet.class)); - cards.add(new SetCardInfo("Shrieking Drake", 43, Rarity.COMMON, mage.cards.s.ShriekingDrake.class)); - cards.add(new SetCardInfo("Simoon", 136, Rarity.UNCOMMON, mage.cards.s.Simoon.class)); - cards.add(new SetCardInfo("Sisay's Ring", 154, Rarity.COMMON, mage.cards.s.SisaysRing.class)); - cards.add(new SetCardInfo("Snake Basket", 155, Rarity.RARE, mage.cards.s.SnakeBasket.class)); - cards.add(new SetCardInfo("Solfatara", 93, Rarity.COMMON, mage.cards.s.Solfatara.class)); - cards.add(new SetCardInfo("Song of Blood", 94, Rarity.COMMON, mage.cards.s.SongOfBlood.class)); - cards.add(new SetCardInfo("Spider Climb", 120, Rarity.COMMON, mage.cards.s.SpiderClimb.class)); - cards.add(new SetCardInfo("Spitting Drake", 95, Rarity.UNCOMMON, mage.cards.s.SpittingDrake.class)); - cards.add(new SetCardInfo("Squandered Resources", 137, Rarity.RARE, mage.cards.s.SquanderedResources.class)); - cards.add(new SetCardInfo("Stampeding Wildebeests", 121, Rarity.UNCOMMON, mage.cards.s.StampedingWildebeests.class)); - cards.add(new SetCardInfo("Suleiman's Legacy", 138, Rarity.RARE, mage.cards.s.SuleimansLegacy.class)); - cards.add(new SetCardInfo("Summer Bloom", 122, Rarity.UNCOMMON, mage.cards.s.SummerBloom.class)); - cards.add(new SetCardInfo("Sun Clasp", 21, Rarity.COMMON, mage.cards.s.SunClasp.class)); - cards.add(new SetCardInfo("Suq'Ata Assassin", 69, Rarity.UNCOMMON, mage.cards.s.SuqAtaAssassin.class)); - cards.add(new SetCardInfo("Suq'Ata Lancer", 96, Rarity.COMMON, mage.cards.s.SuqAtaLancer.class)); - cards.add(new SetCardInfo("Talruum Champion", 97, Rarity.COMMON, mage.cards.t.TalruumChampion.class)); - cards.add(new SetCardInfo("Talruum Piper", 98, Rarity.UNCOMMON, mage.cards.t.TalruumPiper.class)); - cards.add(new SetCardInfo("Tar Pit Warrior", 70, Rarity.COMMON, mage.cards.t.TarPitWarrior.class)); - cards.add(new SetCardInfo("Teferi's Honor Guard", 22, Rarity.UNCOMMON, mage.cards.t.TeferisHonorGuard.class)); - cards.add(new SetCardInfo("Teferi's Puzzle Box", 156, Rarity.RARE, mage.cards.t.TeferisPuzzleBox.class)); - cards.add(new SetCardInfo("Teferi's Realm", 44, Rarity.RARE, mage.cards.t.TeferisRealm.class)); - cards.add(new SetCardInfo("Tempest Drake", 139, Rarity.UNCOMMON, mage.cards.t.TempestDrake.class)); - cards.add(new SetCardInfo("Three Wishes", 45, Rarity.RARE, mage.cards.t.ThreeWishes.class)); - cards.add(new SetCardInfo("Tin-Wing Chimera", 157, Rarity.UNCOMMON, mage.cards.t.TinWingChimera.class)); - cards.add(new SetCardInfo("Tithe", 23, Rarity.RARE, mage.cards.t.Tithe.class)); - cards.add(new SetCardInfo("Tremor", 99, Rarity.COMMON, mage.cards.t.Tremor.class)); - cards.add(new SetCardInfo("Triangle of War", 158, Rarity.RARE, mage.cards.t.TriangleOfWar.class)); - cards.add(new SetCardInfo("Uktabi Orangutan", 123, Rarity.UNCOMMON, mage.cards.u.UktabiOrangutan.class)); - cards.add(new SetCardInfo("Undiscovered Paradise", 167, Rarity.RARE, mage.cards.u.UndiscoveredParadise.class)); - cards.add(new SetCardInfo("Undo", 47, Rarity.COMMON, mage.cards.u.Undo.class)); - cards.add(new SetCardInfo("Urborg Mindsucker", 71, Rarity.COMMON, mage.cards.u.UrborgMindsucker.class)); - cards.add(new SetCardInfo("Vampiric Tutor", 72, Rarity.RARE, mage.cards.v.VampiricTutor.class)); - cards.add(new SetCardInfo("Vampirism", 73, Rarity.UNCOMMON, mage.cards.v.Vampirism.class)); - cards.add(new SetCardInfo("Vanishing", 48, Rarity.COMMON, mage.cards.v.Vanishing.class)); - cards.add(new SetCardInfo("Viashino Sandstalker", 100, Rarity.UNCOMMON, mage.cards.v.ViashinoSandstalker.class)); - cards.add(new SetCardInfo("Viashivan Dragon", 140, Rarity.RARE, mage.cards.v.ViashivanDragon.class)); - cards.add(new SetCardInfo("Vision Charm", 49, Rarity.COMMON, mage.cards.v.VisionCharm.class)); - cards.add(new SetCardInfo("Wake of Vultures", 74, Rarity.COMMON, mage.cards.w.WakeOfVultures.class)); - cards.add(new SetCardInfo("Wand of Denial", 159, Rarity.RARE, mage.cards.w.WandOfDenial.class)); - cards.add(new SetCardInfo("Warrior's Honor", 24, Rarity.COMMON, mage.cards.w.WarriorsHonor.class)); - cards.add(new SetCardInfo("Warthog", 124, Rarity.COMMON, mage.cards.w.Warthog.class)); - cards.add(new SetCardInfo("Waterspout Djinn", 50, Rarity.UNCOMMON, mage.cards.w.WaterspoutDjinn.class)); - cards.add(new SetCardInfo("Wicked Reward", 75, Rarity.COMMON, mage.cards.w.WickedReward.class)); - cards.add(new SetCardInfo("Wind Shear", 125, Rarity.UNCOMMON, mage.cards.w.WindShear.class)); - cards.add(new SetCardInfo("Zhalfirin Crusader", 25, Rarity.RARE, mage.cards.z.ZhalfirinCrusader.class)); - } -} +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author North + */ +public final class Visions extends ExpansionSet { + + private static final Visions instance = new Visions(); + + public static Visions getInstance() { + return instance; + } + + private Visions() { + super("Visions", "VIS", ExpansionSet.buildDate(1997, 1, 11), SetType.EXPANSION); + this.blockName = "Mirage"; + this.parentSet = Mirage.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Aku Djinn", 51, Rarity.RARE, mage.cards.a.AkuDjinn.class)); + cards.add(new SetCardInfo("Anvil of Bogardan", 141, Rarity.RARE, mage.cards.a.AnvilOfBogardan.class)); + cards.add(new SetCardInfo("Archangel", 1, Rarity.RARE, mage.cards.a.Archangel.class)); + cards.add(new SetCardInfo("Army Ants", 126, Rarity.UNCOMMON, mage.cards.a.ArmyAnts.class)); + cards.add(new SetCardInfo("Betrayal", 26, Rarity.COMMON, mage.cards.b.Betrayal.class)); + cards.add(new SetCardInfo("Blanket of Night", 52, Rarity.UNCOMMON, mage.cards.b.BlanketOfNight.class)); + cards.add(new SetCardInfo("Bogardan Phoenix", 76, Rarity.RARE, mage.cards.b.BogardanPhoenix.class)); + cards.add(new SetCardInfo("Brass-Talon Chimera", 142, Rarity.UNCOMMON, mage.cards.b.BrassTalonChimera.class)); + cards.add(new SetCardInfo("Breathstealer's Crypt", 127, Rarity.RARE, mage.cards.b.BreathstealersCrypt.class)); + cards.add(new SetCardInfo("Breezekeeper", 27, Rarity.COMMON, mage.cards.b.Breezekeeper.class)); + cards.add(new SetCardInfo("Brood of Cockroaches", 53, Rarity.UNCOMMON, mage.cards.b.BroodOfCockroaches.class)); + cards.add(new SetCardInfo("Bull Elephant", 101, Rarity.COMMON, mage.cards.b.BullElephant.class)); + cards.add(new SetCardInfo("Chronatog", 28, Rarity.RARE, mage.cards.c.Chronatog.class)); + cards.add(new SetCardInfo("City of Solitude", 102, Rarity.RARE, mage.cards.c.CityOfSolitude.class)); + cards.add(new SetCardInfo("Cloud Elemental", 29, Rarity.COMMON, mage.cards.c.CloudElemental.class)); + cards.add(new SetCardInfo("Coercion", 54, Rarity.COMMON, mage.cards.c.Coercion.class)); + cards.add(new SetCardInfo("Coral Atoll", 160, Rarity.UNCOMMON, mage.cards.c.CoralAtoll.class)); + cards.add(new SetCardInfo("Corrosion", 128, Rarity.RARE, mage.cards.c.Corrosion.class)); + cards.add(new SetCardInfo("Creeping Mold", 103, Rarity.UNCOMMON, mage.cards.c.CreepingMold.class)); + cards.add(new SetCardInfo("Crypt Rats", 55, Rarity.COMMON, mage.cards.c.CryptRats.class)); + cards.add(new SetCardInfo("Daraja Griffin", 2, Rarity.UNCOMMON, mage.cards.d.DarajaGriffin.class)); + cards.add(new SetCardInfo("Dark Privilege", 56, Rarity.COMMON, mage.cards.d.DarkPrivilege.class)); + cards.add(new SetCardInfo("Death Watch", 57, Rarity.COMMON, mage.cards.d.DeathWatch.class)); + cards.add(new SetCardInfo("Desertion", 30, Rarity.RARE, mage.cards.d.Desertion.class)); + cards.add(new SetCardInfo("Desolation", 58, Rarity.UNCOMMON, mage.cards.d.Desolation.class)); + cards.add(new SetCardInfo("Diamond Kaleidoscope", 143, Rarity.RARE, mage.cards.d.DiamondKaleidoscope.class)); + cards.add(new SetCardInfo("Dormant Volcano", 161, Rarity.UNCOMMON, mage.cards.d.DormantVolcano.class)); + cards.add(new SetCardInfo("Dragon Mask", 144, Rarity.UNCOMMON, mage.cards.d.DragonMask.class)); + cards.add(new SetCardInfo("Dream Tides", 31, Rarity.UNCOMMON, mage.cards.d.DreamTides.class)); + cards.add(new SetCardInfo("Dwarven Vigilantes", 77, Rarity.COMMON, mage.cards.d.DwarvenVigilantes.class)); + cards.add(new SetCardInfo("Elephant Grass", 104, Rarity.UNCOMMON, mage.cards.e.ElephantGrass.class)); + cards.add(new SetCardInfo("Elkin Lair", 78, Rarity.RARE, mage.cards.e.ElkinLair.class)); + cards.add(new SetCardInfo("Elven Cache", 105, Rarity.COMMON, mage.cards.e.ElvenCache.class)); + cards.add(new SetCardInfo("Emerald Charm", 106, Rarity.COMMON, mage.cards.e.EmeraldCharm.class)); + cards.add(new SetCardInfo("Equipoise", 3, Rarity.RARE, mage.cards.e.Equipoise.class)); + cards.add(new SetCardInfo("Everglades", 162, Rarity.UNCOMMON, mage.cards.e.Everglades.class)); + cards.add(new SetCardInfo("Eye of Singularity", 4, Rarity.RARE, mage.cards.e.EyeOfSingularity.class)); + cards.add(new SetCardInfo("Fallen Askari", 59, Rarity.COMMON, mage.cards.f.FallenAskari.class)); + cards.add(new SetCardInfo("Femeref Enchantress", 129, Rarity.RARE, mage.cards.f.FemerefEnchantress.class)); + cards.add(new SetCardInfo("Feral Instinct", 107, Rarity.COMMON, mage.cards.f.FeralInstinct.class)); + cards.add(new SetCardInfo("Fireblast", 79, Rarity.COMMON, mage.cards.f.Fireblast.class)); + cards.add(new SetCardInfo("Firestorm Hellkite", 130, Rarity.RARE, mage.cards.f.FirestormHellkite.class)); + cards.add(new SetCardInfo("Flooded Shoreline", 32, Rarity.RARE, mage.cards.f.FloodedShoreline.class)); + cards.add(new SetCardInfo("Foreshadow", 33, Rarity.UNCOMMON, mage.cards.f.Foreshadow.class)); + cards.add(new SetCardInfo("Freewind Falcon", 5, Rarity.COMMON, mage.cards.f.FreewindFalcon.class)); + cards.add(new SetCardInfo("Funeral Charm", 61, Rarity.COMMON, mage.cards.f.FuneralCharm.class)); + cards.add(new SetCardInfo("Giant Caterpillar", 108, Rarity.COMMON, mage.cards.g.GiantCaterpillar.class)); + cards.add(new SetCardInfo("Goblin Recruiter", 80, Rarity.UNCOMMON, mage.cards.g.GoblinRecruiter.class)); + cards.add(new SetCardInfo("Goblin Swine-Rider", 81, Rarity.COMMON, mage.cards.g.GoblinSwineRider.class)); + cards.add(new SetCardInfo("Gossamer Chains", 6, Rarity.COMMON, mage.cards.g.GossamerChains.class)); + cards.add(new SetCardInfo("Griffin Canyon", 163, Rarity.RARE, mage.cards.g.GriffinCanyon.class)); + cards.add(new SetCardInfo("Guiding Spirit", 131, Rarity.RARE, mage.cards.g.GuidingSpirit.class)); + cards.add(new SetCardInfo("Hearth Charm", 82, Rarity.COMMON, mage.cards.h.HearthCharm.class)); + cards.add(new SetCardInfo("Helm of Awakening", 145, Rarity.UNCOMMON, mage.cards.h.HelmOfAwakening.class)); + cards.add(new SetCardInfo("Honorable Passage", 7, Rarity.UNCOMMON, mage.cards.h.HonorablePassage.class)); + cards.add(new SetCardInfo("Hope Charm", 8, Rarity.COMMON, mage.cards.h.HopeCharm.class)); + cards.add(new SetCardInfo("Hulking Cyclops", 84, Rarity.UNCOMMON, mage.cards.h.HulkingCyclops.class)); + cards.add(new SetCardInfo("Impulse", 34, Rarity.COMMON, mage.cards.i.Impulse.class)); + cards.add(new SetCardInfo("Infantry Veteran", 9, Rarity.COMMON, mage.cards.i.InfantryVeteran.class)); + cards.add(new SetCardInfo("Infernal Harvest", 62, Rarity.COMMON, mage.cards.i.InfernalHarvest.class)); + cards.add(new SetCardInfo("Inspiration", 35, Rarity.COMMON, mage.cards.i.Inspiration.class)); + cards.add(new SetCardInfo("Iron-Heart Chimera", 146, Rarity.UNCOMMON, mage.cards.i.IronHeartChimera.class)); + cards.add(new SetCardInfo("Jamuraan Lion", 10, Rarity.COMMON, mage.cards.j.JamuraanLion.class)); + cards.add(new SetCardInfo("Juju Bubble", 147, Rarity.UNCOMMON, mage.cards.j.JujuBubble.class)); + cards.add(new SetCardInfo("Jungle Basin", 164, Rarity.UNCOMMON, mage.cards.j.JungleBasin.class)); + cards.add(new SetCardInfo("Kaervek's Spite", 63, Rarity.RARE, mage.cards.k.KaerveksSpite.class)); + cards.add(new SetCardInfo("Karoo", 165, Rarity.UNCOMMON, mage.cards.k.Karoo.class)); + cards.add(new SetCardInfo("Katabatic Winds", 109, Rarity.RARE, mage.cards.k.KatabaticWinds.class)); + cards.add(new SetCardInfo("Keeper of Kookus", 85, Rarity.COMMON, mage.cards.k.KeeperOfKookus.class)); + cards.add(new SetCardInfo("King Cheetah", 110, Rarity.COMMON, mage.cards.k.KingCheetah.class)); + cards.add(new SetCardInfo("Knight of the Mists", 36, Rarity.COMMON, mage.cards.k.KnightOfTheMists.class)); + cards.add(new SetCardInfo("Knight of Valor", 11, Rarity.COMMON, mage.cards.k.KnightOfValor.class)); + cards.add(new SetCardInfo("Kookus", 86, Rarity.RARE, mage.cards.k.Kookus.class)); + cards.add(new SetCardInfo("Kyscu Drake", 111, Rarity.RARE, mage.cards.k.KyscuDrake.class)); + cards.add(new SetCardInfo("Lead-Belly Chimera", 148, Rarity.UNCOMMON, mage.cards.l.LeadBellyChimera.class)); + cards.add(new SetCardInfo("Lichenthrope", 112, Rarity.RARE, mage.cards.l.Lichenthrope.class)); + cards.add(new SetCardInfo("Lightning Cloud", 87, Rarity.RARE, mage.cards.l.LightningCloud.class)); + cards.add(new SetCardInfo("Longbow Archer", 12, Rarity.UNCOMMON, mage.cards.l.LongbowArcher.class)); + cards.add(new SetCardInfo("Magma Mine", 149, Rarity.UNCOMMON, mage.cards.m.MagmaMine.class)); + cards.add(new SetCardInfo("Man-o'-War", 37, Rarity.COMMON, mage.cards.m.ManOWar.class)); + cards.add(new SetCardInfo("Miraculous Recovery", 13, Rarity.UNCOMMON, mage.cards.m.MiraculousRecovery.class)); + cards.add(new SetCardInfo("Mob Mentality", 88, Rarity.UNCOMMON, mage.cards.m.MobMentality.class)); + cards.add(new SetCardInfo("Mortal Wound", 113, Rarity.COMMON, mage.cards.m.MortalWound.class)); + cards.add(new SetCardInfo("Mundungu", 132, Rarity.UNCOMMON, mage.cards.m.Mundungu.class)); + cards.add(new SetCardInfo("Mystic Veil", 38, Rarity.COMMON, mage.cards.m.MysticVeil.class)); + cards.add(new SetCardInfo("Natural Order", 114, Rarity.RARE, mage.cards.n.NaturalOrder.class)); + cards.add(new SetCardInfo("Necromancy", 64, Rarity.UNCOMMON, mage.cards.n.Necromancy.class)); + cards.add(new SetCardInfo("Necrosavant", 65, Rarity.RARE, mage.cards.n.Necrosavant.class)); + cards.add(new SetCardInfo("Nekrataal", 66, Rarity.UNCOMMON, mage.cards.n.Nekrataal.class)); + cards.add(new SetCardInfo("Ovinomancer", 39, Rarity.UNCOMMON, mage.cards.o.Ovinomancer.class)); + cards.add(new SetCardInfo("Panther Warriors", 115, Rarity.COMMON, mage.cards.p.PantherWarriors.class)); + cards.add(new SetCardInfo("Parapet", 14, Rarity.COMMON, mage.cards.p.Parapet.class)); + cards.add(new SetCardInfo("Peace Talks", 15, Rarity.UNCOMMON, mage.cards.p.PeaceTalks.class)); + cards.add(new SetCardInfo("Phyrexian Marauder", 151, Rarity.RARE, mage.cards.p.PhyrexianMarauder.class)); + cards.add(new SetCardInfo("Phyrexian Walker", 152, Rarity.COMMON, mage.cards.p.PhyrexianWalker.class)); + cards.add(new SetCardInfo("Pillar Tombs of Aku", 67, Rarity.RARE, mage.cards.p.PillarTombsOfAku.class)); + cards.add(new SetCardInfo("Prosperity", 40, Rarity.UNCOMMON, mage.cards.p.Prosperity.class)); + cards.add(new SetCardInfo("Python", 68, Rarity.COMMON, mage.cards.p.Python.class)); + cards.add(new SetCardInfo("Quicksand", 166, Rarity.UNCOMMON, mage.cards.q.Quicksand.class)); + cards.add(new SetCardInfo("Quirion Druid", 116, Rarity.RARE, mage.cards.q.QuirionDruid.class)); + cards.add(new SetCardInfo("Quirion Ranger", 117, Rarity.COMMON, mage.cards.q.QuirionRanger.class)); + cards.add(new SetCardInfo("Raging Gorilla", 90, Rarity.COMMON, mage.cards.r.RagingGorilla.class)); + cards.add(new SetCardInfo("Rainbow Efreet", 41, Rarity.RARE, mage.cards.r.RainbowEfreet.class)); + cards.add(new SetCardInfo("Relentless Assault", 91, Rarity.RARE, mage.cards.r.RelentlessAssault.class)); + cards.add(new SetCardInfo("Relic Ward", 16, Rarity.UNCOMMON, mage.cards.r.RelicWard.class)); + cards.add(new SetCardInfo("Remedy", 17, Rarity.COMMON, mage.cards.r.Remedy.class)); + cards.add(new SetCardInfo("Resistance Fighter", 18, Rarity.COMMON, mage.cards.r.ResistanceFighter.class)); + cards.add(new SetCardInfo("Retribution of the Meek", 19, Rarity.RARE, mage.cards.r.RetributionOfTheMeek.class)); + cards.add(new SetCardInfo("Righteous Aura", 20, Rarity.COMMON, mage.cards.r.RighteousAura.class)); + cards.add(new SetCardInfo("Righteous War", 134, Rarity.RARE, mage.cards.r.RighteousWar.class)); + cards.add(new SetCardInfo("River Boa", 118, Rarity.COMMON, mage.cards.r.RiverBoa.class)); + cards.add(new SetCardInfo("Rock Slide", 92, Rarity.COMMON, mage.cards.r.RockSlide.class)); + cards.add(new SetCardInfo("Rowen", 119, Rarity.RARE, mage.cards.r.Rowen.class)); + cards.add(new SetCardInfo("Sands of Time", 153, Rarity.RARE, mage.cards.s.SandsOfTime.class)); + cards.add(new SetCardInfo("Scalebane's Elite", 135, Rarity.UNCOMMON, mage.cards.s.ScalebanesElite.class)); + cards.add(new SetCardInfo("Shimmering Efreet", 42, Rarity.UNCOMMON, mage.cards.s.ShimmeringEfreet.class)); + cards.add(new SetCardInfo("Shrieking Drake", 43, Rarity.COMMON, mage.cards.s.ShriekingDrake.class)); + cards.add(new SetCardInfo("Simoon", 136, Rarity.UNCOMMON, mage.cards.s.Simoon.class)); + cards.add(new SetCardInfo("Sisay's Ring", 154, Rarity.COMMON, mage.cards.s.SisaysRing.class)); + cards.add(new SetCardInfo("Snake Basket", 155, Rarity.RARE, mage.cards.s.SnakeBasket.class)); + cards.add(new SetCardInfo("Solfatara", 93, Rarity.COMMON, mage.cards.s.Solfatara.class)); + cards.add(new SetCardInfo("Song of Blood", 94, Rarity.COMMON, mage.cards.s.SongOfBlood.class)); + cards.add(new SetCardInfo("Spider Climb", 120, Rarity.COMMON, mage.cards.s.SpiderClimb.class)); + cards.add(new SetCardInfo("Spitting Drake", 95, Rarity.UNCOMMON, mage.cards.s.SpittingDrake.class)); + cards.add(new SetCardInfo("Squandered Resources", 137, Rarity.RARE, mage.cards.s.SquanderedResources.class)); + cards.add(new SetCardInfo("Stampeding Wildebeests", 121, Rarity.UNCOMMON, mage.cards.s.StampedingWildebeests.class)); + cards.add(new SetCardInfo("Suleiman's Legacy", 138, Rarity.RARE, mage.cards.s.SuleimansLegacy.class)); + cards.add(new SetCardInfo("Summer Bloom", 122, Rarity.UNCOMMON, mage.cards.s.SummerBloom.class)); + cards.add(new SetCardInfo("Sun Clasp", 21, Rarity.COMMON, mage.cards.s.SunClasp.class)); + cards.add(new SetCardInfo("Suq'Ata Assassin", 69, Rarity.UNCOMMON, mage.cards.s.SuqAtaAssassin.class)); + cards.add(new SetCardInfo("Suq'Ata Lancer", 96, Rarity.COMMON, mage.cards.s.SuqAtaLancer.class)); + cards.add(new SetCardInfo("Talruum Champion", 97, Rarity.COMMON, mage.cards.t.TalruumChampion.class)); + cards.add(new SetCardInfo("Talruum Piper", 98, Rarity.UNCOMMON, mage.cards.t.TalruumPiper.class)); + cards.add(new SetCardInfo("Tar Pit Warrior", 70, Rarity.COMMON, mage.cards.t.TarPitWarrior.class)); + cards.add(new SetCardInfo("Teferi's Honor Guard", 22, Rarity.UNCOMMON, mage.cards.t.TeferisHonorGuard.class)); + cards.add(new SetCardInfo("Teferi's Puzzle Box", 156, Rarity.RARE, mage.cards.t.TeferisPuzzleBox.class)); + cards.add(new SetCardInfo("Teferi's Realm", 44, Rarity.RARE, mage.cards.t.TeferisRealm.class)); + cards.add(new SetCardInfo("Tempest Drake", 139, Rarity.UNCOMMON, mage.cards.t.TempestDrake.class)); + cards.add(new SetCardInfo("Three Wishes", 45, Rarity.RARE, mage.cards.t.ThreeWishes.class)); + cards.add(new SetCardInfo("Tin-Wing Chimera", 157, Rarity.UNCOMMON, mage.cards.t.TinWingChimera.class)); + cards.add(new SetCardInfo("Tithe", 23, Rarity.RARE, mage.cards.t.Tithe.class)); + cards.add(new SetCardInfo("Tremor", 99, Rarity.COMMON, mage.cards.t.Tremor.class)); + cards.add(new SetCardInfo("Triangle of War", 158, Rarity.RARE, mage.cards.t.TriangleOfWar.class)); + cards.add(new SetCardInfo("Uktabi Orangutan", 123, Rarity.UNCOMMON, mage.cards.u.UktabiOrangutan.class)); + cards.add(new SetCardInfo("Undiscovered Paradise", 167, Rarity.RARE, mage.cards.u.UndiscoveredParadise.class)); + cards.add(new SetCardInfo("Undo", 47, Rarity.COMMON, mage.cards.u.Undo.class)); + cards.add(new SetCardInfo("Urborg Mindsucker", 71, Rarity.COMMON, mage.cards.u.UrborgMindsucker.class)); + cards.add(new SetCardInfo("Vampiric Tutor", 72, Rarity.RARE, mage.cards.v.VampiricTutor.class)); + cards.add(new SetCardInfo("Vampirism", 73, Rarity.UNCOMMON, mage.cards.v.Vampirism.class)); + cards.add(new SetCardInfo("Vanishing", 48, Rarity.COMMON, mage.cards.v.Vanishing.class)); + cards.add(new SetCardInfo("Viashino Sandstalker", 100, Rarity.UNCOMMON, mage.cards.v.ViashinoSandstalker.class)); + cards.add(new SetCardInfo("Viashivan Dragon", 140, Rarity.RARE, mage.cards.v.ViashivanDragon.class)); + cards.add(new SetCardInfo("Vision Charm", 49, Rarity.COMMON, mage.cards.v.VisionCharm.class)); + cards.add(new SetCardInfo("Wake of Vultures", 74, Rarity.COMMON, mage.cards.w.WakeOfVultures.class)); + cards.add(new SetCardInfo("Wand of Denial", 159, Rarity.RARE, mage.cards.w.WandOfDenial.class)); + cards.add(new SetCardInfo("Warrior's Honor", 24, Rarity.COMMON, mage.cards.w.WarriorsHonor.class)); + cards.add(new SetCardInfo("Warthog", 124, Rarity.COMMON, mage.cards.w.Warthog.class)); + cards.add(new SetCardInfo("Waterspout Djinn", 50, Rarity.UNCOMMON, mage.cards.w.WaterspoutDjinn.class)); + cards.add(new SetCardInfo("Wicked Reward", 75, Rarity.COMMON, mage.cards.w.WickedReward.class)); + cards.add(new SetCardInfo("Wind Shear", 125, Rarity.UNCOMMON, mage.cards.w.WindShear.class)); + cards.add(new SetCardInfo("Zhalfirin Crusader", 25, Rarity.RARE, mage.cards.z.ZhalfirinCrusader.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Weatherlight.java b/Mage.Sets/src/mage/sets/Weatherlight.java index f93876f43d..ad5e41d063 100644 --- a/Mage.Sets/src/mage/sets/Weatherlight.java +++ b/Mage.Sets/src/mage/sets/Weatherlight.java @@ -1,190 +1,197 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author noxx - */ -public final class Weatherlight extends ExpansionSet { - - private static final Weatherlight instance = new Weatherlight(); - - public static Weatherlight getInstance() { - return instance; - } - - private Weatherlight() { - super("Weatherlight", "WTH", ExpansionSet.buildDate(1997, 5, 31), SetType.EXPANSION); - this.blockName = "Mirage"; - this.parentSet = Mirage.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Abduction", 30, Rarity.UNCOMMON, mage.cards.a.Abduction.class)); - cards.add(new SetCardInfo("Abeyance", 1, Rarity.RARE, mage.cards.a.Abeyance.class)); - cards.add(new SetCardInfo("Abjure", 31, Rarity.COMMON, mage.cards.a.Abjure.class)); - cards.add(new SetCardInfo("Aboroth", 117, Rarity.RARE, mage.cards.a.Aboroth.class)); - cards.add(new SetCardInfo("Abyssal Gatekeeper", 59, Rarity.COMMON, mage.cards.a.AbyssalGatekeeper.class)); - cards.add(new SetCardInfo("Aether Flash", 88, Rarity.UNCOMMON, mage.cards.a.AetherFlash.class)); - cards.add(new SetCardInfo("Agonizing Memories", 60, Rarity.UNCOMMON, mage.cards.a.AgonizingMemories.class)); - cards.add(new SetCardInfo("Alabaster Dragon", 2, Rarity.RARE, mage.cards.a.AlabasterDragon.class)); - cards.add(new SetCardInfo("Alms", 3, Rarity.COMMON, mage.cards.a.Alms.class)); - cards.add(new SetCardInfo("Ancestral Knowledge", 32, Rarity.RARE, mage.cards.a.AncestralKnowledge.class)); - cards.add(new SetCardInfo("Angelic Renewal", 4, Rarity.COMMON, mage.cards.a.AngelicRenewal.class)); +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author noxx + */ +public final class Weatherlight extends ExpansionSet { + + private static final Weatherlight instance = new Weatherlight(); + + public static Weatherlight getInstance() { + return instance; + } + + private Weatherlight() { + super("Weatherlight", "WTH", ExpansionSet.buildDate(1997, 5, 31), SetType.EXPANSION); + this.blockName = "Mirage"; + this.parentSet = Mirage.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Abduction", 30, Rarity.UNCOMMON, mage.cards.a.Abduction.class)); + cards.add(new SetCardInfo("Abeyance", 1, Rarity.RARE, mage.cards.a.Abeyance.class)); + cards.add(new SetCardInfo("Abjure", 31, Rarity.COMMON, mage.cards.a.Abjure.class)); + cards.add(new SetCardInfo("Aboroth", 117, Rarity.RARE, mage.cards.a.Aboroth.class)); + cards.add(new SetCardInfo("Abyssal Gatekeeper", 59, Rarity.COMMON, mage.cards.a.AbyssalGatekeeper.class)); + cards.add(new SetCardInfo("Aether Flash", 88, Rarity.UNCOMMON, mage.cards.a.AetherFlash.class)); + cards.add(new SetCardInfo("Agonizing Memories", 60, Rarity.UNCOMMON, mage.cards.a.AgonizingMemories.class)); + cards.add(new SetCardInfo("Alabaster Dragon", 2, Rarity.RARE, mage.cards.a.AlabasterDragon.class)); + cards.add(new SetCardInfo("Alms", 3, Rarity.COMMON, mage.cards.a.Alms.class)); + cards.add(new SetCardInfo("Ancestral Knowledge", 32, Rarity.RARE, mage.cards.a.AncestralKnowledge.class)); + cards.add(new SetCardInfo("Angelic Renewal", 4, Rarity.COMMON, mage.cards.a.AngelicRenewal.class)); cards.add(new SetCardInfo("Apathy", 33, Rarity.COMMON, mage.cards.a.Apathy.class)); - cards.add(new SetCardInfo("Arctic Wolves", 118, Rarity.UNCOMMON, mage.cards.a.ArcticWolves.class)); - cards.add(new SetCardInfo("Ardent Militia", 5, Rarity.COMMON, mage.cards.a.ArdentMilitia.class)); - cards.add(new SetCardInfo("Argivian Find", 6, Rarity.UNCOMMON, mage.cards.a.ArgivianFind.class)); - cards.add(new SetCardInfo("Argivian Restoration", 34, Rarity.UNCOMMON, mage.cards.a.ArgivianRestoration.class)); - cards.add(new SetCardInfo("Aura of Silence", 7, Rarity.UNCOMMON, mage.cards.a.AuraOfSilence.class)); - cards.add(new SetCardInfo("Avizoa", 35, Rarity.RARE, mage.cards.a.Avizoa.class)); - cards.add(new SetCardInfo("Barishi", 119, Rarity.UNCOMMON, mage.cards.b.Barishi.class)); - cards.add(new SetCardInfo("Barrow Ghoul", 61, Rarity.COMMON, mage.cards.b.BarrowGhoul.class)); - cards.add(new SetCardInfo("Benalish Infantry", 8, Rarity.COMMON, mage.cards.b.BenalishInfantry.class)); - cards.add(new SetCardInfo("Benalish Knight", 9, Rarity.COMMON, mage.cards.b.BenalishKnight.class)); - cards.add(new SetCardInfo("Benalish Missionary", 10, Rarity.COMMON, mage.cards.b.BenalishMissionary.class)); - cards.add(new SetCardInfo("Betrothed of Fire", 89, Rarity.COMMON, mage.cards.b.BetrothedOfFire.class)); - cards.add(new SetCardInfo("Bloodrock Cyclops", 90, Rarity.COMMON, mage.cards.b.BloodrockCyclops.class)); - cards.add(new SetCardInfo("Blossoming Wreath", 120, Rarity.COMMON, mage.cards.b.BlossomingWreath.class)); - cards.add(new SetCardInfo("Bogardan Firefiend", 91, Rarity.COMMON, mage.cards.b.BogardanFirefiend.class)); - cards.add(new SetCardInfo("Boiling Blood", 92, Rarity.COMMON, mage.cards.b.BoilingBlood.class)); - cards.add(new SetCardInfo("Bone Dancer", 62, Rarity.RARE, mage.cards.b.BoneDancer.class)); - cards.add(new SetCardInfo("Bosium Strip", 147, Rarity.RARE, mage.cards.b.BosiumStrip.class)); - cards.add(new SetCardInfo("Briar Shield", 121, Rarity.COMMON, mage.cards.b.BriarShield.class)); - cards.add(new SetCardInfo("Bubble Matrix", 146, Rarity.RARE, mage.cards.b.BubbleMatrix.class)); - cards.add(new SetCardInfo("Buried Alive", 63, Rarity.UNCOMMON, mage.cards.b.BuriedAlive.class)); - cards.add(new SetCardInfo("Call of the Wild", 122, Rarity.RARE, mage.cards.c.CallOfTheWild.class)); - cards.add(new SetCardInfo("Chimeric Sphere", 148, Rarity.UNCOMMON, mage.cards.c.ChimericSphere.class)); - cards.add(new SetCardInfo("Cinder Giant", 93, Rarity.UNCOMMON, mage.cards.c.CinderGiant.class)); - cards.add(new SetCardInfo("Cinder Wall", 94, Rarity.COMMON, mage.cards.c.CinderWall.class)); - cards.add(new SetCardInfo("Cloud Djinn", 36, Rarity.UNCOMMON, mage.cards.c.CloudDjinn.class)); - cards.add(new SetCardInfo("Cone of Flame", 95, Rarity.UNCOMMON, mage.cards.c.ConeOfFlame.class)); - cards.add(new SetCardInfo("Debt of Loyalty", 11, Rarity.RARE, mage.cards.d.DebtOfLoyalty.class)); - cards.add(new SetCardInfo("Dense Foliage", 124, Rarity.RARE, mage.cards.d.DenseFoliage.class)); - cards.add(new SetCardInfo("Desperate Gambit", 96, Rarity.UNCOMMON, mage.cards.d.DesperateGambit.class)); - cards.add(new SetCardInfo("Dingus Staff", 149, Rarity.UNCOMMON, mage.cards.d.DingusStaff.class)); - cards.add(new SetCardInfo("Disrupt", 37, Rarity.COMMON, mage.cards.d.Disrupt.class)); - cards.add(new SetCardInfo("Doomsday", 66, Rarity.RARE, mage.cards.d.Doomsday.class)); - cards.add(new SetCardInfo("Downdraft", 125, Rarity.UNCOMMON, mage.cards.d.Downdraft.class)); - cards.add(new SetCardInfo("Duskrider Falcon", 12, Rarity.COMMON, mage.cards.d.DuskriderFalcon.class)); - cards.add(new SetCardInfo("Dwarven Berserker", 97, Rarity.COMMON, mage.cards.d.DwarvenBerserker.class)); - cards.add(new SetCardInfo("Dwarven Thaumaturgist", 98, Rarity.RARE, mage.cards.d.DwarvenThaumaturgist.class)); - cards.add(new SetCardInfo("Empyrial Armor", 13, Rarity.COMMON, mage.cards.e.EmpyrialArmor.class)); - cards.add(new SetCardInfo("Fallow Wurm", 126, Rarity.UNCOMMON, mage.cards.f.FallowWurm.class)); - cards.add(new SetCardInfo("Familiar Ground", 127, Rarity.UNCOMMON, mage.cards.f.FamiliarGround.class)); - cards.add(new SetCardInfo("Fatal Blow", 67, Rarity.COMMON, mage.cards.f.FatalBlow.class)); - cards.add(new SetCardInfo("Fervor", 99, Rarity.RARE, mage.cards.f.Fervor.class)); - cards.add(new SetCardInfo("Festering Evil", 68, Rarity.UNCOMMON, mage.cards.f.FesteringEvil.class)); - cards.add(new SetCardInfo("Fire Whip", 100, Rarity.COMMON, mage.cards.f.FireWhip.class)); - cards.add(new SetCardInfo("Firestorm", 101, Rarity.RARE, mage.cards.f.Firestorm.class)); - cards.add(new SetCardInfo("Fit of Rage", 102, Rarity.COMMON, mage.cards.f.FitOfRage.class)); - cards.add(new SetCardInfo("Fledgling Djinn", 69, Rarity.COMMON, mage.cards.f.FledglingDjinn.class)); - cards.add(new SetCardInfo("Flux", 39, Rarity.COMMON, mage.cards.f.Flux.class)); - cards.add(new SetCardInfo("Fog Elemental", 40, Rarity.COMMON, mage.cards.f.FogElemental.class)); - cards.add(new SetCardInfo("Foriysian Brigade", 14, Rarity.UNCOMMON, mage.cards.f.ForiysianBrigade.class)); - cards.add(new SetCardInfo("Fungus Elemental", 128, Rarity.RARE, mage.cards.f.FungusElemental.class)); - cards.add(new SetCardInfo("Gaea's Blessing", 129, Rarity.UNCOMMON, mage.cards.g.GaeasBlessing.class)); - cards.add(new SetCardInfo("Gallowbraid", 70, Rarity.RARE, mage.cards.g.Gallowbraid.class)); - cards.add(new SetCardInfo("Gemstone Mine", 164, Rarity.UNCOMMON, mage.cards.g.GemstoneMine.class)); - cards.add(new SetCardInfo("Gerrard's Wisdom", 15, Rarity.UNCOMMON, mage.cards.g.GerrardsWisdom.class)); - cards.add(new SetCardInfo("Goblin Bomb", 103, Rarity.RARE, mage.cards.g.GoblinBomb.class)); - cards.add(new SetCardInfo("Goblin Grenadiers", 104, Rarity.UNCOMMON, mage.cards.g.GoblinGrenadiers.class)); - cards.add(new SetCardInfo("Goblin Vandal", 105, Rarity.COMMON, mage.cards.g.GoblinVandal.class)); - cards.add(new SetCardInfo("Guided Strike", 16, Rarity.COMMON, mage.cards.g.GuidedStrike.class)); - cards.add(new SetCardInfo("Harvest Wurm", 130, Rarity.COMMON, mage.cards.h.HarvestWurm.class)); - cards.add(new SetCardInfo("Haunting Misery", 71, Rarity.COMMON, mage.cards.h.HauntingMisery.class)); - cards.add(new SetCardInfo("Heart of Bogardan", 106, Rarity.RARE, mage.cards.h.HeartOfBogardan.class)); - cards.add(new SetCardInfo("Heat Stroke", 107, Rarity.RARE, mage.cards.h.HeatStroke.class)); - cards.add(new SetCardInfo("Heavy Ballista", 17, Rarity.COMMON, mage.cards.h.HeavyBallista.class)); - cards.add(new SetCardInfo("Hidden Horror", 72, Rarity.UNCOMMON, mage.cards.h.HiddenHorror.class)); - cards.add(new SetCardInfo("Hurloon Shaman", 108, Rarity.UNCOMMON, mage.cards.h.HurloonShaman.class)); - cards.add(new SetCardInfo("Infernal Tribute", 73, Rarity.RARE, mage.cards.i.InfernalTribute.class)); - cards.add(new SetCardInfo("Inner Sanctum", 18, Rarity.RARE, mage.cards.i.InnerSanctum.class)); - cards.add(new SetCardInfo("Jabari's Banner", 150, Rarity.UNCOMMON, mage.cards.j.JabarisBanner.class)); - cards.add(new SetCardInfo("Kithkin Armor", 19, Rarity.COMMON, mage.cards.k.KithkinArmor.class)); - cards.add(new SetCardInfo("Lava Hounds", 109, Rarity.UNCOMMON, mage.cards.l.LavaHounds.class)); - cards.add(new SetCardInfo("Lava Storm", 110, Rarity.COMMON, mage.cards.l.LavaStorm.class)); - cards.add(new SetCardInfo("Llanowar Behemoth", 132, Rarity.UNCOMMON, mage.cards.l.LlanowarBehemoth.class)); - cards.add(new SetCardInfo("Llanowar Druid", 133, Rarity.COMMON, mage.cards.l.LlanowarDruid.class)); - cards.add(new SetCardInfo("Llanowar Sentinel", 134, Rarity.COMMON, mage.cards.l.LlanowarSentinel.class)); - cards.add(new SetCardInfo("Lotus Vale", 165, Rarity.RARE, mage.cards.l.LotusVale.class)); - cards.add(new SetCardInfo("Mana Chains", 41, Rarity.COMMON, mage.cards.m.ManaChains.class)); - cards.add(new SetCardInfo("Mana Web", 152, Rarity.RARE, mage.cards.m.ManaWeb.class)); - cards.add(new SetCardInfo("Manta Ray", 42, Rarity.COMMON, mage.cards.m.MantaRay.class)); - cards.add(new SetCardInfo("Maraxus of Keld", 111, Rarity.RARE, mage.cards.m.MaraxusOfKeld.class)); - cards.add(new SetCardInfo("Master of Arms", 20, Rarity.UNCOMMON, mage.cards.m.MasterOfArms.class)); - cards.add(new SetCardInfo("Merfolk Traders", 43, Rarity.COMMON, mage.cards.m.MerfolkTraders.class)); - cards.add(new SetCardInfo("Mind Stone", 153, Rarity.COMMON, mage.cards.m.MindStone.class)); - cards.add(new SetCardInfo("Mischievous Poltergeist", 74, Rarity.UNCOMMON, mage.cards.m.MischievousPoltergeist.class)); - cards.add(new SetCardInfo("Mistmoon Griffin", 21, Rarity.UNCOMMON, mage.cards.m.MistmoonGriffin.class)); - cards.add(new SetCardInfo("Morinfen", 75, Rarity.RARE, mage.cards.m.Morinfen.class)); - cards.add(new SetCardInfo("Mwonvuli Ooze", 135, Rarity.RARE, mage.cards.m.MwonvuliOoze.class)); - cards.add(new SetCardInfo("Nature's Kiss", 136, Rarity.COMMON, mage.cards.n.NaturesKiss.class)); - cards.add(new SetCardInfo("Nature's Resurgence", 137, Rarity.RARE, mage.cards.n.NaturesResurgence.class)); - cards.add(new SetCardInfo("Necratog", 76, Rarity.UNCOMMON, mage.cards.n.Necratog.class)); - cards.add(new SetCardInfo("Noble Benefactor", 44, Rarity.UNCOMMON, mage.cards.n.NobleBenefactor.class)); - cards.add(new SetCardInfo("Null Rod", 154, Rarity.RARE, mage.cards.n.NullRod.class)); - cards.add(new SetCardInfo("Odylic Wraith", 77, Rarity.UNCOMMON, mage.cards.o.OdylicWraith.class)); - cards.add(new SetCardInfo("Ophidian", 45, Rarity.COMMON, mage.cards.o.Ophidian.class)); - cards.add(new SetCardInfo("Orcish Settlers", 112, Rarity.UNCOMMON, mage.cards.o.OrcishSettlers.class)); - cards.add(new SetCardInfo("Paradigm Shift", 46, Rarity.RARE, mage.cards.p.ParadigmShift.class)); - cards.add(new SetCardInfo("Peacekeeper", 22, Rarity.RARE, mage.cards.p.Peacekeeper.class)); - cards.add(new SetCardInfo("Pendrell Mists", 47, Rarity.RARE, mage.cards.p.PendrellMists.class)); - cards.add(new SetCardInfo("Phantom Warrior", 48, Rarity.UNCOMMON, mage.cards.p.PhantomWarrior.class)); - cards.add(new SetCardInfo("Phantom Wings", 49, Rarity.COMMON, mage.cards.p.PhantomWings.class)); - cards.add(new SetCardInfo("Phyrexian Furnace", 155, Rarity.UNCOMMON, mage.cards.p.PhyrexianFurnace.class)); - cards.add(new SetCardInfo("Psychic Vortex", 50, Rarity.RARE, mage.cards.p.PsychicVortex.class)); - cards.add(new SetCardInfo("Razortooth Rats", 78, Rarity.COMMON, mage.cards.r.RazortoothRats.class)); - cards.add(new SetCardInfo("Redwood Treefolk", 138, Rarity.COMMON, mage.cards.r.RedwoodTreefolk.class)); - cards.add(new SetCardInfo("Relearn", 51, Rarity.UNCOMMON, mage.cards.r.Relearn.class)); - cards.add(new SetCardInfo("Revered Unicorn", 23, Rarity.UNCOMMON, mage.cards.r.ReveredUnicorn.class)); - cards.add(new SetCardInfo("Roc Hatchling", 113, Rarity.UNCOMMON, mage.cards.r.RocHatchling.class)); - cards.add(new SetCardInfo("Rogue Elephant", 139, Rarity.COMMON, mage.cards.r.RogueElephant.class)); - cards.add(new SetCardInfo("Sage Owl", 52, Rarity.COMMON, mage.cards.s.SageOwl.class)); - cards.add(new SetCardInfo("Scorched Ruins", 166, Rarity.RARE, mage.cards.s.ScorchedRuins.class)); - cards.add(new SetCardInfo("Serenity", 24, Rarity.RARE, mage.cards.s.Serenity.class)); - cards.add(new SetCardInfo("Serra's Blessing", 25, Rarity.UNCOMMON, mage.cards.s.SerrasBlessing.class)); - cards.add(new SetCardInfo("Serrated Biskelion", 156, Rarity.UNCOMMON, mage.cards.s.SerratedBiskelion.class)); - cards.add(new SetCardInfo("Shadow Rider", 79, Rarity.COMMON, mage.cards.s.ShadowRider.class)); - cards.add(new SetCardInfo("Shattered Crypt", 80, Rarity.COMMON, mage.cards.s.ShatteredCrypt.class)); - cards.add(new SetCardInfo("Soul Shepherd", 26, Rarity.COMMON, mage.cards.s.SoulShepherd.class)); - cards.add(new SetCardInfo("Southern Paladin", 27, Rarity.RARE, mage.cards.s.SouthernPaladin.class)); - cards.add(new SetCardInfo("Spinning Darkness", 81, Rarity.COMMON, mage.cards.s.SpinningDarkness.class)); - cards.add(new SetCardInfo("Steel Golem", 157, Rarity.UNCOMMON, mage.cards.s.SteelGolem.class)); - cards.add(new SetCardInfo("Strands of Night", 82, Rarity.UNCOMMON, mage.cards.s.StrandsOfNight.class)); - cards.add(new SetCardInfo("Straw Golem", 158, Rarity.UNCOMMON, mage.cards.s.StrawGolem.class)); - cards.add(new SetCardInfo("Striped Bears", 140, Rarity.COMMON, mage.cards.s.StripedBears.class)); - cards.add(new SetCardInfo("Sylvan Hierophant", 141, Rarity.UNCOMMON, mage.cards.s.SylvanHierophant.class)); - cards.add(new SetCardInfo("Tariff", 28, Rarity.RARE, mage.cards.t.Tariff.class)); - cards.add(new SetCardInfo("Teferi's Veil", 53, Rarity.UNCOMMON, mage.cards.t.TeferisVeil.class)); - cards.add(new SetCardInfo("Tendrils of Despair", 83, Rarity.COMMON, mage.cards.t.TendrilsOfDespair.class)); - cards.add(new SetCardInfo("Thran Forge", 159, Rarity.UNCOMMON, mage.cards.t.ThranForge.class)); - cards.add(new SetCardInfo("Thunderbolt", 115, Rarity.COMMON, mage.cards.t.Thunderbolt.class)); - cards.add(new SetCardInfo("Thundermare", 116, Rarity.RARE, mage.cards.t.Thundermare.class)); - cards.add(new SetCardInfo("Timid Drake", 54, Rarity.UNCOMMON, mage.cards.t.TimidDrake.class)); - cards.add(new SetCardInfo("Tolarian Drake", 55, Rarity.COMMON, mage.cards.t.TolarianDrake.class)); - cards.add(new SetCardInfo("Tolarian Entrancer", 56, Rarity.RARE, mage.cards.t.TolarianEntrancer.class)); - cards.add(new SetCardInfo("Tolarian Serpent", 57, Rarity.RARE, mage.cards.t.TolarianSerpent.class)); - cards.add(new SetCardInfo("Touchstone", 161, Rarity.UNCOMMON, mage.cards.t.Touchstone.class)); - cards.add(new SetCardInfo("Tranquil Grove", 142, Rarity.RARE, mage.cards.t.TranquilGrove.class)); - cards.add(new SetCardInfo("Uktabi Efreet", 143, Rarity.COMMON, mage.cards.u.UktabiEfreet.class)); - cards.add(new SetCardInfo("Urborg Justice", 84, Rarity.RARE, mage.cards.u.UrborgJustice.class)); - cards.add(new SetCardInfo("Urborg Stalker", 85, Rarity.RARE, mage.cards.u.UrborgStalker.class)); - cards.add(new SetCardInfo("Veteran Explorer", 144, Rarity.UNCOMMON, mage.cards.v.VeteranExplorer.class)); - cards.add(new SetCardInfo("Vitalize", 145, Rarity.COMMON, mage.cards.v.Vitalize.class)); - cards.add(new SetCardInfo("Vodalian Illusionist", 58, Rarity.UNCOMMON, mage.cards.v.VodalianIllusionist.class)); - cards.add(new SetCardInfo("Volunteer Reserves", 29, Rarity.UNCOMMON, mage.cards.v.VolunteerReserves.class)); - cards.add(new SetCardInfo("Wave of Terror", 86, Rarity.RARE, mage.cards.w.WaveOfTerror.class)); - cards.add(new SetCardInfo("Well of Knowledge", 162, Rarity.RARE, mage.cards.w.WellOfKnowledge.class)); - cards.add(new SetCardInfo("Winding Canyons", 167, Rarity.RARE, mage.cards.w.WindingCanyons.class)); - cards.add(new SetCardInfo("Xanthic Statue", 163, Rarity.RARE, mage.cards.x.XanthicStatue.class)); - cards.add(new SetCardInfo("Zombie Scavengers", 87, Rarity.COMMON, mage.cards.z.ZombieScavengers.class)); - } -} + cards.add(new SetCardInfo("Arctic Wolves", 118, Rarity.UNCOMMON, mage.cards.a.ArcticWolves.class)); + cards.add(new SetCardInfo("Ardent Militia", 5, Rarity.COMMON, mage.cards.a.ArdentMilitia.class)); + cards.add(new SetCardInfo("Argivian Find", 6, Rarity.UNCOMMON, mage.cards.a.ArgivianFind.class)); + cards.add(new SetCardInfo("Argivian Restoration", 34, Rarity.UNCOMMON, mage.cards.a.ArgivianRestoration.class)); + cards.add(new SetCardInfo("Aura of Silence", 7, Rarity.UNCOMMON, mage.cards.a.AuraOfSilence.class)); + cards.add(new SetCardInfo("Avizoa", 35, Rarity.RARE, mage.cards.a.Avizoa.class)); + cards.add(new SetCardInfo("Barishi", 119, Rarity.UNCOMMON, mage.cards.b.Barishi.class)); + cards.add(new SetCardInfo("Barrow Ghoul", 61, Rarity.COMMON, mage.cards.b.BarrowGhoul.class)); + cards.add(new SetCardInfo("Benalish Infantry", 8, Rarity.COMMON, mage.cards.b.BenalishInfantry.class)); + cards.add(new SetCardInfo("Benalish Knight", 9, Rarity.COMMON, mage.cards.b.BenalishKnight.class)); + cards.add(new SetCardInfo("Benalish Missionary", 10, Rarity.COMMON, mage.cards.b.BenalishMissionary.class)); + cards.add(new SetCardInfo("Betrothed of Fire", 89, Rarity.COMMON, mage.cards.b.BetrothedOfFire.class)); + cards.add(new SetCardInfo("Bloodrock Cyclops", 90, Rarity.COMMON, mage.cards.b.BloodrockCyclops.class)); + cards.add(new SetCardInfo("Blossoming Wreath", 120, Rarity.COMMON, mage.cards.b.BlossomingWreath.class)); + cards.add(new SetCardInfo("Bogardan Firefiend", 91, Rarity.COMMON, mage.cards.b.BogardanFirefiend.class)); + cards.add(new SetCardInfo("Boiling Blood", 92, Rarity.COMMON, mage.cards.b.BoilingBlood.class)); + cards.add(new SetCardInfo("Bone Dancer", 62, Rarity.RARE, mage.cards.b.BoneDancer.class)); + cards.add(new SetCardInfo("Bosium Strip", 147, Rarity.RARE, mage.cards.b.BosiumStrip.class)); + cards.add(new SetCardInfo("Briar Shield", 121, Rarity.COMMON, mage.cards.b.BriarShield.class)); + cards.add(new SetCardInfo("Bubble Matrix", 146, Rarity.RARE, mage.cards.b.BubbleMatrix.class)); + cards.add(new SetCardInfo("Buried Alive", 63, Rarity.UNCOMMON, mage.cards.b.BuriedAlive.class)); + cards.add(new SetCardInfo("Call of the Wild", 122, Rarity.RARE, mage.cards.c.CallOfTheWild.class)); + cards.add(new SetCardInfo("Chimeric Sphere", 148, Rarity.UNCOMMON, mage.cards.c.ChimericSphere.class)); + cards.add(new SetCardInfo("Choking Vines", 123, Rarity.COMMON, mage.cards.c.ChokingVines.class)); + cards.add(new SetCardInfo("Cinder Giant", 93, Rarity.UNCOMMON, mage.cards.c.CinderGiant.class)); + cards.add(new SetCardInfo("Cinder Wall", 94, Rarity.COMMON, mage.cards.c.CinderWall.class)); + cards.add(new SetCardInfo("Circling Vultures", 64, Rarity.UNCOMMON, mage.cards.c.CirclingVultures.class)); + cards.add(new SetCardInfo("Cloud Djinn", 36, Rarity.UNCOMMON, mage.cards.c.CloudDjinn.class)); + cards.add(new SetCardInfo("Coils of the Medusa", 65, Rarity.COMMON, mage.cards.c.CoilsOfTheMedusa.class)); + cards.add(new SetCardInfo("Cone of Flame", 95, Rarity.UNCOMMON, mage.cards.c.ConeOfFlame.class)); + cards.add(new SetCardInfo("Debt of Loyalty", 11, Rarity.RARE, mage.cards.d.DebtOfLoyalty.class)); + cards.add(new SetCardInfo("Dense Foliage", 124, Rarity.RARE, mage.cards.d.DenseFoliage.class)); + cards.add(new SetCardInfo("Desperate Gambit", 96, Rarity.UNCOMMON, mage.cards.d.DesperateGambit.class)); + cards.add(new SetCardInfo("Dingus Staff", 149, Rarity.UNCOMMON, mage.cards.d.DingusStaff.class)); + cards.add(new SetCardInfo("Disrupt", 37, Rarity.COMMON, mage.cards.d.Disrupt.class)); + cards.add(new SetCardInfo("Doomsday", 66, Rarity.RARE, mage.cards.d.Doomsday.class)); + cards.add(new SetCardInfo("Downdraft", 125, Rarity.UNCOMMON, mage.cards.d.Downdraft.class)); + cards.add(new SetCardInfo("Duskrider Falcon", 12, Rarity.COMMON, mage.cards.d.DuskriderFalcon.class)); + cards.add(new SetCardInfo("Dwarven Berserker", 97, Rarity.COMMON, mage.cards.d.DwarvenBerserker.class)); + cards.add(new SetCardInfo("Dwarven Thaumaturgist", 98, Rarity.RARE, mage.cards.d.DwarvenThaumaturgist.class)); + cards.add(new SetCardInfo("Empyrial Armor", 13, Rarity.COMMON, mage.cards.e.EmpyrialArmor.class)); + cards.add(new SetCardInfo("Fallow Wurm", 126, Rarity.UNCOMMON, mage.cards.f.FallowWurm.class)); + cards.add(new SetCardInfo("Familiar Ground", 127, Rarity.UNCOMMON, mage.cards.f.FamiliarGround.class)); + cards.add(new SetCardInfo("Fatal Blow", 67, Rarity.COMMON, mage.cards.f.FatalBlow.class)); + cards.add(new SetCardInfo("Fervor", 99, Rarity.RARE, mage.cards.f.Fervor.class)); + cards.add(new SetCardInfo("Festering Evil", 68, Rarity.UNCOMMON, mage.cards.f.FesteringEvil.class)); + cards.add(new SetCardInfo("Fire Whip", 100, Rarity.COMMON, mage.cards.f.FireWhip.class)); + cards.add(new SetCardInfo("Firestorm", 101, Rarity.RARE, mage.cards.f.Firestorm.class)); + cards.add(new SetCardInfo("Fit of Rage", 102, Rarity.COMMON, mage.cards.f.FitOfRage.class)); + cards.add(new SetCardInfo("Fledgling Djinn", 69, Rarity.COMMON, mage.cards.f.FledglingDjinn.class)); + cards.add(new SetCardInfo("Flux", 39, Rarity.COMMON, mage.cards.f.Flux.class)); + cards.add(new SetCardInfo("Fog Elemental", 40, Rarity.COMMON, mage.cards.f.FogElemental.class)); + cards.add(new SetCardInfo("Foriysian Brigade", 14, Rarity.UNCOMMON, mage.cards.f.ForiysianBrigade.class)); + cards.add(new SetCardInfo("Fungus Elemental", 128, Rarity.RARE, mage.cards.f.FungusElemental.class)); + cards.add(new SetCardInfo("Gaea's Blessing", 129, Rarity.UNCOMMON, mage.cards.g.GaeasBlessing.class)); + cards.add(new SetCardInfo("Gallowbraid", 70, Rarity.RARE, mage.cards.g.Gallowbraid.class)); + cards.add(new SetCardInfo("Gemstone Mine", 164, Rarity.UNCOMMON, mage.cards.g.GemstoneMine.class)); + cards.add(new SetCardInfo("Gerrard's Wisdom", 15, Rarity.UNCOMMON, mage.cards.g.GerrardsWisdom.class)); + cards.add(new SetCardInfo("Goblin Bomb", 103, Rarity.RARE, mage.cards.g.GoblinBomb.class)); + cards.add(new SetCardInfo("Goblin Grenadiers", 104, Rarity.UNCOMMON, mage.cards.g.GoblinGrenadiers.class)); + cards.add(new SetCardInfo("Goblin Vandal", 105, Rarity.COMMON, mage.cards.g.GoblinVandal.class)); + cards.add(new SetCardInfo("Guided Strike", 16, Rarity.COMMON, mage.cards.g.GuidedStrike.class)); + cards.add(new SetCardInfo("Harvest Wurm", 130, Rarity.COMMON, mage.cards.h.HarvestWurm.class)); + cards.add(new SetCardInfo("Haunting Misery", 71, Rarity.COMMON, mage.cards.h.HauntingMisery.class)); + cards.add(new SetCardInfo("Heart of Bogardan", 106, Rarity.RARE, mage.cards.h.HeartOfBogardan.class)); + cards.add(new SetCardInfo("Heat Stroke", 107, Rarity.RARE, mage.cards.h.HeatStroke.class)); + cards.add(new SetCardInfo("Heavy Ballista", 17, Rarity.COMMON, mage.cards.h.HeavyBallista.class)); + cards.add(new SetCardInfo("Hidden Horror", 72, Rarity.UNCOMMON, mage.cards.h.HiddenHorror.class)); + cards.add(new SetCardInfo("Hurloon Shaman", 108, Rarity.UNCOMMON, mage.cards.h.HurloonShaman.class)); + cards.add(new SetCardInfo("Infernal Tribute", 73, Rarity.RARE, mage.cards.i.InfernalTribute.class)); + cards.add(new SetCardInfo("Inner Sanctum", 18, Rarity.RARE, mage.cards.i.InnerSanctum.class)); + cards.add(new SetCardInfo("Jabari's Banner", 150, Rarity.UNCOMMON, mage.cards.j.JabarisBanner.class)); + cards.add(new SetCardInfo("Jangling Automaton", 151, Rarity.COMMON, mage.cards.j.JanglingAutomaton.class)); + cards.add(new SetCardInfo("Kithkin Armor", 19, Rarity.COMMON, mage.cards.k.KithkinArmor.class)); + cards.add(new SetCardInfo("Lava Hounds", 109, Rarity.UNCOMMON, mage.cards.l.LavaHounds.class)); + cards.add(new SetCardInfo("Lava Storm", 110, Rarity.COMMON, mage.cards.l.LavaStorm.class)); + cards.add(new SetCardInfo("Liege of the Hollows", 131, Rarity.RARE, mage.cards.l.LiegeOfTheHollows.class)); + cards.add(new SetCardInfo("Llanowar Behemoth", 132, Rarity.UNCOMMON, mage.cards.l.LlanowarBehemoth.class)); + cards.add(new SetCardInfo("Llanowar Druid", 133, Rarity.COMMON, mage.cards.l.LlanowarDruid.class)); + cards.add(new SetCardInfo("Llanowar Sentinel", 134, Rarity.COMMON, mage.cards.l.LlanowarSentinel.class)); + cards.add(new SetCardInfo("Lotus Vale", 165, Rarity.RARE, mage.cards.l.LotusVale.class)); + cards.add(new SetCardInfo("Mana Chains", 41, Rarity.COMMON, mage.cards.m.ManaChains.class)); + cards.add(new SetCardInfo("Mana Web", 152, Rarity.RARE, mage.cards.m.ManaWeb.class)); + cards.add(new SetCardInfo("Manta Ray", 42, Rarity.COMMON, mage.cards.m.MantaRay.class)); + cards.add(new SetCardInfo("Maraxus of Keld", 111, Rarity.RARE, mage.cards.m.MaraxusOfKeld.class)); + cards.add(new SetCardInfo("Master of Arms", 20, Rarity.UNCOMMON, mage.cards.m.MasterOfArms.class)); + cards.add(new SetCardInfo("Merfolk Traders", 43, Rarity.COMMON, mage.cards.m.MerfolkTraders.class)); + cards.add(new SetCardInfo("Mind Stone", 153, Rarity.COMMON, mage.cards.m.MindStone.class)); + cards.add(new SetCardInfo("Mischievous Poltergeist", 74, Rarity.UNCOMMON, mage.cards.m.MischievousPoltergeist.class)); + cards.add(new SetCardInfo("Mistmoon Griffin", 21, Rarity.UNCOMMON, mage.cards.m.MistmoonGriffin.class)); + cards.add(new SetCardInfo("Morinfen", 75, Rarity.RARE, mage.cards.m.Morinfen.class)); + cards.add(new SetCardInfo("Mwonvuli Ooze", 135, Rarity.RARE, mage.cards.m.MwonvuliOoze.class)); + cards.add(new SetCardInfo("Nature's Kiss", 136, Rarity.COMMON, mage.cards.n.NaturesKiss.class)); + cards.add(new SetCardInfo("Nature's Resurgence", 137, Rarity.RARE, mage.cards.n.NaturesResurgence.class)); + cards.add(new SetCardInfo("Necratog", 76, Rarity.UNCOMMON, mage.cards.n.Necratog.class)); + cards.add(new SetCardInfo("Noble Benefactor", 44, Rarity.UNCOMMON, mage.cards.n.NobleBenefactor.class)); + cards.add(new SetCardInfo("Null Rod", 154, Rarity.RARE, mage.cards.n.NullRod.class)); + cards.add(new SetCardInfo("Odylic Wraith", 77, Rarity.UNCOMMON, mage.cards.o.OdylicWraith.class)); + cards.add(new SetCardInfo("Ophidian", 45, Rarity.COMMON, mage.cards.o.Ophidian.class)); + cards.add(new SetCardInfo("Orcish Settlers", 112, Rarity.UNCOMMON, mage.cards.o.OrcishSettlers.class)); + cards.add(new SetCardInfo("Paradigm Shift", 46, Rarity.RARE, mage.cards.p.ParadigmShift.class)); + cards.add(new SetCardInfo("Peacekeeper", 22, Rarity.RARE, mage.cards.p.Peacekeeper.class)); + cards.add(new SetCardInfo("Pendrell Mists", 47, Rarity.RARE, mage.cards.p.PendrellMists.class)); + cards.add(new SetCardInfo("Phantom Warrior", 48, Rarity.UNCOMMON, mage.cards.p.PhantomWarrior.class)); + cards.add(new SetCardInfo("Phantom Wings", 49, Rarity.COMMON, mage.cards.p.PhantomWings.class)); + cards.add(new SetCardInfo("Phyrexian Furnace", 155, Rarity.UNCOMMON, mage.cards.p.PhyrexianFurnace.class)); + cards.add(new SetCardInfo("Psychic Vortex", 50, Rarity.RARE, mage.cards.p.PsychicVortex.class)); + cards.add(new SetCardInfo("Razortooth Rats", 78, Rarity.COMMON, mage.cards.r.RazortoothRats.class)); + cards.add(new SetCardInfo("Redwood Treefolk", 138, Rarity.COMMON, mage.cards.r.RedwoodTreefolk.class)); + cards.add(new SetCardInfo("Relearn", 51, Rarity.UNCOMMON, mage.cards.r.Relearn.class)); + cards.add(new SetCardInfo("Revered Unicorn", 23, Rarity.UNCOMMON, mage.cards.r.ReveredUnicorn.class)); + cards.add(new SetCardInfo("Roc Hatchling", 113, Rarity.UNCOMMON, mage.cards.r.RocHatchling.class)); + cards.add(new SetCardInfo("Rogue Elephant", 139, Rarity.COMMON, mage.cards.r.RogueElephant.class)); + cards.add(new SetCardInfo("Sage Owl", 52, Rarity.COMMON, mage.cards.s.SageOwl.class)); + cards.add(new SetCardInfo("Sawtooth Ogre", 114, Rarity.COMMON, mage.cards.s.SawtoothOgre.class)); + cards.add(new SetCardInfo("Scorched Ruins", 166, Rarity.RARE, mage.cards.s.ScorchedRuins.class)); + cards.add(new SetCardInfo("Serenity", 24, Rarity.RARE, mage.cards.s.Serenity.class)); + cards.add(new SetCardInfo("Serra's Blessing", 25, Rarity.UNCOMMON, mage.cards.s.SerrasBlessing.class)); + cards.add(new SetCardInfo("Serrated Biskelion", 156, Rarity.UNCOMMON, mage.cards.s.SerratedBiskelion.class)); + cards.add(new SetCardInfo("Shadow Rider", 79, Rarity.COMMON, mage.cards.s.ShadowRider.class)); + cards.add(new SetCardInfo("Shattered Crypt", 80, Rarity.COMMON, mage.cards.s.ShatteredCrypt.class)); + cards.add(new SetCardInfo("Soul Shepherd", 26, Rarity.COMMON, mage.cards.s.SoulShepherd.class)); + cards.add(new SetCardInfo("Southern Paladin", 27, Rarity.RARE, mage.cards.s.SouthernPaladin.class)); + cards.add(new SetCardInfo("Spinning Darkness", 81, Rarity.COMMON, mage.cards.s.SpinningDarkness.class)); + cards.add(new SetCardInfo("Steel Golem", 157, Rarity.UNCOMMON, mage.cards.s.SteelGolem.class)); + cards.add(new SetCardInfo("Strands of Night", 82, Rarity.UNCOMMON, mage.cards.s.StrandsOfNight.class)); + cards.add(new SetCardInfo("Straw Golem", 158, Rarity.UNCOMMON, mage.cards.s.StrawGolem.class)); + cards.add(new SetCardInfo("Striped Bears", 140, Rarity.COMMON, mage.cards.s.StripedBears.class)); + cards.add(new SetCardInfo("Sylvan Hierophant", 141, Rarity.UNCOMMON, mage.cards.s.SylvanHierophant.class)); + cards.add(new SetCardInfo("Tariff", 28, Rarity.RARE, mage.cards.t.Tariff.class)); + cards.add(new SetCardInfo("Teferi's Veil", 53, Rarity.UNCOMMON, mage.cards.t.TeferisVeil.class)); + cards.add(new SetCardInfo("Tendrils of Despair", 83, Rarity.COMMON, mage.cards.t.TendrilsOfDespair.class)); + cards.add(new SetCardInfo("Thran Forge", 159, Rarity.UNCOMMON, mage.cards.t.ThranForge.class)); + cards.add(new SetCardInfo("Thran Tome", 160, Rarity.RARE, mage.cards.t.ThranTome.class)); + cards.add(new SetCardInfo("Thunderbolt", 115, Rarity.COMMON, mage.cards.t.Thunderbolt.class)); + cards.add(new SetCardInfo("Thundermare", 116, Rarity.RARE, mage.cards.t.Thundermare.class)); + cards.add(new SetCardInfo("Timid Drake", 54, Rarity.UNCOMMON, mage.cards.t.TimidDrake.class)); + cards.add(new SetCardInfo("Tolarian Drake", 55, Rarity.COMMON, mage.cards.t.TolarianDrake.class)); + cards.add(new SetCardInfo("Tolarian Entrancer", 56, Rarity.RARE, mage.cards.t.TolarianEntrancer.class)); + cards.add(new SetCardInfo("Tolarian Serpent", 57, Rarity.RARE, mage.cards.t.TolarianSerpent.class)); + cards.add(new SetCardInfo("Touchstone", 161, Rarity.UNCOMMON, mage.cards.t.Touchstone.class)); + cards.add(new SetCardInfo("Tranquil Grove", 142, Rarity.RARE, mage.cards.t.TranquilGrove.class)); + cards.add(new SetCardInfo("Uktabi Efreet", 143, Rarity.COMMON, mage.cards.u.UktabiEfreet.class)); + cards.add(new SetCardInfo("Urborg Justice", 84, Rarity.RARE, mage.cards.u.UrborgJustice.class)); + cards.add(new SetCardInfo("Urborg Stalker", 85, Rarity.RARE, mage.cards.u.UrborgStalker.class)); + cards.add(new SetCardInfo("Veteran Explorer", 144, Rarity.UNCOMMON, mage.cards.v.VeteranExplorer.class)); + cards.add(new SetCardInfo("Vitalize", 145, Rarity.COMMON, mage.cards.v.Vitalize.class)); + cards.add(new SetCardInfo("Vodalian Illusionist", 58, Rarity.UNCOMMON, mage.cards.v.VodalianIllusionist.class)); + cards.add(new SetCardInfo("Volunteer Reserves", 29, Rarity.UNCOMMON, mage.cards.v.VolunteerReserves.class)); + cards.add(new SetCardInfo("Wave of Terror", 86, Rarity.RARE, mage.cards.w.WaveOfTerror.class)); + cards.add(new SetCardInfo("Well of Knowledge", 162, Rarity.RARE, mage.cards.w.WellOfKnowledge.class)); + cards.add(new SetCardInfo("Winding Canyons", 167, Rarity.RARE, mage.cards.w.WindingCanyons.class)); + cards.add(new SetCardInfo("Xanthic Statue", 163, Rarity.RARE, mage.cards.x.XanthicStatue.class)); + cards.add(new SetCardInfo("Zombie Scavengers", 87, Rarity.COMMON, mage.cards.z.ZombieScavengers.class)); + } +} diff --git a/Mage.Tests/CommanderDuel_Mairisil_UBR.dck b/Mage.Tests/CommanderDuel_Mairisil_UBR.dck new file mode 100644 index 0000000000..e2368e5a05 --- /dev/null +++ b/Mage.Tests/CommanderDuel_Mairisil_UBR.dck @@ -0,0 +1,90 @@ +1 [DGM:11] Aetherling +1 [JUD:77] Anger +1 [10E:66] Arcanis the Omnipotent +1 [SOM:28] Argent Sphinx +1 [CHK:52] Azami, Lady of Scrolls +1 [AVR:47] Deadeye Navigator +1 [DRK:44] Eater of the Dead +1 [EXO:33] Ertai, Wizard Adept +1 [ALA:43] Fatestitcher +1 [SOM:64] Geth, Lord of the Vault +1 [EVE:55] Hateflayer +1 [DKA:139] Havengul Lich +1 [10E:87] Horseshoe Crab +1 [ICE:136] Infernal Denizen +1 [M12:59] Jace's Archivist +1 [SHM:42] Knacksaw Clique +1 [OGW:4] Kozilek, the Great Distortion +1 [PLC:43] Magus of the Bazaar +1 [ICE:150] Minion of Leshrac +1 [SOM:72] Necrotic Ooze +1 [SHM:258] Pili-Pala +1 [MRD:47] Quicksilver Elemental +1 [SOK:53] Sakashima the Impostor +1 [MIR:142] Shauku, Endbringer +1 [M15:231] Soul of New Phyrexia +1 [PLC:110] Torchling +1 [EMN:109] Tree of Perdition +1 [M20:64] Leyline of Anticipation +1 [9ED:152] Phyrexian Arena +1 [PCY:45] Rhystic Study +1 [RNA:245] Blood Crypt +1 [KTK:230] Bloodstained Mire +1 [WWK:132] Bojuka Bog +1 [ELD:333] Command Tower +1 [ALA:222] Crumbling Necropolis +1 [RAV:276] Dimir Aqueduct +1 [CON:142] Exotic Orchard +1 [CHK:277] Hall of the Bandit Lord +8 [IKO:263] Island +1 [GPT:159] Izzet Boilerworks +1 [CHK:279] Minamo, School at Water's Edge +3 [IKO:269] Mountain +1 [C20:298] Path of Ancestry +1 [KTK:239] Polluted Delta +1 [M19:254] Reliquary Tower +1 [ONS:322] Riptide Laboratory +1 [ORI:251] Shivan Reef +1 [GRN:257] Steam Vents +1 [10E:359] Sulfurous Springs +1 [BFZ:249] Sunken Hollow +4 [IKO:266] Swamp +1 [10E:362] Underground River +1 [GRN:259] Watery Grave +1 [ODY:118] Buried Alive +1 [3ED:105] Demonic Tutor +1 [USG:188] Gamble +1 [AVR:151] Reforge the Soul +1 [EMA:108] Toxic Deluge +1 [MB1:1603] Mana Crypt +1 [5ED:388] Mana Vault +1 [HOU:165] Mirage Mirror +1 [5ED:391] Nevinyrral's Disk +1 [CHK:268] Sensei's Divining Top +1 [3ED:274] Sol Ring +1 [5DN:156] Staff of Domination +1 [LRW:263] Thousand-Year Elixir +1 [UDS:139] Thran Dynamo +1 [5DN:163] Vedalken Orrery +1 [TMP:55] Capsize +1 [C20:146] Chaos Warp +1 [RTR:35] Cyclonic Rift +1 [ODY:132] Entomb +1 [INV:57] Fact or Fiction +1 [TMP:70] Intuition +1 [THS:65] Swan Song +1 [RTR:111] Vandalblast +1 [3ED:185] Wheel of Fortune +1 [GTC:207] Whispering Madness +1 [USG:111] Windfall +1 [SHM:248] Cauldron of Souls +1 [RAV:260] Dimir Signet +1 [9ED:297] Fellwar Stone +1 [DOM:215] Gilded Lotus +1 [ULG:126] Grim Monolith +1 [GTC:231] Illusionist's Bracers +1 [GPT:152] Izzet Signet +1 [MRD:199] Lightning Greaves +SB: 1 [C17:41] Mairsil, the Pretender +LAYOUT MAIN:(1,7)(CARD_TYPE,false,50)|([SHM:258],[M15:231])([SHM:248],[RAV:260],[9ED:297],[DOM:215],[ULG:126],[GTC:231],[GPT:152],[MRD:199],[MB1:1603],[5ED:388],[HOU:165],[5ED:391],[CHK:268],[3ED:274],[5DN:156],[LRW:263],[UDS:139],[5DN:163])([DGM:11],[JUD:77],[10E:66],[SOM:28],[CHK:52],[AVR:47],[DRK:44],[EXO:33],[ALA:43],[SOM:64],[EVE:55],[DKA:139],[10E:87],[ICE:136],[M12:59],[SHM:42],[OGW:4],[PLC:43],[ICE:150],[SOM:72],[MRD:47],[SOK:53],[MIR:142],[PLC:110],[EMN:109])([M20:64],[9ED:152],[PCY:45])([TMP:55],[C20:146],[RTR:35],[ODY:132],[INV:57],[TMP:70],[THS:65])([RNA:245],[KTK:230],[WWK:132],[ELD:333],[ALA:222],[RAV:276],[CON:142],[CHK:277],[IKO:263],[IKO:263],[IKO:263],[IKO:263],[IKO:263],[IKO:263],[IKO:263],[IKO:263],[GPT:159],[CHK:279],[IKO:269],[IKO:269],[IKO:269],[C20:298],[KTK:239],[M19:254],[ONS:322],[ORI:251],[GRN:257],[10E:359],[BFZ:249],[IKO:266],[IKO:266],[IKO:266],[IKO:266],[10E:362],[GRN:259])([ODY:118],[3ED:105],[USG:188],[AVR:151],[EMA:108],[RTR:111],[3ED:185],[GTC:207],[USG:111]) +LAYOUT SIDEBOARD:(1,1)(NONE,false,50)|([C17:41]) diff --git a/Mage.Tests/Oathbreaker_UR.dck b/Mage.Tests/Oathbreaker_UR.dck new file mode 100644 index 0000000000..9574902d44 --- /dev/null +++ b/Mage.Tests/Oathbreaker_UR.dck @@ -0,0 +1,48 @@ +NAME:Oathbreaker_UR +1 [M15:44] Aetherspouts +1 [ISD:130] Blasphemous Act +1 [M21:46] Cancel +1 [C20:146] Chaos Warp +1 [GRN:233] Chromatic Lantern +1 [7ED:67] Counterspell +1 [M15:242] Darksteel Citadel +1 [EMN:55] Displace +1 [RAV:46] Drift of Phantasms +1 [JMP:313] Dualcaster Mage +1 [NPH:35] Gitaxian Probe +1 [MRD:282] Great Furnace +1 [M19:145] Guttersnipe +1 [LRW:175] Heat Shimmer +1 [DTK:140] Impact Tremors +1 [VIS:34] Impulse +9 [M21:263] Island +1 [GRN:251] Izzet Guildgate +1 [GRN:238] Izzet Locket +1 [M12:63] Mana Leak +1 [SOI:73] Manic Scribe +7 [M21:269] Mountain +1 [M20:69] Negate +1 [M21:59] Opt +1 [AER:167] Ornithopter +1 [ELD:60] Overwhelmed Apprentice +1 [M11:70] Preordain +1 [M19:68] Psychic Corrosion +1 [THS:135] Purphoros, God of the Forge +1 [KLD:126] Reckless Fireweaver +1 [M19:254] Reliquary Tower +1 [MRD:283] Seat of the Synod +1 [CHK:268] Sensei's Divining Top +1 [M15:161] Shrapnel Blast +1 [5DN:150] Silent Arbiter +1 [XLN:81] Spell Pierce +1 [M10:220] Spellbook +1 [SOM:46] Stoic Rebuttal +1 [MH1:231] Talisman of Creativity +1 [M11:229] Terramorphic Expanse +1 [KTK:59] Treasure Cruise +1 [JOU:115] Twinflame +1 [DOM:72] Unwind +1 [M14:163] Young Pyromancer +SB: 1 [WAR:234] Saheeli, Sublime Artificer +SB: 1 [MRD:54] Thoughtcast + diff --git a/Mage.Tests/pom.xml b/Mage.Tests/pom.xml index 7b61c3be4c..7245037acc 100644 --- a/Mage.Tests/pom.xml +++ b/Mage.Tests/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 mage-tests @@ -67,13 +67,13 @@ com.sun.xml.bind jaxb-impl - 2.3.2 + 2.3.3 org.glassfish.jaxb jaxb-runtime - 2.3.2 + 2.3.3 diff --git a/Mage.Tests/src/test/java/PyromancersGauntletTest.java b/Mage.Tests/src/test/java/PyromancersGauntletTest.java new file mode 100644 index 0000000000..6e0c6569b8 --- /dev/null +++ b/Mage.Tests/src/test/java/PyromancersGauntletTest.java @@ -0,0 +1,117 @@ + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.GameException; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class PyromancersGauntletTest extends CardTestPlayerBase { + + @Test + public void basicTest() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + // If a red instant or sorcery spell you control or a red planeswalker you control + // would deal damage to a permanent or player, it deals that much damage plus 2 to that permanent or player instead. + addCard(Zone.BATTLEFIELD, playerA, "Pyromancer's Gauntlet"); // Artifact {5} + + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Pillarfield Ox"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertLife(playerB, 15); // Bolt 3 + 2 + + assertGraveyardCount(playerA, "Lightning Bolt", 2); + assertGraveyardCount(playerB, "Pillarfield Ox", 1); + } + + @Test + public void opponentsPyromancersGauntletAppliedToOwnPlaneswalkerTest() { + setStrictChooseMode(true); + + // +1: Elementals you control get +2/+0 until end of turn. + // −1: Add {R}{R}. + // −2: Chandra, Novice Pyromancer deals 2 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Chandra, Novice Pyromancer"); // Planeswalker (5) {3}{R} + + // If a red instant or sorcery spell you control or a red planeswalker you control + // would deal damage to a permanent or player, it deals that much damage plus 2 to that permanent or player instead. + addCard(Zone.BATTLEFIELD, playerB, "Pyromancer's Gauntlet"); // Creature 2/4 {1}{R}{R}{R} + addCard(Zone.BATTLEFIELD, playerB, "Chandra, Novice Pyromancer"); // Planeswalker (5) {3}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Barbarian Horde"); // Creature 3/3 {3}{R} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2:", playerB); + + attack(2, playerB, "Barbarian Horde"); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "-2:", playerA); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "-2:", "Barbarian Horde"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 13); // Attack from Horde 3 + Dmage 2+2 from planeswalker + assertLife(playerB, 18); // Damage from planeswalker 2 + + assertPermanentCount(playerB, "Barbarian Horde", 1); + + assertCounterCount(playerA, "Chandra, Novice Pyromancer", CounterType.LOYALTY, 1); + assertCounterCount(playerB, "Chandra, Novice Pyromancer", CounterType.LOYALTY, 3); + } + + @Test + public void with3PlayersTest() throws GameException { + playerC = createPlayer(currentGame, playerC, "PlayerC"); + + setStrictChooseMode(true); + + // +1: Elementals you control get +2/+0 until end of turn. + // −1: Add {R}{R}. + // −2: Chandra, Novice Pyromancer deals 2 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Chandra, Novice Pyromancer"); // Planeswalker (5) {3}{R} + + // If a red instant or sorcery spell you control or a red planeswalker you control + // would deal damage to a permanent or player, it deals that much damage plus 2 to that permanent or player instead. + addCard(Zone.BATTLEFIELD, playerC, "Pyromancer's Gauntlet"); // Creature 2/4 {1}{R}{R}{R} + addCard(Zone.BATTLEFIELD, playerB, "Chandra, Novice Pyromancer"); // Planeswalker (5) {3}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Barbarian Horde"); // Creature 3/3 {3}{R} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2:", playerB); + + attack(3, playerB, "Barbarian Horde"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerB, "-2:", playerA); + + activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerA, "-2:", "Barbarian Horde"); + + setStopAt(4, PhaseStep.BEGIN_COMBAT); + + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 15); // Attack from Horde 3 + Dmage 2 from planeswalker + assertLife(playerB, 18); // Damage from planeswalker 2 + assertLife(playerC, 20); + assertPermanentCount(playerB, "Barbarian Horde", 1); + + assertCounterCount(playerA, "Chandra, Novice Pyromancer", CounterType.LOYALTY, 1); + assertCounterCount(playerB, "Chandra, Novice Pyromancer", CounterType.LOYALTY, 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/ExileTargetTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/ExileTargetTest.java index fd345c9e9b..67b03d91fd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/AI/basic/ExileTargetTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/ExileTargetTest.java @@ -25,11 +25,11 @@ public class ExileTargetTest extends CardTestCommander4Players { addCard(Zone.BATTLEFIELD, playerC, "Balduvian Bears", 1); // 2/2 // must select opponent's Balduvian Bears - // showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + // showAvailableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Oblivion Ring"); //addTarget(playerA, "Balduvian Bears"); // disable to activate AI target choose - // showAvaileableAbilities("after", 1, PhaseStep.BEGIN_COMBAT, playerA); + // showAvailableAbilities("after", 1, PhaseStep.BEGIN_COMBAT, playerA); //setStrictChooseMode(true); // disable strict mode to activate AI for choosing setStopAt(1, PhaseStep.END_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/TestFrameworkCanPlayAITest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/TestFrameworkCanPlayAITest.java index 64c863ba4a..38b1c2286c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/AI/basic/TestFrameworkCanPlayAITest.java +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/TestFrameworkCanPlayAITest.java @@ -52,6 +52,40 @@ public class TestFrameworkCanPlayAITest extends CardTestPlayerBaseWithAIHelps { assertPermanentCount(playerB, "Balduvian Bears", 5 - 3); } + @Test + public void test_AI_Attack() { + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + // AI must attack + aiPlayStep(1, PhaseStep.DECLARE_ATTACKERS, playerA); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 2); + } + + @Test + public void test_AI_Block() { + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + + // AI must block + attack(1, playerA, "Balduvian Bears"); + aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, 1); + assertGraveyardCount(playerB, 1); + assertLife(playerB, 20); + } + @Test @Ignore // AI can't play blade cause score system give priority for boost instead restriction effects like goad public void test_AI_GoadedByBloodthirstyBlade_Normal() { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ChaosWandTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ChaosWandTest.java index d570d87c7f..4b98e27289 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ChaosWandTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ChaosWandTest.java @@ -25,7 +25,6 @@ public class ChaosWandTest extends CardTestPlayerBase { activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}, {T}: "); addTarget(playerA, playerB); setChoice(playerA, "Yes"); // cast for free - setChoice(playerA, "Cast Blood Tithe"); setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java index a7dffb1a1d..3f74e4b399 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java @@ -16,8 +16,8 @@ public class CursesTest extends CardTestPlayerBase { Enchantment - Aura Curse Enchant player At the beginning of enchanted player's upkeep, that player sacrifices a creature or planeswalker. If the player can't, they lose 5 life. - */ - private String cReality = "Cruel Reality"; + */ + private final String cReality = "Cruel Reality"; @Test public void testCurseOfBloodletting() { @@ -29,7 +29,6 @@ public class CursesTest extends CardTestPlayerBase { castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerA); - setStopAt(1, PhaseStep.END_TURN); execute(); @@ -43,7 +42,7 @@ public class CursesTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Island", 5); // Enchant player // Whenever enchanted player casts an instant or sorcery spell, each other player may copy that - // spell and may choose new targets for the copy they control. + // spell and may choose new targets for the copy they control. addCard(Zone.HAND, playerA, "Curse of Echoes"); // Draw three cards. addCard(Zone.HAND, playerB, "Jace's Ingenuity"); @@ -71,7 +70,6 @@ public class CursesTest extends CardTestPlayerBase { castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", playerA); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", playerA); - setStopAt(1, PhaseStep.END_TURN); execute(); @@ -90,7 +88,6 @@ public class CursesTest extends CardTestPlayerBase { castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); - setStopAt(1, PhaseStep.END_TURN); execute(); @@ -102,64 +99,59 @@ public class CursesTest extends CardTestPlayerBase { * Checks if Copy Enchantment works for player auras */ @Test - public void testCurseOfExhaustion3() { + public void testCurseOfExhaustion3() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); - - addCard(Zone.BATTLEFIELD, playerB, "Island", 3); - + + addCard(Zone.BATTLEFIELD, playerB, "Island", 3); + // Enchant player // Enchanted player can't cast more than one spell each turn. addCard(Zone.HAND, playerA, "Curse of Exhaustion"); - addCard(Zone.HAND, playerA, "Lightning Bolt", 2); - + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + addCard(Zone.HAND, playerB, "Copy Enchantment", 1); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Exhaustion", playerB); - + castSpell(4, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Copy Enchantment"); setChoice(playerB, "Yes"); setChoice(playerB, "Curse of Exhaustion"); setChoice(playerB, "targetPlayer=PlayerA"); castSpell(4, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); - - setStopAt(4, PhaseStep.END_TURN); execute(); assertHandCount(playerB, "Copy Enchantment", 0); assertGraveyardCount(playerB, "Copy Enchantment", 0); - - assertPermanentCount(playerA, "Curse of Exhaustion", 1); + + assertPermanentCount(playerA, "Curse of Exhaustion", 1); assertPermanentCount(playerB, "Curse of Exhaustion", 1); - + assertLife(playerA, 20); assertLife(playerB, 17); } - + // returng curse enchantment from graveyard to battlefield @Test - public void testCurseOfExhaustion4() { + public void testCurseOfExhaustion4() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); - + addCard(Zone.BATTLEFIELD, playerB, "Plains", 3); addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); - + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); - + addCard(Zone.GRAVEYARD, playerB, "Curse of Exhaustion", 1); addCard(Zone.HAND, playerB, "Obzedat's Aid", 1); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Obzedat's Aid", "Curse of Exhaustion"); setChoice(playerB, "PlayerA"); - - - castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); - castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); setStopAt(2, PhaseStep.END_TURN); execute(); @@ -167,13 +159,13 @@ public class CursesTest extends CardTestPlayerBase { assertHandCount(playerB, "Obzedat's Aid", 0); assertGraveyardCount(playerB, "Obzedat's Aid", 1); assertGraveyardCount(playerB, "Curse of Exhaustion", 0); - + assertPermanentCount(playerB, "Curse of Exhaustion", 1); - + assertLife(playerA, 20); assertLife(playerB, 17); - } - + } + @Test public void testCurseOfThirst1() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); @@ -240,10 +232,9 @@ public class CursesTest extends CardTestPlayerBase { assertLife(playerA, 20); assertLife(playerB, 20); assertPermanentCount(playerA, "Curse of Misfortunes", 1); - assertPermanentCount(playerA, "Curse of Bloodletting", 1); + assertPermanentCount(playerA, "Curse of Bloodletting", 1); } - - + @Test public void testCurseOfDeathsHold() { // Creatures enchanted player controls get -1/-1. @@ -251,7 +242,7 @@ public class CursesTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB); setStopAt(1, PhaseStep.END_COMBAT); @@ -259,12 +250,12 @@ public class CursesTest extends CardTestPlayerBase { assertLife(playerA, 20); assertLife(playerB, 20); - + assertPermanentCount(playerA, "Curse of Death's Hold", 1); - + assertPowerToughness(playerB, "Silvercoat Lion", 1, 1); } - + @Test public void testCurseOfDeathsHold2() { // Creatures enchanted player controls get -1/-1. @@ -276,31 +267,29 @@ public class CursesTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); addCard(Zone.BATTLEFIELD, playerB, "Forest", 3); addCard(Zone.HAND, playerB, "Reclamation Sage"); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Reclamation Sage"); addTarget(playerB, "Curse of Death's Hold"); - + // {2}{G/U}{G/U}: Put the top two cards of your library into your graveyard, then return a nonland card of an opponent's choice from your graveyard to your hand. - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{G/U}{G/U}: Put the top two cards"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{G/U}{G/U}: Mill two cards"); castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB); - + setStopAt(3, PhaseStep.END_TURN); execute(); assertLife(playerA, 20); assertLife(playerB, 20); - + assertGraveyardCount(playerB, "Reclamation Sage", 1); assertPermanentCount(playerA, "Curse of Death's Hold", 1); assertGraveyardCount(playerA, 2); - + assertPowerToughness(playerB, "Silvercoat Lion", 1, 1); } - - @Test public void cruelRealityHasBothCreatureAndPwChoosePw() { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/GolemSkinGauntletsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/GolemSkinGauntletsTest.java index 8d4772bc3b..0941fb5c26 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/GolemSkinGauntletsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/GolemSkinGauntletsTest.java @@ -19,6 +19,9 @@ public class GolemSkinGauntletsTest extends CardTestPlayerBase { @Test public void testBoostOnEquip() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + // Equipped creature doesn't untap during its controller's untap step. + // Equipped creature has "{T}: This creature deals 2 damage to any target." + // Equip {4) addCard(Zone.BATTLEFIELD, playerA, "Heavy Arbalest"); addCard(Zone.BATTLEFIELD, playerA, "Golem-Skin Gauntlets"); addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java index c0fea22a22..d5c2da1270 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java @@ -19,8 +19,29 @@ public class AfflictTest extends CardTestPlayerBase { attack(1, playerA, khenra); block(1, playerB, elves, khenra); + setStrictChooseMode(true); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 19); + } + + @Test + public void testBecomesDoubleBlocked() { + addCard(Zone.BATTLEFIELD, playerA, khenra); + addCard(Zone.BATTLEFIELD, playerB, elves, 2); + + // afflict must trigger only once + attack(1, playerA, khenra); + block(1, playerB, elves + ":0", khenra); + block(1, playerB, elves + ":1", khenra); + setChoice(playerA, "X=1"); // assign damage + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); assertLife(playerB, 19); @@ -28,17 +49,17 @@ public class AfflictTest extends CardTestPlayerBase { @Test public void testNotBlocked() { - addCard(Zone.BATTLEFIELD, playerA, khenra); addCard(Zone.BATTLEFIELD, playerB, elves); attack(1, playerA, khenra); + setStrictChooseMode(true); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertLife(playerB, 18); - } // My afflict didn't come through after using Endless Sands on my own creature. The afflict ability was on the stack already. @@ -62,9 +83,11 @@ public class AfflictTest extends CardTestPlayerBase { attack(1, playerA, "Frontline Devastator"); block(1, playerB, "Ruin Rat", "Frontline Devastator"); + setStrictChooseMode(true); activateAbility(1, PhaseStep.DECLARE_BLOCKERS, playerA, "{2},", "Frontline Devastator"); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertExileCount(playerA, "Frontline Devastator", 1); assertPermanentCount(playerB, "Ruin Rat", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AftermathTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AftermathTest.java index 50d999743b..c39bcc250c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AftermathTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AftermathTest.java @@ -1,31 +1,31 @@ -package org.mage.test.cards.abilities.keywords; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author jeffwadsworth - */ -public class AftermathTest extends CardTestPlayerBase { - - @Test - public void testCastFromGraveyard() { - addCard(Zone.GRAVEYARD, playerA, "Spring // Mind", 1); - /* - Aftermath (Cast this spell only from your graveyard. Then exile it.) - Draw two cards. - */ - addCard(Zone.BATTLEFIELD, playerA, "Island", 6); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mind"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertExileCount(playerA, "Spring // Mind", 1); // card is exiled after casting from graveyard - assertHandCount(playerA, 2); // two cards drawn - - } -} +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author jeffwadsworth + */ +public class AftermathTest extends CardTestPlayerBase { + + @Test + public void testCastFromGraveyard() { + addCard(Zone.GRAVEYARD, playerA, "Spring // Mind", 1); + /* + Aftermath (Cast this spell only from your graveyard. Then exile it.) + Draw two cards. + */ + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mind"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertExileCount(playerA, "Spring // Mind", 1); // card is exiled after casting from graveyard + assertHandCount(playerA, 2); // two cards drawn + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AmplifyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AmplifyTest.java index d614bd3f8c..7fb7acf7da 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AmplifyTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AmplifyTest.java @@ -3,6 +3,7 @@ package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -17,7 +18,7 @@ public class AmplifyTest extends CardTestPlayerBase { * Tests if +1/+1 counters are added */ @Test - public void testAmplifyOneCard() { + public void test_Amplify_OneCard() { // Creature — Dragon - Dragon 5/5 {5}{R}{R} // Amplify 3 (As this creature enters the battlefield, put three +1/+1 counters on it for each Dragon card you reveal in your hand.) // {T}: Kilnmouth Dragon deals damage equal to the number of +1/+1 counters on it to any target @@ -28,8 +29,10 @@ public class AmplifyTest extends CardTestPlayerBase { setChoice(playerA, "Yes"); addTarget(playerA, "Kilnmouth Dragon"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Kilnmouth Dragon", 1); assertPowerToughness(playerA, "Kilnmouth Dragon", 8,8); // 5 + 3 from Amplify @@ -38,7 +41,7 @@ public class AmplifyTest extends CardTestPlayerBase { * Tests if +1/+1 counters are added */ @Test - public void testAmplifyTwoCards() { + public void test_Amplify_TwoCards() { // Creature — Dragon - Dragon 5/5 {5}{R}{R} // Amplify 3 (As this creature enters the battlefield, put three +1/+1 counters on it for each Dragon card you reveal in your hand.) // {T}: Kilnmouth Dragon deals damage equal to the number of +1/+1 counters on it to any target @@ -50,8 +53,10 @@ public class AmplifyTest extends CardTestPlayerBase { setChoice(playerA, "Yes"); addTarget(playerA, "Kilnmouth Dragon^Phantasmal Dragon"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Kilnmouth Dragon", 1); assertPowerToughness(playerA, "Kilnmouth Dragon", 11,11); // 5 + 6 from Amplify @@ -60,7 +65,7 @@ public class AmplifyTest extends CardTestPlayerBase { * Tests that it works for Clone */ @Test - public void testAmplifyWithClone() { + public void test_Amplify_WithClone() { // Creature — Dragon - Dragon 5/5 {5}{R}{R} // Amplify 3 (As this creature enters the battlefield, put three +1/+1 counters on it for each Dragon card you reveal in your hand.) // {T}: Kilnmouth Dragon deals damage equal to the number of +1/+1 counters on it to any target @@ -76,12 +81,15 @@ public class AmplifyTest extends CardTestPlayerBase { addTarget(playerA, "Kilnmouth Dragon"); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Clone"); - setChoice(playerB, "Kilnmouth Dragon"); - setChoice(playerB, "Yes"); - addTarget(playerB, "Phantasmal Dragon"); + setChoice(playerB, "Yes"); // use clone + setChoice(playerB, "Kilnmouth Dragon"); // what clone + setChoice(playerB, "Yes"); // use amplify + addTarget(playerB, "Phantasmal Dragon"); // reveal + setStrictChooseMode(true); setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Kilnmouth Dragon", 1); assertPowerToughness(playerA, "Kilnmouth Dragon", 8,8); // 5 + 3 from Amplify @@ -90,4 +98,35 @@ public class AmplifyTest extends CardTestPlayerBase { assertPowerToughness(playerB, "Kilnmouth Dragon", 8,8); // 5 + 3 from Amplify } + /** + * Tests if a creature with Amplify is able to select itself if it's put + * onto the battlefield from hand (without casting). + * https://github.com/magefree/mage/issues/6776 + */ + @Test + public void test_Amplify_PutOntoBattlefieldFromHand() { + // Creature — Dragon - Dragon 5/5 {5}{R}{R} + // Amplify 3 (As this creature enters the battlefield, put three +1/+1 counters on it for each Dragon card you reveal in your hand.) + // {T}: Kilnmouth Dragon deals damage equal to the number of +1/+1 counters on it to any target + addCard(Zone.HAND, playerA, "Kilnmouth Dragon", 1); + // + // {4}{R} + // You may put a creature card from your hand onto the battlefield. That creature gains haste. Sacrifice that creature at the beginning of the next end step. + addCard(Zone.HAND, playerA, "Through the Breach", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Through the Breach"); + setChoice(playerA, "Yes"); // Put a creature into play with Through the Breach? + setChoice(playerA, "Kilnmouth Dragon"); + //setChoice(playerA, "Yes"); // no reveal request cause no cards to show + //addTarget(playerA, "Kilnmouth Dragon"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Kilnmouth Dragon", 1); + assertPowerToughness(playerA, "Kilnmouth Dragon", 5,5); // 5 + 0 from Amplify + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AssistTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AssistTest.java new file mode 100644 index 0000000000..7210f12e69 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AssistTest.java @@ -0,0 +1,138 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * @author JayDi85 + */ + +public class AssistTest extends CardTestPlayerBaseWithAIHelps { + + @Test + public void test_Playable_NoMana_NoAssist() { + // {7}{G} + // Assist (Another player can pay up to {7} of this spell's cost.) + addCard(Zone.HAND, playerA, "Charging Binox", 1); + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Charging Binox", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Playable_Mana_NoAssist() { + // {7}{G} + // Assist (Another player can pay up to {7} of this spell's cost.) + addCard(Zone.HAND, playerA, "Charging Binox", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Charging Binox", true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Playable_NoMana_Assist() { + // {7}{G} + // Assist (Another player can pay up to {7} of this spell's cost.) + addCard(Zone.HAND, playerA, "Charging Binox", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); // assist pay + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Charging Binox", true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Playable_Mana_Assist() { + // {7}{G} + // Assist (Another player can pay up to {7} of this spell's cost.) + addCard(Zone.HAND, playerA, "Charging Binox", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); // assist pay + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Charging Binox", true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Playable_ManaPartly_AssistPartly() { + // {7}{G} + // Assist (Another player can pay up to {7} of this spell's cost.) + addCard(Zone.HAND, playerA, "Charging Binox", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); // assist pay + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Charging Binox", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_PlayAssist_Manual() { + // {7}{G} + // Assist (Another player can pay up to {7} of this spell's cost.) + addCard(Zone.HAND, playerA, "Charging Binox", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8 - 2); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); // assist pay + + // disabled auto-payment and prepare mana pool to control payment + disableManaAutoPayment(playerA); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 6); + // cast and assist + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Charging Binox"); + setChoice(playerA, "Green", 6); // normal pay x6 + setChoice(playerA, "Assist"); // activate assist + addTarget(playerA, playerB); // player to assist + setChoice(playerB, "X=2"); // can pay (auto-pay from B to A's mana pool as colorless x2) + setChoice(playerA, "Colorless", 2 - 1); // assist pay x2, but 1 mana was unlocked in assist code (wtf) + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Charging Binox", 1); + } + + @Test + public void test_PlayAssist_AI_MustIgnoreAssist() { + // {7}{G} + // Assist (Another player can pay up to {7} of this spell's cost.) + addCard(Zone.HAND, playerA, "Charging Binox", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8 - 2); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); // assist pay + + // AI must ignore assist + checkPlayableAbility("playable", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Charging Binox", true); + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, "Charging Binox", 1); + assertTappedCount("Forest", false, 8); // no mana used + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java index 5427cb8460..9404a4dbfa 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java @@ -11,7 +11,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class BestowTest extends CardTestPlayerBase { @@ -20,7 +19,6 @@ public class BestowTest extends CardTestPlayerBase { * Tests that if from bestow permanent targeted creature gets protection * from the color of the bestow permanent, the bestow permanent becomes a * creature on the battlefield. - * */ /* Silent Artisan @@ -157,7 +155,7 @@ public class BestowTest extends CardTestPlayerBase { * // Away casting both sides, will the creature that has bestow come in * time for it to be sacrificed or does it fully resolve before the creature * comes in? - * + *

* Bestowed creature can be used to sacrifice a creature for the Away part. * http://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/513828-bestow-far-away */ @@ -188,12 +186,15 @@ public class BestowTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nyxborn Rollicker using bestow", "Cyclops of One-Eyed Pass"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "fused Far // Away", "Cyclops of One-Eyed Pass"); - addTarget(playerB, playerA); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "fused Far // Away"); + addTarget(playerB, "Cyclops of One-Eyed Pass"); // Far + addTarget(playerB, playerA); // Away addTarget(playerA, "Nyxborn Rollicker"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertHandCount(playerA, "Cyclops of One-Eyed Pass", 1); assertHandCount(playerB, 0); @@ -246,8 +247,6 @@ public class BestowTest extends CardTestPlayerBase { } /** - * - * * */ @Test @@ -463,4 +462,26 @@ public class BestowTest extends CardTestPlayerBase { Assert.assertFalse("The unattached Nighthowler may not have the aura subtype.", nighthowler.getSubtype(currentGame).contains(SubType.AURA)); } + @Test + public void testCastBestowWithCostReduction() { + // Enchantment Creature — Horror + // Bestow {5}{G} (If you cast this card for its bestow cost, it's an Aura spell with enchant creature. It becomes a creature again if it's not attached to a creature.) + // Trample + // Enchanted creature gets +3/+3 and has trample. + addCard(Zone.HAND, playerA, "Nylea's Emissary"); // Enchantment Creature + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // {1}{W} 2/2 creature + // Aura spells you cast cost {1} less to cast. + addCard(Zone.BATTLEFIELD, playerA, "Transcendent Envoy", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nylea's Emissary using bestow", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Nylea's Emissary", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 5, 5); + assertType("Nylea's Emissary", CardType.CREATURE, false); + assertType("Nylea's Emissary", CardType.ENCHANTMENT, SubType.AURA); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java new file mode 100644 index 0000000000..c83bc8fa8d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java @@ -0,0 +1,131 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BolsterTest extends CardTestPlayerBase { + + /** + * Tests keyword bolster + * + * 701.32. Bolster
+ * 701.32a "Bolster N" means “Choose a creature you control with the least + * toughness or tied for least toughness among creatures you control. Put N + * +1/+1 counters on that creature.” + */ + @Test + public void Basic1Test() { + // When Elite Scaleguard enters the battlefield, bolster 2. + // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls. + addCard(Zone.HAND, playerA, "Elite Scaleguard"); // Creature 2/3 {4}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Scaleguard"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertCounterCount("Elite Scaleguard", CounterType.P1P1, 2); + assertPowerToughness(playerA, "Elite Scaleguard", 4, 5); + } + + @Test + public void Basic2Test() { + setStrictChooseMode(true); + // When Elite Scaleguard enters the battlefield, bolster 2. + // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls. + addCard(Zone.HAND, playerA, "Elite Scaleguard"); // Creature 2/3 {4}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Scaleguard"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Elite Scaleguard", 2, 3); + assertCounterCount("Silvercoat Lion", CounterType.P1P1, 2); + assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); + } + + @Test + public void EliteScaleguardTriggerTest() { + // When Elite Scaleguard enters the battlefield, bolster 2. + // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls. + addCard(Zone.HAND, playerA, "Elite Scaleguard"); // Creature 2/3 {4}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Scaleguard"); + + attack(3, playerA, "Silvercoat Lion"); + addTarget(playerA, "Pillarfield Ox"); // Tap from triggered ability of Elite Scaleguard + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Elite Scaleguard", 2, 3); + assertCounterCount("Silvercoat Lion", CounterType.P1P1, 2); + assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); + assertTapped("Silvercoat Lion", true); + + assertPermanentCount(playerB, "Pillarfield Ox", 1); + assertTapped("Pillarfield Ox", true); + + assertLife(playerB, 16); + } + + + @Test + public void EliteScaleguardTriggerTwiceTest() { + // When Elite Scaleguard enters the battlefield, bolster 2. + // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls. + addCard(Zone.HAND, playerA, "Elite Scaleguard"); // Creature 2/3 {4}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Scaleguard"); + + attack(3, playerA, "Silvercoat Lion"); + addTarget(playerA, "Pillarfield Ox"); // Tap from triggered ability of Elite Scaleguard + + attack(5, playerA, "Silvercoat Lion"); + addTarget(playerA, "Pillarfield Ox"); // Tap from triggered ability of Elite Scaleguard + + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Elite Scaleguard", 2, 3); + assertCounterCount("Silvercoat Lion", CounterType.P1P1, 2); + assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); + assertTapped("Silvercoat Lion", true); + + assertPermanentCount(playerB, "Pillarfield Ox", 1); + assertTapped("Pillarfield Ox", true); + + assertLife(playerB, 12); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/ConvergeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvergeTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/ConvergeTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvergeTest.java index 5d79565a58..205da4bd24 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/ConvergeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvergeTest.java @@ -1,5 +1,4 @@ - -package org.mage.test.cards.abilities.abilitywords; +package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -7,7 +6,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class ConvergeTest extends CardTestPlayerBase { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java index b96e9efabc..ffccd56353 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java @@ -1,145 +1,370 @@ - - package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; -import mage.game.permanent.Permanent; -import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; /** - * - * @author LevelX2 + * @author JayDi85 */ -public class ConvokeTest extends CardTestPlayerBase { - - /* - Test are set to Ignore because the new way to handle this alternate mana payment methods - are not supported yet from AI and getPlayable logic. - */ +public class ConvokeTest extends CardTestPlayerBaseWithAIHelps { @Test - @Ignore - public void testConvokeTwoCreatures() { - /** - * Ephemeral Shields {1}{W} - * Instant - * Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.) - * Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.) - */ + public void test_Playable_NoMana_NoConvoke() { + // {2}{R}{R} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.) + // Stoke the Flames deals 4 damage to any target. + addCard(Zone.HAND, playerA, "Stoke the Flames", 1); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); // must be added because getPlayable does not take Convoke into account - + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Playable_Mana_NoConvoke() { + // {2}{R}{R} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.) + // Stoke the Flames deals 4 damage to any target. + addCard(Zone.HAND, playerA, "Stoke the Flames", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Playable_NoMana_Convoke() { + // {2}{R}{R} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.) + // Stoke the Flames deals 4 damage to any target. + addCard(Zone.HAND, playerA, "Stoke the Flames", 1); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 4); // convoke pay + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Playable_Mana_Convoke() { + // {2}{R}{R} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.) + // Stoke the Flames deals 4 damage to any target. + addCard(Zone.HAND, playerA, "Stoke the Flames", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Playable_ManaPartly_ConvokePartly() { + // {2}{R}{R} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.) + // Stoke the Flames deals 4 damage to any target. + addCard(Zone.HAND, playerA, "Stoke the Flames", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 - 1); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2 - 1); // convoke pay + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_PlayConvoke_Manual() { + // {2}{R}{R} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.) + // Stoke the Flames deals 4 damage to any target. + addCard(Zone.HAND, playerA, "Stoke the Flames", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay + + // use special action to pay (need disabled auto-payment and prepared mana pool) + disableManaAutoPayment(playerA); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stoke the Flames", playerB); + setChoice(playerA, "Red"); // pay 1 + setChoice(playerA, "Red"); // pay 2 + setChoice(playerA, "Convoke"); + addTarget(playerA, "Goblin Racketeer"); // pay 3 as convoke + setChoice(playerA, "Convoke"); + addTarget(playerA, "Goblin Racketeer"); // pay 4 as convoke + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 4); + } + + @Test + public void test_PlayConvoke_AI_AutoPay() { + // {2}{R}{R} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.) + // Stoke the Flames deals 4 damage to any target. + addCard(Zone.HAND, playerA, "Stoke the Flames", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay + + // AI must use special actions to pay as convoke + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stoke the Flames", playerB); + + //setStrictChooseMode(true); AI must choose targets + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 4); + } + + @Test + public void test_PlayConvoke_AI_AutoPayAsConvoke() { + // {2}{R}{R} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.) + // Stoke the Flames deals 4 damage to any target. + addCard(Zone.HAND, playerA, "Stoke the Flames", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay + + // AI must use special actions to pay as convoke + // Current version uses special mana pay as last, after no normal mana available (it can be changed in the future, see playManaHandling) + // e.g. it must tap lands 2 times and convoke 2 times + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stoke the Flames", playerB); + addTarget(playerA, "Goblin Racketeer"); // pay 1 as convoke + addTarget(playerA, "Goblin Racketeer"); // pay 2 as convoke + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 4); + } + + @Test + public void test_PlayConvoke_AI_FullPlay() { + // {2}{R}{R} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.) + // Stoke the Flames deals 4 damage to any target. + addCard(Zone.HAND, playerA, "Stoke the Flames", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay + + // AI must use special actions to pay as convoke and play card + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 4); + } + + @Test + public void test_Other_ConvokeTwoCreatures() { + // {1}{W} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.) + // Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.) + addCard(Zone.HAND, playerA, "Ephemeral Shields"); addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); addCard(Zone.BATTLEFIELD, playerA, "Oreskos Swiftclaw", 1); - addCard(Zone.HAND, playerA, "Ephemeral Shields"); - - addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); - addCard(Zone.HAND, playerB, "Lightning Bolt"); - - + // AI automaticly use convoke to pay castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemeral Shields", "Silvercoat Lion"); - setChoice(playerA, "Yes"); - addTarget(playerA, "Silvercoat Lion^Oreskos Swiftclaw"); + addTarget(playerA, "Silvercoat Lion"); // pay 1 as convoke + addTarget(playerA, "Oreskos Swiftclaw"); // pay 2 as convoke - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); - assertGraveyardCount(playerB, "Lightning Bolt", 1); - assertGraveyardCount(playerA, "Ephemeral Shields", 1); - assertPermanentCount(playerA, "Silvercoat Lion", 1); // was indestructible + assertPermanentCount(playerA, "Silvercoat Lion", 1); assertPermanentCount(playerA, "Oreskos Swiftclaw", 1); - - for (Permanent permanent: currentGame.getBattlefield().getAllActivePermanents(new FilterLandPermanent(), playerA.getId(), currentGame)) { - Assert.assertTrue(permanent.getName() + " may not be tapped", !permanent.isTapped()); - } } @Test - @Ignore - public void testConvokeTwoCreaturesOneWithProtection() { - /** - * Ephemeral Shields {1}{W} - * Instant - * Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.) - * Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.) - */ - - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); // must be added because getPlayable does not take Convoke into account - + public void test_Other_ConvokeProtection() { + // {1}{W} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.) + // Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.) + addCard(Zone.HAND, playerA, "Ephemeral Shields"); addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + // Protection from white addCard(Zone.BATTLEFIELD, playerA, "Black Knight", 1); - addCard(Zone.HAND, playerA, "Ephemeral Shields"); - - addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); - addCard(Zone.HAND, playerB, "Lightning Bolt"); - - + // convoke must be able to target card with protection (it's no target) castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemeral Shields", "Silvercoat Lion"); - setChoice(playerA, "Yes"); - addTarget(playerA, "Silvercoat Lion^Black Knight"); + addTarget(playerA, "Silvercoat Lion"); + addTarget(playerA, "Black Knight"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); - - assertGraveyardCount(playerB, "Lightning Bolt", 1); - - assertGraveyardCount(playerA, "Ephemeral Shields", 1); - assertPermanentCount(playerA, "Silvercoat Lion", 1); // was indestructible - assertPermanentCount(playerA, "Black Knight", 1); - assertTapped("Silvercoat Lion", true); - assertTapped("Black Knight", true); - - for (Permanent permanent: currentGame.getBattlefield().getAllActivePermanents(new FilterLandPermanent(), playerA.getId(), currentGame)) { - Assert.assertTrue(permanent.getName() + " may not be tapped", !permanent.isTapped()); - } } @Test @Ignore - public void testConvokeFromChiefEngineer() { - /** - * Chief Engineer {1}{U} - * Creature - Vedalken, Artificer - * Artifact spells you cast have convoke. - */ - - // THIS TEST IS NOT FINISHED - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); // creatures to use for convoek + // TODO: fix gain ability for spells to apply in all zones instead stack only or change getPlayable to look ahead and simulate spell on stack (wtf) + public void test_Other_ConvokeAsGains() { + // {1}{U} + // Artifact spells you cast have convoke. addCard(Zone.BATTLEFIELD, playerA, "Chief Engineer", 1); - - addCard(Zone.HAND, playerA, "ARTIFACT TO CAST", 1); + // + // {2} + addCard(Zone.HAND, playerA, "Alpha Myr", 1); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "ARTIFACT TO CAST"); - setChoice(playerA, "Yes"); + // Chief Engineer gives convoke to Alpha Myr and xmage must see it as playable before put real spell to stack + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpha Myr"); + addTarget(playerA, "Silvercoat Lion"); addTarget(playerA, "Silvercoat Lion"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); - assertLife(playerA, 20); - assertLife(playerB, 20); - - + assertPermanentCount(playerA, "Alpha Myr", 1); } + @Test + @Ignore + // I don't know how to test it by framework - manual test works fine for HumanPlayer + // (he get warning message and can't activate mana abilities after convoke) + public void test_Other_CantUseConvokeBeforeManaAbilities() { + // https://github.com/magefree/mage/issues/768 + + // {6} + // Convoke + addCard(Zone.HAND, playerA, "Will-Forged Golem", 1); + // + // {2}{G} + // Create two 1/1 colorless Eldrazi Scion creature tokens. They have “Sacrifice this creature: Add {C}.” + addCard(Zone.HAND, playerA, "Call the Scions", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3 * 2); + + // prepare scions + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Call the Scions"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Call the Scions"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPermanentCount("scions", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Eldrazi Scion", 4); + + // test case 1 - playable abilities must not show it as playable (not work, cause we don't known real payment order before payment) + //checkPlayableAbility("can't use convoke", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Will-Forged Golem", false); + + // test case 2 - it's in playable list, but mana abilities can't be activated after convoke pay + //castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Will-Forged Golem"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Other_CastFromGraveayrd_Convoke() { + // https://github.com/magefree/mage/issues/6680 + + // {5}{B/G}{B/G} + // You can't spend mana to cast this spell. + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.) + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + // You may cast Hogaak, Arisen Necropolis from your graveyard. + addCard(Zone.GRAVEYARD, playerA, "Hogaak, Arisen Necropolis", 1); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 7); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hogaak, Arisen Necropolis"); + addTarget(playerA, "Balduvian Bears", 7); // convoke pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Hogaak, Arisen Necropolis", 1); + } + + @Test + public void test_Other_CastFromGraveayrd_ConvokeAndDelve() { + // https://github.com/magefree/mage/issues/6680 + + // {5}{B/G}{B/G} + // You can't spend mana to cast this spell. + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.) + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + // You may cast Hogaak, Arisen Necropolis from your graveyard. + addCard(Zone.GRAVEYARD, playerA, "Hogaak, Arisen Necropolis", 1); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2); // convoke (you can't pay normal mana here) + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 5); // delve + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hogaak, Arisen Necropolis"); + addTarget(playerA, "Balduvian Bears", 2); // convoke pay + setChoice(playerA, "Balduvian Bears", 5); // delve pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Hogaak, Arisen Necropolis", 1); + } + + @Test + public void test_Mana_MemoryOverflow() { + // possible bug: convoke mana calculation can overflow server's memory (too much mana options from too much permanents) + // https://github.com/magefree/mage/issues/6938 + + // Create X 1/1 white Soldier creature tokens with lifelink. + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.) + addCard(Zone.HAND, playerA, "March of the Multitudes", 1); // {X}{G}{W}{W} + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 500); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "March of the Multitudes"); + setChoice(playerA, "X=1"); + addTarget(playerA, "Grizzly Bears"); // convoke pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Soldier", 1); + } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CrewTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CrewTest.java index 2dabdd22fb..06a8fdda30 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CrewTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CrewTest.java @@ -1,82 +1,82 @@ - -package org.mage.test.cards.abilities.keywords; - -import mage.abilities.keyword.HasteAbility; -import mage.constants.CardType; -import mage.constants.PhaseStep; -import mage.constants.SubType; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -// http://magic.wizards.com/en/articles/archive/feature/kaladesh-mechanics-2016-09-02 -public class CrewTest extends CardTestPlayerBase { - - @Test - public void crewBasicTest() { - // {T}: Add one mana of any color. - // Crew 3 (Tap any number of creatures you control with total power 3 or more: This Vehicle becomes an artifact creature until end of turn.)"; - addCard(Zone.BATTLEFIELD, playerA, "Cultivator's Caravan", 1); - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 3"); - setChoice(playerA, "Silvercoat Lion^Silvercoat Lion"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertTappedCount("Silvercoat Lion", true, 2); - assertPowerToughness(playerA, "Cultivator's Caravan", 5, 5); - assertType("Cultivator's Caravan", CardType.CREATURE, SubType.VEHICLE); - } - - @Test - public void crewTriggerPilotTest() { - // Flying - // Whenever Smuggler's Copter attacks or blocks, you may draw a card. If you do, discard a card. - // Crew 1 (Tap any number of creatures you control with total power 3 or more: This Vehicle becomes an artifact creature until end of turn.)"; - addCard(Zone.BATTLEFIELD, playerA, "Smuggler's Copter", 1); - - addCard(Zone.BATTLEFIELD, playerA, "Speedway Fanatic", 1); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 1"); - setChoice(playerA, "Speedway Fanatic"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertTappedCount("Speedway Fanatic", true, 1); - assertPowerToughness(playerA, "Smuggler's Copter", 3, 3); - assertAbility(playerA, "Smuggler's Copter", HasteAbility.getInstance(), true); - assertType("Smuggler's Copter", CardType.CREATURE, SubType.VEHICLE); - } - - @Test - public void testThatBouncingACrewedVehicleWillUncrewIt() { - addCard(Zone.BATTLEFIELD, playerA, "Smuggler's Copter", 1); - addCard(Zone.BATTLEFIELD, playerA, "Speedway Fanatic", 1); - addCard(Zone.BATTLEFIELD, playerA, "Island", 7); - addCard(Zone.HAND, playerA, "Evacuation", 1); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 1"); - setChoice(playerA, "Speedway Fanatic"); - - // Return all creatures to there owners hands - castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Evacuation"); - - // (Re)Cast Smugglers Copter - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Smuggler's Copter"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - // Only crewed vehicles have card type creature - assertNotType("Smuggler's Copter", CardType.CREATURE); - } - -} + +package org.mage.test.cards.abilities.keywords; + +import mage.abilities.keyword.HasteAbility; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +// http://magic.wizards.com/en/articles/archive/feature/kaladesh-mechanics-2016-09-02 +public class CrewTest extends CardTestPlayerBase { + + @Test + public void crewBasicTest() { + // {T}: Add one mana of any color. + // Crew 3 (Tap any number of creatures you control with total power 3 or more: This Vehicle becomes an artifact creature until end of turn.)"; + addCard(Zone.BATTLEFIELD, playerA, "Cultivator's Caravan", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 3"); + setChoice(playerA, "Silvercoat Lion^Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertTappedCount("Silvercoat Lion", true, 2); + assertPowerToughness(playerA, "Cultivator's Caravan", 5, 5); + assertType("Cultivator's Caravan", CardType.CREATURE, SubType.VEHICLE); + } + + @Test + public void crewTriggerPilotTest() { + // Flying + // Whenever Smuggler's Copter attacks or blocks, you may draw a card. If you do, discard a card. + // Crew 1 (Tap any number of creatures you control with total power 3 or more: This Vehicle becomes an artifact creature until end of turn.)"; + addCard(Zone.BATTLEFIELD, playerA, "Smuggler's Copter", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Speedway Fanatic", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 1"); + setChoice(playerA, "Speedway Fanatic"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertTappedCount("Speedway Fanatic", true, 1); + assertPowerToughness(playerA, "Smuggler's Copter", 3, 3); + assertAbility(playerA, "Smuggler's Copter", HasteAbility.getInstance(), true); + assertType("Smuggler's Copter", CardType.CREATURE, SubType.VEHICLE); + } + + @Test + public void testThatBouncingACrewedVehicleWillUncrewIt() { + addCard(Zone.BATTLEFIELD, playerA, "Smuggler's Copter", 1); + addCard(Zone.BATTLEFIELD, playerA, "Speedway Fanatic", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + addCard(Zone.HAND, playerA, "Evacuation", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 1"); + setChoice(playerA, "Speedway Fanatic"); + + // Return all creatures to there owners hands + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Evacuation"); + + // (Re)Cast Smugglers Copter + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Smuggler's Copter"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + // Only crewed vehicles have card type creature + assertNotType("Smuggler's Copter", CardType.CREATURE); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DeliriumTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DeliriumTest.java new file mode 100644 index 0000000000..09485c1c5a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DeliriumTest.java @@ -0,0 +1,167 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * + * @author LevelX2 + */ +public class DeliriumTest extends CardTestPlayerBaseWithAIHelps { + + // Delirium - if there are four or more card types among cards in your graveyard + @Test + public void noDeliriumTest() { + /** + * 4/8/2016 If you have three non-sorcery card types among cards in your + * graveyard at the time Descend upon the Sinful resolves, you won’t get + * an Angel token. Descend upon the Sinful isn’t put into your graveyard + * until after it’s finished resolving. + */ + + // Exile all creatures + // Delirium - Create a 4/4 white Angel creature token with flying if there are four or more card types among cards in your graveyard. + addCard(Zone.HAND, playerA, "Descend upon the Sinful", 1); // Sorcery {4}{W}{W} + + addCard(Zone.GRAVEYARD, playerA, "Thought Vessel", 2); // Artifact + addCard(Zone.GRAVEYARD, playerA, "Tempered Steel", 2); // Enchantment + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 2); // Creature + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Descend upon the Sinful"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerA, "Balduvian Bears", 4); + assertGraveyardCount(playerA, "Descend upon the Sinful", 1); + + assertPermanentCount(playerA, "Angel", 0); + + } + + @Test + public void ItsDeliriumTest() { + /** + * 4/8/2016 The number of card types matters, not the number of cards. + * For example, Wicker Witch (an artifact creature) along with Catalog + * (an instant) and Chaplain’s Blessing (a sorcery) will enable + * delirium. + */ + // Exile all creatures + // Delirium - Create a 4/4 white Angel creature token with flying if there are four or more card types among cards in your graveyard. + addCard(Zone.HAND, playerA, "Descend upon the Sinful", 1); // Sorcery {4}{W}{W} + + addCard(Zone.GRAVEYARD, playerA, "Wicker Witch", 1); // Artifact Creature + addCard(Zone.GRAVEYARD, playerA, "Catalog", 1); // Instant + addCard(Zone.GRAVEYARD, playerA, "Chaplain's Blessing", 1); // Sorcery + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Descend upon the Sinful"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerA, "Balduvian Bears", 4); + assertGraveyardCount(playerA, "Descend upon the Sinful", 1); + + assertPermanentCount(playerA, "Angel", 1); + + } + + @Test + public void noDeliriumWithCreatureEnchantmentTest() { + + /** + * 4/8/2016 If one of those creatures was enchanted, its Aura won’t be + * put into a player’s graveyard until after Descend upon the Sinful has + * finished resolving. If the controller of Descend upon the Sinful + * owned the Aura, it won’t be in the graveyard in time to be counted + * for the delirium ability. + */ + // Exile all creatures + // Delirium - Create a 4/4 white Angel creature token with flying if there are four or more card types among cards in your graveyard. + addCard(Zone.HAND, playerA, "Descend upon the Sinful", 1); // Sorcery {4}{W}{W} + + // Enchant creature + // Enchanted creature gets +1/+1 and has protection from creatures. + addCard(Zone.HAND, playerA, "Spirit Mantle", 1); // Enchantment Aura {1}{W} + + addCard(Zone.GRAVEYARD, playerA, "Thought Vessel", 2); // Artifact + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 2); // Creature + addCard(Zone.GRAVEYARD, playerA, "Plains", 2); // Land + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 8); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spirit Mantle", "Balduvian Bears"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Descend upon the Sinful"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 0); + assertExileCount(playerA, "Balduvian Bears", 4); + assertGraveyardCount(playerA, "Spirit Mantle", 1); + assertGraveyardCount(playerA, "Descend upon the Sinful", 1); + + assertPermanentCount(playerA, "Angel", 0); + + } + + @Test + public void noDeliriumWithDoubleFacedCardsTest() { + + /** + * 4/8/2016 Because you consider only the characteristics of a + * double-faced card’s front face while it’s not on the battlefield, the + * types of its back face won’t be counted for delirium. + */ + // Exile all creatures + // Delirium - Create a 4/4 white Angel creature token with flying if there are four or more card types among cards in your graveyard. + addCard(Zone.HAND, playerA, "Descend upon the Sinful", 1); // Sorcery {4}{W}{W} + + addCard(Zone.GRAVEYARD, playerA, "Elbrus, the Binding Blade", 1); // Artifact (not counted as Creature from Backside) + addCard(Zone.GRAVEYARD, playerA, "Catalog", 1); // Instant + addCard(Zone.GRAVEYARD, playerA, "Plains", 1); // Land + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Descend upon the Sinful"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 0); + assertExileCount(playerA, "Balduvian Bears", 4); + assertGraveyardCount(playerA, "Descend upon the Sinful", 1); + + assertPermanentCount(playerA, "Angel", 0); + + } + + /** + * 4/8/2016 In some rare cases, you can have a token or a copy of a spell in + * your graveyard at the moment that an object’s delirium ability counts the + * card types among cards in your graveyard, before that token or copy + * ceases to exist. Because tokens and copies of spells are not cards, even + * if they are copies of cards, their types will never be counted. + */ + + // No test added yet +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java new file mode 100644 index 0000000000..854f9c9ada --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java @@ -0,0 +1,121 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * @author JayDi85 + */ + +public class DelveTest extends CardTestPlayerBaseWithAIHelps { + + // no simple playable tests for delve, it's same as ConvokeTest + + @Test + public void test_PlayDelve_Manual() { + // {4}{U}{U} creature + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + addCard(Zone.HAND, playerA, "Ethereal Forager", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 4); // delve pay + + // use special action to pay (need disabled auto-payment and prepared mana pool) + disableManaAutoPayment(playerA); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ethereal Forager"); + setChoice(playerA, "Blue"); // pay 1 + setChoice(playerA, "Blue"); // pay 2 + // delve can be payed in test only by one card + setChoice(playerA, "Exile cards"); + setChoice(playerA, "Balduvian Bears"); // pay 3 as delve + setChoice(playerA, "Exile cards"); + setChoice(playerA, "Balduvian Bears"); // pay 4 as delve + setChoice(playerA, "Exile cards"); + setChoice(playerA, "Balduvian Bears"); // pay 5 as delve + setChoice(playerA, "Exile cards"); + setChoice(playerA, "Balduvian Bears"); // pay 6 as delve + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Ethereal Forager", 1); + } + + @Test + public void test_PlayDelve_AI_AutoPay() { + // {4}{U}{U} creature + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + addCard(Zone.HAND, playerA, "Ethereal Forager", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 4); // delve pay + + // AI must use special actions to pay as delve + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ethereal Forager"); + + //setStrictChooseMode(true); AI must choose targets + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Ethereal Forager", 1); + } + + @Test + public void test_PlayDelve_AI_FullPlay() { + // {4}{U}{U} creature + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + addCard(Zone.HAND, playerA, "Ethereal Forager", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 4); // delve pay + + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Ethereal Forager", 1); + } + + @Test + public void test_CheatWithCancel() { + // possible bug: users can start to pay delve special action with nothing (done button) and gets no mana cast + // https://github.com/magefree/mage/issues/6937 + + disableManaAutoPayment(playerA); + removeAllCardsFromHand(playerA); + + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + // Draw 3 Cards + addCard(Zone.HAND, playerA, "Treasure Cruise", 1); // {7}{U} sorcery + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 7); // delve pay + + // use case: + // 1. Use mana from land (fill mana pool) + // 2. Use delve as special action + // 3. Press done without real delve pay + // 4. All generic cost will be removed and card go to stack for {U} instead {7}{U} + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treasure Cruise"); + setChoice(playerA, "Exile cards"); // delve activate (special button in UI) + setChoice(playerA, TestPlayer.CHOICE_SKIP); // devle cost with nothing (done button in UI) + setChoice(playerA, TestPlayer.MANA_CANCEL); // mana payment cancel (cancel button in UI) + setChoice(playerA, TestPlayer.SKIP_FAILED_COMMAND); // delete cast/activate command from queue + + // it uses bug with rollback, so test player will execute it multiple times + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 1); // no resolve, so no draw cards (if rollback bug active then it shows 3 cards) + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java index 6126e65dd7..487aeb0ba3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java @@ -120,4 +120,45 @@ public class DiscardTest extends CardTestPlayerBase { assertHandCount(playerB, "Driven // Despair", 0); } + + + /** + * Test a discard after selecting the cards from another player + */ + @Test + public void GruesomeDiscoveryTest(){ + // Target player discards two cards. + // Morbid - If a creature died this turn, instead that player reveals their hand, + // you choose two cards from it, then that player discards those cards. + addCard(Zone.HAND, playerA, "Gruesome Discovery", 1); // Sorcery {2}{B}{B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + addCard(Zone.HAND, playerB, "Silvercoat Lion"); + addCard(Zone.HAND, playerB, "Aluren"); + addCard(Zone.HAND, playerB, "Contagion"); + + attack(3, playerA, "Silvercoat Lion"); + block(3, playerB, "Silvercoat Lion", "Silvercoat Lion"); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gruesome Discovery", playerB); + + setChoice(playerA, "Aluren^Contagion"); + + // addTarget(playerA, "Aluren"); + // addTarget(playerA, "Contagion"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + assertGraveyardCount(playerA, "Gruesome Discovery", 1); + + assertGraveyardCount(playerB, "Aluren", 1); + assertGraveyardCount(playerB, "Contagion", 1); + assertGraveyardCount(playerB, 3); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/DomainTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DomainTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/DomainTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DomainTest.java index 5a79581708..81513456f3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/DomainTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DomainTest.java @@ -1,5 +1,4 @@ - -package org.mage.test.cards.abilities.abilitywords; +package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -7,7 +6,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class DomainTest extends CardTestPlayerBase { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EmergeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EmergeTest.java index 3e9cead7b5..b2f71f7e17 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EmergeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EmergeTest.java @@ -1,40 +1,40 @@ - -package org.mage.test.cards.abilities.keywords; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class EmergeTest extends CardTestPlayerBase { - - /** - * Wretched Gryff is bugged. I could not use its Emerge ability. Clicking on - * the card did not give me the interaction menu. - */ - @Test - public void testCastWithEmerge() { - - addCard(Zone.BATTLEFIELD, playerA, "Island", 4); - // Emerge {5}{U} (You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost.) - // When you cast Wretched Gryff, draw a card. - // Flying - addCard(Zone.HAND, playerA, "Wretched Gryff"); // Creature - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wretched Gryff with emerge"); - setChoice(playerA, "Silvercoat Lion"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Silvercoat Lion", 1); - assertPermanentCount(playerA, "Wretched Gryff", 1); - } - -} + +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class EmergeTest extends CardTestPlayerBase { + + /** + * Wretched Gryff is bugged. I could not use its Emerge ability. Clicking on + * the card did not give me the interaction menu. + */ + @Test + public void testCastWithEmerge() { + + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + // Emerge {5}{U} (You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost.) + // When you cast Wretched Gryff, draw a card. + // Flying + addCard(Zone.HAND, playerA, "Wretched Gryff"); // Creature + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wretched Gryff with emerge"); + setChoice(playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerA, "Wretched Gryff", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EscalateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EscalateTest.java index 6d70d44787..02138f1497 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EscalateTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EscalateTest.java @@ -1,134 +1,134 @@ - -package org.mage.test.cards.abilities.keywords; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Ignore; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class EscalateTest extends CardTestPlayerBase { - - @Test - public void testUseOneMode() { - - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - // Escalate {1} (Pay this cost for each mode chosen beyond the first.) - // Choose one or more — - // Creatures target player controls gain trample until end of turn. - // Savage Alliance deals 2 damage to target creature; - // Savage Alliance deals 1 damage to each creature target opponent controls. - addCard(Zone.HAND, playerA, "Savage Alliance"); // Instant {2}{R} - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Savage Alliance", "mode=2Silvercoat Lion"); - setModeChoice(playerA, "2"); - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertGraveyardCount(playerB, "Silvercoat Lion", 1); - assertGraveyardCount(playerA, "Savage Alliance", 1); - } - - @Test - public void testGaddockTeegInteraction_ThreeCMC_OneMode() { - - // Noncreature spells with converted mana cost {4} or greater can't be cast. - // Noncreature spells with {X} in their mana costs can't be cast. - addCard(Zone.BATTLEFIELD, playerB, "Gaddock Teeg"); // {W}{G} 2/2 Legendary Kithkin - - // Escalate {1} (Pay this cost for each mode chosen beyond the first.) - // Choose one or more — - // * Target player discards all the cards in their hand, then draws that many cards. - // * Collective Defiance deals 4 damage to target creature. - // * Collective Defiance deals 3 damage to target opponent. - addCard(Zone.HAND, playerA, "Collective Defiance"); // {1}{R}{R} sorcery - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Collective Defiance", playerB); - setModeChoice(playerA, "3"); // deal 3 dmg to opponent - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerB, "Gaddock Teeg", 1); - assertGraveyardCount(playerA, "Collective Defiance", 1); - assertLife(playerB, 17); - } - - // Reported bug: Escalate CMC is not calculated correctly when more than 1 mode chosen - // With 1 extra mode, the casting cost is {2}{R}{R} but the CMC should still be 3 - @Test - public void testGaddockTeegInteraction_ThreeCMC_TwoModes() { - - // Noncreature spells with converted mana cost {4} or greater can't be cast. - // Noncreature spells with {X} in their mana costs can't be cast. - addCard(Zone.BATTLEFIELD, playerB, "Gaddock Teeg"); // {W}{G} 2/2 Legendary Kithkin - - // Escalate {1} (Pay this cost for each mode chosen beyond the first.) - // Choose one or more — - // * Target player discards all the cards in their hand, then draws that many cards. - // * Collective Defiance deals 4 damage to target creature. - // * Collective Defiance deals 3 damage to target opponent. - addCard(Zone.HAND, playerA, "Collective Defiance"); // {1}{R}{R} sorcery - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Collective Defiance", "mode=2Gaddock Teeg"); - setModeChoice(playerA, "2"); // deal 4 dmg to target creature (gaddock teeg) - setModeChoice(playerA, "3"); // deal 3 dmg to opponent - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Collective Defiance", 1); - assertGraveyardCount(playerB, "Gaddock Teeg", 1); - assertLife(playerB, 17); - } - - @Test - public void testSpellQuellerInteraction_ThreeCMC_ThreeModes() { - - // {1}{W}{U} Flash Flying 2/3 Spirit - // When Spell Queller enters the battlefield, exile target spell with converted mana cost 4 or less. - // When Spell Queller leaves the battlefield, the exiled card's owner may cast that card without paying its mana cost. - addCard(Zone.HAND, playerB, "Spell Queller"); - addCard(Zone.BATTLEFIELD, playerB, "Wall of Omens"); // {1}{W} 0/4 - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - addCard(Zone.BATTLEFIELD, playerB, "Plains", 1); - - // Escalate {1} (Pay this cost for each mode chosen beyond the first.) - // Choose one or more — - // * Target player discards all the cards in their hand, then draws that many cards. - // * Collective Defiance deals 4 damage to target creature. - // * Collective Defiance deals 3 damage to target opponent. - addCard(Zone.HAND, playerA, "Collective Defiance"); // {1}{R}{R} sorcery - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Collective Defiance", "mode=2Wall of Omens"); - setModeChoice(playerA, "1"); // opponent discards hand and draws that many - setModeChoice(playerA, "2"); // deal 4 dmg to target creature (Wall of Omens) - setModeChoice(playerA, "3"); // deal 3 dmg to opponent - addTarget(playerA, playerB); // mode 1 - addTarget(playerA, playerB); // mode 3 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Spell Queller"); - addTarget(playerB, "Collective Defiance"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerB, "Spell Queller", 1); - assertHandCount(playerA, "Collective Defiance", 0); - assertExileCount("Collective Defiance", 1); - assertGraveyardCount(playerA, "Collective Defiance", 0); - assertPermanentCount(playerB, "Wall of Omens", 1); - assertLife(playerA, 20); - assertLife(playerB, 20); - } - -} + +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class EscalateTest extends CardTestPlayerBase { + + @Test + public void testUseOneMode() { + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Escalate {1} (Pay this cost for each mode chosen beyond the first.) + // Choose one or more — + // Creatures target player controls gain trample until end of turn. + // Savage Alliance deals 2 damage to target creature; + // Savage Alliance deals 1 damage to each creature target opponent controls. + addCard(Zone.HAND, playerA, "Savage Alliance"); // Instant {2}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Savage Alliance", "mode=2Silvercoat Lion"); + setModeChoice(playerA, "2"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + assertGraveyardCount(playerA, "Savage Alliance", 1); + } + + @Test + public void testGaddockTeegInteraction_ThreeCMC_OneMode() { + + // Noncreature spells with converted mana cost {4} or greater can't be cast. + // Noncreature spells with {X} in their mana costs can't be cast. + addCard(Zone.BATTLEFIELD, playerB, "Gaddock Teeg"); // {W}{G} 2/2 Legendary Kithkin + + // Escalate {1} (Pay this cost for each mode chosen beyond the first.) + // Choose one or more — + // * Target player discards all the cards in their hand, then draws that many cards. + // * Collective Defiance deals 4 damage to target creature. + // * Collective Defiance deals 3 damage to target opponent. + addCard(Zone.HAND, playerA, "Collective Defiance"); // {1}{R}{R} sorcery + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Collective Defiance", playerB); + setModeChoice(playerA, "3"); // deal 3 dmg to opponent + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Gaddock Teeg", 1); + assertGraveyardCount(playerA, "Collective Defiance", 1); + assertLife(playerB, 17); + } + + // Reported bug: Escalate CMC is not calculated correctly when more than 1 mode chosen + // With 1 extra mode, the casting cost is {2}{R}{R} but the CMC should still be 3 + @Test + public void testGaddockTeegInteraction_ThreeCMC_TwoModes() { + + // Noncreature spells with converted mana cost {4} or greater can't be cast. + // Noncreature spells with {X} in their mana costs can't be cast. + addCard(Zone.BATTLEFIELD, playerB, "Gaddock Teeg"); // {W}{G} 2/2 Legendary Kithkin + + // Escalate {1} (Pay this cost for each mode chosen beyond the first.) + // Choose one or more — + // * Target player discards all the cards in their hand, then draws that many cards. + // * Collective Defiance deals 4 damage to target creature. + // * Collective Defiance deals 3 damage to target opponent. + addCard(Zone.HAND, playerA, "Collective Defiance"); // {1}{R}{R} sorcery + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Collective Defiance", "mode=2Gaddock Teeg"); + setModeChoice(playerA, "2"); // deal 4 dmg to target creature (gaddock teeg) + setModeChoice(playerA, "3"); // deal 3 dmg to opponent + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Collective Defiance", 1); + assertGraveyardCount(playerB, "Gaddock Teeg", 1); + assertLife(playerB, 17); + } + + @Test + public void testSpellQuellerInteraction_ThreeCMC_ThreeModes() { + + // {1}{W}{U} Flash Flying 2/3 Spirit + // When Spell Queller enters the battlefield, exile target spell with converted mana cost 4 or less. + // When Spell Queller leaves the battlefield, the exiled card's owner may cast that card without paying its mana cost. + addCard(Zone.HAND, playerB, "Spell Queller"); + addCard(Zone.BATTLEFIELD, playerB, "Wall of Omens"); // {1}{W} 0/4 + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 1); + + // Escalate {1} (Pay this cost for each mode chosen beyond the first.) + // Choose one or more — + // * Target player discards all the cards in their hand, then draws that many cards. + // * Collective Defiance deals 4 damage to target creature. + // * Collective Defiance deals 3 damage to target opponent. + addCard(Zone.HAND, playerA, "Collective Defiance"); // {1}{R}{R} sorcery + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Collective Defiance", "mode=2Wall of Omens"); + setModeChoice(playerA, "1"); // opponent discards hand and draws that many + setModeChoice(playerA, "2"); // deal 4 dmg to target creature (Wall of Omens) + setModeChoice(playerA, "3"); // deal 3 dmg to opponent + addTarget(playerA, playerB); // mode 1 + addTarget(playerA, playerB); // mode 3 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Spell Queller"); + addTarget(playerB, "Collective Defiance"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Spell Queller", 1); + assertHandCount(playerA, "Collective Defiance", 0); + assertExileCount("Collective Defiance", 1); + assertGraveyardCount(playerA, "Collective Defiance", 0); + assertPermanentCount(playerB, "Wall of Omens", 1); + assertLife(playerA, 20); + assertLife(playerB, 20); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvolveTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvolveTest.java index 28ee070968..20f0f861c4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvolveTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvolveTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; @@ -8,7 +7,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class EvolveTest extends CardTestPlayerBase { @@ -166,9 +164,12 @@ public class EvolveTest extends CardTestPlayerBase { addCard(Zone.HAND, playerB, "Sudden Disappearance", 2); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Sudden Disappearance", playerA); + setChoice(playerA, "Evolve"); // two triggers + setStrictChooseMode(true); setStopAt(3, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java new file mode 100644 index 0000000000..02ef1d7332 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java @@ -0,0 +1,120 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.abilities.keywords; + +import java.io.FileNotFoundException; +import mage.constants.MultiplayerAttackOption; +import mage.constants.PhaseStep; +import mage.constants.RangeOfInfluence; +import mage.constants.Zone; +import mage.game.FreeForAll; +import mage.game.Game; +import mage.game.GameException; +import mage.game.mulligan.MulliganType; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestMultiPlayerBase; + +/** + * + * @author LevelX2 + */ +public class GoadTest extends CardTestMultiPlayerBase { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40); + // Player order: A -> D -> C -> B + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + playerC = createPlayer(game, playerC, "PlayerC"); + playerD = createPlayer(game, playerD, "PlayerD"); + return game; + } + + @Test + public void goadWithOwnedCreatureTest() { + // Your opponents can't cast spells during combat. + // Whenever a creature you control deals combat damage to a player, goad each creature that player controls + // (Until your next turn, that creature attacks each combat if able and attacks a player other than you if able.) + addCard(Zone.BATTLEFIELD, playerD, "Marisi, Breaker of the Coil", 1); // Creature 5/4 + + addCard(Zone.BATTLEFIELD, playerC, "Abbey Griffin", 3); // Creature 2/2 + + attack(2, playerD, "Marisi, Breaker of the Coil", playerC); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + Permanent griffinPermanent = getPermanent("Abbey Griffin"); + + Assert.assertFalse("Griffin can attack playerD but should not be able", + griffinPermanent.canAttack(playerD.getId(), currentGame)); + Assert.assertTrue("Griffin can't attack playerA but should be able", + griffinPermanent.canAttack(playerA.getId(), currentGame)); + Assert.assertTrue("Griffin can't attack playerB but should be able", + griffinPermanent.canAttack(playerB.getId(), currentGame)); + + assertLife(playerC, 35); + assertLife(playerD, 40); // player D can not be attacked from C because the creatures are goaded + assertLife(playerA, 40); + assertLife(playerB, 40); + + } + + /** + * In a game of commander, my opponent gained control of Marisi, Breaker of + * Coils (until end of turn) and did combat damage to another player. This + * caused the creatures damaged by Marisi's controller to be goaded. + * However, when the goaded creatures went to attack, they could not attack + * me but could attack the (former) controller of Marisi. + */ + @Test + public void goadWithNotOwnedCreatureTest() { + // Your opponents can't cast spells during combat. + // Whenever a creature you control deals combat damage to a player, goad each creature that player controls + // (Until your next turn, that creature attacks each combat if able and attacks a player other than you if able.) + addCard(Zone.BATTLEFIELD, playerA, "Marisi, Breaker of the Coil", 1); // Creature 5/4 + + // Untap target creature an opponent controls and gain control of it until end of turn. + // That creature gains haste until end of turn. + // When you lose control of the creature, tap it. + addCard(Zone.HAND, playerD, "Ray of Command"); // Instant {3}{U} + addCard(Zone.BATTLEFIELD, playerD, "Island", 4); + + addCard(Zone.BATTLEFIELD, playerC, "Silvercoat Lion", 1); // Creature 2/2 + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Ray of Command", "Marisi, Breaker of the Coil"); + + attack(2, playerD, "Marisi, Breaker of the Coil", playerC); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerD, "Ray of Command", 1); + assertPermanentCount(playerA, "Marisi, Breaker of the Coil", 1); + + Permanent lion = getPermanent("Silvercoat Lion", playerC); + + Assert.assertFalse("Silvercoat lion shouldn't be able to attack player D but can", lion.canAttack(playerD.getId(), currentGame)); + Assert.assertTrue("Silvercoat lion should be able to attack player A but can't", lion.canAttack(playerA.getId(), currentGame)); + Assert.assertTrue("Silvercoat lion should be able to attack player B but can't", lion.canAttack(playerB.getId(), currentGame)); + + assertLife(playerD, 40); + assertLife(playerC, 35); + assertLife(playerA, 40); + assertLife(playerB, 40); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java index 668479559a..c5fc534f91 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java @@ -9,7 +9,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class HexproofTest extends CardTestPlayerBase { @@ -37,6 +36,7 @@ public class HexproofTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Elder of Laurels", 3, 4); assertAbility(playerA, "Elder of Laurels", HexproofAbility.getInstance(), true); } + /** * Tests one target gets hexproof */ @@ -64,4 +64,42 @@ public class HexproofTest extends CardTestPlayerBase { assertAbility(playerA, "Elder of Laurels", HexproofAbility.getInstance(), true); assertPermanentCount(playerA, "Arbor Elf", 0); } + + /** + * Tests hexproof from a color with opponent's spells + */ + @Test + public void testHexproofFromColorOpponentSpells() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + addCard(Zone.HAND, playerA, "Murder", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Knight of Grace"); + addCard(Zone.BATTLEFIELD, playerB, "Knight of Malice"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder", "Knight of Grace"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder", "Knight of Malice"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Knight of Grace", 1); + assertPermanentCount(playerB, "Knight of Malice", 0); + } + + /** + * Tests hexproof from a color with controller's spells + */ + @Test + public void testHexproofFromColorOwnSpells() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, "Knight of Grace"); + addCard(Zone.HAND, playerA, "Murder"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder", "Knight of Grace"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Knight of Grace", 0); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HideawayTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HideawayTest.java index 9855c5d002..feb3feb906 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HideawayTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HideawayTest.java @@ -35,14 +35,20 @@ public class HideawayTest extends CardTestPlayerBase { @Test public void testHideaway() { addCard(Zone.HAND, playerA, "Shelldock Isle"); + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion"); + skipInitShuffling(); this.playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shelldock Isle"); + setChoice(playerA, "Silvercoat Lion"); setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Shelldock Isle", 1); - assertExileCount(playerA, 1); + assertExileCount(playerA, "Silvercoat Lion", 1); for (Card card : currentGame.getExile().getAllCards(currentGame)) { Assert.assertTrue("Exiled card is not face down", card.isFaceDown(currentGame)); } @@ -73,10 +79,15 @@ public class HideawayTest extends CardTestPlayerBase { setChoice(playerA, "Ulamog, the Ceaseless Hunger"); activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{G},"); + setChoice(playerA, "Yes"); // play Ghost Quarter + addTarget(playerA, "Dross Crocodile^Dross Crocodile"); setStopAt(3, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertExileCount("Dross Crocodile", 2); assertPermanentCount(playerA, "Mosswort Bridge", 1); @@ -94,6 +105,9 @@ public class HideawayTest extends CardTestPlayerBase { @Test public void testCannotPlayLandIfPlayedLand() { + // Hideaway (This land enters the battlefield tapped. When it does, look at the top four cards of your library, exile one face down, then put the rest on the bottom of your library.) + // {tap}: Add {W}. + // {W}, {T}: You may play the exiled card without paying its mana cost if you attacked with three or more creatures this turn. addCard(Zone.HAND, playerA, "Windbrisk Heights"); addCard(Zone.HAND, playerA, "Plains"); addCard(Zone.LIBRARY, playerA, "Ghost Quarter"); @@ -112,9 +126,13 @@ public class HideawayTest extends CardTestPlayerBase { attack(3, playerA, "Auriok Champion"); activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerA, "{W},"); + setChoice(playerA, "Yes"); // play Ghost Quarter setStopAt(3, PhaseStep.END_COMBAT); + + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Ghost Quarter", 0); assertTapped("Windbrisk Heights", true); @@ -122,6 +140,9 @@ public class HideawayTest extends CardTestPlayerBase { @Test public void testCannotPlayLandIfNotOwnTurn() { + // Hideaway (This land enters the battlefield tapped. When it does, look at the top four cards of your library, exile one face down, then put the rest on the bottom of your library.) + // {T}: Add {G}. + // {G}, {T}: You may play the exiled card without paying its mana cost if creatures you control have total power 10 or greater. addCard(Zone.HAND, playerA, "Mosswort Bridge"); addCard(Zone.LIBRARY, playerA, "Ghost Quarter"); skipInitShuffling(); @@ -135,6 +156,7 @@ public class HideawayTest extends CardTestPlayerBase { activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{G},"); setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); assertPermanentCount(playerA, "Ghost Quarter", 0); @@ -158,13 +180,17 @@ public class HideawayTest extends CardTestPlayerBase { attack(3, playerA, "Auriok Champion"); activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerA, "{W},"); + setChoice(playerA, "Yes"); // play Ghost Quarter setStopAt(3, PhaseStep.END_COMBAT); + + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Ghost Quarter", 1); assertTapped("Windbrisk Heights", true); - Assert.assertEquals(playerA.getLandsPlayed(), 1); + Assert.assertEquals(1, playerA.getLandsPlayed()); } @Test @@ -188,13 +214,17 @@ public class HideawayTest extends CardTestPlayerBase { attack(3, playerA, "Auriok Champion"); activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerA, "{W},"); + setChoice(playerA, "Yes"); // play Ghost Quarter setStopAt(3, PhaseStep.END_COMBAT); + + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Ghost Quarter", 1); assertTapped("Windbrisk Heights", true); - Assert.assertEquals(playerA.getLandsPlayed(), 2); + Assert.assertEquals(2, playerA.getLandsPlayed()); } /** @@ -204,7 +234,7 @@ public class HideawayTest extends CardTestPlayerBase { * never the opponents is 20 or fewer */ @Test - public void shelldockIsleHideawayConditionOwnLibrary() { + public void testShelldockIsleHideawayConditionOwnLibrary() { /* Shelldock Isle @@ -224,9 +254,13 @@ public class HideawayTest extends CardTestPlayerBase { playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, sIsle); setChoice(playerA, ulamog); activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{U}"); + setChoice(playerA, "Yes"); // play Ulamog's Crusher setStopAt(3, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertTappedCount("Island", true, 1); assertPermanentCount(playerA, sIsle, 1); @@ -243,7 +277,7 @@ public class HideawayTest extends CardTestPlayerBase { * NOTE: test is currently failing due to bug in code. see issue #3310 */ @Test - public void shelldockIsleHideawayConditionOpponentsLibrary() { + public void testShelldockIsleHideawayConditionOpponentsLibrary() { /* Shelldock Isle @@ -265,6 +299,7 @@ public class HideawayTest extends CardTestPlayerBase { playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, sIsle); setChoice(playerA, ulamog); activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{U}"); + setChoice(playerA, "Yes"); // play Ulamog's Crusher setStopAt(3, PhaseStep.BEGIN_COMBAT); execute(); @@ -281,7 +316,7 @@ public class HideawayTest extends CardTestPlayerBase { * blocking. */ @Test - public void blinkWatcherFortomorrow() { + public void testBlinkWatcherForTomorrow() { /* Hideaway (This permanent enters the battlefield tapped" and "When this permanent enters the battlefield, look at the top four cards of @@ -316,4 +351,41 @@ public class HideawayTest extends CardTestPlayerBase { assertExileCount(playerA, 2); assertTapped("Watcher for Tomorrow", true); } + + /** + * Possible bug with the Hideaway mechanic or with Watcher for Tomorrow. If + * it leaves the bf before Hideaway resolves you don't get to exile a card + * when the former eventually resolves, that shouldn't be the case. + */ + @Test + public void testWatcherForTomorrowLeft() { + + // Hideaway + // When Watcher for Tomorrow leaves the battlefield, put the exiled card into its owner's hand. + addCard(Zone.HAND, playerA, "Watcher for Tomorrow"); // Creature 2/1 - {1}{U} + + // Exile target creature you control, then return it to the battlefield under its owner's control. + // Rebound + addCard(Zone.HAND, playerA, "Ephemerate"); // Instant {W} + + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 4); + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Watcher for Tomorrow"); + setChoice(playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Ephemerate", "Watcher for Tomorrow"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Watcher for Tomorrow", 1); + assertExileCount(playerA, "Ephemerate", 1); + + assertHandCount(playerA, "Silvercoat Lion", 1); + assertExileCount(playerA, 2); + assertTapped("Watcher for Tomorrow", true); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImproviseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImproviseTest.java new file mode 100644 index 0000000000..e0712c2c41 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImproviseTest.java @@ -0,0 +1,79 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * @author JayDi85 + */ + +public class ImproviseTest extends CardTestPlayerBaseWithAIHelps { + + // no simple playable tests for improvise, it's same as ConvokeTest + + @Test + public void test_PlayImprovise_Manual() { + // {5}{U} creature + // Improvise (Your artifacts can help cast this spell. Each artifact you tap after you’re done activating mana abilities pays for {1}.) + addCard(Zone.HAND, playerA, "Bastion Inventor", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.BATTLEFIELD, playerA, "Alpha Myr", 2); // improvise pay + + // use special action to pay (need disabled auto-payment and prepared mana pool) + disableManaAutoPayment(playerA); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 4); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bastion Inventor"); + setChoice(playerA, "Blue", 4); // pay 1-4 + // improvise pay by one card + setChoice(playerA, "Improvise"); + addTarget(playerA, "Alpha Myr"); // pay 5 as improvise + setChoice(playerA, "Improvise"); + addTarget(playerA, "Alpha Myr"); // pay 6 as improvise + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Bastion Inventor", 1); + } + + @Test + public void test_PlayImprovise_AI_AutoPay() { + // {5}{U} creature + // Improvise (Your artifacts can help cast this spell. Each artifact you tap after you’re done activating mana abilities pays for {1}.) + addCard(Zone.HAND, playerA, "Bastion Inventor", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.BATTLEFIELD, playerA, "Alpha Myr", 2); // improvise pay + + // AI must use special actions to pay as delve + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bastion Inventor"); + + //setStrictChooseMode(true); AI must choose targets + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Bastion Inventor", 1); + } + + @Test + public void test_PlayImprovise_AI_FullPlay() { + // {5}{U} creature + // Improvise (Your artifacts can help cast this spell. Each artifact you tap after you’re done activating mana abilities pays for {1}.) + addCard(Zone.HAND, playerA, "Bastion Inventor", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.BATTLEFIELD, playerA, "Alpha Myr", 2); // improvise pay + + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Bastion Inventor", 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerWithFlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerWithFlashbackTest.java index 44e1dde0c8..6133020672 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerWithFlashbackTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerWithFlashbackTest.java @@ -75,7 +75,7 @@ public class KickerWithFlashbackTest extends CardTestPlayerBase { addTarget(playerA, "Burst Lightning"); // cast burst by flashback - // showAvaileableAbilities("after", 1, PhaseStep.BEGIN_COMBAT, playerA); + // showAvailableAbilities("after", 1, PhaseStep.BEGIN_COMBAT, playerA); activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "Flashback"); setChoice(playerA, "Yes"); // use kicker addTarget(playerA, playerB); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java index 2687181768..3c41b01166 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java @@ -23,15 +23,11 @@ public class MadnessTest extends CardTestPlayerBase { * card into their graveyard.” 702.34b Casting a spell using its * madness ability follows the rules for paying alternative costs in rules * 601.2b and 601.2e–g. - * - */ - /** + *

* Arrogant Wurm 3GG Creature -- Wurm 4/4 Trample Madness {2}{G} (If you * discard this card, you may cast it for its madness cost instead of * putting it into your graveyard.) - * - */ - /** + *

* Raven's Crime B Sorcery Target player discards a card. Retrace (You may * cast this card from your graveyard by discarding a land card in addition * to paying its other costs.) @@ -45,7 +41,6 @@ public class MadnessTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raven's Crime", playerA); setChoice(playerA, "Yes"); // use madness triggered ability - setChoice(playerA, "Yes"); // use madness cast setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); @@ -125,7 +120,6 @@ public class MadnessTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Haunting Hymn", playerA); setChoice(playerA, "Yes"); // use madness triggered ability - setChoice(playerA, "Yes"); // use madness cast setChoice(playerA, "X=4"); addTargetAmount(playerA, "Pillarfield Ox", 4); @@ -177,12 +171,11 @@ public class MadnessTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Forest"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Falkenrath Gorger"); + setChoice(playerA, "Yes"); // Discard a card and put a +1/+1 counter on that creature, it gains haste until end of turn, and it becomes a Vampire in addition to its other types? - setChoice(playerA, "Asylum Visitor"); - setChoice(playerA, "Asylum Visitor: Madness {1}{B}"); // choose replacement effect (TODO: 2 same madness effetcs: one from Asylum Visitor, one from Falkenrath -- is that ok?!) - // + setChoice(playerA, "Asylum Visitor"); // Card to discard from Falkenrath entering by Olivia effect + setChoice(playerA, "Asylum Visito"); // Madness {1}{B} setChoice(playerA, "Yes"); // use madness triggered ability - setChoice(playerA, "Yes"); // use madness cast setChoice(playerA, "Yes"); // Discard a card and put a +1/+1 counter on that creature, it gains haste until end of turn, and it becomes a Vampire in addition to its other types? setChoice(playerA, "Forest"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java index 850580b7b4..07e2c95045 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java @@ -123,6 +123,8 @@ public class ManifestTest extends CardTestPlayerBase { */ @Test public void testNylea() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); // Exile target creature. Its controller manifests the top card of their library {1}{U} addCard(Zone.HAND, playerB, "Reality Shift"); @@ -274,9 +276,12 @@ public class ManifestTest extends CardTestPlayerBase { // Check if a Megamorph card is manifested and turned face up by their megamorph ability // it gets the +1/+1 counter. + // 701.33c + // If a card with morph is manifested, its controller may turn that card face up using + // either the procedure described in rule 702.36e to turn a face-down permanent with morph face up + // or the procedure described above to turn a manifested permanent face up. @Test - public void testManifestMegamorph() { - + public void testManifestMegamorph_TurnUpByMegamorphCost() { addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); addCard(Zone.BATTLEFIELD, playerB, "Forest", 6); // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. @@ -295,6 +300,7 @@ public class ManifestTest extends CardTestPlayerBase { activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{5}{G}: Turn"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.END_TURN); execute(); assertAllCommandsUsed(); @@ -310,7 +316,45 @@ public class ManifestTest extends CardTestPlayerBase { assertPowerToughness(playerB, "Aerie Bowmasters", 4, 5); // 3/4 and the +1/+1 counter from Megamorph Permanent aerie = getPermanent("Aerie Bowmasters", playerB); Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen()); + } + @Test + public void testManifestMegamorph_TurnUpBySimpleCost() { + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 4); + // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. + addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + // {2}{G}{G} + // Reach (This creature can block creatures with flying.) + // Megamorph {5}{G} + addCard(Zone.LIBRARY, playerB, "Aerie Bowmasters", 1); + addCard(Zone.LIBRARY, playerB, "Mountain", 1); + + skipInitShuffling(); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); + setChoice(playerB, "Silvercoat Lion"); + + activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{2}{G}{G}: Turn"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0); + assertPermanentCount(playerB, "Aerie Bowmasters", 1); + assertPowerToughness(playerB, "Aerie Bowmasters", 3, 4); // 3/4 without counter (megamorph not used) + Permanent aerie = getPermanent("Aerie Bowmasters", playerB); + Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen()); } /** diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MeldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MeldTest.java index 5daa529e90..d449ddd501 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MeldTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MeldTest.java @@ -1,183 +1,216 @@ - -package org.mage.test.cards.abilities.keywords; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class MeldTest extends CardTestPlayerBase { - - @Test - public void testMeldAndRestrict() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); - // When you cast Bruna, the Fading Light, you may return target Angel or Human creature card from your graveyard to the battlefield. - // Flying, Vigilance - // (Melds with Gisela, the Broken Blade.) - addCard(Zone.HAND, playerA, "Bruna, the Fading Light"); // {5}{W}{W} - // Flying, First strike, Lifelink - // At the beginning of your end step, if you both own and control Gisela, the Broken Blade and a - // creature named Bruna, the Fading Light, exile them, then meld them into Brisela, Voice of Nightmares. - addCard(Zone.HAND, playerA, "Gisela, the Broken Blade"); // {2}{W}{W} - // Brisela, Voice of Nightmares - // Flying, First strike, Vigilance, Lifelink - // Your opponents can't cast spells with converted mana cost 3 or less. - - addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); - addCard(Zone.HAND, playerB, "Silvercoat Lion", 2); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bruna, the Fading Light"); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); - castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Gisela, the Broken Blade"); - castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); - - setStopAt(4, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Brisela, Voice of Nightmares", 1); - assertPermanentCount(playerA, "Bruna, the Fading Light", 0); - assertPermanentCount(playerA, "Gisela, the Broken Blade", 0); - - assertPermanentCount(playerB, "Silvercoat Lion", 1); - assertHandCount(playerB, "Silvercoat Lion", 1); - - } - - /** - * Brisela is bugged she is still "active" when dead - */ - @Test - public void testMeldAndStopRestrictIfMeldCreatureLeftBattlefield() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); - // When you cast Bruna, the Fading Light, you may return target Angel or Human creature card from your graveyard to the battlefield. - // Flying, Vigilance - // (Melds with Gisela, the Broken Blade.) - addCard(Zone.HAND, playerA, "Bruna, the Fading Light"); // {5}{W}{W} - // Flying, First strike, Lifelink - // At the beginning of your end step, if you both own and control Gisela, the Broken Blade and a - // creature named Bruna, the Fading Light, exile them, then meld them into Brisela, Voice of Nightmares. - addCard(Zone.HAND, playerA, "Gisela, the Broken Blade"); // {2}{W}{W} - // Brisela, Voice of Nightmares 9/10 - // Flying, First strike, Vigilance, Lifelink - // Your opponents can't cast spells with converted mana cost 3 or less. - - addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - addCard(Zone.HAND, playerB, "Silvercoat Lion", 2); - // Exile target creature. You draw cards equal to that creature's power. - // At the beginning of your next upkeep, return that card to the battlefield under its owner's control. - // If you do, discard cards equal to that creature's toughness. - addCard(Zone.HAND, playerB, "Vanish into Memory", 1); // Instant {2}{W}{U} - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bruna, the Fading Light"); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); - castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Gisela, the Broken Blade"); - castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Vanish into Memory", "Brisela, Voice of Nightmares"); - castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); - - setStopAt(4, PhaseStep.BEGIN_COMBAT); - execute(); - - assertExileCount("Bruna, the Fading Light", 1); - assertExileCount("Gisela, the Broken Blade", 1); - assertPermanentCount(playerA, "Brisela, Voice of Nightmares", 0); - - assertGraveyardCount(playerB, "Vanish into Memory", 1); - assertPermanentCount(playerB, "Silvercoat Lion", 2); - assertHandCount(playerB, 2 + 9); - - } - - /** - * Check that if the exiled parts return Brisela is created again - */ - @Test - public void testMeld3() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); - // When you cast Bruna, the Fading Light, you may return target Angel or Human creature card from your graveyard to the battlefield. - // Flying, Vigilance - // (Melds with Gisela, the Broken Blade.) - addCard(Zone.HAND, playerA, "Bruna, the Fading Light"); // Creature {5}{W}{W} 5/7 - // Flying, First strike, Lifelink - // At the beginning of your end step, if you both own and control Gisela, the Broken Blade and a - // creature named Bruna, the Fading Light, exile them, then meld them into Brisela, Voice of Nightmares. - addCard(Zone.HAND, playerA, "Gisela, the Broken Blade"); // Creature {2}{W}{W} 4/3 - // Brisela, Voice of Nightmares 9/10 - // Flying, First strike, Vigilance, Lifelink - // Your opponents can't cast spells with converted mana cost 3 or less. - - addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - addCard(Zone.HAND, playerB, "Silvercoat Lion", 2); - // Exile target creature. You draw cards equal to that creature's power. - // At the beginning of your next upkeep, return that card to the battlefield under its owner's control. - // If you do, discard cards equal to that creature's toughness. - addCard(Zone.HAND, playerB, "Vanish into Memory", 1); // Instant {2}{W}{U} - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bruna, the Fading Light"); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); - castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Gisela, the Broken Blade"); - castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Vanish into Memory", "Brisela, Voice of Nightmares"); - castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); - - // End step turn 7 the meld takes place again - setStopAt(8, PhaseStep.UPKEEP); - execute(); - - assertExileCount("Bruna, the Fading Light", 0); - assertExileCount("Gisela, the Broken Blade", 0); - assertPermanentCount(playerA, "Brisela, Voice of Nightmares", 1); - - assertGraveyardCount(playerB, "Vanish into Memory", 1); - assertPermanentCount(playerB, "Silvercoat Lion", 2); - assertHandCount(playerB, 1); // discard 10 upkeep turn 6 ==> 0 + draw 1 at draw phase turn 6 - - } - - /** - * With Hanweir Garrison and Hanweir Battlements in your control put Hanweir - * Battlements' ability in the stack to transform(i.e. meld). In answer to - * that, return to hand Hanweir Garrison. Resolve Hanweir Battlements - * ability. - * - * Expected result: The ability fizzles. - * - * Actual results: A NPE error is lauched. - */ - @Test - public void testReturnToHand() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); - - // Whenever Hanweir Garrison attacks, put two 1/1 red Human creature tokens onto the battlefield tapped and attacking. - // (Melds with Hanweir Battlements.) - addCard(Zone.BATTLEFIELD, playerA, "Hanweir Garrison"); // Creature 2/3 {2}{R} - - // {T}: Add {C}. - // {R},{T}: Target creature gains haste until end of turn. - // {3}{R}{R},{T}: If you both own and control Hanweir Battlements and a creature named Hanweir Garrison, exile them, then meld them into Hanweir, the Writhing Township. - addCard(Zone.BATTLEFIELD, playerA, "Hanweir Battlements"); // Land - - // Brisela, Voice of Nightmares 9/10 - // Flying, First strike, Vigilance, Lifelink - // Your opponents can't cast spells with converted mana cost 3 or less. - addCard(Zone.BATTLEFIELD, playerB, "Island", 1); - // Return target creature to its owner's hand. - addCard(Zone.HAND, playerB, "Unsummon", 1); // Instant {U} - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{R}{R}"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Unsummon", "Hanweir Garrison", "{3}{R}{R}"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerB, "Unsummon", 1); - - assertPermanentCount(playerA, "Hanweir Battlements", 1); - assertHandCount(playerA, "Hanweir Garrison", 1); - - } -} +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class MeldTest extends CardTestPlayerBase { + + @Test + public void testMeldAndRestrict() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + // When you cast Bruna, the Fading Light, you may return target Angel or Human creature card from your graveyard to the battlefield. + // Flying, Vigilance + // (Melds with Gisela, the Broken Blade.) + addCard(Zone.HAND, playerA, "Bruna, the Fading Light"); // {5}{W}{W} + // Flying, First strike, Lifelink + // At the beginning of your end step, if you both own and control Gisela, the Broken Blade and a + // creature named Bruna, the Fading Light, exile them, then meld them into Brisela, Voice of Nightmares. + addCard(Zone.HAND, playerA, "Gisela, the Broken Blade"); // {2}{W}{W} + // Brisela, Voice of Nightmares + // Flying, First strike, Vigilance, Lifelink + // Your opponents can't cast spells with converted mana cost 3 or less. + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + addCard(Zone.HAND, playerB, "Silvercoat Lion", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bruna, the Fading Light"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Gisela, the Broken Blade"); + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); + + setStopAt(4, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Brisela, Voice of Nightmares", 1); + assertPermanentCount(playerA, "Bruna, the Fading Light", 0); + assertPermanentCount(playerA, "Gisela, the Broken Blade", 0); + + assertPermanentCount(playerB, "Silvercoat Lion", 1); + assertHandCount(playerB, "Silvercoat Lion", 1); + + } + + /** + * Brisela is bugged she is still "active" when dead + */ + @Test + public void testMeldAndStopRestrictIfMeldCreatureLeftBattlefield() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + // When you cast Bruna, the Fading Light, you may return target Angel or Human creature card from your graveyard to the battlefield. + // Flying, Vigilance + // (Melds with Gisela, the Broken Blade.) + addCard(Zone.HAND, playerA, "Bruna, the Fading Light"); // {5}{W}{W} + // Flying, First strike, Lifelink + // At the beginning of your end step, if you both own and control Gisela, the Broken Blade and a + // creature named Bruna, the Fading Light, exile them, then meld them into Brisela, Voice of Nightmares. + addCard(Zone.HAND, playerA, "Gisela, the Broken Blade"); // {2}{W}{W} + // Brisela, Voice of Nightmares 9/10 + // Flying, First strike, Vigilance, Lifelink + // Your opponents can't cast spells with converted mana cost 3 or less. + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.HAND, playerB, "Silvercoat Lion", 2); + // Exile target creature. You draw cards equal to that creature's power. + // At the beginning of your next upkeep, return that card to the battlefield under its owner's control. + // If you do, discard cards equal to that creature's toughness. + addCard(Zone.HAND, playerB, "Vanish into Memory", 1); // Instant {2}{W}{U} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bruna, the Fading Light"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Gisela, the Broken Blade"); + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Vanish into Memory", "Brisela, Voice of Nightmares"); + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); + + setStopAt(4, PhaseStep.BEGIN_COMBAT); + execute(); + + assertExileCount("Bruna, the Fading Light", 1); + assertExileCount("Gisela, the Broken Blade", 1); + assertPermanentCount(playerA, "Brisela, Voice of Nightmares", 0); + + assertGraveyardCount(playerB, "Vanish into Memory", 1); + assertPermanentCount(playerB, "Silvercoat Lion", 2); + assertHandCount(playerB, 2 + 9); + + } + + /** + * Check that if the exiled parts return Brisela is created again + */ + @Test + public void testMeld3() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + // When you cast Bruna, the Fading Light, you may return target Angel or Human creature card from your graveyard to the battlefield. + // Flying, Vigilance + // (Melds with Gisela, the Broken Blade.) + addCard(Zone.HAND, playerA, "Bruna, the Fading Light"); // Creature {5}{W}{W} 5/7 + // Flying, First strike, Lifelink + // At the beginning of your end step, if you both own and control Gisela, the Broken Blade and a + // creature named Bruna, the Fading Light, exile them, then meld them into Brisela, Voice of Nightmares. + addCard(Zone.HAND, playerA, "Gisela, the Broken Blade"); // Creature {2}{W}{W} 4/3 + // Brisela, Voice of Nightmares 9/10 + // Flying, First strike, Vigilance, Lifelink + // Your opponents can't cast spells with converted mana cost 3 or less. + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.HAND, playerB, "Silvercoat Lion", 2); + // Exile target creature. You draw cards equal to that creature's power. + // At the beginning of your next upkeep, return that card to the battlefield under its owner's control. + // If you do, discard cards equal to that creature's toughness. + addCard(Zone.HAND, playerB, "Vanish into Memory", 1); // Instant {2}{W}{U} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bruna, the Fading Light"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Gisela, the Broken Blade"); + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Vanish into Memory", "Brisela, Voice of Nightmares"); + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); + + // End step turn 7 the meld takes place again + setStopAt(8, PhaseStep.UPKEEP); + execute(); + + assertExileCount("Bruna, the Fading Light", 0); + assertExileCount("Gisela, the Broken Blade", 0); + assertPermanentCount(playerA, "Brisela, Voice of Nightmares", 1); + + assertGraveyardCount(playerB, "Vanish into Memory", 1); + assertPermanentCount(playerB, "Silvercoat Lion", 2); + assertHandCount(playerB, 1); // discard 10 upkeep turn 6 ==> 0 + draw 1 at draw phase turn 6 + + } + + /** + * With Hanweir Garrison and Hanweir Battlements in your control put Hanweir + * Battlements' ability in the stack to transform(i.e. meld). In answer to + * that, return to hand Hanweir Garrison. Resolve Hanweir Battlements + * ability. + * + * Expected result: The ability fizzles. + * + * Actual results: A NPE error is lauched. + */ + @Test + public void testReturnToHand() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + + // Whenever Hanweir Garrison attacks, put two 1/1 red Human creature tokens onto the battlefield tapped and attacking. + // (Melds with Hanweir Battlements.) + addCard(Zone.BATTLEFIELD, playerA, "Hanweir Garrison"); // Creature 2/3 {2}{R} + + // {T}: Add {C}. + // {R},{T}: Target creature gains haste until end of turn. + // {3}{R}{R},{T}: If you both own and control Hanweir Battlements and a creature named Hanweir Garrison, exile them, + // then meld them into Hanweir, the Writhing Township. + addCard(Zone.BATTLEFIELD, playerA, "Hanweir Battlements"); // Land + + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + // Return target creature to its owner's hand. + addCard(Zone.HAND, playerB, "Unsummon", 1); // Instant {U} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{R}{R}"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Unsummon", "Hanweir Garrison", "{3}{R}{R}"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, "Unsummon", 1); + + assertPermanentCount(playerA, "Hanweir Battlements", 1); + assertHandCount(playerA, "Hanweir Garrison", 1); + + } + + @Test + public void testUnmeldAfterRollback() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + + // Whenever Hanweir Garrison attacks, put two 1/1 red Human creature tokens onto the battlefield tapped and attacking. + // (Melds with Hanweir Battlements.) + addCard(Zone.BATTLEFIELD, playerA, "Hanweir Garrison"); // Creature 2/3 {2}{R} + + // {T}: Add {C}. + // {R},{T}: Target creature gains haste until end of turn. + // {3}{R}{R},{T}: If you both own and control Hanweir Battlements and a creature named Hanweir Garrison, exile them, + // then meld them into Hanweir, the Writhing Township. + addCard(Zone.BATTLEFIELD, playerA, "Hanweir Battlements"); // Land + + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + // Return target creature to its owner's hand. + addCard(Zone.HAND, playerB, "Unsummon", 1); // Instant {U} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{R}{R}"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Unsummon", "Hanweir, the Writhing Township"); + + rollbackTurns(2, PhaseStep.BEGIN_COMBAT, playerB, 0); + rollbackAfterActionsStart(); + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Unsummon", "Hanweir, the Writhing Township"); + rollbackAfterActionsEnd(); + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Unsummon", 1); + + assertHandCount(playerA, "Hanweir Battlements", 1); + assertHandCount(playerA, "Hanweir Garrison", 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java index dacc8d11e2..9b4a52df42 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java @@ -636,15 +636,14 @@ public class MorphTest extends CardTestPlayerBase { * restriction." */ @Test - public void testReflectorMageBouncesFaceupCreatureReplayAsMorph() { - + public void test_ReflectorMageCantStopMorphToCast_TryNormalCast() { // {1}{W}{U} When Reflector Mage enters the battlefield, return target creature an opponent controls to its owner's hand. // That creature's owner can't cast spells with the same name as that creature until your next turn. addCard(Zone.HAND, playerA, "Reflector Mage"); // 2/3 addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); addCard(Zone.BATTLEFIELD, playerA, "Island", 2); - //Tap: Add {G}, {U}, or {R}. + // Tap: Add {G}, {U}, or {R}. // Morph 2 (You may cast this card face down as a 2/2 creature for 3. Turn it face up any time for its morph cost.) // When Rattleclaw Mystic is turned face up, add {G}{U}{R}. addCard(Zone.BATTLEFIELD, playerB, "Rattleclaw Mystic"); // 2/1 @@ -652,20 +651,58 @@ public class MorphTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Island"); addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + // return to hand castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reflector Mage"); addTarget(playerA, "Rattleclaw Mystic"); + // try cast as normal -- must not work castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Rattleclaw Mystic"); - setChoice(playerB, "Yes"); // cast it face down as 2/2 creature + setChoice(playerB, "No"); // try cast as normal + //setStrictChooseMode(true); // no strict mode - cause can't cast as normal setStopAt(2, PhaseStep.BEGIN_COMBAT); - execute(); + //assertAllCommandsUsed(); assertPermanentCount(playerA, "Reflector Mage", 1); assertPermanentCount(playerB, "Rattleclaw Mystic", 0); - assertHandCount(playerB, "Rattleclaw Mystic", 0); // should have been replayed - assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); // Rattleclaw played as a morph + assertHandCount(playerB, "Rattleclaw Mystic", 1); // can't play + assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0); // don't try as morph + } + + @Test + public void test_ReflectorMageCantStopMorphToCast_TryMorph() { + // {1}{W}{U} When Reflector Mage enters the battlefield, return target creature an opponent controls to its owner's hand. + // That creature's owner can't cast spells with the same name as that creature until your next turn. + addCard(Zone.HAND, playerA, "Reflector Mage"); // 2/3 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + // Tap: Add {G}, {U}, or {R}. + // Morph 2 (You may cast this card face down as a 2/2 creature for 3. Turn it face up any time for its morph cost.) + // When Rattleclaw Mystic is turned face up, add {G}{U}{R}. + addCard(Zone.BATTLEFIELD, playerB, "Rattleclaw Mystic"); // 2/1 + addCard(Zone.BATTLEFIELD, playerB, "Forest"); + addCard(Zone.BATTLEFIELD, playerB, "Island"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + + // return to hand + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reflector Mage"); + addTarget(playerA, "Rattleclaw Mystic"); + + // try cast as morph - must work + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Rattleclaw Mystic"); + setChoice(playerB, "Yes"); // try cast as morph + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Reflector Mage", 1); + assertPermanentCount(playerB, "Rattleclaw Mystic", 0); + assertHandCount(playerB, "Rattleclaw Mystic", 0); // able cast as morph + assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); } /** @@ -914,4 +951,195 @@ public class MorphTest extends CardTestPlayerBase { Permanent akroma = getPermanent("Akroma, Angel of Fury"); Assert.assertTrue("Akroma has to be red", akroma.getColor(currentGame).isRed()); } + + @Test + public void test_LandWithMorph_PlayLand() { + // Morph {2} + addCard(Zone.HAND, playerA, "Zoetic Cavern"); + + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Zoetic Cavern", true); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Zoetic Cavern"); + setChoice(playerA, "No"); // no morph (canPay for generic/colored mana returns true all the time, so xmage ask about face down cast) + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Zoetic Cavern", 1); + } + + @Test + public void test_LandWithMorph_Morph() { + // Morph {2} + addCard(Zone.HAND, playerA, "Zoetic Cavern"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Zoetic Cavern", true); + checkPlayableAbility("morph must be replaced by play ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Morph", false); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Zoetic Cavern"); + setChoice(playerA, "Yes"); // morph + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Zoetic Cavern", 0); + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + } + + @Test + public void test_LandWithMorph_MorphAfterLand() { + removeAllCardsFromHand(playerA); + + // Morph {2} + addCard(Zone.HAND, playerA, "Zoetic Cavern"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + addCard(Zone.HAND, playerA, "Island", 1); + + // play land first + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Island"); + + // morph ability (play as face down) calls from playLand method, so it visible for play land command + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Zoetic Cavern", true); + checkPlayableAbility("morph must be replaced by play ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Morph", false); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Zoetic Cavern"); + setChoice(playerA, "Yes"); // morph + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Island", 1); + assertPermanentCount(playerA, "Zoetic Cavern", 0); + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + } + + @Test + public void test_LandWithMorph_MorphFromLibrary() { + removeAllCardsFromLibrary(playerA); + + // You may play lands and cast spells from the top of your library. + addCard(Zone.BATTLEFIELD, playerA, "Future Sight"); + // + // Morph {2} + addCard(Zone.LIBRARY, playerA, "Zoetic Cavern"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Zoetic Cavern", true); + checkPlayableAbility("morph must be replaced by play ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Morph", false); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Zoetic Cavern"); + setChoice(playerA, "Yes"); // morph + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Zoetic Cavern", 0); + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + } + + @Test + public void test_CantActivateOnOpponentTurn() { + // https://github.com/magefree/mage/issues/6698 + + // Morph {1}{U} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.) + // When Willbender is turned face up, change the target of target spell or ability with a single target. + addCard(Zone.HAND, playerA, "Willbender"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + // can play on own turn + checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", true); + + // can't play on opponent turn + checkPlayableAbility("can't", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", false); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_MorphWithCostReductionMustBePlayable_NormalCondition() { + // {1}{U} creature + // Morph {1}{U} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.) + // When Willbender is turned face up, change the target of target spell or ability with a single target. + addCard(Zone.HAND, playerA, "Willbender"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + // Creature spells you cast cost {1} less to cast. + addCard(Zone.BATTLEFIELD, playerA, "Nylea, Keen-Eyed"); + + checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender"); + setChoice(playerA, "Yes"); // morph + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + } + + @Test + public void test_MorphWithCostReductionMustBePlayable_MorphCondition1() { + // {1}{U} creature + // Morph {1}{U} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.) + // When Willbender is turned face up, change the target of target spell or ability with a single target. + addCard(Zone.HAND, playerA, "Willbender"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + // Face-down creature spells you cast cost {1} less to cast. + addCard(Zone.BATTLEFIELD, playerA, "Dream Chisel"); + + checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender"); + setChoice(playerA, "Yes"); // morph + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + } + + @Test + public void test_MorphWithCostReductionMustBePlayable_MorphCondition2() { + // {1}{U} creature + // Morph {1}{U} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.) + // When Willbender is turned face up, change the target of target spell or ability with a single target. + addCard(Zone.HAND, playerA, "Willbender", 2); + //addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + // The first face-down creature spell you cast each turn costs {3} less to cast. + addCard(Zone.BATTLEFIELD, playerA, "Kadena, Slinking Sorcerer"); + + // creature one - get cost reduce + checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender"); + setChoice(playerA, "Yes"); // morph + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // creature two - do not get cost reduce + checkPlayableAbility("can't by no reduce", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", false); + + // on next turn it can cost reduce again + checkPlayableAbility("can't by not your turn", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", false); + checkPlayableAbility("can", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", true); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ProwlTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ProwlTest.java index 894073777c..38db073ae3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ProwlTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ProwlTest.java @@ -1,75 +1,77 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public class ProwlTest extends CardTestPlayerBase { - - @Ignore // have not figured out how to have the test API cast a card using Prowl yet + @Test - public void testBasicProwlCasting() { + public void test_ProwlNormal() { // Auntie's Snitch {2}{B} Creature — Goblin Rogue (3/1) // Auntie's Snitch can't block. // Prowl {1}{B} (You may cast this for its prowl cost if you dealt combat damage to a player this turn with a Goblin or Rogue.) // Whenever a Goblin or Rogue you control deals combat damage to a player, if Auntie's Snitch is in your graveyard, you may return Auntie's Snitch to your hand. addCard(Zone.HAND, playerA, "Auntie's Snitch"); - + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // // {1}{R} Creature — Goblin Warrior 1/1 // Red creatures you control have first strike. - addCard(Zone.BATTLEFIELD, playerA, "Bloodmark Mentor"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); - + addCard(Zone.BATTLEFIELD, playerA, "Bloodmark Mentor"); + + // prepare prowl condition + checkPlayableAbility("can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Auntie's Snitch", false); attack(1, playerA, "Bloodmark Mentor"); - - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Auntie's Snitch using prowl"); + + checkPlayableAbility("must play", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Auntie's Snitch", true); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Auntie's Snitch"); setChoice(playerA, "Yes"); // choosing to pay prowl cost + + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); - + assertAllCommandsUsed(); + assertLife(playerB, 19); assertPermanentCount(playerA, "Bloodmark Mentor", 1); assertPermanentCount(playerA, "Auntie's Snitch", 1); } - + /* * Reported bug: Prowl is not taking into consideration other cost reducing effects. For instance Goblin Warchief * does not reduce the Prowl cost of other Goblin cards with Prowl ability. - */ - @Ignore // have not figured out how to have the test API cast a card using Prowl yet + */ @Test - public void testProwlWithCostDiscount() { - + public void test_ProwlWithCostReduce() { // Auntie's Snitch {2}{B} Creature — Goblin Rogue (3/1) // Auntie's Snitch can't block. // Prowl {1}{B} (You may cast this for its prowl cost if you dealt combat damage to a player this turn with a Goblin or Rogue.) // Whenever a Goblin or Rogue you control deals combat damage to a player, if Auntie's Snitch is in your graveyard, you may return Auntie's Snitch to your hand. - addCard(Zone.HAND, playerA, "Auntie's Snitch"); - + addCard(Zone.HAND, playerA, "Auntie's Snitch"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // Goblin Warchief {1}{R}{R} Creature — Goblin Warrior (2/2) // Goblin spells you cast cost 1 less to cast. // Goblin creatures you control have haste. addCard(Zone.BATTLEFIELD, playerA, "Goblin Warchief"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - + + // prepare prowl condition + checkPlayableAbility("can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Auntie's Snitch", false); attack(1, playerA, "Goblin Warchief"); - - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Auntie's Snitch using prowl"); // should only cost {B} with Warchief discount + + checkPlayableAbility("must play", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Auntie's Snitch", true); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Auntie's Snitch"); // should only cost {B} with Warchief discount setChoice(playerA, "Yes"); // choosing to pay prowl cost + + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); - + assertAllCommandsUsed(); + assertLife(playerB, 18); assertPermanentCount(playerA, "Goblin Warchief", 1); assertPermanentCount(playerA, "Auntie's Snitch", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/RevoltTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RevoltTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/RevoltTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RevoltTest.java index c25d6cad2d..151446d6c3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/RevoltTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RevoltTest.java @@ -1,5 +1,4 @@ - -package org.mage.test.cards.abilities.abilitywords; +package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -7,7 +6,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class RevoltTest extends CardTestPlayerBase { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java index 5c0cd5bce1..7315ce32c6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java @@ -79,6 +79,7 @@ public class RiotTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); execute(); assertAllCommandsUsed(); @@ -88,6 +89,34 @@ public class RiotTest extends CardTestPlayerBase { assertAbility(playerA, "Silvercoat Lion", new RiotAbility(), true); } + @Test + public void RiotRhythmOfTheWildDoubleBoost() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + // Creature spells you control can't be countered. + // Nontoken creatures you control have riot. + addCard(Zone.BATTLEFIELD, playerA, "Rhythm of the Wild", 2); + + // Riot (This creature enters the battleifled with your choice of a +1/+1 counter or haste.) + addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); // Creature {1}{W} 2/2 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + setChoice(playerA, "Rhythm of the Wild"); // choose replacement effect + setChoice(playerA, "Yes"); // yes - counter + setChoice(playerA, "Yes"); // yes - counter + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 2 + 2, 2 + 2); + assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), false); + assertAbility(playerA, "Silvercoat Lion", new RiotAbility(), true); + } + @Test public void RiotRhythmOfTheWildHaste() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); @@ -104,6 +133,7 @@ public class RiotTest extends CardTestPlayerBase { setStopAt(2, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); execute(); assertAllCommandsUsed(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ScavengeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ScavengeTest.java index b71c2fd51c..26ec57c58e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ScavengeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ScavengeTest.java @@ -1,122 +1,122 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.mage.test.cards.abilities.keywords; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class ScavengeTest extends CardTestPlayerBase { - -// -// 702.95. Scavenge -// -// 702.95a Scavenge is an activated ability that functions only while the card -// with scavenge is in a graveyard. "Scavenge [cost]" means "[Cost], Exile this -// card from your graveyard: Put a number of +1/+1 counters equal to this card's -// power on target creature. Activate this ability only any time you could cast -// a sorcery." -// - @Test - public void SimpleScavenge() { - addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); - - // Scavenge {4}{G}{G} ({4}{G}{G} , Exile this card from your graveyard: Put a number of +1/+1 counter's equal to this card's power on target creature. Scavenge only as a sorcery.) - addCard(Zone.GRAVEYARD, playerA, "Deadbridge Goliath"); // 5/5 - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scavenge", "Silvercoat Lion"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Deadbridge Goliath", 0); - assertExileCount(playerA, "Deadbridge Goliath", 1); - assertPowerToughness(playerA, "Silvercoat Lion", 7, 7); - } - - @Test - public void SimpleScavengeWithCostReduction() { - addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); - - // Scavenge {4}{G}{G} ({4}{G}{G} , Exile this card from your graveyard: Put a number of +1/+1 counter's equal to this card's power on target creature. Scavenge only as a sorcery.) - addCard(Zone.GRAVEYARD, playerA, "Deadbridge Goliath"); // 5/5 - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); - - // Activated abilities of creature cards in your graveyard cost {1} less to activate. - // Tap an untapped Zombie you control: Target player puts the top card of their library into their graveyard. - addCard(Zone.BATTLEFIELD, playerA, "Embalmer's Tools", 1); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scavenge", "Silvercoat Lion"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Deadbridge Goliath", 0); - assertExileCount(playerA, "Deadbridge Goliath", 1); - assertPowerToughness(playerA, "Silvercoat Lion", 7, 7); - assertTappedCount("Forest", true, 5); - assertTappedCount("Forest", false, 1); - } - - @Test - public void ScavengeWithVaroz() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); - - addCard(Zone.GRAVEYARD, playerA, "Pillarfield Ox"); // 2/4 - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); - - // Each creature card in your graveyard has scavenge. The scavenge cost is equal to its mana cost. - // Sacrifice another creature: Regenerate Varolz, the Scar-Striped. - addCard(Zone.BATTLEFIELD, playerA, "Varolz, the Scar-Striped", 1); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scavenge", "Silvercoat Lion"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Pillarfield Ox", 0); - assertExileCount(playerA, "Pillarfield Ox", 1); - assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); - assertTappedCount("Plains", true, 4); - } - - @Test - public void ScavengeWithVarozAndCostReduction() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); - - addCard(Zone.GRAVEYARD, playerA, "Pillarfield Ox"); // 2/4 - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); - - // Each creature card in your graveyard has scavenge. The scavenge cost is equal to its mana cost. - // Sacrifice another creature: Regenerate Varolz, the Scar-Striped. - addCard(Zone.BATTLEFIELD, playerA, "Varolz, the Scar-Striped", 1); - - // Activated abilities of creature cards in your graveyard cost {1} less to activate. - // Tap an untapped Zombie you control: Target player puts the top card of their library into their graveyard. - addCard(Zone.BATTLEFIELD, playerA, "Embalmer's Tools", 1); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scavenge", "Silvercoat Lion"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Pillarfield Ox", 0); - assertExileCount(playerA, "Pillarfield Ox", 1); - assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); - assertTappedCount("Plains", true, 3); - assertTappedCount("Plains", false, 1); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class ScavengeTest extends CardTestPlayerBase { + +// +// 702.95. Scavenge +// +// 702.95a Scavenge is an activated ability that functions only while the card +// with scavenge is in a graveyard. "Scavenge [cost]" means "[Cost], Exile this +// card from your graveyard: Put a number of +1/+1 counters equal to this card's +// power on target creature. Activate this ability only any time you could cast +// a sorcery." +// + @Test + public void SimpleScavenge() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + + // Scavenge {4}{G}{G} ({4}{G}{G} , Exile this card from your graveyard: Put a number of +1/+1 counter's equal to this card's power on target creature. Scavenge only as a sorcery.) + addCard(Zone.GRAVEYARD, playerA, "Deadbridge Goliath"); // 5/5 + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scavenge", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Deadbridge Goliath", 0); + assertExileCount(playerA, "Deadbridge Goliath", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 7, 7); + } + + @Test + public void SimpleScavengeWithCostReduction() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + + // Scavenge {4}{G}{G} ({4}{G}{G} , Exile this card from your graveyard: Put a number of +1/+1 counter's equal to this card's power on target creature. Scavenge only as a sorcery.) + addCard(Zone.GRAVEYARD, playerA, "Deadbridge Goliath"); // 5/5 + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + // Activated abilities of creature cards in your graveyard cost {1} less to activate. + // Tap an untapped Zombie you control: Target player puts the top card of their library into their graveyard. + addCard(Zone.BATTLEFIELD, playerA, "Embalmer's Tools", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scavenge", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Deadbridge Goliath", 0); + assertExileCount(playerA, "Deadbridge Goliath", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 7, 7); + assertTappedCount("Forest", true, 5); + assertTappedCount("Forest", false, 1); + } + + @Test + public void ScavengeWithVaroz() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + addCard(Zone.GRAVEYARD, playerA, "Pillarfield Ox"); // 2/4 + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + // Each creature card in your graveyard has scavenge. The scavenge cost is equal to its mana cost. + // Sacrifice another creature: Regenerate Varolz, the Scar-Striped. + addCard(Zone.BATTLEFIELD, playerA, "Varolz, the Scar-Striped", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scavenge", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Pillarfield Ox", 0); + assertExileCount(playerA, "Pillarfield Ox", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); + assertTappedCount("Plains", true, 4); + } + + @Test + public void ScavengeWithVarozAndCostReduction() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + addCard(Zone.GRAVEYARD, playerA, "Pillarfield Ox"); // 2/4 + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + // Each creature card in your graveyard has scavenge. The scavenge cost is equal to its mana cost. + // Sacrifice another creature: Regenerate Varolz, the Scar-Striped. + addCard(Zone.BATTLEFIELD, playerA, "Varolz, the Scar-Striped", 1); + + // Activated abilities of creature cards in your graveyard cost {1} less to activate. + // Tap an untapped Zombie you control: Target player puts the top card of their library into their graveyard. + addCard(Zone.BATTLEFIELD, playerA, "Embalmer's Tools", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scavenge", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Pillarfield Ox", 0); + assertExileCount(playerA, "Pillarfield Ox", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); + assertTappedCount("Plains", true, 3); + assertTappedCount("Plains", false, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java index b0bc8a24a2..2e76856b86 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java @@ -45,7 +45,7 @@ public class SpliceOnArcaneTest extends CardTestPlayerBase { assertHandCount(playerA, "Through the Breach", 1); assertPermanentCount(playerA, "Silvercoat Lion", 1); assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), true); - Assert.assertEquals("All available mana has to be used", 0, playerA.getManaAvailable(currentGame).size()); + Assert.assertEquals("All available mana has to be used", "[]", playerA.getManaAvailable(currentGame).toString()); } @Test diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SupportTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SupportTest.java index d8a08c2ee5..c826edcc87 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SupportTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SupportTest.java @@ -1,16 +1,14 @@ - package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; /** - * * @author LevelX2 */ -public class SupportTest extends CardTestPlayerBase { +public class SupportTest extends CardTestPlayerBaseWithAIHelps { /** * Support Ability can target its source. Its cannot really. @@ -18,22 +16,26 @@ public class SupportTest extends CardTestPlayerBase { @Test public void testCreatureSupport() { addCard(Zone.BATTLEFIELD, playerA, "Forest", 7); - // When Gladehart Cavalry enters the battlefield, support 6. + // When Gladehart Cavalry enters the battlefield, support 6. (Put a +1/+1 counter on each of up to six other target creatures.) // Whenever a creature you control with a +1/+1 counter on it dies, you gain 2 life. addCard(Zone.HAND, playerA, "Gladehart Cavalry"); // {5}{G}{G} 6/6 - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // 2/2 + addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox"); // 2/4 - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gladehart Cavalry"); - addTarget(playerA, "Silvercoat Lion^Pillarfield Ox^Gladehart Cavalry");// Gladehart Cavalry should not be allowed + // test framework do not support possible target checks, so allow AI to cast and choose maximum targets + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + //castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gladehart Cavalry"); + //addTarget(playerA, "Silvercoat Lion^Pillarfield Ox^Gladehart Cavalry");// Gladehart Cavalry should not be allowed + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); - assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); - assertPowerToughness(playerA, "Pillarfield Ox", 3, 5); - assertPowerToughness(playerA, "Gladehart Cavalry", 6, 6); + assertPowerToughness(playerA, "Silvercoat Lion", 2 + 1, 2 + 1); + assertPowerToughness(playerA, "Pillarfield Ox", 2 + 1, 4 + 1); + assertPowerToughness(playerA, "Gladehart Cavalry", 6, 6); // no counters } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java index 8e6f75590f..69db45b6dc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.abilities.keywords; import mage.abilities.keyword.HasteAbility; @@ -9,15 +8,13 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * - * @author LevelX2 + * @author LevelX2, JayDi85 */ public class SuspendTest extends CardTestPlayerBase { /** * Tests Epochrasite works (give suspend to a exiled card) When Epochrasite * dies, exile it with three time counters on it and it gains suspend. - * */ @Test public void testEpochrasite() { @@ -46,7 +43,6 @@ public class SuspendTest extends CardTestPlayerBase { * Tests Jhoira of the Ghitu works (give suspend to a exiled card) {2}, * Exile a nonland card from your hand: Put four time counters on the exiled * card. If it doesn't have suspend, it gains suspend. - * */ @Test public void testJhoiraOfTheGhitu() { @@ -71,7 +67,6 @@ public class SuspendTest extends CardTestPlayerBase { /** * Tests that a spell countered with delay goes to exile with 3 time * counters and can be cast after the 3 counters are removed - * */ @Test public void testDelay() { @@ -141,7 +136,6 @@ public class SuspendTest extends CardTestPlayerBase { /** * Suppression Field incorrectly makes suspend cards cost 2 more to suspend. * It made my Rift Bolt cost 2R to suspend instead of R - * */ @Test public void testCostManipulation() { @@ -164,9 +158,8 @@ public class SuspendTest extends CardTestPlayerBase { * Cards cast from other zones that aren't the hand should not trigger * Knowledge Pool, as it states that only cards cast from the hand should be * exiled afterwards. - * + *

* Example: cards coming off suspend shouldn't trigger Knowledge Pool. - * */ @Test public void testThatNotCastFromHand() { @@ -199,4 +192,122 @@ public class SuspendTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Silvercoat Lion", 0); } + + /* + Delay {1}{U} + + Counter target spell. If the spell is countered this way, exile it with three time counters on it instead of putting + it into its owner’s graveyard. If it doesn’t have suspend, it gains suspend. (At the beginning of its owner’s upkeep, + remove a time counter from that card. When the last is removed, the player plays it without paying its mana cost. + If it’s a creature, it has haste.) + + Bug: Casting Delay on a fused Wear // Tear resulted in time counters never coming off it. It just sat there with + three counters every turn. See https://github.com/magefree/mage/issues/6549 + */ + + @Test + public void test_Delay_SimpleSpell() { + // + addCard(Zone.HAND, playerA, "Delay", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // cast spell and counter it with delay + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Delay", "Lightning Bolt", "Lightning Bolt"); + // + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkLife("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 20); + checkExileCount("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + + // 3 time counters removes on upkeep (3, 5, 7) and cast again + addTarget(playerA, playerB); + checkLife("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 3); + checkGraveyardCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Delay_SplitSingle() { + addCard(Zone.HAND, playerA, "Delay", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + // + // Wear {1}{R} Destroy target artifact. + // Tear {W} Destroy target enchantment. + addCard(Zone.HAND, playerA, "Wear // Tear", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + // + addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact + addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact + + // cast spell and counter it with delay + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear", "Bident of Thassa"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Delay", "Wear", "Wear"); + // + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Bident of Thassa", 1); + checkPermanentCount("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Bow of Nylea", 1); + checkExileCount("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear // Tear", 1); + + // 3 time counters removes on upkeep (3, 5, 7) and cast again + addTarget(playerA, "Bident of Thassa"); + checkPermanentCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Bident of Thassa", 0); + checkPermanentCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Bow of Nylea", 1); + checkGraveyardCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear // Tear", 1); + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Delay_SplitFused() { + /* + Bug: Casting Delay on a fused Wear // Tear resulted in time counters never coming off it. It just sat there with + three counters every turn. See https://github.com/magefree/mage/issues/6549 + */ + + // + addCard(Zone.HAND, playerA, "Delay", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + // + // Wear {1}{R} Destroy target artifact. + // Tear {W} Destroy target enchantment. + addCard(Zone.HAND, playerA, "Wear // Tear", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + // + addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact + addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact + + // cast fused spell and counter it with delay + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Wear // Tear"); + addTarget(playerA, "Bident of Thassa"); + addTarget(playerA, "Bow of Nylea"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Delay", "Cast fused Wear // Tear", "Cast fused Wear // Tear"); + // + checkPermanentCount("after counter", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Bident of Thassa", 1); + checkPermanentCount("after counter", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Bow of Nylea", 1); + checkExileCount("after counter", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Wear // Tear", 1); + + // 3 time counters removes on upkeep (3, 5, 7) and cast again (fused cards can't be played from exile zone, so select split spell only) + setChoice(playerA, "Cast Wear"); + addTarget(playerA, "Bident of Thassa"); + checkPermanentCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Bident of Thassa", 0); + checkPermanentCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Bow of Nylea", 1); + checkGraveyardCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear // Tear", 1); + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TributeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TributeTest.java index e18d3ec7dd..fa6817c02f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TributeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TributeTest.java @@ -1,48 +1,48 @@ - -package org.mage.test.cards.abilities.keywords; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class TributeTest extends CardTestPlayerBase { - - /** - * When returning a Pharagax Giant from the graveyard to the battlefield via - * the ability of Ink-Eyes, Servant of Oni the Tribute mechanic did not - * trigger, even though it says 'enters the battlefield' and not 'cast' on - * the card. - */ - @Test - public void testPharagaxGiant() { - addCard(Zone.BATTLEFIELD, playerA, "Island", 3); - - // Ninjutsu {3}{B}{B} ({3}{B}{B}, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking.) - // Whenever Ink-Eyes, Servant of Oni deals combat damage to a player, you may put target creature card from that player's graveyard onto the battlefield under your control. - // {1}{B}: Regenerate Ink-Eyes. - addCard(Zone.BATTLEFIELD, playerA, "Ink-Eyes, Servant of Oni"); // 5/4 - - // Tribute 2 ((As this creature enters the battlefield, an opponent of your choice may put two +1/+1 counter on it.) - // When Pharagax Giant enters the battlefield, if tribute wasn't paid, Pharagax Giant deals 5 damage to each opponent. - addCard(Zone.GRAVEYARD, playerB, "Pharagax Giant"); // Creature 3/3 - - attack(1, playerA, "Ink-Eyes, Servant of Oni"); - setChoice(playerB, "Yes"); // pay tribute - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertTapped("Ink-Eyes, Servant of Oni", true); - assertLife(playerB, 15); - - assertPermanentCount(playerA, "Pharagax Giant", 1); - assertPowerToughness(playerA, "Pharagax Giant", 5, 5); - - } - -} + +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class TributeTest extends CardTestPlayerBase { + + /** + * When returning a Pharagax Giant from the graveyard to the battlefield via + * the ability of Ink-Eyes, Servant of Oni the Tribute mechanic did not + * trigger, even though it says 'enters the battlefield' and not 'cast' on + * the card. + */ + @Test + public void testPharagaxGiant() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + // Ninjutsu {3}{B}{B} ({3}{B}{B}, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking.) + // Whenever Ink-Eyes, Servant of Oni deals combat damage to a player, you may put target creature card from that player's graveyard onto the battlefield under your control. + // {1}{B}: Regenerate Ink-Eyes. + addCard(Zone.BATTLEFIELD, playerA, "Ink-Eyes, Servant of Oni"); // 5/4 + + // Tribute 2 ((As this creature enters the battlefield, an opponent of your choice may put two +1/+1 counter on it.) + // When Pharagax Giant enters the battlefield, if tribute wasn't paid, Pharagax Giant deals 5 damage to each opponent. + addCard(Zone.GRAVEYARD, playerB, "Pharagax Giant"); // Creature 3/3 + + attack(1, playerA, "Ink-Eyes, Servant of Oni"); + setChoice(playerB, "Yes"); // pay tribute + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertTapped("Ink-Eyes, Servant of Oni", true); + assertLife(playerB, 15); + + assertPermanentCount(playerA, "Pharagax Giant", 1); + assertPowerToughness(playerA, "Pharagax Giant", 5, 5); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java new file mode 100644 index 0000000000..cdb534c3f3 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java @@ -0,0 +1,78 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.abilities.oneshot; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class ConditionalOneShotEffectTest extends CardTestPlayerBase { + + @Test + public void testDisintegrationWithoutArtifact() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // Destroy target creature. If you control an artifact, Unlicensed Disintegration deals 3 damage to that creature's controller. + addCard(Zone.HAND, playerA, "Unlicensed Disintegration", 1); // Instant {1}{B}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Unlicensed Disintegration", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + } + + /** + * Noticed that everytime that i succesfully cast Unlicensed Disintigration + * with an artifact on the board the opponent wont lose 3 life. The creature + * dies but the last piece of text does not work + */ + @Test + public void testDisintegrationWithArtifact() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + // Whenever a player casts a red spell, you may gain 1 life. + addCard(Zone.BATTLEFIELD, playerA, "Dragon's Claw", 1); + + // Destroy target creature. If you control an artifact, Unlicensed Disintegration deals 3 damage to that creature's controller. + addCard(Zone.HAND, playerA, "Unlicensed Disintegration", 1); // Instant {1}{B}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Silvercoat Lion"); + + setChoice(playerA, "Yes"); // Get life from Dragon's Claw + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Unlicensed Disintegration", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + assertLife(playerA, 21); + assertLife(playerB, 17); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterspellTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterspellTest.java index c0674b42b4..8812ef5bc4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterspellTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterspellTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.abilities.oneshot.counterspell; import mage.constants.PhaseStep; @@ -10,29 +9,26 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ - public class CounterspellTest extends CardTestPlayerBase { /** - It looks like Boom//Bust can't be countered (although it says it's countered in the log). - - Code: Select all - 13:10: Benno casts Boom [8ce] targeting Mountain [4c8] Island [80c] - 13:10: Benno casts Counterspell [2b7] targeting Boom [8ce] - 13:10: Benno puts Boom [8ce] from stack into their graveyard - 13:10: Boom is countered by Counterspell [2b7] - 13:10: Benno puts Counterspell [2b7] from stack into their graveyard - 13:10: Mountain [4c8] was destroyed - 13:10: Island [80c] was destroyed + * It looks like Boom//Bust can't be countered (although it says it's + * countered in the log). + * + * Code: Select all 13:10: Benno casts Boom [8ce] targeting Mountain [4c8] + * Island [80c] 13:10: Benno casts Counterspell [2b7] targeting Boom [8ce] + * 13:10: Benno puts Boom [8ce] from stack into their graveyard 13:10: Boom + * is countered by Counterspell [2b7] 13:10: Benno puts Counterspell [2b7] + * from stack into their graveyard 13:10: Mountain [4c8] was destroyed + * 13:10: Island [80c] was destroyed */ - @Test public void testCounterSplitSpell() { // Boom - Sorcery {1}{R} - // Destroy target land you control and target land you don't control. + // Destroy target land you control and target land you don't control. addCard(Zone.HAND, playerA, "Boom // Bust"); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); - + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); addCard(Zone.HAND, playerB, "Counterspell"); @@ -44,11 +40,134 @@ public class CounterspellTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Boom // Bust", 1); assertGraveyardCount(playerB, "Counterspell", 1); - + assertPermanentCount(playerA, "Mountain", 2); assertPermanentCount(playerB, "Island", 2); - + assertLife(playerA, 20); assertLife(playerB, 20); } + + /** + * https://github.com/magefree/mage/issues/6823 + * + * I cast Lightning Bolt, then tried to counter it with Memory Lapse, then + * Twincast the Memory Lapse, using the copy to counter the original. + * + * Expected result: Original Memory Lapse countered, put on top of library; + * Lightning Bolt resolves + * + * Actual Result: Original Memory Lapse says it gets countered, but still + * resolves, Lightning Bolt is countered and put on top of library + * + * Of particular note, both the original and the copy of Memory Lapse have + * the same card ID (6b5), which seems likely to cause issues. + */ + @Test + public void testCopyCounterToCounter() { + // Lightning Bolt deals 3 damage to any target. + addCard(Zone.HAND, playerA, "Lightning Bolt"); + // Copy target instant or sorcery spell. You may choose new targets for the copy. + addCard(Zone.HAND, playerA, "Twincast"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + // Counter target spell. If that spell is countered this way, put it on top of its owner's library instead of into that player's graveyard. + addCard(Zone.HAND, playerB, "Memory Lapse"); // Instant {1}{U} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Memory Lapse", "Lightning Bolt"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Twincast", "Memory Lapse"); + + setChoice(playerA, "Yes"); // change the target + addTarget(playerA, "Memory Lapse"); + + setStrictChooseMode(true); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertGraveyardCount(playerA, "Twincast", 1); + + assertLibraryCount(playerB, "Memory Lapse", 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + } + + @Test + public void testCopyCounterToCounterGraveyard() { + // Lightning Bolt deals 3 damage to any target. + addCard(Zone.HAND, playerA, "Lightning Bolt"); + // Copy target instant or sorcery spell. You may choose new targets for the copy. + addCard(Zone.HAND, playerA, "Twincast"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + // Counter target spell + addCard(Zone.HAND, playerB, "Counterspell"); // Instant {1}{U} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell", "Lightning Bolt"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Twincast", "Counterspell"); + + setChoice(playerA, "Yes"); // change the target + addTarget(playerA, "Counterspell"); + + setStrictChooseMode(true); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertGraveyardCount(playerA, "Twincast", 1); + + assertGraveyardCount(playerB, "Counterspell", 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + } + + @Test + public void testCopyCounterToCounterExile() { + // Lightning Bolt deals 3 damage to any target. + addCard(Zone.HAND, playerA, "Lightning Bolt"); + // Copy target instant or sorcery spell. You may choose new targets for the copy. + addCard(Zone.HAND, playerA, "Twincast"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 3); + // CCounter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. + addCard(Zone.HAND, playerB, "Dissipate"); // Instant {1}{U} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Dissipate", "Lightning Bolt"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Twincast", "Dissipate"); + + setChoice(playerA, "Yes"); // change the target + addTarget(playerA, "Dissipate"); + + setStrictChooseMode(true); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertGraveyardCount(playerA, "Twincast", 1); + + assertExileCount(playerB, "Dissipate", 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/DeflectingPalmTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/DeflectingPalmTest.java deleted file mode 100644 index 172daea20d..0000000000 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/DeflectingPalmTest.java +++ /dev/null @@ -1,54 +0,0 @@ - -package org.mage.test.cards.abilities.oneshot.damage; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class DeflectingPalmTest extends CardTestPlayerBase { - - /** - * Test that prevented damage will be created with the correct source and - * will trigger the ability of Satyr Firedance - * https://github.com/magefree/mage/issues/804 - */ - - @Test - public void testDamageInPlayer() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Plains"); - // The next time a source of your choice would deal damage to you this turn, prevent that damage. - // If damage is prevented this way, Deflecting Palm deals that much damage to that source's controller. - addCard(Zone.HAND, playerA, "Deflecting Palm"); - // Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals - // that much damage to target creature that player controls. - addCard(Zone.BATTLEFIELD, playerA, "Satyr Firedancer"); - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - addCard(Zone.BATTLEFIELD, playerB, "Mountain"); - addCard(Zone.HAND, playerB, "Lightning Bolt"); - - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB ,"Lightning Bolt", playerA); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA ,"Deflecting Palm", null, "Lightning Bolt"); - setChoice(playerA, "Lightning Bolt"); - addTarget(playerA, "Silvercoat Lion"); // target for Satyr Firedancer - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Deflecting Palm", 1); - assertGraveyardCount(playerB, "Lightning Bolt", 1); - - - assertGraveyardCount(playerB, "Silvercoat Lion", 1); - - assertLife(playerA, 20); - assertLife(playerB, 17); - } -} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/IzzetStaticasterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/IzzetStaticasterTest.java index b911b98ef6..df86ccc624 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/IzzetStaticasterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/IzzetStaticasterTest.java @@ -1,40 +1,40 @@ - -package org.mage.test.cards.abilities.oneshot.damage; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class IzzetStaticasterTest extends CardTestPlayerBase { - - /** - * Izzet Staticaster killed 2 Birds of Paradise under my opponent control - * but it didn't kill my own Birds of Paradise. I remember this worked good - * on XMage but this time it just did not. Is it possibile because the 3 - * Birds come from different sets? (my opponent had those Ravnica, while - * mine was from M12). - */ - @Test - public void testAllReceiveDamage() { - // Flash (You may cast this spell any time you could cast an instant.) - // Haste - // {T}: Izzet Staticaster deals 1 damage to target creature and each other creature with the same name as that creature. - addCard(Zone.BATTLEFIELD, playerA, "Izzet Staticaster"); - addCard(Zone.BATTLEFIELD, playerA, "Birds of Paradise", 2); - addCard(Zone.BATTLEFIELD, playerB, "Birds of Paradise", 2); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {this} deals", "Birds of Paradise"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Birds of Paradise", 2); - assertGraveyardCount(playerB, "Birds of Paradise", 2); - - } -} + +package org.mage.test.cards.abilities.oneshot.damage; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class IzzetStaticasterTest extends CardTestPlayerBase { + + /** + * Izzet Staticaster killed 2 Birds of Paradise under my opponent control + * but it didn't kill my own Birds of Paradise. I remember this worked good + * on XMage but this time it just did not. Is it possibile because the 3 + * Birds come from different sets? (my opponent had those Ravnica, while + * mine was from M12). + */ + @Test + public void testAllReceiveDamage() { + // Flash (You may cast this spell any time you could cast an instant.) + // Haste + // {T}: Izzet Staticaster deals 1 damage to target creature and each other creature with the same name as that creature. + addCard(Zone.BATTLEFIELD, playerA, "Izzet Staticaster"); + addCard(Zone.BATTLEFIELD, playerA, "Birds of Paradise", 2); + addCard(Zone.BATTLEFIELD, playerB, "Birds of Paradise", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {this} deals", "Birds of Paradise"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Birds of Paradise", 2); + assertGraveyardCount(playerB, "Birds of Paradise", 2); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/RuneflareTrapTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/RuneflareTrapTest.java index 5adec57b27..02d2e98251 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/RuneflareTrapTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/RuneflareTrapTest.java @@ -1,38 +1,38 @@ - -package org.mage.test.cards.abilities.oneshot.damage; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class RuneflareTrapTest extends CardTestPlayerBase { - - /** - * Runeflare Trap counts the starting hand as card draw for the purpose of - * enabling its alternative casting cost, which allows for a turn 1 cast for - * 1 red mana dealing up to 7 damage. - */ - @Test - public void testDamageFromInstantToPlayer() { - // If an opponent drew three or more cards this turn, you may pay {R} rather than pay Runeflare Trap's mana cost. - // Runeflare Trap deals damage to target player equal to the number of cards in that player's hand. - addCard(Zone.HAND, playerB, "Runeflare Trap"); - addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Runeflare Trap", playerA); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertLife(playerA, 20); - assertLife(playerB, 20); - - assertHandCount(playerB, "Runeflare Trap", 1); - } - -} + +package org.mage.test.cards.abilities.oneshot.damage; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class RuneflareTrapTest extends CardTestPlayerBase { + + /** + * Runeflare Trap counts the starting hand as card draw for the purpose of + * enabling its alternative casting cost, which allows for a turn 1 cast for + * 1 red mana dealing up to 7 damage. + */ + @Test + public void testDamageFromInstantToPlayer() { + // If an opponent drew three or more cards this turn, you may pay {R} rather than pay Runeflare Trap's mana cost. + // Runeflare Trap deals damage to target player equal to the number of cards in that player's hand. + addCard(Zone.HAND, playerB, "Runeflare Trap"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Runeflare Trap", playerA); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertHandCount(playerB, "Runeflare Trap", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java index 5491a9b7bc..779479ecd4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java @@ -1,8 +1,8 @@ - package org.mage.test.cards.abilities.oneshot.damage; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.game.GameException; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -10,12 +10,11 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ - public class SatyrFiredancerTest extends CardTestPlayerBase { @Test public void testDamageFromInstantToPlayer() { - // Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals that much damage to target creature that player controls. + // Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals that much damage to target creature that player controls. addCard(Zone.BATTLEFIELD, playerA, "Satyr Firedancer"); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); addCard(Zone.HAND, playerA, "Lightning Bolt"); @@ -30,20 +29,20 @@ public class SatyrFiredancerTest extends CardTestPlayerBase { assertLife(playerA, 20); assertLife(playerB, 17); - + assertGraveyardCount(playerA, "Lightning Bolt", 1); assertGraveyardCount(playerB, "Silvercoat Lion", 1); } @Test public void testDamageFromAttackWontTrigger() { - // Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals that much damage to target creature that player controls. + // Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals that much damage to target creature that player controls. addCard(Zone.BATTLEFIELD, playerA, "Satyr Firedancer"); addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox", 1); addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - attack(1, playerA, "Pillarfield Ox"); + attack(1, playerA, "Pillarfield Ox"); addTarget(playerA, "Silvercoat Lion"); setStopAt(1, PhaseStep.END_TURN); @@ -51,28 +50,87 @@ public class SatyrFiredancerTest extends CardTestPlayerBase { assertLife(playerA, 20); assertLife(playerB, 18); - + assertGraveyardCount(playerB, "Silvercoat Lion", 0); assertPermanentCount(playerB, "Silvercoat Lion", 1); } - @Test public void testDamageFromOtherCreature() { - // Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals that much damage to target creature that player controls. + // Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals that much damage to target creature that player controls. addCard(Zone.BATTLEFIELD, playerA, "Satyr Firedancer"); // {T}: Prodigal Pyromancer deals 1 damage to any target. addCard(Zone.BATTLEFIELD, playerA, "Prodigal Pyromancer", 1); - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", playerB); - addTarget(playerA, playerB); - + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", playerB); + addTarget(playerA, playerB); + setStopAt(3, PhaseStep.END_TURN); execute(); assertLife(playerA, 20); assertLife(playerB, 19); - + + } + + @Test + public void testPriceOfProgressMultiplayer() throws GameException { + playerC = createPlayer(currentGame, playerC, "PlayerC"); + addCard(Zone.BATTLEFIELD, playerA, "Satyr Firedancer", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // Price of Progress deals damage to each player equal to twice the number of nonbasic lands that player controls. + addCard(Zone.HAND, playerA, "Price of Progress", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Taiga", 1); + addCard(Zone.BATTLEFIELD, playerB, "Swab Goblin", 1); + addCard(Zone.BATTLEFIELD, playerC, "Savannah", 1); + addCard(Zone.BATTLEFIELD, playerC, "Grizzly Bears", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Price of Progress"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + execute(); + + assertPermanentCount(playerB, "Swab Goblin", 0); + assertGraveyardCount(playerB, "Swab Goblin", 1); + + assertPermanentCount(playerC, "Grizzly Bears", 0); + assertGraveyardCount(playerC, "Grizzly Bears", 1); + } + + @Test + public void testMultipleInstanceOfDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Satyr Firedancer", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, "Fiery Confluence", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Taiga", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Bear Cub", 1); + addCard(Zone.BATTLEFIELD, playerB, "Forest Bear", 1); + addCard(Zone.BATTLEFIELD, playerB, "Runeclaw Bear", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fiery Confluence"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "2"); + + addTarget(playerA, "Grizzly Bears"); + addTarget(playerA, "Bear Cub"); + addTarget(playerA, "Forest Bear"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + execute(); + + assertPermanentCount(playerB, "Grizzly Bears", 0); + assertPermanentCount(playerB, "Bear Cub", 0); + assertPermanentCount(playerB, "Forest Bear", 0); + assertPermanentCount(playerB, "Runeclaw Bear", 1); + + assertGraveyardCount(playerB, "Grizzly Bears", 1); + assertGraveyardCount(playerB, "Bear Cub", 1); + assertGraveyardCount(playerB, "Forest Bear", 1); + assertGraveyardCount(playerB, "Runeclaw Bear", 0); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SoulBurnTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SoulBurnTest.java index 29140f0313..017d4715f6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SoulBurnTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SoulBurnTest.java @@ -1,179 +1,179 @@ -package org.mage.test.cards.abilities.oneshot.damage; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * @author Johnny E. Hastings - */ -public class SoulBurnTest extends CardTestPlayerBase { - - @Test - public void testDamageOpponentAllBlackMana() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.HAND, playerA, "Soul Burn"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", playerB); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertLife(playerA, 22); - assertLife(playerB, 18); - } - - @Test - public void testDamageOpponentOneBlackFourRedMana() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.HAND, playerA, "Soul Burn"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", playerB); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertLife(playerA, 20); - assertLife(playerB, 18); - } - - @Test - public void testDamageOpponentAllKindsOfMana() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Island"); - addCard(Zone.BATTLEFIELD, playerA, "Plains"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.HAND, playerA, "Soul Burn"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", playerB); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertLife(playerA, 21); - assertLife(playerB, 17); - } - - @Test - public void testDamageSelfAllSwamps() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.HAND, playerA, "Soul Burn"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", playerA); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertLife(playerA, 20); - assertLife(playerB, 20); - } - - @Test - public void testDamageSelfWithSwampsAndMountains() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.HAND, playerA, "Soul Burn"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", playerA); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertLife(playerA, 18); - assertLife(playerB, 20); - } - - @Test - public void testDamageSmallCreatureAllSwamps() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.HAND, playerA, "Soul Burn"); - - addCard(Zone.BATTLEFIELD, playerB, "Bog Imp"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", "Bog Imp"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertPermanentCount(playerB, "Bog Imp", 0); - assertLife(playerA, 21); - assertLife(playerB, 20); - } - - @Test - public void testDamageSmallCreatureSwampsAndMountains() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.HAND, playerA, "Soul Burn"); - - addCard(Zone.BATTLEFIELD, playerB, "Bog Imp"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", "Bog Imp"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertPermanentCount(playerB, "Bog Imp", 0); - assertLife(playerA, 20); - assertLife(playerB, 20); - } - - @Test - public void testDamageBigCreatureAllSwamps() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.HAND, playerA, "Soul Burn"); - - addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", "Craw Wurm"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertPermanentCount(playerB, "Craw Wurm", 1); - assertLife(playerA, 22); - assertLife(playerB, 20); - } - - @Test - public void testDamageBigCreatureSwampsAndMountains() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - addCard(Zone.HAND, playerA, "Soul Burn"); - - addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", "Craw Wurm"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertPermanentCount(playerB, "Craw Wurm", 1); - assertLife(playerA, 21); - assertLife(playerB, 20); - } - -} +package org.mage.test.cards.abilities.oneshot.damage; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Johnny E. Hastings + */ +public class SoulBurnTest extends CardTestPlayerBase { + + @Test + public void testDamageOpponentAllBlackMana() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.HAND, playerA, "Soul Burn"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", playerB); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertLife(playerA, 22); + assertLife(playerB, 18); + } + + @Test + public void testDamageOpponentOneBlackFourRedMana() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.HAND, playerA, "Soul Burn"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", playerB); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertLife(playerA, 20); + assertLife(playerB, 18); + } + + @Test + public void testDamageOpponentAllKindsOfMana() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Island"); + addCard(Zone.BATTLEFIELD, playerA, "Plains"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.HAND, playerA, "Soul Burn"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", playerB); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertLife(playerA, 21); + assertLife(playerB, 17); + } + + @Test + public void testDamageSelfAllSwamps() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.HAND, playerA, "Soul Burn"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", playerA); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertLife(playerA, 20); + assertLife(playerB, 20); + } + + @Test + public void testDamageSelfWithSwampsAndMountains() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.HAND, playerA, "Soul Burn"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", playerA); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertLife(playerA, 18); + assertLife(playerB, 20); + } + + @Test + public void testDamageSmallCreatureAllSwamps() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.HAND, playerA, "Soul Burn"); + + addCard(Zone.BATTLEFIELD, playerB, "Bog Imp"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", "Bog Imp"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertPermanentCount(playerB, "Bog Imp", 0); + assertLife(playerA, 21); + assertLife(playerB, 20); + } + + @Test + public void testDamageSmallCreatureSwampsAndMountains() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.HAND, playerA, "Soul Burn"); + + addCard(Zone.BATTLEFIELD, playerB, "Bog Imp"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", "Bog Imp"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertPermanentCount(playerB, "Bog Imp", 0); + assertLife(playerA, 20); + assertLife(playerB, 20); + } + + @Test + public void testDamageBigCreatureAllSwamps() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.HAND, playerA, "Soul Burn"); + + addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", "Craw Wurm"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertPermanentCount(playerB, "Craw Wurm", 1); + assertLife(playerA, 22); + assertLife(playerB, 20); + } + + @Test + public void testDamageBigCreatureSwampsAndMountains() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.HAND, playerA, "Soul Burn"); + + addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", "Craw Wurm"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertPermanentCount(playerB, "Craw Wurm", 1); + assertLife(playerA, 21); + assertLife(playerB, 20); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/UnlicensedDisintegrationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/UnlicensedDisintegrationTest.java new file mode 100644 index 0000000000..4e0469f3cb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/UnlicensedDisintegrationTest.java @@ -0,0 +1,128 @@ +package org.mage.test.cards.abilities.oneshot.destroy; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + + +/* +Unlicensed Disintigration - Hi! Noticed that everytime that i succesfully cast +Unlicensed Disintigration with an artifact on the board the opponent wont lose 3 life. +The creature dies but the last piece of text does not work (teM, 2020-02-24 15:17:36) +*/ +public class UnlicensedDisintegrationTest extends CardTestPlayerBase{ + + /* + Unlicensed Disintegration {1}{B}{R} + + Destroy target creature. If you control an artifact, + Unlicensed Disintegration deals 3 damage to that creature's controller. + + + Avacyn, Angel of Hope {5}{W}{W} + + Flying, vigilance, indestructible + Other permanents you control have indestructible. + */ + + @Test + public void testDestroyCreatureLifeLoss(){ + addCard(Zone.HAND, playerA, "Unlicensed Disintegration"); + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp",2); + + // Need an artifact to trigger the damage + addCard(Zone.BATTLEFIELD, playerA, "Sol Ring"); + + // Play Unlicensed Disintegration, targeting Balduvian Bears + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Balduvian Bears"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertGraveyardCount(playerA, "Unlicensed Disintegration", 1); + assertLife(playerB, 17); + assertGraveyardCount(playerB, "Balduvian Bears", 1); + } + + @Test + public void testDestroyCreatureLifeLossIndestructible(){ + addCard(Zone.HAND, playerA, "Unlicensed Disintegration"); + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears"); + addCard(Zone.BATTLEFIELD, playerB, "Avacyn, Angel of Hope"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp",2); + + // Need an artifact to trigger the damage + addCard(Zone.BATTLEFIELD, playerA, "Sol Ring"); + + // Play Unlicensed Disintegration, targeting Balduvian Bears + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Balduvian Bears"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertGraveyardCount(playerA, "Unlicensed Disintegration", 1); + + assertLife(playerB, 17); + assertPermanentCount(playerB, "Balduvian Bears", 1); + assertPermanentCount(playerB, "Avacyn, Angel of Hope", 1); + } + + @Test + public void testDestroyCreatureNoLifeLossNoArtifact(){ + addCard(Zone.HAND, playerA, "Unlicensed Disintegration"); + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp",2); + + // Play Unlicensed Disintegration, targeting Balduvian Bears + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Balduvian Bears"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertGraveyardCount(playerA, "Unlicensed Disintegration", 1); + assertLife(playerB, 20); + assertGraveyardCount(playerB, "Balduvian Bears", 1); + } + + @Test + public void testDestroyCreatureNoLifeLossNoArtifactIndestructible(){ + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp",2); + addCard(Zone.HAND, playerA, "Unlicensed Disintegration"); + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears"); + addCard(Zone.BATTLEFIELD, playerB, "Avacyn, Angel of Hope"); + + // Play Unlicensed Disintegration, targeting Balduvian Bears + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Balduvian Bears"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertGraveyardCount(playerA, "Unlicensed Disintegration", 1); + + assertLife(playerB, 20); + assertPermanentCount(playerB, "Balduvian Bears", 1); + assertPermanentCount(playerB, "Avacyn, Angel of Hope", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/library/PutToLibraryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/library/PutToLibraryTest.java index fbaf5f07ad..6867ebb018 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/library/PutToLibraryTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/library/PutToLibraryTest.java @@ -1,76 +1,76 @@ -package org.mage.test.cards.abilities.oneshot.library; - -import java.util.ArrayList; -import mage.cards.Card; -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Assert; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class PutToLibraryTest extends CardTestPlayerBase { - - @Test - public void testPutSecondFromTop() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); - // Put target creature into its owner's library second from the top. Its controller gains 3 life. - addCard(Zone.HAND, playerA, "Oust"); // Sorcery {W} - - addCard(Zone.BATTLEFIELD, playerB, "Dread Wanderer"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Oust", "Dread Wanderer"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Oust", 1); - assertLibraryCount(playerB, "Dread Wanderer", 1); - - assertLife(playerA, 20); - assertLife(playerB, 23); - - ArrayList cardArray = new ArrayList<>(playerB.getLibrary().getCards(currentGame)); - Assert.assertTrue("Library has no cards but should have", cardArray.size() > 1); - Card secondCard = cardArray.get(1); - Assert.assertTrue("Second card from top should be Dread Wanderer, but it isn't", - secondCard != null && secondCard.getName().equals("Dread Wanderer")); - - } - - // Unexpectedly Absent doesn't work properly, no matter how much you pay for X the card is always returned on top of the library. - @Test - public void testUnexpectedlyAbsent() { - // Flying - // At the beginning of combat on your turn, you may pay {G}{U}. When you do, put a +1/+1 counter on another target creature you control, and that creature gains flying until end of turn. - addCard(Zone.BATTLEFIELD, playerA, "Skyrider Patrol", 1); - - // Put target nonland permanent into its owner's library just beneath the top X cards of that library. - addCard(Zone.HAND, playerB, "Unexpectedly Absent"); // Instant {X}{W}{W} - addCard(Zone.BATTLEFIELD, playerB, "Plains", 5); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Unexpectedly Absent", "Skyrider Patrol"); - setChoice(playerB, "X=3"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerB, "Unexpectedly Absent", 1); - assertPermanentCount(playerA, "Skyrider Patrol", 0); - assertLibraryCount(playerA, "Skyrider Patrol", 1); - - ArrayList cardArray = new ArrayList<>(playerA.getLibrary().getCards(currentGame)); - Assert.assertTrue("Library has no cards but should have", cardArray.size() > 3); - Card fourthCard = cardArray.get(3);// get the 4th element - Assert.assertTrue("Fourth card from top should be Skyrider Patrol, but it isn't", - fourthCard != null && fourthCard.getName().equals("Skyrider Patrol")); - - assertLife(playerA, 20); - assertLife(playerB, 20); - - } - -} +package org.mage.test.cards.abilities.oneshot.library; + +import java.util.ArrayList; +import mage.cards.Card; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class PutToLibraryTest extends CardTestPlayerBase { + + @Test + public void testPutSecondFromTop() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + // Put target creature into its owner's library second from the top. Its controller gains 3 life. + addCard(Zone.HAND, playerA, "Oust"); // Sorcery {W} + + addCard(Zone.BATTLEFIELD, playerB, "Dread Wanderer"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Oust", "Dread Wanderer"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Oust", 1); + assertLibraryCount(playerB, "Dread Wanderer", 1); + + assertLife(playerA, 20); + assertLife(playerB, 23); + + ArrayList cardArray = new ArrayList<>(playerB.getLibrary().getCards(currentGame)); + Assert.assertTrue("Library has no cards but should have", cardArray.size() > 1); + Card secondCard = cardArray.get(1); + Assert.assertTrue("Second card from top should be Dread Wanderer, but it isn't", + secondCard != null && secondCard.getName().equals("Dread Wanderer")); + + } + + // Unexpectedly Absent doesn't work properly, no matter how much you pay for X the card is always returned on top of the library. + @Test + public void testUnexpectedlyAbsent() { + // Flying + // At the beginning of combat on your turn, you may pay {G}{U}. When you do, put a +1/+1 counter on another target creature you control, and that creature gains flying until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Skyrider Patrol", 1); + + // Put target nonland permanent into its owner's library just beneath the top X cards of that library. + addCard(Zone.HAND, playerB, "Unexpectedly Absent"); // Instant {X}{W}{W} + addCard(Zone.BATTLEFIELD, playerB, "Plains", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Unexpectedly Absent", "Skyrider Patrol"); + setChoice(playerB, "X=3"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, "Unexpectedly Absent", 1); + assertPermanentCount(playerA, "Skyrider Patrol", 0); + assertLibraryCount(playerA, "Skyrider Patrol", 1); + + ArrayList cardArray = new ArrayList<>(playerA.getLibrary().getCards(currentGame)); + Assert.assertTrue("Library has no cards but should have", cardArray.size() > 3); + Card fourthCard = cardArray.get(3);// get the 4th element + Assert.assertTrue("Fourth card from top should be Skyrider Patrol, but it isn't", + fourthCard != null && fourthCard.getName().equals("Skyrider Patrol")); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/optional/IdentityThiefTests.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/optional/IdentityThiefTests.java index 668521023a..0f10103563 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/optional/IdentityThiefTests.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/optional/IdentityThiefTests.java @@ -1,39 +1,39 @@ -package org.mage.test.cards.abilities.optional; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -public class IdentityThiefTests extends CardTestPlayerBase { - - @Test - public void shouldntExileIfAbilityDeclined() { - addCard(Zone.BATTLEFIELD, playerA, "Identity Thief"); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - - attack(1, playerA, "Identity Thief"); - setChoice(playerA, "No"); - - setStopAt(1, PhaseStep.DECLARE_BLOCKERS); - execute(); - - assertExileCount("Silvercoat Lion", 0); - assertPermanentCount(playerB, "Silvercoat Lion", 1); - } - - @Test - public void shouldExileIfAbilityChosen() { - addCard(Zone.BATTLEFIELD, playerA, "Identity Thief"); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - - attack(1, playerA, "Identity Thief"); - setChoice(playerA, "Yes"); - - setStopAt(1, PhaseStep.DECLARE_BLOCKERS); - execute(); - - assertExileCount("Silvercoat Lion", 1); - assertPermanentCount(playerB, "Silvercoat Lion", 0); - } -} +package org.mage.test.cards.abilities.optional; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class IdentityThiefTests extends CardTestPlayerBase { + + @Test + public void shouldntExileIfAbilityDeclined() { + addCard(Zone.BATTLEFIELD, playerA, "Identity Thief"); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + attack(1, playerA, "Identity Thief"); + setChoice(playerA, "No"); + + setStopAt(1, PhaseStep.DECLARE_BLOCKERS); + execute(); + + assertExileCount("Silvercoat Lion", 0); + assertPermanentCount(playerB, "Silvercoat Lion", 1); + } + + @Test + public void shouldExileIfAbilityChosen() { + addCard(Zone.BATTLEFIELD, playerA, "Identity Thief"); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + attack(1, playerA, "Identity Thief"); + setChoice(playerA, "Yes"); + + setStopAt(1, PhaseStep.DECLARE_BLOCKERS); + execute(); + + assertExileCount("Silvercoat Lion", 1); + assertPermanentCount(playerB, "Silvercoat Lion", 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java index 1d45c86ac0..f5dd0a2ee9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java @@ -1,90 +1,90 @@ - -package org.mage.test.cards.abilities.other; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Ignore; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author jeffwadsworth - */ -public class AuratouchedMageTest extends CardTestPlayerBase { - - /** - * Auratouched Mage Creature — Human Wizard 3/3, 5W When Auratouched Mage - * enters the battlefield, search your library for an Aura card that could - * enchant it. If Auratouched Mage is still on the battlefield, put that - * Aura card onto the battlefield attached to it. Otherwise, reveal the Aura - * card and put it into your hand. Then shuffle your library. - * - */ - - //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice. - @Ignore - public void testAuratouchedMageEffectHasMadeIntoTypeArtifact() { - //Any Aura card you find must be able to enchant Auratouched Mage as it currently exists, or as it most recently existed on the battlefield if it's no - //longer on the battlefield. If an effect has made the Mage an artifact, for example, you could search for an Aura with “enchant artifact.” - //Expected result: An effect has made Auratouched Mage into an artifact upon entering the battlefield. An aura that only works on artifacts should work. - addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); - addCard(Zone.BATTLEFIELD, playerA, "Island", 7); - addCard(Zone.HAND, playerA, "Auratouched Mage"); //5W cost - addCard(Zone.HAND, playerA, "Argent Mutation"); //2U cost. Target is an artifact until end of turn - addCard(Zone.LIBRARY, playerA, "Relic Ward"); //Only enchants an artifact permanent - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Auratouched Mage"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Argent Mutation", "Auratouched Mage"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - - execute(); - - assertPermanentCount(playerA, "Auratouched Mage", 1); - assertPermanentCount(playerA, "Relic Ward", 1); - - } - - @Test - public void testGainsLegalAura() { - // Expected result: Brainwash gets placed on Auratouched Mage - addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); - addCard(Zone.HAND, playerA, "Auratouched Mage"); - addCard(Zone.LIBRARY, playerA, "Brainwash");//legal aura for Auratouched Mage - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Auratouched Mage"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - - execute(); - - assertPermanentCount(playerA, "Auratouched Mage", 1); - assertPermanentCount(playerA, "Brainwash", 1); - - } - - //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice. - @Test - public void testAuratouchedMageNotOnBattlefield() { - // Expected result: Auratouched Mage is exiled immediately after entering the battlefield, the legal aura (Brainwash) gets put into controller's hand - addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); - addCard(Zone.HAND, playerA, "Auratouched Mage"); - addCard(Zone.HAND, playerA, "Swords to Plowshares"); //exiles Auratouched Mage - addCard(Zone.LIBRARY, playerA, "Brainwash"); //valid aura for Auratouched Mage - addCard(Zone.LIBRARY, playerA, "Animate Wall"); //not a valid aura for the Auratouched Mage - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Auratouched Mage"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swords to Plowshares", "Auratouched Mage"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - - execute(); - - assertPermanentCount(playerA, "Auratouched Mage", 0); - assertPermanentCount(playerA, "Brainwash", 0); - assertHandCount(playerA, "Brainwash", 1); - assertLibraryCount(playerA, "Animate Wall", 1); - - } -} + +package org.mage.test.cards.abilities.other; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author jeffwadsworth + */ +public class AuratouchedMageTest extends CardTestPlayerBase { + + /** + * Auratouched Mage Creature — Human Wizard 3/3, 5W When Auratouched Mage + * enters the battlefield, search your library for an Aura card that could + * enchant it. If Auratouched Mage is still on the battlefield, put that + * Aura card onto the battlefield attached to it. Otherwise, reveal the Aura + * card and put it into your hand. Then shuffle your library. + * + */ + + //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice. + @Ignore + public void testAuratouchedMageEffectHasMadeIntoTypeArtifact() { + //Any Aura card you find must be able to enchant Auratouched Mage as it currently exists, or as it most recently existed on the battlefield if it's no + //longer on the battlefield. If an effect has made the Mage an artifact, for example, you could search for an Aura with “enchant artifact.” + //Expected result: An effect has made Auratouched Mage into an artifact upon entering the battlefield. An aura that only works on artifacts should work. + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + addCard(Zone.HAND, playerA, "Auratouched Mage"); //5W cost + addCard(Zone.HAND, playerA, "Argent Mutation"); //2U cost. Target is an artifact until end of turn + addCard(Zone.LIBRARY, playerA, "Relic Ward"); //Only enchants an artifact permanent + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Auratouched Mage"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Argent Mutation", "Auratouched Mage"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertPermanentCount(playerA, "Auratouched Mage", 1); + assertPermanentCount(playerA, "Relic Ward", 1); + + } + + @Test + public void testGainsLegalAura() { + // Expected result: Brainwash gets placed on Auratouched Mage + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + addCard(Zone.HAND, playerA, "Auratouched Mage"); + addCard(Zone.LIBRARY, playerA, "Brainwash");//legal aura for Auratouched Mage + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Auratouched Mage"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertPermanentCount(playerA, "Auratouched Mage", 1); + assertPermanentCount(playerA, "Brainwash", 1); + + } + + //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice. + @Test + public void testAuratouchedMageNotOnBattlefield() { + // Expected result: Auratouched Mage is exiled immediately after entering the battlefield, the legal aura (Brainwash) gets put into controller's hand + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + addCard(Zone.HAND, playerA, "Auratouched Mage"); + addCard(Zone.HAND, playerA, "Swords to Plowshares"); //exiles Auratouched Mage + addCard(Zone.LIBRARY, playerA, "Brainwash"); //valid aura for Auratouched Mage + addCard(Zone.LIBRARY, playerA, "Animate Wall"); //not a valid aura for the Auratouched Mage + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Auratouched Mage"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swords to Plowshares", "Auratouched Mage"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertPermanentCount(playerA, "Auratouched Mage", 0); + assertPermanentCount(playerA, "Brainwash", 0); + assertHandCount(playerA, "Brainwash", 1); + assertLibraryCount(playerA, "Animate Wall", 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainAbilitiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainAbilitiesTest.java new file mode 100644 index 0000000000..03a690f696 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainAbilitiesTest.java @@ -0,0 +1,70 @@ +package org.mage.test.cards.abilities.other; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class GainAbilitiesTest extends CardTestPlayerBase { + + @Test + public void test_AttachmentSingleton() { + // {2}{W} + // Enchanted creature gets +2/+2. + // Enchanted creature has vigilance as long as you control a black or green permanent. + addCard(Zone.HAND, playerA, "Abzan Runemark@attach", 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears@bear", 1); // 2/2 + + // attach all + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.1", "@bear"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.2", "@bear"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear", VigilanceAbility.class, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + Permanent permanent = getPermanent("Balduvian Bears"); + Assert.assertEquals("must have only 1 singleton ability instance from two attachments", + 1, permanent.getAbilities(currentGame).stream().filter(a -> a instanceof VigilanceAbility).count()); + } + + @Test + public void test_AttachmentUnique() { + // {R} + // Enchanted creature has "{R}, {T}, Discard a card: Draw a card." + addCard(Zone.HAND, playerA, "Epiphany Storm@attach", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears@bear", 1); // 2/2 + + // attach all + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.1", "@bear"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.2", "@bear"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + //checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear", VigilanceAbility.class, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + Permanent permanent = getPermanent("Balduvian Bears"); + Assert.assertEquals("must have 2 dynamic ability instances from two attachments", + 2, permanent.getAbilities(currentGame).stream().filter( + a -> a.getEffects().stream().anyMatch(e -> e instanceof DrawCardSourceControllerEffect) + ).count()); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainMenaceAbilityAsSingletonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainMenaceAbilityAsSingletonTest.java new file mode 100644 index 0000000000..473f9327a3 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainMenaceAbilityAsSingletonTest.java @@ -0,0 +1,59 @@ +package org.mage.test.cards.abilities.other; + +import mage.abilities.keyword.MenaceAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class GainMenaceAbilityAsSingletonTest extends CardTestPlayerBase { + + @Test + @Ignore // enable it after MenaceAbility will be singleton, see https://github.com/magefree/mage/issues/6720 + public void test_SingletonNonMultiInstances() { + // bug: permanent shows multiple instances of one ability in card's text + // https://github.com/magefree/mage/issues/6720 + + // {3}{R}{R} + // Whenever Sethron, Hurloon General or another nontoken Minotaur enters the battlefield under your control, create a 2/3 red Minotaur creature token. + // {2}{B/R}: Minotaurs you control get +1/+0 and gain menace and haste until end of turn. ({B/R} can be paid with either {B} or {R}.) + addCard(Zone.HAND, playerA, "Sethron, Hurloon General", 7); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5 + 3 * 3); + + // prepare minotaur token + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sethron, Hurloon General"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("token", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Minotaur", 1); + + // boost 1 + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{B/R}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPT("boost 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Minotaur", 2 + 1, 3); + + // boost 2 + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{B/R}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPT("boost 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Minotaur", 2 + 1 * 2, 3); + + // boost 3 + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{B/R}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPT("boost 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Minotaur", 2 + 1 * 3, 3); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + Permanent permanent = getPermanent("Minotaur", playerA); + Assert.assertEquals("must have only 1 Menace instance", 1, permanent.getAbilities(currentGame).stream() + .filter(a -> a instanceof MenaceAbility).count()); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/ImpelledGiantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/ImpelledGiantTest.java index 61255dd588..9a0ffce1b7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/ImpelledGiantTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/ImpelledGiantTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.abilities.other; import mage.constants.PhaseStep; @@ -10,14 +9,20 @@ public class ImpelledGiantTest extends CardTestPlayerBase { @Test public void testGainsPower() { - addCard(Zone.BATTLEFIELD, playerA, "Impelled Giant"); - addCard(Zone.BATTLEFIELD, playerA, "Hurloon Minotaur"); + setStrictChooseMode(true); + // Trample + // Tap an untapped red creature you control other than Impelled Giant: Impelled Giant gets +X/+0 until end of turn, where X is the power of the creature tapped this way. + addCard(Zone.BATTLEFIELD, playerA, "Impelled Giant"); // Creature 3/3 + addCard(Zone.BATTLEFIELD, playerA, "Hurloon Minotaur"); // Creature 2/3 activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tap an untapped red creature you control other than Impelled Giant"); + setChoice(playerA, "Hurloon Minotaur"); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); + assertTapped("Hurloon Minotaur", true); assertPowerToughness(playerA, "Impelled Giant", 5, 3); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java index 8feb498747..03abb401a3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java @@ -1,5 +1,3 @@ - - package org.mage.test.cards.abilities.other; import mage.constants.PhaseStep; @@ -8,19 +6,17 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author BetaSteward */ public class PastInFlamesTest extends CardTestPlayerBase { - /** + /** * Past in Flames * Sorcery, 3R (4) - * Each instant and sorcery card in your graveyard gains flashback until end + * Each instant and sorcery card in your graveyard gains flashback until end * of turn. The flashback cost is equal to its mana cost. - * Flashback {4}{R} (You may cast this card from your graveyard for its + * Flashback {4}{R} (You may cast this card from your graveyard for its * flashback cost. Then exile it.) - * */ @Test @@ -31,7 +27,7 @@ public class PastInFlamesTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Past in Flames"); activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback"); - + setStopAt(1, PhaseStep.END_TURN); execute(); @@ -40,7 +36,6 @@ public class PastInFlamesTest extends CardTestPlayerBase { assertExileCount("Lightning Bolt", 1); assertGraveyardCount(playerA, "Lightning Bolt", 0); - } - + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SakashimaTheImpostorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SakashimaTheImpostorTest.java index 267229cccd..b6cbba80e8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SakashimaTheImpostorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SakashimaTheImpostorTest.java @@ -9,10 +9,17 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * Created by alexsandro on 06/03/17. */ public class SakashimaTheImpostorTest extends CardTestPlayerBase { + @Test public void copySpellStutterTest() { + // Flash, Flying + // When Spellstutter Sprite enters the battlefield, counter target spell with converted mana cost X or less, + // where X is the number of Faeries you control. addCard(Zone.BATTLEFIELD, playerA, "Spellstutter Sprite", 1); addCard(Zone.BATTLEFIELD, playerB, "Island", 4); + // You may have Sakashima the Impostor enter the battlefield as a copy of any creature on the battlefield, + // except its name is Sakashima the Impostor, it's legendary in addition to its other types, + // and it has "{2}{U}{U}: Return Sakashima the Impostor to its owner's hand at the beginning of the next end step." addCard(Zone.HAND, playerB, "Sakashima the Impostor", 4); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Sakashima the Impostor"); @@ -25,4 +32,42 @@ public class SakashimaTheImpostorTest extends CardTestPlayerBase { assertPowerToughness(playerB, "Sakashima the Impostor", 1, 1); } + + /** + * I played Sakashima the Imposter copying an opponents Pawn of Ulamaog. + * Sakashima gained the following ability: "Whenever Pawn of Ulamog or + * another nontoken creature you control dies, you may create a 0/1 + * colorless Eldrazi Spawn creature token. It has "Sacrifice this creature: + * Add {C}." Then Sakashima died due to combat damage and the ability did + * not trigger. + * + */ + @Test + public void copyDiesTriggeredTest() { + // Whenever Pawn of Ulamog or another nontoken creature you control dies, you may create a 0/1 colorless + // Eldrazi Spawn creature token. It has "Sacrifice this creature: Add {C}." + addCard(Zone.BATTLEFIELD, playerA, "Pawn of Ulamog", 1); // Creature 2/2 + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); // Creature 2/2 + + addCard(Zone.BATTLEFIELD, playerB, "Island", 4); + // You may have Sakashima the Impostor enter the battlefield as a copy of any creature on the battlefield, + // except its name is Sakashima the Impostor, it's legendary in addition to its other types, + // and it has "{2}{U}{U}: Return Sakashima the Impostor to its owner's hand at the beginning of the next end step." + addCard(Zone.HAND, playerB, "Sakashima the Impostor", 4); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Sakashima the Impostor"); + setChoice(playerB, "Pawn of Ulamog"); + + attack(4, playerB, "Sakashima the Impostor"); + block(4, playerA, "Silvercoat Lion", "Sakashima the Impostor"); + + setStopAt(4, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerB, "Sakashima the Impostor", 1); + + assertPermanentCount(playerA, "Eldrazi Spawn", 1); + assertPermanentCount(playerB, "Eldrazi Spawn", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/CastFromLibraryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/CastFromLibraryTest.java index 63380c9cce..99c4706b1d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/CastFromLibraryTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/CastFromLibraryTest.java @@ -1,69 +1,69 @@ -package org.mage.test.cards.asthough; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class CastFromLibraryTest extends CardTestPlayerBase { - - /** - * Any creature that you cast through Vizier of the Menagerie (ie the card - * on top of your deck) is cast-able at instant speed. This means that they - * can be cast on your opponent's turn, before you scry (and thus change the - * top card), or even right before the Vizier gets destroyed by a fatal push - * or similar. - */ - @Test - public void testVizierOfTheMenagerieWithGenericCreatures() { - addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); - - addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 2); - // You may look at the top card of your library. (You may do this at any time.) - // You may cast the top card of your library if it's a creature card. - // You may spend mana as though it were mana of any type to cast creature spells. - addCard(Zone.HAND, playerA, "Vizier of the Menagerie", 1); // Creature 3/4 {3}{G} - - skipInitShuffling(); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vizier of the Menagerie"); - castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Silvercoat Lion"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - assertPermanentCount(playerA, "Vizier of the Menagerie", 1); - assertPermanentCount(playerA, "Silvercoat Lion", 1); - assertLibraryCount(playerA, "Silvercoat Lion", 1); - - } - - @Test - public void testVizierOfTheMenagerieWithDryadArbor() { - addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); - addCard(Zone.LIBRARY, playerA, "Dryad Arbor", 2); - // You may look at the top card of your library. (You may do this at any time.) - // You may cast the top card of your library if it's a creature card. - // You may spend mana as though it were mana of any type to cast creature spells. - addCard(Zone.HAND, playerA, "Vizier of the Menagerie", 1); // Creature 3/4 {3}{G} - - skipInitShuffling(); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vizier of the Menagerie"); - castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Dryad Arbor"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Dryad Arbor"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - assertPermanentCount(playerA, "Vizier of the Menagerie", 1); - assertPermanentCount(playerA, "Dryad Arbor", 0); // can't be cast, only played - assertLibraryCount(playerA, "Dryad Arbor", 2); - - } -} +package org.mage.test.cards.asthough; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class CastFromLibraryTest extends CardTestPlayerBase { + + /** + * Any creature that you cast through Vizier of the Menagerie (ie the card + * on top of your deck) is cast-able at instant speed. This means that they + * can be cast on your opponent's turn, before you scry (and thus change the + * top card), or even right before the Vizier gets destroyed by a fatal push + * or similar. + */ + @Test + public void testVizierOfTheMenagerieWithGenericCreatures() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); + + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 2); + // You may look at the top card of your library. (You may do this at any time.) + // You may cast the top card of your library if it's a creature card. + // You may spend mana as though it were mana of any type to cast creature spells. + addCard(Zone.HAND, playerA, "Vizier of the Menagerie", 1); // Creature 3/4 {3}{G} + + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vizier of the Menagerie"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Vizier of the Menagerie", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertLibraryCount(playerA, "Silvercoat Lion", 1); + + } + + @Test + public void testVizierOfTheMenagerieWithDryadArbor() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); + addCard(Zone.LIBRARY, playerA, "Dryad Arbor", 2); + // You may look at the top card of your library. (You may do this at any time.) + // You may cast the top card of your library if it's a creature card. + // You may spend mana as though it were mana of any type to cast creature spells. + addCard(Zone.HAND, playerA, "Vizier of the Menagerie", 1); // Creature 3/4 {3}{G} + + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vizier of the Menagerie"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Dryad Arbor"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Dryad Arbor"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Vizier of the Menagerie", 1); + assertPermanentCount(playerA, "Dryad Arbor", 0); // can't be cast, only played + assertLibraryCount(playerA, "Dryad Arbor", 2); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java index 11fca407dd..7a7aa3f7b9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java @@ -3,12 +3,12 @@ package org.mage.test.cards.asthough; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; /** * @author LevelX2 */ -public class PlayFromNonHandZoneTest extends CardTestPlayerBase { +public class PlayFromNonHandZoneTest extends CardTestPlayerBaseWithAIHelps { @Test public void testWorldheartPhoenixNormal() { @@ -114,34 +114,71 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase { @Test public void testNarsetEnlightenedMasterAdditionalCost() { + skipInitShuffling(); + removeAllCardsFromLibrary(playerA); + // First strike // Hexproof // Whenever Narset, Enlightented Master attacks, exile the top four cards of your library. Until end of turn, you may cast noncreature cards exiled with Narset this turn without paying their mana costs. - skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Narset, Enlightened Master", 1); + // + // {1}{R} + // As an additional cost to cast this spell, discard two cards. + addCard(Zone.LIBRARY, playerA, "Swamp", 3); // 3 cards for draw effect + addCard(Zone.LIBRARY, playerA, "Cathartic Reunion", 1); // exile from lib + addCard(Zone.LIBRARY, playerA, "Plains", 3); // exile from lib + addCard(Zone.HAND, playerA, "Swamp", 1); + addCard(Zone.HAND, playerA, "Forest", 1); - addCard(Zone.BATTLEFIELD, playerB, "Narset, Enlightened Master", 1); - addCard(Zone.HAND, playerB, "Swamp"); - addCard(Zone.LIBRARY, playerB, "Plains"); - addCard(Zone.LIBRARY, playerB, "Plains"); - addCard(Zone.LIBRARY, playerB, "Plains"); - addCard(Zone.LIBRARY, playerB, "Cathartic Reunion"); - addCard(Zone.LIBRARY, playerB, "Forest"); + attack(1, playerA, "Narset, Enlightened Master"); - attack(2, playerB, "Narset, Enlightened Master"); + // can play for 2 discard + checkPlayableAbility("must play", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Cathartic Reunion", true); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cathartic Reunion"); + setChoice(playerA, "Swamp^Forest"); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); - castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Cathartic Reunion"); - setChoice(playerB, "Swamp^Forest"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); - assertHandCount(playerB, 3); - assertGraveyardCount(playerB, "Forest", 1); - assertGraveyardCount(playerB, "Swamp", 1); - assertGraveyardCount(playerB, "Cathartic Reunion", 1); - assertGraveyardCount(playerB, 3); - assertExileCount(playerB, "Plains", 3); - assertExileCount(playerB, 3); + assertGraveyardCount(playerA, "Swamp", 1); + assertGraveyardCount(playerA, "Forest", 1); + assertGraveyardCount(playerA, "Cathartic Reunion", 1); + } + @Test + public void testNarsetEnlightenedMasterAdditionalCost_AI() { + skipInitShuffling(); + removeAllCardsFromLibrary(playerA); + + // First strike + // Hexproof + // Whenever Narset, Enlightented Master attacks, exile the top four cards of your library. Until end of turn, you may cast noncreature cards exiled with Narset this turn without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerA, "Narset, Enlightened Master", 1); + // + // {1}{R} + // As an additional cost to cast this spell, discard two cards. + addCard(Zone.LIBRARY, playerA, "Swamp", 3); // 3 cards for draw effect + addCard(Zone.LIBRARY, playerA, "Cathartic Reunion", 1); // exile from lib + addCard(Zone.LIBRARY, playerA, "Plains", 3); // exile from lib + addCard(Zone.HAND, playerA, "Swamp", 1); + addCard(Zone.HAND, playerA, "Forest", 1); + + attack(1, playerA, "Narset, Enlightened Master"); + + // AI simulation to cast exiled card (possible bug: ai real cast uses cost payed status from one of the simulation and don't pay) + aiPlayPriority(1, PhaseStep.POSTCOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Swamp", 1); + assertGraveyardCount(playerA, "Forest", 1); + assertGraveyardCount(playerA, "Cathartic Reunion", 1); } /** @@ -173,4 +210,289 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase { } + /** + * Can't cast Karn's Temporal Sundering when exiled with Golos, Tireless + * Pilgrim + */ + @Test + public void castSunderingWithGolosTest() { + // When Golos, Tireless Pilgrim enters the battlefield, you may search your library for a land card, put that card onto the battlefield tapped, then shuffle your library. + // {2}{W}{U}{B}{R}{G}: Exile the top three cards of your library. You may play them this turn without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerA, "Golos, Tireless Pilgrim", 1); + + // (You may cast a legendary sorcery only if you control a legendary creature or planeswalker.) + // Target player takes an extra turn after this one. Return up to one target nonland permanent to its owner's hand. Exile Karn's Temporal Sundering. + addCard(Zone.LIBRARY, playerA, "Karn's Temporal Sundering"); // Sorcery + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion"); + addCard(Zone.LIBRARY, playerA, "Mountain"); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{W}{U}{B}{R}{G}: Exile"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Karn's Temporal Sundering"); + addTarget(playerA, playerA); + addTarget(playerA, "Silvercoat Lion"); + + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Mountain", 2); + assertPermanentCount(playerA, "Silvercoat Lion", 0); + assertExileCount(playerA, "Karn's Temporal Sundering", 1); + + assertHandCount(playerA, "Silvercoat Lion", 1); + assertExileCount(playerA, 1); + + assertActivePlayer(playerA); + } + + /** + * Can't cast Karn's Temporal Sundering when exiled with Golos, Tireless + * Pilgrim + */ + @Test + public void castSunderingWithGolos2Test() { + // When Golos, Tireless Pilgrim enters the battlefield, you may search your library for a land card, put that card onto the battlefield tapped, then shuffle your library. + // {2}{W}{U}{B}{R}{G}: Exile the top three cards of your library. You may play them this turn without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerA, "Golos, Tireless Pilgrim", 1); + + // (You may cast a legendary sorcery only if you control a legendary creature or planeswalker.) + // Target player takes an extra turn after this one. Return up to one target nonland permanent to its owner's hand. Exile Karn's Temporal Sundering. + addCard(Zone.LIBRARY, playerA, "Karn's Temporal Sundering"); // Sorcery + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion"); + addCard(Zone.LIBRARY, playerA, "Mountain"); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{W}{U}{B}{R}{G}: Exile"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Karn's Temporal Sundering"); + addTarget(playerA, playerA); + addTarget(playerA, "Golos, Tireless Pilgrim"); // Return to hand + + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Mountain", 2); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertExileCount(playerA, "Karn's Temporal Sundering", 1); + + assertHandCount(playerA, "Golos, Tireless Pilgrim", 1); + assertExileCount(playerA, 1); + + assertActivePlayer(playerA); + } + + /** + * #6580 + * Fallen Shinobi - In the second log, when Tormenting Voice is cast first, + * the discard was required. In the first log, when it was cast after + * Angelic Purge the discard was not required. + */ + + @Test + public void castFromExileButWithAdditionalCostTest() { + // Ninjutsu {2}{U}{B} + // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards + // of their library. Until end of turn, you may play those cards without paying their mana cost. + addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4 + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.HAND, playerB, "Pillarfield Ox"); + + addCard(Zone.LIBRARY, playerB, "Pillarfield Ox"); // Card to draw on turn 2 + // As an additional cost to cast Tormenting Voice, discard a card. + // Draw two cards. + addCard(Zone.LIBRARY, playerA, "Tormenting Voice"); // Sorcery {1}{R} + // As an additional cost to cast this spell, sacrifice a creature. + // Flying, Trample + addCard(Zone.LIBRARY, playerA, "Demon of Catastrophes"); // Creature {2}{B}{B} 6/6 + + skipInitShuffling(); + + attack(2, playerB, "Fallen Shinobi"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tormenting Voice"); + setChoice(playerB, "Pillarfield Ox"); // Discord for Tormenting Voice + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Demon of Catastrophes"); + setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Demon + + setStopAt(2, PhaseStep.END_TURN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 15); + assertPermanentCount(playerB, "Fallen Shinobi", 1); + + assertGraveyardCount(playerA, "Tormenting Voice", 1); + assertGraveyardCount(playerB, "Pillarfield Ox", 1); // Discarded for Tormenting Voice + + + assertPermanentCount(playerB, "Demon of Catastrophes", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); // sacrificed for Demon + + assertHandCount(playerB, "Pillarfield Ox", 1); + assertHandCount(playerB, 3); // 2 from Tormenting Voice + 1 from Turn 2 + assertExileCount(playerA, 0); // Both exiled cards are cast + } + + + @Test + public void castFromExileButWithAdditionalCost2Test() { + // Ninjutsu {2}{U}{B} + // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards + // of their library. Until end of turn, you may play those cards without paying their mana cost. + addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4 + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.HAND, playerB, "Pillarfield Ox"); + + addCard(Zone.BATTLEFIELD, playerA, "Amulet of Kroog"); // Just to exile for Angelic Purge + + // As an additional cost to cast Tormenting Voice, discard a card. + // Draw two cards. + addCard(Zone.LIBRARY, playerA, "Tormenting Voice"); // Sorcery {1}{R} + + // As an additional cost to cast Angelic Purge, sacrifice a permanent. + // Exile target artifact, creature, or enchantment. + addCard(Zone.LIBRARY, playerA, "Angelic Purge"); // Sorcery {2}{W} + + skipInitShuffling(); + + attack(2, playerB, "Fallen Shinobi"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Angelic Purge"); + setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Purge + addTarget(playerB, "Amulet of Kroog"); // Exile with Purge + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tormenting Voice"); + setChoice(playerB, "Pillarfield Ox"); // Discord for Tormenting Voice + + setStopAt(2, PhaseStep.END_TURN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 15); + assertPermanentCount(playerB, "Fallen Shinobi", 1); + + assertGraveyardCount(playerA, "Angelic Purge", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); // sacrificed for Purge + + assertGraveyardCount(playerA, "Tormenting Voice", 1); + assertGraveyardCount(playerB, "Pillarfield Ox", 1); // Discarded for Tormenting Voice + + assertHandCount(playerB, 3); // 2 from Tormenting Voice + 1 from Turn 2 draw + + assertExileCount(playerA, 1); // Both exiled cards are cast + assertExileCount(playerA, "Amulet of Kroog", 1); // Exiled with Purge + } + + @Test + public void castAdventureWithFallenShinobiTest() { + // Ninjutsu {2}{U}{B} + // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards + // of their library. Until end of turn, you may play those cards without paying their mana cost. + addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4 + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + addCard(Zone.BATTLEFIELD, playerA, "Amulet of Kroog"); // Just to exile for Angelic Purge + + /* Curious Pair {1}{G} + * Creature — Human Peasant + * 1/3 + * ---- + * Treats to Share {G} + * Sorcery — Adventure + * Create a Food token. + */ + addCard(Zone.LIBRARY, playerA, "Curious Pair"); + + // As an additional cost to cast Angelic Purge, sacrifice a permanent. + // Exile target artifact, creature, or enchantment. + addCard(Zone.LIBRARY, playerA, "Angelic Purge"); // Sorcery {2}{W} + + skipInitShuffling(); + + attack(2, playerB, "Fallen Shinobi"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Angelic Purge"); + setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Purge + addTarget(playerB, "Amulet of Kroog"); // Exile with Purge + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Treats to Share"); + + setStopAt(2, PhaseStep.END_TURN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 15); + assertPermanentCount(playerB, "Fallen Shinobi", 1); + + assertGraveyardCount(playerA, "Angelic Purge", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); // sacrificed for Purge + + assertPermanentCount(playerB, "Food", 1); + assertExileCount(playerA, "Curious Pair", 1); + + assertHandCount(playerB, 1); // 1 from Turn 2 draw + + assertExileCount(playerA, 2); // Both exiled cards are cast + assertExileCount(playerA, "Amulet of Kroog", 1); // Exiled with Purge + } + + @Test + public void test_ActivateFromOpponentCreature() { + // Players can’t search libraries. Any player may pay {2} for that player to ignore this effect until end of turn. + addCard(Zone.BATTLEFIELD, playerB, "Leonin Arbiter", 1); + // + // {3}{U}{U} + // Search target opponent’s library for an artifact card and put that card onto the battlefield under your control. Then that player shuffles their library. + addCard(Zone.HAND, playerA, "Acquire", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5 * 2 + 2); + addCard(Zone.LIBRARY, playerB, "Alpha Myr", 1); + + // first cast -- can't search library + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Acquire", playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // second cast -- unlock library and search + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}: Any player may"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + // + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Acquire", playerB); + addTarget(playerA, "Alpha Myr"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Alpha Myr", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java index 3176f1d128..a25c1ea544 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java @@ -2,6 +2,7 @@ package org.mage.test.cards.asthough; import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -200,4 +201,60 @@ public class SpendOtherManaTest extends CardTestPlayerBase { assertLife(playerA, 20); assertLife(playerB, 20 - 1); } + + /** + * Chromatic Orrery allows it's controller to spend mana of any color as + * though it were mana of any color. With mana from Food Chain I should be + * able to cast creature spells using abritrary color of mana. But the game + * still requires to pay appropriate color as though there was no Orrery on + * my side of battlefield. + */ + @Test + public void testFoodChainWithChromaticOrrery() { + setStrictChooseMode(true); + addCard(Zone.HAND, playerA, "Adriana, Captain of the Guard", 1); // Creature {3}{R}{W} + + addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox", 1); // Creature {3}{W} + + // Exile a creature you control: Add X mana of any one color, where X is the exiled creature's converted mana cost plus one. + // Spend this mana only to cast creature spells. + addCard(Zone.BATTLEFIELD, playerA, "Food Chain"); // Enchantment {2}{G} + + // You may spend mana as though it were mana of any color. + // {T}: Add {C}{C}{C}{C}{C}. + // {5}, {T}: Draw a card for each color among permanents you control. + addCard(Zone.BATTLEFIELD, playerA, "Chromatic Orrery"); // Artifact {7} + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exile a creature you control"); + setChoice(playerA, "Pillarfield Ox"); + setChoice(playerA, "Red"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Adriana, Captain of the Guard"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + Assert.assertTrue("Mana pool of conditional mana has to be empty", playerA.getManaPool().getConditionalMana().isEmpty()); + assertExileCount("Pillarfield Ox", 1); + assertPermanentCount(playerA, "Adriana, Captain of the Guard", 1); + } + + @Test + public void testChromaticOrrery() { + setStrictChooseMode(true); + addCard(Zone.HAND, playerA, "Adriana, Captain of the Guard", 1); // Creature {3}{R}{W} + + // You may spend mana as though it were mana of any color. + // {T}: Add {C}{C}{C}{C}{C}. + // {5}, {T}: Draw a card for each color among permanents you control. + addCard(Zone.BATTLEFIELD, playerA, "Chromatic Orrery"); // Artifact {7} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Adriana, Captain of the Guard"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Adriana, Captain of the Guard", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java index 7a4f461f51..b18bdaeacc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java @@ -22,11 +22,11 @@ public class TheWretchedTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "The Wretched"); addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration addCard(Zone.BATTLEFIELD, playerB, "Living Wall"); // 0/6 Wall with regeneration - + attack(3, playerA, "The Wretched"); block(3, playerB, "Wall of Pine Needles", "The Wretched"); block(3, playerB, "Living Wall", "The Wretched"); - setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + setStopAt(3, PhaseStep.END_TURN); execute(); assertPermanentCount(playerA, "The Wretched", 1); @@ -39,17 +39,17 @@ public class TheWretchedTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "The Wretched"); addCard(Zone.BATTLEFIELD, playerA, "Bad Moon"); // +1/+1 for black creatures - + addCard(Zone.BATTLEFIELD, playerB, "Forest"); // a 3/3 with regeneration addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration addCard(Zone.BATTLEFIELD, playerB, "Living Wall"); // 0/6 Wall with regeneration - + // The Wretched // Creature — Demon - Demon 2/5 attack(3, playerA, "The Wretched"); block(3, playerB, "Wall of Pine Needles", "The Wretched"); block(3, playerB, "Living Wall", "The Wretched"); - + activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerB, "{G}: Regenerate {this}."); // Wall of Pine Needles setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); @@ -57,53 +57,52 @@ public class TheWretchedTest extends CardTestPlayerBase { assertPermanentCount(playerA, "The Wretched", 1); assertPermanentCount(playerA, "Living Wall", 1); - + assertPermanentCount(playerB, "Wall of Pine Needles", 1); - } - + @Test public void testLoseControlOfTheWretched() { - + // At end of combat, gain control of all creatures blocking The Wretched for as long as you control The Wretched. addCard(Zone.BATTLEFIELD, playerA, "The Wretched"); - + addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration addCard(Zone.BATTLEFIELD, playerB, "Living Wall"); // 0/6 Wall with regeneration addCard(Zone.BATTLEFIELD, playerB, "Island", 4); addCard(Zone.HAND, playerB, "Control Magic"); - + attack(3, playerA, "The Wretched"); block(3, playerB, "Wall of Pine Needles", "The Wretched"); block(3, playerB, "Living Wall", "The Wretched"); - + castSpell(4, PhaseStep.POSTCOMBAT_MAIN, playerB, "Control Magic", "The Wretched"); setStopAt(4, PhaseStep.END_TURN); execute(); assertPermanentCount(playerB, "The Wretched", 1); + assertPermanentCount(playerA, "Wall of Pine Needles", 0); assertPermanentCount(playerB, "Wall of Pine Needles", 1); assertPermanentCount(playerB, "Living Wall", 1); } - + @Test public void testRegenTheWretchedThusRemovingFromCombat() { addCard(Zone.BATTLEFIELD, playerA, "The Wretched"); - addCard(Zone.HAND, playerA, "Regenerate"); - addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); - + addCard(Zone.HAND, playerA, "Regenerate"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration - addCard(Zone.BATTLEFIELD, playerB, "Wall of Spears"); // 3/2 - - + addCard(Zone.BATTLEFIELD, playerB, "Wall of Spears"); // 3/2 + attack(3, playerA, "The Wretched"); block(3, playerB, "Wall of Pine Needles", "The Wretched"); block(3, playerB, "Wall of Spears", "The Wretched"); - + castSpell(3, PhaseStep.DECLARE_BLOCKERS, playerA, "Regenerate", "The Wretched"); - + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/AngelOfJubilationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/AngelOfJubilationTest.java index 1f3373df03..01aa07c6b9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/AngelOfJubilationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/AngelOfJubilationTest.java @@ -84,7 +84,7 @@ public class AngelOfJubilationTest extends CardTestPlayerBase { activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{2}, Sacrifice a permanent you control: Return target creature to its owner's hand."); addTarget(playerB, "Angel of Jubilation"); // return to hand - setChoice(playerB, "Food Chain"); // cacrifice cost + setChoice(playerB, "Food Chain"); // sacrifice cost setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); @@ -177,4 +177,71 @@ public class AngelOfJubilationTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Wasteland", 0); } + /** + * https://github.com/magefree/mage/issues/3663 + * + * Angel of Jubilation should just prevent paying life for activating + * abilities, but currently when it is out the opponent is not prompted to + * choose whether or not to pay life for Athreos. + */ + @Test + public void testAthreosLifePayNotPrevented() { + setStrictChooseMode(true); + // Other nonblack creatures you control get +1/+1. + // Players can't pay life or sacrifice creatures to cast spells or activate abilities + addCard(Zone.BATTLEFIELD, playerA, "Angel of Jubilation"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + // Indestructible + // As long as your devotion to white and black is less than seven, Athreos isn't a creature. + // Whenever another creature you own dies, return it to your hand unless target opponent pays 3 life. + addCard(Zone.BATTLEFIELD, playerA, "Athreos, God of Passage"); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + addCard(Zone.HAND, playerB, "Lightning Bolt"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion"); + setChoice(playerB, "Yes"); // Pay 3 life to prevent that returns to PlayerA's hand? + + addTarget(playerA, playerB); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + + } + + /** + * 5/1/2012 + * + * If a spell or activated ability has a cost that requires a player to pay + * life (as Griselbrand’s activated ability does) or sacrifice a creature + * (as Fling does), that spell or ability can’t be cast or activated. + */ + + @Test + public void testGriselbrandCantPay() { + setStrictChooseMode(true); + // Other nonblack creatures you control get +1/+1. + // Players can't pay life or sacrifice creatures to cast spells or activate abilities + addCard(Zone.BATTLEFIELD, playerA, "Angel of Jubilation"); + + // Pay 7 life: Draw seven cards. + addCard(Zone.BATTLEFIELD, playerB, "Griselbrand"); + + checkPlayableAbility("activated ability", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Pay 7 life", false); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java index 4caf5347b1..c8ea023d90 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java @@ -17,7 +17,6 @@ public class CommandersCastTest extends CardTestCommander4Players { addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); - // showCommand("commanders", 1, PhaseStep.PRECOMBAT_MAIN, playerA); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); setStopAt(1, PhaseStep.END_TURN); @@ -70,9 +69,7 @@ public class CommandersCastTest extends CardTestCommander4Players { public void test_PlayAsLandOneTime() { addCard(Zone.COMMAND, playerA, "Academy Ruins", 1); - // showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA); playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins"); - //castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins"); setStopAt(1, PhaseStep.END_TURN); setStrictChooseMode(true); @@ -113,7 +110,6 @@ public class CommandersCastTest extends CardTestCommander4Players { waitStackResolved(5, PhaseStep.POSTCOMBAT_MAIN); checkPermanentCount("after cast 2", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Academy Ruins", 1); -// showBattlefield("end battlefield", 5, PhaseStep.END_TURN, playerA); setStopAt(5, PhaseStep.END_TURN); setStrictChooseMode(true); execute(); @@ -259,7 +255,6 @@ public class CommandersCastTest extends CardTestCommander4Players { addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2); // cast overload - // showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Weapon Surge with overload"); setChoice(playerA, "Yes"); // move to command zone checkAbility("after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Balduvian Bears", FirstStrikeAbility.class, true); @@ -269,4 +264,54 @@ public class CommandersCastTest extends CardTestCommander4Players { execute(); assertAllCommandsUsed(); } + + @Test + public void test_ExileWithDelvePayAndReturn() { + // https://github.com/magefree/mage/issues/5121 + // Exiling your commander from your graveyard should give you the option to put it in command zone + // We were playing in a restarted-by-Karn game (if that mattered), and a player who exiled their + // commander from graveyard via Delve was not given the opportunity to place it in the command zone. + // Instead, it went directly to the exiled zone. + + // disable auto-payment for delve test + disableManaAutoPayment(playerA); + + // commander + addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // + // {4}{U}{U} creature + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + addCard(Zone.HAND, playerA, "Ethereal Forager", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); // one from delve + + // prepare commander and put it to graveyard + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + setChoice(playerA, "Green", 2); // pay + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + // + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + setChoice(playerA, "Red"); // pay + setChoice(playerA, "No"); // leave in graveyard + + // use commander as delve pay + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 5); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ethereal Forager"); + setChoice(playerA, "Blue", 5); // pay normal + setChoice(playerA, "Exile cards"); // pay delve + setChoice(playerA, "Balduvian Bears"); + setChoice(playerA, "Yes"); // move to command zone + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertCommandZoneCount(playerA, "Balduvian Bears", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java index 8dbe972ea3..71a9242073 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java @@ -5,10 +5,12 @@ import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.condition.common.NotMyTurnCondition; import mage.abilities.decorator.ConditionalCostModificationEffect; import mage.abilities.effects.common.cost.AbilitiesCostReductionControllerEffect; -import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.abilities.keyword.EquipAbility; import mage.constants.PhaseStep; +import mage.constants.TargetController; import mage.constants.Zone; +import mage.filter.FilterCard; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -113,7 +115,7 @@ public class ConditionalCostModificationTest extends CardTestPlayerBase { new ConditionalCostModificationEffect( new AbilitiesCostReductionControllerEffect(EquipAbility.class, "equip"), NotMyTurnCondition.instance, - new SpellsCostIncreasementAllEffect(1), + new SpellsCostIncreasingAllEffect(1, new FilterCard(), TargetController.ANY), "" ) )); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java index 2a7e23d40a..5f61921279 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java @@ -135,7 +135,7 @@ public class ConditionalPreventionTest extends CardTestPlayerBase { } @Test - public void test_PrentableCombatDamage() { + public void test_PreventableCombatDamage() { // Prevent all damage that would be dealt to creatures. addCard(Zone.BATTLEFIELD, playerA, "Bubble Matrix", 1); addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); @@ -190,4 +190,77 @@ public class ConditionalPreventionTest extends CardTestPlayerBase { assertLife(playerA, 20); assertLife(playerB, 20 - 2); } + + @Test + public void test_PreventSomeDamage_Normal() { + // Kicker-Sacrifice a land. + // Prevent the next 3 damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose. + // If Pollen Remedy was kicked, prevent the next 6 damage this way instead. + addCard(Zone.HAND, playerA, "Pollen Remedy", 1); // {W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.HAND, playerA, "Swamp", 1); // for kicker + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // add shield for 3 damage + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pollen Remedy"); + setChoice(playerA, "No"); // no kicker + addTargetAmount(playerA, "Balduvian Bears", 3); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("shield", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pollen Remedy", 1); + + // 6 damage to die (if no shield then can cast only 1 bolt) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkGraveyardCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 2); + checkGraveyardCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_PreventSomeDamage_Kicked() { + // Kicker-Sacrifice a land. + // Prevent the next 3 damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose. + // If Pollen Remedy was kicked, prevent the next 6 damage this way instead. + addCard(Zone.HAND, playerA, "Pollen Remedy", 1); // {W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); // for kicker + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // add shield for 6 damage + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pollen Remedy"); + setChoice(playerA, "Yes"); // use kicker + setChoice(playerA, "Swamp"); // kicker cost + addTargetAmount(playerA, "Balduvian Bears", 6); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("shield", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pollen Remedy", 1); + + // 9 damage to die (if no shield then can cast only 1 bolt) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkGraveyardCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 3); + checkGraveyardCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/EightAndAHalfTailsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/EightAndAHalfTailsTest.java index a304256f44..dbb31c5623 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/EightAndAHalfTailsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/EightAndAHalfTailsTest.java @@ -1,82 +1,82 @@ - -package org.mage.test.cards.continuous; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.game.permanent.Permanent; -import org.junit.Assert; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class EightAndAHalfTailsTest extends CardTestPlayerBase { - - @Test - public void testColorChange() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); - - // {1}{W}: Target permanent you control gains protection from white until end of turn. - // {1}: Target spell or permanent becomes white until end of turn. - addCard(Zone.BATTLEFIELD, playerA, "Eight-and-a-Half-Tails", 1); // Creature 2/2 - - addCard(Zone.BATTLEFIELD, playerB, "Nekusar, the Mindrazer", 1); - - // At the beginning of each player's draw step, that player draws an additional card. - // Whenever an opponent draws a card, Nekusar, the Mindrazer deals 1 damage to that player. - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}:", "Nekusar, the Mindrazer"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerB, "Nekusar, the Mindrazer", 1); - Permanent nekusar = getPermanent("Nekusar, the Mindrazer"); - - Assert.assertTrue("Nekusar should be white", nekusar.getColor(currentGame).isWhite()); - Assert.assertFalse("Nekusar should not be blue", nekusar.getColor(currentGame).isBlue()); - Assert.assertFalse("Nekusar should not be black", nekusar.getColor(currentGame).isBlack()); - Assert.assertFalse("Nekusar should not be red", nekusar.getColor(currentGame).isRed()); - - } - - /** - * I made opponent's Nekusar white with 8.5 tails and when Nekusar was - * recast he was still white. - */ - @Test - public void testColorChangeIsReset() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); - - // {1}{W}: Target permanent you control gains protection from white until end of turn. - // {1}: Target spell or permanent becomes white until end of turn. - addCard(Zone.BATTLEFIELD, playerA, "Eight-and-a-Half-Tails", 1); // Creature 2/2 - - // At the beginning of each player's draw step, that player draws an additional card. - // Whenever an opponent draws a card, Nekusar, the Mindrazer deals 1 damage to that player. - addCard(Zone.BATTLEFIELD, playerB, "Nekusar, the Mindrazer", 1); - - // Exile target creature you control, then return it to the battlefield under its owner's control. - // Flashback {3}{U} - addCard(Zone.HAND, playerB, "Momentary Blink", 1); // Instant {1}{W} - addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}:", "Nekusar, the Mindrazer"); - castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Momentary Blink", "Nekusar, the Mindrazer"); - - setStopAt(1, PhaseStep.END_COMBAT); - execute(); - - assertPermanentCount(playerB, "Nekusar, the Mindrazer", 1); - Permanent nekusar = getPermanent("Nekusar, the Mindrazer"); - - assertGraveyardCount(playerB, "Momentary Blink", 1); - Assert.assertFalse("Nekusar should not be white", nekusar.getColor(currentGame).isWhite()); - Assert.assertTrue("Nekusar should be blue", nekusar.getColor(currentGame).isBlue()); - Assert.assertTrue("Nekusar should be black", nekusar.getColor(currentGame).isBlack()); - Assert.assertTrue("Nekusar should be red", nekusar.getColor(currentGame).isRed()); - - } - -} + +package org.mage.test.cards.continuous; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class EightAndAHalfTailsTest extends CardTestPlayerBase { + + @Test + public void testColorChange() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // {1}{W}: Target permanent you control gains protection from white until end of turn. + // {1}: Target spell or permanent becomes white until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Eight-and-a-Half-Tails", 1); // Creature 2/2 + + addCard(Zone.BATTLEFIELD, playerB, "Nekusar, the Mindrazer", 1); + + // At the beginning of each player's draw step, that player draws an additional card. + // Whenever an opponent draws a card, Nekusar, the Mindrazer deals 1 damage to that player. + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}:", "Nekusar, the Mindrazer"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Nekusar, the Mindrazer", 1); + Permanent nekusar = getPermanent("Nekusar, the Mindrazer"); + + Assert.assertTrue("Nekusar should be white", nekusar.getColor(currentGame).isWhite()); + Assert.assertFalse("Nekusar should not be blue", nekusar.getColor(currentGame).isBlue()); + Assert.assertFalse("Nekusar should not be black", nekusar.getColor(currentGame).isBlack()); + Assert.assertFalse("Nekusar should not be red", nekusar.getColor(currentGame).isRed()); + + } + + /** + * I made opponent's Nekusar white with 8.5 tails and when Nekusar was + * recast he was still white. + */ + @Test + public void testColorChangeIsReset() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // {1}{W}: Target permanent you control gains protection from white until end of turn. + // {1}: Target spell or permanent becomes white until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Eight-and-a-Half-Tails", 1); // Creature 2/2 + + // At the beginning of each player's draw step, that player draws an additional card. + // Whenever an opponent draws a card, Nekusar, the Mindrazer deals 1 damage to that player. + addCard(Zone.BATTLEFIELD, playerB, "Nekusar, the Mindrazer", 1); + + // Exile target creature you control, then return it to the battlefield under its owner's control. + // Flashback {3}{U} + addCard(Zone.HAND, playerB, "Momentary Blink", 1); // Instant {1}{W} + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}:", "Nekusar, the Mindrazer"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Momentary Blink", "Nekusar, the Mindrazer"); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertPermanentCount(playerB, "Nekusar, the Mindrazer", 1); + Permanent nekusar = getPermanent("Nekusar, the Mindrazer"); + + assertGraveyardCount(playerB, "Momentary Blink", 1); + Assert.assertFalse("Nekusar should not be white", nekusar.getColor(currentGame).isWhite()); + Assert.assertTrue("Nekusar should be blue", nekusar.getColor(currentGame).isBlue()); + Assert.assertTrue("Nekusar should be black", nekusar.getColor(currentGame).isBlack()); + Assert.assertTrue("Nekusar should be red", nekusar.getColor(currentGame).isRed()); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ErsatzGnomesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ErsatzGnomesTest.java index a46c95b388..4f6a30eb32 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ErsatzGnomesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ErsatzGnomesTest.java @@ -1,136 +1,136 @@ - -package org.mage.test.cards.continuous; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.game.permanent.Permanent; -import org.junit.Assert; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class ErsatzGnomesTest extends CardTestPlayerBase { - - /** - * Ersatz Gnomes is incorrectly used I targeted a spell which is a permanent - * to colorless. When it enters the field its suppose to be colorless not go - * back to normal. It's colorless until it leaves the battlefield when you - * make a permanent spell colorless when you cast it. - */ - @Test - public void testColorlessSpellCreatesColorlessPermanent() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); - addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); - - // {T}: Target spell becomes colorless. - // {T}: Target permanent becomes colorless until end of turn. - addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Silvercoat Lion"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Silvercoat Lion", 1); - assertTapped("Ersatz Gnomes", true); - Permanent lion = getPermanent("Silvercoat Lion", playerA); - Assert.assertTrue("Silvercoat lion has to be colorless", lion.getColor(currentGame).isColorless()); - } - - @Test - public void testColorlessSpellCreatesColorlessPermanentUntilItBattlefieldLeft() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); - addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); - - // {T}: Target spell becomes colorless. - // {T}: Target permanent becomes colorless until end of turn. - addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes"); - - addCard(Zone.BATTLEFIELD, playerB, "Island", 1); - addCard(Zone.HAND, playerB, "Unsummon"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Silvercoat Lion"); - - castSpell(1, PhaseStep.END_COMBAT, playerB, "Unsummon", "Silvercoat Lion"); - - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - assertGraveyardCount(playerB, "Unsummon", 1); - assertHandCount(playerA, "Silvercoat Lion", 0); - assertPermanentCount(playerA, "Silvercoat Lion", 1); - assertTapped("Ersatz Gnomes", true); - Permanent lion = getPermanent("Silvercoat Lion", playerA); - Assert.assertTrue("Silvercoat lion has to be white", lion.getColor(currentGame).isWhite()); - } - - @Test - public void testChangeColorOfBestowSpell() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - // Bestow {3}{W} - // Lifelink - // Echanted creature gets +1/+1 and has lifelink. - addCard(Zone.HAND, playerA, "Hopeful Eidolon");// Creature {W} - - // {T}: Target spell becomes colorless. - // {T}: Target permanent becomes colorless until end of turn. - addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes"); - - addCard(Zone.BATTLEFIELD, playerB, "Island", 1); - addCard(Zone.HAND, playerB, "Unsummon"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hopeful Eidolon using bestow", "Silvercoat Lion"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Hopeful Eidolon"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - assertHandCount(playerA, "Hopeful Eidolon", 0); - assertPermanentCount(playerA, "Hopeful Eidolon", 1); - assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); - assertTapped("Ersatz Gnomes", true); - Permanent eidolon = getPermanent("Hopeful Eidolon", playerA); - Assert.assertTrue("Hopeful Eidolon Enchantment has to be colorless", eidolon.getColor(currentGame).isColorless()); - } - - @Test - public void testChangeColorOfBestowSpellUnsummon() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); - // Bestow {3}{W} - // Lifelink - // Echanted creature gets +1/+1 and has lifelink. - addCard(Zone.HAND, playerA, "Hopeful Eidolon");// Creature {W} - - // {T}: Target spell becomes colorless. - // {T}: Target permanent becomes colorless until end of turn. - addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes"); - - addCard(Zone.BATTLEFIELD, playerB, "Island", 1); - addCard(Zone.HAND, playerB, "Unsummon"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hopeful Eidolon"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Hopeful Eidolon"); - - castSpell(1, PhaseStep.END_COMBAT, playerB, "Unsummon", "Hopeful Eidolon"); - - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Hopeful Eidolon"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - assertGraveyardCount(playerB, "Unsummon", 1); - assertHandCount(playerA, "Hopeful Eidolon", 0); - assertPermanentCount(playerA, "Hopeful Eidolon", 1); - assertTapped("Ersatz Gnomes", true); - Permanent lion = getPermanent("Hopeful Eidolon", playerA); - Assert.assertTrue("Hopeful Eidolon has to be white", lion.getColor(currentGame).isWhite()); - } -} + +package org.mage.test.cards.continuous; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class ErsatzGnomesTest extends CardTestPlayerBase { + + /** + * Ersatz Gnomes is incorrectly used I targeted a spell which is a permanent + * to colorless. When it enters the field its suppose to be colorless not go + * back to normal. It's colorless until it leaves the battlefield when you + * make a permanent spell colorless when you cast it. + */ + @Test + public void testColorlessSpellCreatesColorlessPermanent() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); + + // {T}: Target spell becomes colorless. + // {T}: Target permanent becomes colorless until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertTapped("Ersatz Gnomes", true); + Permanent lion = getPermanent("Silvercoat Lion", playerA); + Assert.assertTrue("Silvercoat lion has to be colorless", lion.getColor(currentGame).isColorless()); + } + + @Test + public void testColorlessSpellCreatesColorlessPermanentUntilItBattlefieldLeft() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); + + // {T}: Target spell becomes colorless. + // {T}: Target permanent becomes colorless until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + addCard(Zone.HAND, playerB, "Unsummon"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Silvercoat Lion"); + + castSpell(1, PhaseStep.END_COMBAT, playerB, "Unsummon", "Silvercoat Lion"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Unsummon", 1); + assertHandCount(playerA, "Silvercoat Lion", 0); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertTapped("Ersatz Gnomes", true); + Permanent lion = getPermanent("Silvercoat Lion", playerA); + Assert.assertTrue("Silvercoat lion has to be white", lion.getColor(currentGame).isWhite()); + } + + @Test + public void testChangeColorOfBestowSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + // Bestow {3}{W} + // Lifelink + // Echanted creature gets +1/+1 and has lifelink. + addCard(Zone.HAND, playerA, "Hopeful Eidolon");// Creature {W} + + // {T}: Target spell becomes colorless. + // {T}: Target permanent becomes colorless until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + addCard(Zone.HAND, playerB, "Unsummon"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hopeful Eidolon using bestow", "Silvercoat Lion"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Hopeful Eidolon"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertHandCount(playerA, "Hopeful Eidolon", 0); + assertPermanentCount(playerA, "Hopeful Eidolon", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); + assertTapped("Ersatz Gnomes", true); + Permanent eidolon = getPermanent("Hopeful Eidolon", playerA); + Assert.assertTrue("Hopeful Eidolon Enchantment has to be colorless", eidolon.getColor(currentGame).isColorless()); + } + + @Test + public void testChangeColorOfBestowSpellUnsummon() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // Bestow {3}{W} + // Lifelink + // Echanted creature gets +1/+1 and has lifelink. + addCard(Zone.HAND, playerA, "Hopeful Eidolon");// Creature {W} + + // {T}: Target spell becomes colorless. + // {T}: Target permanent becomes colorless until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + addCard(Zone.HAND, playerB, "Unsummon"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hopeful Eidolon"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Hopeful Eidolon"); + + castSpell(1, PhaseStep.END_COMBAT, playerB, "Unsummon", "Hopeful Eidolon"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Hopeful Eidolon"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Unsummon", 1); + assertHandCount(playerA, "Hopeful Eidolon", 0); + assertPermanentCount(playerA, "Hopeful Eidolon", 1); + assertTapped("Ersatz Gnomes", true); + Permanent lion = getPermanent("Hopeful Eidolon", playerA); + Assert.assertTrue("Hopeful Eidolon has to be white", lion.getColor(currentGame).isWhite()); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/IrencragFeatTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/IrencragFeatTest.java index 93f09009b9..3ebb83bc4c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/IrencragFeatTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/IrencragFeatTest.java @@ -18,14 +18,23 @@ public class IrencragFeatTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Irencrag Feat", 1); addCard(Zone.HAND, playerA, "Dwarven Trader", 2); + // 1 + checkPlayableAbility("can cast before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Irencrag Feat"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // 2 + checkPlayableAbility("can cast after feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // 3 + checkPlayableAbility("can't cast after feat 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", false); setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - //assertAllCommandsUsed(); // second trader must be restricted to cast + assertAllCommandsUsed(); assertHandCount(playerA, "Dwarven Trader", 1); assertPermanentCount(playerA, "Dwarven Trader", 1); @@ -38,16 +47,32 @@ public class IrencragFeatTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Irencrag Feat", 1); addCard(Zone.HAND, playerA, "Dwarven Trader", 4); + // 1 + checkPlayableAbility("can cast before feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // 2 + checkPlayableAbility("can cast before feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // 3 castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Irencrag Feat"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // 4 + checkPlayableAbility("can cast after feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // 5 + checkPlayableAbility("can't cast after feat 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", false); setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - //assertAllCommandsUsed(); // second trader must be restricted to cast + assertAllCommandsUsed(); assertHandCount(playerA, "Dwarven Trader", 1); assertPermanentCount(playerA, "Dwarven Trader", 3); @@ -59,6 +84,7 @@ public class IrencragFeatTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Irencrag Feat", 1); addCard(Zone.HAND, playerA, "Lightning Bolt", 4); + // no restrictions for stack castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Irencrag Feat"); @@ -80,17 +106,25 @@ public class IrencragFeatTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Irencrag Feat", 1); addCard(Zone.HAND, playerA, "Lightning Bolt", 4); + // on stack before + checkPlayableAbility("can cast before feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + checkPlayableAbility("can cast before feat 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + // feat castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Irencrag Feat"); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // after resolve + checkPlayableAbility("can cast after feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + checkPlayableAbility("can't cast after feat 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", false); setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - //assertAllCommandsUsed(0); // second bolt must be restricted to cast + assertAllCommandsUsed(); assertHandCount(playerA, "Lightning Bolt", 4 - 3); assertLife(playerB, 20 - 3 * 3); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java index 224c316f9a..d011d952b3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java @@ -14,20 +14,17 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { /** - * * Playing a commander game. Opponent had a Magus of the Moon, and I later * dropped a Chromatic Lantern. - * + *

* I was not allowed to use the Chromatic Lantern's ability. Since layers * are tricky I asked on the Judge's chat to confirm and the user "luma" * said it should work on this scenario. - * */ @Test public void testMagusOfTheMoonAndChromaticLantern() { @@ -42,8 +39,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Chromatic Lantern"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerB, "Chromatic Lantern", 1); @@ -66,8 +65,11 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Chromatic Lantern"); castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Magus of the Moon"); + + setStrictChooseMode(true); setStopAt(3, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerB, "Chromatic Lantern", 1); assertPermanentCount(playerA, "Magus of the Moon", 1); @@ -97,8 +99,11 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aquitect's Will", "Forbidding Watchtower"); activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{W}:"); + + setStrictChooseMode(true); setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerA, "Aquitect's Will", 1); @@ -128,8 +133,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bloodmoon); playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, urborgtoy); + setStrictChooseMode(true); setStopAt(2, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, bloodmoon, 1); assertPermanentCount(playerA, urborgtoy, 1); @@ -157,8 +164,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, urborgtoy); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bloodmoon); + setStrictChooseMode(true); setStopAt(2, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, bloodmoon, 1); assertPermanentCount(playerA, urborgtoy, 1); @@ -176,7 +185,7 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { In terms of time-stamp order, Urborg was down first, then Kormus Bell, then Quicksilver. When I put a flood counter on a basic swamp, it would become a 0/0 instead of a 1/1 and die. */ - + @Test public void testCormusBellAfterUrborg() { // Land - Legendary @@ -198,14 +207,14 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Kormus Bell"); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Quicksilver Fountain"); - + addTarget(playerA, "Mountain"); - + setStrictChooseMode(true); setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); assertAllCommandsUsed(); - + assertPermanentCount(playerA, urborgtoy, 1); assertPermanentCount(playerA, "Kormus Bell", 1); assertPermanentCount(playerB, "Quicksilver Fountain", 1); @@ -245,8 +254,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Stormtide Leviathan"); // all lands are islands in addition to their other types addCard(Zone.BATTLEFIELD, playerA, "Darksteel Citadel"); // land has indestructible ability + setStrictChooseMode(true); setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); Permanent darksteel = getPermanent("Darksteel Citadel", playerA.getId()); Assert.assertNotNull(darksteel); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java index bc1b83802f..42cbe4458e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java @@ -1,196 +1,196 @@ - - -package org.mage.test.cards.continuous; - -import mage.constants.CardType; -import mage.constants.PhaseStep; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.Filter; -import org.junit.Ignore; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author jeffwadsworth - */ -public class LayerTests extends CardTestPlayerBase { - - @Test - public void testMultipleLayeredDependency() { - //Conspiracy->Opalescence->Enchanted Evening - //Conspiracy is dependent on Opalescence - //Opalescence is dependent on Enchanted Evening - //So, the effects should be applied as follows: - //Enchanted Evening->Opalescence->Conspiracy - - addCard(Zone.HAND, playerA, "Conspiracy"); // creatures get chosen subtype - addCard(Zone.HAND, playerA, "Opalescence"); // enchantments become creatures P/T equal to CMC - addCard(Zone.HAND, playerA, "Enchanted Evening"); // all permanents become enchantments - - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); - addCard(Zone.BATTLEFIELD, playerA, "Island", 5); - addCard(Zone.BATTLEFIELD, playerA, "Glorious Anthem", 1); // keep lands alive // all creatures +1/+1 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Conspiracy"); - setChoice(playerA, "Advisor"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Opalescence"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Enchanted Evening"); - - setStrictChooseMode(true); - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - assertAllCommandsUsed(); - - assertType("Swamp", CardType.LAND, SubType.ADVISOR); // Subtype changed with Conspiracy - assertPowerToughness(playerA, "Swamp", 1, 1); // boosted with Glorious Anthem - assertType("Enchanted Evening", CardType.ENCHANTMENT, SubType.ADVISOR); // Subtype changed with Conspiracy - assertPowerToughness(playerA, "Enchanted Evening", 6, 6); // boosted with Glorious Anthem - - } - - @Test - public void testMycosynthLatticeAndMarchOfTheMachinesAndHumility() { - // example from Reddit - /* - This came up in a recent EDH game and we had no idea how to progress. - Player A cast a Humility, then a March of the Machines, and finally - a Mycosynth Lattice. - Does the game get stuck in an endless loop of each card gaining and - losing its respective creature-ness and abilities? - Answer: No, they all die - */ - - addCard(Zone.HAND, playerA, "Mycosynth Lattice"); // all permanents are artifacts - addCard(Zone.HAND, playerA, "March of the Machines"); // artifacts become creatures - addCard(Zone.HAND, playerA, "Humility"); // all creatures lose abilities and P/T is 1/1 - - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 10); - addCard(Zone.BATTLEFIELD, playerA, "Island", 10); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Humility"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "March of the Machines"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mycosynth Lattice"); - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - assertAllCommandsUsed(); - - // everything dies - assertPermanentCount(playerA, "Humility", 0); - assertPermanentCount(playerA, "March of the Machines", 0); - assertPermanentCount(playerA, "Mycosynth Lattice", 0); - assertPermanentCount(playerA, "Island", 0); - - } - - @Test - public void testBloodMoon_UrborgTombOfYawgmothInteraction() { - // Blood Moon : Nonbasic lands are Mountains. - // Urborg, Tomb of Yawgmoth : Each land is a Swamp in addition to its other types. - // Expected behavior: Urborg loses all abilities and becomes a Mountain. The Plains does not have subtype Swamp due to this effect. - addCard(Zone.BATTLEFIELD, playerA, "Blood Moon"); - addCard(Zone.BATTLEFIELD, playerA, "Urborg, Tomb of Yawgmoth", 1); // non-basic land - addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertType("Urborg, Tomb of Yawgmoth", CardType.LAND, SubType.MOUNTAIN); // Urborg is a Mountain now - assertPermanentCount(playerA, "Swamp", 0); // no Swamp subtypes on the battlefield - assertPermanentCount(playerA, "Plains", 1); // the Plains is not affected by the Urborg - assertType("Plains", CardType.LAND, SubType.PLAINS); - - } - - @Test - @Ignore //Works fine in the game. Test fails, though. - public void complexExampleFromLayersArticle() { - /*In play there is a Grizzly Bears which has already been Giant Growthed, - a Bog Wraith enchanted by a Lignify, and Figure of Destiny with its 3rd ability activated. - I then cast a Mirrorweave targeting the Figure of Destiny. What does each creature look like? - */ - addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); - addCard(Zone.HAND, playerA, "Giant Growth", 1); - addCard(Zone.BATTLEFIELD, playerA, "Bog Wraith", 1); - addCard(Zone.HAND, playerA, "Lignify", 1); - addCard(Zone.BATTLEFIELD, playerA, "Figure of Destiny", 1); - addCard(Zone.HAND, playerA, "Mirrorweave", 1); - - addCard(Zone.BATTLEFIELD, playerA, "Forest", 20); - addCard(Zone.BATTLEFIELD, playerA, "Island", 20); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 20); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Giant Growth", "Grizzly Bears"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lignify", "Bog Wrath"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}:"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}{R/W}{R/W}:"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}{R/W}{R/W}{R/W}{R/W}{R/W}:"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mirrorweave", "Figure of Destiny"); - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertPermanentCount(playerA, "Figure of Destiny", 3); - assertPowerToughness(playerA, "Figure of Destiny", 4, 4, Filter.ComparisonScope.All); - assertPowerToughness(playerA, "Figure of Destiny", 8, 8, Filter.ComparisonScope.All); - assertPowerToughness(playerA, "Figure of Destiny", 0, 4, Filter.ComparisonScope.All); - - } - - @Test - public void testUrborgWithAnimateLandAndOvinize() { - // Animate Land: target land is a 3/3 until end of turn and is still a land. - // Ovinize: target creature becomes 0/1 and loses all abilities until end of turn. - // Urborg, Tomb of Yawgmoth : Each land is a Swamp in addition to its other types. - // Expected behavior: Urborg loses all abilities and becomes a 0/1 creature. - addCard(Zone.HAND, playerA, "Animate Land", 1); - addCard(Zone.HAND, playerA, "Ovinize", 1); - addCard(Zone.BATTLEFIELD, playerA, "Urborg, Tomb of Yawgmoth", 1); - addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); - addCard(Zone.BATTLEFIELD, playerA, "Island", 2); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Land", "Urborg, Tomb of Yawgmoth"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ovinize", "Urborg, Tomb of Yawgmoth"); - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertType("Urborg, Tomb of Yawgmoth", CardType.CREATURE, SubType.SWAMP); // Urborg is a creature - assertPowerToughness(playerA, "Urborg, Tomb of Yawgmoth", 0, 1); // Urborg is a 0/1 creature - - } - - @Test - @Ignore //This works fine in the game. Test fails. - public void testFromAnArticle() { - /* - Aiden has a Battlegate Mimic on the battlefield. Nick controls two Wilderness Hypnotists. - Aiden casts a Scourge of the Nobilis, targeting the Mimic; after that resolves Nick activates - one of his Hypnotist's abilities, targeting the Mimic. Aiden attacks with the Mimic, and - casts Inside Out before the damage step. Once Inside Out resolves, Nick activates the ability - of his other Hypnotist. How much damage will the Mimic deal? - */ - addCard(Zone.HAND, playerA, "Scourge of the Nobilis", 1); - addCard(Zone.HAND, playerA, "Inside Out", 1); - addCard(Zone.BATTLEFIELD, playerA, "Battlegate Mimic", 1); - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); - addCard(Zone.BATTLEFIELD, playerA, "Island", 8); - - addCard(Zone.BATTLEFIELD, playerB, "Wilderness Hypnotist", 2); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scourge of the Nobilis", "Battlegate Mimic"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}:", "Battlegate Mimic"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Inside Out", "Battlegate Mimic"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}:", "Battlegate Mimic"); - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertPowerToughness(playerA, "Battlegate Mimic", 4, 2); - - } - -} + + +package org.mage.test.cards.continuous; + +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.Filter; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author jeffwadsworth + */ +public class LayerTests extends CardTestPlayerBase { + + @Test + public void testMultipleLayeredDependency() { + //Conspiracy->Opalescence->Enchanted Evening + //Conspiracy is dependent on Opalescence + //Opalescence is dependent on Enchanted Evening + //So, the effects should be applied as follows: + //Enchanted Evening->Opalescence->Conspiracy + + addCard(Zone.HAND, playerA, "Conspiracy"); // creatures get chosen subtype + addCard(Zone.HAND, playerA, "Opalescence"); // enchantments become creatures P/T equal to CMC + addCard(Zone.HAND, playerA, "Enchanted Evening"); // all permanents become enchantments + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + addCard(Zone.BATTLEFIELD, playerA, "Glorious Anthem", 1); // keep lands alive // all creatures +1/+1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Conspiracy"); + setChoice(playerA, "Advisor"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Opalescence"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Enchanted Evening"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertType("Swamp", CardType.LAND, SubType.ADVISOR); // Subtype changed with Conspiracy + assertPowerToughness(playerA, "Swamp", 1, 1); // boosted with Glorious Anthem + assertType("Enchanted Evening", CardType.ENCHANTMENT, SubType.ADVISOR); // Subtype changed with Conspiracy + assertPowerToughness(playerA, "Enchanted Evening", 6, 6); // boosted with Glorious Anthem + + } + + @Test + public void testMycosynthLatticeAndMarchOfTheMachinesAndHumility() { + // example from Reddit + /* + This came up in a recent EDH game and we had no idea how to progress. + Player A cast a Humility, then a March of the Machines, and finally + a Mycosynth Lattice. + Does the game get stuck in an endless loop of each card gaining and + losing its respective creature-ness and abilities? + Answer: No, they all die + */ + + addCard(Zone.HAND, playerA, "Mycosynth Lattice"); // all permanents are artifacts + addCard(Zone.HAND, playerA, "March of the Machines"); // artifacts become creatures + addCard(Zone.HAND, playerA, "Humility"); // all creatures lose abilities and P/T is 1/1 + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 10); + addCard(Zone.BATTLEFIELD, playerA, "Island", 10); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Humility"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "March of the Machines"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mycosynth Lattice"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + // everything dies + assertPermanentCount(playerA, "Humility", 0); + assertPermanentCount(playerA, "March of the Machines", 0); + assertPermanentCount(playerA, "Mycosynth Lattice", 0); + assertPermanentCount(playerA, "Island", 0); + + } + + @Test + public void testBloodMoon_UrborgTombOfYawgmothInteraction() { + // Blood Moon : Nonbasic lands are Mountains. + // Urborg, Tomb of Yawgmoth : Each land is a Swamp in addition to its other types. + // Expected behavior: Urborg loses all abilities and becomes a Mountain. The Plains does not have subtype Swamp due to this effect. + addCard(Zone.BATTLEFIELD, playerA, "Blood Moon"); + addCard(Zone.BATTLEFIELD, playerA, "Urborg, Tomb of Yawgmoth", 1); // non-basic land + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertType("Urborg, Tomb of Yawgmoth", CardType.LAND, SubType.MOUNTAIN); // Urborg is a Mountain now + assertPermanentCount(playerA, "Swamp", 0); // no Swamp subtypes on the battlefield + assertPermanentCount(playerA, "Plains", 1); // the Plains is not affected by the Urborg + assertType("Plains", CardType.LAND, SubType.PLAINS); + + } + + @Test + @Ignore //Works fine in the game. Test fails, though. + public void complexExampleFromLayersArticle() { + /*In play there is a Grizzly Bears which has already been Giant Growthed, + a Bog Wraith enchanted by a Lignify, and Figure of Destiny with its 3rd ability activated. + I then cast a Mirrorweave targeting the Figure of Destiny. What does each creature look like? + */ + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.HAND, playerA, "Giant Growth", 1); + addCard(Zone.BATTLEFIELD, playerA, "Bog Wraith", 1); + addCard(Zone.HAND, playerA, "Lignify", 1); + addCard(Zone.BATTLEFIELD, playerA, "Figure of Destiny", 1); + addCard(Zone.HAND, playerA, "Mirrorweave", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 20); + addCard(Zone.BATTLEFIELD, playerA, "Island", 20); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 20); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Giant Growth", "Grizzly Bears"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lignify", "Bog Wrath"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}:"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}{R/W}{R/W}:"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}{R/W}{R/W}{R/W}{R/W}{R/W}:"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mirrorweave", "Figure of Destiny"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Figure of Destiny", 3); + assertPowerToughness(playerA, "Figure of Destiny", 4, 4, Filter.ComparisonScope.All); + assertPowerToughness(playerA, "Figure of Destiny", 8, 8, Filter.ComparisonScope.All); + assertPowerToughness(playerA, "Figure of Destiny", 0, 4, Filter.ComparisonScope.All); + + } + + @Test + public void testUrborgWithAnimateLandAndOvinize() { + // Animate Land: target land is a 3/3 until end of turn and is still a land. + // Ovinize: target creature becomes 0/1 and loses all abilities until end of turn. + // Urborg, Tomb of Yawgmoth : Each land is a Swamp in addition to its other types. + // Expected behavior: Urborg loses all abilities and becomes a 0/1 creature. + addCard(Zone.HAND, playerA, "Animate Land", 1); + addCard(Zone.HAND, playerA, "Ovinize", 1); + addCard(Zone.BATTLEFIELD, playerA, "Urborg, Tomb of Yawgmoth", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Land", "Urborg, Tomb of Yawgmoth"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ovinize", "Urborg, Tomb of Yawgmoth"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertType("Urborg, Tomb of Yawgmoth", CardType.CREATURE, SubType.SWAMP); // Urborg is a creature + assertPowerToughness(playerA, "Urborg, Tomb of Yawgmoth", 0, 1); // Urborg is a 0/1 creature + + } + + @Test + @Ignore //This works fine in the game. Test fails. + public void testFromAnArticle() { + /* + Aiden has a Battlegate Mimic on the battlefield. Nick controls two Wilderness Hypnotists. + Aiden casts a Scourge of the Nobilis, targeting the Mimic; after that resolves Nick activates + one of his Hypnotist's abilities, targeting the Mimic. Aiden attacks with the Mimic, and + casts Inside Out before the damage step. Once Inside Out resolves, Nick activates the ability + of his other Hypnotist. How much damage will the Mimic deal? + */ + addCard(Zone.HAND, playerA, "Scourge of the Nobilis", 1); + addCard(Zone.HAND, playerA, "Inside Out", 1); + addCard(Zone.BATTLEFIELD, playerA, "Battlegate Mimic", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); + addCard(Zone.BATTLEFIELD, playerA, "Island", 8); + + addCard(Zone.BATTLEFIELD, playerB, "Wilderness Hypnotist", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scourge of the Nobilis", "Battlegate Mimic"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}:", "Battlegate Mimic"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Inside Out", "Battlegate Mimic"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}:", "Battlegate Mimic"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, "Battlegate Mimic", 4, 2); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MasterThiefTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MasterThiefTest.java index 8bfe2420bd..af26181472 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MasterThiefTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MasterThiefTest.java @@ -34,6 +34,7 @@ public class MasterThiefTest extends CardTestPlayerBase { @Test public void testMasterThief_LostControlOnSacrifice() { addCard(Zone.BATTLEFIELD, playerA, "Island", 10); + // When Master Thief enters the battlefield, gain control of target artifact for as long as you control Master Thief. addCard(Zone.HAND, playerA, "Master Thief", 3); addCard(Zone.BATTLEFIELD, playerB, "Accorder's Shield", 1); addCard(Zone.BATTLEFIELD, playerA, "Bearer of the Heavens", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PraetorsGraspTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PraetorsGraspTest.java index 941ca49a46..477b208390 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PraetorsGraspTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PraetorsGraspTest.java @@ -22,7 +22,7 @@ public class PraetorsGraspTest extends CardTestPlayerBase { addTarget(playerA, playerB); addTarget(playerA, "Mountain"); - // showAvaileableAbilities("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA); + // showAvailableAbilities("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA); setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java index e892764104..91f3f82a04 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java @@ -55,8 +55,8 @@ public class TappedForManaFromMultipleEffects extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nyxbloom Ancient"); // TODO: TAPPED_FOR_MANA replace event called from checkTappedForManaReplacement and start to choose replace events (is that problem?) // use case (that test): comment one 1-2 choices to fail in 1-2 calls - setChoice(playerA, "Nyxbloom Ancient: If you tap a permanent"); // getPlayable... checkTappedForManaReplacement... chooseReplacementEffect - setChoice(playerA, "Nyxbloom Ancient: If you tap a permanent"); // playManaAbility... resolve... checkToFirePossibleEvents... chooseReplacementEffect + setChoice(playerA, "Nyxbloom Ancient"); // getPlayable... checkTappedForManaReplacement... chooseReplacementEffect + setChoice(playerA, "Nyxbloom Ancient"); // playManaAbility... resolve... checkToFirePossibleEvents... chooseReplacementEffect // cast chloro castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Chlorophant"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java index 440074c3e2..d4c13331a8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java @@ -40,7 +40,7 @@ public class WonderTest extends CardTestPlayerBase { // check no flying in graveyard for (Card card : playerA.getGraveyard().getCards(currentGame)) { if (card.getName().equals("Runeclaw Bear")) { - Assert.assertFalse(card.getAbilities().contains(FlyingAbility.getInstance())); + Assert.assertFalse(card.hasAbility(FlyingAbility.getInstance(), currentGame)); } } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/BronzeBombshellTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/BronzeBombshellTest.java index ec4a08bfbe..501204af15 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/BronzeBombshellTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/BronzeBombshellTest.java @@ -1,44 +1,44 @@ - -package org.mage.test.cards.control; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX - */ -public class BronzeBombshellTest extends CardTestPlayerBase { - - @Test - public void testEndlessWhispers() { - // When a player other than Bronze Bombshell's owner controls it, that player sacrifices it. - // If the player does, Bronze Bombshell deals 7 damage to the player. - addCard(Zone.BATTLEFIELD, playerA, "Bronze Bombshell", 1); - - // Each creature has "When this creature dies, choose target opponent. - // That player puts this card from its owner's graveyard onto the battlefield - // under their control at the beginning of the next end step." - addCard(Zone.BATTLEFIELD, playerA, "Endless Whispers", 1); - - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); - // Destroy target creature or planeswalker.. - addCard(Zone.HAND, playerA, "Hero's Downfall"); // {1}{B}{B} - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hero's Downfall", "Bronze Bombshell"); - addTarget(playerA, playerB); - - setStopAt(2, PhaseStep.UPKEEP); - execute(); - - assertGraveyardCount(playerA, "Hero's Downfall", 1); - assertGraveyardCount(playerA, "Bronze Bombshell", 1); - - assertLife(playerA, 20); - assertLife(playerB, 13); - - } - -} + +package org.mage.test.cards.control; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX + */ +public class BronzeBombshellTest extends CardTestPlayerBase { + + @Test + public void testEndlessWhispers() { + // When a player other than Bronze Bombshell's owner controls it, that player sacrifices it. + // If the player does, Bronze Bombshell deals 7 damage to the player. + addCard(Zone.BATTLEFIELD, playerA, "Bronze Bombshell", 1); + + // Each creature has "When this creature dies, choose target opponent. + // That player puts this card from its owner's graveyard onto the battlefield + // under their control at the beginning of the next end step." + addCard(Zone.BATTLEFIELD, playerA, "Endless Whispers", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + // Destroy target creature or planeswalker.. + addCard(Zone.HAND, playerA, "Hero's Downfall"); // {1}{B}{B} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hero's Downfall", "Bronze Bombshell"); + addTarget(playerA, playerB); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertGraveyardCount(playerA, "Hero's Downfall", 1); + assertGraveyardCount(playerA, "Bronze Bombshell", 1); + + assertLife(playerA, 20); + assertLife(playerB, 13); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlOfOwnedCreaturesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlOfOwnedCreaturesTest.java new file mode 100644 index 0000000000..d5cc75e601 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlOfOwnedCreaturesTest.java @@ -0,0 +1,115 @@ +package org.mage.test.cards.control; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class GainControlOfOwnedCreaturesTest extends CardTestPlayerBase { + + /** + * + * + */ + @Test + public void TrostaniDiscordantTest() { + // Other creatures you control get +1/+1. + // When Trostani Discordant enters the battlefield, create two 1/1 white Soldier creature tokens with lifelink. + // At the beginning of your end step, each player gains control of all creatures they own. + addCard(Zone.LIBRARY, playerA, "Trostani Discordant", 1); // Creature {3}{G}{W} 1/4 + + // Your opponent can't cast spells with even converted mana costs. (Zero is even.) + // Your opponents can't block with creatures with even converted mana costs. + addCard(Zone.LIBRARY, playerA, "Void Winnower", 1); // Creature 11/9 + skipInitShuffling(); + + // Look at the top ten cards of your library, exile up to two creature cards from among them, then shuffle your library. + // Target opponent may choose one of the exiled cards and put it onto the battlefield under their control. Put the rest onto the battlefield under your control. + addCard(Zone.HAND, playerA, "Dubious Challenge", 1); // Sorcery {3}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dubious Challenge"); + setChoice(playerA, "Trostani Discordant^Void Winnower"); + + addTarget(playerA, playerB); + + setChoice(playerB, "Trostani Discordant"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Dubious Challenge", 1); + assertPermanentCount(playerA, "Void Winnower", 1); + assertPowerToughness(playerA, "Void Winnower", 11, 9); + assertPermanentCount(playerB, "Trostani Discordant", 1); + assertPowerToughness(playerB, "Trostani Discordant", 1, 4); + + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + assertPowerToughness(playerB, "Silvercoat Lion", 3, 3); + } + + /** + * https://www.slightlymagic.net/forum/viewtopic.php?f=70&t=29805&p=243997#p243989 + * + * I was playing a Dubious Challenge Pionner deck. In my game, Trostani + * Discordant was put on the battlefield after Ive launched a Dubious + * Challenge sorcery. My opponent took the Trostani Discordant and I took a + * Void Winnower . Unfortnately, at the end step, the Trostani Discordant + * ability that makes all players gain control of the creatures they OWN + * doesn't happened. + */ + @Test + public void TrostaniDiscordantTriggerTest() { + // Other creatures you control get +1/+1. + // When Trostani Discordant enters the battlefield, create two 1/1 white Soldier creature tokens with lifelink. + // At the beginning of your end step, each player gains control of all creatures they own. + addCard(Zone.LIBRARY, playerA, "Trostani Discordant", 1); // Creature {3}{G}{W} 1/4 + + // Your opponent can't cast spells with even converted mana costs. (Zero is even.) + // Your opponents can't block with creatures with even converted mana costs. + addCard(Zone.LIBRARY, playerA, "Void Winnower", 1); // Creature 11/9 + skipInitShuffling(); + + // Look at the top ten cards of your library, exile up to two creature cards from among them, then shuffle your library. + // Target opponent may choose one of the exiled cards and put it onto the battlefield under their control. Put the rest onto the battlefield under your control. + addCard(Zone.HAND, playerA, "Dubious Challenge", 1); // Sorcery {3}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dubious Challenge"); + setChoice(playerA, "Trostani Discordant^Void Winnower"); + + addTarget(playerA, playerB); + + setChoice(playerB, "Trostani Discordant"); + + setStrictChooseMode(true); + setStopAt(8, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Dubious Challenge", 1); + assertPermanentCount(playerA, "Void Winnower", 1); + assertPowerToughness(playerA, "Void Winnower", 12, 10); + assertPermanentCount(playerA, "Trostani Discordant", 1); + assertPowerToughness(playerA, "Trostani Discordant", 1, 4); + + assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); + assertPowerToughness(playerB, "Silvercoat Lion", 2, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/GontiLordOfLuxuryEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/GontiLordOfLuxuryEffectTest.java index 3f3ae2b439..1877430ba4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/GontiLordOfLuxuryEffectTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/GontiLordOfLuxuryEffectTest.java @@ -1,202 +1,202 @@ - -package org.mage.test.cards.control; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class GontiLordOfLuxuryEffectTest extends CardTestPlayerBase { - - /** - * Returning to your hand a creature you own but is controlled by an - * opponent doesn't let you replay it. Happened after I Aether Tradewinded - * my Rashmi that an opponent cast with Gonti, Lord of Luxury (the exile - * part could have something to do with this?). Then on my turn I couldn't - * replay it. - */ - @Test - public void testCanBeCastAgain() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8); - // Deathtouch - // When Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, - // then put the rest on the bottom of that library in a random order. For as long as that card remains exiled, - // you may look at it, you may cast it, and you may spend mana as though it were mana of any type to cast it. - addCard(Zone.HAND, playerA, "Gonti, Lord of Luxury", 1); // Creature {2}{B}{B} - - addCard(Zone.LIBRARY, playerB, "Rashmi, Eternities Crafter"); // Creature {2}{G}{U} - skipInitShuffling(); - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); - // Return target permanent you control and target permanent you don't control to their owners' hands. - addCard(Zone.HAND, playerB, "Aether Tradewinds", 1); // Intant {2}{U} - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gonti, Lord of Luxury"); - addTarget(playerA, playerB); - setChoice(playerA, "Rashmi, Eternities Crafter"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Rashmi, Eternities Crafter"); - - castSpell(1, PhaseStep.END_TURN, playerB, "Aether Tradewinds", "Silvercoat Lion^Rashmi, Eternities Crafter"); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Rashmi, Eternities Crafter"); - - setStopAt(2, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerB, "Aether Tradewinds", 1); - assertHandCount(playerB, "Silvercoat Lion", 1); - assertHandCount(playerB, "Rashmi, Eternities Crafter", 0); - assertPermanentCount(playerB, "Rashmi, Eternities Crafter", 1); - - } - - /** - * Opponent using Gonti, Lord of Luxury took Mirari's Wake out of my library - * and cast it. I cast Cyclonic Rift on Mirari's Wake to put it back in my - * hand and was unable to recast Mirari's Wake. - */ - @Test - public void testCanBeCastAgainCyclonicRift() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 9); - // Deathtouch - // When Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, - // then put the rest on the bottom of that library in a random order. For as long as that card remains exiled, - // you may look at it, you may cast it, and you may spend mana as though it were mana of any type to cast it. - addCard(Zone.HAND, playerA, "Gonti, Lord of Luxury", 1); // Creature 2/3 {2}{B}{B} - - // Creatures you control get +1/+1. - // Whenever you tap a land for mana, add one mana of any type that land produced. - addCard(Zone.LIBRARY, playerB, "Mirari's Wake"); // Enchantment {3}{G}{W} - skipInitShuffling(); - - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); - addCard(Zone.BATTLEFIELD, playerB, "Plains", 1); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - // Return target nonland permanent you don't control to its owner's hand. - // Overload {6}{U} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.") - addCard(Zone.HAND, playerB, "Cyclonic Rift", 1); // Intant {1}{U} - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gonti, Lord of Luxury"); - addTarget(playerA, playerB); - setChoice(playerA, "Mirari's Wake"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Mirari's Wake"); - castSpell(1, PhaseStep.END_TURN, playerB, "Cyclonic Rift", "Mirari's Wake"); - - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Mirari's Wake"); - - setStopAt(2, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Gonti, Lord of Luxury", 1); - assertPowerToughness(playerA, "Gonti, Lord of Luxury", 2, 3); - assertGraveyardCount(playerB, "Cyclonic Rift", 1); - - assertPermanentCount(playerB, "Mirari's Wake", 1); - assertPowerToughness(playerB, "Silvercoat Lion", 3, 3); - - } - - /** - * I noticed in a game that when you cast Lingering Souls off of Gonti, Lord - * of Luxury and then the lingering souls goes to the graveyard it cannot be - * flashed back. The gonti was my opponent's and the lingering souls was - * mine for reference. - */ - @Test - public void testCanBeCastLaterWithFlashBack() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7); - // Deathtouch - // When Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, - // then put the rest on the bottom of that library in a random order. For as long as that card remains exiled, - // you may look at it, you may cast it, and you may spend mana as though it were mana of any type to cast it. - addCard(Zone.HAND, playerA, "Gonti, Lord of Luxury", 1); // Creature 2/3 {2}{B}{B} - - // Create two 1/1 white Spirit creature tokens with flying. - // Flashback {1}{B} - addCard(Zone.LIBRARY, playerB, "Lingering Souls"); // Sorcery {2}{W} - skipInitShuffling(); - - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gonti, Lord of Luxury"); - addTarget(playerA, playerB); - setChoice(playerA, "Lingering Souls"); - - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lingering Souls"); - - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Flashback"); - - setStopAt(2, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Gonti, Lord of Luxury", 1); - assertPowerToughness(playerA, "Gonti, Lord of Luxury", 2, 3); - assertPermanentCount(playerA, "Spirit", 2); - - assertPermanentCount(playerB, "Spirit", 2); - - assertExileCount("Lingering Souls", 1); - - } - - /** - * An opponent used a Gonti against me, and took my Ob Nixilis Reignited. He - * later played it, and I killed my Ob Nixilis. Then later, using Seasons - * Past, I got Ob Nixilis back in my hand. However, the Ob Nixilis was now - * uncastable. Has anyone else encountered this? - */ - @Test - public void testPlaneswalkerCanBeCastLaterFromHand() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 9); - // Deathtouch - // When Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, - // then put the rest on the bottom of that library in a random order. For as long as that card remains exiled, - // you may look at it, you may cast it, and you may spend mana as though it were mana of any type to cast it. - addCard(Zone.HAND, playerA, "Gonti, Lord of Luxury", 1); // Creature 2/3 {2}{B}{B} - - // +1: You draw a card and you lose 1 life. - // -3: Destroy target creature. - // -8: Target opponent gets an emblem with "Whenever a player draws a card, you lose 2 life." - addCard(Zone.LIBRARY, playerB, "Ob Nixilis Reignited"); // Planeswalker [5] {3}{B}{B} - skipInitShuffling(); - - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3); - addCard(Zone.BATTLEFIELD, playerB, "Forest", 3); - - // Return any number of cards with different converted mana costs from your graveyard to your hand. Put Seasons Past on the bottom of its owner's library. - addCard(Zone.HAND, playerB, "Seasons Past", 1); // Sorcery {4}{G}{G} - - addCard(Zone.BATTLEFIELD, playerB, "Dross Crocodile", 2); // Creature 5/1 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gonti, Lord of Luxury"); - addTarget(playerA, playerB); - setChoice(playerA, "Ob Nixilis Reignited"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Ob Nixilis Reignited"); - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "-3:", "Dross Crocodile"); - - attack(2, playerB, "Dross Crocodile", "Ob Nixilis Reignited"); - - castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Seasons Past"); - setChoice(playerB, "Ob Nixilis Reignited"); - - castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Ob Nixilis Reignited"); - - setStopAt(4, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Gonti, Lord of Luxury", 1); - assertPermanentCount(playerB, "Dross Crocodile", 1); - assertGraveyardCount(playerB, "Dross Crocodile", 1); - - assertGraveyardCount(playerB, "Seasons Past", 0); - - assertHandCount(playerB, "Ob Nixilis Reignited", 0); - assertPermanentCount(playerB, "Ob Nixilis Reignited", 1); - - } -} + +package org.mage.test.cards.control; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class GontiLordOfLuxuryEffectTest extends CardTestPlayerBase { + + /** + * Returning to your hand a creature you own but is controlled by an + * opponent doesn't let you replay it. Happened after I Aether Tradewinded + * my Rashmi that an opponent cast with Gonti, Lord of Luxury (the exile + * part could have something to do with this?). Then on my turn I couldn't + * replay it. + */ + @Test + public void testCanBeCastAgain() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8); + // Deathtouch + // When Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, + // then put the rest on the bottom of that library in a random order. For as long as that card remains exiled, + // you may look at it, you may cast it, and you may spend mana as though it were mana of any type to cast it. + addCard(Zone.HAND, playerA, "Gonti, Lord of Luxury", 1); // Creature {2}{B}{B} + + addCard(Zone.LIBRARY, playerB, "Rashmi, Eternities Crafter"); // Creature {2}{G}{U} + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); + // Return target permanent you control and target permanent you don't control to their owners' hands. + addCard(Zone.HAND, playerB, "Aether Tradewinds", 1); // Intant {2}{U} + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gonti, Lord of Luxury"); + addTarget(playerA, playerB); + setChoice(playerA, "Rashmi, Eternities Crafter"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Rashmi, Eternities Crafter"); + + castSpell(1, PhaseStep.END_TURN, playerB, "Aether Tradewinds", "Silvercoat Lion^Rashmi, Eternities Crafter"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Rashmi, Eternities Crafter"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, "Aether Tradewinds", 1); + assertHandCount(playerB, "Silvercoat Lion", 1); + assertHandCount(playerB, "Rashmi, Eternities Crafter", 0); + assertPermanentCount(playerB, "Rashmi, Eternities Crafter", 1); + + } + + /** + * Opponent using Gonti, Lord of Luxury took Mirari's Wake out of my library + * and cast it. I cast Cyclonic Rift on Mirari's Wake to put it back in my + * hand and was unable to recast Mirari's Wake. + */ + @Test + public void testCanBeCastAgainCyclonicRift() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 9); + // Deathtouch + // When Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, + // then put the rest on the bottom of that library in a random order. For as long as that card remains exiled, + // you may look at it, you may cast it, and you may spend mana as though it were mana of any type to cast it. + addCard(Zone.HAND, playerA, "Gonti, Lord of Luxury", 1); // Creature 2/3 {2}{B}{B} + + // Creatures you control get +1/+1. + // Whenever you tap a land for mana, add one mana of any type that land produced. + addCard(Zone.LIBRARY, playerB, "Mirari's Wake"); // Enchantment {3}{G}{W} + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + // Return target nonland permanent you don't control to its owner's hand. + // Overload {6}{U} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.") + addCard(Zone.HAND, playerB, "Cyclonic Rift", 1); // Intant {1}{U} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gonti, Lord of Luxury"); + addTarget(playerA, playerB); + setChoice(playerA, "Mirari's Wake"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Mirari's Wake"); + castSpell(1, PhaseStep.END_TURN, playerB, "Cyclonic Rift", "Mirari's Wake"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Mirari's Wake"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Gonti, Lord of Luxury", 1); + assertPowerToughness(playerA, "Gonti, Lord of Luxury", 2, 3); + assertGraveyardCount(playerB, "Cyclonic Rift", 1); + + assertPermanentCount(playerB, "Mirari's Wake", 1); + assertPowerToughness(playerB, "Silvercoat Lion", 3, 3); + + } + + /** + * I noticed in a game that when you cast Lingering Souls off of Gonti, Lord + * of Luxury and then the lingering souls goes to the graveyard it cannot be + * flashed back. The gonti was my opponent's and the lingering souls was + * mine for reference. + */ + @Test + public void testCanBeCastLaterWithFlashBack() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7); + // Deathtouch + // When Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, + // then put the rest on the bottom of that library in a random order. For as long as that card remains exiled, + // you may look at it, you may cast it, and you may spend mana as though it were mana of any type to cast it. + addCard(Zone.HAND, playerA, "Gonti, Lord of Luxury", 1); // Creature 2/3 {2}{B}{B} + + // Create two 1/1 white Spirit creature tokens with flying. + // Flashback {1}{B} + addCard(Zone.LIBRARY, playerB, "Lingering Souls"); // Sorcery {2}{W} + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gonti, Lord of Luxury"); + addTarget(playerA, playerB); + setChoice(playerA, "Lingering Souls"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lingering Souls"); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Flashback"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Gonti, Lord of Luxury", 1); + assertPowerToughness(playerA, "Gonti, Lord of Luxury", 2, 3); + assertPermanentCount(playerA, "Spirit", 2); + + assertPermanentCount(playerB, "Spirit", 2); + + assertExileCount("Lingering Souls", 1); + + } + + /** + * An opponent used a Gonti against me, and took my Ob Nixilis Reignited. He + * later played it, and I killed my Ob Nixilis. Then later, using Seasons + * Past, I got Ob Nixilis back in my hand. However, the Ob Nixilis was now + * uncastable. Has anyone else encountered this? + */ + @Test + public void testPlaneswalkerCanBeCastLaterFromHand() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 9); + // Deathtouch + // When Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, + // then put the rest on the bottom of that library in a random order. For as long as that card remains exiled, + // you may look at it, you may cast it, and you may spend mana as though it were mana of any type to cast it. + addCard(Zone.HAND, playerA, "Gonti, Lord of Luxury", 1); // Creature 2/3 {2}{B}{B} + + // +1: You draw a card and you lose 1 life. + // -3: Destroy target creature. + // -8: Target opponent gets an emblem with "Whenever a player draws a card, you lose 2 life." + addCard(Zone.LIBRARY, playerB, "Ob Nixilis Reignited"); // Planeswalker [5] {3}{B}{B} + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 3); + + // Return any number of cards with different converted mana costs from your graveyard to your hand. Put Seasons Past on the bottom of its owner's library. + addCard(Zone.HAND, playerB, "Seasons Past", 1); // Sorcery {4}{G}{G} + + addCard(Zone.BATTLEFIELD, playerB, "Dross Crocodile", 2); // Creature 5/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gonti, Lord of Luxury"); + addTarget(playerA, playerB); + setChoice(playerA, "Ob Nixilis Reignited"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Ob Nixilis Reignited"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "-3:", "Dross Crocodile"); + + attack(2, playerB, "Dross Crocodile", "Ob Nixilis Reignited"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Seasons Past"); + setChoice(playerB, "Ob Nixilis Reignited"); + + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Ob Nixilis Reignited"); + + setStopAt(4, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Gonti, Lord of Luxury", 1); + assertPermanentCount(playerB, "Dross Crocodile", 1); + assertGraveyardCount(playerB, "Dross Crocodile", 1); + + assertGraveyardCount(playerB, "Seasons Past", 0); + + assertHandCount(playerB, "Ob Nixilis Reignited", 0); + assertPermanentCount(playerB, "Ob Nixilis Reignited", 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/VengefulPharaohTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/VengefulPharaohTest.java index 9a543562b4..0e0ae36cb5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/VengefulPharaohTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/VengefulPharaohTest.java @@ -1,48 +1,48 @@ - -package org.mage.test.cards.control; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class VengefulPharaohTest extends CardTestPlayerBase { - - @Test - public void controlledByOtherBeforeGraveyardTriggerTest() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); - // Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.) - // Whenever combat damage is dealt to you or a planeswalker you control, if Vengeful Pharaoh is in your graveyard, destroy target attacking creature, then put Vengeful Pharaoh on top of your library. - addCard(Zone.HAND, playerA, "Vengeful Pharaoh", 1); // Creature 5/4 {2}{B}{B}{B} - // Destroy target permanent. - addCard(Zone.HAND, playerA, "Vindicate", 1); // Sorcery {1}{W}{B} - - addCard(Zone.HAND, playerB, "Control Magic", 1); // Enchantment - addCard(Zone.BATTLEFIELD, playerB, "Island", 4); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vengeful Pharaoh"); - - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Control Magic", "Vengeful Pharaoh"); - - castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Vindicate", "Vengeful Pharaoh"); - - attack(4, playerB, "Silvercoat Lion"); - - setStopAt(4, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertGraveyardCount(playerA, "Vindicate", 1); - assertGraveyardCount(playerB, "Control Magic", 1); - assertGraveyardCount(playerB, "Silvercoat Lion", 1); - assertGraveyardCount(playerA, "Vengeful Pharaoh", 0); - assertLibraryCount(playerA, "Vengeful Pharaoh", 1); - - assertLife(playerA, 18); - } -} + +package org.mage.test.cards.control; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class VengefulPharaohTest extends CardTestPlayerBase { + + @Test + public void controlledByOtherBeforeGraveyardTriggerTest() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.) + // Whenever combat damage is dealt to you or a planeswalker you control, if Vengeful Pharaoh is in your graveyard, destroy target attacking creature, then put Vengeful Pharaoh on top of your library. + addCard(Zone.HAND, playerA, "Vengeful Pharaoh", 1); // Creature 5/4 {2}{B}{B}{B} + // Destroy target permanent. + addCard(Zone.HAND, playerA, "Vindicate", 1); // Sorcery {1}{W}{B} + + addCard(Zone.HAND, playerB, "Control Magic", 1); // Enchantment + addCard(Zone.BATTLEFIELD, playerB, "Island", 4); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vengeful Pharaoh"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Control Magic", "Vengeful Pharaoh"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Vindicate", "Vengeful Pharaoh"); + + attack(4, playerB, "Silvercoat Lion"); + + setStopAt(4, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Vindicate", 1); + assertGraveyardCount(playerB, "Control Magic", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + assertGraveyardCount(playerA, "Vengeful Pharaoh", 0); + assertLibraryCount(playerA, "Vengeful Pharaoh", 1); + + assertLife(playerA, 18); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java index 1cb16170ab..e6b6eb16ef 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.copy; import mage.constants.CardType; @@ -9,7 +8,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class CleverImpersonatorTest extends CardTestPlayerBase { @@ -54,22 +52,29 @@ public class CleverImpersonatorTest extends CardTestPlayerBase { */ @Test public void testCopyPlaneswalker() { - addCard(Zone.BATTLEFIELD, playerA, "Island", 4); - // You may have Clever Impersonator enter the battlefield as a copy of any nonland permanent on the battlefield. addCard(Zone.HAND, playerA, "Clever Impersonator", 1); // {2}{U}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); // +2: Each player discards a card. // -X: Return target nonlegendary creature with converted mana cost X from your graveyard to the battlefield. // -8: You get an emblem with "Whenever a creature dies, return it to the battlefield under your control at the beginning of the next end step."; addCard(Zone.BATTLEFIELD, playerB, "Liliana, Defiant Necromancer", 1); + // + addCard(Zone.HAND, playerA, "Balduvian Bears", 1); + addCard(Zone.HAND, playerB, "Balduvian Bears", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Clever Impersonator"); - setChoice(playerA, "Liliana, Defiant Necromancer"); + setChoice(playerA, "Yes"); // make copy + setChoice(playerA, "Liliana, Defiant Necromancer"); // copy target activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: Each player discards a card"); + addTarget(playerA, "Balduvian Bears"); // discard + addTarget(playerB, "Balduvian Bears"); // discard + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertHandCount(playerA, "Clever Impersonator", 0); assertCounterCount(playerB, "Liliana, Defiant Necromancer", CounterType.LOYALTY, 3); // 3 @@ -157,8 +162,7 @@ public class CleverImpersonatorTest extends CardTestPlayerBase { * Reported bug: could not use Clever Impersonator to copy Dawn's Reflection */ @Test - public void dawnsReflectionCopiedByImpersonator() - { + public void dawnsReflectionCopiedByImpersonator() { String impersonator = "Clever Impersonator"; String dReflection = "Dawn's Reflection"; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java index 0d4a005c3c..d38e19f92a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java @@ -3,9 +3,11 @@ package org.mage.test.cards.copy; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectsList; import mage.cards.Card; +import mage.constants.EmptyNames; import mage.constants.PhaseStep; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.Filter; import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.NamePredicate; @@ -231,4 +233,60 @@ public class CloneTest extends CardTestPlayerBase { Assert.assertTrue("The cloned Adaptive Automaton should be a Goblin", clone.hasSubtype(SubType.GOBLIN, currentGame)); } + /** + * Cloning a face-down creature should produce a plain 2/2 creature #3582 + * + * What I expected: Using a clone effect on a face-down creature should + * create another 2/2 creature. + * + * What I observed: The resulting token appears face-up as a copy of the + * face-down creature's front face, copying its power/toughness but not + * abilities. + * + * To reproduce: Play a Terastodon, then an Ixidron. Terastodon is turned + * face-down. Then play a clone creature (such as Progenitor Mimic). It + * appears as a face-up 9/9 Terastodon token, but without any ETB triggers. + * The next turn, when it copies itself, it creates a Terastodon that does + * have its ETB ability. + * + * https://blogs.magicjudges.org/articles/2014/09/16/morph-rules-problems-face-down-in-a-face-up-world/ + * explains that cloning a face-down 2/2 creature should create another 2/2 + * creature with no abilities (see Face-down Creatures and Clone). + */ + + @Test + public void testCloningFaceDownCreature() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + // As Ixidron enters the battlefield, turn all other nontoken creatures face down. + // Ixidron's power and toughness are each equal to the number of face-down creatures on the battlefield. + addCard(Zone.HAND, playerA, "Ixidron"); // Creature {3}{U}{U} + + // When Terastodon enters the battlefield, you may destroy up to three target noncreature permanents. + // For each permanent put into a graveyard this way, its controller creates a 3/3 green Elephant creature token. + addCard(Zone.BATTLEFIELD, playerA, "Terastodon", 1); // Creature 9/9 {6}{G}{G} + + // You may have Clone enter the battlefield as a copy of any creature on the battlefield. + addCard(Zone.HAND, playerA, "Clone"); // Creature {3}{U} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ixidron"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Clone"); + setChoice(playerA, "Yes"); + setChoice(playerA, EmptyNames.FACE_DOWN_CREATURE.toString()); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + + assertPermanentCount(playerA, "Terastodon", 0); + + assertPermanentCount(playerA, "Ixidron", 1); + assertPowerToughness(playerA, "Ixidron", 1, 1); + + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2); + assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2, Filter.ComparisonScope.All); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/EssenceOfTheWildCopyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/EssenceOfTheWildCopyTest.java index 07fb2dcec3..b83b65c939 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/EssenceOfTheWildCopyTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/EssenceOfTheWildCopyTest.java @@ -1,19 +1,19 @@ package org.mage.test.cards.copy; -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.filter.Filter; +import mage.game.Game; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; /** * @author JayDi85 @@ -22,7 +22,6 @@ public class EssenceOfTheWildCopyTest extends CardTestPlayerBase { // Essence of the Wild {3}{G}{G}{G} // Creatures you control enter the battlefield as a copy of Essence of the Wild. - private Permanent findCopyPermanent(Game game, int num) { int currentCopyNumber = 1; for (Permanent perm : game.getBattlefield().getAllActivePermanents()) { @@ -169,4 +168,36 @@ public class EssenceOfTheWildCopyTest extends CardTestPlayerBase { Assert.assertEquals("copy must have 6 p/t", 6, copy.getPower().getValue()); Assert.assertEquals("copy must have 6 p/t", 6, copy.getToughness().getValue()); } + + @Test + public void testCopyFromTheCopy() { + // Creatures you control enter the battlefield as a copy of Essence of the Wild. + addCard(Zone.BATTLEFIELD, playerA, "Essence of the Wild", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.HAND, playerA, "Silvercoat Lion", 2); + + // Destroy target nonartifact, nonblack creature. It can't be regenerated. + addCard(Zone.HAND, playerB, "Terror", 1); // Instant {1}{B} + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Terror", "Essence of the Wild[no copy]"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Terror", 1); + assertGraveyardCount(playerA, "Essence of the Wild", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 0); + assertPermanentCount(playerA, "Essence of the Wild", 2); + + assertPowerToughness(playerA, "Essence of the Wild", 6, 6, Filter.ComparisonScope.All); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/FeldonOfTheThirdPathTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/FeldonOfTheThirdPathTest.java index 00330d4932..4e4f13b750 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/FeldonOfTheThirdPathTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/FeldonOfTheThirdPathTest.java @@ -1,9 +1,8 @@ - - package org.mage.test.cards.copy; import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -14,7 +13,6 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * addition to its other types. It gains haste. Sacrifice it at the beginning of * the next end step. * - * * @author LevelX2 */ public class FeldonOfTheThirdPathTest extends CardTestPlayerBase { @@ -22,27 +20,41 @@ public class FeldonOfTheThirdPathTest extends CardTestPlayerBase { /** * Checking that enters the battlefield abilities of the copied creature * card works. - * */ @Test public void testETBEffect() { - // When Highway Robber enters the battlefield, target opponent loses 2 life and you gain 2 life. - addCard(Zone.GRAVEYARD, playerA, "Highway Robber", 1); + // {2}{R}, {T}: Create a token that's a copy of target creature card in your graveyard, except it's an artifact + // in addition to its other types. It gains haste. Sacrifice it at the beginning of the next end step. addCard(Zone.BATTLEFIELD, playerA, "Feldon of the Third Path", 1); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + // When Highway Robber enters the battlefield, target opponent loses 2 life and you gain 2 life. + addCard(Zone.GRAVEYARD, playerA, "Highway Robber", 1); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, - "{2}{R}, {T}: Create a token that's a copy of target creature card in your graveyard, except it's an artifact in addition to its other types. It gains haste. Sacrifice it at the beginning of the next end step.", - "Highway Robber"); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}, {T}: Create a token", "Highway Robber"); + addTarget(playerA, playerB); // opponent to loses 2 life + waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("must have token", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Highway Robber", 1); + checkPermanentCount("must have card", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Feldon of the Third Path", 1); + + // destroy token + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Highway Robber"); + waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("must haven't token", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Highway Robber", 0); + + setStrictChooseMode(true); setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); - - assertPermanentCount(playerA, "Highway Robber", 1); - assertPermanentCount(playerA, "Feldon of the Third Path", 1); + assertAllCommandsUsed(); assertLife(playerA, 22); // +2 from Robber assertLife(playerB, 18); // -2 from Robber + // possible bug: triggers from destroyed permanents keeps in game state (e.g. 2 triggers in game state) + Assert.assertEquals("game state must have only 1 trigger from original card", 1, currentGame.getState().getTriggers().size()); } @Test @@ -59,8 +71,11 @@ public class FeldonOfTheThirdPathTest extends CardTestPlayerBase { "{2}{R}, {T}: Create a token that's a copy of target creature card in your graveyard, except it's an artifact in addition to its other types. It gains haste. Sacrifice it at the beginning of the next end step.", "Sepulchral Primordial"); addTarget(playerA, "Silvercoat Lion"); // target for ETB Sepulchral Primordial + + setStrictChooseMode(true); setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Feldon of the Third Path", 1); assertPermanentCount(playerA, "Sepulchral Primordial", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java index fde9b4d0c7..36bcc01fbe 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java @@ -1,27 +1,23 @@ package org.mage.test.cards.copy; import mage.abilities.keyword.DeathtouchAbility; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.permanent.Permanent; import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; -/** - * - * Lazav, Dimir Mastermind - * - * Legendary Creature — Shapeshifter 3/3, UUBB - * Hexproof - * Whenever a creature card is put into an opponent's graveyard from anywhere, you may have - * Lazav, Dimir Mastermind become a copy of that card except its name is still - * Lazav, Dimir Mastermind, it's legendary in addition to its other types, and - * it gains hexproof and this ability. - * - * @author LevelX2 +/* + Lazav, Dimir Mastermind + Legendary Creature — Shapeshifter 3/3, UUBB + Hexproof Whenever a creature card is put into an opponent's graveyard from anywhere, you may have + Lazav, Dimir Mastermind become a copy of that card except its name is still Lazav, Dimir Mastermind, + it's legendary in addition to its other types, and it gains hexproof and this ability. + + @author LevelX2 */ public class LazavDimirMastermindTest extends CardTestPlayerBase { @@ -32,15 +28,15 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase { public void testCopySimpleCreature() { addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1); // Codex Shredder - Artifact - // {T}: Target player puts the top card of their library into their graveyard. + // {T}: Target player mills a card. // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand. addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1); // Flying 3/2 - addCard(Zone.LIBRARY, playerB, "Assault Griffin",5); + addCard(Zone.LIBRARY, playerB, "Assault Griffin", 5); skipInitShuffling(); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB); setStopAt(1, PhaseStep.END_TURN); execute(); @@ -50,7 +46,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase { Permanent lazav = getPermanent("Lazav, Dimir Mastermind", playerA.getId()); Assert.assertTrue(lazav.getSubtype(currentGame).contains(SubType.GRIFFIN)); - Assert.assertTrue("Lazav, Dimir Mastermind must have flying",lazav.getAbilities().contains(FlyingAbility.getInstance())); + Assert.assertTrue("Lazav, Dimir Mastermind must have flying", lazav.getAbilities().contains(FlyingAbility.getInstance())); } /** @@ -64,10 +60,10 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase { // Whenever another nontoken creature dies, you may put a 1/1 black Rat creature token onto the battlefield. // Rats you control have deathtouch. - addCard(Zone.LIBRARY, playerB, "Ogre Slumlord",5); + addCard(Zone.LIBRARY, playerB, "Ogre Slumlord", 5); skipInitShuffling(); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB); setStopAt(1, PhaseStep.END_TURN); execute(); @@ -83,15 +79,13 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase { } - /** + /* * Tests copy Nightveil Specter * - * Nightveil Specter - * Creature — Specter 2/3, {U/B}{U/B}{U/B} + * Nightveil Specter Creature — Specter 2/3, {U/B}{U/B}{U/B} * Flying - * Whenever Nightveil Specter deals combat damage to a player, that player exiles the top card of their library. - * You may play cards exiled with Nightveil Specter. - * + * Whenever Nightveil Specter deals combat damage to a player, that player exiles the + * top card of their library. You may play cards exiled with Nightveil Specter. */ @Test public void testCopyNightveilSpecter() { @@ -99,11 +93,11 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1); addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1); - addCard(Zone.LIBRARY, playerB, "Silvercoat Lion",2); - addCard(Zone.LIBRARY, playerB, "Nightveil Specter",1); + addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 2); + addCard(Zone.LIBRARY, playerB, "Nightveil Specter", 1); skipInitShuffling(); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB); attack(3, playerA, "Lazav, Dimir Mastermind"); @@ -130,15 +124,15 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1); addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1); - addCard(Zone.LIBRARY, playerB, "Silvercoat Lion",2); - addCard(Zone.LIBRARY, playerB, "Nightveil Specter",1); + addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 2); + addCard(Zone.LIBRARY, playerB, "Nightveil Specter", 1); skipInitShuffling(); // Lazav becomes a Nightveil Specter - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB); // Lazav becomes a Silvercoat Lion - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB); setStopAt(3, PhaseStep.END_TURN); execute(); @@ -151,52 +145,53 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase { Assert.assertTrue(lazav.isLegendary()); } + /** * Tests old copy is discarded after reanmiation of Lazav */ @Test public void testCopyAfterReanimation() { - addCard(Zone.BATTLEFIELD, playerA ,"Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); // Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost. - addCard(Zone.HAND, playerA ,"Reanimate"); + addCard(Zone.HAND, playerA, "Reanimate"); addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1); // Codex Shredder - Artifact - // {T}: Target player puts the top card of their library into their graveyard. + // {T}: Target player mills a card. // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand. addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1); - addCard(Zone.BATTLEFIELD, playerB ,"Swamp", 3); + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3); // Flying 3/2 - addCard(Zone.LIBRARY, playerB, "Assault Griffin",1); + addCard(Zone.LIBRARY, playerB, "Assault Griffin", 1); // Target opponent sacrifices a creature. You gain life equal to that creature's toughness. - addCard(Zone.HAND, playerB ,"Tribute to Hunger"); - + addCard(Zone.HAND, playerB, "Tribute to Hunger"); + skipInitShuffling(); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tribute to Hunger"); - + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Reanimate", "Lazav, Dimir Mastermind"); - + setStopAt(1, PhaseStep.END_TURN); execute(); assertGraveyardCount(playerB, "Tribute to Hunger", 1); assertGraveyardCount(playerA, "Reanimate", 1); - + assertLife(playerA, 16); // -4 from Reanmiate - assertLife(playerB, 22); // +3 from Tribute to Hunger because Lazav is 3/2 - + assertLife(playerB, 22); // +3 from Tribute to Hunger because Lazav is 3/2 + assertPermanentCount(playerA, "Lazav, Dimir Mastermind", 1); assertPowerToughness(playerA, "Lazav, Dimir Mastermind", 3, 3); Permanent lazav = getPermanent("Lazav, Dimir Mastermind", playerA.getId()); Assert.assertFalse(lazav.getSubtype(currentGame).contains(SubType.GRIFFIN)); // no Griffin type - Assert.assertFalse("Lazav, Dimir Mastermind must have flying",lazav.getAbilities().contains(FlyingAbility.getInstance())); - - + Assert.assertFalse("Lazav, Dimir Mastermind must have flying", lazav.getAbilities().contains(FlyingAbility.getInstance())); + + } - + /** * Tests if Lazav remains a copy of the creature after it is exiled */ @@ -204,20 +199,20 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase { public void testCopyCreatureExiled() { addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1); // Codex Shredder - Artifact - // {T}: Target player puts the top card of their library into their graveyard. + // {T}: Target player mills a card. // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand. addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1); - + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); - + addCard(Zone.HAND, playerA, "Rest in Peace", 1); // Flying 3/2 - addCard(Zone.LIBRARY, playerB, "Assault Griffin",5); + addCard(Zone.LIBRARY, playerB, "Assault Griffin", 5); skipInitShuffling(); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB); - + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest in Peace"); setStopAt(1, PhaseStep.END_TURN); @@ -228,6 +223,6 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase { Permanent lazav = getPermanent("Lazav, Dimir Mastermind", playerA.getId()); Assert.assertTrue(lazav.getSubtype(currentGame).contains(SubType.GRIFFIN)); - Assert.assertTrue("Lazav, Dimir Mastermind must have flying",lazav.getAbilities().contains(FlyingAbility.getInstance())); + Assert.assertTrue("Lazav, Dimir Mastermind must have flying", lazav.getAbilities().contains(FlyingAbility.getInstance())); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java index fc01423064..7c3d07f2e4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java @@ -229,7 +229,7 @@ public class SparkDoubleTest extends CardTestPlayerBase { // make copy of copy by CreateTokenCopyTargetEffect // showBattlefield("before last copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA); - // showAvaileableAbilities("before last copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + // showAvailableAbilities("before last copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "create a token that"); addTarget(playerA, "Akroma, Angel of Wrath[only copy]"); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SpelltwineTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SpelltwineTest.java index f4859dc486..b6e792f528 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SpelltwineTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SpelltwineTest.java @@ -48,7 +48,6 @@ public class SpelltwineTest extends CardTestPlayerBase { * after this, failing to be in the stack box or resolve all. */ @Test - @Ignore // TODO: test is wrong -- mirari exile cards and must cast their copies, on copies cast mirari triggers again (two times). public void testCopyCardsMirari() { addCard(Zone.BATTLEFIELD, playerA, "Island", 9); // Exile target instant or sorcery card from your graveyard and target instant or sorcery card from an opponent's graveyard. @@ -69,10 +68,8 @@ public class SpelltwineTest extends CardTestPlayerBase { // cast spellwin castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spelltwine"); - addTarget(playerA, "Impulse"); // target 1 to excile - addTarget(playerA, "Blasphemous Act"); // target 2 to excile - - + addTarget(playerA, "Impulse"); // target 1 to exile + addTarget(playerA, "Blasphemous Act"); // target 2 to exile setChoice(playerA, "Yes"); // pay {3} and copy spell setChoice(playerA, "Yes"); // Change targets diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java index d8f4d44902..f79cf477d3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java @@ -1,27 +1,25 @@ package org.mage.test.cards.copy; import mage.abilities.Ability; -import mage.constants.SubType; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.discard.DiscardControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.permanent.Permanent; - import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** * Volrath's Shapeshifter - * - * As long as the top card of your graveyard is a creature card, - * Volrath's Shapeshifter has the full text of that card and has the - * text "2: Discard a card." (Volrath's Shapeshifter has that card's name, - * mana cost, color, types, abilities, power, and toughness.) - * + *

+ * As long as the top card of your graveyard is a creature card, Volrath's + * Shapeshifter has the full text of that card and has the text "2: Discard a + * card." (Volrath's Shapeshifter has that card's name, mana cost, color, types, + * abilities, power, and toughness.) */ public class VolrathsShapshifterTest extends CardTestPlayerBase { @@ -36,9 +34,11 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase { addCard(Zone.GRAVEYARD, playerA, "Assault Griffin", 1); addCard(Zone.LIBRARY, playerA, "Forest", 1); skipInitShuffling(); - + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Assault Griffin", 1); assertPowerToughness(playerA, "Assault Griffin", 3, 2); @@ -48,15 +48,16 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase { Assert.assertTrue("Volrath's Shapeshifter must have flying", shapeshifter.getAbilities().contains(FlyingAbility.getInstance())); Assert.assertTrue("Volrath's Shapeshifter must have {2} : Discard a card", hasShapeshiftersOriginalAbility(shapeshifter)); } - + /** - * Tests turing back into Volrath's Shapeshifter after a new card is put on top that isn't a creature + * Tests turing back into Volrath's Shapeshifter after a new card is put on + * top that isn't a creature */ @Test - public void testLosingCopy() { + public void testLosingCopy() { addCard(Zone.BATTLEFIELD, playerA, "Volrath's Shapeshifter", 1); // Codex Shredder - Artifact - // {T}: Target player puts the top card of their library into their graveyard. + // {T}: Target player mills a card. // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand. addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1); @@ -65,10 +66,12 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase { addCard(Zone.LIBRARY, playerA, "Forest", 1); skipInitShuffling(); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerA); setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Volrath's Shapeshifter", 1); assertPowerToughness(playerA, "Volrath's Shapeshifter", 0, 1); @@ -77,46 +80,46 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase { Assert.assertTrue(shapeshifter.getSubtype(currentGame).contains(SubType.SHAPESHIFTER)); Assert.assertTrue("Volrath's Shapeshifter must have {2} : Discard a card", hasShapeshiftersOriginalAbility(shapeshifter)); } - + @Test public void testControlChange() { addCard(Zone.BATTLEFIELD, playerA, "Island", 5); addCard(Zone.HAND, playerA, "Mind Control", 1); - addCard(Zone.LIBRARY, playerA, "Forest", 1); addCard(Zone.GRAVEYARD, playerA, "Dutiful Thrull", 1); - + addCard(Zone.BATTLEFIELD, playerB, "Volrath's Shapeshifter", 1); addCard(Zone.GRAVEYARD, playerB, "Assault Griffin", 1); - + skipInitShuffling(); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mind Control", "Assault Griffin"); - + setStopAt(1, PhaseStep.END_TURN); + + setStrictChooseMode(true); execute(); - + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Dutiful Thrull", 1); assertPowerToughness(playerA, "Dutiful Thrull", 1, 1); Permanent shapeshifter = getPermanent("Dutiful Thrull", playerA.getId()); Assert.assertTrue(shapeshifter.getSubtype(currentGame).contains(SubType.THRULL)); Assert.assertTrue("Volrath's Shapeshifter must have {2} : Discard a card", hasShapeshiftersOriginalAbility(shapeshifter)); - - } - + private boolean hasShapeshiftersOriginalAbility(Permanent shapeshifter) { for (Ability ability : shapeshifter.getAbilities()) { - if(ability instanceof SimpleActivatedAbility) { - SimpleActivatedAbility simpleActivatedAbility = (SimpleActivatedAbility)ability; - return simpleActivatedAbility.getZone() == Zone.BATTLEFIELD && simpleActivatedAbility.getEffects().size() == 1 && - simpleActivatedAbility.getEffects().get(0) instanceof DiscardControllerEffect && simpleActivatedAbility.getManaCosts().size() == 1 - && simpleActivatedAbility.getManaCosts().get(0) instanceof GenericManaCost && simpleActivatedAbility.getManaCosts().get(0).convertedManaCost() == 2; - } - } - + if (ability instanceof SimpleActivatedAbility) { + SimpleActivatedAbility simpleActivatedAbility = (SimpleActivatedAbility) ability; + return simpleActivatedAbility.getZone() == Zone.BATTLEFIELD && simpleActivatedAbility.getEffects().size() == 1 + && simpleActivatedAbility.getEffects().get(0) instanceof DiscardControllerEffect && simpleActivatedAbility.getManaCosts().size() == 1 + && simpleActivatedAbility.getManaCosts().get(0) instanceof GenericManaCost && simpleActivatedAbility.getManaCosts().get(0).convertedManaCost() == 2; + } + } + return false; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java index 6fdacc5560..92ba5af9a8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java @@ -39,14 +39,20 @@ public class AdventureCardsTest extends CardTestPlayerBase { @Test public void testCantCastTreatsToShareTwice() { - setStrictChooseMode(true); - addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); addCard(Zone.HAND, playerA, "Curious Pair"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + checkPlayableAbility("can play on first", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("can't play on second", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", false); + + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - assertActionsCount(playerA, 1); + assertAllCommandsUsed(); + assertHandCount(playerA, 0); assertPermanentCount(playerA, "Food", 1); assertExileCount(playerA, "Curious Pair", 1); @@ -430,18 +436,21 @@ public class AdventureCardsTest extends CardTestPlayerBase { @Test public void testCantCastCuriousPairWithMelek() { - setStrictChooseMode(true); - addCard(Zone.BATTLEFIELD, playerA, "Melek, Izzet Paragon"); - addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); removeAllCardsFromLibrary(playerA); + + addCard(Zone.BATTLEFIELD, playerA, "Melek, Izzet Paragon"); + // addCard(Zone.LIBRARY, playerA, "Curious Pair"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair"); + checkPlayableAbility("can't play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", false); + checkPlayableAbility("can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", true); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); - assertActionsCount(playerA, 1); assertPermanentCount(playerA, "Curious Pair", 0); assertLibraryCount(playerA, 1); } @@ -477,19 +486,21 @@ public class AdventureCardsTest extends CardTestPlayerBase { @Test public void testCantCastTreatsToShareWithGarruksHorde() { - setStrictChooseMode(true); - addCard(Zone.BATTLEFIELD, playerA, "Garruk's Horde"); - addCard(Zone.BATTLEFIELD, playerA, "Forest"); removeAllCardsFromLibrary(playerA); + + addCard(Zone.BATTLEFIELD, playerA, "Garruk's Horde"); + // addCard(Zone.LIBRARY, playerA, "Curious Pair"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); - // showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); + checkPlayableAbility("can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", true); + checkPlayableAbility("can't play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", false); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); - assertActionsCount(playerA, 1); assertPermanentCount(playerA, "Food", 0); assertLibraryCount(playerA, 1); } @@ -514,7 +525,7 @@ public class AdventureCardsTest extends CardTestPlayerBase { addCounters(1, PhaseStep.UPKEEP, playerA, "Wrenn and Six", CounterType.LOYALTY, 5); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-7: You get an emblem"); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); - // showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + // showAvailableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA); // retrace - You may cast this card from your graveyard by discarding a land card as an additional cost to cast it castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); @@ -551,7 +562,7 @@ public class AdventureCardsTest extends CardTestPlayerBase { activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Until your next"); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); - // showAvaileableAbilities("abils", 1, PhaseStep.BEGIN_COMBAT, playerA); + // showAvailableAbilities("abils", 1, PhaseStep.BEGIN_COMBAT, playerA); castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Treats to Share"); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); @@ -639,4 +650,43 @@ public class AdventureCardsTest extends CardTestPlayerBase { execute(); assertAllCommandsUsed(); } + + @Test + public void test_BonecrusherGiant_Stopm() { + // bug with non working stopm: https://github.com/magefree/mage/issues/6915 + + // If noncombat damage would be dealt to Stormwild Capridor, prevent that damage. + // Put a +1/+1 counter on Stormwild Capridor for each 1 damage prevented this way. + addCard(Zone.BATTLEFIELD, playerA, "Stormwild Capridor@storm", 2); // 1/3 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + // Stomp {1}{R} + // Damage can’t be prevented this turn. Stomp deals 2 damage to any target. + addCard(Zone.HAND, playerA, "Bonecrusher Giant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + // prevent + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "@storm.1"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("prevent", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkGraveyardCount("prevent", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@storm.1", 0); + + // prepare protect by stomp + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stomp"); + addTarget(playerA, playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // can't prevent + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "@storm.2"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("can't prevent", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 2); + checkGraveyardCount("can't prevent", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@storm.2", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/BolassCitadelTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/BolassCitadelTest.java index 8fba1058df..66398a7dff 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/BolassCitadelTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/BolassCitadelTest.java @@ -129,4 +129,34 @@ public class BolassCitadelTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Ferocious Zheng", 1); assertLife(playerA, 20 - 3); } + + @Test + public void testOpponentCantUseMyBolas() { + // https://github.com/magefree/mage/issues/6741 + removeAllCardsFromLibrary(playerA); + removeAllCardsFromLibrary(playerB); + + // You may play the top card of your library. If you cast a spell this way, pay life equal to its converted mana cost rather than pay its mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel"); + // + addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 2); // {1}{G} + addCard(Zone.LIBRARY, playerB, "Grizzly Bears", 2); // {1}{G} + + // 1 turn + checkPlayableAbility("A can use bolas on 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Balduvian Bears", true); + checkPlayableAbility("B cant use bolas on 1", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Grizzly Bears", false); + + // 2 turn + checkPlayableAbility("A can't use bolas on 2", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Balduvian Bears", false); + checkPlayableAbility("B can't use bolas on 2", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Grizzly Bears", false); + + // 3 turn + checkPlayableAbility("A can use bolas on 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Balduvian Bears", true); + checkPlayableAbility("B cant use bolas on 3", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Grizzly Bears", false); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java index ae8ea580ce..fe78134d2c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java @@ -1,13 +1,12 @@ - package org.mage.test.cards.cost.alternate; import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class UseAlternateSourceCostsTest extends CardTestPlayerBase { @@ -75,4 +74,131 @@ public class UseAlternateSourceCostsTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Gray Ogre", 1); assertGraveyardCount(playerA, "Lightning Bolt", 1); } + + + @Test + public void test_Playable_WithMana() { + // {1}{W}{W} instant + // You may discard a Plains card rather than pay Abolish's mana cost. + // Destroy target artifact or enchantment. + addCard(Zone.HAND, playerA, "Abolish"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.HAND, playerA, "Plains", 1); // discard cost + // + addCard(Zone.BATTLEFIELD, playerB, "Alpha Myr"); + + checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Abolish", true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Abolish", "Alpha Myr"); + setChoice(playerA, "Yes"); // use alternative cost + setChoice(playerA, "Plains"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Alpha Myr", 1); + assertTappedCount("Plains", false, 3); // must discard 1 instead tap + } + + @Test + public void test_Playable_WithoutMana() { + // {1}{W}{W} instant + // You may discard a Plains card rather than pay Abolish's mana cost. + // Destroy target artifact or enchantment. + addCard(Zone.HAND, playerA, "Abolish"); + //addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.HAND, playerA, "Plains", 1); // discard cost + // + addCard(Zone.BATTLEFIELD, playerB, "Alpha Myr"); + + checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Abolish", true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Abolish", "Alpha Myr"); + setChoice(playerA, "Yes"); // use alternative cost + setChoice(playerA, "Plains"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Alpha Myr", 1); + } + + @Test + public void test_Playable_WithoutManaAndCost() { + // {1}{W}{W} instant + // You may discard a Plains card rather than pay Abolish's mana cost. + // Destroy target artifact or enchantment. + addCard(Zone.HAND, playerA, "Abolish"); + //addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + //addCard(Zone.HAND, playerA, "Plains", 1); // discard cost + // + addCard(Zone.BATTLEFIELD, playerB, "Alpha Myr"); + + // can't see as playable (no mana for normal, no discard for alternative) + checkPlayableAbility("can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Abolish", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Playable_WithOpponentGainingLive() { + // If you control a Forest, rather than pay Invigorate's mana cost, you may have an opponent gain 3 life. + // Target creature gets +4/+4 until end of turn. + addCard(Zone.HAND, playerA, "Invigorate"); // Instant {2}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Invigorate", "Silvercoat Lion"); + setChoice(playerA, "Yes"); // use alternative cost + addTarget(playerA, playerB); // Opponent to gain live + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Invigorate", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 6, 6); + assertLife(playerB, 23); + } + + @Test + public void test_Not_Playable_WithOpponentGainingLive() { + // If you control a Forest, rather than pay Invigorate's mana cost, you may have an opponent gain 3 life. + // Target creature gets +4/+4 until end of turn. + addCard(Zone.GRAVEYARD, playerA, "Invigorate"); // Instant {2}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Forest"); + + // can't see as playable because in graveyard + checkPlayableAbility("can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Invigorate", false); + + checkPlayableAbility("can't", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Invigorate", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Invigorate", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + assertLife(playerB, 20); + } + + @Test + @Ignore // TODO: make test to check combo of alternative cost and cost reduction effects + public void test_Playable_WithCostReduction() { + addCard(Zone.HAND, playerA, "xxx"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java index 40ffdc079f..a7b09ac9ed 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java @@ -7,10 +7,7 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * - * also tests cost reduction effects - * - * @author BetaSteward + * @author BetaSteward, JayDi85 */ public class CostModificationTest extends CardTestPlayerBase { @@ -71,7 +68,7 @@ public class CostModificationTest extends CardTestPlayerBase { /** * Test that cost reduction also works with mana source restriction Myr * Superion Spend only mana produced by creatures to cast Myr Superion - * + *

* Etherium Sculptor {1}{U} Artifact Creature - Vedalken Artificer 1/2 * Artifact spells you cast cost {1} less to cast. */ @@ -143,7 +140,7 @@ public class CostModificationTest extends CardTestPlayerBase { } /* - * Reported bug: Grand Arbiter Augustin IV makes moth spells you cast and your opponent cast {1} more. Should only affect opponent's spells costs. + * Reported bug: Grand Arbiter Augustin IV makes moth spells you cast and your opponent cast {1} more. Should only affect opponent's spells costs. */ @Test public void testArbiterIncreasingCostBothPlayers() { @@ -244,6 +241,145 @@ public class CostModificationTest extends CardTestPlayerBase { assertCounterCount(playerA, "Animar, Soul of Elements", CounterType.P1P1, 2); assertTappedCount("Plains", true, 3); + } + @Test + public void test_ThatTargetSourceEffect_AccursedWitch_CanPlayWithReduction() { + // creature 4/2 + // Spells your opponents cast that target Accursed Witch cost {1} less to cast. + addCard(Zone.BATTLEFIELD, playerB, "Accursed Witch"); + // + // {1}{R} SORCERY + // Grapeshot deals 1 damage to any target. + addCard(Zone.HAND, playerA, "Grapeshot"); + addCard(Zone.HAND, playerA, "Mountain", 1); // play to add mana + + checkPlayableAbility("0 mana, can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", false); + + // add 1 mana, can cast by target + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain"); + checkPlayableAbility("1 mana, can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", true); + + // cast with target + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grapeshot", "Accursed Witch"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Grapeshot", 1); + } + + @Test + public void test_ThatTargetSourceEffect_AccursedWitch_CantPlayOnProtection() { + // creature 4/2 + // Spells your opponents cast that target Accursed Witch cost {1} less to cast. + addCard(Zone.BATTLEFIELD, playerB, "Accursed Witch"); + // + // {1}{R} SORCERY + // Grapeshot deals 1 damage to any target. + addCard(Zone.HAND, playerA, "Grapeshot"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // + // Artifact — Equipment + // Equip {2} + // Equipped creature gets +2/+2 and has protection from red and from white. + addCard(Zone.BATTLEFIELD, playerB, "Sword of War and Peace"); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + + // 1 mana, can cast by target + checkPlayableAbility("1 mana, can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", true); + + // add protection from red + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {2}", "Accursed Witch"); + + // can't cast cause can't target to red + checkPlayableAbility("can't cast cause protection", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", false); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ThatTargetSourceEffect_BorealElemental() { + // use case: cost increase for getPlayable works only for no other targets available + // so if you can targets another target then allows to cast (don't apply cost increase) + + // creature 3/4 + // Spells your opponents cast that target Boreal Elemental cost {2} more to cast. + addCard(Zone.BATTLEFIELD, playerB, "Boreal Elemental"); + // + // {R} instant + // Engulfing Flames deals 1 damage to target creature. It can't be regenerated this turn. + addCard(Zone.HAND, playerA, "Engulfing Flames"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // + addCard(Zone.HAND, playerA, "Grizzly Bears"); // {1}{G} + addCard(Zone.HAND, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + + // no second target, so must cost increase + checkPlayableAbility("one target, can't play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Engulfing Flames", false); + + // prepare second target + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("two targets, can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Engulfing Flames", true); + + // try to cast (only one target possible to targets/play) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Engulfing Flames"); + //addTarget(playerA, "Boreal Elemental"); // you can't target Boreal Elemental cause it will increase cost + addTarget(playerA, "Grizzly Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Engulfing Flames", 1); + } + + @Test + public void test_ThatTargetEnchantedPlayer_InfectiousCurse() { + // When Accursed Witch dies, return it to the battlefield transformed under your control attached to target opponent. + // + // transformed: Infectious Curse + // Enchant player + // Spells you cast that target enchanted player cost {1} less to cast. + addCard(Zone.BATTLEFIELD, playerA, "Accursed Witch", 1); // 4/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // + // {1}{R} SORCERY + // Grapeshot deals 1 damage to any target. + addCard(Zone.HAND, playerA, "Grapeshot"); + + checkPlayableAbility("no mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", false); + + // transform + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Accursed Witch"); + addTarget(playerA, playerB); // attach curse to + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("transformed", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Infectious Curse", 1); + checkPlayableAbility("reduced, but no mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", false); + + // cast + // possible bug: transform command can generate duplicated triggers (player get choose trigger dialog but must not) + checkPlayableAbility("reduced, with mana", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", true); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Grapeshot", playerB); + waitStackResolved(3, PhaseStep.PRECOMBAT_MAIN); + checkLife("a", 3, PhaseStep.PRECOMBAT_MAIN, playerA, 20 + 1); // +1 from curse on turn 2 + checkLife("b", 3, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 1 - 1); // -1 from grapeshot, -1 from curse on turn 2 + + setStrictChooseMode(true); + setStopAt(6, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java new file mode 100644 index 0000000000..0128bbeb5e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java @@ -0,0 +1,271 @@ +package org.mage.test.cards.cost.modification; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * @author JayDi85 + */ +public class CostReduceForEachTest extends CardTestPlayerBaseWithAIHelps { + + // tests for https://github.com/magefree/mage/issues/6698 + + @Test + public void test_AncientStoneIdol_Attacking() { + // {10} + // Flash + // This spell costs {1} less to cast for each attacking creature. + addCard(Zone.HAND, playerA, "Ancient Stone Idol", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10 - 2); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2); // give 2 cost reduction + + // before + checkPlayableAbility("before attack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ancient Stone Idol", false); + + // prepare for attack + attack(1, playerA, "Balduvian Bears"); + attack(1, playerA, "Balduvian Bears"); + + // on attack + checkPlayableAbility("on attack", 1, PhaseStep.DECLARE_BLOCKERS, playerA, "Cast Ancient Stone Idol", true); + castSpell(1, PhaseStep.DECLARE_BLOCKERS, playerA, "Ancient Stone Idol"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Ancient Stone Idol", 1); + } + + @Test + public void test_AncientStoneIdol_AttackingWithSacrifice() { + // The total cost to cast a spell is locked in before you pay that cost. For example, if you control five attacking + // creatures, including one you can sacrifice to add {C} to your mana pool, Ancient Stone Idol costs {5} to cast. + // Then you can sacrifice the creature when you activate mana abilities just before paying the cost, and it still + // costs only {5} to cast. + // (2018-07-13) + + // {10} + // Flash + // This spell costs {1} less to cast for each attacking creature. + addCard(Zone.HAND, playerA, "Ancient Stone Idol", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10 - 4); + // + // Sacrifice Blood Pet: Add {B}. + addCard(Zone.BATTLEFIELD, playerA, "Blood Pet", 2); // give 2 cost reduction + can be sacrificed as 2 mana + + // before + checkPlayableAbility("before attack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ancient Stone Idol", false); + + // prepare for attack + attack(1, playerA, "Blood Pet"); + attack(1, playerA, "Blood Pet"); + + // on attack (must automaticly sacrifice creatures as mana pay) + checkPlayableAbility("on attack", 1, PhaseStep.DECLARE_BLOCKERS, playerA, "Cast Ancient Stone Idol", true); + castSpell(1, PhaseStep.DECLARE_BLOCKERS, playerA, "Ancient Stone Idol"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Ancient Stone Idol", 1); + assertGraveyardCount(playerA, "Blood Pet", 2); + } + + @Test + public void test_KhalniHydra_ColorReduce() { + // {G}{G}{G}{G}{G}{G}{G}{G} + // This spell costs {G} less to cast for each green creature you control. + addCard(Zone.HAND, playerA, "Khalni Hydra", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8 - 2); + addCard(Zone.HAND, playerA, "Balduvian Bears", 2); // give 2 cost reduction + + checkPlayableAbility("no cost reduction 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Khalni Hydra", false); + + // prepare creatures for reduce + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPlayableAbility("no cost reduction 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Khalni Hydra", false); + + // can cast on next turn + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Khalni Hydra"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Khalni Hydra", 1); + } + + @Test + public void test_TorgaarFamineIncarnate_SacrificeXTargets() { + // {6}{B}{B} + // As an additional cost to cast this spell, you may sacrifice any number of creatures. + // This spell costs {2} less to cast for each creature sacrificed this way. + // When Torgaar, Famine Incarnate enters the battlefield, up to one target player's life total becomes half their starting life total, rounded down. + addCard(Zone.HAND, playerA, "Torgaar, Famine Incarnate", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8 - 4 - 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.HAND, playerA, "Balduvian Bears", 2); // give 4 cost reduction on sacrifice + + checkPlayableAbility("no cost reduction 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Torgaar, Famine Incarnate", false); + + // prepare creatures for reduce + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 4); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPlayableAbility("no cost reduction 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Torgaar, Famine Incarnate", false); + + // can cast on next turn + checkPlayableAbility("must reduce", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Torgaar, Famine Incarnate", true); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Torgaar, Famine Incarnate"); + setChoice(playerA, "X=2"); // two creatures sacrifice + setChoice(playerA, "Balduvian Bears"); + setChoice(playerA, "Balduvian Bears"); + addTarget(playerA, playerB); // target player for half life + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Torgaar, Famine Incarnate", 1); + assertLife(playerB, 20 / 2); + } + + @Test + public void test_AshnodsAltar_SacrificeCost() { + // Sacrifice a creature: Add {C}{C}. + addCard(Zone.BATTLEFIELD, playerA, "Ashnod's Altar", 1); + // + addCard(Zone.HAND, playerA, "Alloy Myr", 1); // {3} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3 - 2); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // give 2 mana on sacrifice + + checkPlayableAbility("must play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Alloy Myr", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alloy Myr"); + setChoice(playerA, "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Alloy Myr", 1); + } + + @Test + public void test_BattlefieldThaumaturge_TargetCostReduce() { + // Each instant and sorcery spell you cast costs {1} less to cast for each creature it targets. + addCard(Zone.BATTLEFIELD, playerA, "Battlefield Thaumaturge", 1); + // + // {3}{R}{R} sorcery + // Shower of Coals deals 2 damage to each of up to three target creatures and/or players. + addCard(Zone.HAND, playerA, "Shower of Coals", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5 - 3); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears@bear", 3); // add 3 cost reduce on target + + checkPlayableAbility("must play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Shower of Coals", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shower of Coals"); + addTarget(playerA, "@bear.1^@bear.2^@bear.3"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Shower of Coals", 1); + } + + @Test + public void test_BenthicExplorers_ManaAbilityFromOpponentCard() { + // {T}, Untap a tapped land an opponent controls: Add one mana of any type that land could produce. + addCard(Zone.BATTLEFIELD, playerA, "Benthic Explorers", 1); + // + addCard(Zone.HAND, playerA, "Balduvian Bears", 1); // {1}{G} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 1); // give 1 mana + // + // {1}, {T}: Tap target land. + addCard(Zone.BATTLEFIELD, playerA, "Rishadan Port", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + + // prepare tapped land + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}: Tap target land", "Forest"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentTapped("must be tapped", 1, PhaseStep.PRECOMBAT_MAIN, playerA, playerB, "Forest", true, 1); + + // cast with opponent's mana + checkPlayableAbility("must play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Balduvian Bears", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + setChoice(playerA, "Forest"); // mana from tapped + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Balduvian Bears", 1); + } + + @Test + public void test_CloudKey_ChooseCardTypeForCostReduce() { + // {3} + // As Cloud Key enters the battlefield, choose artifact, creature, enchantment, instant, or sorcery. + // Spells you cast of the chosen type cost {1} less to cast. + addCard(Zone.HAND, playerA, "Cloud Key", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + // + addCard(Zone.HAND, playerA, "Balduvian Bears", 1); // {1}{G} + addCard(Zone.HAND, playerA, "Forest", 1); + + // prepare cloud key to reduce + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloud Key"); + setChoice(playerA, "Creature"); + + // prepare lands + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("can't with no mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Balduvian Bears", false); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); + checkPlayableAbility("can play with mana and reduce", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Balduvian Bears", true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Balduvian Bears", 1); + } + + @Test + public void test_InspiringStatuary_PayByArtifacts() { + // {3} + // Nonartifact spells you cast have improvise. (Your artifacts can help cast those spells. Each artifact you + // tap after you’re done activating mana abilities pays for {1}.) + addCard(Zone.BATTLEFIELD, playerA, "Inspiring Statuary", 5); + // + addCard(Zone.HAND, playerA, "Keeper of Tresserhorn", 1); // {5}{B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6 - 5); // 5 mana from artifact + + checkPlayableAbility("can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Keeper of Tresserhorn", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Keeper of Tresserhorn"); + addTarget(playerA, "Inspiring Statuary", 5); // as pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Keeper of Tresserhorn", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java new file mode 100644 index 0000000000..7ab2978d83 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java @@ -0,0 +1,74 @@ +package org.mage.test.cards.cost.modification; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * @author JayDi85 + */ +public class CostReduceWithConditionTest extends CardTestPlayerBaseWithAIHelps { + + @Test + public void test_PriceOfFame_Normal() { + // {3}{B} + // This spell costs {2} less to cast if it targets a legendary creature. + // Destroy target creature. + addCard(Zone.HAND, playerA, "Price of Fame", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Price of Fame", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Balduvian Bears", 1); + } + + @Test + public void test_PriceOfFame_Reduce_Manual() { + // https://github.com/magefree/mage/issues/6685 + + // {3}{B} + // This spell costs {2} less to cast if it targets a legendary creature. + // Destroy target creature. + addCard(Zone.HAND, playerA, "Price of Fame", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4 - 2); + addCard(Zone.BATTLEFIELD, playerB, "Anje Falkenrath", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Price of Fame", "Anje Falkenrath"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Anje Falkenrath", 1); + } + + @Test + public void test_PriceOfFame_Reduce_AI() { + // https://github.com/magefree/mage/issues/6685 + + // {3}{B} + // This spell costs {2} less to cast if it targets a legendary creature. + // Destroy target creature. + addCard(Zone.HAND, playerA, "Price of Fame", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4 - 2); + addCard(Zone.BATTLEFIELD, playerB, "Anje Falkenrath", 1); + + // AI must see and play that cards too + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Anje Falkenrath", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FerventChampionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FerventChampionTest.java new file mode 100644 index 0000000000..974e818df2 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FerventChampionTest.java @@ -0,0 +1,44 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.cost.modification; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class FerventChampionTest extends CardTestPlayerBase { + + @Test + public void testFerventChampion() { + setStrictChooseMode(true); + // First strike, Haste + // Whenever Fervent Champion attacks, another target attacking Knight you control gets +1/+0 until end of turn. + // Equip abilities you activate that target Fervent Champion cost {3} less to activate. + addCard(Zone.BATTLEFIELD, playerA, "Fervent Champion"); + + // Equipped creature gets +2/+2 and has protection from red and from blue. + // Whenever equipped creature deals combat damage to a player, Sword of Fire + // and Ice deals 2 damage to any target and you draw a card. + // Equip {2} + addCard(Zone.BATTLEFIELD, playerA, "Sword of Fire and Ice", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip"); + addTarget(playerA, "Fervent Champion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Fervent Champion", 3,3); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FluctuatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FluctuatorTest.java index 171e219083..9209eeb6f5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FluctuatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/FluctuatorTest.java @@ -1,95 +1,95 @@ - -package org.mage.test.cards.cost.modification; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * @author noxx - */ -public class FluctuatorTest extends CardTestPlayerBase { - - /** - * NOTE: As of 4/19/2017 this test is failing due to a bug in code. - * See issue #3148 - * - * Fluctuator makes 'Akroma's Vengeance' cyclic cost reduced to {1} Test it - * with one Plains on battlefield. - */ - @Test - public void testFluctuatorReducedBy2() { - - // Destroy all artifacts, creatures, and enchantments. - // Cycling ({3}, Discard this card: Draw a card.) - addCard(Zone.HAND, playerA, "Akroma's Vengeance"); - - // Cycling abilities you activate cost you up to {2} less to activate. - addCard(Zone.BATTLEFIELD, playerA, "Fluctuator"); - - // One mana should be enough - addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); - setChoice(playerA, "2"); // reduce 2 generic mana - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Akroma's Vengeance", 1); - assertHandCount(playerA, 1); - } - - /** - * Fluctuator makes 'Akroma's Vengeance' cyclic cost reduced to {1} - * - * Make sure it wasn't reduced more than by two. - */ - @Test - public void testFluctuatorReducedNotBy3() { - - // Destroy all artifacts, creatures, and enchantments. - // Cycling ({3}, Discard this card: Draw a card.) - addCard(Zone.HAND, playerA, "Akroma's Vengeance"); - - // Cycling abilities you activate cost you up to {2} less to activate. - addCard(Zone.BATTLEFIELD, playerA, "Fluctuator"); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); - setChoice(playerA, "2"); // reduce 1 generic mana - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Akroma's Vengeance", 0); - assertHandCount(playerA, 1); - } - - /** - * NOTE: As of 4/19/2017 this test is failing due to a bug in code. - * See issue #3148 - * - * Test 2 Fluctuators reduce cycling cost up to 4. - */ - @Test - public void testTwoFluctuatorsReduceBy4() { - - // Destroy all artifacts, creatures, and enchantments. - // Cycling ({3}, Discard this card: Draw a card.) - addCard(Zone.HAND, playerA, "Akroma's Vengeance"); - - // Cycling abilities you activate cost you up to {2} less to activate. - addCard(Zone.BATTLEFIELD, playerA, "Fluctuator", 2); // 2 copies - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); - setChoice(playerA, "2"); // reduce 2 generic mana - setChoice(playerA, "1"); // reduce 1 generic mana - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Akroma's Vengeance", 1); - assertHandCount(playerA, 1); - } -} + +package org.mage.test.cards.cost.modification; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author noxx + */ +public class FluctuatorTest extends CardTestPlayerBase { + + /** + * NOTE: As of 4/19/2017 this test is failing due to a bug in code. + * See issue #3148 + * + * Fluctuator makes 'Akroma's Vengeance' cyclic cost reduced to {1} Test it + * with one Plains on battlefield. + */ + @Test + public void testFluctuatorReducedBy2() { + + // Destroy all artifacts, creatures, and enchantments. + // Cycling ({3}, Discard this card: Draw a card.) + addCard(Zone.HAND, playerA, "Akroma's Vengeance"); + + // Cycling abilities you activate cost you up to {2} less to activate. + addCard(Zone.BATTLEFIELD, playerA, "Fluctuator"); + + // One mana should be enough + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); + setChoice(playerA, "2"); // reduce 2 generic mana + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Akroma's Vengeance", 1); + assertHandCount(playerA, 1); + } + + /** + * Fluctuator makes 'Akroma's Vengeance' cyclic cost reduced to {1} + * + * Make sure it wasn't reduced more than by two. + */ + @Test + public void testFluctuatorReducedNotBy3() { + + // Destroy all artifacts, creatures, and enchantments. + // Cycling ({3}, Discard this card: Draw a card.) + addCard(Zone.HAND, playerA, "Akroma's Vengeance"); + + // Cycling abilities you activate cost you up to {2} less to activate. + addCard(Zone.BATTLEFIELD, playerA, "Fluctuator"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); + setChoice(playerA, "2"); // reduce 1 generic mana + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Akroma's Vengeance", 0); + assertHandCount(playerA, 1); + } + + /** + * NOTE: As of 4/19/2017 this test is failing due to a bug in code. + * See issue #3148 + * + * Test 2 Fluctuators reduce cycling cost up to 4. + */ + @Test + public void testTwoFluctuatorsReduceBy4() { + + // Destroy all artifacts, creatures, and enchantments. + // Cycling ({3}, Discard this card: Draw a card.) + addCard(Zone.HAND, playerA, "Akroma's Vengeance"); + + // Cycling abilities you activate cost you up to {2} less to activate. + addCard(Zone.BATTLEFIELD, playerA, "Fluctuator", 2); // 2 copies + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); + setChoice(playerA, "2"); // reduce 2 generic mana + setChoice(playerA, "1"); // reduce 1 generic mana + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Akroma's Vengeance", 1); + assertHandCount(playerA, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/PayDelveFromCommandZoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/PayDelveFromCommandZoneTest.java new file mode 100644 index 0000000000..797e35260a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/PayDelveFromCommandZoneTest.java @@ -0,0 +1,35 @@ +package org.mage.test.cards.cost.modification; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +/** + * @author JayDi85 + */ +public class PayDelveFromCommandZoneTest extends CardTestCommander4Players { + + // Player order: A -> D -> C -> B + @Test + public void test_Other_CastFromCommand_Delve() { + // https://github.com/magefree/mage/issues/6698 + // Having this problem with Tasigur, the Golden Fang. I can't attempt to use delve to cast him from command zone. + + // {5}{B} creature + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + addCard(Zone.COMMAND, playerA, "Tasigur, the Golden Fang", 1); + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 5); // delve + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tasigur, the Golden Fang"); + setChoice(playerA, "Balduvian Bears", 5); // delve pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Tasigur, the Golden Fang", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java index bbe7b8c8ea..54b1f44a9f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.enchantments; import mage.constants.PhaseStep; @@ -119,7 +118,7 @@ public class AnimateDeadTest extends CardTestPlayerBase { castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cruel Edict", playerA); - setStopAt(2, PhaseStep.BEGIN_COMBAT); + setStopAt(2, PhaseStep.END_TURN); execute(); assertGraveyardCount(playerB, "Cruel Edict", 1); @@ -168,6 +167,8 @@ public class AnimateDeadTest extends CardTestPlayerBase { */ @Test public void testAnimateAndDragonlordAtarkaWithNoTargets() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); // Enchant creature card in a graveyard @@ -188,6 +189,11 @@ public class AnimateDeadTest extends CardTestPlayerBase { setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + assertPermanentCount(playerA, "Animate Dead", 1); assertPermanentCount(playerA, "Dragonlord Atarka", 1); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraTargetRemovedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraTargetRemovedTest.java index ce3cc0de6a..4e46099f9b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraTargetRemovedTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AuraTargetRemovedTest.java @@ -1,60 +1,60 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.mage.test.cards.enchantments; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class AuraTargetRemovedTest extends CardTestPlayerBase { - - /** - * Spreading Seas is bugged, opp casted it on my Field of Ruin, I sacced - * with the spell on stack but it resolved anyway and let them draw. - * - * 303.4. Some enchantments have the subtype “Aura.” An Aura enters the - * battlefield attached to an object or player. What an Aura can be attached - * to is restricted by its enchant keyword ability (see rule 702.5, - * “Enchant”). Other effects can limit what a permanent can be enchanted by. - * 303.4a An Aura spell requires a target, which is restricted by its - * enchant ability. - */ - @Test - public void testOneAttackerDamage() { - addCard(Zone.BATTLEFIELD, playerA, "Academy Ruins", 1); - addCard(Zone.BATTLEFIELD, playerA, "Island", 2); - // Enchant land - // When Spreading Seas enters the battlefield, draw a card. - // Enchanted land is an Island. - addCard(Zone.HAND, playerA, "Spreading Seas", 1); //Enchantment {1}{U} - - // {T}: Add {C}. - // {2}, {T}, Sacrifice Field of Ruin: Destroy target nonbasic land an opponent controls. Each player searches their library for a basic land card, puts it onto the battlefield, then shuffles their library. - addCard(Zone.BATTLEFIELD, playerB, "Field of Ruin", 1); // - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spreading Seas", "Field of Ruin"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{2}, {T}", "Academy Ruins", "Spreading Seas"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerB, 3); - assertGraveyardCount(playerB, "Field of Ruin", 1); - - assertPermanentCount(playerA, 3); - assertGraveyardCount(playerA, "Spreading Seas", 1); - assertGraveyardCount(playerA, "Academy Ruins", 1); - - assertHandCount(playerA, 0); // Because Spreading Seas is counterd (no valid target), no card is drawn from it - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.enchantments; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class AuraTargetRemovedTest extends CardTestPlayerBase { + + /** + * Spreading Seas is bugged, opp casted it on my Field of Ruin, I sacced + * with the spell on stack but it resolved anyway and let them draw. + * + * 303.4. Some enchantments have the subtype “Aura.” An Aura enters the + * battlefield attached to an object or player. What an Aura can be attached + * to is restricted by its enchant keyword ability (see rule 702.5, + * “Enchant”). Other effects can limit what a permanent can be enchanted by. + * 303.4a An Aura spell requires a target, which is restricted by its + * enchant ability. + */ + @Test + public void testOneAttackerDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Academy Ruins", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // Enchant land + // When Spreading Seas enters the battlefield, draw a card. + // Enchanted land is an Island. + addCard(Zone.HAND, playerA, "Spreading Seas", 1); //Enchantment {1}{U} + + // {T}: Add {C}. + // {2}, {T}, Sacrifice Field of Ruin: Destroy target nonbasic land an opponent controls. Each player searches their library for a basic land card, puts it onto the battlefield, then shuffles their library. + addCard(Zone.BATTLEFIELD, playerB, "Field of Ruin", 1); // + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spreading Seas", "Field of Ruin"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{2}, {T}", "Academy Ruins", "Spreading Seas"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, 3); + assertGraveyardCount(playerB, "Field of Ruin", 1); + + assertPermanentCount(playerA, 3); + assertGraveyardCount(playerA, "Spreading Seas", 1); + assertGraveyardCount(playerA, "Academy Ruins", 1); + + assertHandCount(playerA, 0); // Because Spreading Seas is counterd (no valid target), no card is drawn from it + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java index 1cdb2b4280..952c6122d8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java @@ -138,8 +138,8 @@ public class OathOfLiegesTest extends CardTestPlayerBase { // turn 1 - A // cast oath A + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 4); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Replenish"); -// showBattlefield("A perms", 1, PhaseStep.POSTCOMBAT_MAIN, playerA); // cast oath copy castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Copy Enchantment"); setChoice(playerA, "Yes"); // use copy effect @@ -151,12 +151,14 @@ public class OathOfLiegesTest extends CardTestPlayerBase { // turn 2 - B // oath A triggers for B and do nothing // copy oath A triggers for B and do nothing + setChoice(playerA, "At the beginning of each player's upkeep"); // two triggers on upkeep checkPermanentCount("A have 10 plains", 1, PhaseStep.END_TURN, playerA, "Plains", 10); checkPermanentCount("B have 12 plains", 1, PhaseStep.END_TURN, playerB, "Plains", 12); // turn 3 - A // oath A triggers for A and activates // copy oath A triggers for A and activates + setChoice(playerA, "At the beginning of each player's upkeep"); // two triggers on upkeep // 1 addTarget(playerA, playerB); // who control more lands setChoice(playerA, "Yes"); // search library @@ -166,6 +168,7 @@ public class OathOfLiegesTest extends CardTestPlayerBase { setChoice(playerA, "Yes"); // search library addTarget(playerA, "Plains"); // card from library + setStrictChooseMode(true); setStopAt(3, PhaseStep.END_TURN); execute(); assertAllCommandsUsed(); @@ -197,10 +200,6 @@ public class OathOfLiegesTest extends CardTestPlayerBase { // cast oath A castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Replenish"); // cast oath copy by opponent - // showBattlefield("A perms", 2, PhaseStep.POSTCOMBAT_MAIN, playerA); - // showBattlefield("B perms", 2, PhaseStep.POSTCOMBAT_MAIN, playerB); - // showAvaileableAbilities("B abils", 2, PhaseStep.POSTCOMBAT_MAIN, playerB); - // showHand("B hand", 2, PhaseStep.POSTCOMBAT_MAIN, playerB); castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Copy Enchantment"); setChoice(playerB, "Yes"); // use copy effect setChoice(playerB, "Oath of Lieges"); // target for copy @@ -208,7 +207,6 @@ public class OathOfLiegesTest extends CardTestPlayerBase { checkPermanentCount("B have 1 oath", 2, PhaseStep.END_TURN, playerA, "Oath of Lieges", 1); checkPermanentCount("A have 10 plains", 2, PhaseStep.END_TURN, playerA, "Plains", 10); checkPermanentCount("B have 12 plains", 2, PhaseStep.END_TURN, playerB, "Plains", 12); - // showLibrary("lib B", 2, PhaseStep.END_TURN, playerB); // turn 3 - A // oath A triggers for A and activates diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java index 157692786a..ad524a51a4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java @@ -1,13 +1,14 @@ package org.mage.test.cards.enchantments; import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; import mage.constants.EmptyNames; import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.Filter; import mage.game.permanent.Permanent; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -25,7 +26,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase { */ @Test public void testCloudform() { - // At the beginning of your upkeep, if you control an artifact, put a 1/1 + // At the beginning of your upkeep, if you control an artifact, put a 1/1 // colorless Thopter artifact creature token with flying onto the battlefield. // Whenever one or more artifact creatures you control deal combat damage to a player, draw a card. addCard(Zone.BATTLEFIELD, playerA, "Thopter Spy Network", 2); // {2}{U}{U} - added to come to 5 enchantments on the battlefield @@ -35,7 +36,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase { // As long as you control five or more enchantments, each other non-Aura enchantment you control is a creature in // addition to its other types and has base power and base toughness each equal to its converted mana cost. addCard(Zone.HAND, playerA, "Starfield of Nyx"); // "{4}{W}" - // When Cloudform enters the battlefield, it becomes an Aura with enchant creature. + // When Cloudform enters the battlefield, it becomes an Aura with enchant creature. // Manifest the top card of your library and attach Cloudform to it. // Enchanted creature has flying and hexproof. addCard(Zone.HAND, playerA, "Cloudform"); // {1}{U}{U} @@ -51,7 +52,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase { assertAllCommandsUsed(); assertGraveyardCount(playerA, "Thopter Spy Network", 0); - assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), + assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2, Filter.ComparisonScope.All); // the manifested cards assertPermanentCount(playerA, "Starfield of Nyx", 1); assertPowerToughness(playerA, "Thopter Spy Network", 4, 4, Filter.ComparisonScope.All); @@ -66,7 +67,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase { */ @Test public void testHexproof() { - // At the beginning of your upkeep, if you control an artifact, put a 1/1 colorless + // At the beginning of your upkeep, if you control an artifact, put a 1/1 colorless // Thopter artifact creature token with flying onto the battlefield. // Whenever one or more artifact creatures you control deal combat damage to a player, draw a card. addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); @@ -114,7 +115,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); execute(); assertAllCommandsUsed(); - + assertPowerToughness(playerA, "Master of the Feast", 3, 3, Filter.ComparisonScope.All); assertPowerToughness(playerA, "Humility", 4, 4, Filter.ComparisonScope.All); // Humility loses its ability in layer 6. Layer 7 never gets Humility's effect @@ -139,10 +140,9 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase { * there. */ @Test - @Ignore public void testStarfieldOfNyxAndSongOfTheDryads() { // Nontoken creatures you control get +1/+1 and have vigilance. - addCard(Zone.BATTLEFIELD, playerA, "Always Watching", 5); + addCard(Zone.BATTLEFIELD, playerA, "Always Watching", 5); // Enchantment {1}{W}{W} addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); // At the beginning of your upkeep, you may return target enchantment card from your graveyard to the battlefield. // As long as you control five or more enchantments, each other non-Aura enchantment you control is a creature in @@ -157,15 +157,19 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase { castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Song of the Dryads", "Starfield of Nyx"); setStopAt(2, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Always Watching", 5); assertPermanentCount(playerB, "Song of the Dryads", 1); + assertType("Always Watching", CardType.ENCHANTMENT, true); + assertType("Always Watching", CardType.CREATURE, false); assertPowerToughness(playerA, "Always Watching", 0, 0, Filter.ComparisonScope.All); - assertPermanentCount(playerA, "Forest", 1); - + assertType("Starfield of Nyx", CardType.LAND, SubType.FOREST); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/flip/SasayaOrochiAscendantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/flip/SasayaOrochiAscendantTest.java index 00f9d4a148..60a93062f6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/flip/SasayaOrochiAscendantTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/flip/SasayaOrochiAscendantTest.java @@ -29,12 +29,15 @@ public class SasayaOrochiAscendantTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Upwelling", 1); // Enchantment {3}{G} activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reveal your hand: If you have seven or more land cards in your hand, flip"); + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}"); activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Upwelling"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Sasaya's Essence", 1); assertPermanentCount(playerA, "Upwelling", 1); @@ -45,7 +48,88 @@ public class SasayaOrochiAscendantTest extends CardTestPlayerBase { assertDuplicatedManaOptions(manaOptions); Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); - assertManaOptions("{G}", manaOptions); + assertManaOptions("{G}{G}{G}{G}{G}", manaOptions); } + + @Test + public void testSasayasEssence2() { + addCard(Zone.HAND, playerA, "Plains", 7); + addCard(Zone.BATTLEFIELD, playerA, "Brushland", 3); + + // Reveal your hand: If you have seven or more land cards in your hand, flip Sasaya, Orochi Ascendant. + // Sasaya's Essence: Legendary Enchantment + // Whenever a land you control is tapped for mana, for each other land you control with the same name, add one mana of any type that land produced. + addCard(Zone.BATTLEFIELD, playerA, "Sasaya, Orochi Ascendant", 1); + // Mana pools don't empty as steps and phases end. + addCard(Zone.HAND, playerA, "Upwelling", 1); // Enchantment {3}{G} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reveal your hand: If you have seven or more land cards in your hand, flip"); + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}"); + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Upwelling"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Sasaya's Essence", 1); + assertPermanentCount(playerA, "Upwelling", 1); + + assertManaPool(playerA, ManaType.GREEN, 2); + + assertLife(playerA, 18); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + assertDuplicatedManaOptions(manaOptions); + + Assert.assertEquals("mana variations don't fit", 3, manaOptions.size()); + assertManaOptions("{C}{C}{C}{G}{G}", manaOptions); + assertManaOptions("{G}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{W}{W}{W}{G}{G}", manaOptions); + + } + + @Test + public void testSasayasEssence3() { + addCard(Zone.HAND, playerA, "Plains", 7); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + // {1}, {T}: Add {R}{G}. + addCard(Zone.BATTLEFIELD, playerA, "Mossfire Valley", 2); + + // Reveal your hand: If you have seven or more land cards in your hand, flip Sasaya, Orochi Ascendant. + // Sasaya's Essence: Legendary Enchantment + // Whenever a land you control is tapped for mana, for each other land you control with the same name, add one mana of any type that land produced. + addCard(Zone.BATTLEFIELD, playerA, "Sasaya, Orochi Ascendant", 1); + // Mana pools don't empty as steps and phases end. + addCard(Zone.HAND, playerA, "Upwelling", 1); // Enchantment {3}{G} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reveal your hand: If you have seven or more land cards in your hand, flip"); + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}"); + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Upwelling"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Sasaya's Essence", 1); + assertPermanentCount(playerA, "Upwelling", 1); + + assertManaPool(playerA, ManaType.GREEN, 2); + + assertLife(playerA, 20); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + assertDuplicatedManaOptions(manaOptions); + + Assert.assertEquals("mana variations don't fit", 4, manaOptions.size()); + assertManaOptions("{R}{R}{R}{R}{G}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{R}{R}{R}{G}{G}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{R}{R}{G}{G}{G}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{R}{G}{G}{G}{G}{G}{G}{G}{G}", manaOptions); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java index 3f7fe970cf..598eedc8c8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java @@ -53,7 +53,6 @@ public class ConditionalManaTest extends CardTestPlayerBase { } @Test - @Ignore public void testWorkingWithReflectingPool() { addCard(Zone.BATTLEFIELD, playerA, "Cavern of Souls", 1); // can give {C] or {any} mana ({any} with restrictions) addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1); // must give {C} or {any} mana from the Cavern, but without restrictions @@ -321,7 +320,6 @@ public class ConditionalManaTest extends CardTestPlayerBase { // and process all available net mana by special call like TriggeredManaAbility->getNetManaForEvent(ManaEvent xxx) @Test - @Ignore public void TriggeredManaAbilityMustGivesExtraManaOptions() { // TriggeredManaAbility must give extra mana options (2 red instead 1) // Whenever you tap a land for mana, add one mana of any type that land produced. @@ -340,7 +338,6 @@ public class ConditionalManaTest extends CardTestPlayerBase { } @Test - @Ignore public void DictateOfKarametra_AutoPay() { // Whenever you tap a land for mana, add one mana of any type that land produced. addCard(Zone.BATTLEFIELD, playerA, "Dictate of Karametra"); @@ -351,7 +348,7 @@ public class ConditionalManaTest extends CardTestPlayerBase { // computer must see available mana (4 red mana instead 2) //activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); //activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + showAvailableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Precision Bolt", playerB); setStrictChooseMode(true); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/DoublingCubeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/DoublingCubeTest.java index 7e2bb98b15..69744bf943 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/DoublingCubeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/DoublingCubeTest.java @@ -31,9 +31,10 @@ public class DoublingCubeTest extends CardTestPlayerBase { activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}, {T}:"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.PRECOMBAT_MAIN); execute(); assertManaPool(playerA, ManaType.COLORLESS, 4); - + assertAllCommandsUsed(); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvestMageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvestMageTest.java index ac39080b79..e4036fd280 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvestMageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/HarvestMageTest.java @@ -5,10 +5,13 @@ */ package org.mage.test.cards.mana; +import mage.abilities.mana.ManaOptions; import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.utils.ManaOptionsTestUtils.assertManaOptions; /** * @@ -18,8 +21,11 @@ public class HarvestMageTest extends CardTestPlayerBase { @Test public void testOneInstance() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Silvercoat Lion"); // {G}, {T}, Discard a card: Until end of turn, if you tap a land for mana, it produces one mana of a color of your choice instead of any other type and amount. addCard(Zone.HAND, playerA, "Harvest Mage", 1); // Creature 1/1 {G} @@ -27,9 +33,47 @@ public class HarvestMageTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Harvest Mage"); activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}, {T}, Discard a card: Until end of turn"); + setChoice(playerA, "Silvercoat Lion"); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Harvest Mage", 1); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{Any}", manaOptions); } + + @Test + public void test_AncientTomb() { + setStrictChooseMode(true); + + + addCard(Zone.BATTLEFIELD, playerA, "Ancient Tomb", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + + // {G}, {T}, Discard a card: Until end of turn, if you tap a land for mana, it produces one mana of a color of your choice instead of any other type and amount. + addCard(Zone.HAND, playerA, "Harvest Mage", 1); // Creature 1/1 {G} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Harvest Mage"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}, {T}, Discard a card: Until end of turn"); + setChoice(playerA, "Silvercoat Lion"); + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Harvest Mage", 1); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{Any}{Any}", manaOptions); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java index 7e3d76fa30..32a3ed8a6c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java @@ -241,7 +241,7 @@ public class ManaPoolTest extends CardTestPlayerBase { checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 4); // use for ability - // showAvaileableAbilities("before ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + // showAvailableAbilities("before ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}:", playerB); setChoice(playerA, "X=3"); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaSourceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaSourceTest.java index 358442d8b9..f381bc6b39 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaSourceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaSourceTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.mana; import mage.constants.PhaseStep; @@ -35,7 +34,6 @@ public class ManaSourceTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Myr Superion", 0); assertHandCount(playerB, "Myr Superion", 1); - } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/MultipleTimesUsableActivatedManaAbilitiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/MultipleTimesUsableActivatedManaAbilitiesTest.java new file mode 100644 index 0000000000..f6fe75c5fe --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/MultipleTimesUsableActivatedManaAbilitiesTest.java @@ -0,0 +1,45 @@ +package org.mage.test.cards.mana; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class MultipleTimesUsableActivatedManaAbilitiesTest extends CardTestPlayerBase { + + /** + * Seton, Krosan Protector - only seems to get counted as if it were one + * mana for determining if a spell can be cast, regardless of how many + * druids you have in playF + */ + @Test + public void testCanBeCastWithSetonKrosanProtector() { + // Tap an untapped Druid you control: Add {G}. + addCard(Zone.BATTLEFIELD, playerA, "Seton, Krosan Protector", 1); // Creature {G}{G}{G} + addCard(Zone.BATTLEFIELD, playerA, "Citanul Druid", 3); + + addCard(Zone.HAND, playerA, "Leatherback Baloth", 1); // Creature 4/5 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Leatherback Baloth"); + + setChoice(playerA, "Citanul Druid"); + setChoice(playerA, "Citanul Druid"); + setChoice(playerA, "Citanul Druid"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + + setStrictChooseMode(true); + execute(); + + assertAllCommandsUsed(); + + assertTappedCount("Citanul Druid", true, 3); + assertPermanentCount(playerA, "Leatherback Baloth", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/NonTappingManaAbilitiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/NonTappingManaAbilitiesTest.java new file mode 100644 index 0000000000..a0caa23549 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/NonTappingManaAbilitiesTest.java @@ -0,0 +1,292 @@ +package org.mage.test.cards.mana; + +import mage.abilities.mana.ManaOptions; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.utils.ManaOptionsTestUtils.assertManaOptions; + +/** + * + * @author LevelX2 + */ +public class NonTappingManaAbilitiesTest extends CardTestPlayerBase { + + @Test + public void druidsRepositoryTest() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, "Alaborn Grenadier", 1); //Creature {W}{W} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2); + // Whenever a creature you control attacks, put a charge counter on Druids' Repository. + // Remove a charge counter from Druids' Repository: Add one mana of any color. + addCard(Zone.BATTLEFIELD, playerA, "Druids' Repository", 1); // Enchantment {1}{G}{G} + + attack(1, playerA, "Silvercoat Lion"); + attack(1, playerA, "Silvercoat Lion"); + setChoice(playerA, "Whenever a creature you control"); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertTappedCount("Silvercoat Lion", true, 2); + assertCounterCount(playerA, "Druids' Repository", CounterType.CHARGE, 2); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{Any}{Any}", manaOptions); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Alaborn Grenadier"); + setChoice(playerA, "White"); + setChoice(playerA, "White"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertCounterCount(playerA, "Druids' Repository", CounterType.CHARGE, 0); + assertPermanentCount(playerA, "Alaborn Grenadier", 1); + } + + @Test + public void TestWorkhorse() { + setStrictChooseMode(true); + + // Workhorse enters the battlefield with four +1/+1 counters on it. + // Remove a +1/+1 counter from Workhorse: Add {C}. + addCard(Zone.BATTLEFIELD, playerA, "Workhorse", 1); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{C}{C}{C}{C}", manaOptions); + } + + @Test + public void TestMorselhoarder() { + setStrictChooseMode(true); + // Morselhoarder enters the battlefield with two -1/-1 counters on it. + // Remove a -1/-1 counter from Morselhoarder: Add one mana of any color. + addCard(Zone.BATTLEFIELD, playerA, "Morselhoarder", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{B}{B}{Any}{Any}{Any}{Any}", manaOptions); + } + + @Test + public void TestFarrelitePriest() { + setStrictChooseMode(true); + // {1}: Add {W}. If this ability has been activated four or more times this turn, sacrifice Farrelite Priest at the beginning of the next end step. + addCard(Zone.BATTLEFIELD, playerA, "Farrelite Priest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 5, manaOptions.size()); + assertManaOptions("{W}{W}{W}{W}", manaOptions); + assertManaOptions("{W}{W}{W}{B}", manaOptions); + assertManaOptions("{W}{W}{B}{B}", manaOptions); + assertManaOptions("{W}{B}{B}{B}", manaOptions); + assertManaOptions("{B}{B}{B}{B}", manaOptions); + } + + @Test + public void TestCrystallineCrawler() { + setStrictChooseMode(true); + // Converge - Crystalline Crawler enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. + // Remove a +1/+1 counter from Crystalline Crawler: Add one mana of any color. + // {T}: Put a +1/+1 counter on Crystalline Crawler. + addCard(Zone.BATTLEFIELD, playerA, "Crystalline Crawler", 1); + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crystalline Crawler", CounterType.P1P1, 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{Any}{Any}", manaOptions); + } + + @Test + public void TestCoalGolemAndDromarsAttendant() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + + // {1}, Sacrifice Dromar's Attendant: Add {W}{U}{B}. + addCard(Zone.BATTLEFIELD, playerA, "Dromar's Attendant", 1); + + // {3}, Sacrifice Coal Golem: Add {R}{R}{R}. + addCard(Zone.BATTLEFIELD, playerA, "Coal Golem", 1); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 3, manaOptions.size()); + assertManaOptions("{G}", manaOptions); + assertManaOptions("{W}{U}{B}", manaOptions); + assertManaOptions("{R}{R}{R}", manaOptions); + } + + /** + * The order the mana abilities are checked during available mana + * calculation does matter. Because Coal Golem can not be used as long as + * Dromar's Attendant is not used yet to produce the 3 mana. + */ + @Test + public void TestCoalGolemAndDromarsAttendantOrder2() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + + // {3}, Sacrifice Coal Golem: Add {R}{R}{R}. + addCard(Zone.BATTLEFIELD, playerA, "Coal Golem", 1); + + // {1}, Sacrifice Dromar's Attendant: Add {W}{U}{B}. + addCard(Zone.BATTLEFIELD, playerA, "Dromar's Attendant", 1); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 3, manaOptions.size()); + assertManaOptions("{G}", manaOptions); + assertManaOptions("{W}{U}{B}", manaOptions); + assertManaOptions("{R}{R}{R}", manaOptions); + } + + @Test + public void TestJunglePatrol() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + // {1}{G}, {T}: Create a 0/1 green Wall creature token with defender named Wood. + // Sacrifice a token named Wood: Add {R}. + addCard(Zone.BATTLEFIELD, playerA, "Jungle Patrol", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{G}, {T}: Create"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{G}, {T}: Create"); + activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{G}, {T}: Create"); + + setStopAt(5, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Wood", 3); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{R}{R}{R}", manaOptions); + } + + @Test + public void TestSquanderedResources() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Taiga", 1); // ({T}: Add {R} or {G}.) + // {T}: Add {C}. + // {1}, {T}: Put a storage counter on Calciform Pools. + // {1}, Remove X storage counters from Calciform Pools: Add X mana in any combination of {W} and/or {U}. + addCard(Zone.BATTLEFIELD, playerA, "Calciform Pools", 1); + // {T}: Add {U}. If you played a land this turn, add {B} instead. + addCard(Zone.BATTLEFIELD, playerA, "River of Tears", 1); + + // Sacrifice a land: Add one mana of any type the sacrificed land could produce. + addCard(Zone.BATTLEFIELD, playerA, "Squandered Resources", 1); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 9, manaOptions.size()); + assertManaOptions("{C}{U}{U}{U}{R}{R}{G}{G}", manaOptions); + assertManaOptions("{C}{U}{U}{U}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{C}{U}{U}{U}{R}{G}{G}{G}", manaOptions); + assertManaOptions("{C}{C}{U}{U}{R}{R}{G}{G}", manaOptions); + assertManaOptions("{C}{C}{U}{U}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{C}{C}{U}{U}{R}{G}{G}{G}", manaOptions); + assertManaOptions("{C}{W}{U}{U}{R}{R}{G}{G}", manaOptions); + assertManaOptions("{C}{W}{U}{U}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{C}{W}{U}{U}{R}{G}{G}{G}", manaOptions); + } + + @Test + public void TestSquanderedResourcesWithManaConfluence() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // {T}, Pay 1 life: Add one mana of any color. + addCard(Zone.BATTLEFIELD, playerA, "Mana Confluence", 1); + + // Sacrifice a land: Add one mana of any type the sacrificed land could produce. + addCard(Zone.BATTLEFIELD, playerA, "Squandered Resources", 1); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{G}{G}{Any}{Any}", manaOptions); + } + + @Test + public void TestTreasonousOgre() { + setStrictChooseMode(true); + // Dethrone + // Pay 3 life: Add {R}. + addCard(Zone.BATTLEFIELD, playerA, "Treasonous Ogre", 1); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{R}{R}{R}{R}{R}{R}", manaOptions); + } + + @Test + public void TestSquanderedResourcesTwoSwamps() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + // Sacrifice a land: Add one mana of any type the sacrificed land could produce. + addCard(Zone.BATTLEFIELD, playerA, "Squandered Resources", 1); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{B}{B}{B}{B}", manaOptions); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ReflectingPoolTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ReflectingPoolTest.java index 5ba92659ea..7dad6a73ad 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ReflectingPoolTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ReflectingPoolTest.java @@ -5,7 +5,6 @@ import mage.constants.ManaType; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -133,7 +132,6 @@ public class ReflectingPoolTest extends CardTestPlayerBase { * producing mana */ @Test - @Ignore public void testWithDifferentLands() { addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); @@ -215,7 +213,6 @@ public class ReflectingPoolTest extends CardTestPlayerBase { } @Test - @Ignore public void testReflectingPoolAnyManaNeedWithoutCondition() { // any mana source without conditions (use any mana at any time) addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); @@ -233,7 +230,6 @@ public class ReflectingPoolTest extends CardTestPlayerBase { } @Test - @Ignore public void testReflectingPoolAnyManaNeedWithCondition() { // any mana source have condition to use (Reflecting Pool must ignore that condition) addCard(Zone.BATTLEFIELD, playerA, "Cavern of Souls", 1); // {C} or {any} @@ -252,7 +248,7 @@ public class ReflectingPoolTest extends CardTestPlayerBase { public void testReflectingPoolAnyManaTapped() { // any mana source with tapped must allow use any too addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); - addCard(Zone.BATTLEFIELD, playerA, "City of Brass", 1); + addCard(Zone.BATTLEFIELD, playerA, "City of Brass", 1); addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1); addCard(Zone.BATTLEFIELD, playerA, "Upwelling", 1); @@ -270,4 +266,49 @@ public class ReflectingPoolTest extends CardTestPlayerBase { assertTapped("Reflecting Pool", false); Assert.assertEquals(1, playerA.getManaPool().get(ManaType.BLACK)); } + + /** + * I only control 3 lands, a Triome, a Reflecting Pool, and a Mana + * Confluence. The Reflecting Pool is able to tap for colorless, but it + * should not be able to. + * + * https://blogs.magicjudges.org/rulestips/2012/09/you-have-to-name-a-color-when-you-add-one-mana-of-any-color-to-your-mana-pool/ + */ + @Test + public void testWithTriomeAndManaConfluence() { + // {T}: Add {C}. + // {1}{U}, {T}: Put target artifact card from your graveyard on top of your library. + addCard(Zone.BATTLEFIELD, playerA, "Academy Ruins", 1); + // {T}, Pay 1 life: Add one mana of any color. + addCard(Zone.BATTLEFIELD, playerA, "Mana Confluence", 1); + // {T}: Add one mana of any type that a land you control could produce. + addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + ManaOptions options = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("Player A should be able to create only 2 different mana options", 2, options.size()); + assertManaOptions("{C}{C}{Any}", options); + assertManaOptions("{C}{Any}{Any}", options); + } + + @Test + public void testWithCalciformPools() { + // {T}: Add {C}. + // {1}, {T}: Put a storage counter on Calciform Pools. + // {1}, Remove X storage counters from Calciform Pools: Add X mana in any combination of {W} and/or {U}. + addCard(Zone.BATTLEFIELD, playerA, "Calciform Pools", 1); + // {T}: Add one mana of any type that a land you control could produce. + addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + ManaOptions options = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("Player A should be able to create only 3 different mana options", 3, options.size()); + assertManaOptions("{C}{C}", options); + assertManaOptions("{C}{W}", options); + assertManaOptions("{C}{U}", options); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/RiverOfTearsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/RiverOfTearsTest.java index 6faaf0f6ad..3f764e329d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/RiverOfTearsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/RiverOfTearsTest.java @@ -1,61 +1,61 @@ -package org.mage.test.cards.mana; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class RiverOfTearsTest extends CardTestPlayerBase { - - /** - * River of Tears doesn't produce black mana any time. Gatherer says: - * - * 17/11/2017 The turn you play River of Tears, it will produce Black when - * tapped for mana. - * - * 17/11/2017 River of Tears produces Black only after you've played a land, - * not after you've put a land onto the battlefield (such as with Evolving - * Wilds). - */ - @Test - public void testBlackAfterPlayed() { - // {T}: Add {U}. If you played a land this turn, add {B} instead. - addCard(Zone.HAND, playerA, "River of Tears", 1); // Land - - addCard(Zone.HAND, playerA, "Nightshade Stinger", 1); // Creature {B} - - playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "River of Tears"); - - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Nightshade Stinger"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - assertPermanentCount(playerA, "River of Tears", 1); - assertTapped("River of Tears", true); - assertPermanentCount(playerA, "Nightshade Stinger", 1); - } - - @Test - public void testBlueInSecondTurn() { - // {T}: Add {U}. If you played a land this turn, add {B} instead. - addCard(Zone.HAND, playerA, "River of Tears", 1); // Land - - addCard(Zone.HAND, playerA, "Aven Envoy", 1); // Creature {B} - - playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "River of Tears"); - - castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Aven Envoy"); - - setStopAt(3, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "River of Tears", 1); - assertTapped("River of Tears", true); - assertPermanentCount(playerA, "Aven Envoy", 1); - } -} +package org.mage.test.cards.mana; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class RiverOfTearsTest extends CardTestPlayerBase { + + /** + * River of Tears doesn't produce black mana any time. Gatherer says: + * + * 17/11/2017 The turn you play River of Tears, it will produce Black when + * tapped for mana. + * + * 17/11/2017 River of Tears produces Black only after you've played a land, + * not after you've put a land onto the battlefield (such as with Evolving + * Wilds). + */ + @Test + public void testBlackAfterPlayed() { + // {T}: Add {U}. If you played a land this turn, add {B} instead. + addCard(Zone.HAND, playerA, "River of Tears", 1); // Land + + addCard(Zone.HAND, playerA, "Nightshade Stinger", 1); // Creature {B} + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "River of Tears"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Nightshade Stinger"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "River of Tears", 1); + assertTapped("River of Tears", true); + assertPermanentCount(playerA, "Nightshade Stinger", 1); + } + + @Test + public void testBlueInSecondTurn() { + // {T}: Add {U}. If you played a land this turn, add {B} instead. + addCard(Zone.HAND, playerA, "River of Tears", 1); // Land + + addCard(Zone.HAND, playerA, "Aven Envoy", 1); // Creature {B} + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "River of Tears"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Aven Envoy"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "River of Tears", 1); + assertTapped("River of Tears", true); + assertPermanentCount(playerA, "Aven Envoy", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java index 7351eba115..e48aeed312 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java @@ -14,23 +14,6 @@ import org.mage.test.serverside.base.CardTestPlayerBase; */ public class SylvokExplorerTest extends CardTestPlayerBase { - /** - * java.lang.StackOverflowError at - * mage.filter.predicate.Predicates.and(Predicates.java:68) at - * mage.filter.FilterImpl.match(FilterImpl.java:62) at - * mage.filter.FilterPermanent.match(FilterPermanent.java:74) at - * mage.game.permanent.Battlefield.getActivePermanents(Battlefield.java:362) - * at - * mage.abilities.mana.AnyColorLandsProduceManaEffect.getManaTypes(AnyColorLandsProduceManaAbility.java:164) - * at - * mage.abilities.mana.AnyColorLandsProduceManaEffect.getNetMana(AnyColorLandsProduceManaAbility.java:181) - * at - * mage.abilities.mana.AnyColorLandsProduceManaAbility.getNetMana(AnyColorLandsProduceManaAbility.java:70) - * at - * mage.abilities.mana.AnyColorLandsProduceManaEffect.getManaTypes(AnyColorLandsProduceManaAbility.java:170) - * at - * mage.abilities.mana.AnyColorLandsProduceManaEffect.getNetMana(AnyColorLandsProduceManaAbility.java:181) - */ @Test @Ignore public void testOneInstance() { @@ -42,7 +25,9 @@ public class SylvokExplorerTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); ManaOptions options = playerA.getAvailableManaTest(currentGame); Assert.assertEquals("Player should be able to create 1 red and 1 white mana", "{W}{R}", options.get(0).toString()); @@ -51,6 +36,7 @@ public class SylvokExplorerTest extends CardTestPlayerBase { @Test public void testTwoInstances() { + // {T}: Add one mana of any color that a land an opponent controls could produce. addCard(Zone.BATTLEFIELD, playerB, "Exotic Orchard", 2); // {T}: Add one mana of any color that a land an opponent controls could produce. @@ -58,8 +44,11 @@ public class SylvokExplorerTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + + setStrictChooseMode(true); execute(); - + assertAllCommandsUsed(); + ManaOptions options = playerA.getAvailableManaTest(currentGame); Assert.assertEquals("Player should be able to create 3 white mana", "{W}{W}{W}", options.get(0).toString()); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/TappedForManaRelatedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/TappedForManaRelatedTest.java new file mode 100644 index 0000000000..6e941e49d2 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/TappedForManaRelatedTest.java @@ -0,0 +1,313 @@ +package org.mage.test.cards.mana; + +import mage.abilities.mana.ManaOptions; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.utils.ManaOptionsTestUtils.assertManaOptions; + +/** + * + * @author LevelX2 + */ +public class TappedForManaRelatedTest extends CardTestPlayerBase { + + /** + * This is a new rule that slightly changes how we resolve abilities that + * trigger whenever a permanent is tapped for mana or for mana of a + * specified type. Now, you look at what was actually produced after the + * activated mana ability resolves. So, tapping a Gaea's Cradle while you no + * control no creatures won't cause a Wild Growth attached to it to trigger. + */ + @Test + public void TestCradleWithWildGrowthNoCreatures() { + // {T}: Add {G} for each creature you control. + addCard(Zone.BATTLEFIELD, playerA, "Gaea's Cradle", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // Enchant land + // Whenever enchanted land is tapped for mana, its controller adds {G}. + addCard(Zone.HAND, playerA, "Wild Growth", 1); // Enchantment {G} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wild Growth", "Gaea's Cradle"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Wild Growth", 1); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{G}{G}", manaOptions); + + } + + @Test + public void TestCradleWithWildGrowthTwoCreatures() { + // {T}: Add {G} for each creature you control. + addCard(Zone.BATTLEFIELD, playerA, "Gaea's Cradle", 1); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2); + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // Enchant land + // Whenever enchanted land is tapped for mana, its controller adds {G}. + addCard(Zone.HAND, playerA, "Wild Growth", 1); // Enchantment {G} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wild Growth", "Gaea's Cradle"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Wild Growth", 1); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{G}{G}{G}{G}", manaOptions); + + } + + @Test + public void TestWildGrowth() { + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2); + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // Enchant land + // Whenever enchanted land is tapped for mana, its controller adds {G}. + addCard(Zone.HAND, playerA, "Wild Growth", 1); // Enchantment {G} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wild Growth", "Forest"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Wild Growth", 1); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{G}{G}", manaOptions); + + } + + @Test + public void TestCalciformPools() { + // {T}: Add {C}. + // {1}, {T}: Put a storage counter on Calciform Pools. + // {1}, Remove X storage counters from Calciform Pools: Add X mana in any combination of {W} and/or {U}. + addCard(Zone.BATTLEFIELD, playerA, "Calciform Pools", 1); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{C}", manaOptions); + + } + + @Test + public void TestCalciformPools2Counter() { + // {T}: Add {C}. + // {1}, {T}: Put a storage counter on Calciform Pools. + // {1}, Remove X storage counters from Calciform Pools: Add X mana in any combination of {W} and/or {U}. + addCard(Zone.BATTLEFIELD, playerA, "Calciform Pools", 1); + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Calciform Pools", CounterType.STORAGE, 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertCounterCount("Calciform Pools", CounterType.STORAGE, 2); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 4, manaOptions.size()); + assertManaOptions("{W}{W}", manaOptions); + assertManaOptions("{W}{U}", manaOptions); + assertManaOptions("{U}{U}", manaOptions); + assertManaOptions("{C}", manaOptions); + } + + @Test + public void TestCalciformPools2CounterAndTrigger() { + setStrictChooseMode(true); + // {T}: Add {C}. + // {1}, {T}: Put a storage counter on Calciform Pools. + // {1}, Remove X storage counters from Calciform Pools: Add X mana in any combination of {W} and/or {U}. + addCard(Zone.BATTLEFIELD, playerA, "Calciform Pools", 1); + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Calciform Pools", CounterType.STORAGE, 2); + + // As Caged Sun enters the battlefield, choose a color. + // Creatures you control of the chosen color get +1/+1. + // Whenever a land's ability adds one or more mana of the chosen color, add one additional mana of that color. + addCard(Zone.BATTLEFIELD, playerA, "Caged Sun", 1); + setChoice(playerA, "White"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertCounterCount("Calciform Pools", CounterType.STORAGE, 2); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 4, manaOptions.size()); + assertManaOptions("{W}{W}{W}", manaOptions); + assertManaOptions("{W}{W}{U}", manaOptions); + assertManaOptions("{U}{U}", manaOptions); + assertManaOptions("{C}", manaOptions); + } + + @Test + public void TestCastleSengir() { + setStrictChooseMode(true); + // {T}: Add Colorless. + // {1}, {T}: Add Black. + // {2}, {T}: Add Blue or Red. + addCard(Zone.BATTLEFIELD, playerA, "Castle Sengir", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 2, manaOptions.size()); + assertManaOptions("{C}{R}", manaOptions); + assertManaOptions("{B}", manaOptions); + } + + @Test + public void TestCastleSengir2() { + setStrictChooseMode(true); + // {T}: Add {C}. + // {1}, {T}: Add {B}. + // {2}, {T}: Add {U} or {R}. + addCard(Zone.BATTLEFIELD, playerA, "Castle Sengir", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 4, manaOptions.size()); + assertManaOptions("{C}{W}{W}", manaOptions); + assertManaOptions("{W}{B}", manaOptions); + assertManaOptions("{U}", manaOptions); + assertManaOptions("{R}", manaOptions); + } + + + + @Test + @Ignore // Because this is no mana ability, this mana will not be calculated during available mana calculation + public void TestDeathriteShaman() { + setStrictChooseMode(true); + // {T}: Exile target land card from a graveyard. Add one mana of any color. + // {B}, {T}: Exile target instant or sorcery card from a graveyard. Each opponent loses 2 life. + // {G}, {T}: Exile target creature card from a graveyard. You gain 2 life. + addCard(Zone.BATTLEFIELD, playerA, "Deathrite Shaman", 1); + + addCard(Zone.GRAVEYARD, playerA, "Mountain", 3); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{Any}", manaOptions); + } + + @Test + public void TestEyeOfRamos() { + setStrictChooseMode(true); + // {T}: Add {U}. + // Sacrifice Eye of Ramos: Add {U}. + addCard(Zone.BATTLEFIELD, playerA, "Eye of Ramos", 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{U}{U}{U}{U}", manaOptions); + } + + + + @Test + public void TestChromaticOrrery() { + setStrictChooseMode(true); + // You may spend mana as though it were mana of any color. + // {T}: Add {C}{C}{C}{C}{C}. + // {5}, {T}: Draw a card for each color among permanents you control. + addCard(Zone.BATTLEFIELD, playerA, "Chromatic Orrery", 1); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{C}{C}{C}{C}{C}", manaOptions); + } + + @Test + public void TestViridianJoiner() { + setStrictChooseMode(true); + + // {T}: Add an amount of {G} equal to Viridian Joiner's power. + addCard(Zone.HAND, playerA, "Viridian Joiner", 1); // Creature {2}{G} 1/2 + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + + addCard(Zone.BATTLEFIELD, playerB, "Forest", 1); + addCard(Zone.HAND, playerB, "Giant Growth", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Viridian Joiner"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerB, "Giant Growth", "Viridian Joiner"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Giant Growth", 1); + assertPowerToughness(playerA, "Viridian Joiner", 4, 5); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{G}{G}{G}{G}{G}{G}{G}", manaOptions); + } + + @Test + public void TestPriestOfYawgmoth() { + setStrictChooseMode(true); + + // {T}, Sacrifice an artifact: Add an amount of {B} equal to the sacrificed artifact's converted mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Priest of Yawgmoth", 1); // Creature {1}{B} 1/2 + + addCard(Zone.BATTLEFIELD, playerA, "Abandoned Sarcophagus", 1); // {3} + addCard(Zone.BATTLEFIELD, playerA, "Accorder's Shield", 1); // {0} + addCard(Zone.BATTLEFIELD, playerA, "Adarkar Sentinel", 1); // {5} + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{B}{B}{B}{B}{B}", manaOptions); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/phyrexian/PhyrexianManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/phyrexian/PhyrexianManaTest.java index 69e7f6d90e..8ce24a2953 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/phyrexian/PhyrexianManaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/phyrexian/PhyrexianManaTest.java @@ -31,25 +31,35 @@ public class PhyrexianManaTest extends CardTestPlayerBase { @Test public void testKrrikOnlyUsableByController() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + setStrictChooseMode(true); + + // ({B/P} can be paid with either {B} or 2 life.) + // Lifelink + // For each {B} in a cost, you may pay 2 life rather than pay that mana. + // Whenever you cast a black spell, put a +1/+1 counter on K'rrik, Son of Yawgmoth. addCard(Zone.BATTLEFIELD, playerA, "K'rrik, Son of Yawgmoth"); addCard(Zone.HAND, playerA, "Banehound"); - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1); - addCard(Zone.HAND, playerB, "Banehound"); + // Lifelink, haste + addCard(Zone.HAND, playerB, "Banehound"); // Creature {B} 1/1 - setChoice(playerA, "Yes"); + checkPlayableAbility("pay 2 life for Banehound", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Banehound", true); + + setChoice(playerA, "Yes"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Banehound"); - setChoice(playerB, "Yes"); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Banehound"); + + checkPlayableAbility("no Mana for Banehound", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Banehound", false); + setStopAt(2, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); + //PlayerA pays life but PlayerB cannot assertLife(playerA, 18); assertLife(playerB, 20); assertPermanentCount(playerA, "Banehound", 1); - assertPermanentCount(playerB, "Banehound", 1); + assertPermanentCount(playerB, "Banehound", 0); } @Test @@ -121,4 +131,65 @@ public class PhyrexianManaTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Dismember", 1); assertGraveyardCount(playerB, "Banehound", 1); } + + @Test + public void testPlayerCanCastBanehoundWithoutAvailableBlackMana() { + setStrictChooseMode(true); + + // ({B/P} can be paid with either {B} or 2 life.) + // Lifelink + // For each {B} in a cost, you may pay 2 life rather than pay that mana. + // Whenever you cast a black spell, put a +1/+1 counter on K'rrik, Son of Yawgmoth. + addCard(Zone.BATTLEFIELD, playerA, "K'rrik, Son of Yawgmoth"); // Creature {4}{B/P}{B/P}{B/P} 2/2 + addCard(Zone.HAND, playerA, "Banehound"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Banehound"); + setChoice(playerA, "Yes"); // Pay 2 life for {B} + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + //PlayerA pays life + assertLife(playerA, 18); + assertLife(playerB, 20); + + + assertPermanentCount(playerA, "Banehound", 1); + } + + @Test + public void testPlayerEffectNotUsableIfKrrikNotOnBattlefield() { + setStrictChooseMode(true); + + // addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // ({B/P} can be paid with either {B} or 2 life.) + // Lifelink + // For each {B} in a cost, you may pay 2 life rather than pay that mana. + // Whenever you cast a black spell, put a +1/+1 counter on K'rrik, Son of Yawgmoth. + addCard(Zone.BATTLEFIELD, playerA, "K'rrik, Son of Yawgmoth"); // Creature {4}{B/P}{B/P}{B/P} 2/2 + addCard(Zone.HAND, playerA, "Banehound"); + + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.UPKEEP, playerB, "Lightning Bolt", "K'rrik, Son of Yawgmoth"); + + checkPlayableAbility("no Mana for Banehound", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Banehound", false); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertGraveyardCount(playerA, "K'rrik, Son of Yawgmoth", 1); + + assertHandCount(playerA, "Banehound", 1); + //PlayerA pays life + assertLife(playerA, 20); + assertLife(playerB, 20); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/modal/ChooseOneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/modal/ChooseOneTest.java index fc28594a26..5453c1cb6c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/modal/ChooseOneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/modal/ChooseOneTest.java @@ -1,79 +1,79 @@ - -package org.mage.test.cards.modal; - -import mage.abilities.keyword.SwampwalkAbility; -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class ChooseOneTest extends CardTestPlayerBase { - - @Test - public void testFirstMode() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - // Choose one - // - Target player discards a card - // - Target creature gets +2/-1 until end of turn. - // - Target creature gains swampwalk until end of turn. - addCard(Zone.HAND, playerA, "Funeral Charm"); // Instant {B} - - addCard(Zone.HAND, playerB, "Silvercoat Lion"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Funeral Charm", playerB); - setModeChoice(playerA, "1"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Funeral Charm", 1); - assertGraveyardCount(playerB, "Silvercoat Lion", 1); - } - - @Test - public void testSecondMode() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - // Choose one - // - Target player discards a card - // - Target creature gets +2/-1 until end of turn. - // - Target creature gains swampwalk until end of turn. - addCard(Zone.HAND, playerA, "Funeral Charm"); // Instant {B} - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Funeral Charm", "Silvercoat Lion"); - setModeChoice(playerA, "2"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Funeral Charm", 1); - assertPowerToughness(playerB, "Silvercoat Lion", 4, 1); - } - - @Test - public void testThirdMode() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - // Choose one - // - Target player discards a card - // - Target creature gets +2/-1 until end of turn. - // - Target creature gains swampwalk until end of turn. - addCard(Zone.HAND, playerA, "Funeral Charm"); // Instant {B} - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Funeral Charm", "Silvercoat Lion"); - setModeChoice(playerA, "3"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Funeral Charm", 1); - assertPowerToughness(playerB, "Silvercoat Lion", 2, 2); - assertAbility(playerB, "Silvercoat Lion", new SwampwalkAbility(), true); - } -} + +package org.mage.test.cards.modal; + +import mage.abilities.keyword.SwampwalkAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class ChooseOneTest extends CardTestPlayerBase { + + @Test + public void testFirstMode() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // Choose one + // - Target player discards a card + // - Target creature gets +2/-1 until end of turn. + // - Target creature gains swampwalk until end of turn. + addCard(Zone.HAND, playerA, "Funeral Charm"); // Instant {B} + + addCard(Zone.HAND, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Funeral Charm", playerB); + setModeChoice(playerA, "1"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Funeral Charm", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + } + + @Test + public void testSecondMode() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // Choose one + // - Target player discards a card + // - Target creature gets +2/-1 until end of turn. + // - Target creature gains swampwalk until end of turn. + addCard(Zone.HAND, playerA, "Funeral Charm"); // Instant {B} + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Funeral Charm", "Silvercoat Lion"); + setModeChoice(playerA, "2"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Funeral Charm", 1); + assertPowerToughness(playerB, "Silvercoat Lion", 4, 1); + } + + @Test + public void testThirdMode() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // Choose one + // - Target player discards a card + // - Target creature gets +2/-1 until end of turn. + // - Target creature gains swampwalk until end of turn. + addCard(Zone.HAND, playerA, "Funeral Charm"); // Instant {B} + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Funeral Charm", "Silvercoat Lion"); + setModeChoice(playerA, "3"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Funeral Charm", 1); + assertPowerToughness(playerB, "Silvercoat Lion", 2, 2); + assertAbility(playerB, "Silvercoat Lion", new SwampwalkAbility(), true); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrMoreTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrMoreTest.java new file mode 100644 index 0000000000..2160bc9bc5 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrMoreTest.java @@ -0,0 +1,100 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.modal; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author LevelX2 + */ +public class OneOrMoreTest extends CardTestPlayerBase { + + /** + * Sublime Epiphany can bounce and + * copy the same creature. This is because legality of targets is checked + * only as the spell begins to resolve, not in between modes, and because + * the games can use last known info of the legal target. + */ + @Test + public void test_ChooseModes_AsCardOrder() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + // Choose one or more — + // 1 • Counter target spell + // 2 • Counter target activated or triggered ability. + // 3 • Return target nonland permanent to its owner's hand. + // 4 • Create a token that's a copy of target creature you control. + // 5 • Target player draws a card. + addCard(Zone.HAND, playerA, "Sublime Epiphany"); // Instant {4}{U}{U} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sublime Epiphany"); + setModeChoice(playerA, "3"); + setModeChoice(playerA, "4"); + setModeChoice(playerA, "5"); + addTarget(playerA, "Silvercoat Lion"); // for 3 + addTarget(playerA, "Silvercoat Lion"); // for 4 + addTarget(playerA, playerB); // for 5 + setModeChoice(playerA, null); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerB, 1); + assertHandCount(playerA, "Silvercoat Lion", 1); + + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + + Permanent perm = getPermanent("Silvercoat Lion"); + Assert.assertTrue("Silvercoat Lion has to be a Token", perm instanceof PermanentToken); + } + + @Test + public void test_ChooseModes_AsCustomOrder() { + // user can select modes in any order, but resolves/targets must be standard (in same order as card's text) + + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + // Choose one or more — + // 1 • Counter target spell + // 2 • Counter target activated or triggered ability. + // 3 • Return target nonland permanent to its owner's hand. + // 4 • Create a token that's a copy of target creature you control. + // 5 • Target player draws a card. + addCard(Zone.HAND, playerA, "Sublime Epiphany"); // Instant {4}{U}{U} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sublime Epiphany"); + setModeChoice(playerA, "4"); + setModeChoice(playerA, "5"); + setModeChoice(playerA, "3"); + addTarget(playerA, "Silvercoat Lion"); // for 3 + addTarget(playerA, "Silvercoat Lion"); // for 4 + addTarget(playerA, playerB); // for 5 + setModeChoice(playerA, null); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerB, 1); + assertHandCount(playerA, "Silvercoat Lion", 1); + + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + + Permanent perm = getPermanent("Silvercoat Lion"); + Assert.assertTrue("Silvercoat Lion has to be a Token", perm instanceof PermanentToken); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/modal/SameModeMoreThanOnceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/modal/SameModeMoreThanOnceTest.java index db9c00b8de..5f3d44a022 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/modal/SameModeMoreThanOnceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/modal/SameModeMoreThanOnceTest.java @@ -1,73 +1,73 @@ - -package org.mage.test.cards.modal; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class SameModeMoreThanOnceTest extends CardTestPlayerBase { - - @Test - public void testEachModeOnce() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); - // Choose three. You may choose the same mode more than once. - // - Target player draws a card and loses 1 life; - // - Target creature gets -2/-2 until end of turn; - // - Return target creature card from your graveyard to your hand. - addCard(Zone.HAND, playerA, "Wretched Confluence"); // Instant {3}{B}{B} - - addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); - addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wretched Confluence", "mode=1targetPlayer=PlayerA^mode=2Pillarfield Ox^mode=3Silvercoat Lion"); - setModeChoice(playerA, "1"); - setModeChoice(playerA, "2"); - setModeChoice(playerA, "3"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Wretched Confluence", 1); - assertLife(playerA, 19); - assertLife(playerB, 20); - assertHandCount(playerA, 2); - assertPowerToughness(playerB, "Pillarfield Ox", 0, 2); - assertGraveyardCount(playerA, "Silvercoat Lion", 0); - - } - - @Test - public void testSecondModeTwiceThridModeOnce() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); - // Choose three. You may choose the same mode more than once. - // - Target player draws a card and loses 1 life; - // - Target creature gets -2/-2 until end of turn; - // - Return target creature card from your graveyard to your hand. - addCard(Zone.HAND, playerA, "Wretched Confluence"); // Instant {3}{B}{B} - - addCard(Zone.BATTLEFIELD, playerB, "Wall of Air"); - addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); - addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wretched Confluence", "mode=1Pillarfield Ox^mode=2Wall of Air^mode=3Silvercoat Lion"); - setModeChoice(playerA, "2"); - setModeChoice(playerA, "2"); - setModeChoice(playerA, "3"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Wretched Confluence", 1); - assertLife(playerA, 20); - assertLife(playerB, 20); - assertPowerToughness(playerB, "Wall of Air", -1, 3); - assertPowerToughness(playerB, "Pillarfield Ox", 0, 2); - assertGraveyardCount(playerA, "Silvercoat Lion", 0); - - } -} + +package org.mage.test.cards.modal; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class SameModeMoreThanOnceTest extends CardTestPlayerBase { + + @Test + public void testEachModeOnce() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + // Choose three. You may choose the same mode more than once. + // - Target player draws a card and loses 1 life; + // - Target creature gets -2/-2 until end of turn; + // - Return target creature card from your graveyard to your hand. + addCard(Zone.HAND, playerA, "Wretched Confluence"); // Instant {3}{B}{B} + + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wretched Confluence", "mode=1targetPlayer=PlayerA^mode=2Pillarfield Ox^mode=3Silvercoat Lion"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "3"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Wretched Confluence", 1); + assertLife(playerA, 19); + assertLife(playerB, 20); + assertHandCount(playerA, 2); + assertPowerToughness(playerB, "Pillarfield Ox", 0, 2); + assertGraveyardCount(playerA, "Silvercoat Lion", 0); + + } + + @Test + public void testSecondModeTwiceThridModeOnce() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + // Choose three. You may choose the same mode more than once. + // - Target player draws a card and loses 1 life; + // - Target creature gets -2/-2 until end of turn; + // - Return target creature card from your graveyard to your hand. + addCard(Zone.HAND, playerA, "Wretched Confluence"); // Instant {3}{B}{B} + + addCard(Zone.BATTLEFIELD, playerB, "Wall of Air"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wretched Confluence", "mode=1Pillarfield Ox^mode=2Wall of Air^mode=3Silvercoat Lion"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "3"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Wretched Confluence", 1); + assertLife(playerA, 20); + assertLife(playerB, 20); + assertPowerToughness(playerB, "Wall of Air", -1, 3); + assertPowerToughness(playerB, "Pillarfield Ox", 0, 2); + assertGraveyardCount(playerA, "Silvercoat Lion", 0); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java index 0363ca6c1e..a8302a41b6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.planeswalker; import mage.abilities.keyword.IndestructibleAbility; @@ -11,7 +10,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class GideonTest extends CardTestPlayerBase { @@ -116,16 +114,23 @@ public class GideonTest extends CardTestPlayerBase { // Equip {2} addCard(Zone.BATTLEFIELD, playerB, "Stitcher's Graft", 1); + // transform attack(2, playerB, "Kytheon, Hero of Akros"); attack(2, playerB, "Silvercoat Lion"); attack(2, playerB, "Pillarfield Ox"); + checkPermanentCount("after transform", 2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Gideon, Battle-Forged", 1); + // become creature and equip activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, "0: Until "); + waitStackResolved(4, PhaseStep.PRECOMBAT_MAIN); activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {2}", "Gideon, Battle-Forged"); + attack(4, playerB, "Gideon, Battle-Forged"); // 7 damage + setStrictChooseMode(true); setStopAt(5, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerB, "Silvercoat Lion", 1); assertLife(playerA, 7); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java index 598ed67bdc..397ba5e480 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java @@ -1,9 +1,9 @@ - package org.mage.test.cards.planeswalker; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.counters.CounterType; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -72,35 +72,74 @@ public class JaceTest extends CardTestPlayerBase { assertExileCount("Jace, Vryn's Prodigy", 0); assertPermanentCount(playerA, "Jace, Telepath Unbound", 1); } - + + /** + * Rollback doesn't unflip a newly flipped Jace #1973 Reporting on behalf of + * someone else (but he plays a lot on xmage) so I don't have any further + * information, but a rollback didn't reset a jace, vryn's prodigy. Haven't + * tried to recreate it. + */ + @Test + public void rollbackDoesntUnflipJaceTest() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.GRAVEYARD, playerA, "Mountain", 4); + + // {T}: Draw a card, then discard a card. If there are five or more cards in your graveyard, + // exile Jace, Vryn's Prodigy, then return him to the battefield transformed under his owner's control. + addCard(Zone.BATTLEFIELD, playerA, "Jace, Vryn's Prodigy", 1); // {U}{1} - 0/2 + addCard(Zone.HAND, playerA, "Pillarfield Ox", 1); + + // Flash + // If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead. + addCard(Zone.BATTLEFIELD, playerB, "Containment Priest", 1); // {2}{U}{U} + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card"); + setChoice(playerA, "Pillarfield Ox"); + + rollbackTurns(3, PhaseStep.BEGIN_COMBAT, playerA, 0); // Start of turn 3 + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Pillarfield Ox", 0); // Goes back to hand + assertHandCount(playerA, "Pillarfield Ox", 1); + + assertExileCount("Jace, Vryn's Prodigy", 0); + + assertPermanentCount(playerA, "Jace, Telepath Unbound", 0); + assertPermanentCount(playerA, "Jace, Vryn's Prodigy", 1); + + Assert.assertFalse("Jace, Vryn's Prodigy may not be flipped", getPermanent("Jace, Vryn's Prodigy").isFlipped()); + } + @Test public void vrynCannotCastAncestralVisions() { - + // {T}: Draw a card, then discard a card. If there are five or more cards in your graveyard, // exile Jace, Vryn's Prodigy, then return him to the battefield transformed under his owner's control. String jVryn = "Jace, Vryn's Prodigy"; // {U}{1} 0/2 - - //−3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead. + + //−3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead. String jTelepath = "Jace, Telepath Unbound"; // 5 loyalty - + // Sorcery, Suspend 4 {U}. Target player draws three cards. - String ancestralVision = "Ancestral Vision"; - + String ancestralVision = "Ancestral Vision"; + addCard(Zone.BATTLEFIELD, playerA, "Jace, Vryn's Prodigy", 1); // {U}{1} - 0/2 addCard(Zone.BATTLEFIELD, playerA, "Island"); addCard(Zone.GRAVEYARD, playerA, "Island", 4); addCard(Zone.GRAVEYARD, playerA, ancestralVision); addCard(Zone.HAND, playerA, "Swamp", 1); - + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card, then discard a card. If there are five or more cards in your graveyard"); setChoice(playerA, "Swamp"); activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "-3:"); addTarget(playerA, ancestralVision); castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, ancestralVision); - + setStopAt(3, PhaseStep.BEGIN_COMBAT); execute(); - + assertPermanentCount(playerA, jTelepath, 1); assertGraveyardCount(playerA, "Swamp", 1); assertGraveyardCount(playerA, ancestralVision, 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/NissaVastwoodSeerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/NissaVastwoodSeerTest.java index 0ca072667c..48bd050aeb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/NissaVastwoodSeerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/NissaVastwoodSeerTest.java @@ -1,80 +1,80 @@ -package org.mage.test.cards.planeswalker; - -import mage.constants.CardType; -import mage.constants.PhaseStep; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.Filter; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - - -/** - * - * @author LevelX2 - */ -public class NissaVastwoodSeerTest extends CardTestPlayerBase { - -// https://github.com/magefree/mage/issues/5677 -/* -EDH again, I'm playing Nissa, Vastwood Seer as my commander. -I got her to 7 loyalty, used her -7 to untap lands and make them into creatures. So far so good. -Nissa died, went back to command zone, lands stayed as creatures (as they should). -Next turn I replay Nissa, but the moment I try to cast another spell (or activate an ability, -maybe it was directly on replaying her, can't remember exactly), all my 6 creature lands are back to being regular lands. -A few turns later, my opponent uses overloaded cyclonic rift, which also bounces Nissa to my hand. -Not entirely sure, but I believe it again took at least until priority got passed for my lands to reappear, -maybe even until I tried to activate an ability or cast something. -This was not a 1 time thing either. -After replaying Nissa (again) and casting another card, my creature lands went back to being regular lands again. -Later I used her -2 to kill her, which (after putting another spell on the stack) got me my creature lands back again. - */ - @Test - public void NissaVastwoodSeerAnimationTest() { - setStrictChooseMode(true); - - addCard(Zone.LIBRARY, playerA, "Forest"); - addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - - // When Nissa, Vastwood Seer enters the battlefield, you may search your library for a basic Forest card, - // reveal it, put it into your hand, then shuffle your library. - // Whenever a land enters the battlefield under your control, if you control seven or more lands, - // exile Nissa, then return her to the battlefield transformed under her owner's control. - // Nissa, Sage Animist - // +1: Reveal the top card of your library. If it's a land card, put it onto the battlefield. Otherwise, put it into your hand. - // -2: Create a legendary 4/4 green Elemental creature token named Ashaya, the Awoken World. - // -7: Untap up to six target lands. They become 6/6 Elemental creatures. They're still lands. - addCard(Zone.HAND, playerA, "Nissa, Vastwood Seer"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nissa, Vastwood Seer"); - setChoice(playerA, "Yes"); - addTarget(playerA, "Forest"); - - playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); - - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "+1: Reveal"); - activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "+1: Reveal"); - activateAbility(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "+1: Reveal"); - activateAbility(7, PhaseStep.POSTCOMBAT_MAIN, playerA, "+1: Reveal"); - activateAbility(9, PhaseStep.POSTCOMBAT_MAIN, playerA, "-7: Untap up to six target"); - addTarget(playerA, "Forest^Forest^Forest^Forest^Forest^Forest"); - setStopAt(9, PhaseStep.END_TURN); - execute(); - - assertPermanentCount(playerA, "Nissa, Vastwood Seer", 0); - assertPermanentCount(playerA, "Nissa, Sage Animist", 0); - assertGraveyardCount(playerA, "Nissa, Vastwood Seer", 1); - - assertPermanentCount(playerA, "Swamp", 1); - - assertType("Forest", CardType.CREATURE, SubType.ELEMENTAL); - assertPermanentCount(playerA, "Forest", 6); - assertPowerToughness(playerA, "Forest", 6, 6, Filter.ComparisonScope.All); - - - - - } - -} +package org.mage.test.cards.planeswalker; + +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.Filter; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + + +/** + * + * @author LevelX2 + */ +public class NissaVastwoodSeerTest extends CardTestPlayerBase { + +// https://github.com/magefree/mage/issues/5677 +/* +EDH again, I'm playing Nissa, Vastwood Seer as my commander. +I got her to 7 loyalty, used her -7 to untap lands and make them into creatures. So far so good. +Nissa died, went back to command zone, lands stayed as creatures (as they should). +Next turn I replay Nissa, but the moment I try to cast another spell (or activate an ability, +maybe it was directly on replaying her, can't remember exactly), all my 6 creature lands are back to being regular lands. +A few turns later, my opponent uses overloaded cyclonic rift, which also bounces Nissa to my hand. +Not entirely sure, but I believe it again took at least until priority got passed for my lands to reappear, +maybe even until I tried to activate an ability or cast something. +This was not a 1 time thing either. +After replaying Nissa (again) and casting another card, my creature lands went back to being regular lands again. +Later I used her -2 to kill her, which (after putting another spell on the stack) got me my creature lands back again. + */ + @Test + public void NissaVastwoodSeerAnimationTest() { + setStrictChooseMode(true); + + addCard(Zone.LIBRARY, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + + // When Nissa, Vastwood Seer enters the battlefield, you may search your library for a basic Forest card, + // reveal it, put it into your hand, then shuffle your library. + // Whenever a land enters the battlefield under your control, if you control seven or more lands, + // exile Nissa, then return her to the battlefield transformed under her owner's control. + // Nissa, Sage Animist + // +1: Reveal the top card of your library. If it's a land card, put it onto the battlefield. Otherwise, put it into your hand. + // -2: Create a legendary 4/4 green Elemental creature token named Ashaya, the Awoken World. + // -7: Untap up to six target lands. They become 6/6 Elemental creatures. They're still lands. + addCard(Zone.HAND, playerA, "Nissa, Vastwood Seer"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nissa, Vastwood Seer"); + setChoice(playerA, "Yes"); + addTarget(playerA, "Forest"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "+1: Reveal"); + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "+1: Reveal"); + activateAbility(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "+1: Reveal"); + activateAbility(7, PhaseStep.POSTCOMBAT_MAIN, playerA, "+1: Reveal"); + activateAbility(9, PhaseStep.POSTCOMBAT_MAIN, playerA, "-7: Untap up to six target"); + addTarget(playerA, "Forest^Forest^Forest^Forest^Forest^Forest"); + setStopAt(9, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Nissa, Vastwood Seer", 0); + assertPermanentCount(playerA, "Nissa, Sage Animist", 0); + assertGraveyardCount(playerA, "Nissa, Vastwood Seer", 1); + + assertPermanentCount(playerA, "Swamp", 1); + + assertType("Forest", CardType.CREATURE, SubType.ELEMENTAL); + assertPermanentCount(playerA, "Forest", 6); + assertPowerToughness(playerA, "Forest", 6, 6, Filter.ComparisonScope.All); + + + + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/RalZarekTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/RalZarekTest.java index ac0a180bc1..79e352625d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/RalZarekTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/RalZarekTest.java @@ -1,42 +1,42 @@ - -package org.mage.test.cards.planeswalker; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.counters.CounterType; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX - */ -public class RalZarekTest extends CardTestPlayerBase { - - @Test - public void testFirstAbility() { - // +1: Tap target permanent, then untap another target permanent. - // -2: Ral Zarek deals 3 damage to any target. - // -7: Flip five coins. Take an extra turn after this one for each coin that comes up heads. - String ralZarek = "Ral Zarek"; // {2}{U}{R} - addCard(Zone.HAND, playerA, ralZarek); - addCard(Zone.BATTLEFIELD, playerA, "Island", 3); - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ralZarek); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1:", "Silvercoat Lion"); // Ral Zarek +1 - addTarget(playerA, "Mountain"); // Untap the Mountain - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, ralZarek, 1); - assertCounterCount(playerA, ralZarek, CounterType.LOYALTY, 5); - - assertTapped("Mountain", false); - assertTapped("Silvercoat Lion", true); - } -} + +package org.mage.test.cards.planeswalker; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX + */ +public class RalZarekTest extends CardTestPlayerBase { + + @Test + public void testFirstAbility() { + // +1: Tap target permanent, then untap another target permanent. + // -2: Ral Zarek deals 3 damage to any target. + // -7: Flip five coins. Take an extra turn after this one for each coin that comes up heads. + String ralZarek = "Ral Zarek"; // {2}{U}{R} + addCard(Zone.HAND, playerA, ralZarek); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ralZarek); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1:", "Silvercoat Lion"); // Ral Zarek +1 + addTarget(playerA, "Mountain"); // Untap the Mountain + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, ralZarek, 1); + assertCounterCount(playerA, ralZarek, CounterType.LOYALTY, 5); + + assertTapped("Mountain", false); + assertTapped("Silvercoat Lion", true); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/VivienTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/VivienTest.java index b8ffd2e2d1..dccf3a7ee1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/VivienTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/VivienTest.java @@ -1,149 +1,149 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.mage.test.cards.planeswalker; - -import mage.abilities.keyword.TrampleAbility; -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.counters.CounterType; -import org.junit.Test; -import org.mage.test.player.TestPlayer; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * @author LevelX2 - */ -public class VivienTest extends CardTestPlayerBase { - - @Test - public void testVivienArkbowRangerAbility1NoTargets() { - setStrictChooseMode(true); - // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. - // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. - // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. - addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters - addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger"); - addTargetAmount(playerA, TestPlayer.TARGET_SKIP); // stop choosing (not targets) - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1); - assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5); - - } - - @Test - public void testVivienArkbowRangerAbilityOnePossibleTargetWithOne() { - setStrictChooseMode(true); - // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. - // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. - // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. - addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters - addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger"); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute"); - addTargetAmount(playerA, "Silvercoat Lion", 1); - addTargetAmount(playerA, TestPlayer.TARGET_SKIP); // stop choosing (one target) - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1); - assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5); - - assertPowerToughness(playerB, "Silvercoat Lion", 2 + 1, 2 + 1); - } - - @Test - public void testVivienArkbowRangerAbilityOnePossibleTargetWithTwo() { - setStrictChooseMode(true); - // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. - // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. - // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. - addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters - addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger"); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute"); - addTargetAmount(playerA, "Silvercoat Lion", 2); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1); - assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5); - - assertPowerToughness(playerB, "Silvercoat Lion", 2 + 2, 2 + 2); - } - - @Test - public void testVivienArkbowRangerAbility1OneOwnPossibleTarget() { - // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. - // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. - // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. - addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters - addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger"); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute"); - addTargetAmount(playerA, "Silvercoat Lion", 2); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1); - assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5); - - assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); - - } - - @Test - public void testVivienArkbowRangerAbility1TwoOwnPossibleTarget() { - // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. - // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. - // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. - addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters - addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger"); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute"); - addTargetAmount(playerA, "Silvercoat Lion", 1); - addTargetAmount(playerA, "Pillarfield Ox", 1); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1); - assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5); - - assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); - assertPowerToughness(playerA, "Pillarfield Ox", 3, 5); - assertAbility(playerA, "Silvercoat Lion", TrampleAbility.getInstance(), true); - assertAbility(playerA, "Pillarfield Ox", TrampleAbility.getInstance(), true); - - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.planeswalker; + +import mage.abilities.keyword.TrampleAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author LevelX2 + */ +public class VivienTest extends CardTestPlayerBase { + + @Test + public void testVivienArkbowRangerAbility1NoTargets() { + setStrictChooseMode(true); + // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. + // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. + // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. + addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger"); + addTargetAmount(playerA, TestPlayer.TARGET_SKIP); // stop choosing (not targets) + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1); + assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5); + + } + + @Test + public void testVivienArkbowRangerAbilityOnePossibleTargetWithOne() { + setStrictChooseMode(true); + // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. + // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. + // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. + addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute"); + addTargetAmount(playerA, "Silvercoat Lion", 1); + addTargetAmount(playerA, TestPlayer.TARGET_SKIP); // stop choosing (one target) + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1); + assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5); + + assertPowerToughness(playerB, "Silvercoat Lion", 2 + 1, 2 + 1); + } + + @Test + public void testVivienArkbowRangerAbilityOnePossibleTargetWithTwo() { + setStrictChooseMode(true); + // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. + // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. + // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. + addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute"); + addTargetAmount(playerA, "Silvercoat Lion", 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1); + assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5); + + assertPowerToughness(playerB, "Silvercoat Lion", 2 + 2, 2 + 2); + } + + @Test + public void testVivienArkbowRangerAbility1OneOwnPossibleTarget() { + // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. + // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. + // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. + addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute"); + addTargetAmount(playerA, "Silvercoat Lion", 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1); + assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5); + + assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); + + } + + @Test + public void testVivienArkbowRangerAbility1TwoOwnPossibleTarget() { + // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. + // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. + // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. + addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute"); + addTargetAmount(playerA, "Silvercoat Lion", 1); + addTargetAmount(playerA, "Pillarfield Ox", 1); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1); + assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5); + + assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); + assertPowerToughness(playerA, "Pillarfield Ox", 3, 5); + assertAbility(playerA, "Silvercoat Lion", TrampleAbility.getInstance(), true); + assertAbility(playerA, "Pillarfield Ox", TrampleAbility.getInstance(), true); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DamageEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DamageEffectsTest.java index ea2975c8af..d6a9b1226f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DamageEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DamageEffectsTest.java @@ -1,118 +1,118 @@ - -package org.mage.test.cards.replacement; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Ignore; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class DamageEffectsTest extends CardTestPlayerBase { - - /** - * Just encountered another bug. With Wurmcoil Engine out and with a - * Gratuitous Violence on the field, I only gained 6 life on blocking rather - * than 12 life. - */ - @Test - public void testDamageIsDoubledWithLifelink() { - // Landfall - Whenever a land enters the battlefield under your control, you may have target player lose 3 life. - // If you do, put three +1/+1 counters on Ob Nixilis, the Fallen. - addCard(Zone.BATTLEFIELD, playerB, "Ob Nixilis, the Fallen"); - addCard(Zone.HAND, playerB, "Mountain"); - - // Deathtouch, lifelink - // When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink. - addCard(Zone.BATTLEFIELD, playerA, "Wurmcoil Engine"); - // If a creature you control would deal damage to a creature or player, it deals double that damage to that creature or player instead. - addCard(Zone.BATTLEFIELD, playerA, "Gratuitous Violence"); - - playLand(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Mountain"); - setChoice(playerB, "Yes"); - addTarget(playerB, playerA); - - attack(2, playerB, "Ob Nixilis, the Fallen"); - block(2, playerA, "Wurmcoil Engine", "Ob Nixilis, the Fallen"); - - setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertGraveyardCount(playerB, "Ob Nixilis, the Fallen", 1); - - assertGraveyardCount(playerA, "Wurmcoil Engine", 1); - assertPermanentCount(playerA, "Wurm", 2); - - assertLife(playerB, 20); - assertLife(playerA, 29); // -2 from Ob Nixilis + 12 from double damage with lifelink from Wurmcoil Engine - - } - - @Test - public void testDamageToPlayer() { - // Deathtouch, lifelink - // When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink. - addCard(Zone.BATTLEFIELD, playerA, "Wurmcoil Engine"); - // If a creature you control would deal damage to a creature or player, it deals double that damage to that creature or player instead. - addCard(Zone.BATTLEFIELD, playerA, "Gratuitous Violence"); - - attack(1, playerA, "Wurmcoil Engine"); - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertPermanentCount(playerA, "Wurmcoil Engine", 1); - - assertLife(playerB, 8); - assertLife(playerA, 32); - - } - - @Ignore - @Test - public void vexingDevilFurnaceRathRedirectToPlaneswalker() { - - /* - Vexing Devil {R} - Creature — Devil - When Vexing Devil enters the battlefield, any opponent may have it deal 4 damage to them. If a player does, sacrifice Vexing Devil. - */ - String vDevil = "Vexing Devil"; - - /* - Nissa, Worldwaker {3}{G}{G} - Planeswalker — Nissa - +1: Target land you control becomes a 4/4 Elemental creature with trample. It's still a land. - +1: Untap up to four target Forests. - −7: Search your library for any number of basic land cards, put them onto the battlefield, then shuffle your library. Those lands become 4/4 Elemental creatures with trample. They're still lands. - */ - String nissa = "Nissa, Worldwaker"; - - /* - Furnace of Rath {1}{R}{R}{R} - Enchantment - If a source would deal damage to a creature or player, it deals double that damage to that creature or player instead. - */ - addCard(Zone.BATTLEFIELD, playerB, "Furnace of Rath"); - addCard(Zone.HAND, playerB, vDevil); - addCard(Zone.HAND, playerA, nissa); - addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); - addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nissa); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, vDevil); - setChoice(playerA, "Yes"); // deal 8 damage to playerA and sac vexing devil (8 due to furnace) - setChoice(playerB, "Yes"); // redirect to planeswalker - addTarget(playerB, nissa); - - setStopAt(2, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerB, vDevil, 1); - assertLife(playerA, 20); - assertGraveyardCount(playerA, nissa, 1); - } -} + +package org.mage.test.cards.replacement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class DamageEffectsTest extends CardTestPlayerBase { + + /** + * Just encountered another bug. With Wurmcoil Engine out and with a + * Gratuitous Violence on the field, I only gained 6 life on blocking rather + * than 12 life. + */ + @Test + public void testDamageIsDoubledWithLifelink() { + // Landfall - Whenever a land enters the battlefield under your control, you may have target player lose 3 life. + // If you do, put three +1/+1 counters on Ob Nixilis, the Fallen. + addCard(Zone.BATTLEFIELD, playerB, "Ob Nixilis, the Fallen"); + addCard(Zone.HAND, playerB, "Mountain"); + + // Deathtouch, lifelink + // When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink. + addCard(Zone.BATTLEFIELD, playerA, "Wurmcoil Engine"); + // If a creature you control would deal damage to a creature or player, it deals double that damage to that creature or player instead. + addCard(Zone.BATTLEFIELD, playerA, "Gratuitous Violence"); + + playLand(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Mountain"); + setChoice(playerB, "Yes"); + addTarget(playerB, playerA); + + attack(2, playerB, "Ob Nixilis, the Fallen"); + block(2, playerA, "Wurmcoil Engine", "Ob Nixilis, the Fallen"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerB, "Ob Nixilis, the Fallen", 1); + + assertGraveyardCount(playerA, "Wurmcoil Engine", 1); + assertPermanentCount(playerA, "Wurm", 2); + + assertLife(playerB, 20); + assertLife(playerA, 29); // -2 from Ob Nixilis + 12 from double damage with lifelink from Wurmcoil Engine + + } + + @Test + public void testDamageToPlayer() { + // Deathtouch, lifelink + // When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink. + addCard(Zone.BATTLEFIELD, playerA, "Wurmcoil Engine"); + // If a creature you control would deal damage to a creature or player, it deals double that damage to that creature or player instead. + addCard(Zone.BATTLEFIELD, playerA, "Gratuitous Violence"); + + attack(1, playerA, "Wurmcoil Engine"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Wurmcoil Engine", 1); + + assertLife(playerB, 8); + assertLife(playerA, 32); + + } + + @Ignore + @Test + public void vexingDevilFurnaceRathRedirectToPlaneswalker() { + + /* + Vexing Devil {R} + Creature — Devil + When Vexing Devil enters the battlefield, any opponent may have it deal 4 damage to them. If a player does, sacrifice Vexing Devil. + */ + String vDevil = "Vexing Devil"; + + /* + Nissa, Worldwaker {3}{G}{G} + Planeswalker — Nissa + +1: Target land you control becomes a 4/4 Elemental creature with trample. It's still a land. + +1: Untap up to four target Forests. + −7: Search your library for any number of basic land cards, put them onto the battlefield, then shuffle your library. Those lands become 4/4 Elemental creatures with trample. They're still lands. + */ + String nissa = "Nissa, Worldwaker"; + + /* + Furnace of Rath {1}{R}{R}{R} + Enchantment + If a source would deal damage to a creature or player, it deals double that damage to that creature or player instead. + */ + addCard(Zone.BATTLEFIELD, playerB, "Furnace of Rath"); + addCard(Zone.HAND, playerB, vDevil); + addCard(Zone.HAND, playerA, nissa); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nissa); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, vDevil); + setChoice(playerA, "Yes"); // deal 8 damage to playerA and sac vexing devil (8 due to furnace) + setChoice(playerB, "Yes"); // redirect to planeswalker + addTarget(playerB, nissa); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, vDevil, 1); + assertLife(playerA, 20); + assertGraveyardCount(playerA, nissa, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/GrindstoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/GrindstoneTest.java index db664d3bf5..604df87eeb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/GrindstoneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/GrindstoneTest.java @@ -8,7 +8,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class GrindstoneTest extends CardTestPlayerBase { @@ -34,7 +33,7 @@ public class GrindstoneTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); setChoice(playerA, "Blue"); - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player puts the top two cards of their library into their graveyard. If both cards share a color, repeat this process."); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player mills"); addTarget(playerA, playerB); setStopAt(1, PhaseStep.END_TURN); @@ -65,7 +64,7 @@ public class GrindstoneTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); setChoice(playerA, "Blue"); - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player puts the top two cards of their library into their graveyard. If both cards share a color, repeat this process."); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player mills"); addTarget(playerA, playerB); setStopAt(1, PhaseStep.END_TURN); @@ -98,7 +97,7 @@ public class GrindstoneTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); setChoice(playerA, "Blue"); - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player puts the top two cards of their library into their graveyard. If both cards share a color, repeat this process."); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player mills"); addTarget(playerA, playerB); setStopAt(1, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java index b35f8571c0..627877016a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java @@ -33,7 +33,7 @@ public class LeylineOfTheVoidTest extends CardTestPlayerBase { // {X}, {T}: Target opponent puts cards from the top of their library into their graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice Helm of Obedience and put that card onto the battlefield under your control. X can't be 0. addCard(Zone.BATTLEFIELD, playerA, "Helm of Obedience"); - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{X}, {T}: Target opponent puts cards", playerB); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{X}, {T}: Target opponent mills", playerB); setChoice(playerA, "X=1"); setStopAt(1, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ManaReflectionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ManaReflectionTest.java index f947ba0afd..7ff304ebdf 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ManaReflectionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ManaReflectionTest.java @@ -1,57 +1,83 @@ -package org.mage.test.cards.replacement; - -import mage.constants.ManaType; -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.counters.CounterType; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -public class ManaReflectionTest extends CardTestPlayerBase { - - @Test - public void generatesCorrectManaFromMarwyn() { - addCard(Zone.BATTLEFIELD, playerA, "Mana Reflection"); - addCard(Zone.BATTLEFIELD, playerA, "Marwyn, the Nurturer"); - addCard(Zone.BATTLEFIELD, playerA, "Upwelling"); // Prevent mana from emptying before we can check it - - addCounters(1, PhaseStep.UPKEEP, playerA, "Marwyn, the Nurturer", CounterType.P1P1, 2); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add"); - - setStopAt(1, PhaseStep.PRECOMBAT_MAIN); - execute(); - - assertPowerToughness(playerA, "Marwyn, the Nurturer", 3, 3); - assertManaPool(playerA, ManaType.GREEN, 6); - } - - @Test - public void generatesCorrectManaFromGemstoneCaverns() { - addCard(Zone.BATTLEFIELD, playerA, "Mana Reflection"); - addCard(Zone.BATTLEFIELD, playerA, "Gemstone Caverns"); - addCard(Zone.BATTLEFIELD, playerA, "Upwelling"); // Prevent mana from emptying before we can check it - - addCounters(1, PhaseStep.UPKEEP, playerA, "Gemstone Caverns", CounterType.LUCK, 1); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add"); - setChoice(playerA, "Green"); - - setStopAt(1, PhaseStep.PRECOMBAT_MAIN); - execute(); - - assertManaPool(playerA, ManaType.GREEN, 2); - } - - @Test - public void generatesCorrectManaFromLlanowarElves() { - addCard(Zone.BATTLEFIELD, playerA, "Mana Reflection"); - addCard(Zone.BATTLEFIELD, playerA, "Llanowar Elves"); - addCard(Zone.BATTLEFIELD, playerA, "Upwelling"); // Prevent mana from emptying before we can check it - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add"); - - setStopAt(1, PhaseStep.PRECOMBAT_MAIN); - execute(); - - assertManaPool(playerA, ManaType.GREEN, 2); - } -} +package org.mage.test.cards.replacement; + +import mage.abilities.mana.ManaOptions; +import mage.constants.ManaType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.utils.ManaOptionsTestUtils.assertDuplicatedManaOptions; +import static org.mage.test.utils.ManaOptionsTestUtils.assertManaOptions; + +public class ManaReflectionTest extends CardTestPlayerBase { + + @Test + public void generatesCorrectManaFromMarwyn() { + addCard(Zone.BATTLEFIELD, playerA, "Mana Reflection"); + addCard(Zone.BATTLEFIELD, playerA, "Marwyn, the Nurturer"); + addCard(Zone.BATTLEFIELD, playerA, "Upwelling"); // Prevent mana from emptying before we can check it + + addCounters(1, PhaseStep.UPKEEP, playerA, "Marwyn, the Nurturer", CounterType.P1P1, 2); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add"); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, "Marwyn, the Nurturer", 3, 3); + assertManaPool(playerA, ManaType.GREEN, 6); + } + + @Test + public void generatesCorrectManaFromGemstoneCaverns() { + addCard(Zone.BATTLEFIELD, playerA, "Mana Reflection"); + addCard(Zone.BATTLEFIELD, playerA, "Gemstone Caverns"); + addCard(Zone.BATTLEFIELD, playerA, "Upwelling"); // Prevent mana from emptying before we can check it + + addCounters(1, PhaseStep.UPKEEP, playerA, "Gemstone Caverns", CounterType.LUCK, 1); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add"); + setChoice(playerA, "Green"); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertManaPool(playerA, ManaType.GREEN, 2); + } + + @Test + public void generatesCorrectManaFromLlanowarElves() { + addCard(Zone.BATTLEFIELD, playerA, "Mana Reflection"); + addCard(Zone.BATTLEFIELD, playerA, "Llanowar Elves"); + addCard(Zone.BATTLEFIELD, playerA, "Upwelling"); // Prevent mana from emptying before we can check it + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add"); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertManaPool(playerA, ManaType.GREEN, 2); + } + + @Test + public void ManaReflectionWithGoblinClearcutterTest() { + // If you tap a permanent for mana, it produces twice as much of that mana instead. + addCard(Zone.BATTLEFIELD, playerA, "Mana Reflection"); + // {T}, Sacrifice a Forest: Add three mana in any combination of {R} and/or {G}. + addCard(Zone.BATTLEFIELD, playerA, "Goblin Clearcutter"); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + assertDuplicatedManaOptions(manaOptions); + + Assert.assertEquals("mana variations don't fit", 4, manaOptions.size()); + assertManaOptions("{R}{R}{R}{R}{R}{R}{G}{G}", manaOptions); + assertManaOptions("{R}{R}{R}{R}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{R}{R}{G}{G}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{G}{G}{G}{G}{G}{G}{G}{G}", manaOptions); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PanharmoniconTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PanharmoniconTest.java index 33fd49746a..8d36c92cf4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PanharmoniconTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PanharmoniconTest.java @@ -1,103 +1,103 @@ - -package org.mage.test.cards.replacement; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class PanharmoniconTest extends CardTestPlayerBase { - - /** - * Check that Panharmonicon adds EtB triggers correctly. - * - */ - @Test - public void testAddsTrigger() { - // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. - addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); - // Whenever another creature enters the battlefield, you gain 1 life. - addCard(Zone.HAND, playerA, "Soul Warden"); - // When Devout Monk enters the battlefield, you gain 1 life. - addCard(Zone.HAND, playerA, "Devout Monk"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Warden"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Devout Monk"); // Life: 20 + 2*1 + 2*1 = 24 - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertLife(playerA, 24); - } - - /** - * Check that Panharmonicon doesn't add to opponents' triggers. - * - */ - @Test - public void testDoesntAddOpponentsTriggers() { - // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. - addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); - addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); - // Whenever another creature enters the battlefield, you gain 1 life. - addCard(Zone.HAND, playerB, "Soul Warden"); - // When Devout Monk enters the battlefield, you gain 1 life. - addCard(Zone.HAND, playerB, "Devout Monk"); - - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Soul Warden"); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Devout Monk"); // Life: 20 + 1 + 1 = 22 - - setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertLife(playerB, 22); - } - - /** - * Check that Panharmonicon doesn't add to lands triggers. - * - */ - @Test - public void testDoesntAddLandsTriggers() { - // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. - addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); - // When Radiant Fountain enters the battlefield, you gain 2 life. - addCard(Zone.HAND, playerA, "Radiant Fountain"); - - playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Radiant Fountain"); // Life: 20 + 2 = 22 - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertLife(playerA, 22); - } - - /** - * Check that Panharmonicon doesn't add to non-permanents triggers. - * - */ - @Test - public void testDoesntAddNonPermanentsTriggers() { - // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. - addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); - // When a Dragon enters the battlefield, you may return Bladewing's Thrall from your graveyard to the battlefield. - addCard(Zone.GRAVEYARD, playerA, "Bladewing's Thrall"); - // A 4/4 vanilla dragon - addCard(Zone.HAND, playerA, "Scion of Ugin"); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scion of Ugin"); - setChoice(playerA, "No"); // Return Bladewing's Thrall from your graveyard to the battlefield? - setChoice(playerA, "Yes"); // Should not get run since there is only one trigger. - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertGraveyardCount(playerA, "Bladewing's Thrall", 1); - } -} + +package org.mage.test.cards.replacement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class PanharmoniconTest extends CardTestPlayerBase { + + /** + * Check that Panharmonicon adds EtB triggers correctly. + * + */ + @Test + public void testAddsTrigger() { + // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. + addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // Whenever another creature enters the battlefield, you gain 1 life. + addCard(Zone.HAND, playerA, "Soul Warden"); + // When Devout Monk enters the battlefield, you gain 1 life. + addCard(Zone.HAND, playerA, "Devout Monk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Warden"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Devout Monk"); // Life: 20 + 2*1 + 2*1 = 24 + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 24); + } + + /** + * Check that Panharmonicon doesn't add to opponents' triggers. + * + */ + @Test + public void testDoesntAddOpponentsTriggers() { + // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. + addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + // Whenever another creature enters the battlefield, you gain 1 life. + addCard(Zone.HAND, playerB, "Soul Warden"); + // When Devout Monk enters the battlefield, you gain 1 life. + addCard(Zone.HAND, playerB, "Devout Monk"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Soul Warden"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Devout Monk"); // Life: 20 + 1 + 1 = 22 + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 22); + } + + /** + * Check that Panharmonicon doesn't add to lands triggers. + * + */ + @Test + public void testDoesntAddLandsTriggers() { + // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. + addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); + // When Radiant Fountain enters the battlefield, you gain 2 life. + addCard(Zone.HAND, playerA, "Radiant Fountain"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Radiant Fountain"); // Life: 20 + 2 = 22 + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 22); + } + + /** + * Check that Panharmonicon doesn't add to non-permanents triggers. + * + */ + @Test + public void testDoesntAddNonPermanentsTriggers() { + // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. + addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); + // When a Dragon enters the battlefield, you may return Bladewing's Thrall from your graveyard to the battlefield. + addCard(Zone.GRAVEYARD, playerA, "Bladewing's Thrall"); + // A 4/4 vanilla dragon + addCard(Zone.HAND, playerA, "Scion of Ugin"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scion of Ugin"); + setChoice(playerA, "No"); // Return Bladewing's Thrall from your graveyard to the battlefield? + setChoice(playerA, "Yes"); // Should not get run since there is only one trigger. + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Bladewing's Thrall", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/RegenerateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/RegenerateTest.java index d02ac24547..fe310095d5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/RegenerateTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/RegenerateTest.java @@ -1,43 +1,370 @@ - package org.mage.test.cards.replacement; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; +import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * - * @author LevelX2 + * @author LevelX2, JayDi85 */ public class RegenerateTest extends CardTestPlayerBase { @Test - public void testRegenerateAbilityGainedByEnchantment() { + public void test_GainedByEnchantment() { addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus"); addCard(Zone.BATTLEFIELD, playerB, "Sagu Archer"); addCard(Zone.HAND, playerB, "Molting Snakeskin"); addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Molting Snakeskin", "Sagu Archer"); attack(2, playerB, "Sagu Archer"); block(2, playerA, "Underworld Cerberus", "Sagu Archer"); activateAbility(2, PhaseStep.DECLARE_BLOCKERS, playerB, "{2}{B}: Regenerate {this}."); + setStrictChooseMode(true); setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); - // attacker has to regenerat because of damage - + // attacker has to regenerate because of damage + assertPermanentCount(playerA, "Underworld Cerberus", 1); assertPermanentCount(playerB, "Sagu Archer", 1); assertPermanentCount(playerB, "Molting Snakeskin", 1); + } + @Test + public void test_Source_Normal() { + // {B}: Regenerate Drudge Skeletons. (The next time this creature would be destroyed this turn, it isn’t. + // Instead tap it, remove all damage from it, and remove it from combat.) + addCard(Zone.BATTLEFIELD, playerA, "Drudge Skeletons", 1); // 1/1 + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}: Regenerate"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}: Regenerate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // damage 1 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Drudge Skeletons"); + setChoice(playerA, "Drudge Skeletons"); // two replacement effects + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Drudge Skeletons", 1); + + // damage 2 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Drudge Skeletons"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Drudge Skeletons", 1); + + // damage 3 - die + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Drudge Skeletons"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Drudge Skeletons", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Source_Blinked() { + // {B}: Regenerate Drudge Skeletons. (The next time this creature would be destroyed this turn, it isn’t. + // Instead tap it, remove all damage from it, and remove it from combat.) + addCard(Zone.BATTLEFIELD, playerA, "Drudge Skeletons", 1); // 1/1 + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + // Exile target creature you control, then return that card to the battlefield under your control. + addCard(Zone.HAND, playerA, "Cloudshift", 1); // {W} instant + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}: Regenerate"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}: Regenerate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // blink and reset regens + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", "Drudge Skeletons"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive blinked", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Drudge Skeletons", 1); + + // damage - die (no regens) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Drudge Skeletons"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Drudge Skeletons", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Target_Normal() { + // {G}, {T}, Discard a card: Regenerate target creature. + addCard(Zone.BATTLEFIELD, playerA, "Rushwood Herbalist", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.HAND, playerA, "Forest", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}, {T}, Discard", "Grizzly Bears"); + setChoice(playerA, "Forest"); // discard cost + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}, {T}, Discard", "Grizzly Bears"); + setChoice(playerA, "Forest"); // discard cost + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // damage 1 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + setChoice(playerA, "Rushwood Herbalist"); // two replacement effects + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage 2 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage 3 - die + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Target_Blinked() { + // {G}, {T}, Discard a card: Regenerate target creature. + addCard(Zone.BATTLEFIELD, playerA, "Rushwood Herbalist", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.HAND, playerA, "Forest", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + // Exile target creature you control, then return that card to the battlefield under your control. + addCard(Zone.HAND, playerA, "Cloudshift", 1); // {W} instant + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}, {T}, Discard", "Grizzly Bears"); + setChoice(playerA, "Forest"); // discard cost + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}, {T}, Discard", "Grizzly Bears"); + setChoice(playerA, "Forest"); // discard cost + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // blink and reset regens + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive blinked", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage - die (no regens) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Attached_Normal() { + // Enchant creature (Target a creature as you cast this. This card enters the battlefield attached to that creature.) + // {G}: Regenerate enchanted creature. (The next time that creature would be destroyed this turn, it isn’t. + // Instead tap it, remove all damage from it, and remove it from combat.) + addCard(Zone.HAND, playerA, "Regeneration", 1); // {1}{G} aura + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 + 2); // cast + 2 activates + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // attach + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Regeneration", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // damage 1 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + setChoice(playerA, "Regeneration"); // two replacement effects + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage 2 - regen + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage 3 - die + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Attached_Blinked() { + // Enchant creature (Target a creature as you cast this. This card enters the battlefield attached to that creature.) + // {G}: Regenerate enchanted creature. (The next time that creature would be destroyed this turn, it isn’t. + // Instead tap it, remove all damage from it, and remove it from combat.) + addCard(Zone.HAND, playerA, "Regeneration", 1); // {1}{G} aura + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 + 2); // cast + 2 activates + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + // Exile target creature you control, then return that card to the battlefield under your control. + addCard(Zone.HAND, playerA, "Cloudshift", 1); // {W} instant + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // attach + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Regeneration", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // blink and reset regens + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive blinked", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage - die (no regens) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Attached_Moved() { + // regen shields must be accumulated in permanent, not aura + + // Enchant creature (Target a creature as you cast this. This card enters the battlefield attached to that creature.) + // {G}: Regenerate enchanted creature. (The next time that creature would be destroyed this turn, it isn’t. + // Instead tap it, remove all damage from it, and remove it from combat.) + addCard(Zone.HAND, playerA, "Regeneration", 1); // {1}{G} aura + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 + 2); // cast + 2 activates + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 permanent 1 + addCard(Zone.BATTLEFIELD, playerA, "Kitesail Corsair", 1); // 2/1 permanent 2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + // Attach target Aura you control to target creature. + addCard(Zone.HAND, playerA, "Aura Finesse", 1); // {U} instant + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + // attach to first + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Regeneration", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + // add multiple regen shields + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{G}: Regenerate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // re-attach to second + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aura Finesse"); + addTarget(playerA, "Regeneration"); // aura to move + addTarget(playerA, "Kitesail Corsair"); // attach to permanent 2 + + // damage to one - have regens + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + setChoice(playerA, "Regeneration"); // two replacement effects + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("alive", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // damage to second -- die (no regens) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Kitesail Corsair"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitesail Corsair", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Attached_Sacrifice() { + // if you sacrifice aura to enable regen then it must works + + // Enchant creature + // Enchanted creature gets +0/+2. + // Sacrifice Carapace: Regenerate enchanted creature. + addCard(Zone.HAND, playerA, "Carapace", 1); // {G} aura + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + // attach + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Carapace", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPT("boosted", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 2, 2 + 2); + + // try to kill (boost lost after regen activate, so need only 1 bolt) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + // activate regen before kill + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sacrifice", TestPlayer.NO_TARGET, "Cast Lightning Bolt", StackClause.WHILE_ON_STACK); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // permanent must be alive + checkGraveyardCount("aura sacrificed", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Carapace", 1); + checkPermanentCount("permanent alive", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ScorchingLavaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ScorchingLavaTest.java index 76aee236c9..3a5e909e70 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ScorchingLavaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ScorchingLavaTest.java @@ -1,37 +1,37 @@ - -package org.mage.test.cards.replacement; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class ScorchingLavaTest extends CardTestPlayerBase { - - /** - * Tests that spells don't work for opponents but still work for controller - */ - @Test - public void testTargetGetExiled() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - // Kicker {R} - // Scorching Lava deals 2 damage to any target. If Scorching Lava was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead. - addCard(Zone.HAND, playerA, "Scorching Lava"); // Instant {1}{R} - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scorching Lava", "Silvercoat Lion"); - setChoice(playerA, "Yes"); // with Kicker - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Scorching Lava", 1); - assertExileCount(playerB, "Silvercoat Lion", 1); - } - -} + +package org.mage.test.cards.replacement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class ScorchingLavaTest extends CardTestPlayerBase { + + /** + * Tests that spells don't work for opponents but still work for controller + */ + @Test + public void testTargetGetExiled() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Kicker {R} + // Scorching Lava deals 2 damage to any target. If Scorching Lava was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead. + addCard(Zone.HAND, playerA, "Scorching Lava"); // Instant {1}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scorching Lava", "Silvercoat Lion"); + setChoice(playerA, "Yes"); // with Kicker + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Scorching Lava", 1); + assertExileCount(playerB, "Silvercoat Lion", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/EntersTheBattlefieldTappedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/EntersTheBattlefieldTappedTest.java index ddd643e27f..d74cabb6d8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/EntersTheBattlefieldTappedTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/EntersTheBattlefieldTappedTest.java @@ -1,66 +1,66 @@ -package org.mage.test.cards.replacement.entersBattlefield; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class EntersTheBattlefieldTappedTest extends CardTestPlayerBase { - - /** - * Creatures that enter the battlefield tapped, like Dread Wanderer, if you - * bring them back from graveyard to the battlefield they enter untapped!! - */ - @Test - public void testTappedFromHand() { - - // Dread Wanderer enters the battlefield tapped. - // {2}{B}: Return Dread Wanderer from your graveyard to the battlefield. - // Activate this ability only any time you could cast a sorcery and only if you have one or fewer cards in hand. - addCard(Zone.HAND, playerA, "Dread Wanderer"); // Creature {B} - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dread Wanderer"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Dread Wanderer", 1); - assertTapped("Dread Wanderer", true); - } - - @Test - public void testTappedFromGraveyard() { - - // Dread Wanderer enters the battlefield tapped. - // {2}{B}: Return Dread Wanderer from your graveyard to the battlefield. - // Activate this ability only any time you could cast a sorcery and only if you have one or fewer cards in hand. - addCard(Zone.GRAVEYARD, playerA, "Dread Wanderer"); // Creature {B} - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{B}: Return "); - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Dread Wanderer", 1); - assertTapped("Dread Wanderer", true); - } - - @Test - public void testScryLandEntersTapped() { - // Temple of Enlightenment enters the battlefield tapped. - // When Temple of Enlightenment enters the battlefield, scry 1. (Look at the top card of your library. You may put that card on the bottom of your library.) - addCard(Zone.HAND, playerA, "Temple of Enlightenment"); // Land - - playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Temple of Enlightenment"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Temple of Enlightenment", 1); - assertTapped("Temple of Enlightenment", true); - } - -} +package org.mage.test.cards.replacement.entersBattlefield; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class EntersTheBattlefieldTappedTest extends CardTestPlayerBase { + + /** + * Creatures that enter the battlefield tapped, like Dread Wanderer, if you + * bring them back from graveyard to the battlefield they enter untapped!! + */ + @Test + public void testTappedFromHand() { + + // Dread Wanderer enters the battlefield tapped. + // {2}{B}: Return Dread Wanderer from your graveyard to the battlefield. + // Activate this ability only any time you could cast a sorcery and only if you have one or fewer cards in hand. + addCard(Zone.HAND, playerA, "Dread Wanderer"); // Creature {B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dread Wanderer"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Dread Wanderer", 1); + assertTapped("Dread Wanderer", true); + } + + @Test + public void testTappedFromGraveyard() { + + // Dread Wanderer enters the battlefield tapped. + // {2}{B}: Return Dread Wanderer from your graveyard to the battlefield. + // Activate this ability only any time you could cast a sorcery and only if you have one or fewer cards in hand. + addCard(Zone.GRAVEYARD, playerA, "Dread Wanderer"); // Creature {B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{B}: Return "); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Dread Wanderer", 1); + assertTapped("Dread Wanderer", true); + } + + @Test + public void testScryLandEntersTapped() { + // Temple of Enlightenment enters the battlefield tapped. + // When Temple of Enlightenment enters the battlefield, scry 1. (Look at the top card of your library. You may put that card on the bottom of your library.) + addCard(Zone.HAND, playerA, "Temple of Enlightenment"); // Land + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Temple of Enlightenment"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Temple of Enlightenment", 1); + assertTapped("Temple of Enlightenment", true); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/HardenedScaleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/HardenedScaleTest.java index f58d501a83..1a98c55f13 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/HardenedScaleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/HardenedScaleTest.java @@ -1,116 +1,116 @@ - -package org.mage.test.cards.replacement.entersBattlefield; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.counters.CounterType; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class HardenedScaleTest extends CardTestPlayerBase { - - /* - Reported bug: Hangarback interaciton with Hardened Scales and Metallic Mimic on board is incorrect. - */ - @Test - public void hangarBackHardenedScalesMetallicMimicTest() { - - /* - Hangarback Walker {X}{X} - Artifact Creature — Construct 0/0 - Hangarback Walker enters the battlefield with X +1/+1 counters on it. - When Hangarback Walker dies, create a 1/1 colorless Thopter artifact creature token with flying for each +1/+1 counter on Hangarback Walker. - {1}, {T}: Put a +1/+1 counter on Hangarback Walker. - */ - String hWalker = "Hangarback Walker"; - - /* - Hardened Scales {G} - Enchantment - If one or more +1/+1 counters would be placed on a creature you control, that many plus one +1/+1 counters are placed on it instead. - */ - String hScales = "Hardened Scales"; - - /* - Metallic Mimic {2} - Artifact Creature — Shapeshifter 2/1 - As Metallic Mimic enters the battlefield, choose a creature type. - Metallic Mimic is the chosen type in addition to its other types. - Each other creature you control of the chosen type enters the battlefield with an additional +1/+1 counter on it. - */ - String mMimic = "Metallic Mimic"; - - addCard(Zone.BATTLEFIELD, playerA, hScales); - addCard(Zone.HAND, playerA, mMimic); - addCard(Zone.HAND, playerA, hWalker); - addCard(Zone.BATTLEFIELD, playerA, "Wastes", 4); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mMimic); - setChoice(playerA, "Construct"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hWalker); - setChoice(playerA, "X=1"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, mMimic, 1); - assertPermanentCount(playerA, hWalker, 1); - assertCounterCount(playerA, hWalker, CounterType.P1P1, 3); - assertPowerToughness(playerA, hWalker, 3, 3); - } - - @Test - public void testWithVigorMortis() { - - /* - Vigor Mortis {2}{B}{B} - Sorcery - Return target creature card from your graveyard to the battlefield. If {G} was spent to cast Vigor Mortis, - that creature enters the battlefield with an additional +1/+1 counter on it. - */ - String vMortis = "Vigor Mortis"; - - /* - Hardened Scales {G} - Enchantment - If one or more +1/+1 counters would be placed on a creature you control, that many plus one +1/+1 counters are placed on it instead. - */ - String hScales = "Hardened Scales"; - - /* - Metallic Mimic {2} - Artifact Creature — Shapeshifter 2/1 - As Metallic Mimic enters the battlefield, choose a creature type. - Metallic Mimic is the chosen type in addition to its other types. - Each other creature you control of the chosen type enters the battlefield with an additional +1/+1 counter on it. - */ - String mMimic = "Metallic Mimic"; - - addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); - addCard(Zone.BATTLEFIELD, playerA, hScales); - addCard(Zone.HAND, playerA, mMimic); - addCard(Zone.HAND, playerA, vMortis); - addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mMimic); - setChoice(playerA, "Cat"); - - castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, vMortis, "Silvercoat Lion"); - - setStopAt(3, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, mMimic, 1); - - assertPermanentCount(playerA, "Silvercoat Lion", 1); - assertCounterCount(playerA, "Silvercoat Lion", CounterType.P1P1, 3); - assertPowerToughness(playerA, "Silvercoat Lion", 5, 5); // Hardened Scales is only once applied to EntersTheBattlefield event - assertGraveyardCount(playerA, vMortis, 1); - } -} + +package org.mage.test.cards.replacement.entersBattlefield; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class HardenedScaleTest extends CardTestPlayerBase { + + /* + Reported bug: Hangarback interaciton with Hardened Scales and Metallic Mimic on board is incorrect. + */ + @Test + public void hangarBackHardenedScalesMetallicMimicTest() { + + /* + Hangarback Walker {X}{X} + Artifact Creature — Construct 0/0 + Hangarback Walker enters the battlefield with X +1/+1 counters on it. + When Hangarback Walker dies, create a 1/1 colorless Thopter artifact creature token with flying for each +1/+1 counter on Hangarback Walker. + {1}, {T}: Put a +1/+1 counter on Hangarback Walker. + */ + String hWalker = "Hangarback Walker"; + + /* + Hardened Scales {G} + Enchantment + If one or more +1/+1 counters would be placed on a creature you control, that many plus one +1/+1 counters are placed on it instead. + */ + String hScales = "Hardened Scales"; + + /* + Metallic Mimic {2} + Artifact Creature — Shapeshifter 2/1 + As Metallic Mimic enters the battlefield, choose a creature type. + Metallic Mimic is the chosen type in addition to its other types. + Each other creature you control of the chosen type enters the battlefield with an additional +1/+1 counter on it. + */ + String mMimic = "Metallic Mimic"; + + addCard(Zone.BATTLEFIELD, playerA, hScales); + addCard(Zone.HAND, playerA, mMimic); + addCard(Zone.HAND, playerA, hWalker); + addCard(Zone.BATTLEFIELD, playerA, "Wastes", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mMimic); + setChoice(playerA, "Construct"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hWalker); + setChoice(playerA, "X=1"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, mMimic, 1); + assertPermanentCount(playerA, hWalker, 1); + assertCounterCount(playerA, hWalker, CounterType.P1P1, 3); + assertPowerToughness(playerA, hWalker, 3, 3); + } + + @Test + public void testWithVigorMortis() { + + /* + Vigor Mortis {2}{B}{B} + Sorcery + Return target creature card from your graveyard to the battlefield. If {G} was spent to cast Vigor Mortis, + that creature enters the battlefield with an additional +1/+1 counter on it. + */ + String vMortis = "Vigor Mortis"; + + /* + Hardened Scales {G} + Enchantment + If one or more +1/+1 counters would be placed on a creature you control, that many plus one +1/+1 counters are placed on it instead. + */ + String hScales = "Hardened Scales"; + + /* + Metallic Mimic {2} + Artifact Creature — Shapeshifter 2/1 + As Metallic Mimic enters the battlefield, choose a creature type. + Metallic Mimic is the chosen type in addition to its other types. + Each other creature you control of the chosen type enters the battlefield with an additional +1/+1 counter on it. + */ + String mMimic = "Metallic Mimic"; + + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerA, hScales); + addCard(Zone.HAND, playerA, mMimic); + addCard(Zone.HAND, playerA, vMortis); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mMimic); + setChoice(playerA, "Cat"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, vMortis, "Silvercoat Lion"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, mMimic, 1); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertCounterCount(playerA, "Silvercoat Lion", CounterType.P1P1, 3); + assertPowerToughness(playerA, "Silvercoat Lion", 5, 5); // Hardened Scales is only once applied to EntersTheBattlefield event + assertGraveyardCount(playerA, vMortis, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/UnbreathingHordeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/UnbreathingHordeTest.java index f2e551db8f..5bdeebda90 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/UnbreathingHordeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/UnbreathingHordeTest.java @@ -1,39 +1,39 @@ - -package org.mage.test.cards.replacement.entersBattlefield; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class UnbreathingHordeTest extends CardTestPlayerBase { - - /** - * That the +1/+1 counters are added to Living Lore before state based - * actions take place - */ - @Test - public void testCountersAdded() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); - // Unbreathing Horde enters the battlefield with a +1/+1 counter on it for each other Zombie you control and each Zombie card in your graveyard. - // If Unbreathing Horde would be dealt damage, prevent that damage and remove a +1/+1 counter from it. - addCard(Zone.HAND, playerA, "Unbreathing Horde"); //{2}{B} - 0/0 - - addCard(Zone.BATTLEFIELD, playerA, "Dross Crocodile", 2); - addCard(Zone.BATTLEFIELD, playerB, "Dross Crocodile", 2); - addCard(Zone.GRAVEYARD, playerA, "Dross Crocodile", 2); - addCard(Zone.GRAVEYARD, playerB, "Dross Crocodile", 2); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unbreathing Horde"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Unbreathing Horde", 1); - assertPowerToughness(playerA, "Unbreathing Horde", 4, 4); - } - -} + +package org.mage.test.cards.replacement.entersBattlefield; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class UnbreathingHordeTest extends CardTestPlayerBase { + + /** + * That the +1/+1 counters are added to Living Lore before state based + * actions take place + */ + @Test + public void testCountersAdded() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + // Unbreathing Horde enters the battlefield with a +1/+1 counter on it for each other Zombie you control and each Zombie card in your graveyard. + // If Unbreathing Horde would be dealt damage, prevent that damage and remove a +1/+1 counter from it. + addCard(Zone.HAND, playerA, "Unbreathing Horde"); //{2}{B} - 0/0 + + addCard(Zone.BATTLEFIELD, playerA, "Dross Crocodile", 2); + addCard(Zone.BATTLEFIELD, playerB, "Dross Crocodile", 2); + addCard(Zone.GRAVEYARD, playerA, "Dross Crocodile", 2); + addCard(Zone.GRAVEYARD, playerB, "Dross Crocodile", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unbreathing Horde"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Unbreathing Horde", 1); + assertPowerToughness(playerA, "Unbreathing Horde", 4, 4); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/DeflectingPalmTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/DeflectingPalmTest.java index ff6fe1e86a..8f58df1b51 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/DeflectingPalmTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/DeflectingPalmTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.replacement.prevent; import mage.constants.PhaseStep; @@ -49,7 +48,7 @@ public class DeflectingPalmTest extends CardTestPlayerBase { */ @Test public void testPreventDamageWithDromokasCommand() { - + setStrictChooseMode(true); // Choose two - // - Prevent all damage target instant or sorcery spell would deal this turn; // - or Target player sacrifices an enchantment; @@ -68,15 +67,21 @@ public class DeflectingPalmTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Deflecting Palm"); setChoice(playerB, "Silvercoat Lion"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dromoka's Command", "Deflecting Palm"); - addTarget(playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dromoka's Command", null, "Deflecting Palm"); + setModeChoice(playerA, "1"); + addTarget(playerA, "Deflecting Palm"); setModeChoice(playerA, "3"); + addTarget(playerA, "Silvercoat Lion"); attack(1, playerA, "Silvercoat Lion"); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerB, "Deflecting Palm", 1); assertGraveyardCount(playerA, "Dromoka's Command", 1); @@ -87,4 +92,40 @@ public class DeflectingPalmTest extends CardTestPlayerBase { } + /** + * Test that prevented damage will be created with the correct source and + * will trigger the ability of Satyr Firedance + * https://github.com/magefree/mage/issues/804 + */ + @Test + public void testDamageInPlayer() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Plains"); + // The next time a source of your choice would deal damage to you this turn, prevent that damage. + // If damage is prevented this way, Deflecting Palm deals that much damage to that source's controller. + addCard(Zone.HAND, playerA, "Deflecting Palm"); + // Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals + // that much damage to target creature that player controls. + addCard(Zone.BATTLEFIELD, playerA, "Satyr Firedancer"); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + addCard(Zone.HAND, playerB, "Lightning Bolt"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Deflecting Palm", null, "Lightning Bolt"); + setChoice(playerA, "Lightning Bolt"); + addTarget(playerA, "Silvercoat Lion"); // target for Satyr Firedancer + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Deflecting Palm", 1); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java new file mode 100644 index 0000000000..c7e67c0c05 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java @@ -0,0 +1,60 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.replacement.redirect; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class PalisadeGiantTest extends CardTestPlayerBase { + + /** + * when you have two Palisade Giants in play and your opponent deals you + * damage, the damage is applied to both of them rather than allowing you to + * choose which replacement effect applies (as it should). I experienced + * this multiple times in games vs the A.I. + */ + @Test + public void testRedirectDamage() { + + // All damage that would be dealt to you or another permanent you control is dealt to Palisade Giant instead. + addCard(Zone.BATTLEFIELD, playerA, "Palisade Giant", 2); // Creature 2/7 + + addCard(Zone.HAND, playerB, "Lightning Bolt"); // Instant {R} + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + + setChoice(playerA, "Palisade Giant"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Lightning Bolt", 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + int damage = 0; + for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(playerA.getId())) { + if (permanent.getName().equals("Palisade Giant")) { + damage += permanent.getDamage(); + } + } + Assert.assertEquals("Only 3 damage in sum should be dealt to the Palisade Giants", 3, damage); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BecomeBlockTriggersAITest.java b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BecomeBlockTriggersAITest.java new file mode 100644 index 0000000000..3012f38419 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BecomeBlockTriggersAITest.java @@ -0,0 +1,93 @@ +package org.mage.test.cards.requirement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * @author JayDi85 + */ +public class BecomeBlockTriggersAITest extends CardTestPlayerBaseWithAIHelps { + + @Test + public void test_Manual_AutoBlock() { + removeAllCardsFromHand(playerA); + removeAllCardsFromHand(playerB); + + // All creatures able to block Nessian Boar do so. + // Whenever Nessian Boar becomes blocked by a creature, that creature’s controller draws a card. + addCard(Zone.BATTLEFIELD, playerA, "Nessian Boar", 1); + // + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + + // auto-block by requirement effect + attack(1, playerA, "Nessian Boar"); + //block(1, playerB, "Balduvian Bears", "Nessian Boar"); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, 0); + assertGraveyardCount(playerB, 1); + assertHandCount(playerA, 0); + assertHandCount(playerB, 1); + } + + @Test + public void test_Manual_CantBlockAgain() { + removeAllCardsFromHand(playerA); + removeAllCardsFromHand(playerB); + + // All creatures able to block Nessian Boar do so. + // Whenever Nessian Boar becomes blocked by a creature, that creature’s controller draws a card. + addCard(Zone.BATTLEFIELD, playerA, "Nessian Boar", 1); + // + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + + // auto-block by requirement effect + attack(1, playerA, "Nessian Boar"); + // try to block manually, but it must raise error + block(1, playerB, "Balduvian Bears", "Nessian Boar"); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + try { + execute(); + Assert.fail("Expected exception, but not raise"); + } catch (UnsupportedOperationException ue) { + Assert.assertEquals("Balduvian Bears cannot block Nessian Boar it is already blocking the maximum amount of creatures.", ue.getMessage()); + } + //assertAllCommandsUsed(); // must have 1 missing command (block) + } + + @Test + public void test_AI_CantBlockAgain() { + removeAllCardsFromHand(playerA); + removeAllCardsFromHand(playerB); + + // All creatures able to block Nessian Boar do so. + // Whenever Nessian Boar becomes blocked by a creature, that creature’s controller draws a card. + addCard(Zone.BATTLEFIELD, playerA, "Nessian Boar", 1); + // + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + + // auto-block by requirement effect + attack(1, playerA, "Nessian Boar"); + // AI can't block same creature twice + aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, 0); + assertGraveyardCount(playerB, 1); + assertHandCount(playerA, 0); + assertHandCount(playerB, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BecomeBlockTriggersMonteCarloAITest.java b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BecomeBlockTriggersMonteCarloAITest.java new file mode 100644 index 0000000000..f84378c9f4 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BecomeBlockTriggersMonteCarloAITest.java @@ -0,0 +1,42 @@ +package org.mage.test.cards.requirement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithMonteCarloAIHelps; + +/** + * @author JayDi85 + */ +public class BecomeBlockTriggersMonteCarloAITest extends CardTestPlayerBaseWithMonteCarloAIHelps { + + // continue from BecomeBlockTriggersTest + @Test + public void test_AI_CantBlockAgain() { + // Monte Carlo bug: Triggered ability triggered twice (should be once), see https://github.com/magefree/mage/issues/6367 + + removeAllCardsFromHand(playerA); + removeAllCardsFromHand(playerB); + + // All creatures able to block Nessian Boar do so. + // Whenever Nessian Boar becomes blocked by a creature, that creature’s controller draws a card. + addCard(Zone.BATTLEFIELD, playerA, "Nessian Boar", 1); + // + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + + // auto-block by requirement effect + attack(1, playerA, "Nessian Boar"); + // AI can't block same creature twice + aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, 0); + assertGraveyardCount(playerB, 1); + assertHandCount(playerA, 0); + assertHandCount(playerB, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java index 8cdd2f194b..ee778d4e57 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.requirement; import mage.constants.PhaseStep; @@ -10,7 +9,6 @@ import static junit.framework.TestCase.assertEquals; import static org.junit.Assert.fail; /** - * * @author LevelX2, icetc */ public class BlockRequirementTest extends CardTestPlayerBase { @@ -91,7 +89,7 @@ public class BlockRequirementTest extends CardTestPlayerBase { /** * Elemental Uprising - "it must be blocked this turn if able", not working - * + *

* The bug just happened for me today as well - the problem is "must be * blocked" is not being enforced correctly. During opponent's main phase * they cast Elemental Uprising targeting an untapped land. They attacked @@ -186,7 +184,7 @@ public class BlockRequirementTest extends CardTestPlayerBase { attack(1, playerA, "Breaker of Armies"); // not allowed due to Breaker of Armies having menace - block(1, playerB, "Hill Giant", "Breaker of Armies"); + //block(1, playerB, "Hill Giant", "Breaker of Armies"); // auto-block from requrement effect must add blocker without command setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); @@ -197,7 +195,7 @@ public class BlockRequirementTest extends CardTestPlayerBase { assertEquals("Breaker of Armies is blocked by 1 creature(s). It has to be blocked by 2 or more.", e.getMessage()); } } - + /* Reported bug: Slayer's Cleaver did not force Wretched Gryff (an eldrazi) to block */ @@ -225,7 +223,7 @@ public class BlockRequirementTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Dimensional Infiltrator", 1); assertGraveyardCount(playerB, "Llanowar Elves", 1); } - + /* Reported bug: Challenger Troll on field not enforcing block restrictions */ @@ -237,30 +235,30 @@ public class BlockRequirementTest extends CardTestPlayerBase { Each creature you control with power 4 or greater can’t be blocked by more than one creature. */ String cTroll = "Challenger Troll"; - + String bSable = "Bronze Sable"; // {2} 2/1 String hGiant = "Hill Giant"; // {3}{R} 3/3 - + addCard(Zone.BATTLEFIELD, playerA, cTroll); addCard(Zone.BATTLEFIELD, playerB, bSable); addCard(Zone.BATTLEFIELD, playerB, hGiant); - + attack(1, playerA, cTroll); // only 1 should be able to block it since Troll >=4 power block restriction block(1, playerB, bSable, cTroll); block(1, playerB, hGiant, cTroll); - + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - + try { execute(); fail("Expected exception not thrown"); } catch (UnsupportedOperationException e) { assertEquals("Challenger Troll is blocked by 2 creature(s). It can only be blocked by 1 or less.", e.getMessage()); - } + } } - + /* Reported bug: Challenger Troll on field not enforcing block restrictions */ @@ -273,29 +271,29 @@ public class BlockRequirementTest extends CardTestPlayerBase { */ String cTroll = "Challenger Troll"; String bHulk = "Bloom Hulk"; // {3}{G} 4/4 ETB: proliferate - + String bSable = "Bronze Sable"; // {2} 2/1 String hGiant = "Hill Giant"; // {3}{R} 3/3 - + addCard(Zone.BATTLEFIELD, playerA, cTroll); addCard(Zone.BATTLEFIELD, playerA, bHulk); addCard(Zone.BATTLEFIELD, playerB, bSable); addCard(Zone.BATTLEFIELD, playerB, hGiant); - + attack(1, playerA, cTroll); attack(1, playerA, bHulk); // only 1 should be able to block Bloom Hulk since >=4 power and Troll on field block(1, playerB, bSable, bHulk); block(1, playerB, hGiant, bHulk); - + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - + try { execute(); fail("Expected exception not thrown"); } catch (UnsupportedOperationException e) { assertEquals("Bloom Hulk is blocked by 2 creature(s). It can only be blocked by 1 or less.", e.getMessage()); - } + } } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantUseActivatedAbilitiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantUseActivatedAbilitiesTest.java new file mode 100644 index 0000000000..456d12c811 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantUseActivatedAbilitiesTest.java @@ -0,0 +1,57 @@ +package org.mage.test.cards.restriction; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class CantUseActivatedAbilitiesTest extends CardTestPlayerBase { + + /** + * I can activate artifacts despite my opponent having Collector Ouphe or + * Karn, the Great Creator in play. The artifact says it can't be activated, but that's a lie. + */ + @Test + public void testCantActivateManaAbility() { + // Activated abilities of artifacts can't be activated. + addCard(Zone.HAND, playerA, "Collector Ouphe"); // Creature {1}{G} 2/2 + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + // {T}: Add {C}{C} + addCard(Zone.BATTLEFIELD, playerB, "Sol Ring"); // Artifact + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Collector Ouphe"); + + setStopAt(1, PhaseStep.END_COMBAT); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + // Sol Ring can't produce mana + Assert.assertTrue("PlayerB may not be able to produce any mana but he he can produce " + playerB.getManaAvailable(currentGame).toString(), playerB.getManaAvailable(currentGame).toString().equals("[]")); + } + + @Test + public void testCantActivateActivatedAbility() { + // Activated abilities of artifacts can't be activated. + addCard(Zone.BATTLEFIELD, playerA, "Collector Ouphe"); // Creature {1}{G} 2/2 + + // {1}: Adarkar Sentinel gets +0/+1 until end of turn. + addCard(Zone.BATTLEFIELD, playerB, "Adarkar Sentinel"); // Artifact Creature — Soldier (3/3) + addCard(Zone.BATTLEFIELD, playerB, "Island"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}: "); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertPowerToughness(playerB, "Adarkar Sentinel", 3, 3); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/restriction/MeddlingMageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/MeddlingMageTest.java new file mode 100644 index 0000000000..3830a9c40e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/MeddlingMageTest.java @@ -0,0 +1,195 @@ +package org.mage.test.cards.restriction; + +import mage.constants.EmptyNames; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class MeddlingMageTest extends CardTestPlayerBase { + + //As Meddling Mage enters the battlefield, choose a nonland card name. Spells with the chosen name can't be cast. + + @Test + public void testMeddlingMageDefaultScenario() { + addCard(Zone.HAND, playerA, "Meddling Mage"); + addCard(Zone.HAND, playerA, "Savannah Lions"); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Savannah Lions", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage"); + setChoice(playerA, "Savannah Lions"); // name a spell that can't be cast + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Savannah Lions", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Meddling Mage", 1); + assertPermanentCount(playerA, "Savannah Lions", 0); + assertHandCount(playerA, "Meddling Mage", 0); + assertHandCount(playerA, "Savannah Lions", 1); + } + + @Test + public void testMeddlingMageIsochronScepterScenario() { + addCard(Zone.HAND, playerA, "Meddling Mage"); + addCard(Zone.HAND, playerA, "Isochron Scepter"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage"); + setChoice(playerA, "Lightning Bolt"); // name a spell that can't be cast + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Isochron Scepter"); + setChoice(playerA, "Yes"); // use imprint + setChoice(playerA, "Lightning Bolt"); // target for imprint + + // copy and cast imprinted card + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{2}, {T}:"); + setChoice(playerA, "Yes"); // create copy + setChoice(playerA, "Yes"); // cast copy + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Meddling Mage", 1); + assertPermanentCount(playerA, "Isochron Scepter", 1); + assertExileCount("Lightning Bolt", 1); + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + + @Test + public void testMeddlingMageFaceDownCreature() { + addCard(Zone.HAND, playerA, "Meddling Mage"); + addCard(Zone.HAND, playerA, "Ainok Tracker"); // red morph creature to prevent it casting from Islands and Plains + + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage"); + setChoice(playerA, "Ainok Tracker"); // name a spell that can't be cast + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ainok Tracker"); + setChoice(playerA, "Yes"); // cast it face down as 2/2 creature + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Meddling Mage", 1); + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + assertHandCount(playerA, "Meddling Mage", 0); + assertHandCount(playerA, "Ainok Tracker", 0); + } + + @Test + public void testMeddlingMageFuseCardStopAndCastWell() { + addCard(Zone.HAND, playerA, "Meddling Mage"); + + // Create a 3/3 green Centaur creature token. + // You gain 2 life for each creature you control. + addCard(Zone.HAND, playerA, "Alive // Well"); // {3}{G} // {W} + + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage"); + setChoice(playerA, "Well"); // name a spell that can't be cast + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Alive", true); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Well", false); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Alive // Well", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + + assertPermanentCount(playerA, "Meddling Mage", 1); + assertHandCount(playerA, "Meddling Mage", 0); + assertHandCount(playerA, "Alive // Well", 1); + assertLife(playerA, 20); + + } + + @Test + public void testMeddlingMageFuseCardStopAliveAndCastWell() { + addCard(Zone.HAND, playerA, "Meddling Mage"); + + // Create a 3/3 green Centaur creature token. + // You gain 2 life for each creature you control. + addCard(Zone.HAND, playerA, "Alive // Well"); // {3}{G} // {W} + + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage"); + setChoice(playerA, "Alive"); // name a spell that can't be cast + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Alive", false); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Well", true); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Alive // Well", false); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Well"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Meddling Mage", 1); + assertHandCount(playerA, "Meddling Mage", 0); + assertHandCount(playerA, "Alive // Well", 0); + assertLife(playerA, 22); + } + + @Test + public void testMeddlingMageFuseCardStopAliveAndCastFused() { + addCard(Zone.HAND, playerA, "Meddling Mage"); + + // Create a 3/3 green Centaur creature token. + // You gain 2 life for each creature you control. + addCard(Zone.HAND, playerA, "Alive // Well"); // {3}{G} // {W} + + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage"); + setChoice(playerA, "Alive"); // name a spell that can't be cast + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Alive", false); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Well", true); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Alive // Well", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Meddling Mage", 1); + assertHandCount(playerA, "Meddling Mage", 0); + + assertHandCount(playerA, "Alive // Well", 1); + assertLife(playerA, 20); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/CantDrawTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/CantDrawTest.java index 212acbc5aa..a5e73c2361 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/rules/CantDrawTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/CantDrawTest.java @@ -1,61 +1,61 @@ - -package org.mage.test.cards.rules; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class CantDrawTest extends CardTestPlayerBase { - - /** - * Leovold, Emissary of Trest does not stop Sylvan Library from drawing - * extra cards. - */ - @Test - public void testCardsNotDrawnFromLibraryEffect() { - // Each opponent can't draw more than one card each turn. - // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. - addCard(Zone.BATTLEFIELD, playerB, "Leovold, Emissary of Trest"); - - // At the beginning of your draw step, you may draw two additional cards. - // If you do, choose two cards in your hand drawn this turn. For each of those cards, pay 4 life or put the card on top of your library. - addCard(Zone.BATTLEFIELD, playerA, "Sylvan Library", 1); - - setStopAt(3, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, 0); - - assertHandCount(playerA, 1); // only one card drawn at start of turn 3 - assertLife(playerA, 16); // pay 4 life for the card dawn normally (no other cards drawn) - - } - - @Test - public void testCardsNotDrawnFromLibraryEffectNoUse() { - // Each opponent can't draw more than one card each turn. - // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. - addCard(Zone.BATTLEFIELD, playerB, "Leovold, Emissary of Trest"); - - // At the beginning of your draw step, you may draw two additional cards. - // If you do, choose two cards in your hand drawn this turn. For each of those cards, pay 4 life or put the card on top of your library. - addCard(Zone.BATTLEFIELD, playerA, "Sylvan Library", 1); - - setChoice(playerA, "No"); - - setStopAt(3, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, 0); - - assertHandCount(playerA, 1); // only one card drawn at start of turn 3 - assertLife(playerA, 20); // not used no damage - - } - -} + +package org.mage.test.cards.rules; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class CantDrawTest extends CardTestPlayerBase { + + /** + * Leovold, Emissary of Trest does not stop Sylvan Library from drawing + * extra cards. + */ + @Test + public void testCardsNotDrawnFromLibraryEffect() { + // Each opponent can't draw more than one card each turn. + // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. + addCard(Zone.BATTLEFIELD, playerB, "Leovold, Emissary of Trest"); + + // At the beginning of your draw step, you may draw two additional cards. + // If you do, choose two cards in your hand drawn this turn. For each of those cards, pay 4 life or put the card on top of your library. + addCard(Zone.BATTLEFIELD, playerA, "Sylvan Library", 1); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, 0); + + assertHandCount(playerA, 1); // only one card drawn at start of turn 3 + assertLife(playerA, 16); // pay 4 life for the card dawn normally (no other cards drawn) + + } + + @Test + public void testCardsNotDrawnFromLibraryEffectNoUse() { + // Each opponent can't draw more than one card each turn. + // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. + addCard(Zone.BATTLEFIELD, playerB, "Leovold, Emissary of Trest"); + + // At the beginning of your draw step, you may draw two additional cards. + // If you do, choose two cards in your hand drawn this turn. For each of those cards, pay 4 life or put the card on top of your library. + addCard(Zone.BATTLEFIELD, playerA, "Sylvan Library", 1); + + setChoice(playerA, "No"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, 0); + + assertHandCount(playerA, 1); // only one card drawn at start of turn 3 + assertLife(playerA, 20); // not used no damage + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/MeliraSylvokOutcastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/MeliraSylvokOutcastTest.java index 9edbd27587..4a24591d52 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/rules/MeliraSylvokOutcastTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/MeliraSylvokOutcastTest.java @@ -1,47 +1,47 @@ - -package org.mage.test.cards.rules; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.counters.CounterType; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -/** - * with Melira, Sylvok Outcast on the table and Devoted Druid you can activated - * his untap ability for infinity mana. This shouldn't work like this as its an - * unpayable cost " 601.2g. The player pays the total cost in any order. Partial - * payments are not allowed. Unpayable costs cant be paid" - */ -public class MeliraSylvokOutcastTest extends CardTestPlayerBase { - - /** - * Test that the target of Vines of Vastwood can't be the target of spells - * or abilities your opponents control this turn - */ - @Test - public void testUnpayableCost() { - // You can't get poison counters. - // Creatures you control can't have -1/-1 counters placed on them. - // Creatures your opponents control lose infect. - addCard(Zone.BATTLEFIELD, playerA, "Melira, Sylvok Outcast", 2); // 2/2 - // {T}: Add {G}. - // Put a -1/-1 counter on Devoted Druid: Untap Devoted Druid. - addCard(Zone.BATTLEFIELD, playerA, "Devoted Druid", 1); // 0/2 - - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Put a -1/-1 counter on "); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPowerToughness(playerA, "Devoted Druid", 0, 2); - assertCounterCount("Devoted Druid", CounterType.M1M1, 0); - assertTapped("Devoted Druid", true); // Because untapping can't be paid - - } -} + +package org.mage.test.cards.rules; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +/** + * with Melira, Sylvok Outcast on the table and Devoted Druid you can activated + * his untap ability for infinity mana. This shouldn't work like this as its an + * unpayable cost " 601.2g. The player pays the total cost in any order. Partial + * payments are not allowed. Unpayable costs cant be paid" + */ +public class MeliraSylvokOutcastTest extends CardTestPlayerBase { + + /** + * Test that the target of Vines of Vastwood can't be the target of spells + * or abilities your opponents control this turn + */ + @Test + public void testUnpayableCost() { + // You can't get poison counters. + // Creatures you control can't have -1/-1 counters placed on them. + // Creatures your opponents control lose infect. + addCard(Zone.BATTLEFIELD, playerA, "Melira, Sylvok Outcast", 2); // 2/2 + // {T}: Add {G}. + // Put a -1/-1 counter on Devoted Druid: Untap Devoted Druid. + addCard(Zone.BATTLEFIELD, playerA, "Devoted Druid", 1); // 0/2 + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Put a -1/-1 counter on "); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Devoted Druid", 0, 2); + assertCounterCount("Devoted Druid", CounterType.M1M1, 0); + assertTapped("Devoted Druid", true); // Because untapping can't be paid + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java index 0d8fa4034a..e2866affcc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java @@ -1,44 +1,145 @@ - package org.mage.test.cards.rules; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; +import org.mage.test.serverside.base.CardTestMultiPlayerBase; /** * * @author LevelX2 */ - -public class WorldEnchantmentsRuleTest extends CardTestPlayerBase { +public class WorldEnchantmentsRuleTest extends CardTestMultiPlayerBase { /** - * 704.5m If two or more permanents have the supertype world, all except the one that has had - * the world supertype for the shortest amount of time are put into their owners' graveyards. - * In the event of a tie for the shortest amount of time, all are put into their owners' graveyards. - * This is called the “world rule. - * + * 704.5m If two or more permanents have the supertype world, all except the + * one that has had the world supertype for the shortest amount of time are + * put into their owners' graveyards. In the event of a tie for the shortest + * amount of time, all are put into their owners' graveyards. This is called + * the “world rule. + * */ @Test - public void TestTwoWorldEnchantsments() { + public void TestTwoWorldEnchantments() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); addCard(Zone.HAND, playerA, "Nether Void", 1); addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 7); - addCard(Zone.HAND, playerB, "Nether Void", 1); + + addCard(Zone.BATTLEFIELD, playerD, "Swamp", 7); + addCard(Zone.HAND, playerD, "Nether Void", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nether Void"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); // just needed to get different craete time to second Nether Void - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Nether Void"); - + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Nether Void"); + setStopAt(2, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Nether Void", 0); - assertPermanentCount(playerB, "Nether Void", 1); + assertPermanentCount(playerD, "Nether Void", 1); } - -} \ No newline at end of file + + // 801.12 The "world rule" applies to a permanent only if other world permanents are within its controller's range of influence. + @Test + public void TestTwoWorldEnchantmentsNotInRangeOfInfluence() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerA, "Nether Void", 1); + addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); + + addCard(Zone.BATTLEFIELD, playerC, "Swamp", 7); + addCard(Zone.HAND, playerC, "Nether Void", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nether Void"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); // just needed to get different craete time to second Nether Void + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Nether Void"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Nether Void", 1); + assertPermanentCount(playerC, "Nether Void", 1); + } + + // 704.5 In the event of a tie for the shortest amount of time, all are put into their owners’ graveyards. This is called the “world rule.” + // In this example the execution order of the leaves the battlefield triggers of the two Oblivion Rings decide, which World Enchnatment may stay + // Player order: A -> D -> C -> B + @Test + public void TestTwoWorldEnchantmentsFromTriggers() { + setStrictChooseMode(true); + // When Oblivion Ring enters the battlefield, exile another target nonland permanent. + // When Oblivion Ring leaves the battlefield, return the exiled card to the battlefield under its owner's control. + addCard(Zone.HAND, playerA, "Oblivion Ring", 1); + // All creatures have haste. + addCard(Zone.HAND, playerA, "Concordant Crossroads", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // When Oblivion Ring enters the battlefield, exile another target nonland permanent. + // When Oblivion Ring leaves the battlefield, return the exiled card to the battlefield under its owner's control. + addCard(Zone.HAND, playerD, "Oblivion Ring", 1); // Enchantment {2}{W} + // Destroy all white permanents. + addCard(Zone.HAND, playerD, "Anarchy", 1); // Sorcery {2}{R}{R} + + // All creatures have haste. + addCard(Zone.BATTLEFIELD, playerD, "Concordant Crossroads", 1); // World Enchantment {G} + addCard(Zone.BATTLEFIELD, playerD, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerD, "Mountain", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Oblivion Ring"); + addTarget(playerA, "Concordant Crossroads"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Concordant Crossroads"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Oblivion Ring"); + addTarget(playerD, "Concordant Crossroads"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Anarchy"); // Both World Enchantments return at the same time and go to grave + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerD, "Concordant Crossroads", 0); + assertPermanentCount(playerA, "Concordant Crossroads", 1); + + assertGraveyardCount(playerA, "Oblivion Ring", 1); + assertGraveyardCount(playerA, "Concordant Crossroads", 0); + assertGraveyardCount(playerD, "Oblivion Ring", 1); + assertGraveyardCount(playerD, "Concordant Crossroads", 1); + assertGraveyardCount(playerD, "Anarchy", 1); + } + + @Test + public void TestTwoWorldEnchantmentsWithSameOrder() { + setStrictChooseMode(true); + // When Charmed Griffin enters the battlefield, each other player may put an artifact or enchantment card onto the battlefield from their hand. + addCard(Zone.HAND, playerA, "Charmed Griffin", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + // All creatures have haste. + addCard(Zone.HAND, playerD, "Concordant Crossroads", 1); + addCard(Zone.HAND, playerB, "Concordant Crossroads", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Charmed Griffin"); + + setChoice(playerD, "Yes"); // Put an artifact or enchantment card from your hand onto the battlefield? + setChoice(playerD, "Concordant Crossroads"); + + setChoice(playerB, "Yes"); // Put an artifact or enchantment card from your hand onto the battlefield? + setChoice(playerB, "Concordant Crossroads"); + + concede(1, PhaseStep.PRECOMBAT_MAIN, playerC); // World Enchantments come into range + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Charmed Griffin", 1); + assertPermanentCount(playerD, "Concordant Crossroads", 0); + assertPermanentCount(playerB, "Concordant Crossroads", 0); + + assertGraveyardCount(playerB, "Concordant Crossroads", 1); + assertGraveyardCount(playerD, "Concordant Crossroads", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ExquisiteBloodTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ExquisiteBloodTest.java deleted file mode 100644 index 509fec3e15..0000000000 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ExquisiteBloodTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.mage.test.cards.single; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author noxx - */ -public class ExquisiteBloodTest extends CardTestPlayerBase { - - @Test - public void testCard() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - - // card we test - addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); - - addCard(Zone.HAND, playerA, "Lightning Bolt"); - addCard(Zone.HAND, playerA, "Bump in the Night"); - addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin", 2); - - addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); - addCard(Zone.HAND, playerB, "Shock"); - - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bump in the Night", playerB); - - attack(1, playerA, "Raging Goblin"); - attack(1, playerA, "Raging Goblin"); - - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock", playerA); - - setStopAt(2, PhaseStep.BEGIN_COMBAT); - execute(); - - assertLife(playerB, 12); - assertLife(playerA, 26); - } - -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/NornsAnnexTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/NornsAnnexTest.java deleted file mode 100644 index 0ca9b59bd3..0000000000 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/NornsAnnexTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.mage.test.cards.single; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Ignore; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * Created by glerman on 23/6/15. - * what is this test??? - */ -@Ignore -public class NornsAnnexTest extends CardTestPlayerBase{ - @Test - @Ignore - public void testNornsAnnex() { - addCard(Zone.BATTLEFIELD, playerA, "Norn's Annex"); - addCard(Zone.BATTLEFIELD, playerB, "Brindle Boar"); - attack(2, playerB, "Brindle Boar", playerA); - setStopAt(2, PhaseStep.END_TURN); - execute(); - - - - } -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/AliFromCairoTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/an/AliFromCairoTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/AliFromCairoTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/an/AliFromCairoTest.java index 1bb9787515..47d9f2da46 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/AliFromCairoTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/an/AliFromCairoTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.an; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java index bfbaef65bb..4bbe5029b3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java @@ -255,4 +255,45 @@ public class CavernOfSoulsTest extends CardTestPlayerBase { } + @Test + public void testBouncedCreatureNotCountered() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Forest"); + addCard(Zone.HAND, playerA, "Cavern of Souls"); + addCard(Zone.HAND, playerA, "Runeclaw Bear"); + + addCard(Zone.HAND, playerB, "Counterspell", 2); + addCard(Zone.HAND, playerB, "Unsummon"); + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cavern of Souls"); + setChoice(playerA, "Bear"); + + //wait for next turn, we'll need our next land drop + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Runeclaw Bear"); + + //make sure we used our cavern already and try to counter + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell"); + waitStackResolved(3, PhaseStep.PRECOMBAT_MAIN); + + checkPermanentCount("bear not countered", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Runeclaw Bear", 1); + + //counterspell fizzled, return bear to hand to try countering it again + castSpell(3, PhaseStep.BEGIN_COMBAT, playerB, "Unsummon", "Runeclaw Bear"); + waitStackResolved(3, PhaseStep.BEGIN_COMBAT); + + //recast bear, without cavern of souls conditional mana + playLand(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Forest"); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Runeclaw Bear"); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Counterspell"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Cavern of Souls", 1); + assertGraveyardCount(playerA, "Runeclaw Bear", 1); + assertGraveyardCount(playerB, "Counterspell", 2); + assertGraveyardCount(playerB, "Unsummon", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ChampionOfLambholtTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/ChampionOfLambholtTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ChampionOfLambholtTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/avr/ChampionOfLambholtTest.java index e46d6767b6..ed78013f1f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ChampionOfLambholtTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/ChampionOfLambholtTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.avr; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/ExquisiteBloodTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/ExquisiteBloodTest.java new file mode 100644 index 0000000000..03c762d5ee --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/ExquisiteBloodTest.java @@ -0,0 +1,135 @@ +package org.mage.test.cards.single.avr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author noxx + */ +public class ExquisiteBloodTest extends CardTestPlayerBase { + + @Test + public void BasicCardTest() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + + // Whenever an opponent loses life, you gain that much life. + addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); // Enchantment {4}{B} + + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.HAND, playerA, "Bump in the Night"); + addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, "Shock"); + + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bump in the Night", playerB); + + attack(1, playerA, "Raging Goblin"); + attack(1, playerA, "Raging Goblin"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock", playerA); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerB, 12); + assertLife(playerA, 26); + } + + /** + * Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464 + */ + @Test + public void triggerCascadeTest() { + // +2: You gain 2 life. Put two +1/+1 counters on up to one target creature. + // −3: Exile target creature. Its controller gains 2 life. + // −10: Creatures you control gain flying and double strike until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Ajani, Inspiring Leader", 1); // Planeswalker (5) + + // Flying + // Whenever you gain life, target opponent loses that much life. + addCard(Zone.BATTLEFIELD, playerA, "Defiant Bloodlord", 1); // Creature 4/5 {5}{B}{B} + + // Whenever an opponent loses life, you gain that much life. + addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); // Enchantment {4}{B} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2:", "Defiant Bloodlord"); + addTarget(playerA, playerB); // Target opponent of Defiant Bloodlord triggered ability (looping until opponent is dead) + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Defiant Bloodlord", 6, 7); + assertCounterCount("Ajani, Inspiring Leader", CounterType.LOYALTY, 7); + + assertLife(playerB, 0); // Player B is dead, game ends + assertLife(playerA, 40); + + + } + + /** + * Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464 + */ + @Test + public void triggerCascadeAjaniSecondAbilityTest() { + // +2: You gain 2 life. Put two +1/+1 counters on up to one target creature. + // −3: Exile target creature. Its controller gains 2 life. + // −10: Creatures you control gain flying and double strike until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Ajani, Inspiring Leader", 1); // Planeswalker (5) + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); // Creature 2/2 + + // Flying + // Whenever you gain life, target opponent loses that much life. + addCard(Zone.BATTLEFIELD, playerA, "Defiant Bloodlord", 1); // Creature 4/5 {5}{B}{B} + + // Whenever an opponent loses life, you gain that much life. + addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); // Enchantment {4}{B} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-3:", "Silvercoat Lion"); + addTarget(playerA, playerB); // Target opponent of Defiant Bloodlord triggered ability (looping until opponent is dead) + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + addTarget(playerA, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertExileCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Defiant Bloodlord", 4, 5); + assertCounterCount("Ajani, Inspiring Leader", CounterType.LOYALTY, 2); + + assertLife(playerB, 0); // Player B is dead, game ends + assertLife(playerA, 40); + + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/GiselaBladeOfGoldnightTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/GiselaBladeOfGoldnightTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/GiselaBladeOfGoldnightTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/avr/GiselaBladeOfGoldnightTest.java index 672e6b8d1d..a9227c968c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/GiselaBladeOfGoldnightTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/GiselaBladeOfGoldnightTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.avr; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/HomicidalSeclusionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/HomicidalSeclusionTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/HomicidalSeclusionTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/avr/HomicidalSeclusionTest.java index c94e0b4da5..53c2b84532 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/HomicidalSeclusionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/HomicidalSeclusionTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.avr; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/bfz/ConduitOfRuinTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/bfz/ConduitOfRuinTest.java new file mode 100644 index 0000000000..44aca020c9 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/bfz/ConduitOfRuinTest.java @@ -0,0 +1,51 @@ +package org.mage.test.cards.single.bfz; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class ConduitOfRuinTest extends CardTestPlayerBase { + + @Test + public void testCast() { + setStrictChooseMode(true); + + // Emrakul, the Aeons Torn can't be countered. + // When you cast Emrakul, take an extra turn after this one. + // Flying, protection from colored spells, annihilator 6 + // When Emrakul is put into a graveyard from anywhere, its owner shuffles their graveyard into their library. + addCard(Zone.LIBRARY, playerA, "Emrakul, the Aeons Torn"); // Creature {15} 15/15 + + // When you cast Conduit of Ruin, you may search your library for a colorless creature card with converted mana cost 7 or greater, then shuffle your library and put that card on top of it. + // The first creature spell you cast each turn costs {2} less to cast. + addCard(Zone.HAND, playerA, "Conduit of Ruin"); // Creature {6} 5/5 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 13); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Conduit of Ruin"); + setChoice(playerA, "Yes"); // When you cast this spell, you may search... + addTarget(playerA, "Emrakul, the Aeons Torn"); + + setStopAt(3, PhaseStep.DRAW); + + execute(); + + assertLibraryCount(playerA, "Emrakul, the Aeons Torn", 0); + assertHandCount(playerA, "Emrakul, the Aeons Torn", 1); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Emrakul, the Aeons Torn"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Conduit of Ruin", 1); + assertPermanentCount(playerA, "Emrakul, the Aeons Torn", 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/PatronOfTheNezumiTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/bok/PatronOfTheNezumiTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/PatronOfTheNezumiTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/bok/PatronOfTheNezumiTest.java index 67d0f76e21..6fb6d8d674 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/PatronOfTheNezumiTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/bok/PatronOfTheNezumiTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.bok; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/DivergentTransformationsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c16/DivergentTransformationsTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/DivergentTransformationsTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/c16/DivergentTransformationsTest.java index 87f5169f96..7f03e8ad0f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/DivergentTransformationsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c16/DivergentTransformationsTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.c16; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c17/KessDissidentMageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c17/KessDissidentMageTest.java new file mode 100644 index 0000000000..1278853b2e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c17/KessDissidentMageTest.java @@ -0,0 +1,104 @@ +package org.mage.test.cards.single.c17; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ + +public class KessDissidentMageTest extends CardTestPlayerBase { + + // Kess, Dissident Mage + // During each of your turns, you may cast an instant or sorcery card from your graveyard. + // If a card cast this way would be put into your graveyard this turn, exile it instead. + + @Test + public void test_Simple() { + addCard(Zone.BATTLEFIELD, playerA, "Kess, Dissident Mage", 1); + // + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + checkPlayableAbility("must play simple", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerA, "Lightning Bolt", 1); + assertLife(playerB, 20 - 3); + } + + @Test + public void test_Split_OnePart() { + addCard(Zone.BATTLEFIELD, playerA, "Kess, Dissident Mage", 1); + // + // Create a 3/3 green Centaur creature token. + // You gain 2 life for each creature you control. + addCard(Zone.GRAVEYARD, playerA, "Alive // Well", 1); // {3}{G} // {W} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + checkPlayableAbility("must play part", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Alive", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alive"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Centaur", 1); + assertLife(playerA, 20); + assertExileCount(playerA, "Alive // Well", 1); + } + + @Test + public void test_Split_Check() { + // testing check command only for fused cards + + // Create a 3/3 green Centaur creature token. + // You gain 2 life for each creature you control. + addCard(Zone.HAND, playerA, "Alive // Well", 1); // {3}{G} // {W} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // tap green first for Alive spell + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 4); + checkPlayableAbility("must play fused", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Alive // Well", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Alive // Well"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Centaur", 1); + assertLife(playerA, 20 + 2); + assertGraveyardCount(playerA, "Alive // Well", 1); + } + + @Test + public void test_Split_CantPlay() { + addCard(Zone.BATTLEFIELD, playerA, "Kess, Dissident Mage", 1); + // + // Create a 3/3 green Centaur creature token. + // You gain 2 life for each creature you control. + addCard(Zone.GRAVEYARD, playerA, "Alive // Well", 1); // {3}{G} // {W} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // tap green first for Alive spell + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 4); + checkPlayableAbility("can't play fused from graveyard", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Alive // Well", false); + //castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Alive // Well"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ArchfiendOfSpiteTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c19/ArchfiendOfSpiteTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ArchfiendOfSpiteTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/c19/ArchfiendOfSpiteTest.java index 4037097d6b..44885d8b6d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ArchfiendOfSpiteTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c19/ArchfiendOfSpiteTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.c19; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c20/EonFrolickerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c20/EonFrolickerTest.java new file mode 100644 index 0000000000..f49af39c62 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c20/EonFrolickerTest.java @@ -0,0 +1,46 @@ +package org.mage.test.cards.single.c20; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ + +public class EonFrolickerTest extends CardTestPlayerBase { + + @Test + public void test_EonFrolicker_NormalPlay() { + // https://github.com/magefree/mage/issues/6780 + + // {2}{U}{U} + // When Eon Frolicker enters the battlefield, if you cast it, target opponent takes an extra turn after this one. + // Until your next turn, you and planeswalkers you control gain protection from that player. + // (You and planeswalkers you control can’t be targeted, dealt damage, or enchanted by anything controlled by that player.) + addCard(Zone.HAND, playerA, "Eon Frolicker", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + // + // Chandra’s Fury deals 4 damage to target player or planeswalker and 1 damage to each creature that player or that planeswalker’s controller controls. + addCard(Zone.HAND, playerB, "Chandra's Fury", 1); // {4}{R} + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Eon Frolicker"); + addTarget(playerA, playerB); + + // AI can targets only Eon Frolicker (cause A protected from B) + checkPlayableAbility("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Cast Chandra's Fury", true); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Chandra's Fury"); + //addTarget(playerB, playerB); + + //setStrictChooseMode(true); // AI must choose target for fury (itself) + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Chandra's Fury", 1); + assertLife(playerA, 20); + assertLife(playerB, 20 - 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/cmd/AcornCatapultTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmd/AcornCatapultTest.java new file mode 100644 index 0000000000..ec15bbb2b0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmd/AcornCatapultTest.java @@ -0,0 +1,72 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.single.cmd; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class AcornCatapultTest extends CardTestPlayerBase { + + @Test + public void testAcornCatapultCreature() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + // {1}, {T}: Acorn Catapult deals 1 damage to any target. + // That permanent's controller or that player creates a 1/1 green Squirrel creature token. + addCard(Zone.HAND, playerA, "Acorn Catapult"); //Artifact {4} + + // {1}{B}, {T}: Target player loses 1 life. + addCard(Zone.BATTLEFIELD, playerB, "Acolyte of Xathrid"); // Creature 0/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Acorn Catapult"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}:", "Acolyte of Xathrid"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Acorn Catapult", 1); + + assertGraveyardCount(playerB, "Acolyte of Xathrid", 1); + + assertPermanentCount(playerB, "Squirrel", 1); + } + + @Test + public void testAcornCatapultPlayer() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + // {1}, {T}: Acorn Catapult deals 1 damage to any target. + // That permanent's controller or that player creates a 1/1 green Squirrel creature token. + addCard(Zone.HAND, playerA, "Acorn Catapult"); //Artifact {4} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Acorn Catapult"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}:", playerB); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Acorn Catapult", 1); + + assertLife(playerB, 19); + + assertPermanentCount(playerB, "Squirrel", 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/FlusterstormTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmd/FlusterstormTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/FlusterstormTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/cmd/FlusterstormTest.java index 0271ec68f1..d306f63b02 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/FlusterstormTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmd/FlusterstormTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.cmd; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -6,6 +6,7 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; public class FlusterstormTest extends CardTestPlayerBase { + @Test public void testThatFlusterstormGoesToGraveyard() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/RimescaleDragonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/csp/RimescaleDragonTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/RimescaleDragonTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/csp/RimescaleDragonTest.java index b9f0a8b047..d03b384c2e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/RimescaleDragonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/csp/RimescaleDragonTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.csp; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/MorgueBurstTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dgm/MorgueBurstTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/MorgueBurstTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dgm/MorgueBurstTest.java index 7c19a0f763..1f6eb26701 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/MorgueBurstTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dgm/MorgueBurstTest.java @@ -4,7 +4,7 @@ * and open the template in the editor. */ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dgm; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/CourtHussarTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dis/CourtHussarTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/CourtHussarTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dis/CourtHussarTest.java index 18905ac927..f8e43c0df8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/CourtHussarTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dis/CourtHussarTest.java @@ -1,6 +1,6 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dis; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/MagewrightStoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dis/MagewrightStoneTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/MagewrightStoneTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dis/MagewrightStoneTest.java index 9f931fbd17..5c1d00ecbc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/MagewrightStoneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dis/MagewrightStoneTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dis; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/AlphaBrawlTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/AlphaBrawlTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/AlphaBrawlTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/AlphaBrawlTest.java index 1beb30f7d4..c03c2ce21d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/AlphaBrawlTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/AlphaBrawlTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/AltarOfTheLostTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/AltarOfTheLostTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/AltarOfTheLostTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/AltarOfTheLostTest.java index 7632c68935..d16a357817 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/AltarOfTheLostTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/AltarOfTheLostTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ArchangelsLightTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ArchangelsLightTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ArchangelsLightTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ArchangelsLightTest.java index b668eaa019..573d1ededc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ArchangelsLightTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ArchangelsLightTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ClingingMistsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ClingingMistsTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ClingingMistsTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ClingingMistsTest.java index efa0fef118..36d2ce6564 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ClingingMistsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ClingingMistsTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/CounterlashTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/CounterlashTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/CounterlashTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/CounterlashTest.java index bf65a42577..29aa4b67f1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/CounterlashTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/CounterlashTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/DungeonGeistsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/DungeonGeistsTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/DungeonGeistsTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/DungeonGeistsTest.java index 07b27ebbda..38e3e095d0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/DungeonGeistsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/DungeonGeistsTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ElbrusTheBindingBladeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ElbrusTheBindingBladeTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ElbrusTheBindingBladeTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ElbrusTheBindingBladeTest.java index d814f82d54..f24c7476f4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ElbrusTheBindingBladeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ElbrusTheBindingBladeTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/FaithsShieldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FaithsShieldTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/FaithsShieldTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FaithsShieldTest.java index a1e9e885f0..42af5ed049 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/FaithsShieldTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FaithsShieldTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/FeedThePackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FeedThePackTest.java similarity index 94% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/FeedThePackTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FeedThePackTest.java index f798cdf37e..e04dc44a16 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/FeedThePackTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FeedThePackTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/FiendOfTheShadowsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FiendOfTheShadowsTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/FiendOfTheShadowsTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FiendOfTheShadowsTest.java index 4b9330199d..ec64fa99eb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/FiendOfTheShadowsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FiendOfTheShadowsTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/FlayerOfTheHateboundTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FlayerOfTheHateboundTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/FlayerOfTheHateboundTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FlayerOfTheHateboundTest.java index dcd58f8ba6..6e621f4af9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/FlayerOfTheHateboundTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/FlayerOfTheHateboundTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/GrafdiggersCageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/GrafdiggersCageTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/GrafdiggersCageTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/GrafdiggersCageTest.java index 1f9a01bb50..2b6c3ff9b6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/GrafdiggersCageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/GrafdiggersCageTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/GravecrawlerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/GravecrawlerTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/GravecrawlerTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/GravecrawlerTest.java index 3eb71efe78..11be16db11 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/GravecrawlerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/GravecrawlerTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/HavengulLichTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HavengulLichTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/HavengulLichTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HavengulLichTest.java index ec685693e6..6e2d745fa8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/HavengulLichTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HavengulLichTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/HinterlandScourgeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HinterlandScourgeTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/HinterlandScourgeTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HinterlandScourgeTest.java index bb5b69d360..546dbc3c3e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/HinterlandScourgeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HinterlandScourgeTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/HollowhengeSpiritTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HollowhengeSpiritTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/HollowhengeSpiritTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HollowhengeSpiritTest.java index 3175a3c029..9ed167d4a2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/HollowhengeSpiritTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HollowhengeSpiritTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/HuntmasterOfTheFellsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HuntmasterOfTheFellsTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/HuntmasterOfTheFellsTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HuntmasterOfTheFellsTest.java index c0287193a7..f87d56042d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/HuntmasterOfTheFellsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/HuntmasterOfTheFellsTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ImmerwolfTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ImmerwolfTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ImmerwolfTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ImmerwolfTest.java index 4391096294..4563484229 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ImmerwolfTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ImmerwolfTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/IncreasingCardsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/IncreasingCardsTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/IncreasingCardsTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/IncreasingCardsTest.java index cd75d177c9..8d7b1eed05 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/IncreasingCardsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/IncreasingCardsTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/SeanceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SeanceTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/SeanceTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SeanceTest.java index 0af26956ed..b345f8b3b1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/SeanceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SeanceTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/SecretsOfTheDeadTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SecretsOfTheDeadTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/SecretsOfTheDeadTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SecretsOfTheDeadTest.java index ca66849f49..5d798749ea 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/SecretsOfTheDeadTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SecretsOfTheDeadTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/SightlessGhoulTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SightlessGhoulTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/SightlessGhoulTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SightlessGhoulTest.java index 17aec3c9ec..f7217c8844 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/SightlessGhoulTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SightlessGhoulTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/SorinLordOfInnistradTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SorinLordOfInnistradTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/SorinLordOfInnistradTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SorinLordOfInnistradTest.java index 2cce92b120..43b56c46b0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/SorinLordOfInnistradTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SorinLordOfInnistradTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/SoulSeizerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SoulSeizerTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/SoulSeizerTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SoulSeizerTest.java index 319122efd8..1b904c4f13 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/SoulSeizerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SoulSeizerTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/SuddenDisappearanceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SuddenDisappearanceTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/SuddenDisappearanceTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SuddenDisappearanceTest.java index 9e893a2b64..4f2bb88b1f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/SuddenDisappearanceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/SuddenDisappearanceTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/UnhallowedCatharTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/UnhallowedCatharTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/UnhallowedCatharTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/UnhallowedCatharTest.java index e7252ec6de..9a158d3f9e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/UnhallowedCatharTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/UnhallowedCatharTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/WardenOfTheWallTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/WardenOfTheWallTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/WardenOfTheWallTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/WardenOfTheWallTest.java index a6eea9f04f..0794992ddc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/WardenOfTheWallTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/WardenOfTheWallTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.CardType; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/WerewolfRansackerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/WerewolfRansackerTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/WerewolfRansackerTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/WerewolfRansackerTest.java index 92484919e8..64b705e9b4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/WerewolfRansackerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/WerewolfRansackerTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ZombieApocalypseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ZombieApocalypseTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ZombieApocalypseTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ZombieApocalypseTest.java index 7d94ad6ab9..c6b4459e22 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ZombieApocalypseTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dka/ZombieApocalypseTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dka; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dst/DemonsHornTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dst/DemonsHornTest.java new file mode 100644 index 0000000000..7f41496aa1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dst/DemonsHornTest.java @@ -0,0 +1,77 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.single.dst; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class DemonsHornTest extends CardTestPlayerBase { + + + @Test + public void testWithBlackSpell() { + setStrictChooseMode(true); + + // When Abyssal Gatekeeper dies, each player sacrifices a creature. + addCard(Zone.HAND, playerA, "Abyssal Gatekeeper", 1); // Creature {2}{B} 1/1 + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + // Whenever a player casts a black spell, you may gain 1 life. + addCard(Zone.BATTLEFIELD, playerB, "Demon's Horn", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Abyssal Gatekeeper"); + setChoice(playerB, "Yes"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Abyssal Gatekeeper", 1); + + assertLife(playerA, 20); + assertLife(playerB, 21); + } + + /** + * https://github.com/magefree/mage/issues/6890 + * + * Color == Color Identity #6890 + * + * Alesha, Who Smiles at Death triggers Demon's Horn + */ + @Test + public void testSpellWithBlackManaOnlyInTriggeredOptionalCost() { + setStrictChooseMode(true); + + // First strike + // Whenever Alesha, Who Smiles at Death attacks, you may pay {W/B}{W/B}. If you do, return target creature card with power 2 or less from your graveyard to the battlefield tapped and attacking. + addCard(Zone.HAND, playerA, "Alesha, Who Smiles at Death", 1); // Creature {2}{R} 3/2 + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // Whenever a player casts a black spell, you may gain 1 life. + addCard(Zone.BATTLEFIELD, playerB, "Demon's Horn", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alesha, Who Smiles at Death"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Alesha, Who Smiles at Death", 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/EchoingTruthTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dst/EchoingTruthTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/EchoingTruthTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/dst/EchoingTruthTest.java index 179f966b3b..2c3b67abd4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/EchoingTruthTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dst/EchoingTruthTest.java @@ -1,5 +1,5 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.dst; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/BarteredCowTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/BarteredCowTest.java new file mode 100644 index 0000000000..4e951974c1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/BarteredCowTest.java @@ -0,0 +1,88 @@ +package org.mage.test.cards.single.eld; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BarteredCowTest extends CardTestPlayerBase { + + @Test + public void testDiesTrigger() { + setStrictChooseMode(true); + + // When Bartered Cow dies or when you discard it, create a Food token. + addCard(Zone.BATTLEFIELD, playerA, "Bartered Cow"); // Creature {3}{W} 3/3 + + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", "Bartered Cow"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertGraveyardCount(playerA, "Bartered Cow", 1); + assertPermanentCount(playerA, "Food", 1); + } + + @Test + public void testDiscardTrigger() { + setStrictChooseMode(true); + + // When Bartered Cow dies or when you discard it, create a Food token. + addCard(Zone.HAND, playerA, "Bartered Cow"); // Creature {3}{W} 3/3 + // Choose one — + // • Target player discards a card. + // • Target creature gets +2/-1 until end of turn. + // • Target creature gains swampwalk until end of turn. (It can't be blocked as long as defending player controls a Swamp.) + addCard(Zone.HAND, playerB, "Funeral Charm", 1); // Instant {B} + addCard(Zone.BATTLEFIELD, playerB, "Swamp"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Funeral Charm"); + setModeChoice(playerB, "1"); + addTarget(playerB, playerA); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Funeral Charm", 1); + assertGraveyardCount(playerA, "Bartered Cow", 1); + assertPermanentCount(playerA, "Food", 1); + } + + @Test + public void testDiscardTriggerWithTorturedExistence() { + setStrictChooseMode(true); + + // When Bartered Cow dies or when you discard it, create a Food token. + addCard(Zone.HAND, playerA, "Bartered Cow"); // Creature {3}{W} 3/3 + + // {B}, Discard a creature card: Return target creature card from your graveyard to your hand. + addCard(Zone.BATTLEFIELD, playerA, "Tortured Existence", 1); // Instant {B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}, Discard a creature card"); + setChoice(playerA, "Bartered Cow"); + addTarget(playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Bartered Cow", 1); + assertHandCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerA, "Food", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/SyrGwynHeroOfAshvaleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/SyrGwynHeroOfAshvaleTest.java new file mode 100644 index 0000000000..bfeab03b91 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/SyrGwynHeroOfAshvaleTest.java @@ -0,0 +1,62 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.single.eld; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class SyrGwynHeroOfAshvaleTest extends CardTestPlayerBase { + + @Test + public void equipKnightTest() { + // Equipped creature gets +2/+2 and has trample and lifelink. + addCard(Zone.BATTLEFIELD, playerA, "Behemoth Sledge"); // Artifact - Equipment {1}{G}{W} + + // Vigilance, menace + // Whenever an equipped creature you control attacks, you draw a card and you lose 1 life. + // Equipment you control have equip Knight {0}. + addCard(Zone.BATTLEFIELD, playerA, "Syr Gwyn, Hero of Ashvale"); // Legendary Creature {3}{R}{W}{B} 5/5 Human Knight + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip Knight", "Syr Gwyn, Hero of Ashvale"); + + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Syr Gwyn, Hero of Ashvale", 7, 7); + + } + + @Test + public void equipKnightTestInstantSpeed() { + // Equipped creature gets +2/+2 and has trample and lifelink. + addCard(Zone.BATTLEFIELD, playerA, "Behemoth Sledge"); // Artifact - Equipment {1}{G}{W} + + // You may activate equip abilities any time you could cast an instant. + addCard(Zone.BATTLEFIELD, playerA, "Leonin Shikari", 2); // Creature 2/2 + + // Vigilance, menace + // Whenever an equipped creature you control attacks, you draw a card and you lose 1 life. + // Equipment you control have equip Knight {0}. + addCard(Zone.BATTLEFIELD, playerA, "Syr Gwyn, Hero of Ashvale"); // Legendary Creature {3}{R}{W}{B} 5/5 Human Knight + + activateAbility(1, PhaseStep.DECLARE_ATTACKERS, playerA, "Equip Knight", "Syr Gwyn, Hero of Ashvale"); + + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertPowerToughness(playerA, "Syr Gwyn, Hero of Ashvale", 7, 7); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/SyrKonradTheGrimTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/SyrKonradTheGrimTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/SyrKonradTheGrimTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/eld/SyrKonradTheGrimTest.java index f24543cf40..56c83b7568 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/SyrKonradTheGrimTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/SyrKonradTheGrimTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.eld; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/TorbranThaneOfRedFellTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/TorbranThaneOfRedFellTest.java new file mode 100644 index 0000000000..161b30c8bb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/eld/TorbranThaneOfRedFellTest.java @@ -0,0 +1,114 @@ +package org.mage.test.cards.single.eld; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.GameException; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class TorbranThaneOfRedFellTest extends CardTestPlayerBase { + + @Test + public void basicTest() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // If a red source you control would deal damage to an opponent or a permanent an opponent controls, + // it deals that much damage plus 2 instead. + addCard(Zone.BATTLEFIELD, playerA, "Torbran, Thane of Red Fell"); // Creature 2/4 {1}{R}{R}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + + attack(1, playerA, "Torbran, Thane of Red Fell"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Pillarfield Ox"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertLife(playerB, 11); // damage from: attack 2 + 2 Bolt 3 + 2 + + assertGraveyardCount(playerA, "Lightning Bolt", 2); + assertGraveyardCount(playerB, "Pillarfield Ox", 1); + } + + @Test + public void opponentsTornbanAppliedToOwnPlaneswalkerTest() { + setStrictChooseMode(true); + + // +1: Elementals you control get +2/+0 until end of turn. + // −1: Add {R}{R}. + // −2: Chandra, Novice Pyromancer deals 2 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Chandra, Novice Pyromancer"); // Planeswalker (5) {3}{R} + + // If a red source you control would deal damage to an opponent or a permanent an opponent controls, + // it deals that much damage plus 2 instead. + addCard(Zone.BATTLEFIELD, playerB, "Torbran, Thane of Red Fell"); // Creature 2/4 {1}{R}{R}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Barbarian Horde"); // Creature 3/3 {3}{R} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2:", playerB); + + attack(2, playerB, "Barbarian Horde"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "-2:", "Barbarian Horde"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 15); // Attack from Horde 3+2 + assertLife(playerB, 18); // Damage from planeswalker 2 + + assertPermanentCount(playerB, "Barbarian Horde", 1); + + assertCounterCount("Chandra, Novice Pyromancer", CounterType.LOYALTY, 1); + } + + @Test + public void with3PlayersTest() throws GameException { + playerC = createPlayer(currentGame, playerC, "PlayerC"); + setStrictChooseMode(true); + + // +1: Elementals you control get +2/+0 until end of turn. + // −1: Add {R}{R}. + // −2: Chandra, Novice Pyromancer deals 2 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Chandra, Novice Pyromancer"); // Planeswalker (5) {3}{R} + + // If a red source you control would deal damage to an opponent or a permanent an opponent controls, + // it deals that much damage plus 2 instead. + addCard(Zone.BATTLEFIELD, playerC, "Torbran, Thane of Red Fell"); // Creature 2/4 {1}{R}{R}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Barbarian Horde"); // Creature 3/3 {3}{R} + addCard(Zone.BATTLEFIELD, playerB, "Chandra, Novice Pyromancer"); // Planeswalker (5) {3}{R} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2:", playerB); + + attack(3, playerB, "Barbarian Horde", playerA); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerB, "-2:", playerA); + + activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerA, "-2:", "Barbarian Horde"); + + setStopAt(4, PhaseStep.BEGIN_COMBAT); + + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 15); // Attack from Horde 3+2 + assertLife(playerB, 18); // Damage from planeswalker 2 + + assertPermanentCount(playerB, "Barbarian Horde", 1); + + assertCounterCount(playerA, "Chandra, Novice Pyromancer", CounterType.LOYALTY, 1); + assertCounterCount(playerB, "Chandra, Novice Pyromancer", CounterType.LOYALTY, 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/EmrakulThePromisedEndTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/EmrakulThePromisedEndTest.java index 6ab93d4b73..0a51f5095d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/EmrakulThePromisedEndTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/emn/EmrakulThePromisedEndTest.java @@ -6,14 +6,13 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author Quercitron */ public class EmrakulThePromisedEndTest extends CardTestPlayerBase { // Test that extra turn is added correctly when Emrakul is cast during an opponent's turn. @Test - public void testExtraTurn_Turn2() { + public void test_ExtraTurn_Turn2() { addCard(Zone.BATTLEFIELD, playerB, "Island", 20); // Creature cards you own that aren't on the battlefield have flash. addCard(Zone.HAND, playerB, "Teferi, Mage of Zhalfir"); @@ -23,41 +22,98 @@ public class EmrakulThePromisedEndTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Teferi, Mage of Zhalfir"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Emrakul, the Promised End"); + addTarget(playerB, playerA); + setStrictChooseMode(true); setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertActivePlayer(playerB); } @Test - public void testExtraTurn_Turn3() { + public void test_ExtraTurn_Turn3() { addCard(Zone.BATTLEFIELD, playerB, "Island", 20); addCard(Zone.HAND, playerB, "Teferi, Mage of Zhalfir"); addCard(Zone.HAND, playerB, "Emrakul, the Promised End"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Teferi, Mage of Zhalfir"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Emrakul, the Promised End"); + addTarget(playerB, playerA); + setStrictChooseMode(true); setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertActivePlayer(playerA); } @Test - public void testExtraTurn_Turn4() { + public void test_ExtraTurn_Turn4() { addCard(Zone.BATTLEFIELD, playerB, "Island", 20); addCard(Zone.HAND, playerB, "Teferi, Mage of Zhalfir"); addCard(Zone.HAND, playerB, "Emrakul, the Promised End"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Teferi, Mage of Zhalfir"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Emrakul, the Promised End"); + addTarget(playerB, playerA); + setStrictChooseMode(true); setStopAt(4, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertActivePlayer(playerA); } + @Test + public void test_CostReduction_FromHand() { + // {13} + // This spell costs {1} less to cast for each card type among cards in your graveyard. + // When you cast this spell, you gain control of target opponent during that player’s next turn. After that turn, that player takes an extra turn. + addCard(Zone.HAND, playerA, "Emrakul, the Promised End"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 13 - 2); + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 1); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + + checkPlayableAbility("can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Emrakul, the Promised End", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Emrakul, the Promised End"); + addTarget(playerA, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Emrakul, the Promised End", 1); + } + + @Test + public void test_CostReduction_FromLibrary() { + removeAllCardsFromLibrary(playerA); + + // {13} + // This spell costs {1} less to cast for each card type among cards in your graveyard. + // When you cast this spell, you gain control of target opponent during that player’s next turn. After that turn, that player takes an extra turn. + addCard(Zone.LIBRARY, playerA, "Emrakul, the Promised End"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 13 - 2); + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 1); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + // + // You may cast creature spells from the top of your library. + addCard(Zone.BATTLEFIELD, playerA, "Vivien, Monsters' Advocate", 1); + + checkPlayableAbility("can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Emrakul, the Promised End", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Emrakul, the Promised End"); + addTarget(playerA, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Emrakul, the Promised End", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/DaybreakCoronetTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/DaybreakCoronetTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/DaybreakCoronetTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/fut/DaybreakCoronetTest.java index 6ee24d17e1..f32abed363 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/DaybreakCoronetTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/DaybreakCoronetTest.java @@ -1,5 +1,5 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.fut; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/DustOfMomentsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/DustOfMomentsTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/DustOfMomentsTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/fut/DustOfMomentsTest.java index 0368051b65..507426dfbd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/DustOfMomentsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/DustOfMomentsTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.fut; import java.util.List; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java index a7856156d9..4345d94086 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java @@ -1,5 +1,6 @@ package org.mage.test.cards.single.fut; +import mage.abilities.keyword.HasteAbility; import mage.constants.EmptyNames; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -31,8 +32,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1); + setStrictChooseMode(true); setStopAt(1, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertPowerToughness(playerA, "Grizzly Bears", 4, 4, Filter.ComparisonScope.Any); assertPowerToughness(playerB, "Grizzly Bears", 4, 4, Filter.ComparisonScope.Any); @@ -41,15 +44,20 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase { @Test public void faceDownCreaturesTest() { + // Morph {4}{G} addCard(Zone.HAND, playerA, "Pine Walker"); addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + // + // Creatures with no abilities get +2/+2. addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker"); setChoice(playerA, "Yes"); // cast it face down as 2/2 creature + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 4, 4); @@ -57,21 +65,26 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase { @Test public void faceDownGainedAbilityTest() { + // Morph {4}{G} addCard(Zone.HAND, playerA, "Pine Walker"); addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); addCard(Zone.BATTLEFIELD, playerA, "Mass Hysteria"); // All creatures have haste. + // Creatures with no abilities get +2/+2. addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker"); setChoice(playerA, "Yes"); // cast it face down as 2/2 creature + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); - assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); + //assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); // no boost (permanent have haste) + assertAbility(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), HasteAbility.getInstance(), true); } @Test @@ -83,8 +96,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raise the Alarm"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPowerToughness(playerA, "Soldier", 3, 3); } @@ -99,8 +114,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ovinize", "Goblin Guide"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPowerToughness(playerB, "Goblin Guide", 2, 3); } @@ -110,8 +127,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Hundroog", 1); // Cycling {3}, 4/7 addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPowerToughness(playerA, "Hundroog", 4, 7); } @@ -126,11 +145,12 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1); addCard(Zone.HAND, playerA, "Vastwood Zendikon"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vastwood Zendikon", "Forest"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPowerToughness(playerA, "Forest", 6, 4); @@ -160,8 +180,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dehydration", "Runeclaw Bear"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPowerToughness(playerA, "Grizzly Bears", 4, 2); @@ -179,11 +201,14 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); addCard(Zone.HAND, playerA, "Shadow Slice"); // {4}{B} - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shadow Slice"); - setChoice(playerA, "Grizzly Bears"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shadow Slice", playerB); + setChoice(playerA, "Yes"); // do cipher + addTarget(playerA, "Grizzly Bears"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPowerToughness(playerA, "Grizzly Bears", 2, 2); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/grn/ThousandYearStormTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/grn/ThousandYearStormTest.java index f777c75bab..ccd1a69982 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/grn/ThousandYearStormTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.grn; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -279,8 +279,6 @@ public class ThousandYearStormTest extends CardTestPlayerBase { You control enchanted permanent. Cycling {2} ({2}, Discard this card: Draw a card.) */ - // Test fails sometimes with the following message: - // java.lang.AssertionError: b 0x copy after control - PlayerA have wrong life: 20 <> 17 expected:<17> but was:<20> @Test public void test_GetControlNotCounts() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); @@ -305,6 +303,7 @@ public class ThousandYearStormTest extends CardTestPlayerBase { checkLife("a 1x copy", 2, PhaseStep.END_COMBAT, playerB, 20 - 3 - 3 * 2); // change controller to B + activateManaAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{T}: Add {U}", 7); castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lay Claim", "Thousand-Year Storm"); // cast bolt without pump castSpell(2, PhaseStep.END_TURN, playerB, "Lightning Bolt", playerA); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/OboshThePreypiercerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/OboshThePreypiercerTest.java new file mode 100644 index 0000000000..5f5b7d467d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/OboshThePreypiercerTest.java @@ -0,0 +1,39 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.single.iko; + + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; + +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class OboshThePreypiercerTest extends CardTestPlayerBase { + + @Test + public void testZeroCMSIsHandledAsOdd() { + setStrictChooseMode(true); + // At the beginning of your upkeep, flip a coin. If you lose the flip, Mana Crypt deals 3 damage to you. + // {T}: Add {C}{C}. + addCard(Zone.BATTLEFIELD, playerA, "Mana Crypt"); + // Companion — Your starting deck contains only cards with odd converted mana costs and land cards. + // If a source you control with an odd converted mana cost would deal damage to a permanent or player, it deals double that damage to that permanent or player instead. + addCard(Zone.BATTLEFIELD, playerA, "Obosh, the Preypiercer"); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + Assert.assertTrue("Life has to be 20 or 17 but was " + playerA.getLife() , playerA.getLife() == 17 || playerA.getLife() == 20); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/TheOzolithTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/TheOzolithTest.java new file mode 100644 index 0000000000..2a7d7873bf --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/TheOzolithTest.java @@ -0,0 +1,165 @@ +package org.mage.test.cards.single.iko; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class TheOzolithTest extends CardTestPlayerBase { + + private static final String ozlth = "The Ozolith"; + private static final String frtld = "Fertilid"; + private static final String wlkncrps = "Walking Corpse"; + private static final String flgrn = "Fully Grown"; + private static final String mrdr = "Murder"; + private static final String ttrkt = "Tatterkite"; + private static final String bltbtl = "Blightbeetle"; + private static final String pnctrblst = "Puncture Blast"; + + @Test + public void testTheOzolithGetCounters() { + addCard(Zone.BATTLEFIELD, playerA, "Bayou", 6); + addCard(Zone.BATTLEFIELD, playerA, ozlth); + addCard(Zone.BATTLEFIELD, playerA, frtld); + addCard(Zone.HAND, playerA, flgrn); + addCard(Zone.HAND, playerA, mrdr); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flgrn, frtld); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, frtld); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, frtld, 0); + assertCounterCount(ozlth, CounterType.P1P1, 2); + assertCounterCount(ozlth, CounterType.TRAMPLE, 1); + } + + @Test + public void testTheOzolithGiveCounters() { + addCard(Zone.BATTLEFIELD, playerA, "Bayou", 6); + addCard(Zone.BATTLEFIELD, playerA, ozlth); + addCard(Zone.BATTLEFIELD, playerA, frtld); + addCard(Zone.BATTLEFIELD, playerA, wlkncrps); + addCard(Zone.HAND, playerA, flgrn); + addCard(Zone.HAND, playerA, mrdr); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flgrn, frtld); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, frtld); + + setChoice(playerA, "Yes"); // Move counters at beginning of combat + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, frtld, 0); + assertCounterCount(wlkncrps, CounterType.P1P1, 2); + assertCounterCount(wlkncrps, CounterType.TRAMPLE, 1); + assertCounterCount(ozlth, CounterType.P1P1, 0); + assertCounterCount(ozlth, CounterType.TRAMPLE, 0); + } + + @Test + public void testTheOzolithCantGiveAnyCounters() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, ozlth); + addCard(Zone.BATTLEFIELD, playerA, frtld); + addCard(Zone.BATTLEFIELD, playerA, ttrkt); + addCard(Zone.HAND, playerA, mrdr); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, frtld); + + setChoice(playerA, "Yes"); // Move counters at beginning of combat + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, frtld, 0); + assertCounterCount(ttrkt, CounterType.P1P1, 0); + assertCounterCount(ozlth, CounterType.P1P1, 2); + } + + @Test + public void testTheOzolithCantGiveSomeCounters() { + addCard(Zone.BATTLEFIELD, playerA, "Bayou", 6); + addCard(Zone.BATTLEFIELD, playerA, ozlth); + addCard(Zone.BATTLEFIELD, playerA, frtld); + addCard(Zone.BATTLEFIELD, playerA, wlkncrps); + addCard(Zone.HAND, playerA, flgrn); + addCard(Zone.HAND, playerA, mrdr); + addCard(Zone.BATTLEFIELD, playerB, bltbtl); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flgrn, frtld); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, frtld); + + setChoice(playerA, "Yes"); // Move counters at beginning of combat + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, frtld, 0); + assertCounterCount(wlkncrps, CounterType.P1P1, 0); + assertCounterCount(wlkncrps, CounterType.TRAMPLE, 1); + assertCounterCount(ozlth, CounterType.P1P1, 2); + assertCounterCount(ozlth, CounterType.TRAMPLE, 0); + } + + @Test + public void testTheOzolithMultiples() { + addCard(Zone.BATTLEFIELD, playerA, "Bayou", 6); + addCard(Zone.BATTLEFIELD, playerA, ozlth, 2); + addCard(Zone.BATTLEFIELD, playerA, "Mirror Gallery"); + addCard(Zone.BATTLEFIELD, playerA, frtld); + addCard(Zone.BATTLEFIELD, playerA, wlkncrps); + addCard(Zone.HAND, playerA, flgrn); + addCard(Zone.HAND, playerA, mrdr); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flgrn, frtld); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, frtld); + + setChoice(playerA, "Yes", 2); // Move counters at beginning of combat + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, frtld, 0); + assertCounterCount(wlkncrps, CounterType.P1P1, 4); + assertCounterCount(wlkncrps, CounterType.TRAMPLE, 2); + assertCounterCount(ozlth, CounterType.P1P1, 0); + assertCounterCount(ozlth, CounterType.TRAMPLE, 0); + } + + @Ignore + @Test + public void testTheOzolithMinusPlus() { + // TODO: this test fails because of incorrect last known information handling + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, ozlth); + addCard(Zone.BATTLEFIELD, playerA, frtld); + addCard(Zone.HAND, playerA, pnctrblst); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, pnctrblst, frtld); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, frtld, 0); + assertCounterCount(ozlth, CounterType.P1P1, 2); + assertCounterCount(ozlth, CounterType.M1M1, 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/UnpredictableCycloneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/UnpredictableCycloneTest.java new file mode 100644 index 0000000000..f4b81b34d3 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/UnpredictableCycloneTest.java @@ -0,0 +1,114 @@ +package org.mage.test.cards.single.iko; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class UnpredictableCycloneTest extends CardTestPlayerBase { + + @Test + public void testCyclone() { + // Make sure the card works normally + + addCard(Zone.LIBRARY, playerA, "Goblin Piker"); + addCard(Zone.LIBRARY, playerA, "Swamp", 10); + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Unpredictable Cyclone"); + addCard(Zone.HAND, playerA, "Desert Cerodon"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); + setChoice(playerA, "Yes"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Goblin Piker", 1); + } + + @Test + public void testLandCycle() { + // Make sure it doesn't apply to cycling lands + + addCard(Zone.LIBRARY, playerA, "Swamp", 10); + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Unpredictable Cyclone"); + addCard(Zone.HAND, playerA, "Forgotten Cave"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + assertHandCount(playerA, "Swamp", 1); + } + + @Test + public void testModifiedDraw() { + // If a draw is increased, the ability will apply additional times + + addCard(Zone.LIBRARY, playerA, "Goblin Piker", 2); + addCard(Zone.LIBRARY, playerA, "Swamp", 10); + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Unpredictable Cyclone"); + addCard(Zone.BATTLEFIELD, playerA, "Thought Reflection"); + addCard(Zone.HAND, playerA, "Desert Cerodon"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); + setChoice(playerA, "Thought Reflection"); // apply doubling first + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Goblin Piker", 2); + } + + @Test + public void testModifiedDraw2() { + // Make sure the effect works with multiple stacked replacement effects + + addCard(Zone.LIBRARY, playerA, "Goblin Piker", 4); + addCard(Zone.LIBRARY, playerA, "Swamp", 10); + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Unpredictable Cyclone"); + addCard(Zone.BATTLEFIELD, playerA, "Thought Reflection", 2); + addCard(Zone.HAND, playerA, "Desert Cerodon"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); + setChoice(playerA, "Thought Reflection", 3); // apply doubling first + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Goblin Piker", 4); + } + + @Test + public void testStolenDraw() { + // if your opponent cycles a card and you steal that draw with Notion Thief, the draw will still be replaced + + addCard(Zone.LIBRARY, playerA, "Goblin Piker", 1); + addCard(Zone.LIBRARY, playerA, "Swamp", 10); + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Unpredictable Cyclone"); + addCard(Zone.HAND, playerB, "Desert Cerodon"); + addCard(Zone.HAND, playerA, "Plagiarize"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plagiarize", playerB); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Cycling"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Goblin Piker", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/ZilorthaStrengthIncarnateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/ZilorthaStrengthIncarnateTest.java new file mode 100644 index 0000000000..21818bf65c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/iko/ZilorthaStrengthIncarnateTest.java @@ -0,0 +1,179 @@ +package org.mage.test.cards.single.iko; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.After; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author htrajan + */ +public class ZilorthaStrengthIncarnateTest extends CardTestPlayerBase { + + @Test + public void testNotPresent_combatDamageResolvesLethalityAsNormal() { + addCard(Zone.BATTLEFIELD, playerA, "Savai Sabertooth"); + addCard(Zone.BATTLEFIELD, playerB, "Drannith Healer"); + + attack(1, playerA, "Savai Sabertooth"); + block(1, playerB, "Drannith Healer", "Savai Sabertooth"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Savai Sabertooth", 1); + assertGraveyardCount(playerB, "Drannith Healer", 1); + } + + @Test + public void testPresent_combatDamageResolvesLethalityUsingPower() { + addCard(Zone.BATTLEFIELD, playerA, "Zilortha, Strength Incarnate"); + addCard(Zone.BATTLEFIELD, playerA, "Savai Sabertooth"); + addCard(Zone.BATTLEFIELD, playerB, "Drannith Healer"); + + attack(1, playerA, "Savai Sabertooth"); + block(1, playerB, "Drannith Healer", "Savai Sabertooth"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Savai Sabertooth", 0); + assertGraveyardCount(playerB, "Drannith Healer", 1); + } + + /* + * 2020-04-17 + * A creature with 0 power isn’t destroyed unless it has at least 1 damage marked on it. + */ + @Test + public void testPresent_oneDamageRequiredToDestroyZeroPowerCreature() { + addCard(Zone.BATTLEFIELD, playerA, "Zilortha, Strength Incarnate"); + addCard(Zone.BATTLEFIELD, playerA, "Aegis Turtle"); + addCard(Zone.BATTLEFIELD, playerB, "Aegis Turtle"); + + attack(1, playerA, "Aegis Turtle"); + block(1, playerB, "Aegis Turtle", "Aegis Turtle"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Aegis Turtle", 0); + assertGraveyardCount(playerB, "Aegis Turtle", 0); + } + + @Test + public void testNotPresent_flameSpillResolvesAsNormal() { + addCard(Zone.BATTLEFIELD, playerA, "Savai Sabertooth"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + addCard(Zone.HAND, playerB, "Flame Spill"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Flame Spill", "Savai Sabertooth"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Savai Sabertooth", 1); + assertGraveyardCount(playerB, "Flame Spill", 1); + + assertLife(playerA, 17); + } + + @Test + public void testPresent_flameSpillResolvesUsingPower() { + addCard(Zone.BATTLEFIELD, playerA, "Zilortha, Strength Incarnate"); + addCard(Zone.BATTLEFIELD, playerA, "Savai Sabertooth"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + addCard(Zone.HAND, playerB, "Flame Spill"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Flame Spill", "Savai Sabertooth"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Savai Sabertooth", 1); + assertGraveyardCount(playerB, "Flame Spill", 1); + + assertLife(playerA, 19); + } + + /* + * 2020-04-17 + * Because damage remains marked on a creature until the damage is removed as the turn ends, nonlethal damage dealt to a creature you control may become lethal if Zilortha enters or leaves the battlefield during that turn. + */ + @Test + public void testPresent_leavesBattlefield_damageResolvesLethalityUsingPower_thenCheckedAgainstToughness() { + addCard(Zone.BATTLEFIELD, playerA, "Zilortha, Strength Incarnate"); + addCard(Zone.BATTLEFIELD, playerA, "Savai Sabertooth"); + addCard(Zone.BATTLEFIELD, playerB, "Drannith Healer"); + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3); + addCard(Zone.HAND, playerB, "Murder"); + + attack(1, playerA, "Savai Sabertooth"); + block(1, playerB, "Drannith Healer", "Savai Sabertooth"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Murder", "Zilortha, Strength Incarnate"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Zilortha, Strength Incarnate", 1); + assertGraveyardCount(playerA, "Savai Sabertooth", 1); + assertGraveyardCount(playerB, "Drannith Healer", 1); + assertGraveyardCount(playerB, "Murder", 1); + } + + @Test + public void testPresent_ownedByBothPlayers() { + addCard(Zone.BATTLEFIELD, playerA, "Zilortha, Strength Incarnate"); + addCard(Zone.BATTLEFIELD, playerA, "Maned Serval"); + addCard(Zone.BATTLEFIELD, playerB, "Zilortha, Strength Incarnate"); + addCard(Zone.BATTLEFIELD, playerB, "Maned Serval"); + + attack(1, playerA, "Maned Serval"); + block(1, playerB, "Maned Serval", "Maned Serval"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Maned Serval", 1); + assertGraveyardCount(playerB, "Maned Serval", 1); + } + + @Test + public void testAbsent_entersBattlefield_damageResolvesLethalityUsingToughness_thenCheckedAgainstPower() { + addCard(Zone.HAND, playerA, "Zilortha, Strength Incarnate"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + addCard(Zone.BATTLEFIELD, playerA, "Maned Serval"); + addCard(Zone.BATTLEFIELD, playerB, "Maned Serval"); + + attack(1, playerA, "Maned Serval"); + block(1, playerB, "Maned Serval", "Maned Serval"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Zilortha, Strength Incarnate"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Maned Serval", 1); + assertGraveyardCount(playerB, "Maned Serval", 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/GutterGrimeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/isd/GutterGrimeTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/GutterGrimeTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/isd/GutterGrimeTest.java index 4fb73ffc62..d19d980285 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/GutterGrimeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/isd/GutterGrimeTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.isd; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/HomicidalBruteTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/isd/HomicidalBruteTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/HomicidalBruteTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/isd/HomicidalBruteTest.java index 35411eabbf..165999433b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/HomicidalBruteTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/isd/HomicidalBruteTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.isd; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ktk/GhostfireBladeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ktk/GhostfireBladeTest.java new file mode 100644 index 0000000000..8e2848f646 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ktk/GhostfireBladeTest.java @@ -0,0 +1,43 @@ +package org.mage.test.cards.single.ktk; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ + +public class GhostfireBladeTest extends CardTestPlayerBase { + + @Test + public void test_CanPlayWithCostReduce() { + // Equipped creature gets +2/+2. + // Equip {3} + // Ghostfire Blade’s equip ability costs {2} less to activate if it targets a colorless creature. + addCard(Zone.BATTLEFIELD, playerA, "Ghostfire Blade", 1); + // + addCard(Zone.HAND, playerA, "Alpha Myr", 1); // {2}, 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + checkPlayableAbility("can't play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {3}", false); + + // add creature and activate cost reduce + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpha Myr"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("can't play wit no mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {3}", false); // no mana after creature cast + + // can play on next turn + checkPlayableAbility("can play", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {3}", true); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {3}", "Alpha Myr"); + + setStopAt(3, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Alpha Myr", 2 + 2, 1 + 2); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lea/BalanceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lea/BalanceTest.java new file mode 100644 index 0000000000..a237b94806 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lea/BalanceTest.java @@ -0,0 +1,41 @@ +package org.mage.test.cards.single.lea; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BalanceTest extends CardTestPlayerBase { + + @Test + public void testBalance() { + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Runeclaw Bear", 3); + addCard(Zone.HAND, playerA, "Plains", 1); + addCard(Zone.HAND, playerA, "Balance"); + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerB, "Runeclaw Bear", 2); + addCard(Zone.HAND, playerB, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balance"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Plains", 2); + assertPermanentCount(playerA, "Runeclaw Bear", 2); + assertHandCount(playerA, "Plains", 1); + assertHandCount(playerA, "Balance", 0); + assertGraveyardCount(playerA, "Runeclaw Bear", 1); + assertGraveyardCount(playerA, "Balance", 1); + + assertPermanentCount(playerB, "Swamp", 2); + assertPermanentCount(playerB, "Runeclaw Bear", 2); + assertHandCount(playerB, "Swamp", 1); + assertGraveyardCount(playerB, "Swamp", 2); //1 from hand, 1 from battlefield + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/InfernalCaretakerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lgn/InfernalCaretakerTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/InfernalCaretakerTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/lgn/InfernalCaretakerTest.java index 5384067e99..0df4097782 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/InfernalCaretakerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lgn/InfernalCaretakerTest.java @@ -1,56 +1,56 @@ - -package org.mage.test.cards.single; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author cg5 - */ -public class InfernalCaretakerTest extends CardTestPlayerBase { - - @Test - /* - * Infernal Caretaker {3}{B} - * Creature - Human Cleric - * Morph {3}{B} - * When Infernal Caretaker is turned face up, return all Zombie cards from - * all graveyards to their owners' hands. - */ - public void testInfernalCaretaker() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10); - addCard(Zone.BATTLEFIELD, playerA, "Walking Corpse", 1); - addCard(Zone.HAND, playerA, "Infernal Caretaker", 1); - addCard(Zone.GRAVEYARD, playerA, "Walking Corpse", 4); - addCard(Zone.GRAVEYARD, playerA, "Storm Crow", 4); - - addCard(Zone.GRAVEYARD, playerB, "Festering Goblin", 4); - addCard(Zone.GRAVEYARD, playerB, "Elvish Visionary", 4); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Infernal Caretaker"); - setChoice(playerA, "Yes"); // Cast as a morph - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{B}: Turn this face-down permanent face up."); - setStopAt(1, PhaseStep.END_TURN); - execute(); - - assertPermanentCount(playerA, "Infernal Caretaker", 1); - assertPermanentCount(playerA, "Walking Corpse", 1); - assertHandCount(playerA, "Walking Corpse", 4); - assertHandCount(playerA, "Storm Crow", 0); - assertHandCount(playerA, "Festering Goblin", 0); - assertHandCount(playerA, "Elvish Visionary", 0); - assertGraveyardCount(playerA, 4); // 4 * Storm Crow - - assertPermanentCount(playerB, "Infernal Caretaker", 0); - assertPermanentCount(playerB, "Walking Corpse", 0); - assertHandCount(playerB, "Walking Corpse", 0); - assertHandCount(playerB, "Storm Crow", 0); - assertHandCount(playerB, "Festering Goblin", 4); - assertHandCount(playerB, "Elvish Visionary", 0); - assertGraveyardCount(playerB, 4); // 4 * Elvish Visionary - } -} + +package org.mage.test.cards.single.lgn; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author cg5 + */ +public class InfernalCaretakerTest extends CardTestPlayerBase { + + @Test + /* + * Infernal Caretaker {3}{B} + * Creature - Human Cleric + * Morph {3}{B} + * When Infernal Caretaker is turned face up, return all Zombie cards from + * all graveyards to their owners' hands. + */ + public void testInfernalCaretaker() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10); + addCard(Zone.BATTLEFIELD, playerA, "Walking Corpse", 1); + addCard(Zone.HAND, playerA, "Infernal Caretaker", 1); + addCard(Zone.GRAVEYARD, playerA, "Walking Corpse", 4); + addCard(Zone.GRAVEYARD, playerA, "Storm Crow", 4); + + addCard(Zone.GRAVEYARD, playerB, "Festering Goblin", 4); + addCard(Zone.GRAVEYARD, playerB, "Elvish Visionary", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Infernal Caretaker"); + setChoice(playerA, "Yes"); // Cast as a morph + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{B}: Turn this face-down permanent face up."); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Infernal Caretaker", 1); + assertPermanentCount(playerA, "Walking Corpse", 1); + assertHandCount(playerA, "Walking Corpse", 4); + assertHandCount(playerA, "Storm Crow", 0); + assertHandCount(playerA, "Festering Goblin", 0); + assertHandCount(playerA, "Elvish Visionary", 0); + assertGraveyardCount(playerA, 4); // 4 * Storm Crow + + assertPermanentCount(playerB, "Infernal Caretaker", 0); + assertPermanentCount(playerB, "Walking Corpse", 0); + assertHandCount(playerB, "Walking Corpse", 0); + assertHandCount(playerB, "Storm Crow", 0); + assertHandCount(playerB, "Festering Goblin", 4); + assertHandCount(playerB, "Elvish Visionary", 0); + assertGraveyardCount(playerB, 4); // 4 * Elvish Visionary + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ArbiterOfKnollridgeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/ArbiterOfKnollridgeTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ArbiterOfKnollridgeTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/ArbiterOfKnollridgeTest.java index d36888aeed..f189ed147c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ArbiterOfKnollridgeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/ArbiterOfKnollridgeTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.lrw; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/AshlingThePilgrimTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/AshlingThePilgrimTest.java new file mode 100644 index 0000000000..7dfb3c0c5c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/AshlingThePilgrimTest.java @@ -0,0 +1,210 @@ +package org.mage.test.cards.single.lrw; + +import mage.abilities.keyword.TrampleAbility; +import mage.constants.ManaType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ + +public class AshlingThePilgrimTest extends CardTestPlayerBase { + + private static final String ashling = "Ashling the Pilgrim"; + private static final String ashAbility = "{1}{R}: "; + private static final String cshift = "Cloudshift"; + private static final String slbrt = "Soulbright Flamekin"; + private static final String slbrtAbility = "{2}: "; + private static final String sqlch = "Squelch"; + private static final String crps = "Walking Corpse"; + private static final String mrdr = "Murder"; + + @Test + public void testAshling() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + addCard(Zone.BATTLEFIELD, playerA, ashling); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 0); + assertLife(playerA, 17); + } + + @Test + public void testAshlingMultipleTurns() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); + addCard(Zone.BATTLEFIELD, playerA, ashling); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 1); + assertCounterCount(ashling, CounterType.P1P1, 4); + assertLife(playerA, 20); + } + + @Test + public void testAshlingMultipleTurns2() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 14); + addCard(Zone.BATTLEFIELD, playerA, ashling); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 0); + assertLife(playerA, 13); + } + + @Test + public void testBlinkedAshling() { + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 7); + addCard(Zone.BATTLEFIELD, playerA, ashling); + addCard(Zone.HAND, playerA, cshift); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cshift, ashling); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 1); + assertCounterCount(ashling, CounterType.P1P1, 2); + assertLife(playerA, 20); + } + + @Test + public void testBlinkedAshling2() { + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 9); + addCard(Zone.BATTLEFIELD, playerA, ashling); + addCard(Zone.HAND, playerA, cshift); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cshift, ashling); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 0); + assertLife(playerA, 17); + } + + @Test + public void testAshlingExtraActivations() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + addCard(Zone.BATTLEFIELD, playerA, ashling); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 0); + assertLife(playerA, 17); + } + + @Test + public void testAshlingSoulbright() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + addCard(Zone.BATTLEFIELD, playerA, ashling); + addCard(Zone.BATTLEFIELD, playerA, slbrt); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, slbrtAbility, ashling); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, slbrtAbility, ashling); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, slbrtAbility, ashling); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 0); + assertPermanentCount(playerA, slbrt, 0); + assertLife(playerA, 17); + } + + @Test + public void testAshlingStifle() { + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 8); + addCard(Zone.BATTLEFIELD, playerA, ashling); + addCard(Zone.HAND, playerA, sqlch); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashAbility); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqlch); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ashAbility); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, ashling, 1); + assertCounterCount(playerA, ashling, CounterType.P1P1, 2); + assertLife(playerA, 20); + } + + @Test + public void testSoulbrightFizzle() { + addCard(Zone.BATTLEFIELD, playerA, "Badlands", 9); + addCard(Zone.BATTLEFIELD, playerA, "Upwelling"); + addCard(Zone.BATTLEFIELD, playerA, crps); + addCard(Zone.BATTLEFIELD, playerA, slbrt); + addCard(Zone.HAND, playerA, mrdr); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, slbrtAbility, crps); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mrdr, crps); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, slbrtAbility, slbrt); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, slbrtAbility, slbrt); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertManaPool(playerA, ManaType.RED, 0); + assertAbility(playerA, slbrt, TrampleAbility.getInstance(), true); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/MulldrifterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/MulldrifterTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java index 7a398f9cca..70d3a391f8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/MulldrifterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package org.mage.test.cards.single; +package org.mage.test.cards.single.lrw; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/MindShatterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m10/MindShatterTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/MindShatterTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/m10/MindShatterTest.java index 0ac5766bc1..1de37d2ddd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/MindShatterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m10/MindShatterTest.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package org.mage.test.cards.single; +package org.mage.test.cards.single.m10; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/NecroticPlagueTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/NecroticPlagueTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/NecroticPlagueTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/m11/NecroticPlagueTest.java index 5279a89f60..4908839d0c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/NecroticPlagueTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/NecroticPlagueTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.m11; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/TurnToFrogTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m12/TurnToFrogTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/TurnToFrogTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/m12/TurnToFrogTest.java index ce021a47f0..8f5f310fca 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/TurnToFrogTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m12/TurnToFrogTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.m12; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/FaithsRewardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m13/FaithsRewardTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/FaithsRewardTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/m13/FaithsRewardTest.java index a2c52f5001..6638bb8149 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/FaithsRewardTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m13/FaithsRewardTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.m13; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ChandraPyromasterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m14/ChandraPyromasterTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ChandraPyromasterTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/m14/ChandraPyromasterTest.java index ad7ed8d9ca..33db189545 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ChandraPyromasterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m14/ChandraPyromasterTest.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package org.mage.test.cards.single; +package org.mage.test.cards.single.m14; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/JacesMindseekerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m14/JacesMindseekerTest.java similarity index 95% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/JacesMindseekerTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/m14/JacesMindseekerTest.java index 5163b4b799..686b4d70f0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/JacesMindseekerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m14/JacesMindseekerTest.java @@ -1,53 +1,53 @@ - -package org.mage.test.cards.single; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class JacesMindseekerTest extends CardTestPlayerBase { - - /** - * Jace's Mindseeker trigger ability is not working properly. It doesn't - * allow me to cast an instant or sorcery if there is one among the 5 cards - * put into the graveyard. I think the problem is that when the cards are - * put into the graveyard, the cards can't be cast anymore. What if the - * cards are revealed first before they are put into the graveyard? That - * doesn't follow the sequence on the card, but it might solve the bug. - */ - @Test - public void testJacesMindseeker() { - addCard(Zone.BATTLEFIELD, playerA, "Island", 6); - // Flying - // When Jace's Mindseeker enters the battlefield, target opponent puts the top five cards of their library into their graveyard. - // You may cast an instant or sorcery card from among them without paying its mana cost. - addCard(Zone.HAND, playerA, "Jace's Mindseeker", 1); // Creature 4/4 {4}{U}{U} - - addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 2); - addCard(Zone.LIBRARY, playerB, "Lightning Bolt", 1); - addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 2); - skipInitShuffling(); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Jace's Mindseeker"); - addTarget(playerA, playerB); - setChoice(playerA, "Yes"); - setChoice(playerA, "Lightning Bolt"); - addTarget(playerA, playerB); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Jace's Mindseeker", 1); - assertGraveyardCount(playerB, "Lightning Bolt", 1); - assertGraveyardCount(playerB, "Silvercoat Lion", 4); - - assertLife(playerA, 20); - assertLife(playerB, 17); - - } -} + +package org.mage.test.cards.single.m14; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class JacesMindseekerTest extends CardTestPlayerBase { + + /** + * Jace's Mindseeker trigger ability is not working properly. It doesn't + * allow me to cast an instant or sorcery if there is one among the 5 cards + * put into the graveyard. I think the problem is that when the cards are + * put into the graveyard, the cards can't be cast anymore. What if the + * cards are revealed first before they are put into the graveyard? That + * doesn't follow the sequence on the card, but it might solve the bug. + */ + @Test + public void testJacesMindseeker() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + // Flying + // When Jace's Mindseeker enters the battlefield, target opponent puts the top five cards of their library into their graveyard. + // You may cast an instant or sorcery card from among them without paying its mana cost. + addCard(Zone.HAND, playerA, "Jace's Mindseeker", 1); // Creature 4/4 {4}{U}{U} + + addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 2); + addCard(Zone.LIBRARY, playerB, "Lightning Bolt", 1); + addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 2); + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Jace's Mindseeker"); + addTarget(playerA, playerB); + setChoice(playerA, "Yes"); + setChoice(playerA, "Lightning Bolt"); + addTarget(playerA, playerB); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Jace's Mindseeker", 1); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 4); + + assertLife(playerA, 20); + assertLife(playerB, 17); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/AetherGustTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m20/AetherGustTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/AetherGustTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/m20/AetherGustTest.java index 5ba3d48679..194b6fdbd9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/AetherGustTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m20/AetherGustTest.java @@ -1,6 +1,6 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.m20; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/EmbodimentOfAgoniesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m20/EmbodimentOfAgoniesTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/EmbodimentOfAgoniesTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/m20/EmbodimentOfAgoniesTest.java index 931a84586b..1f34d80f74 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/EmbodimentOfAgoniesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m20/EmbodimentOfAgoniesTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.m20; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/RepeatedReverberationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m20/RepeatedReverberationTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/RepeatedReverberationTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/m20/RepeatedReverberationTest.java index 78315cfb42..92dcac9589 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/RepeatedReverberationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m20/RepeatedReverberationTest.java @@ -1,5 +1,5 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.m20; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AdherentOfHopeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AdherentOfHopeTest.java new file mode 100644 index 0000000000..2dc7fefbf9 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AdherentOfHopeTest.java @@ -0,0 +1,58 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AdherentOfHopeTest extends CardTestPlayerBase { + + + + + @Test + public void test_basri_in_play_controllerturn(){ + // At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope. + addCard(Zone.BATTLEFIELD, playerA, "Adherent of Hope", 1); + addCard(Zone.BATTLEFIELD, playerA, "Basri Ket", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, "Adherent of Hope", CounterType.P1P1, 1); + + } + + @Test + public void test_basri_in_play_opponentturn(){ + // At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope. + addCard(Zone.BATTLEFIELD, playerA, "Adherent of Hope", 1); + addCard(Zone.BATTLEFIELD, playerA, "Basri Ket", 1); + + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, "Adherent of Hope", CounterType.P1P1, 1); + + } + + @Test + public void test_no_basri_in_play_controllerturn(){ + // At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope. + addCard(Zone.BATTLEFIELD, playerA, "Adherent of Hope", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, "Adherent of Hope", CounterType.P1P1, 0); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AlchemistsGiftTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AlchemistsGiftTest.java new file mode 100644 index 0000000000..b2faca78a6 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AlchemistsGiftTest.java @@ -0,0 +1,58 @@ +package org.mage.test.cards.single.m21; + +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AlchemistsGiftTest extends CardTestPlayerBase { + + private final String deathtouch = "Yes"; + private final String lifelink = "No"; + + + + + @Test + public void giveDeathTouch(){ + // Target creature gets +1/+1 and gains your choice of deathtouch or lifelink until end of turn. + addCard(Zone.HAND, playerA, "Alchemist's Gift"); + addCard(Zone.BATTLEFIELD,playerA, "Adherent of Hope", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alchemist's Gift", "Adherent of Hope"); + // give Deathtouch + setChoice(playerA, deathtouch); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertAbility(playerA, "Adherent of Hope", DeathtouchAbility.getInstance(), true); + assertAbility(playerA, "Adherent of Hope", LifelinkAbility.getInstance(), false); + assertPowerToughness(playerA, "Adherent of Hope", 3, 2); + } + + @Test + public void giveLifelink(){ + // Target creature gets +1/+1 and gains your choice of deathtouch or lifelink until end of turn. + addCard(Zone.HAND, playerA, "Alchemist's Gift"); + addCard(Zone.BATTLEFIELD,playerA, "Adherent of Hope", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alchemist's Gift", "Adherent of Hope"); + // give Lifelink + setChoice(playerA, lifelink); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + assertAbility(playerA, "Adherent of Hope", LifelinkAbility.getInstance(), true); + assertAbility(playerA, "Adherent of Hope", DeathtouchAbility.getInstance(), false); + assertPowerToughness(playerA, "Adherent of Hope", 3, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AlpineHoundmasterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AlpineHoundmasterTest.java new file mode 100644 index 0000000000..35f40e4b0b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AlpineHoundmasterTest.java @@ -0,0 +1,103 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AlpineHoundmasterTest extends CardTestPlayerBase { + + + + @Test + public void searchDog() { + // When Alpine Houndmaster enters the battlefield, you may search your library for a card named + // Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library. + addCard(Zone.HAND, playerA, "Alpine Houndmaster", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Plains"); + addCard(Zone.LIBRARY, playerA, "Alpine Watchdog"); + addCard(Zone.LIBRARY, playerA, "Igneous Cur"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster"); + setChoice(playerA, "Yes"); + addTarget(playerA, "Alpine Watchdog"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 1); + + } + + @Test + public void searchCur() { + // When Alpine Houndmaster enters the battlefield, you may search your library for a card named + // Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library. + addCard(Zone.HAND, playerA, "Alpine Houndmaster", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Plains"); + addCard(Zone.LIBRARY, playerA, "Alpine Watchdog"); + addCard(Zone.LIBRARY, playerA, "Igneous Cur"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster"); + setChoice(playerA, "Yes"); + addTarget(playerA, "Igneous Cur"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 1); + + } + + @Test + public void searchBoth() { + // When Alpine Houndmaster enters the battlefield, you may search your library for a card named + // Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library. + addCard(Zone.HAND, playerA, "Alpine Houndmaster", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Plains"); + addCard(Zone.LIBRARY, playerA, "Alpine Watchdog"); + addCard(Zone.LIBRARY, playerA, "Igneous Cur"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster"); + setChoice(playerA, "Yes"); + addTarget(playerA, "Igneous Cur^Alpine Watchdog"); + //addTarget(playerA, ""); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 2); + + } + + + + @Test + public void attack() { + addCard(Zone.BATTLEFIELD, playerA, "Alpine Houndmaster", 1); + addCard(Zone.BATTLEFIELD, playerA, "Alpine Watchdog"); + addCard(Zone.BATTLEFIELD, playerA, "Igneous Cur"); + + // Whenever Alpine Houndmaster attacks, it gets +X/+0 until end of turn, where X is the number of other attacking creatures. + attack(3, playerA, "Alpine Houndmaster"); + attack(3, playerA, "Alpine Watchdog"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Alpine Houndmaster", 3, 2); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AngelicAscensionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AngelicAscensionTest.java new file mode 100644 index 0000000000..5fd3ead499 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AngelicAscensionTest.java @@ -0,0 +1,49 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AngelicAscensionTest extends CardTestPlayerBase { + + + + + @Test + public void exileCreatureOpponent(){ + // Exile target creature or planeswalker. Its controller creates a 4/4 white Angel creature token with flying. + addCard(Zone.HAND, playerA, "Angelic Ascension"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angelic Ascension", "Grizzly Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerB, 1); + assertPermanentCount(playerB, "Angel", 1); + assertPowerToughness(playerB, "Angel", 4, 4); + } + + @Test + public void exileOwnCreature(){ + addCard(Zone.HAND, playerA, "Angelic Ascension"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angelic Ascension", "Grizzly Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertExileCount(playerA, 1); + assertPermanentCount(playerA, "Angel", 1); + assertPowerToughness(playerA, "Angel", 4, 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AnimalSanctuaryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AnimalSanctuaryTest.java new file mode 100644 index 0000000000..445df753fa --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AnimalSanctuaryTest.java @@ -0,0 +1,115 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AnimalSanctuaryTest extends CardTestPlayerBase { + + private static final String sanctuary = "Animal Sanctuary"; + private static final String bird = "Birds of Paradise"; + private static final String cat = "Ajani's Pridemate"; + private static final String dog = "Wild Mongrel"; + private static final String goat = "Mountain Goat"; + private static final String ox = "Raging Bull"; + private static final String snake = "Anaconda"; + + @Test + public void boostBird(){ + addCard(Zone.BATTLEFIELD, playerA, sanctuary); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, bird); + + // {2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake. + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}: ", bird); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, bird, CounterType.P1P1, 1); + } + + @Test + public void boostCat(){ + addCard(Zone.BATTLEFIELD, playerA, sanctuary); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, cat); + + // {2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake. + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}: ", cat); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, cat, CounterType.P1P1, 1); + } + + @Test + public void boostDog(){ + addCard(Zone.BATTLEFIELD, playerA, sanctuary); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, dog); + + // {2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake. + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}: ", dog); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, dog, CounterType.P1P1, 1); + } + + @Test + public void boostGoat(){ + addCard(Zone.BATTLEFIELD, playerA, sanctuary); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, goat); + + // {2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake. + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}: ", goat); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, goat, CounterType.P1P1, 1); + } + + @Test + public void boostOx(){ + addCard(Zone.BATTLEFIELD, playerA, sanctuary); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, ox); + + // {2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake. + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}: ", ox); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, ox, CounterType.P1P1, 1); + } + + @Test + public void boostSnake(){ + addCard(Zone.BATTLEFIELD, playerA, sanctuary); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, snake); + + // {2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake. + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}: ", snake); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, snake, CounterType.P1P1, 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AnointedChoristerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AnointedChoristerTest.java new file mode 100644 index 0000000000..62a2260329 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AnointedChoristerTest.java @@ -0,0 +1,33 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AnointedChoristerTest extends CardTestPlayerBase { + + + private final String chorister = "Anointed Chorister"; + + + @Test + public void buff(){ + // 1/1 Lifelink + addCard(Zone.BATTLEFIELD, playerA, chorister); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 10); + + // {4}{W}: Anointed Chorister gets +3/+3 until end of turn. + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{W}:"); + attack(3, playerA, chorister); + + setStrictChooseMode(true); + setStopAt(4, PhaseStep.PRECOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 24); + assertLife(playerB, 16); + assertPowerToughness(playerA, chorister, 1, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ArchfiendsVesselTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ArchfiendsVesselTest.java new file mode 100644 index 0000000000..cb1f0923b0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ArchfiendsVesselTest.java @@ -0,0 +1,54 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class ArchfiendsVesselTest extends CardTestPlayerBase { + + private static final String archfiendsVessel = "Archfiend's Vessel"; + + @Test + public void enterFromGraveyard(){ + //When Archfiend's Vessel enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, + // exile it. If you do, create a 5/5 black Demon creature token with flying. + + addCard(Zone.GRAVEYARD, playerA, archfiendsVessel); + addCard(Zone.HAND, playerA, "Exhume"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exhume"); + addTarget(playerA, archfiendsVessel); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Demon", 1); + assertExileCount(playerA, archfiendsVessel, 1); + + } + + @Test + public void playFromGraveyard(){ + //When Archfiend's Vessel enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, + // exile it. If you do, create a 5/5 black Demon creature token with flying. + + addCard(Zone.GRAVEYARD, playerA, archfiendsVessel); + // Until end of turn, you may play cards from your graveyard. + addCard(Zone.HAND, playerA, "Yawgmoth's Will"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yawgmoth's Will"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, archfiendsVessel); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Demon", 1); + assertExileCount(playerA, archfiendsVessel, 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AvenGagglemasterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AvenGagglemasterTest.java new file mode 100644 index 0000000000..34216dd83f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AvenGagglemasterTest.java @@ -0,0 +1,25 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AvenGagglemasterTest extends CardTestPlayerBase { + + @Test + public void gainLife(){ + addCard(Zone.HAND, playerA, "Aven Gagglemaster"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 10); + addCard(Zone.BATTLEFIELD, playerB, "Birds of Paradise"); + + // When Aven Gagglemaster enters the battlefield, you gain 2 life for each creature you control with flying. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aven Gagglemaster"); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 22); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AzusaLostButSeekingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AzusaLostButSeekingTest.java new file mode 100644 index 0000000000..bd7ac35872 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/AzusaLostButSeekingTest.java @@ -0,0 +1,31 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AzusaLostButSeekingTest extends CardTestPlayerBase { + + private static final String azusa = "Azusa, Lost but Seeking"; + + @Test + @Ignore + public void playAdditionalLands(){ + addCard(Zone.BATTLEFIELD, playerA, azusa); + addCard(Zone.HAND, playerA, "Forest", 4); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Forest", 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BadDealTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BadDealTest.java new file mode 100644 index 0000000000..a5fcd14856 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BadDealTest.java @@ -0,0 +1,30 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BadDealTest extends CardTestPlayerBase { + + @Test + public void castBadDeal(){ + addCard(Zone.HAND, playerA, "Bad Deal"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + addCard(Zone.HAND, playerB, "Forest", 2); + addCard(Zone.LIBRARY, playerA, "Swamp", 2); + + // You draw two cards and each opponent discards two cards. Each player loses 2 life. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bad Deal"); + addTarget(playerB, "Forest^Forest"); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 18); + assertLife(playerB, 18); + assertHandCount(playerA, 2); + assertHandCount(playerB, 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BarrinTolarianArchmageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BarrinTolarianArchmageTest.java new file mode 100644 index 0000000000..2f24a8502b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BarrinTolarianArchmageTest.java @@ -0,0 +1,72 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BarrinTolarianArchmageTest extends CardTestPlayerBase { + + private static final String barrin = "Barrin, Tolarian Archmage"; + + @Test + public void returnOwnCreature(){ + // When Barrin, Tolarian Archmage enters the battlefield, return up to one other target creature or planeswalker to its owner's hand. + addCard(Zone.HAND, playerA, barrin); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + addCard(Zone.LIBRARY, playerA, "Forest"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, barrin); + addTarget(playerA, "Grizzly Bears"); + setStrictChooseMode(true); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card. + assertHandCount(playerA, 2); + } + + @Test + public void returnOwnPlanesWalker(){ + // When Barrin, Tolarian Archmage enters the battlefield, return up to one other target creature or planeswalker to its owner's hand. + addCard(Zone.HAND, playerA, barrin); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Basri Ket"); + addCard(Zone.LIBRARY, playerA, "Forest"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, barrin); + addTarget(playerA, "Basri Ket"); + setStrictChooseMode(true); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card. + assertHandCount(playerA, 2); + } + + @Test + public void returnOpponentsPlanesWalker(){ + // When Barrin, Tolarian Archmage enters the battlefield, return up to one other target creature or planeswalker to its owner's hand. + addCard(Zone.HAND, playerA, barrin); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerB, "Basri Ket"); + addCard(Zone.LIBRARY, playerA, "Forest"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, barrin); + addTarget(playerA, "Basri Ket"); + setStrictChooseMode(true); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card. + assertHandCount(playerA, 0); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasriDevotedPaladinTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasriDevotedPaladinTest.java new file mode 100644 index 0000000000..4c17af06e7 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasriDevotedPaladinTest.java @@ -0,0 +1,82 @@ +package org.mage.test.cards.single.m21; + +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BasriDevotedPaladinTest extends CardTestPlayerBase { + + private static final String basriDevotedPaladin = "Basri, Devoted Paladin"; + // +1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn. + // −1: Whenever a creature attacks this turn, put a +1/+1 counter on it. + // −6: Creatures you control get +2/+2 and gain flying until end of turn. + + @Test + public void testAddCounter(){ + // Loyalty 4 + // +1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn. + addCard(Zone.BATTLEFIELD, playerA, basriDevotedPaladin,1); + addCard(Zone.BATTLEFIELD, playerA, "Savannah Lions"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: "); + addTarget(playerA, "Savannah Lions"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertAbility(playerA, "Savannah Lions", VigilanceAbility.getInstance(), true); + assertCounterCount(playerA, "Savannah Lions", CounterType.P1P1, 1); + } + + @Test + public void testAttackTrigger(){ + // Loyalty 4 + // −1: Whenever a creature attacks this turn, put a +1/+1 counter on it. + addCard(Zone.BATTLEFIELD, playerA, basriDevotedPaladin,1); + addCard(Zone.BATTLEFIELD, playerA, "Savannah Lions"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "-1: "); + attack(3, playerA, "Savannah Lions"); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.PRECOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 17); + assertCounterCount(playerA, "Savannah Lions", CounterType.P1P1, 1); + } + + @Test + public void testUltimate(){ + // Loyalty 4 + // −6: Creatures you control get +2/+2 and gain flying until end of turn. + addCard(Zone.BATTLEFIELD, playerA, basriDevotedPaladin,1); + addCard(Zone.BATTLEFIELD, playerA, "Savannah Lions"); + + //turn 1, Loyalty = 5 + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: "); + addTarget(playerA, "Savannah Lions"); + + // turn 3, Loyalty = 6 + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: "); + addTarget(playerA, "Savannah Lions"); + + activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "-6: "); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Savannah Lions", 6, 5); + assertAbility(playerA, "Savannah Lions", FlyingAbility.getInstance(), true); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasriKetTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasriKetTest.java new file mode 100644 index 0000000000..649ca14c35 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasriKetTest.java @@ -0,0 +1,76 @@ +package org.mage.test.cards.single.m21; + +import mage.abilities.keyword.IndestructibleAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BasriKetTest extends CardTestPlayerBase { + + private static final String basriKet = "Basri Ket"; + + @Test + public void addCounterOnCreature(){ + addCard(Zone.BATTLEFIELD, playerA, basriKet); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + + // +1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn. + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: "); + addTarget(playerA, "Grizzly Bears"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, "Grizzly Bears", CounterType.P1P1, 1); + assertAbility(playerA, "Grizzly Bears", IndestructibleAbility.getInstance(), true); + } + + @Test + public void attack(){ + addCard(Zone.BATTLEFIELD, playerA, basriKet); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + + // −2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking. + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "-2: "); + attack(3, playerA, "Grizzly Bears"); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 17); + assertPermanentCount(playerA, "Soldier", 1); + } + + @Test + public void emblem(){ + addCard(Zone.BATTLEFIELD, playerA, basriKet); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + + // turn 1: loyalty 4 + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: "); + addTarget(playerA, "Grizzly Bears"); + // turn 3: loyalty 5 + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: "); + addTarget(playerA, "Grizzly Bears"); + // turn 5: loyalty 6 + activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: "); + addTarget(playerA, "Grizzly Bears"); + + // −6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control." + activateAbility(7, PhaseStep.PRECOMBAT_MAIN, playerA, "-6: "); + attack(7, playerA, "Grizzly Bears"); + setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 14); + assertPermanentCount(playerA, "Soldier", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisAcolyteTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisAcolyteTest.java new file mode 100644 index 0000000000..fa9bee3694 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisAcolyteTest.java @@ -0,0 +1,30 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BasrisAcolyteTest extends CardTestPlayerBase { + + private static final String basrisAcolyte = "Basri's Acolyte"; + + @Test + public void checkETB(){ + addCard(Zone.HAND, playerA, basrisAcolyte); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + addCard(Zone.BATTLEFIELD, playerA, "Savannah Lions"); + + // When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, basrisAcolyte); + addTarget(playerA, "Grizzly Bears^Savannah Lions"); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, "Grizzly Bears", CounterType.P1P1, 1); + assertCounterCount(playerA, "Savannah Lions", CounterType.P1P1, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisAegisTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisAegisTest.java new file mode 100644 index 0000000000..49dd7a94ef --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisAegisTest.java @@ -0,0 +1,33 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BasrisAegisTest extends CardTestPlayerBase { + + @Test + public void castBasrisAegis(){ + addCard(Zone.HAND, playerA, "Basri's Aegis"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + addCard(Zone.BATTLEFIELD, playerA, "Savannah Lions"); + addCard(Zone.LIBRARY, playerA, "Basri, Devoted Paladin"); + + // Put a +1/+1 counter on each of up to two target creatures. + // You may search your library and/or graveyard for a card named Basri, Devoted Paladin, reveal it, and put it into your hand. If you search your library this way, shuffle it. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Basri's Aegis"); + addTarget(playerA, "Grizzly Bears^Savannah Lions"); + setChoice(playerA, "Yes"); + addTarget(playerA, "Basri, Devoted Paladin"); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 1); + assertCounterCount(playerA, "Grizzly Bears", CounterType.P1P1, 1); + assertCounterCount(playerA, "Savannah Lions", CounterType.P1P1, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisLieutenantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisLieutenantTest.java new file mode 100644 index 0000000000..1d45a0b40b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisLieutenantTest.java @@ -0,0 +1,79 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BasrisLieutenantTest extends CardTestPlayerBase { + + private static final String basrisLieutenant = "Basri's Lieutenant"; + + @Test + public void counterOnLieutenant(){ + // When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control. + // Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance. + + addCard(Zone.HAND, playerA, basrisLieutenant); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 8); + // Destroy all creatures. They can't be regenerated. + addCard(Zone.HAND, playerA, "Wrath of God"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, basrisLieutenant); + addTarget(playerA, basrisLieutenant); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wrath of God"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Knight", 1); + } + + @Test + public void counterOnOtherCreature(){ + // When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control. + // Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance. + + addCard(Zone.HAND, playerA, basrisLieutenant); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 8); + addCard(Zone.BATTLEFIELD, playerA, "Savannah Lions"); + // Destroy all creatures. They can't be regenerated. + addCard(Zone.HAND, playerA, "Wrath of God"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, basrisLieutenant); + addTarget(playerA, "Savannah Lions"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wrath of God"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Knight", 1); + } + + @Test + public void creatureWithoutCounterDies(){ + // When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control. + // Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance. + + addCard(Zone.HAND, playerA, basrisLieutenant); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Savannah Lions"); + // Destroy target nonartifact, nonblack creature. It can't be regenerated. + addCard(Zone.HAND, playerA, "Terror"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, basrisLieutenant); + addTarget(playerA, "Savannah Lions"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Terror", basrisLieutenant); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Knight", 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisSolidarityTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisSolidarityTest.java new file mode 100644 index 0000000000..cd97cef234 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BasrisSolidarityTest.java @@ -0,0 +1,33 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BasrisSolidarityTest extends CardTestPlayerBase { + + private static final String basrisSolidarity = "Basri's Solidarity"; + + @Test + public void addCountersToControlledCreatures(){ + addCard(Zone.BATTLEFIELD, playerA, "Savannah Lions"); + addCard(Zone.BATTLEFIELD, playerA, "Scryb Sprites"); + addCard(Zone.BATTLEFIELD, playerB, "Raging Goblin"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // Put a +1/+1 counter on each creature you control. + addCard(Zone.HAND, playerA, basrisSolidarity); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, basrisSolidarity); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, "Savannah Lions", CounterType.P1P1, 1); + assertCounterCount(playerA, "Scryb Sprites", CounterType.P1P1, 1); + assertCounterCount(playerB, "Raging Goblin", CounterType.P1P1, 0); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BoltHoundTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BoltHoundTest.java new file mode 100644 index 0000000000..0e6b1c4c4f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BoltHoundTest.java @@ -0,0 +1,30 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BoltHoundTest extends CardTestPlayerBase { + + @Test + public void testBoostOthers(){ + // Haste + // Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Bolt Hound"); + addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); + + attack(1, playerA, "Bolt Hound"); + attack(1, playerA, "Raging Goblin"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 16); + assertPowerToughness(playerA, "Bolt Hound", 2,2); + assertPowerToughness(playerA, "Raging Goblin", 2, 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BonePitBruteTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BonePitBruteTest.java new file mode 100644 index 0000000000..4d663d9ee3 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BonePitBruteTest.java @@ -0,0 +1,27 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BonePitBruteTest extends CardTestPlayerBase { + + @Test + public void boostETB(){ + // {4}{R}{R} + // When Bone Pit Brute enters the battlefield, target creature gets +4/+0 until end of turn. + addCard(Zone.HAND, playerA, "Bone Pit Brute"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bone Pit Brute"); + addTarget(playerA, "Bone Pit Brute"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Bone Pit Brute", 8, 5); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BrashTaunterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BrashTaunterTest.java new file mode 100644 index 0000000000..0ff1790b99 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BrashTaunterTest.java @@ -0,0 +1,45 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BrashTaunterTest extends CardTestPlayerBase { + + @Test + public void testTriggerDamageToOpponent(){ + // Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent. + addCard(Zone.BATTLEFIELD, playerA, "Brash Taunter"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + // Shock deals 2 damage to any target + addCard(Zone.HAND, playerA, "Shock"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shock", "Brash Taunter"); + addTarget(playerA, playerB); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 18); + } + + @Test + public void testFightAbility(){ + // Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent. + // {2}{R}, {T}: Brash Taunter fights another target creature. + addCard(Zone.BATTLEFIELD, playerA, "Brash Taunter"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerB, "Serra Angel"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}", "Serra Angel"); + addTarget(playerA, playerB); + setStrictChooseMode(true); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 16); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BurlfistOakTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BurlfistOakTest.java new file mode 100644 index 0000000000..beae34ae95 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BurlfistOakTest.java @@ -0,0 +1,24 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BurlfistOakTest extends CardTestPlayerBase { + + + @Test + public void boostOnCardDraw(){ + // {2}{G}{G} + // Whenever you draw a card, Burlfist Oak gets +2/+2 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Burlfist Oak"); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Burlfist Oak", 4, 5); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BurnBrightTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BurnBrightTest.java new file mode 100644 index 0000000000..caa23398e2 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/BurnBrightTest.java @@ -0,0 +1,31 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BurnBrightTest extends CardTestPlayerBase { + + @Test + public void boostControlledCreatures(){ + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); + addCard(Zone.BATTLEFIELD, playerA, "Scryb Sprites"); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); + // {2}{R} + // Creatures you control get +2/+0 until end of turn. + addCard(Zone.HAND, playerA, "Burn Bright"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Bright"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Raging Goblin", 3, 1); + assertPowerToughness(playerA, "Scryb Sprites", 3, 1); + assertPowerToughness(playerB, "Grizzly Bears", 2, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CagedZombieTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CagedZombieTest.java new file mode 100644 index 0000000000..593c143f17 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CagedZombieTest.java @@ -0,0 +1,32 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class CagedZombieTest extends CardTestPlayerBase { + + @Test + public void loseLife(){ + + // {1}{B}, {T}: Each opponent loses 2 life. Activate this ability only if a creature died this turn. + addCard(Zone.BATTLEFIELD, playerA, "Caged Zombie"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + // Destroy target nonartifact, nonblack creature. It can't be regenerated. + addCard(Zone.HAND, playerA, "Terror"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Terror", "Grizzly Bears"); + waitStackResolved(3, PhaseStep.PRECOMBAT_MAIN); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{B}"); + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertLife(playerB, 18); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CancelTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CancelTest.java new file mode 100644 index 0000000000..184c466b5e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CancelTest.java @@ -0,0 +1,30 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class CancelTest extends CardTestPlayerBase { + + @Test + public void counterTargetSpell(){ + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + // {1}{U}{U} Counter target spell + addCard(Zone.HAND, playerA, "Cancel"); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); + addCard(Zone.HAND, playerB, "Grizzly Bears"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Grizzly Bears"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cancel", "Grizzly Bears"); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Grizzly Bears", 1); + assertGraveyardCount(playerA, "Cancel", 1); + + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CanopyStalkerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CanopyStalkerTest.java new file mode 100644 index 0000000000..35f47220be --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CanopyStalkerTest.java @@ -0,0 +1,49 @@ +package org.mage.test.cards.single.m21; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class CanopyStalkerTest extends CardTestPlayerBase { + + @Test + public void testMustBeBlocked(){ + // 4/2 + // Canopy Stalker must be blocked if able. + // When Canopy Stalker dies, you gain 1 life for each creature that died this turn. + addCard(Zone.BATTLEFIELD, playerA, "Canopy Stalker"); + // 1/1 Flying + addCard(Zone.BATTLEFIELD, playerB, "Scryb Sprites"); + attack(3, playerA, "Canopy Stalker"); + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Scryb Sprites", 1); + + } + + @Test + public void testDiesEvent(){ + // 4/2 + // Canopy Stalker must be blocked if able. + // When Canopy Stalker dies, you gain 1 life for each creature that died this turn. + addCard(Zone.BATTLEFIELD, playerA, "Canopy Stalker"); + // 2/2 Vanilla + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); + attack(3, playerA, "Canopy Stalker"); + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 22); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CaptureSphereTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CaptureSphereTest.java new file mode 100644 index 0000000000..2adf9ca81d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CaptureSphereTest.java @@ -0,0 +1,29 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class CaptureSphereTest extends CardTestPlayerBase { + + @Test + public void testDontUntap(){ + // Flash + // Enchant creature + // When Capture Sphere enters the battlefield, tap enchanted creature. + // Enchanted creature doesn't untap during its controller's untap step. + + addCard(Zone.HAND, playerA, "Capture Sphere"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA,"Capture Sphere", "Grizzly Bears"); + setStrictChooseMode(true); + setStopAt(4, PhaseStep.PRECOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertTapped("Grizzly Bears", true); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CarrionGrubTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CarrionGrubTest.java new file mode 100644 index 0000000000..7f0f60a6a0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CarrionGrubTest.java @@ -0,0 +1,43 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class CarrionGrubTest extends CardTestPlayerBase { + + @Test + public void etbTrigger() { + // Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard. + // When Carrion Grub enters the battlefield, mill four cards. + addCard(Zone.HAND, playerA, "Carrion Grub"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + addCard(Zone.LIBRARY, playerA, "Forest", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Carrion Grub"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA , 4); + + } + + @Test + public void boostPower() { + // Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard. + // When Carrion Grub enters the battlefield, mill four cards. + addCard(Zone.BATTLEFIELD, playerA, "Carrion Grub"); + addCard(Zone.GRAVEYARD, playerA, "Serra Angel"); + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Carrion Grub", 4, 5); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CelestialEnforcerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CelestialEnforcerTest.java new file mode 100644 index 0000000000..69fc06a0ea --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/CelestialEnforcerTest.java @@ -0,0 +1,25 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class CelestialEnforcerTest extends CardTestPlayerBase { + + @Test + public void controlFlyingCreature(){ + // {1}{W}, {T}: Tap target creature. Activate this ability only if you control a creature with flying. + addCard(Zone.BATTLEFIELD, playerA, "Celestial Enforcer"); + addCard(Zone.BATTLEFIELD, playerA, "Serra Angel"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}", "Grizzly Bears"); + setStrictChooseMode(true); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertTapped("Grizzly Bears", true); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChandrasIncineratorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChandrasIncineratorTest.java new file mode 100644 index 0000000000..1d35e5dd5b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChandrasIncineratorTest.java @@ -0,0 +1,32 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class ChandrasIncineratorTest extends CardTestPlayerBase { + + @Test + public void testCostReduction(){ + // {5}{R} + // This spell costs {X} less to cast, + // where X is the total amount of noncombat damage dealt to your opponents this turn. + addCard(Zone.HAND, playerA, "Chandra's Incinerator"); + + // deal 3 damage to any target + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chandra's Incinerator"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + // {R} lightning bolt + {2}{R} Chandra's Incinerator + assertTappedCount("Mountain", true, 4); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChandrasMagmuttTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChandrasMagmuttTest.java new file mode 100644 index 0000000000..6a391c075b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChandrasMagmuttTest.java @@ -0,0 +1,43 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class ChandrasMagmuttTest extends CardTestPlayerBase { + + @Test + public void pingPlayer(){ + // {T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.< + addCard(Zone.BATTLEFIELD, playerA, "Chandra's Magmutt"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", playerB); + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 19); + } + + @Test + public void pingPlanesWalker(){ + // {T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.< + addCard(Zone.BATTLEFIELD, playerA, "Chandra's Magmutt"); + // +1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn. + // −2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking. + // −6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control." + addCard(Zone.BATTLEFIELD, playerB, "Basri Ket"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Basri Ket"); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertCounterCount("Basri Ket", CounterType.LOYALTY, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChandrasPyrelingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChandrasPyrelingTest.java new file mode 100644 index 0000000000..fe4c470e76 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChandrasPyrelingTest.java @@ -0,0 +1,29 @@ +package org.mage.test.cards.single.m21; + +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class ChandrasPyrelingTest extends CardTestPlayerBase { + + @Test + public void boostAndGetDoubleStrike(){ + // Whenever a source you control deals noncombat damage to an opponent, + // Chandra's Pyreling gets +1/+0 and gains double strike until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Chandra's Pyreling"); + addCard(Zone.HAND, playerA, "Shock"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shock", playerB); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertAbility(playerA, "Chandra's Pyreling", DoubleStrikeAbility.getInstance(), true); + assertPowerToughness(playerA, "Chandra's Pyreling", 2, 3); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChromaticOrreryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChromaticOrreryTest.java new file mode 100644 index 0000000000..978deae3b8 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChromaticOrreryTest.java @@ -0,0 +1,44 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class ChromaticOrreryTest extends CardTestPlayerBase { + + @Test + public void testSpendManyAsThoughAnyColor(){ + // You may spend mana as though it were mana of any color. + addCard(Zone.BATTLEFIELD, playerA, "Chromatic Orrery"); + // {W}{U}{B}{R}{G} + addCard(Zone.HAND, playerA, "Sliver Overlord"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sliver Overlord"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Sliver Overlord", 1); + } + + @Test + public void testDrawCards(){ + // {5}, {T}: Draw a card for each color among permanents you control. + addCard(Zone.BATTLEFIELD, playerA, "Chromatic Orrery"); + // {W}{U}{B}{R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Sliver Overlord"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.LIBRARY, playerA, "Swamp",5); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{5}"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerA, 5); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChromeReplicatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChromeReplicatorTest.java new file mode 100644 index 0000000000..6ad543cb6b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ChromeReplicatorTest.java @@ -0,0 +1,65 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class ChromeReplicatorTest extends CardTestPlayerBase { + + @Test + public void controlTwoSameNonTokens(){ + // When Chrome Replicator enters the battlefield, + // if you control two or more nonland, nontoken permanents with the same name as one another, + // create a 4/4 colorless Construct artifact creature token. + addCard(Zone.HAND, playerA, "Chrome Replicator"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chrome Replicator"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Construct", 1); + } + + @Test + public void controlTwoSameLands(){ + // When Chrome Replicator enters the battlefield, + // if you control two or more nonland, nontoken permanents with the same name as one another, + // create a 4/4 colorless Construct artifact creature token. + addCard(Zone.HAND, playerA, "Chrome Replicator"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chrome Replicator"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Construct", 0); + } + + @Test + public void controlTwoSameTokens(){ + // When Chrome Replicator enters the battlefield, + // if you control two or more nonland, nontoken permanents with the same name as one another, + // create a 4/4 colorless Construct artifact creature token. + addCard(Zone.HAND, playerA, "Chrome Replicator"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // create 2 soldier tokens + addCard(Zone.HAND, playerA, "Raise the Alarm"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raise the Alarm"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chrome Replicator"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Construct", 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ConclaveMentorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ConclaveMentorTest.java new file mode 100644 index 0000000000..7b67f43dfa --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ConclaveMentorTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class ConclaveMentorTest extends CardTestPlayerBase { + + @Test + public void testIncreaseCounters() { + // If one or more +1/+1 counters would be put on a creature you control, + // that many plus one +1/+1 counters are put on that creature instead. + addCard(Zone.BATTLEFIELD, playerA, "Conclave Mentor"); + // When Bond Beetle enters the battlefield, put a +1/+1 counter on target creature. + addCard(Zone.HAND, playerA, "Bond Beetle"); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bond Beetle"); + addTarget(playerA, "Conclave Mentor"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, "Conclave Mentor", CounterType.P1P1, 2); + + + } + + @Test + public void diesTrigger() { + // When Conclave Mentor dies, you gain life equal to its power. + addCard(Zone.BATTLEFIELD, playerA, "Conclave Mentor"); + addCard(Zone.HAND, playerA, "Terror"); + addCard(Zone.HAND, playerA, "Giant Growth"); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Giant Growth","Conclave Mentor"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Terror","Conclave Mentor"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 25); + + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ConspicuousSnoopTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ConspicuousSnoopTest.java new file mode 100644 index 0000000000..140b141c3d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/ConspicuousSnoopTest.java @@ -0,0 +1,65 @@ +package org.mage.test.cards.single.m21; + +import mage.abilities.ActivatedAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class ConspicuousSnoopTest extends CardTestPlayerBase { + + @Test + public void testTopCardLibraryRevealed(){ + // Play with the top card of your library revealed. + addCard(Zone.BATTLEFIELD, playerA, "Conspicuous Snoop"); + addCard(Zone.LIBRARY, playerA, "Swamp"); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertTopCardRevealed(playerA, true); + } + + @Test + public void castGoblinSpellsFromLibrary(){ + // You may cast Goblin spells from the top of your library. + addCard(Zone.BATTLEFIELD, playerA, "Conspicuous Snoop"); + + addCard(Zone.LIBRARY, playerA, "Goblin Lackey"); + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + // Whenever Goblin Lackey deals damage to a player, you may put a Goblin permanent card from your hand onto the battlefield. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Goblin Lackey"); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Goblin Lackey", 1); + + } + + @Test + public void hasActivatedAbilities(){ + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // Play with the top card of your library revealed. + // You may cast Goblin spells from the top of your library. + // As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card. + addCard(Zone.BATTLEFIELD, playerA, "Conspicuous Snoop"); + // {R}: Goblin Balloon Brigade gains flying until end of turn. + addCard(Zone.LIBRARY, playerA, "Goblin Balloon Brigade"); + skipInitShuffling(); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + + assertAllCommandsUsed(); + assertAbilityCount(playerA, "Conspicuous Snoop", ActivatedAbility.class, 3); // (2 X casts + gains flying ) + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/EnthrallingHoldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/EnthrallingHoldTest.java new file mode 100644 index 0000000000..a322395590 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/EnthrallingHoldTest.java @@ -0,0 +1,79 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class EnthrallingHoldTest extends CardTestPlayerBase { + + @Test + public void testTappedTarget_untapped_doesNotFizzle() { + // Traxos, Scourge of Kroog enters the battlefield tapped and doesn't untap during your untap step. + addCard(Zone.BATTLEFIELD, playerB, "Traxos, Scourge of Kroog"); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + /* + * {3}{U}{U} + * Enchant creature + * You can't choose an untapped creature as this spell's target as you cast it. + * You control enchanted creature. + */ + addCard(Zone.HAND, playerA, "Enthralling Hold"); + /* + * {U} + * You may tap or untap target artifact, creature, or land. + */ + addCard(Zone.HAND, playerA, "Twiddle"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Enthralling Hold", "Traxos, Scourge of Kroog"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Twiddle", "Traxos, Scourge of Kroog"); + + setChoice(playerA, "Yes"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerB, "Traxos, Scourge of Kroog", 0); + assertPermanentCount(playerA, "Traxos, Scourge of Kroog", 1); + assertPermanentCount(playerA, "Enthralling Hold", 1); + } + + @Test + public void testTappedTarget_becomesIllegal_fizzles() { + // Traxos, Scourge of Kroog enters the battlefield tapped and doesn't untap during your untap step. + addCard(Zone.BATTLEFIELD, playerB, "Traxos, Scourge of Kroog"); + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + /* + * {3}{U}{U} + * Enchant creature + * You can't choose an untapped creature as this spell's target as you cast it. + * You control enchanted creature. + */ + addCard(Zone.HAND, playerA, "Enthralling Hold"); + /* + * {1}{B} + * Destroy target nonblack creature + */ + addCard(Zone.HAND, playerA, "Doom Blade"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Enthralling Hold", "Traxos, Scourge of Kroog"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Doom Blade", "Traxos, Scourge of Kroog"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerB, "Traxos, Scourge of Kroog", 0); + assertPermanentCount(playerA, "Traxos, Scourge of Kroog", 0); + + assertGraveyardCount(playerB, "Traxos, Scourge of Kroog", 1); + assertGraveyardCount(playerA, "Enthralling Hold", 1); + assertGraveyardCount(playerA, "Doom Blade", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/IdolOfEnduranceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/IdolOfEnduranceTest.java new file mode 100644 index 0000000000..5c8956cdc2 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/IdolOfEnduranceTest.java @@ -0,0 +1,185 @@ +package org.mage.test.cards.single.m21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class IdolOfEnduranceTest extends CardTestPlayerBase { + + private static final String idol = "Idol of Endurance"; + private static final String dsnchnt = "Disenchant"; + private static final String key = "Voltaic Key"; + private static final String sqr = "Squire"; + private static final String glrskr = "Glory Seeker"; + private static final String pnhrmcn = "Panharmonicon"; + private static final String bnyrdwrm = "Boneyard Wurm"; + + @Test + public void testIdolCast() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerA, idol); + addCard(Zone.GRAVEYARD, playerA, sqr); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqr); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, sqr, 1); + } + + @Test + public void testIdolCast2() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerA, idol); + addCard(Zone.GRAVEYARD, playerA, sqr); + addCard(Zone.GRAVEYARD, playerA, glrskr); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}"); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqr); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, glrskr); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, sqr, 1); + assertPermanentCount(playerA, glrskr, 0); + } + + @Test + public void testIdolLeaves() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + addCard(Zone.HAND, playerA, idol); + addCard(Zone.HAND, playerA, dsnchnt); + addCard(Zone.GRAVEYARD, playerA, sqr); + addCard(Zone.GRAVEYARD, playerA, glrskr); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}"); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqr); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dsnchnt, idol); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, sqr, 1); + assertPermanentCount(playerA, idol, 0); + assertGraveyardCount(playerA, glrskr, 1); + } + + @Test + public void testIdolPanharmonicon() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + addCard(Zone.BATTLEFIELD, playerA, pnhrmcn); + addCard(Zone.BATTLEFIELD, playerA, bnyrdwrm, 2); + addCard(Zone.HAND, playerA, idol); + addCard(Zone.HAND, playerA, dsnchnt); + addCard(Zone.GRAVEYARD, playerA, sqr); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol); + + // Boneyard Wurm will die between triggers + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}"); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bnyrdwrm); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dsnchnt, idol); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, bnyrdwrm, 1); + assertPermanentCount(playerA, idol, 0); + assertGraveyardCount(playerA, sqr, 1); + assertGraveyardCount(playerA, bnyrdwrm, 1); + } + + @Test + public void testIdolTwice() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 8); + addCard(Zone.BATTLEFIELD, playerA, key); + addCard(Zone.HAND, playerA, idol); + addCard(Zone.GRAVEYARD, playerA, sqr); + addCard(Zone.GRAVEYARD, playerA, glrskr); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1},", idol); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqr); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, glrskr); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, sqr, 1); + assertPermanentCount(playerA, glrskr, 1); + } + + @Test + public void testIdolTwice2() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 8); + addCard(Zone.BATTLEFIELD, playerA, key); + addCard(Zone.HAND, playerA, idol); + addCard(Zone.GRAVEYARD, playerA, sqr); + addCard(Zone.GRAVEYARD, playerA, glrskr); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqr); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1},", idol); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, glrskr); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, sqr, 1); + assertPermanentCount(playerA, glrskr, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/CinderCloudTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/CinderCloudTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/CinderCloudTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/mir/CinderCloudTest.java index f083e2faac..bdfe96fca2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/CinderCloudTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/CinderCloudTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.mir; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/MisdirectionTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/MisdirectionTest.java index 489e3310eb..ad91b2bea1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/MisdirectionTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.mmq; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/StinkdrinkerBanditTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mor/StinkdrinkerBanditTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/StinkdrinkerBanditTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/mor/StinkdrinkerBanditTest.java index 2c505c9d44..db83ca8ae4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/StinkdrinkerBanditTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mor/StinkdrinkerBanditTest.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package org.mage.test.cards.single; +package org.mage.test.cards.single.mor; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ChaliceOfTheVoidTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mrd/ChaliceOfTheVoidTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ChaliceOfTheVoidTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/mrd/ChaliceOfTheVoidTest.java index ab2271c490..7ffc7f4263 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ChaliceOfTheVoidTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mrd/ChaliceOfTheVoidTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.mrd; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/CullingScalesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mrd/CullingScalesTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/CullingScalesTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/mrd/CullingScalesTest.java index 002fc3d92d..61006f8f70 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/CullingScalesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mrd/CullingScalesTest.java @@ -1,67 +1,67 @@ -package org.mage.test.cards.single; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author cg5 - */ -public class CullingScalesTest extends CardTestPlayerBase { - - @Test - public void testCullingScalesBasic() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain"); // CMC = 0, but not a nonland permanent - addCard(Zone.BATTLEFIELD, playerA, "Culling Scales"); // CMC = 3 - addCard(Zone.BATTLEFIELD, playerB, "Siege Rhino"); // CMC = 4 - setStopAt(1, PhaseStep.PRECOMBAT_MAIN); - execute(); - - assertPermanentCount(playerA, "Mountain", 1); - assertPermanentCount(playerA, "Culling Scales", 0); - assertPermanentCount(playerB, "Siege Rhino", 1); - } - - @Test - public void testCullingScalesPlusHexproof() { - addCard(Zone.BATTLEFIELD, playerB, "Bassara Tower Archer"); // CMC = 2, hexproof - addCard(Zone.BATTLEFIELD, playerA, "Culling Scales"); // CMC = 3 - addCard(Zone.BATTLEFIELD, playerB, "Siege Rhino"); // CMC = 4 - setStopAt(1, PhaseStep.PRECOMBAT_MAIN); - execute(); - - // Nothing happens since no valid targets - // (the only nonland permanent with the lowest CMC has hexproof) - assertPermanentCount(playerB, "Bassara Tower Archer", 1); - assertPermanentCount(playerA, "Culling Scales", 1); - assertPermanentCount(playerB, "Siege Rhino", 1); - } - - @Test - public void testCullingScalesFizzleByMakingLowerCostedPermanent() { - // Gatherer ruling: If the targeted permanent doesn't have the lowest converted mana cost - // when the ability resolves, the ability is countered and the permanent isn't destroyed. - - addCard(Zone.HAND, playerB, "Raise the Alarm"); // Make 2 tokens - addCard(Zone.BATTLEFIELD, playerB, "Elvish Visionary"); // CMC = 2 - addCard(Zone.BATTLEFIELD, playerB, "Plains", 5); - addCard(Zone.BATTLEFIELD, playerA, "Culling Scales"); // CMC = 3 - - // On upkeep Culling Scales targets Elvish Visionary - addTarget(playerA, "Elvish Visionary"); - - // Cast Raise the Alarm in response - castSpell(1, PhaseStep.UPKEEP, playerB, "Raise the Alarm", null, "At the beginning of"); - setStopAt(1, PhaseStep.PRECOMBAT_MAIN); - execute(); - - // Culling Scales trigger fizzles since the Visionary no longer has the lowest CMC - assertPermanentCount(playerB, "Soldier", 2); - assertPermanentCount(playerB, "Elvish Visionary", 1); - assertPermanentCount(playerB, "Plains", 5); - assertPermanentCount(playerA, "Culling Scales", 1); - } - -} +package org.mage.test.cards.single.mrd; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author cg5 + */ +public class CullingScalesTest extends CardTestPlayerBase { + + @Test + public void testCullingScalesBasic() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); // CMC = 0, but not a nonland permanent + addCard(Zone.BATTLEFIELD, playerA, "Culling Scales"); // CMC = 3 + addCard(Zone.BATTLEFIELD, playerB, "Siege Rhino"); // CMC = 4 + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Mountain", 1); + assertPermanentCount(playerA, "Culling Scales", 0); + assertPermanentCount(playerB, "Siege Rhino", 1); + } + + @Test + public void testCullingScalesPlusHexproof() { + addCard(Zone.BATTLEFIELD, playerB, "Bassara Tower Archer"); // CMC = 2, hexproof + addCard(Zone.BATTLEFIELD, playerA, "Culling Scales"); // CMC = 3 + addCard(Zone.BATTLEFIELD, playerB, "Siege Rhino"); // CMC = 4 + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + // Nothing happens since no valid targets + // (the only nonland permanent with the lowest CMC has hexproof) + assertPermanentCount(playerB, "Bassara Tower Archer", 1); + assertPermanentCount(playerA, "Culling Scales", 1); + assertPermanentCount(playerB, "Siege Rhino", 1); + } + + @Test + public void testCullingScalesFizzleByMakingLowerCostedPermanent() { + // Gatherer ruling: If the targeted permanent doesn't have the lowest converted mana cost + // when the ability resolves, the ability is countered and the permanent isn't destroyed. + + addCard(Zone.HAND, playerB, "Raise the Alarm"); // Make 2 tokens + addCard(Zone.BATTLEFIELD, playerB, "Elvish Visionary"); // CMC = 2 + addCard(Zone.BATTLEFIELD, playerB, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, "Culling Scales"); // CMC = 3 + + // On upkeep Culling Scales targets Elvish Visionary + addTarget(playerA, "Elvish Visionary"); + + // Cast Raise the Alarm in response + castSpell(1, PhaseStep.UPKEEP, playerB, "Raise the Alarm", null, "At the beginning of"); + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + // Culling Scales trigger fizzles since the Visionary no longer has the lowest CMC + assertPermanentCount(playerB, "Soldier", 2); + assertPermanentCount(playerB, "Elvish Visionary", 1); + assertPermanentCount(playerB, "Plains", 5); + assertPermanentCount(playerA, "Culling Scales", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/SoulFoundryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mrd/SoulFoundryTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/SoulFoundryTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/mrd/SoulFoundryTest.java index f361638948..cf25aa2ef3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/SoulFoundryTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mrd/SoulFoundryTest.java @@ -1,5 +1,5 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.mrd; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ParallaxWaveTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/nem/ParallaxWaveTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ParallaxWaveTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/nem/ParallaxWaveTest.java index a4cb1e5212..76dcebeec5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ParallaxWaveTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/nem/ParallaxWaveTest.java @@ -1,5 +1,5 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.nem; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/nph/NornsAnnexTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/nph/NornsAnnexTest.java new file mode 100644 index 0000000000..6a69f1c845 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/nph/NornsAnnexTest.java @@ -0,0 +1,34 @@ +package org.mage.test.cards.single.nph; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * Created by glerman on 23/6/15 + */ +public class NornsAnnexTest extends CardTestPlayerBase { + + @Test + public void testNornsAnnex() { + setStrictChooseMode(true); + + // {W/P} ({W/P} can be paid with either or 2 life.) + // Creatures can't attack you or a planeswalker you control unless their controller pays {W/P} for each of those creatures. + addCard(Zone.BATTLEFIELD, playerA, "Norn's Annex"); + addCard(Zone.BATTLEFIELD, playerB, "Brindle Boar"); // Creature 2/2 + + attack(2, playerB, "Brindle Boar", playerA); + setChoice(playerB, "Yes"); // Pay {W/P} to attack? + setChoice(playerB, "Yes"); // Pay 2 life instead of {W}? + + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 18); + assertLife(playerB, 18); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/TriumphOfTheHordesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/nph/TriumphOfTheHordesTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/TriumphOfTheHordesTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/nph/TriumphOfTheHordesTest.java index 8bedb58557..7e7ad85c17 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/TriumphOfTheHordesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/nph/TriumphOfTheHordesTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.nph; import mage.abilities.keyword.InfectAbility; import mage.constants.PhaseStep; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/GratuitousViolenceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ons/GratuitousViolenceTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/GratuitousViolenceTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/ons/GratuitousViolenceTest.java index 8d0994f42f..0254d52bf6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/GratuitousViolenceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ons/GratuitousViolenceTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.ons; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ChronozoaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/plc/ChronozoaTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ChronozoaTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/plc/ChronozoaTest.java index 0112beef2c..79dbc73c79 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ChronozoaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/plc/ChronozoaTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.plc; import java.util.List; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ArrogantBloodlordTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/roe/ArrogantBloodlordTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ArrogantBloodlordTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/roe/ArrogantBloodlordTest.java index e13955e7b9..2f704bd1af 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ArrogantBloodlordTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/roe/ArrogantBloodlordTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.roe; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/FinalPunishmentTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/scg/FinalPunishmentTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/FinalPunishmentTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/scg/FinalPunishmentTest.java index 87a6a2094d..e5733c453c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/FinalPunishmentTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/scg/FinalPunishmentTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.scg; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/FracturingGustTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/shm/FracturingGustTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/FracturingGustTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/shm/FracturingGustTest.java index fda0b5bdc4..16e420daed 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/FracturingGustTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/shm/FracturingGustTest.java @@ -1,5 +1,5 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.shm; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ContagionEngineTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/som/ContagionEngineTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ContagionEngineTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/som/ContagionEngineTest.java index fa4338d60b..057e173d28 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ContagionEngineTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/som/ContagionEngineTest.java @@ -1,5 +1,5 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.som; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/FlailingDrakeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/FlailingDrakeTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/FlailingDrakeTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/FlailingDrakeTest.java index 551a348123..f07db83ce6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/FlailingDrakeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/FlailingDrakeTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.tmp; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/SpiritMirrorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/SpiritMirrorTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/SpiritMirrorTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/SpiritMirrorTest.java index 1bf95ba515..dd8c0c09f5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/SpiritMirrorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/SpiritMirrorTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.tmp; import mage.constants.PhaseStep; import mage.constants.SubType; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/MultaniTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ulg/MultaniTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/MultaniTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/ulg/MultaniTest.java index efb17d444e..803d302912 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/MultaniTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ulg/MultaniTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.ulg; import mage.abilities.Ability; import mage.abilities.keyword.HasteAbility; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/UrzasIncubatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/usd/UrzasIncubatorTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/UrzasIncubatorTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/usd/UrzasIncubatorTest.java index add2333223..b980ae14f1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/UrzasIncubatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/usd/UrzasIncubatorTest.java @@ -1,5 +1,5 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.usd; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/NoRestForTheWickedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/usg/NoRestForTheWickedTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/NoRestForTheWickedTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/usg/NoRestForTheWickedTest.java index d8373052e7..3d5848c270 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/NoRestForTheWickedTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/usg/NoRestForTheWickedTest.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package org.mage.test.cards.single; +package org.mage.test.cards.single.usg; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/TerastodonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/wwk/TerastodonTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/TerastodonTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/wwk/TerastodonTest.java index a0ec36086e..22fd7ace2c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/TerastodonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/wwk/TerastodonTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.wwk; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/CobraTrapTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/CobraTrapTest.java similarity index 97% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/CobraTrapTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/zen/CobraTrapTest.java index a159e5c8d0..0fe5911725 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/CobraTrapTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/CobraTrapTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.zen; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/InfernoTrapTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/InfernoTrapTest.java similarity index 98% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/InfernoTrapTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/zen/InfernoTrapTest.java index 2c776ec530..7fb60eadf9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/InfernoTrapTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/InfernoTrapTest.java @@ -1,5 +1,5 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.zen; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/KalitasBloodchiefOfGhetTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/KalitasBloodchiefOfGhetTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/KalitasBloodchiefOfGhetTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/zen/KalitasBloodchiefOfGhetTest.java index ea8217863b..cf5f535c16 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/KalitasBloodchiefOfGhetTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/KalitasBloodchiefOfGhetTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single; +package org.mage.test.cards.single.zen; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithAsThoughManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithAsThoughManaTest.java new file mode 100644 index 0000000000..4330d9dd40 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithAsThoughManaTest.java @@ -0,0 +1,108 @@ +package org.mage.test.cards.split; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class CastSplitCardsWithAsThoughManaTest extends CardTestPlayerBase { + + @Test + public void test_AsThoughMana_Simple() { + // {1}{R} + // When Dire Fleet Daredevil enters the battlefield, exile target instant or sorcery card from an opponent’s graveyard. + // You may cast that card this turn, and you may spend mana as though it were mana of any type to cast that spell. + // If that card would be put into a graveyard this turn, exile it instead. + addCard(Zone.HAND, playerA, "Dire Fleet Daredevil", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + addCard(Zone.GRAVEYARD, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + // cast fleet + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dire Fleet Daredevil"); + addTarget(playerA, "Lightning Bolt"); + + // cast bolt with blue mana + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 3); + } + + @Test + public void test_AsThoughMana_Split_WearTear() { + // {1}{R} + // When Dire Fleet Daredevil enters the battlefield, exile target instant or sorcery card from an opponent’s graveyard. + // You may cast that card this turn, and you may spend mana as though it were mana of any type to cast that spell. + // If that card would be put into a graveyard this turn, exile it instead. + addCard(Zone.HAND, playerA, "Dire Fleet Daredevil", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + // Wear {1}{R} Destroy target artifact. + // Tear {W} Destroy target enchantment. + addCard(Zone.GRAVEYARD, playerB, "Wear // Tear", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact + addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact + + // cast fleet + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dire Fleet Daredevil"); + addTarget(playerA, "Wear // Tear"); + + // cast Wear with black mana + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear", "Bident of Thassa"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Bident of Thassa", 1); + assertPermanentCount(playerB, "Bow of Nylea", 1); + } + + @Test + public void test_AsThoughMana_Split_CatchRelease() { + // {1}{R} + // When Dire Fleet Daredevil enters the battlefield, exile target instant or sorcery card from an opponent’s graveyard. + // You may cast that card this turn, and you may spend mana as though it were mana of any type to cast that spell. + // If that card would be put into a graveyard this turn, exile it instead. + addCard(Zone.HAND, playerA, "Dire Fleet Daredevil", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + // Catch {1}{U}{R} Gain control of target permanent until end of turn. Untap it. It gains haste until end of turn. + // Release {4}{R}{W} Each player sacrifices an artifact, a creature, an enchantment, a land, and a planeswalker. + addCard(Zone.GRAVEYARD, playerB, "Catch // Release", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + + // cast fleet + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dire Fleet Daredevil"); + addTarget(playerA, "Catch // Release"); + + // cast Catch with black mana + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Catch", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Balduvian Bears", 1); + assertPermanentCount(playerB, "Balduvian Bears", 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java new file mode 100644 index 0000000000..0e01666d15 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java @@ -0,0 +1,315 @@ +package org.mage.test.cards.split; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class CastSplitCardsWithCostModificationTest extends CardTestPlayerBase { + + private void prepareReduceEffect(String cardNameToReduce, int reduceAmount) { + FilterCard filter = new FilterCard(); + filter.add(new NamePredicate(cardNameToReduce)); + addCustomCardWithAbility("reduce", playerA, new SimpleStaticAbility( + new SpellsCostReductionAllEffect(filter, reduceAmount)) + ); + } + + @Test + public void test_Playable_Left() { + // cost reduce for easy test + prepareReduceEffect("Armed", 3); + + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + //addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", false); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armed", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } + + @Test + public void test_Playable_Right() { + // cost reduce for easy test + prepareReduceEffect("Dangerous", 3); + + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + //addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", false); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dangerous", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } + + @Test + public void test_Playable_Fused_Left() { + // cost reduce for easy test + prepareReduceEffect("Armed", 4); + + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", false); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Armed // Dangerous"); + addTarget(playerA, "Balduvian Bears"); + addTarget(playerA, "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } + + @Test + public void test_Playable_Fused_Right() { + // cost reduce for easy test + prepareReduceEffect("Dangerous", 4); + + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true); // no reduced, but have basic lands ({G}{R}) + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true); + checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Armed // Dangerous"); + addTarget(playerA, "Balduvian Bears"); + addTarget(playerA, "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } + + @Test + public void test_CostReduction_Simple() { + // {2}{W}{U} + // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name. + // Your opponents can’t cast spells with the chosen name. + // Spells with the chosen name you cast cost {2} less to cast. + addCard(Zone.HAND, playerA, "Council of the Absolute", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + addCard(Zone.HAND, playerA, "Blastfire Bolt", 1); // {5}{R}, 5 damage + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6 - 2); // -2 for cost reduction + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + + // cast Council + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute"); + setChoice(playerA, "Blastfire Bolt"); + + // cast bolt + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blastfire Bolt", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Balduvian Bears", 1); + } + + @Test + public void test_CostReduction_SplitLeft() { + // {2}{W}{U} + // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name. + // Your opponents can’t cast spells with the chosen name. + // Spells with the chosen name you cast cost {2} less to cast. + addCard(Zone.HAND, playerA, "Council of the Absolute", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 - 1); // -1 from cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 2); // check not working right cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + // cast Council + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute"); + setChoice(playerA, "Armed"); + + // cast Armed + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", false); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armed", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } + + @Test + public void test_CostReduction_SplitRight() { + // {2}{W}{U} + // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name. + // Your opponents can’t cast spells with the chosen name. + // Spells with the chosen name you cast cost {2} less to cast. + addCard(Zone.HAND, playerA, "Council of the Absolute", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 2); // -2 from cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + // cast Council + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute"); + setChoice(playerA, "Dangerous"); + + // cast Dangerous + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", false); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dangerous", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } + + @Test + public void test_CostReduction_SplitFused_ReduceRight() { + // {2}{W}{U} + // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name. + // Your opponents can’t cast spells with the chosen name. + // Spells with the chosen name you cast cost {2} less to cast. + addCard(Zone.HAND, playerA, "Council of the Absolute", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); // no cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 2); // -2 from cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + // cast Council + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute"); + setChoice(playerA, "Dangerous"); + + // cast fused + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Armed // Dangerous"); + addTarget(playerA, "Balduvian Bears"); + addTarget(playerA, "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } + + @Test + public void test_CostReduction_SplitFused_ReduceLeft() { + // {2}{W}{U} + // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name. + // Your opponents can’t cast spells with the chosen name. + // Spells with the chosen name you cast cost {2} less to cast. + addCard(Zone.HAND, playerA, "Council of the Absolute", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 - 1); // -1 from cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 1); // -1 from cost reduction ON FUSED + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + // cast Council + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute"); + setChoice(playerA, "Armed"); + + // cast fused + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Armed // Dangerous"); + addTarget(playerA, "Balduvian Bears"); + addTarget(playerA, "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java new file mode 100644 index 0000000000..9c68cd0f05 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java @@ -0,0 +1,142 @@ +package org.mage.test.cards.split; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase { + + @Test + public void test_Flashback_Simple() { + // {1}{U} + // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. + addCard(Zone.HAND, playerA, "Snapcaster Mage", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + // add flashback + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage"); + addTarget(playerA, "Lightning Bolt"); + + // cast as flashback + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback", playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 3); + assertExileCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void test_Flashback_SplitLeft() { + // {1}{U} + // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. + addCard(Zone.HAND, playerA, "Snapcaster Mage", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + // Wear {1}{R} Destroy target artifact. + // Tear {W} Destroy target enchantment. + addCard(Zone.GRAVEYARD, playerA, "Wear // Tear", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact + addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact + + // add flashback + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage"); + addTarget(playerA, "Wear // Tear"); + + // cast as flashback + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {1}{R}", "Bident of Thassa"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Bident of Thassa", 1); + assertPermanentCount(playerB, "Bow of Nylea", 1); + assertExileCount(playerA, "Wear // Tear", 1); + } + + @Test + public void test_Flashback_SplitLeft_ZCCChanged() { + // {1}{U} + // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. + addCard(Zone.HAND, playerA, "Snapcaster Mage", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + // Wear {1}{R} Destroy target artifact. + // Tear {W} Destroy target enchantment. + addCard(Zone.HAND, playerA, "Wear // Tear", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 + 2); // for first Wear cast + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact + addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact + + // play card as normal to change ZCC for split cards (simulate GUI session - ZCC's was bugged and card's parts was able to have different ZCC) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear", "Bow of Nylea"); + + // add flashback + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage"); + addTarget(playerA, "Wear // Tear"); + + // cast as flashback + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {1}{R}", "Bident of Thassa"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Bident of Thassa", 1); + assertGraveyardCount(playerB, "Bow of Nylea", 1); + assertExileCount(playerA, "Wear // Tear", 1); + } + + @Test + public void test_Flashback_SplitRight() { + // {1}{U} + // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. + addCard(Zone.HAND, playerA, "Snapcaster Mage", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + // Wear {1}{R} Destroy target artifact. + // Tear {W} Destroy target enchantment. + addCard(Zone.GRAVEYARD, playerA, "Wear // Tear", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact + addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact + + // add flashback + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage"); + addTarget(playerA, "Wear // Tear"); + + // cast as flashback + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {W}", "Bident of Thassa"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Bident of Thassa", 1); + assertPermanentCount(playerB, "Bow of Nylea", 1); + assertExileCount(playerA, "Wear // Tear", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java index 034305cd91..1d1274381d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java @@ -80,12 +80,12 @@ public class CastSplitCardsWithFuseTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Absolute Grace"); // Enchantment addCard(Zone.BATTLEFIELD, playerB, "Juggernaut"); // Artifact - showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + // showAvailableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Wear // Tear"); addTarget(playerA, "Juggernaut"); addTarget(playerA, "Absolute Grace"); //playerA.addTarget("Absolute Grace"); - showBattlefield("after", 1, PhaseStep.BEGIN_COMBAT, playerB); + // showBattlefield("after", 1, PhaseStep.BEGIN_COMBAT, playerB); setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithSpliceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithSpliceTest.java new file mode 100644 index 0000000000..e9d2b3d5b1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithSpliceTest.java @@ -0,0 +1,99 @@ +package org.mage.test.cards.split; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class CastSplitCardsWithSpliceTest extends CardTestPlayerBase { + + // splice cost applies for one fused spell, not to every part + // https://github.com/magefree/mage/issues/6493 + + @Test + public void test_ThaliaGuardianOfThraben_CostModification_Fused() { + removeAllCardsFromHand(playerA); + + // Noncreature spells cost {1} more to cast. + addCard(Zone.BATTLEFIELD, playerA, "Thalia, Guardian of Thraben", 1); + // + // Wear {1}{R} Destroy target artifact. + // Tear {W} Destroy target enchantment. + addCard(Zone.HAND, playerA, "Wear // Tear", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 + 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact + addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact + + // cast fused + // old bug: https://github.com/magefree/mage/issues/6493 + // * getPlayable checks fused cost (+1 modification) + // * activate checks fused cost (+1 modification) andeach card's part (+1 modification) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Wear // Tear"); + addTarget(playerA, "Bident of Thassa"); + addTarget(playerA, "Bow of Nylea"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Wear // Tear", 1); + assertGraveyardCount(playerB, "Bident of Thassa", 1); + assertGraveyardCount(playerB, "Bow of Nylea", 1); + assertHandCount(playerA, 0); + + // must used all mana + assertTappedCount("Mountain", true, 3); + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_ThaliaGuardianOfThraben_CostModification_FusedWithSplice() { + removeAllCardsFromHand(playerA); + + // Noncreature spells cost {1} more to cast. + addCard(Zone.BATTLEFIELD, playerA, "Thalia, Guardian of Thraben", 1); + // + // Draw a card. + // Splice onto instant or sorcery {2}{U} + addCard(Zone.HAND, playerA, "Everdream", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); // for splice + // + // Wear {1}{R} Destroy target artifact. + // Tear {W} Destroy target enchantment. + addCard(Zone.HAND, playerA, "Wear // Tear", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 + 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact + addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact + + // cast fused with splice + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Wear // Tear"); + setChoice(playerA, "Yes"); // use splice + addTarget(playerA, "Everdream"); // card to splice + addTarget(playerA, "Bident of Thassa"); // target left + addTarget(playerA, "Bow of Nylea"); // target right + + // must used all mana + //showAvaileableMana("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Wear // Tear", 1); + assertGraveyardCount(playerB, "Bident of Thassa", 1); + assertGraveyardCount(playerB, "Bow of Nylea", 1); + assertHandCount(playerA, 2); // splice card + draw effect from spliced + + // must used all mana + assertTappedCount("Island", true, 3); + assertTappedCount("Mountain", true, 3); + assertTappedCount("Plains", true, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java new file mode 100644 index 0000000000..605de74d77 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java @@ -0,0 +1,89 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.targets; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class TargetRestrictionsTest extends CardTestPlayerBase { + + @Test + public void testDreamLeashWorks() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + // Enchant permanent + // You can't choose an untapped permanent as Dream Leash's target as you cast Dream Leash. + // You control enchanted permanent. + addCard(Zone.HAND, playerA, "Dream Leash"); // Enchantment {3}{U}{U} + + // Tap target creature. It doesn't untap during its controller's next untap step. + addCard(Zone.HAND, playerA, "Take into Custody"); // Instant {U} + + addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Take into Custody", "Sejiri Merfolk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dream Leash", "Sejiri Merfolk"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Dream Leash", 1); + assertPermanentCount(playerA, "Sejiri Merfolk", 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + } + + @Test + public void testDreamLeashUntappingAsResponseToCast() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + // Enchant permanent + // You can't choose an untapped permanent as Dream Leash's target as you cast Dream Leash. + // You control enchanted permanent. + addCard(Zone.HAND, playerA, "Dream Leash"); // Enchantment {3}{U}{U} + + // Tap target creature. It doesn't untap during its controller's next untap step. + addCard(Zone.HAND, playerA, "Take into Custody"); // Instant {U} + + // Untap target creature. It gets +1/+3 until end of turn. + addCard(Zone.HAND, playerB, "Ornamental Courage"); // Instant {G} + + addCard(Zone.BATTLEFIELD, playerB, "Forest"); + addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Take into Custody", "Sejiri Merfolk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dream Leash", "Sejiri Merfolk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Ornamental Courage", "Sejiri Merfolk", "Dream Leash"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + + assertGraveyardCount(playerA, "Take into Custody", 1); + assertGraveyardCount(playerB, "Ornamental Courage", 1); + assertPermanentCount(playerA, "Dream Leash", 1); + assertPermanentCount(playerA, "Sejiri Merfolk", 1); + assertPowerToughness(playerA, "Sejiri Merfolk", 2, 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/text/WrennAndSixTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/text/WrennAndSixTest.java index b4a26f3a17..21a30a73ca 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/text/WrennAndSixTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/text/WrennAndSixTest.java @@ -1,17 +1,17 @@ -package org.mage.test.cards.text; - -import mage.cards.Card; -import mage.cards.repository.CardRepository; -import org.junit.Assert; -import org.junit.Test; - -public class WrennAndSixTest { - - @Test - public void testFirstLoyaltyAbilityRulesText() { - Card wrennAndSix = CardRepository.instance.findCard("Wrenn and Six").getCard(); - String firstLoyaltyAbilityRulesText = wrennAndSix.getRules().get(0); - - Assert.assertEquals(firstLoyaltyAbilityRulesText, "+1: Return up to one target land card from your graveyard to your hand."); - } -} +package org.mage.test.cards.text; + +import mage.cards.Card; +import mage.cards.repository.CardRepository; +import org.junit.Assert; +import org.junit.Test; + +public class WrennAndSixTest { + + @Test + public void testFirstLoyaltyAbilityRulesText() { + Card wrennAndSix = CardRepository.instance.findCard("Wrenn and Six").getCard(); + String firstLoyaltyAbilityRulesText = wrennAndSix.getRules().get(0); + + Assert.assertEquals(firstLoyaltyAbilityRulesText, "+1: Return up to one target land card from your graveyard to your hand."); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BaralChiefOfComplianceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BaralChiefOfComplianceTest.java index a1c94327b5..a885c6bbc2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BaralChiefOfComplianceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BaralChiefOfComplianceTest.java @@ -1,60 +1,60 @@ - -package org.mage.test.cards.triggers; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class BaralChiefOfComplianceTest extends CardTestPlayerBase { - - /** - * In the following scenario baral's loot ability isn't triggered. - * - * Baral, Chief of Compliance in my graveyard, opponent had a creature in - * the stack. - * - * I cast Ojutai's Command with modes: Return creature from graveyard to - * battlefield (targeting Baral), and counter their creature spell. Ojutai's - * command resolves, do the modes in the order they appear in the card. I - * put Baral onto the battlefield, and he is around to witness the creature - * be counted. Baral's loot ability isn't triggered. - * - */ - @Test - public void testBaralTrigger() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); - addCard(Zone.HAND, playerA, "Silvercoat Lion"); - - addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - // Instant and sorcery spells you cast cost {1} less to cast. - // Whenever a spell or ability you control counters a spell, you may draw a card. If you do, discard a card. - addCard(Zone.GRAVEYARD, playerB, "Baral, Chief of Compliance"); // Creature {1}{U} - // Choose two - - // Return target creature card with converted mana cost 2 or less from your graveyard to the battlefield; - // or You gain 4 life; - // or Counter target creature spell; - // or Draw a card - addCard(Zone.HAND, playerB, "Ojutai's Command"); // Instant {2}{W}{U} - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Ojutai's Command", "mode=1Baral, Chief of Compliance^mode=3Silvercoat Lion"); - setModeChoice(playerB, "1"); - setModeChoice(playerB, "3"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Silvercoat Lion", 1); - - assertGraveyardCount(playerB, "Ojutai's Command", 1); - assertGraveyardCount(playerB, 2); - assertPermanentCount(playerB, "Baral, Chief of Compliance", 1); - } - -} + +package org.mage.test.cards.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BaralChiefOfComplianceTest extends CardTestPlayerBase { + + /** + * In the following scenario baral's loot ability isn't triggered. + * + * Baral, Chief of Compliance in my graveyard, opponent had a creature in + * the stack. + * + * I cast Ojutai's Command with modes: Return creature from graveyard to + * battlefield (targeting Baral), and counter their creature spell. Ojutai's + * command resolves, do the modes in the order they appear in the card. I + * put Baral onto the battlefield, and he is around to witness the creature + * be counted. Baral's loot ability isn't triggered. + * + */ + @Test + public void testBaralTrigger() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + // Instant and sorcery spells you cast cost {1} less to cast. + // Whenever a spell or ability you control counters a spell, you may draw a card. If you do, discard a card. + addCard(Zone.GRAVEYARD, playerB, "Baral, Chief of Compliance"); // Creature {1}{U} + // Choose two - + // Return target creature card with converted mana cost 2 or less from your graveyard to the battlefield; + // or You gain 4 life; + // or Counter target creature spell; + // or Draw a card + addCard(Zone.HAND, playerB, "Ojutai's Command"); // Instant {2}{W}{U} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Ojutai's Command", "mode=1Baral, Chief of Compliance^mode=3Silvercoat Lion"); + setModeChoice(playerB, "1"); + setModeChoice(playerB, "3"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + + assertGraveyardCount(playerB, "Ojutai's Command", 1); + assertGraveyardCount(playerB, 2); + assertPermanentCount(playerB, "Baral, Chief of Compliance", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DivineVisitationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DivineVisitationTest.java new file mode 100644 index 0000000000..bc915a4c90 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DivineVisitationTest.java @@ -0,0 +1,81 @@ +package org.mage.test.cards.triggers; + +import mage.ObjectColor; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author luziferius + */ +public class DivineVisitationTest extends CardTestPlayerBase { + + /** + * Test case for issue #6349. Divine Visitation should not replace Treasure tokens created by Smothering Tithe + */ + @Test + public void testDivineVisitationDoesNotReplaceNoncreatureTokens() { + + // If one or more creature tokens would be created under your control, + // that many 4/4 white Angel creature tokens with flying and vigilance are created instead. + addCard(Zone.BATTLEFIELD, playerA, "Divine Visitation"); + // Whenever an opponent draws a card, that player may pay {2}. + // If the player doesn’t, you create a Treasure token. + // (It’s an artifact with “{T}, Sacrifice this artifact: Add one mana of any color.”) + addCard(Zone.BATTLEFIELD, playerA, "Smothering Tithe"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // Target player draws three cards. + addCard(Zone.HAND, playerA, "Ancestral Recall"); // {U} + // Let the opponent draw + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ancestral Recall", playerB); + setChoice(playerA, "Whenever an opponent draws a card", 2); // choose order of triggers + setChoice(playerB, "No", 3); // Decline to pay 2 + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerB, 3); + assertPermanentCount(playerA, "Treasure", 3); + assertType("Treasure", CardType.ARTIFACT, SubType.TREASURE); + assertNotType("Treasure", CardType.CREATURE); + assertNotSubtype("Treasure", SubType.ANGEL); + assertPermanentCount(playerA, "Angel", 0); + assertPermanentCount(playerA, 6); + assertGraveyardCount(playerA, 1); + } + + @Test + public void testDivineVisitationReplacesCreatureTokens() { + + // If one or more creature tokens would be created under your control, + // that many 4/4 white Angel creature tokens with flying and vigilance are created instead. + addCard(Zone.BATTLEFIELD, playerA, "Divine Visitation"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + // Create two 1/1 red Goblin creature tokens. + addCard(Zone.HAND, playerA, "Dragon Fodder"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dragon Fodder"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, 1); + assertPermanentCount(playerA, 5); + assertPermanentCount(playerA, "Goblin", 0); + assertPermanentCount(playerA, "Angel", 2); + assertType("Angel", CardType.CREATURE, SubType.ANGEL); + assertColor(playerA, "Angel", ObjectColor.WHITE, true); + assertColor(playerA, "Angel", ObjectColor.RED, false); + assertPowerToughness(playerA, "Angel", 4,4); + assertNotSubtype("Angel", SubType.GOBLIN); + + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java new file mode 100644 index 0000000000..5ab1c0c851 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java @@ -0,0 +1,92 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class GripOfChaosTest extends CardTestPlayerBase { + + /** + * From #6344 I just had a game where we had an interaction between Grip of + * Chaos, Felidar Guardian, and Panharmonicon in which the cloned Felidar + * trigger fizzled with valid targets on field because Grip retargeted that + * trigger onto Felidar itself, which isn't a valid target. Grip of Chaos + * specifically states it only chooses from valid targets when retargeting, + * so this is a bug somewhere in that interaction, though whether it only + * happens with cloned triggers or if there's a bad interaction between Grip + * and Felidar itself isn't clear. + */ + @Test + public void noValidTargetsTest() { + // Whenever a spell or ability is put onto the stack, if it has a single target, reselect its target at random. + addCard(Zone.BATTLEFIELD, playerB, "Grip of Chaos", 1); // Enchantment + + // If an artifact or creature entering the battlefield causes a triggered ability + // of a permanent you control to trigger, that ability triggers an additional time. + addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon", 1); // Artifact + + // When Felidar Guardian enters the battlefield, you may exile another target permanent you control, + // then return that card to the battlefield under its owner's control. + addCard(Zone.HAND, playerA, "Felidar Guardian"); // Creature {3}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Felidar Guardian"); + + setChoice(playerA, "When "); // Select order of Felidar trigger + + setChoice(playerB, "Whenever "); // Select order of Grip of Chaos trigger + + setChoice(playerA, "Yes"); // use for the original trigger of Felidar Guardian + setChoice(playerA, "Yes"); // use for the copied trigger of Felidar Guardian + + addTarget(playerA, "Forest"); + addTarget(playerA, "Mountain"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + execute(); + + assertPermanentCount(playerA, "Felidar Guardian", 1); + + int zcc = 0; + zcc += getPermanent("Mountain").getZoneChangeCounter(currentGame); + zcc += getPermanent("Forest").getZoneChangeCounter(currentGame); + zcc += getPermanent("Swamp").getZoneChangeCounter(currentGame); + zcc += getPermanent("Plains").getZoneChangeCounter(currentGame); + zcc += getPermanent("Panharmonicon").getZoneChangeCounter(currentGame); + // If both select the same permanent to exile, one spell fizzles so zcc == 7 otherwise 9 + if (zcc != 7) { + Assert.assertEquals("Sum of zone change counter should be 9", 9, zcc); + assertAllCommandsUsed(); // creates error if the random targets do select the same target twice zcc is 7 then the second trigger has an invalid target + } + } + + /** + * Maybe also good situation to create an test for 9/20/2016 Panharmonicon + * + * In some cases involving linked abilities, an ability requires information + * about “the exiled card.” When this happens, the ability gets multiple + * answers. If these answers are being used to determine the value of a + * variable, the sum is used. For example, if Elite Arcanist’s + * enters-the-battlefield ability triggers twice, two cards are exiled. The + * value of X in the activation cost of Elite Arcanist’s other ability is + * the sum of the two cards’ converted mana costs. As the ability resolves, + * you create copies of both cards and can cast none, one, or both of the + * copies in any order. + */ +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/MasterOfCrueltiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/MasterOfCrueltiesTest.java index eb304448a9..0f9f89ba29 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/MasterOfCrueltiesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/MasterOfCrueltiesTest.java @@ -1,54 +1,54 @@ - -package org.mage.test.cards.triggers; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class MasterOfCrueltiesTest extends CardTestPlayerBase { - - /* - The ability of an Alesha-resurrected Master of Cruelties triggered in an EDH game despite being blocked by a creature. - */ - @Test - public void testMasterWasAleshaAnimated() { - // First strike - // Whenever Alesha, Who Smiles at Death attacks, you may pay {W/B}{W/B}. If you do, return target creature card with power 2 or less from your graveyard to the battlefield tapped and attacking. - addCard(Zone.BATTLEFIELD, playerB, "Alesha, Who Smiles at Death"); // 3/2 - - // First strike - // Deathtouch - // Master of Cruelties can only attack alone. - // Whenever Master of Cruelties attacks a player and isn't blocked, that player's life total becomes 1. Master of Cruelties assigns no combat damage this combat. - addCard(Zone.GRAVEYARD, playerB, "Master of Cruelties"); // 1/4 - - addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); - - attack(2, playerB, "Alesha, Who Smiles at Death"); - setChoice(playerB, "Yes"); - addTarget(playerB, "Master of Cruelties"); - - block(2, playerA, "Silvercoat Lion", "Master of Cruelties"); - - setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertPermanentCount(playerB, "Master of Cruelties", 1); - assertTapped("Master of Cruelties", true); - assertTapped("Alesha, Who Smiles at Death", true); - - assertLife(playerA, 17); - assertLife(playerB, 20); - - assertGraveyardCount(playerA, "Silvercoat Lion", 1); - - } - -} + +package org.mage.test.cards.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class MasterOfCrueltiesTest extends CardTestPlayerBase { + + /* + The ability of an Alesha-resurrected Master of Cruelties triggered in an EDH game despite being blocked by a creature. + */ + @Test + public void testMasterWasAleshaAnimated() { + // First strike + // Whenever Alesha, Who Smiles at Death attacks, you may pay {W/B}{W/B}. If you do, return target creature card with power 2 or less from your graveyard to the battlefield tapped and attacking. + addCard(Zone.BATTLEFIELD, playerB, "Alesha, Who Smiles at Death"); // 3/2 + + // First strike + // Deathtouch + // Master of Cruelties can only attack alone. + // Whenever Master of Cruelties attacks a player and isn't blocked, that player's life total becomes 1. Master of Cruelties assigns no combat damage this combat. + addCard(Zone.GRAVEYARD, playerB, "Master of Cruelties"); // 1/4 + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + attack(2, playerB, "Alesha, Who Smiles at Death"); + setChoice(playerB, "Yes"); + addTarget(playerB, "Master of Cruelties"); + + block(2, playerA, "Silvercoat Lion", "Master of Cruelties"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerB, "Master of Cruelties", 1); + assertTapped("Master of Cruelties", true); + assertTapped("Alesha, Who Smiles at Death", true); + + assertLife(playerA, 17); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java index 798f0b88a6..8550b9c081 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/WorldgorgerDragonTest.java @@ -67,6 +67,8 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase { */ @Test public void testWithAnimateDead() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); // When Worldgorger Dragon enters the battlefield, exile all other permanents you control. @@ -79,6 +81,7 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase { // Enchanted creature gets -1/-0. addCard(Zone.HAND, playerA, "Animate Dead"); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Instant {X}{R}{R} // Volcanic Geyser deals X damage to any target. addCard(Zone.HAND, playerA, "Volcanic Geyser", 1); @@ -86,44 +89,61 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Staunch Defenders", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Worldgorger Dragon"); + setChoice(playerA, "Worldgorger Dragon"); + setChoice(playerA, "When {this} enters the battlefield, if it's"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + + setChoice(playerA, "Worldgorger Dragon"); + setChoice(playerA, "When {this} enters the battlefield, if it's"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + + setChoice(playerA, "Worldgorger Dragon"); + setChoice(playerA, "When {this} enters the battlefield, if it's"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + + setChoice(playerA, "Worldgorger Dragon"); + setChoice(playerA, "When {this} enters the battlefield, if it's"); + setChoice(playerA, "No"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + + setChoice(playerA, "Worldgorger Dragon"); setChoice(playerA, "When {this} enters the battlefield, if it's"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + + setChoice(playerA, "Worldgorger Dragon"); + setChoice(playerA, "When {this} enters the battlefield, if it's"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + + setChoice(playerA, "Worldgorger Dragon"); + setChoice(playerA, "When {this} enters the battlefield, if it's"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB, 22); setChoice(playerA, "X=20"); - // not an infinite loop resulting in a draw - for (int i = 0; i < 6; ++i) - { - setChoice(playerA, "No"); - setChoice(playerB, "No"); - } - setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertLife(playerA, 44); assertLife(playerB, 0); @@ -132,7 +152,7 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase { } /** - * v9: Worldgorger Dragon + Animate Dead is still acting up (yey complex + * v9: Worldgorger Dragon + Animate Dead is still acting up (yet complex * rules interactions!). The first time you return Animate Dead from * Worldgorger's exile, it works like it's supposed to. You have to pick a * creature, and it brings it back. But if you pick Worldgorger Dragon @@ -149,6 +169,8 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase { */ @Test public void testWithAnimateDeadDifferentTargets() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); // When Worldgorger Dragon enters the battlefield, exile all other permanents you control. @@ -161,9 +183,9 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase { // under your control and attach Animate Dead to it. When Animate Dead leaves the battlefield, that creature's controller sacrifices it. addCard(Zone.HAND, playerA, "Animate Dead"); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - // Instant {X}{R}{R} + // Volcanic Geyser deals X damage to any target. - addCard(Zone.HAND, playerA, "Volcanic Geyser", 1); + addCard(Zone.HAND, playerA, "Volcanic Geyser", 1);// Instant {X}{R}{R} // When Staunch Defenders enters the battlefield, you gain 4 life. addCard(Zone.BATTLEFIELD, playerA, "Staunch Defenders", 1); @@ -176,13 +198,15 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase { activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); setChoice(playerA, "Worldgorger Dragon"); + setChoice(playerA, "When {this} enters the battlefield, if it's"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); - setChoice(playerA, "Silvercoat Lion"); + setChoice(playerA, "When {this} enters the battlefield, if it's"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); @@ -193,6 +217,8 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); + assertGraveyardCount(playerA, "Volcanic Geyser", 1); assertGraveyardCount(playerA, "Worldgorger Dragon", 1); assertPermanentCount(playerA, "Silvercoat Lion", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SyrCarahTheBoldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SyrCarahTheBoldTest.java index a8d5eb50e5..5962c91cc2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SyrCarahTheBoldTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SyrCarahTheBoldTest.java @@ -55,32 +55,31 @@ public class SyrCarahTheBoldTest extends CardTestPlayerBase { public void test_DamageWithCopyAbility() { removeAllCardsFromLibrary(playerA); removeAllCardsFromHand(playerA); + // When Syr Carah, the Bold or an instant or sorcery spell you control deals damage to a player, exile the top card of your library. You may play that card this turn. // {T}: Syr Carah deals 1 damage to any target. addCard(Zone.BATTLEFIELD, playerA, "Syr Carah, the Bold", 1); - // - addCard(Zone.LIBRARY, playerA, "Swamp", 5); - // - // {T}: Embermage Goblin deals 1 damage to any target. - addCard(Zone.BATTLEFIELD, playerB, "Embermage Goblin", 1); + addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 2); // // Whenever an ability of equipped creature is activated, if it isn't a mana ability, copy that ability. You may choose new targets for the copy. // Equip 3 - addCard(Zone.BATTLEFIELD, playerB, "Illusionist's Bracers", 1); - addCard(Zone.BATTLEFIELD, playerB, "Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Illusionist's Bracers", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); - // equip to copy abilities - // showAvaileableAbilities("abils", 2, PhaseStep.PRECOMBAT_MAIN, playerB); - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {3}", "Embermage Goblin"); - setChoice(playerB, "No"); // no new target + // prepare equip + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {3}", "Syr Carah, the Bold"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkExileCount("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0); - // 3 - 2x damage (copy), but no trigger - // java.lang.ClassCastException: mage.game.stack.StackAbility cannot be cast to mage.game.stack.Spell - activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{T}: {source} deals", playerA); - checkLife("damage 3", 2, PhaseStep.END_TURN, playerA, 20 - 1 - 1); + // activate damage - 2x damage with copy + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", playerB); + setChoice(playerA, "No"); // no new target for copy + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkLife("damage 2", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 1 - 1); + checkExileCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 2); setStrictChooseMode(true); - setStopAt(3, PhaseStep.END_TURN); + setStopAt(1, PhaseStep.END_TURN); execute(); assertAllCommandsUsed(); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/GlimpseOfNatureTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/GlimpseOfNatureTest.java index 135a725f24..fbc5a3b29d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/GlimpseOfNatureTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/GlimpseOfNatureTest.java @@ -1,49 +1,49 @@ - -package org.mage.test.cards.triggers.delayed; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class GlimpseOfNatureTest extends CardTestPlayerBase { - - /** - * Glimpse of Nature triggers do not draw a card. - */ - @Test - public void testCardsAreDrawnFromCreatureCasting() { - // Whenever you cast a creature spell this turn, draw a card. - addCard(Zone.HAND, playerA, "Glimpse of Nature", 1);// Sorcery {G} - addCard(Zone.HAND, playerA, "Silvercoat Lion", 2); - - addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); - - // Flash (You may cast this spell any time you could cast an instant.) - // First strike (This creature deals combat damage before creatures without first strike.) - addCard(Zone.HAND, playerB, "Benalish Knight", 1); // Creature {2}{W} - addCard(Zone.BATTLEFIELD, playerB, "Plains", 3); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Glimpse of Nature"); - - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Benalish Knight"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - assertGraveyardCount(playerA, "Glimpse of Nature", 1); - assertPermanentCount(playerA, "Silvercoat Lion", 2); - assertPermanentCount(playerB, "Benalish Knight", 1); - - assertHandCount(playerA, 2); - assertHandCount(playerB, 0); - } - -} + +package org.mage.test.cards.triggers.delayed; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class GlimpseOfNatureTest extends CardTestPlayerBase { + + /** + * Glimpse of Nature triggers do not draw a card. + */ + @Test + public void testCardsAreDrawnFromCreatureCasting() { + // Whenever you cast a creature spell this turn, draw a card. + addCard(Zone.HAND, playerA, "Glimpse of Nature", 1);// Sorcery {G} + addCard(Zone.HAND, playerA, "Silvercoat Lion", 2); + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + // Flash (You may cast this spell any time you could cast an instant.) + // First strike (This creature deals combat damage before creatures without first strike.) + addCard(Zone.HAND, playerB, "Benalish Knight", 1); // Creature {2}{W} + addCard(Zone.BATTLEFIELD, playerB, "Plains", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Glimpse of Nature"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Benalish Knight"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Glimpse of Nature", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 2); + assertPermanentCount(playerB, "Benalish Knight", 1); + + assertHandCount(playerA, 2); + assertHandCount(playerB, 0); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BloodCultistTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BloodCultistTest.java index 7be406f652..74b40da86f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BloodCultistTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BloodCultistTest.java @@ -1,140 +1,140 @@ - -package org.mage.test.cards.triggers.dies; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class BloodCultistTest extends CardTestPlayerBase { - - @Test - public void testDiedFromDirectDamage() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - // {T}: Blood Cultist deals 1 damage to target creature. - // Whenever a creature dealt damage by Blood Cultist this turn dies, put a +1/+1 counter on Blood Cultist. - addCard(Zone.HAND, playerA, "Blood Cultist", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Devilthorn Fox", 1); // 3/1 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist"); - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Devilthorn Fox"); - - setStopAt(3, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerB, "Devilthorn Fox", 1); - assertPowerToughness(playerA, "Blood Cultist", 2, 2); - } - - @Test - public void testDiedFromCombatDamage() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - // {T}: Blood Cultist deals 1 damage to target creature. - // Whenever a creature dealt damage by Blood Cultist this turn dies, put a +1/+1 counter on Blood Cultist. - addCard(Zone.HAND, playerA, "Blood Cultist", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Devilthorn Fox", 1); // 3/1 - // Lifelink - // {2}, Sacrifice a creature: Put a +1/+1 counter on each Vampire you control. - addCard(Zone.BATTLEFIELD, playerB, "Indulgent Aristocrat", 1); // 1/1 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist"); - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Devilthorn Fox"); - - attack(5, playerA, "Blood Cultist"); - block(5, playerB, "Indulgent Aristocrat", "Blood Cultist"); - - setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertGraveyardCount(playerB, "Devilthorn Fox", 1); - assertGraveyardCount(playerB, "Indulgent Aristocrat", 1); - assertPowerToughness(playerA, "Blood Cultist", 3, 3); - } - - @Test - public void testDiedFromCombatAfterDirectDamageDamage() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); - // {T}: Blood Cultist deals 1 damage to target creature. - // Whenever a creature dealt damage by Blood Cultist this turn dies, put a +1/+1 counter on Blood Cultist. - addCard(Zone.HAND, playerA, "Blood Cultist", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Devilthorn Fox", 1); // 3/1 - // Shambling Ghoul enters the battlefield tapped. - addCard(Zone.BATTLEFIELD, playerB, "Shambling Ghoul", 1); // 2/3 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist"); - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Devilthorn Fox"); - activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Shambling Ghoul"); - attack(5, playerA, "Silvercoat Lion"); - block(5, playerB, "Shambling Ghoul", "Silvercoat Lion"); - - setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertGraveyardCount(playerB, "Devilthorn Fox", 1); - assertGraveyardCount(playerB, "Shambling Ghoul", 1); - assertGraveyardCount(playerA, "Silvercoat Lion", 1); - assertPowerToughness(playerA, "Blood Cultist", 3, 3); - } - - @Test - public void testDiedByDirectDamageAfterCombatDamage() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); - // {T}: Blood Cultist deals 1 damage to target creature. - // Whenever a creature dealt damage by Blood Cultist this turn dies, put a +1/+1 counter on Blood Cultist. - addCard(Zone.HAND, playerA, "Blood Cultist", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Devilthorn Fox", 1); // 3/1 - // Shambling Ghoul enters the battlefield tapped. - addCard(Zone.BATTLEFIELD, playerB, "Shambling Ghoul", 1); // 2/3 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist"); - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Devilthorn Fox"); - activateAbility(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: {source} deals", "Shambling Ghoul"); - attack(5, playerA, "Silvercoat Lion"); - block(5, playerB, "Shambling Ghoul", "Silvercoat Lion"); - - setStopAt(5, PhaseStep.END_TURN); - execute(); - - assertGraveyardCount(playerB, "Devilthorn Fox", 1); - assertGraveyardCount(playerB, "Shambling Ghoul", 1); - assertGraveyardCount(playerA, "Silvercoat Lion", 1); - assertPowerToughness(playerA, "Blood Cultist", 3, 3); - } - - @Test - public void testDiedByTwoDirectDamage() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - // {T}: Blood Cultist deals 1 damage to target creature. - // Whenever a creature dealt damage by Blood Cultist this turn dies, put a +1/+1 counter on Blood Cultist. - addCard(Zone.HAND, playerA, "Blood Cultist", 1); - addCard(Zone.HAND, playerA, "Lightning Bolt", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Shambling Ghoul", 1); // 2/3 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist"); - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Shambling Ghoul"); - castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Shambling Ghoul"); - - setStopAt(3, PhaseStep.END_TURN); - execute(); - - assertGraveyardCount(playerB, "Shambling Ghoul", 1); - assertGraveyardCount(playerA, "Lightning Bolt", 1); - assertPowerToughness(playerA, "Blood Cultist", 2, 2); - } -} + +package org.mage.test.cards.triggers.dies; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BloodCultistTest extends CardTestPlayerBase { + + @Test + public void testDiedFromDirectDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // {T}: Blood Cultist deals 1 damage to target creature. + // Whenever a creature dealt damage by Blood Cultist this turn dies, put a +1/+1 counter on Blood Cultist. + addCard(Zone.HAND, playerA, "Blood Cultist", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Devilthorn Fox", 1); // 3/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Devilthorn Fox"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, "Devilthorn Fox", 1); + assertPowerToughness(playerA, "Blood Cultist", 2, 2); + } + + @Test + public void testDiedFromCombatDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // {T}: Blood Cultist deals 1 damage to target creature. + // Whenever a creature dealt damage by Blood Cultist this turn dies, put a +1/+1 counter on Blood Cultist. + addCard(Zone.HAND, playerA, "Blood Cultist", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Devilthorn Fox", 1); // 3/1 + // Lifelink + // {2}, Sacrifice a creature: Put a +1/+1 counter on each Vampire you control. + addCard(Zone.BATTLEFIELD, playerB, "Indulgent Aristocrat", 1); // 1/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Devilthorn Fox"); + + attack(5, playerA, "Blood Cultist"); + block(5, playerB, "Indulgent Aristocrat", "Blood Cultist"); + + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerB, "Devilthorn Fox", 1); + assertGraveyardCount(playerB, "Indulgent Aristocrat", 1); + assertPowerToughness(playerA, "Blood Cultist", 3, 3); + } + + @Test + public void testDiedFromCombatAfterDirectDamageDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + // {T}: Blood Cultist deals 1 damage to target creature. + // Whenever a creature dealt damage by Blood Cultist this turn dies, put a +1/+1 counter on Blood Cultist. + addCard(Zone.HAND, playerA, "Blood Cultist", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Devilthorn Fox", 1); // 3/1 + // Shambling Ghoul enters the battlefield tapped. + addCard(Zone.BATTLEFIELD, playerB, "Shambling Ghoul", 1); // 2/3 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Devilthorn Fox"); + activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Shambling Ghoul"); + attack(5, playerA, "Silvercoat Lion"); + block(5, playerB, "Shambling Ghoul", "Silvercoat Lion"); + + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerB, "Devilthorn Fox", 1); + assertGraveyardCount(playerB, "Shambling Ghoul", 1); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Blood Cultist", 3, 3); + } + + @Test + public void testDiedByDirectDamageAfterCombatDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + // {T}: Blood Cultist deals 1 damage to target creature. + // Whenever a creature dealt damage by Blood Cultist this turn dies, put a +1/+1 counter on Blood Cultist. + addCard(Zone.HAND, playerA, "Blood Cultist", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Devilthorn Fox", 1); // 3/1 + // Shambling Ghoul enters the battlefield tapped. + addCard(Zone.BATTLEFIELD, playerB, "Shambling Ghoul", 1); // 2/3 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Devilthorn Fox"); + activateAbility(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: {source} deals", "Shambling Ghoul"); + attack(5, playerA, "Silvercoat Lion"); + block(5, playerB, "Shambling Ghoul", "Silvercoat Lion"); + + setStopAt(5, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Devilthorn Fox", 1); + assertGraveyardCount(playerB, "Shambling Ghoul", 1); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Blood Cultist", 3, 3); + } + + @Test + public void testDiedByTwoDirectDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // {T}: Blood Cultist deals 1 damage to target creature. + // Whenever a creature dealt damage by Blood Cultist this turn dies, put a +1/+1 counter on Blood Cultist. + addCard(Zone.HAND, playerA, "Blood Cultist", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Shambling Ghoul", 1); // 2/3 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Shambling Ghoul"); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Shambling Ghoul"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Shambling Ghoul", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertPowerToughness(playerA, "Blood Cultist", 2, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java index fbbbcf4275..23e9e34c1e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.triggers.dies; import mage.constants.PhaseStep; @@ -7,7 +6,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ @@ -16,10 +14,9 @@ public class ShowstopperTest extends CardTestPlayerBase { /** * Tests that the dies triggered ability of silvercoat lion (gained by Showstopper) * triggers as he dies from Lightning Bolt - * */ @Test - public void testDiesTriggeredAbility() { + public void test_OneTrigger() { // Showstopper Instant {1}{B}{R} // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls." addCard(Zone.HAND, playerA, "Showstopper"); @@ -30,10 +27,14 @@ public class ShowstopperTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Ornithopter", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion"); + addTarget(playerA, "Ornithopter"); + + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); @@ -43,12 +44,13 @@ public class ShowstopperTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Ornithopter", 1); } + /** * Test if Showstopper is called twice */ @Test - public void testTwoDiesTriggeredAbilities() { + public void test_TwoTriggers() { // Showstopper Instant {1}{B}{R} // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls." addCard(Zone.HAND, playerA, "Showstopper", 2); @@ -62,12 +64,16 @@ public class ShowstopperTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion"); + setChoice(playerA, "When {this} dies"); // choose from two triggers addTarget(playerA, "Ornithopter"); addTarget(playerA, "Grizzly Bears"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); @@ -79,4 +85,65 @@ public class ShowstopperTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Ornithopter", 1); } + @Test + public void test_TwoTriggersAndCopies() { + // Showstopper Instant {1}{B}{R} + // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls." + addCard(Zone.HAND, playerA, "Showstopper", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + addCard(Zone.HAND, playerB, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerB, "Ornithopter", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Alchemist's Apprentice", 1); + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton", 1); + // + // When you next cast an instant spell, cast a sorcery spell, or activate a loyalty ability this turn, copy that spell or ability twice. + // You may choose new targets for the copies. + addCard(Zone.HAND, playerA, "Repeated Reverberation", 1); // {2}{R}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + + // first spell + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 2); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper"); + + // prepare copy + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 4); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Repeated Reverberation"); + + // second spell with 2x copy + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion"); + setChoice(playerA, "When {this} dies"); // choose from 4 triggers + setChoice(playerA, "When {this} dies"); // choose from 4 triggers + setChoice(playerA, "When {this} dies"); // choose from 4 triggers + addTarget(playerA, "Ornithopter"); + addTarget(playerA, "Grizzly Bears"); + addTarget(playerA, "Alchemist's Apprentice"); + addTarget(playerA, "Augmenting Automaton"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Showstopper", 2); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + + assertGraveyardCount(playerB, "Grizzly Bears", 1); + assertGraveyardCount(playerB, "Ornithopter", 1); + assertGraveyardCount(playerB, "Alchemist's Apprentice", 1); + assertGraveyardCount(playerB, "Augmenting Automaton", 1); + } + } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SupernaturalStaminaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SupernaturalStaminaTest.java index 8dccc5c96b..87175a4f4d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SupernaturalStaminaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SupernaturalStaminaTest.java @@ -1,48 +1,48 @@ - -package org.mage.test.cards.triggers.dies; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class SupernaturalStaminaTest extends CardTestPlayerBase { - - /** - * Channeler Initiate did not get it's -1/-1 counters when reanimated with - * Supernatural Stamina. This was a sealed game against a human player. - */ - @Test - public void testChanneler() { - addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); - - // When Channeler Initiate enters the battlefield, put three -1/-1 counters on target creature you control. - // {T}, Remove a -1/-1 counter from Channeler Initiate: Add one mana of any color. - addCard(Zone.HAND, playerA, "Channeler Initiate"); // Creature 3/4 {1}{G} - - // Until end of turn, target creature gets +2/+0 and gains "When this creature dies, return it to the battlefield tapped under its owner's control." - addCard(Zone.HAND, playerA, "Supernatural Stamina"); // Instant {B} - // Shock deals 2 damage to any target. - addCard(Zone.HAND, playerA, "Shock"); // Instant {R} - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Channeler Initiate"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Supernatural Stamina", "Channeler Initiate"); - castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Shock", "Channeler Initiate"); - - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertGraveyardCount(playerA, "Supernatural Stamina", 1); - assertGraveyardCount(playerA, "Shock", 1); - - assertPermanentCount(playerA, "Channeler Initiate", 1); - assertPowerToughness(playerA, "Channeler Initiate", 0, 1); - - } -} + +package org.mage.test.cards.triggers.dies; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class SupernaturalStaminaTest extends CardTestPlayerBase { + + /** + * Channeler Initiate did not get it's -1/-1 counters when reanimated with + * Supernatural Stamina. This was a sealed game against a human player. + */ + @Test + public void testChanneler() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + // When Channeler Initiate enters the battlefield, put three -1/-1 counters on target creature you control. + // {T}, Remove a -1/-1 counter from Channeler Initiate: Add one mana of any color. + addCard(Zone.HAND, playerA, "Channeler Initiate"); // Creature 3/4 {1}{G} + + // Until end of turn, target creature gets +2/+0 and gains "When this creature dies, return it to the battlefield tapped under its owner's control." + addCard(Zone.HAND, playerA, "Supernatural Stamina"); // Instant {B} + // Shock deals 2 damage to any target. + addCard(Zone.HAND, playerA, "Shock"); // Instant {R} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Channeler Initiate"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Supernatural Stamina", "Channeler Initiate"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Shock", "Channeler Initiate"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Supernatural Stamina", 1); + assertGraveyardCount(playerA, "Shock", 1); + + assertPermanentCount(playerA, "Channeler Initiate", 1); + assertPowerToughness(playerA, "Channeler Initiate", 0, 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java index 2fd417ba20..6714b58284 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java @@ -104,7 +104,7 @@ public class TidehollowScullerTest extends CardTestPlayerBase { for (int i = 1; i <= 10; i++) { try { this.reset(); - System.out.println("run " + i); + // System.out.println("run " + i); test_CastTwoCardFromHandWillBeExiled(); } catch (Exception e) { // diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ZulaportCutthroatTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ZulaportCutthroatTest.java index c7a050b85e..dc91f4ae4f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ZulaportCutthroatTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ZulaportCutthroatTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.triggers.dies; import mage.constants.PhaseStep; @@ -7,7 +6,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class ZulaportCutthroatTest extends CardTestPlayerBase { @@ -16,7 +14,6 @@ public class ZulaportCutthroatTest extends CardTestPlayerBase { * Zulaport's ability doesn't trigger when it dies. I'm not sure if that's * always the case, but I've encountered that bug at least several times * today. - * */ @Test public void testDiesAndControllerDamage() { @@ -40,4 +37,26 @@ public class ZulaportCutthroatTest extends CardTestPlayerBase { } + @Test + public void testTriggersWhenDevoured() { + // Whenever Zulaport Cutthroat or another creature you control dies, each opponent loses 1 life and you gain 1 life. + addCard(Zone.BATTLEFIELD, playerA, "Zulaport Cutthroat", 1); // 1/1 + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + // Devour 1 + addCard(Zone.HAND, playerA, "Gluttonous Slime"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gluttonous Slime"); + setChoice(playerA, "Yes"); // Devour + addTarget(playerA, "Zulaport Cutthroat^Grizzly Bears"); + setChoice(playerA, "Whenever {this}"); // Two triggers + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 22); + assertLife(playerB, 18); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/WatchersFromDelayedTriggeredAbilitiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/WatchersFromDelayedTriggeredAbilitiesTest.java new file mode 100644 index 0000000000..e2b75f82f1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/WatchersFromDelayedTriggeredAbilitiesTest.java @@ -0,0 +1,43 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class WatchersFromDelayedTriggeredAbilitiesTest extends CardTestPlayerBase { + + @Test + public void test_PsychicTheft_WatcherFromDelayedAbility() { + + // Target player reveals their hand. You choose an instant or sorcery card from it and exile that card. + // You may cast that card for as long as it remains exiled. At the beginning of the next end step, + // if you haven't cast the card, return it to its owner's hand. + addCard(Zone.HAND, playerA, "Psychic Theft", 1); // {1}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // + addCard(Zone.HAND, playerB, "Lightning Bolt"); + + // reveal + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 2); + checkPlayableAbility("can't play bolt", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", false); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Psychic Theft", playerB); + setChoice(playerA, "Lightning Bolt"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // can play until end step + checkPlayableAbility("can play bolt on 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true); + checkPlayableAbility("can't play bolt on 2", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", false); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertHandCount(playerB, "Lightning Bolt", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/CanBlockMultipleCreaturesTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/CanBlockMultipleCreaturesTest.java index bc7b9b0bbd..a899d559da 100644 --- a/Mage.Tests/src/test/java/org/mage/test/combat/CanBlockMultipleCreaturesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/combat/CanBlockMultipleCreaturesTest.java @@ -1,200 +1,200 @@ - -package org.mage.test.combat; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Ignore; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -/** - * - * @author jeffwadsworth - * @author Simown - */ -public class CanBlockMultipleCreaturesTest extends CardTestPlayerBase { - - @Test - public void testMultipleBlockWithTrample() { - - addCard(Zone.BATTLEFIELD, playerA, "Watcher in the Web", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Ulrich, Uncontested Alpha", 1); // 6/6 - addCard(Zone.BATTLEFIELD, playerB, "Kessig Dire Swine", 1); // 6/6 (trample if delirium) - addCard(Zone.BATTLEFIELD, playerB, "Howlpack Wolf", 1); // 3/3 - addCard(Zone.BATTLEFIELD, playerB, "Incorrigible Youths", 1); // 4/3 - - // Trample requirement for Kessig Dire Swine - addCard(Zone.GRAVEYARD, playerB, "Forest", 1); - addCard(Zone.GRAVEYARD, playerB, "Memnite", 1); - addCard(Zone.GRAVEYARD, playerB, "Flight", 1); - addCard(Zone.GRAVEYARD, playerB, "Drain Life", 1); - - // Attack with all 4 creatures and block all with the Watcher in the Web - attack(2, playerB, "Kessig Dire Swine"); - attack(2, playerB, "Ulrich, Uncontested Alpha"); - attack(2, playerB, "Howlpack Wolf"); - attack(2, playerB, "Incorrigible Youths"); - - // BLOCKING ORDER MATTERS - the trampling creature must be selected to block first - // You can manually change the blocking order but it's easier to assign them in order - block(2, playerA, "Watcher in the Web", "Kessig Dire Swine"); - block(2, playerA, "Watcher in the Web", "Ulrich, Uncontested Alpha"); - block(2, playerA, "Watcher in the Web", "Howlpack Wolf"); - block(2, playerA, "Watcher in the Web", "Incorrigible Youths"); - - setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertLife(playerA, 19); - - } - - @Test - public void testMultipleBlockWithTrample2() { - - addCard(Zone.BATTLEFIELD, playerA, "Watcher in the Web", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Ulrich, Uncontested Alpha", 1); // 6/6 - addCard(Zone.BATTLEFIELD, playerB, "Kessig Dire Swine", 1); // 6/6 (trample if delirium) - addCard(Zone.BATTLEFIELD, playerB, "Howlpack Wolf", 1); // 3/3 - addCard(Zone.BATTLEFIELD, playerB, "Incorrigible Youths", 1); // 4/3 - - // Trample requirement for Kessig Dire Swine - addCard(Zone.GRAVEYARD, playerB, "Forest", 1); - addCard(Zone.GRAVEYARD, playerB, "Memnite", 1); - addCard(Zone.GRAVEYARD, playerB, "Flight", 1); - addCard(Zone.GRAVEYARD, playerB, "Drain Life", 1); - - // Attack with all 4 creatures and block all with the Watcher in the Web - attack(2, playerB, "Kessig Dire Swine"); - attack(2, playerB, "Ulrich, Uncontested Alpha"); - attack(2, playerB, "Howlpack Wolf"); - attack(2, playerB, "Incorrigible Youths"); - - // BLOCKING ORDER MATTERS - the trampling creature must be selected to block first - block(2, playerA, "Watcher in the Web", "Kessig Dire Swine"); - block(2, playerA, "Watcher in the Web", "Ulrich, Uncontested Alpha"); - block(2, playerA, "Watcher in the Web", "Howlpack Wolf"); - // Don't block Incorrigible Youths - - setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - // Damage 1 from Kessig Dire Swine + 4 from Incorrigible Youths - assertLife(playerA, 15); - } - - @Test - public void testCanOnlyBlockSingle() { - - // Hundred-Handed One {2}{W}{W} - // Monstrosity 3. {3}{W}{W}{W} (If this creature isn't monstrous, put three +1/+1 counters on it and it becomes monstrous.) - //As long as Hundred-Handed One is monstrous, it has reach and can block an additional ninety-nine creatures each combat. - addCard(Zone.BATTLEFIELD, playerA, "Hundred-Handed One", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Bronze Sable", 1); // 2/1 - addCard(Zone.BATTLEFIELD, playerB, "Fabled Hero", 1); // 2/2 double strike - - // Attack with all 4 creatures and try and block both with hundred-handed one - attack(2, playerB, "Bronze Sable"); - attack(2, playerB, "Fabled Hero"); - - block(2, playerA, "Hundred-Handed One", "Bronze Sable"); - block(2, playerA, "Hundred-Handed One", "Fabled Hero"); - - setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); - - // Will fail on purpose - we are trying to block too many creatures! - try { - execute(); - fail("Expected exception not thrown"); - } catch(UnsupportedOperationException e) { - assertEquals("Hundred-Handed One cannot block Fabled Hero it is already blocking the maximum amount of creatures.", e.getMessage()); - } - } - - @Test - public void testCanBlockMultiple() { - - // Hundred-Handed One {2}{W}{W} - // Monstrosity 3. {3}{W}{W}{W} (If this creature isn't monstrous, put three +1/+1 counters on it and it becomes monstrous.) - // As long as Hundred-Handed One is monstrous, it has reach and can block an additional ninety-nine creatures each combat. - addCard(Zone.BATTLEFIELD, playerA, "Hundred-Handed One", 1); - // For monstrosity - addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); - - addCard(Zone.BATTLEFIELD, playerB, "Bronze Sable", 1); // 2/1 - addCard(Zone.BATTLEFIELD, playerB, "Fabled Hero", 1); // 2/2 double strike - - // Make hundred-handed one monstrous - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{W}{W}{W}: Monstrosity 3."); - - // Attack with all 4 creatures and try and block both with hundred-handed one - attack(2, playerB, "Bronze Sable"); - attack(2, playerB, "Fabled Hero"); - - block(2, playerA, "Hundred-Handed One", "Bronze Sable"); - block(2, playerA, "Hundred-Handed One", "Fabled Hero"); - - setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); - - // Will not fail this time as hundred-handed one is monstrous and can block up to 100 creatures - execute(); - - // Was a 3/5 but monstrosity 3 - assertPowerToughness(playerA, "Hundred-Handed One", 6, 8); - // No one has been hit - assertLife(playerA, 20); - assertLife(playerB, 20); - } - - /* - * Reported bug: Night Market Guard was able to block a creature with Menace - */ - @Test - public void testNightMarketGuardShouldNotBlockCreatureWithMenace() - { - /* - Night Market Guard {3} 3/1 - Artifact Creature — Construct - Night Market Guard can block an additional creature each combat. - */ - String nMarketGuard = "Night Market Guard"; - - /* - Embraal Bruiser {1}{B} - Creature - Human Warrior - Embraal Bruiser enters the battlefield tapped. - Embraal Bruiser has menace as long as you control an artifact. - */ - String eBruiser = "Embraal Bruiser"; - - /* - {0} 1/1 - * Artifact Creature — Construct - */ - String memnite = "Memnite"; - - addCard(Zone.BATTLEFIELD, playerA, nMarketGuard); - addCard(Zone.BATTLEFIELD, playerB, eBruiser); - addCard(Zone.BATTLEFIELD, playerB, memnite); // only here to grant Embraal Menace - - attack(4, playerB, eBruiser); - block(4, playerA, nMarketGuard, eBruiser); - - setStopAt(4, PhaseStep.POSTCOMBAT_MAIN); - - // Catch the illegal block - try { - execute(); - fail("Expected exception not thrown"); - } catch(UnsupportedOperationException e) { - assertEquals("Embraal Bruiser is blocked by 1 creature(s). It has to be blocked by 2 or more.", e.getMessage()); - } - - } + +package org.mage.test.combat; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * + * @author jeffwadsworth + * @author Simown + */ +public class CanBlockMultipleCreaturesTest extends CardTestPlayerBase { + + @Test + public void testMultipleBlockWithTrample() { + + addCard(Zone.BATTLEFIELD, playerA, "Watcher in the Web", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Ulrich, Uncontested Alpha", 1); // 6/6 + addCard(Zone.BATTLEFIELD, playerB, "Kessig Dire Swine", 1); // 6/6 (trample if delirium) + addCard(Zone.BATTLEFIELD, playerB, "Howlpack Wolf", 1); // 3/3 + addCard(Zone.BATTLEFIELD, playerB, "Incorrigible Youths", 1); // 4/3 + + // Trample requirement for Kessig Dire Swine + addCard(Zone.GRAVEYARD, playerB, "Forest", 1); + addCard(Zone.GRAVEYARD, playerB, "Memnite", 1); + addCard(Zone.GRAVEYARD, playerB, "Flight", 1); + addCard(Zone.GRAVEYARD, playerB, "Drain Life", 1); + + // Attack with all 4 creatures and block all with the Watcher in the Web + attack(2, playerB, "Kessig Dire Swine"); + attack(2, playerB, "Ulrich, Uncontested Alpha"); + attack(2, playerB, "Howlpack Wolf"); + attack(2, playerB, "Incorrigible Youths"); + + // BLOCKING ORDER MATTERS - the trampling creature must be selected to block first + // You can manually change the blocking order but it's easier to assign them in order + block(2, playerA, "Watcher in the Web", "Kessig Dire Swine"); + block(2, playerA, "Watcher in the Web", "Ulrich, Uncontested Alpha"); + block(2, playerA, "Watcher in the Web", "Howlpack Wolf"); + block(2, playerA, "Watcher in the Web", "Incorrigible Youths"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 19); + + } + + @Test + public void testMultipleBlockWithTrample2() { + + addCard(Zone.BATTLEFIELD, playerA, "Watcher in the Web", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Ulrich, Uncontested Alpha", 1); // 6/6 + addCard(Zone.BATTLEFIELD, playerB, "Kessig Dire Swine", 1); // 6/6 (trample if delirium) + addCard(Zone.BATTLEFIELD, playerB, "Howlpack Wolf", 1); // 3/3 + addCard(Zone.BATTLEFIELD, playerB, "Incorrigible Youths", 1); // 4/3 + + // Trample requirement for Kessig Dire Swine + addCard(Zone.GRAVEYARD, playerB, "Forest", 1); + addCard(Zone.GRAVEYARD, playerB, "Memnite", 1); + addCard(Zone.GRAVEYARD, playerB, "Flight", 1); + addCard(Zone.GRAVEYARD, playerB, "Drain Life", 1); + + // Attack with all 4 creatures and block all with the Watcher in the Web + attack(2, playerB, "Kessig Dire Swine"); + attack(2, playerB, "Ulrich, Uncontested Alpha"); + attack(2, playerB, "Howlpack Wolf"); + attack(2, playerB, "Incorrigible Youths"); + + // BLOCKING ORDER MATTERS - the trampling creature must be selected to block first + block(2, playerA, "Watcher in the Web", "Kessig Dire Swine"); + block(2, playerA, "Watcher in the Web", "Ulrich, Uncontested Alpha"); + block(2, playerA, "Watcher in the Web", "Howlpack Wolf"); + // Don't block Incorrigible Youths + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + // Damage 1 from Kessig Dire Swine + 4 from Incorrigible Youths + assertLife(playerA, 15); + } + + @Test + public void testCanOnlyBlockSingle() { + + // Hundred-Handed One {2}{W}{W} + // Monstrosity 3. {3}{W}{W}{W} (If this creature isn't monstrous, put three +1/+1 counters on it and it becomes monstrous.) + //As long as Hundred-Handed One is monstrous, it has reach and can block an additional ninety-nine creatures each combat. + addCard(Zone.BATTLEFIELD, playerA, "Hundred-Handed One", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Bronze Sable", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerB, "Fabled Hero", 1); // 2/2 double strike + + // Attack with all 4 creatures and try and block both with hundred-handed one + attack(2, playerB, "Bronze Sable"); + attack(2, playerB, "Fabled Hero"); + + block(2, playerA, "Hundred-Handed One", "Bronze Sable"); + block(2, playerA, "Hundred-Handed One", "Fabled Hero"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + + // Will fail on purpose - we are trying to block too many creatures! + try { + execute(); + fail("Expected exception not thrown"); + } catch(UnsupportedOperationException e) { + assertEquals("Hundred-Handed One cannot block Fabled Hero it is already blocking the maximum amount of creatures.", e.getMessage()); + } + } + + @Test + public void testCanBlockMultiple() { + + // Hundred-Handed One {2}{W}{W} + // Monstrosity 3. {3}{W}{W}{W} (If this creature isn't monstrous, put three +1/+1 counters on it and it becomes monstrous.) + // As long as Hundred-Handed One is monstrous, it has reach and can block an additional ninety-nine creatures each combat. + addCard(Zone.BATTLEFIELD, playerA, "Hundred-Handed One", 1); + // For monstrosity + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + + addCard(Zone.BATTLEFIELD, playerB, "Bronze Sable", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerB, "Fabled Hero", 1); // 2/2 double strike + + // Make hundred-handed one monstrous + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{W}{W}{W}: Monstrosity 3."); + + // Attack with all 4 creatures and try and block both with hundred-handed one + attack(2, playerB, "Bronze Sable"); + attack(2, playerB, "Fabled Hero"); + + block(2, playerA, "Hundred-Handed One", "Bronze Sable"); + block(2, playerA, "Hundred-Handed One", "Fabled Hero"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + + // Will not fail this time as hundred-handed one is monstrous and can block up to 100 creatures + execute(); + + // Was a 3/5 but monstrosity 3 + assertPowerToughness(playerA, "Hundred-Handed One", 6, 8); + // No one has been hit + assertLife(playerA, 20); + assertLife(playerB, 20); + } + + /* + * Reported bug: Night Market Guard was able to block a creature with Menace + */ + @Test + public void testNightMarketGuardShouldNotBlockCreatureWithMenace() + { + /* + Night Market Guard {3} 3/1 + Artifact Creature — Construct + Night Market Guard can block an additional creature each combat. + */ + String nMarketGuard = "Night Market Guard"; + + /* + Embraal Bruiser {1}{B} + Creature - Human Warrior + Embraal Bruiser enters the battlefield tapped. + Embraal Bruiser has menace as long as you control an artifact. + */ + String eBruiser = "Embraal Bruiser"; + + /* + {0} 1/1 + * Artifact Creature — Construct + */ + String memnite = "Memnite"; + + addCard(Zone.BATTLEFIELD, playerA, nMarketGuard); + addCard(Zone.BATTLEFIELD, playerB, eBruiser); + addCard(Zone.BATTLEFIELD, playerB, memnite); // only here to grant Embraal Menace + + attack(4, playerB, eBruiser); + block(4, playerA, nMarketGuard, eBruiser); + + setStopAt(4, PhaseStep.POSTCOMBAT_MAIN); + + // Catch the illegal block + try { + execute(); + fail("Expected exception not thrown"); + } catch(UnsupportedOperationException e) { + assertEquals("Embraal Bruiser is blocked by 1 creature(s). It has to be blocked by 2 or more.", e.getMessage()); + } + + } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBGPartnerCommanderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBGPartnerCommanderTest.java new file mode 100644 index 0000000000..37054c54f0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBGPartnerCommanderTest.java @@ -0,0 +1,93 @@ +package org.mage.test.commander.duel; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * @author LevelX2 + */ +public class CastBGPartnerCommanderTest extends CardTestCommanderDuelBase { + + /** + * With commander rule changes 6/2020 Reyhan goes to exile first before it + * goes to command zone. So it's triggerd ability does no longer move the + * counters on it on the battelfield + */ + @Test + public void testExileReyhan() { + addCard(Zone.COMMAND, playerA, "Reyhan, Last of the Abzan", 1); + addCard(Zone.COMMAND, playerA, "Ikra Shidiqi, the Usurper", 1); + addCard(Zone.COMMAND, playerB, "Daxos of Meletis", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + // As an additional cost to cast this spell, reveal a colorless creature card from your hand. + // Exile target creature if its power is less than or equal to the revealed card's power. + addCard(Zone.HAND, playerB, "Titan's Presence", 1); + addCard(Zone.HAND, playerB, "Ancient Stone Idol", 1); // Artifact Creature 12/12 + addCard(Zone.BATTLEFIELD, playerB, "Plains", 3); + + // Reyhan, Last of the Abzan enters the battlefield with three +1/+1 counters on it. + // Whenever a creature you control dies or is put into the command zone, + // if it had one or more +1/+1 counters on it, you may put that many +1/+1 counters on target creature. + // Partner (You can have two commanders if both have partner.) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reyhan, Last of the Abzan"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Titan's Presence", "Reyhan, Last of the Abzan"); + setChoice(playerB, "Ancient Stone Idol"); + + setChoice(playerA, "Yes"); // Commander goes to command zone + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Titan's Presence", 1); + assertCommandZoneCount(playerA, "Reyhan, Last of the Abzan", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + } + + @Test + public void testCastBothPartnerCommanders() { + addCard(Zone.COMMAND, playerA, "Reyhan, Last of the Abzan", 1); + addCard(Zone.COMMAND, playerA, "Ikra Shidiqi, the Usurper", 1); + addCard(Zone.COMMAND, playerB, "Daxos of Meletis", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + // Reyhan, Last of the Abzan enters the battlefield with three +1/+1 counters on it. + // Whenever a creature you control dies or is put into the command zone, + // if it had one or more +1/+1 counters on it, you may put that many +1/+1 counters on target creature. + // Partner (You can have two commanders if both have partner.) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reyhan, Last of the Abzan"); // {1}{B}{G} Creature 0/0 + + // Menace + // Whenever a creature you control deals combat damage to a player, you gain life equal to that creature's toughness. + // Partner + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Ikra Shidiqi, the Usurper"); // Creature 3/7 {3}{B}{G} + + attack(3, playerA, "Reyhan, Last of the Abzan"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + + assertCommandZoneCount(playerA, "Reyhan, Last of the Abzan", 0); + assertCommandZoneCount(playerA, "Ikra Shidiqi, the Usurper", 0); + + assertPowerToughness(playerA, "Reyhan, Last of the Abzan", 3, 3); + assertPowerToughness(playerA, "Ikra Shidiqi, the Usurper", 3, 7); + + assertLife(playerA, 43); + assertLife(playerB, 37); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBRGCommanderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBRGCommanderTest.java index 9abe4909fa..924781998f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBRGCommanderTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBRGCommanderTest.java @@ -160,7 +160,7 @@ public class CastBRGCommanderTest extends CardTestCommanderDuelBase { assertGraveyardCount(playerA, "Mogg Infestation", 1); assertCommandZoneCount(playerB, "Daxos of Meletis", 1); - assertPermanentCount(playerB, "Goblin", 0); + assertPermanentCount(playerB, "Goblin", 2); } diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java index 0b49817680..d62c453d18 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java @@ -3,6 +3,8 @@ package org.mage.test.commander.duel; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestCommanderDuelBase; @@ -11,6 +13,7 @@ import org.mage.test.serverside.base.CardTestCommanderDuelBase; * @author LevelX2 */ public class CastCommanderTest extends CardTestCommanderDuelBase { + @Test public void testCastCommander() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); @@ -25,4 +28,69 @@ public class CastCommanderTest extends CardTestCommanderDuelBase { assertPermanentCount(playerA, "Ob Nixilis of the Black Oath", 1); } + + @Test + public void testCastKestiaCommander() { + // Bestow {3}{G}{W}{U} + // Enchanted creature gets +4/+4. + // Whenever an enchanted creature or enchantment creature you control attacks, draw a card. + addCard(Zone.COMMAND, playerA, "Kestia, the Cultivator", 1); // Creature {1}{G}{W}{U} 4/4 + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kestia, the Cultivator using bestow", "Silvercoat Lion"); + + setStrictChooseMode(true); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + assertLife(playerA, 40); + assertLife(playerB, 40); + + + assertPermanentCount(playerA, "Kestia, the Cultivator", 1); + + Permanent kestia = getPermanent("Kestia, the Cultivator", playerA); + Assert.assertNotEquals("Kestia may not be an creature", true, kestia.isCreature()); + Assert.assertEquals("Kestia has to be an enchantment", true, kestia.isEnchantment()); + + assertPowerToughness(playerA, "Silvercoat Lion", 6, 6); + } + + + @Test + public void testCastPatronOfTheOrochiCommander() { + // Snake offering (You may cast this card any time you could cast an instant + // by sacrificing a Snake and paying the difference in mana costs between this + // and the sacrificed Snake. Mana cost includes color.) + // {T}: Untap all Forests and all green creatures. Activate this ability only once each turn. + addCard(Zone.COMMAND, playerA, "Patron of the Orochi", 1); // Creature {6}{G}{G} 7/7 + + // First strike + addCard(Zone.BATTLEFIELD, playerA, "Coiled Tinviper", 1); // Creature Snake {3} 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); // Cost reduction does not work for getPlayable so you need to have 8 mana available + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Patron of the Orochi"); + setChoice(playerA, "Yes"); + addTarget(playerA, "Coiled Tinviper"); + + setStrictChooseMode(true); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + assertLife(playerA, 40); + assertLife(playerB, 40); + + assertPermanentCount(playerA, "Patron of the Orochi", 1); + assertGraveyardCount(playerA, "Coiled Tinviper", 1); + assertTappedCount("Forest", false, 3); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderReplaceEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderReplaceEffectTest.java index 4e7dcb3b2d..1461804f89 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderReplaceEffectTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderReplaceEffectTest.java @@ -110,8 +110,8 @@ public class CommanderReplaceEffectTest extends CardTestCommanderDuelBase { execute(); assertPermanentCount(playerA, "Soulherder", 1); - assertPermanentCount(playerA, "Daxos of Meletis", 0); - assertCommandZoneCount(playerA, "Daxos of Meletis", 1); + assertPermanentCount(playerA, "Daxos of Meletis", 1); + assertCommandZoneCount(playerA, "Daxos of Meletis", 0); assertPowerToughness(playerA, "Soulherder", 2, 2); } diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java new file mode 100644 index 0000000000..2792c326bb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java @@ -0,0 +1,205 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.commander.duel; + +import java.io.FileNotFoundException; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.GameException; +import org.junit.Test; +import static org.mage.test.player.TestPlayer.NO_TARGET; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * + * @author LevelX2 + */ +public class MairsilThePretenderTest extends CardTestCommanderDuelBase { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + // When Mairsil, the Pretender enters the battlefield, you may exile an artifact or creature card from your hand + // or graveyard and put a cage counter on it. + // Mairsil, the Pretender has all activated abilities of all cards you own in exile with cage counters on them. + // You may activate each of those abilities only once each turn. + setDecknamePlayerA("CommanderDuel_Mairisil_UBR.dck"); // Commander = Mairsil, the Pretender {1}{U}{B}{R} + return super.createNewGameAndPlayers(); + } + + /** + * Basic Test + */ + @Test + public void useShifitingShadowTest() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 3); + skipInitShuffling(); + + // Enchant creature + // Enchanted creature has haste and “At the beginning of your upkeep, destroy this creature. + // Reveal cards from the top of your library until you reveal a creature card. + // Put that card onto the battlefield and attach Shifting Shadow to it, + // then put all other cards revealed this way on the bottom of your library in a random order.” + addCard(Zone.HAND, playerA, "Shifting Shadow", 1); // {2}{R} + + // Tap: Draw three cards. + // {2}{U}{U}: Return Arcanis the Omnipotent to its owner's hand. + addCard(Zone.HAND, playerA, "Arcanis the Omnipotent", 1); // Creature {3}{U}{U}{U} + + setChoice(playerA, "Yes"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mairsil, the Pretender"); + setChoice(playerA, "Yes"); // Exile a card + setChoice(playerA, "Arcanis the Omnipotent"); + + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Shifting Shadow", "Mairsil, the Pretender"); + + setChoice(playerA, "Yes"); // Move commander to command zone + setStopAt(5, PhaseStep.PRECOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 40); + assertLife(playerB, 40); + + assertExileCount("Arcanis the Omnipotent", 1); + + assertCommandZoneCount(playerA, "Mairsil, the Pretender", 1); + + assertPermanentCount(playerA, "Shifting Shadow", 1); + } + /** + * I tried playing it in Mairsil the Pretender commander deck and found 2 + * cases where the creature is not properly destroyed: + * + * Using Arcanis the Omnipotent ability to return my commander to hand while + * trigger is on the stack I got Mairsil to hand then got asked whether I + * want to put him in gy so I answered yes assuming it cannot be destroyed + * while in my hand. He got put in graveyard straight from my hand. + * According to Oracle rules I should get a creature from top of the deck + * and still have my commander in hand. After giving my commander undying + * with shifting shadow trigger on the stack he got put in gy with undying + * not triggering. + * + * All this points to the card being hardcoded to put the creature into + * graveyard instead of simply destroying it + */ + @Test + public void useShifitingShadowAndArcanisTest() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 3); + skipInitShuffling(); + // Enchant creature + // Enchanted creature has haste and “At the beginning of your upkeep, destroy this creature. + // Reveal cards from the top of your library until you reveal a creature card. + // Put that card onto the battlefield and attach Shifting Shadow to it, + // then put all other cards revealed this way on the bottom of your library in a random order.” + addCard(Zone.HAND, playerA, "Shifting Shadow", 1); // {2}{R} + + // Tap: Draw three cards. + // {2}{U}{U}: Return Arcanis the Omnipotent to its owner's hand. + addCard(Zone.HAND, playerA, "Arcanis the Omnipotent", 1); // Creature {3}{U}{U}{U} + + setChoice(playerA, "Yes"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mairsil, the Pretender"); + setChoice(playerA, "Yes"); // Exile a card + setChoice(playerA, "Arcanis the Omnipotent"); + + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Shifting Shadow", "Mairsil, the Pretender"); + + activateAbility(5, PhaseStep.UPKEEP, playerA, "{2}{U}{U}: Return", NO_TARGET, "At the beginning of your upkeep"); + setChoice(playerA, "No"); // Don't move commander to command zone because it goes to hand + + setStopAt(5, PhaseStep.PRECOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 40); + assertLife(playerB, 40); + + assertExileCount("Arcanis the Omnipotent", 1); + + assertCommandZoneCount(playerA, "Mairsil, the Pretender", 0); + assertHandCount(playerA, "Mairsil, the Pretender", 1); + + + assertGraveyardCount(playerA, "Shifting Shadow", 1); // Goes to graveyard because commander left battlefield to hand from Arcanis ability + assertPermanentCount(playerA, "Silvercoat Lion", 1); + } + + @Test + public void useShifitingShadowAndEndlingTest() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 3); + skipInitShuffling(); + // Enchant creature + // Enchanted creature has haste and “At the beginning of your upkeep, destroy this creature. + // Reveal cards from the top of your library until you reveal a creature card. + // Put that card onto the battlefield and attach Shifting Shadow to it, + // then put all other cards revealed this way on the bottom of your library in a random order.” + addCard(Zone.HAND, playerA, "Shifting Shadow", 1); // {2}{R} + // Tap: Draw three cards. + // {2}{U}{U}: Return Arcanis the Omnipotent to its owner's hand. + addCard(Zone.HAND, playerA, "Arcanis the Omnipotent", 1); // Creature {3}{U}{U}{U} + // {B}: Endling gains menace until end of turn. + // {B}: Endling gains deathtouch until end of turn. + // {B}: Endling gains undying until end of turn. + // {1}: Endling gets +1/-1 or -1/+1 until end of turn. + addCard(Zone.HAND, playerA, "Endling", 1); // Creature {3}{U}{U}{U} + + + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mairsil, the Pretender"); + setChoice(playerA, "Yes"); // Exile a card + setChoice(playerA, "Yes"); // Exile from Hand + setChoice(playerA, "Endling"); + + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Shifting Shadow", "Mairsil, the Pretender"); + + activateAbility(5, PhaseStep.UPKEEP, playerA, "{B}: {this} gains Undying", NO_TARGET, "At the beginning of your upkeep"); + setChoice(playerA, "No"); // Don't move commander to command zone because can come back by Undying + + setChoice(playerA, "Yes"); // Exile a card (Mairsil comes back from Undying) + setChoice(playerA, "Yes"); // Exile from hand + setChoice(playerA, "Arcanis the Omnipotent"); + + setStopAt(5, PhaseStep.PRECOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 40); + assertLife(playerB, 40); + + assertExileCount("Endling", 1); + assertExileCount("Arcanis the Omnipotent", 1); + + assertCommandZoneCount(playerA, "Mairsil, the Pretender", 0); + assertGraveyardCount(playerA, "Mairsil, the Pretender", 0); + assertPermanentCount(playerA, "Mairsil, the Pretender", 1); // Returns from Undying + assertPowerToughness(playerA, "Mairsil, the Pretender", 5, 5); + + assertPermanentCount(playerA, "Shifting Shadow", 1); // Enchants Silvercoat Lion + assertPermanentCount(playerA, "Silvercoat Lion", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java index b08652f2bb..3375f7a23c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java @@ -52,7 +52,6 @@ public class MythUnboundTest extends CardTestCommanderDuelBase { assertPermanentCount(playerA, "Myth Unbound", 1); assertGraveyardCount(playerB, "Lightning Bolt", 1); assertPermanentCount(playerA, "Oviya Pashiri, Sage Lifecrafter", 1); - assertHandCount(playerA, 1); assertTappedCount("Forest", false, 4 - 3); // 1 for first, 2 for second cast } } diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java index 8db47a88fb..36a44ea0a9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java @@ -72,7 +72,7 @@ public class NorinTheWaryTest extends CardTestCommanderDuelBase { } @Test - public void castNorinTheWaryToCommandAndReturn() { + public void castNorinTheWaryToCommandAndNotReturn() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); addCard(Zone.HAND, playerA, "Lightning Bolt", 1); @@ -83,8 +83,9 @@ public class NorinTheWaryTest extends CardTestCommanderDuelBase { execute(); assertGraveyardCount(playerA, "Lightning Bolt", 1); - assertPermanentCount(playerA, "Norin the Wary", 1); + assertPermanentCount(playerA, "Norin the Wary", 0); assertExileCount("Norin the Wary", 0); + assertCommandZoneCount(playerA, "Norin the Wary", 1); assertLife(playerB, 37); diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java index e0d3d48582..82a06303bb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java @@ -29,7 +29,7 @@ public class OpalPalaceTest extends CardTestCommanderDuelBase { // showHand("hand", 1, PhaseStep.PRECOMBAT_MAIN, playerA); // showCommand("command", 1, PhaseStep.PRECOMBAT_MAIN, playerA); - // showAvaileableAbilities("abi", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + // showAvailableAbilities("abi", 1, PhaseStep.PRECOMBAT_MAIN, playerA); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}"); setChoice(playerA, "Opal Palace"); // activate mana replace effect first (+3 counters) diff --git a/Mage.Tests/src/test/java/org/mage/test/game/ends/PhageTheUntouchableTest.java b/Mage.Tests/src/test/java/org/mage/test/game/ends/PhageTheUntouchableTest.java index 8679e5c112..04abf5db4b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/game/ends/PhageTheUntouchableTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/game/ends/PhageTheUntouchableTest.java @@ -1,51 +1,57 @@ - -package org.mage.test.game.ends; - -import mage.constants.PhaseStep; -import mage.constants.TurnPhase; -import mage.constants.Zone; -import org.junit.Assert; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LeveöX - */ -public class PhageTheUntouchableTest extends CardTestPlayerBase { - - @Test - public void TestWithEndlessWhispers() { - // Each creature has "When this creature dies, choose target opponent. - // That player puts this card from its owner's graveyard onto the battlefield - // under their control at the beginning of the next end step." - addCard(Zone.BATTLEFIELD, playerA, "Endless Whispers"); - - // Destroy target creature or planeswalker.. - addCard(Zone.HAND, playerA, "Hero's Downfall"); // {1}{B}{B} - - // When Phage the Untouchable enters the battlefield, if you didn't cast it from your hand, you lose the game. - // Whenever Phage deals combat damage to a creature, destroy that creature. It can't be regenerated. - // Whenever Phage deals combat damage to a player, that player loses the game. - addCard(Zone.HAND, playerA, "Phage the Untouchable"); // Creature {3}{B}{B}{B}{B} 4/4 - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phage the Untouchable"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hero's Downfall", "Phage the Untouchable"); - - setStopAt(2, PhaseStep.BEGIN_COMBAT); - execute(); - - assertLife(playerA, 20); - assertLife(playerB, 20); - - assertGraveyardCount(playerA, "Hero's Downfall", 1); - assertPermanentCount(playerB, "Phage the Untouchable", 1); - - Assert.assertTrue("Game has ended.", currentGame.hasEnded()); - Assert.assertTrue("Player A has won.", playerA.hasWon()); - Assert.assertTrue("Game ist At end phase", currentGame.getPhase().getType() == TurnPhase.END); - - } - -} + +package org.mage.test.game.ends; + +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class PhageTheUntouchableTest extends CardTestPlayerBase { + + @Test + public void TestWithEndlessWhispers() { + // Each creature has "When this creature dies, choose target opponent. + // That player puts this card from its owner's graveyard onto the battlefield + // under their control at the beginning of the next end step." + addCard(Zone.BATTLEFIELD, playerA, "Endless Whispers"); + + // Destroy target creature or planeswalker.. + addCard(Zone.HAND, playerA, "Hero's Downfall"); // {1}{B}{B} + + // When Phage the Untouchable enters the battlefield, if you didn't cast it from your hand, you lose the game. + // Whenever Phage deals combat damage to a creature, destroy that creature. It can't be regenerated. + // Whenever Phage deals combat damage to a player, that player loses the game. + addCard(Zone.HAND, playerA, "Phage the Untouchable"); // Creature {3}{B}{B}{B}{B} 4/4 + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phage the Untouchable"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hero's Downfall", "Phage the Untouchable"); + addTarget(playerA, playerB); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + + setStrictChooseMode(true); + + execute(); + + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Hero's Downfall", 1); + assertPermanentCount(playerB, "Phage the Untouchable", 1); + + Assert.assertTrue("Game has ended.", currentGame.hasEnded()); + Assert.assertTrue("Player A has won.", playerA.hasWon()); + Assert.assertTrue("Game ist At end phase", currentGame.getPhase().getType() == TurnPhase.END); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java b/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java index f74a0b360f..90f7793636 100644 --- a/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java +++ b/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java @@ -215,7 +215,7 @@ public class LoadCallbackClient implements CallbackClient { } if (controlCount > 5) { - log.warn(getLogStartInfo() + "Game seems freezed. Sending boolean message to server."); + log.warn(getLogStartInfo() + "Game seems frozen. Sending boolean message to server."); session.sendPlayerBoolean(gameId, false); controlCount = 0; } diff --git a/Mage.Tests/src/test/java/org/mage/test/mulligan/LondonMulliganTest.java b/Mage.Tests/src/test/java/org/mage/test/mulligan/LondonMulliganTest.java index ed9a7c8abb..03dc1a6747 100644 --- a/Mage.Tests/src/test/java/org/mage/test/mulligan/LondonMulliganTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/mulligan/LondonMulliganTest.java @@ -91,7 +91,7 @@ public class LondonMulliganTest extends MulliganTestBase { }); scenario.discardBottom(count -> { scenario.assertSizes(7, 33); - assertEquals(2, count); + assertEquals(1, count); assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7))); assertEquals(discarded, scenario.getLibraryRangeSize(26, 1)); assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6))); @@ -101,11 +101,23 @@ public class LondonMulliganTest extends MulliganTestBase { remainingHand.addAll(Sets.difference(scenario.getHand(), new HashSet<>(discarded))); return discarded; }); + scenario.discardBottom(count -> { + scenario.assertSizes(6, 34); + assertEquals(1, count); + assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7))); + assertEquals(discarded, scenario.getNBottomOfLibrary(1)); + assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6))); + discarded.clear(); + remainingHand.clear(); + scenario.getHand().stream().limit(count).forEach(discarded::add); + remainingHand.addAll(Sets.difference(scenario.getHand(), new HashSet<>(discarded))); + return discarded; + }); scenario.mulligan(() -> { scenario.assertSizes(5, 35); assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7))); assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6))); - assertEquals(discarded, scenario.getNBottomOfLibrary(2)); + assertEquals(discarded, scenario.getNBottomOfLibrary(1)); hand3.addAll(scenario.getHand()); return false; }); @@ -115,7 +127,7 @@ public class LondonMulliganTest extends MulliganTestBase { assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7))); assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6))); assertEquals(hand3, scenario.getHand()); - assertEquals(discarded, scenario.getNBottomOfLibrary(2)); + assertEquals(discarded, scenario.getNBottomOfLibrary(1)); }); } @@ -221,7 +233,12 @@ public class LondonMulliganTest extends MulliganTestBase { }); scenario.discardBottom(count -> { scenario.assertSizes(7, 33); - assertEquals(2, count); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(6, 34); + assertEquals(1, count); return scenario.getHand().stream().limit(count).collect(Collectors.toList()); }); scenario.mulligan(() -> { @@ -230,7 +247,17 @@ public class LondonMulliganTest extends MulliganTestBase { }); scenario.discardBottom(count -> { scenario.assertSizes(7, 33); - assertEquals(3, count); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(6, 34); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(5, 35); + assertEquals(1, count); return scenario.getHand().stream().limit(count).collect(Collectors.toList()); }); scenario.mulligan(() -> { @@ -239,7 +266,22 @@ public class LondonMulliganTest extends MulliganTestBase { }); scenario.discardBottom(count -> { scenario.assertSizes(7, 33); - assertEquals(4, count); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(6, 34); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(5, 35); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(4, 36); + assertEquals(1, count); return scenario.getHand().stream().limit(count).collect(Collectors.toList()); }); scenario.mulligan(() -> { @@ -248,7 +290,27 @@ public class LondonMulliganTest extends MulliganTestBase { }); scenario.discardBottom(count -> { scenario.assertSizes(7, 33); - assertEquals(5, count); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(6, 34); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(5, 35); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(4, 36); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(3, 37); + assertEquals(1, count); return scenario.getHand().stream().limit(count).collect(Collectors.toList()); }); scenario.mulligan(() -> { @@ -257,7 +319,32 @@ public class LondonMulliganTest extends MulliganTestBase { }); scenario.discardBottom(count -> { scenario.assertSizes(7, 33); - assertEquals(6, count); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(6, 34); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(5, 35); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(4, 36); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(3, 37); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(2, 38); + assertEquals(1, count); return scenario.getHand().stream().limit(count).collect(Collectors.toList()); }); scenario.mulligan(() -> { @@ -266,7 +353,37 @@ public class LondonMulliganTest extends MulliganTestBase { }); scenario.discardBottom(count -> { scenario.assertSizes(7, 33); - assertEquals(7, count); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(6, 34); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(5, 35); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(4, 36); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(3, 37); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(2, 38); + assertEquals(1, count); + return scenario.getHand().stream().limit(count).collect(Collectors.toList()); + }); + scenario.discardBottom(count -> { + scenario.assertSizes(1, 39); + assertEquals(1, count); return scenario.getHand().stream().limit(count).collect(Collectors.toList()); }); scenario.run(() -> { diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/AngelOfSerenityTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/AngelOfSerenityTest.java new file mode 100644 index 0000000000..e6ac798929 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/AngelOfSerenityTest.java @@ -0,0 +1,69 @@ +package org.mage.test.multiplayer; + +import mage.constants.MultiplayerAttackOption; +import mage.constants.PhaseStep; +import mage.constants.RangeOfInfluence; +import mage.constants.Zone; +import mage.game.FreeForAll; +import mage.game.Game; +import mage.game.GameException; +import mage.game.mulligan.MulliganType; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestMultiPlayerBase; + +import java.io.FileNotFoundException; + +public class AngelOfSerenityTest extends CardTestMultiPlayerBase { + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + // Start Life = 2 + Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 2); + // Player order: A -> D -> C -> B + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + playerC = createPlayer(game, playerC, "PlayerC"); + playerD = createPlayer(game, playerD, "PlayerD"); + return game; + } + + /** + * In a multiplayer game when Angel of Serenity leaves the Battlefield because the OWNER leaves the game 800.4a, + * it should return the exiled creatures if Angel of Serenity is controlled by a player that does not leave, + * since then 800.4d does not apply. + * 20200417 - 800.4a/800.4d + */ + @Test + public void TestAngelOfSerenityTakeoverExileReturn() { + + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); // add mana to cast card to take over Angel + addCard(Zone.HAND, playerA, "Agent of Treachery"); + + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerB, "Dromoka Dunecaster", 3); + + addCard(Zone.BATTLEFIELD, playerC, "Plains", 7); + addCard(Zone.HAND, playerC, "Angel of Serenity"); // {4}{W}{W}{W} + + + // player C turn is 3 + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Angel of Serenity"); + addTarget(playerC, "Dromoka Dunecaster^Dromoka Dunecaster^Dromoka Dunecaster"); + + // player A 2nd turn is 5 + castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Agent of Treachery"); + addTarget(playerA, "Angel of Serenity"); + + concede(5, PhaseStep.POSTCOMBAT_MAIN, playerC); + + setStopAt(6, PhaseStep.UPKEEP); + execute(); + assertAllCommandsUsed(); + + Assert.assertFalse("Player of Angel of Serenity did not leave the game", playerC.isInGame()); + assertPermanentCount(playerA, 8); + assertPermanentCount(playerB, 3); + assertHandCount(playerB, 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java index c37ce869a2..8699e82cd9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java @@ -1,5 +1,6 @@ package org.mage.test.multiplayer; +import java.io.FileNotFoundException; import mage.constants.MultiplayerAttackOption; import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; @@ -14,8 +15,6 @@ import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestMultiPlayerBase; -import java.io.FileNotFoundException; - /** * @author LevelX2 */ @@ -344,4 +343,54 @@ public class PlayerLeftGameRange1Test extends CardTestMultiPlayerBase { Assert.assertTrue("Staff of player B could be used", staffPlayerB.isTapped()); } + + /** + * Captive Audience doesn't work correctly in multiplayer #5593 + * + * Currently, if the controller of Captive Audience leaves the game, Captive + * Audience returns to its owner instead of being exiled. + */ + @Test + public void TestCaptiveAudienceGoesToExile() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Captive Audience enters the battlefield under the control of an opponent of your choice. + // At the beginning of your upkeep, choose one that hasn't been chosen — + // • Your life total becomes 4. + // • Discard your hand. + // • Each opponent creates five 2/2 black Zombie creature tokens. + addCard(Zone.HAND, playerA, "Captive Audience"); // Enchantment {5}{B}{R} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox", 1); + + setChoice(playerA, "PlayerA"); // Starting Player + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Captive Audience"); + setChoice(playerA, "PlayerD"); + + setModeChoice(playerD, "1"); + + attack(5, playerA, "Silvercoat Lion", playerD); + attack(5, playerA, "Pillarfield Ox", playerD); + + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + + assertAllCommandsUsed(); + + assertLife(playerA, 2); + + Assert.assertFalse("Player D is no longer in the game", playerD.isInGame()); + + assertPermanentCount(playerD, 0); + + assertPermanentCount(playerA, "Captive Audience", 0); + assertGraveyardCount(playerA, "Captive Audience", 0); + assertExileCount(playerA, "Captive Audience", 1); + + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java new file mode 100644 index 0000000000..b2297d779a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java @@ -0,0 +1,160 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.multiplayer; + +import java.io.FileNotFoundException; +import mage.constants.MultiplayerAttackOption; +import mage.constants.PhaseStep; +import mage.constants.RangeOfInfluence; +import mage.constants.Zone; +import mage.game.FreeForAll; +import mage.game.Game; +import mage.game.GameException; +import mage.game.mulligan.MulliganType; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestMultiPlayerBase; + + +/** + * @author LevelX2 + */ +public class PlayerWinsTest extends CardTestMultiPlayerBase { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 40); + // Player order: A -> D -> C -> B + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + playerC = createPlayer(game, playerC, "PlayerC"); + playerD = createPlayer(game, playerD, "PlayerD"); + return game; + } + + /** + * Tests multiplayer effects Player order: A -> D -> C -> B + */ + + /** + * Test that players out of range do not lose the game if effect from Approach of the Seconnd Sun takes effect. + */ + @Test + public void ApproachOfTheSecondSunTest() { + + // If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game, + // you win the game. Otherwise, put Approach of the Second Sun into its owner's library seventh from the top and you gain 7 life. + addCard(Zone.HAND, playerA, "Approach of the Second Sun", 2); // Sorcery {6}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Approach of the Second Sun"); + castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Approach of the Second Sun"); + + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 47); + assertLife(playerC, 40); + Assert.assertTrue("Player D still alive but should have lost the game", !playerD.isInGame()); + Assert.assertTrue("Player B still alive but should have lost the game", !playerB.isInGame()); + Assert.assertTrue("Player C is in the game", playerC.isInGame()); + Assert.assertTrue("Player A is in the game", playerA.isInGame()); + + } + + /** + * Test that players out of range do not lose the game if effect from Laboratory Maniac takes effect. + */ + @Test + public void LaboratoryManiacWinsTest() { + // If you would draw a card while your library has no cards in it, you win the game instead. + addCard(Zone.HAND, playerA, "Laboratory Maniac", 1); // Creature {2}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Laboratory Maniac"); + removeAllCardsFromLibrary(playerA); + addCard(Zone.LIBRARY, playerA, "Mountain"); + + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Laboratory Maniac", 1); + assertHandCount(playerA, "Mountain", 1); + assertLibraryCount(playerA, 0); + Assert.assertTrue("Player D still alive but should have lost the game", !playerD.isInGame()); + Assert.assertTrue("Player B still alive but should have lost the game", !playerB.isInGame()); + Assert.assertTrue("Player C should be in the game but has lost", playerC.isInGame()); + Assert.assertTrue("Player A should be in the game but has lost", playerA.isInGame()); + } + + /** + * Test that player can't win while Platinium Angel ist in range. + */ + @Test + public void LaboratoryManiacAndPlatinumAngelInRangeTest() { + // If you would draw a card while your library has no cards in it, you win the game instead. + addCard(Zone.HAND, playerA, "Laboratory Maniac", 1); // Creature {2}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + removeAllCardsFromLibrary(playerA); + + // You can't lose the game and your opponents can't win the game. + addCard(Zone.HAND, playerD, "Platinum Angel", 1); // Creature {2}{U} + addCard(Zone.BATTLEFIELD, playerD, "Island", 7); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Laboratory Maniac"); + addCard(Zone.LIBRARY, playerA, "Mountain"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Platinum Angel"); + + setStopAt(9, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Laboratory Maniac", 1); + assertHandCount(playerA, "Mountain", 1); + assertLibraryCount(playerA, 0); + + assertPermanentCount(playerD, "Platinum Angel", 1); + + Assert.assertTrue("Player D should be in the game but has lost", playerD.isInGame()); + Assert.assertTrue("Player B should be in the game but has lost", playerB.isInGame()); + Assert.assertTrue("Player C should be in the game but has lost", playerC.isInGame()); + Assert.assertTrue("Player A should be in the game but has lost", playerA.isInGame()); + } + + /** + * Test that player can't win while Platinium Angel ist in range. + */ + @Test + public void LaboratoryManiacAndPlatinumAngelFirstOutOfRangeTest() { + // If you would draw a card while your library has no cards in it, you win the game instead. + addCard(Zone.HAND, playerA, "Laboratory Maniac", 1); // Creature {2}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + removeAllCardsFromLibrary(playerA); + + // You can't lose the game and your opponents can't win the game. + addCard(Zone.HAND, playerC, "Platinum Angel", 1); // Creature {2}{U} + addCard(Zone.BATTLEFIELD, playerC, "Island", 7); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Laboratory Maniac"); + addCard(Zone.LIBRARY, playerA, "Mountain"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Platinum Angel"); + + setStopAt(9, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Laboratory Maniac", 1); + assertHandCount(playerA, "Mountain", 1); + assertLibraryCount(playerA, 0); + + assertPermanentCount(playerC, "Platinum Angel", 1); + + Assert.assertTrue("Player D still alive but should have lost the gamet", !playerD.isInGame()); + Assert.assertTrue("Player B still alive but should have lost the game", !playerB.isInGame()); + Assert.assertTrue("Player C should be in the game but has lost", playerC.isInGame()); + Assert.assertTrue("Player A should be in the game but has lost", playerA.isInGame()); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheekiSublimeArtificerTest.java b/Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheekiSublimeArtificerTest.java new file mode 100644 index 0000000000..da1dae2834 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheekiSublimeArtificerTest.java @@ -0,0 +1,69 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.oathbreaker.FFA3; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestOathbreaker3PlayersFFA; + +/** + * + * @author LevelX2 + */ + +public class BasicSaheekiSublimeArtificerTest extends CardTestOathbreaker3PlayersFFA { + + /** + * Check that if a player left the game, it's commander is also removed + */ + @Test + public void commanderRemovedTest() { + + concede(1, PhaseStep.PRECOMBAT_MAIN, playerA); + setStopAt(1, PhaseStep.DECLARE_ATTACKERS); + execute(); + + assertCommandZoneCount(playerB, "Saheeli, Sublime Artificer", 1); + assertCommandZoneCount(playerB, "Thoughtcast", 1); + assertCommandZoneCount(playerC, "Saheeli, Sublime Artificer", 1); + assertCommandZoneCount(playerC, "Thoughtcast", 1); + assertCommandZoneCount(playerA, "Saheeli, Sublime Artificer", 0); + assertCommandZoneCount(playerA, "Thoughtcast", 0); + + } + + @Test + public void castCommanderTest() { + + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.BATTLEFIELD, playerA, "Ornithopter", 4); + + // Planeswalker 5 Loyality Counter + // Whenever you cast a noncreature spell, create a 1/1 colorless Servo artifact creature token. + // -2: Target artifact you control becomes a copy of another target artifact or creature you control until end of turn, + // except it's an artifact in addition to its other types. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Saheeli, Sublime Artificer"); // Planeswalker 5 {1}{U/R}{U/R} + + // Affinity for artifacts + // Draw two cards. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thoughtcast"); // Sorcery {4}{U} + + setStopAt(1, PhaseStep.DECLARE_ATTACKERS); + execute(); + + + assertCommandZoneCount(playerB, "Saheeli, Sublime Artificer", 1); + assertCommandZoneCount(playerB, "Thoughtcast", 1); + assertCommandZoneCount(playerC, "Saheeli, Sublime Artificer", 1); + assertCommandZoneCount(playerC, "Thoughtcast", 1); + + assertHandCount(playerA, 3); + assertPermanentCount(playerA, "Saheeli, Sublime Artificer", 1); + assertCommandZoneCount(playerA, "Thoughtcast", 1); + + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/player/PlayerAction.java b/Mage.Tests/src/test/java/org/mage/test/player/PlayerAction.java index 62ffe0fa4c..a3509cd70b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/PlayerAction.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/PlayerAction.java @@ -37,7 +37,11 @@ public class PlayerAction { } /** - * Calls after action removed from commands queue later (for multi steps action, e.g. AI related) + * Calls after action removed from commands queue later (for multi steps + * action, e.g.AI related) + * + * @param game + * @param player */ public void onActionRemovedLater(Game game, TestPlayer player) { // diff --git a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java index 3a5fdc13bf..f2db711c38 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java @@ -73,7 +73,7 @@ public class RandomPlayer extends ComputerPlayer { } private Ability getAction(Game game) { - List playables = getPlayableAbilities(game); + List playables = getPlayableAbilities(game); Ability ability; while (true) { if (playables.size() == 1) { @@ -115,8 +115,8 @@ public class RandomPlayer extends ComputerPlayer { return ability; } - protected List getPlayableAbilities(Game game) { - List playables = getPlayable(game, true); + protected List getPlayableAbilities(Game game) { + List playables = getPlayable(game, true); playables.add(pass); return playables; } diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer.java index b0ec4c7b39..244e572636 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer.java @@ -29,39 +29,6 @@ public class TestComputerPlayer extends ComputerPlayer { this.testPlayerLink = testPlayerLink; } - @Override - public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { - // copy-paste for TestComputerXXX - - // workaround to cast fused cards in tests by it's NAMES (Wear, Tear, Wear // Tear) - // reason: TestPlayer uses outer computerPlayer to cast, not TestPlayer - switch (ability.getSpellAbilityType()) { - case SPLIT: - case SPLIT_FUSED: - case SPLIT_AFTERMATH: - if (!this.testPlayerLink.getChoices().isEmpty()) { - MageObject object = game.getObject(ability.getSourceId()); - if (object != null) { - LinkedHashMap useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game); - - // left, right or fused cast - for (String choose : this.testPlayerLink.getChoices()) { - for (ActivatedAbility activatedAbility : useableAbilities.values()) { - if (activatedAbility instanceof SpellAbility) { - if (((SpellAbility) activatedAbility).getCardName().equals(choose)) { - return (SpellAbility) activatedAbility; - } - } - } - } - } - } - } - - // default implementation by AI - return super.chooseSpellAbilityForCast(ability, game, noMana); - } - @Override public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { // copy-paste for TestComputerXXX diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer7.java b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer7.java index f8d9933eb4..d09df3087f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer7.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer7.java @@ -29,39 +29,6 @@ public class TestComputerPlayer7 extends ComputerPlayer7 { this.testPlayerLink = testPlayerLink; } - @Override - public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { - // copy-paste for TestComputerXXX - - // workaround to cast fused cards in tests by it's NAMES (Wear, Tear, Wear // Tear) - // reason: TestPlayer uses outer computerPlayer to cast, not TestPlayer - switch (ability.getSpellAbilityType()) { - case SPLIT: - case SPLIT_FUSED: - case SPLIT_AFTERMATH: - if (!this.testPlayerLink.getChoices().isEmpty()) { - MageObject object = game.getObject(ability.getSourceId()); - if (object != null) { - LinkedHashMap useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game); - - // left, right or fused cast - for (String choose : this.testPlayerLink.getChoices()) { - for (ActivatedAbility activatedAbility : useableAbilities.values()) { - if (activatedAbility instanceof SpellAbility) { - if (((SpellAbility) activatedAbility).getCardName().equals(choose)) { - return (SpellAbility) activatedAbility; - } - } - } - } - } - } - } - - // default implementation by AI - return super.chooseSpellAbilityForCast(ability, game, noMana); - } - @Override public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { // copy-paste for TestComputerXXX diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayerMonteCarlo.java b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayerMonteCarlo.java new file mode 100644 index 0000000000..3dd236fcd9 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayerMonteCarlo.java @@ -0,0 +1,40 @@ +package org.mage.test.player; + +import mage.MageObject; +import mage.abilities.ActivatedAbility; +import mage.abilities.SpellAbility; +import mage.constants.Outcome; +import mage.constants.RangeOfInfluence; +import mage.game.Game; +import mage.player.ai.ComputerPlayerMCTS; +import mage.target.Target; + +import java.util.LinkedHashMap; +import java.util.UUID; + +/** + * @author JayDi85 + */ + +// mock class to override AI logic in tests +public class TestComputerPlayerMonteCarlo extends ComputerPlayerMCTS { + + private TestPlayer testPlayerLink; + + public TestComputerPlayerMonteCarlo(String name, RangeOfInfluence range, int skill) { + super(name, range, skill); + } + + public void setTestPlayerLink(TestPlayer testPlayerLink) { + this.testPlayerLink = testPlayerLink; + } + + @Override + public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { + // copy-paste for TestComputerXXX + + // workaround for discard spells + // reason: TestPlayer uses outer computerPlayer to discard but inner code uses choose + return testPlayerLink.choose(outcome, target, sourceId, game); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 672140af98..016adfd48d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -1,9 +1,6 @@ package org.mage.test.player; -import mage.MageItem; -import mage.MageObject; -import mage.MageObjectReference; -import mage.ObjectColor; +import mage.*; import mage.abilities.*; import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.Cost; @@ -42,6 +39,7 @@ import mage.game.match.Match; import mage.game.match.MatchPlayer; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; +import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.game.tournament.Tournament; import mage.player.ai.ComputerPlayer; @@ -76,6 +74,9 @@ public class TestPlayer implements Player { private static final Logger LOGGER = Logger.getLogger(TestPlayer.class); public static final String TARGET_SKIP = "[target_skip]"; // stop/skip targeting + public static final String CHOICE_SKIP = "[choice_skip]"; // stop/skip choice + public static final String MANA_CANCEL = "[mana_cancel]"; // cancel payment + public static final String SKIP_FAILED_COMMAND = "[skip_failed_command]"; // skip next command in player's queue (can remove cast commands after try to activate) public static final String BLOCK_SKIP = "[block_skip]"; public static final String ATTACK_SKIP = "[attack_skip]"; public static final String NO_TARGET = "NO_TARGET"; // cast spell or activate ability without target defines @@ -85,7 +86,8 @@ public class TestPlayer implements Player { private boolean AIPlayer; // full playable AI private boolean AICanChooseInStrictMode = false; // AI can choose in custom aiXXX commands (e.g. on one priority or step) private final List actions = new ArrayList<>(); - private final Map actionsToRemovesLater = new HashMap<>(); // remove actions later, on next step (e.g. for AI commands) + private final Map actionsToRemoveLater = new HashMap<>(); // remove actions later, on next step (e.g. for AI commands) + private final Map>> rollbackActions = new HashMap<>(); // actions to add after a executed rollback private final List choices = new ArrayList<>(); // choices stack for choice private final List targets = new ArrayList<>(); // targets stack for choose (it's uses on empty direct target by cast command) private final Map aliases = new HashMap<>(); // aliases for game objects/players (use it for cards with same name to save and use) @@ -112,6 +114,12 @@ public class TestPlayer implements Player { computerPlayer.setTestPlayerLink(this); } + public TestPlayer(TestComputerPlayerMonteCarlo computerPlayer) { + this.computerPlayer = computerPlayer; + AIPlayer = false; + computerPlayer.setTestPlayerLink(this); + } + public TestPlayer(final TestPlayer testPlayer) { this.AIPlayer = testPlayer.AIPlayer; this.AICanChooseInStrictMode = testPlayer.AICanChooseInStrictMode; @@ -307,17 +315,7 @@ public class TestPlayer implements Player { if (group.startsWith("spell") || group.startsWith("!spell") || group.startsWith("target=null") || group.startsWith("manaInPool=")) { break; } - if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { - if (group.contains("FuseLeft-")) { - result = handleTargetString(group.substring(group.indexOf("FuseLeft-") + 9), ability, game); - } else if (group.startsWith("FuseRight-")) { - result = handleTargetString(group.substring(group.indexOf("FuseRight-") + 10), ability, game); - } else { - result = false; - } - } else { - result = handleTargetString(group, ability, game); - } + result = handleTargetString(group, ability, game); } return result; } @@ -362,7 +360,7 @@ public class TestPlayer implements Player { } } - public boolean isAbilityHaveTargetNameOrAlias(Game game, Ability ability, String nameOrAlias) { + public boolean hasAbilityTargetNameOrAlias(Game game, Ability ability, String nameOrAlias) { // use cases: // * Cast cardName with extra // * Cast @ref @@ -382,7 +380,7 @@ public class TestPlayer implements Player { foundObject = true; foundAbility = ability.toString().startsWith(nameOrAlias); } else { - foundObject = isObjectHaveTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject); + foundObject = hasObjectTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject); foundAbility = searchObject.startsWith(ALIAS_PREFIX) || ability.toString().startsWith(nameOrAlias); } } else if (nameOrAlias.startsWith(ALIAS_PREFIX)) { @@ -391,7 +389,7 @@ public class TestPlayer implements Player { Assert.assertTrue("ability alias must contains space", nameOrAlias.contains(" ")); String searchObject = nameOrAlias.substring(0, nameOrAlias.indexOf(" ")); String searchAbility = nameOrAlias.substring(nameOrAlias.indexOf(" ") + 1); - foundObject = isObjectHaveTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject); + foundObject = hasObjectTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject); foundAbility = ability.toString().startsWith(searchAbility); } else { // ability text @@ -402,7 +400,7 @@ public class TestPlayer implements Player { return foundObject && foundAbility; } - public boolean isObjectHaveTargetNameOrAlias(MageObject object, String nameOrAlias) { + public boolean hasObjectTargetNameOrAlias(MageObject object, String nameOrAlias) { if (object == null || nameOrAlias == null) { return false; } @@ -424,6 +422,8 @@ public class TestPlayer implements Player { // two search mode: for cards/permanents (strict) and for abilities (like) if (object instanceof Ability) { return object.getName().startsWith(nameOrAlias); + } else if (object instanceof Spell) { + return ((Spell) object).getSpellAbility().getName().startsWith(nameOrAlias); } else { return object.getName().equals(nameOrAlias); } @@ -506,7 +506,7 @@ public class TestPlayer implements Player { } // need by alias or by name - if (!isObjectHaveTargetNameOrAlias(object, targetName)) { + if (!hasObjectTargetNameOrAlias(object, targetName)) { continue; } @@ -552,16 +552,16 @@ public class TestPlayer implements Player { @Override public boolean priority(Game game) { // later remove actions (ai commands related) - if (actionsToRemovesLater.size() > 0) { + if (actionsToRemoveLater.size() > 0) { List removed = new ArrayList<>(); - actionsToRemovesLater.forEach((action, step) -> { + actionsToRemoveLater.forEach((action, step) -> { if (game.getStep().getType() != step) { action.onActionRemovedLater(game, this); actions.remove(action); removed.add(action); } }); - removed.forEach(actionsToRemovesLater::remove); + removed.forEach(actionsToRemoveLater::remove); } int numberOfActions = actions.size(); @@ -578,20 +578,27 @@ public class TestPlayer implements Player { if (groups.length > 2 && !checkExecuteCondition(groups, game)) { break; } - for (Ability ability : computerPlayer.getPlayable(game, true)) { // add wrong action log? - if (isAbilityHaveTargetNameOrAlias(game, ability, groups[0])) { + for (ActivatedAbility ability : computerPlayer.getPlayable(game, true)) { // add wrong action log? + if (hasAbilityTargetNameOrAlias(game, ability, groups[0])) { int bookmark = game.bookmarkState(); - Ability newAbility = ability.copy(); + ActivatedAbility newAbility = ability.copy(); if (groups.length > 1 && !groups[1].equals("target=" + NO_TARGET)) { groupsForTargetHandling = groups; } - if (computerPlayer.activateAbility((ActivatedAbility) newAbility, game)) { + if (computerPlayer.activateAbility(newAbility, game)) { actions.remove(action); groupsForTargetHandling = null; foundNoAction = 0; // Reset enless loop check because of no action return true; } else { - game.restoreState(bookmark, ability.getRule()); + computerPlayer.restoreState(bookmark, ability.getRule(), game); + + // skip failed command + if (!choices.isEmpty() && choices.get(0).equals(SKIP_FAILED_COMMAND)) { + actions.remove(action); + choices.remove(0); + return true; + } } groupsForTargetHandling = null; } @@ -607,7 +614,7 @@ public class TestPlayer implements Player { for (MageObject mageObject : manaObjects) { if (mageObject instanceof Permanent) { for (Ability manaAbility : ((Permanent) mageObject).getAbilities(game).getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { - if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0])) { + if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0])) { Ability newManaAbility = manaAbility.copy(); computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); actions.remove(action); @@ -616,7 +623,7 @@ public class TestPlayer implements Player { } } else if (mageObject instanceof Card) { for (Ability manaAbility : ((Card) mageObject).getAbilities(game).getAvailableActivatedManaAbilities(game.getState().getZone(mageObject.getId()), game)) { - if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0])) { + if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0])) { Ability newManaAbility = manaAbility.copy(); computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); actions.remove(action); @@ -625,7 +632,7 @@ public class TestPlayer implements Player { } } else { for (Ability manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(game.getState().getZone(mageObject.getId()), game)) { - if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0])) { + if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0])) { Ability newManaAbility = manaAbility.copy(); computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); actions.remove(action); @@ -637,7 +644,7 @@ public class TestPlayer implements Player { List manaPermsWithCost = computerPlayer.getAvailableManaProducersWithCost(game); for (Permanent perm : manaPermsWithCost) { for (ActivatedManaAbilityImpl manaAbility : perm.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { - if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0]) + if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0]) && manaAbility.canActivate(computerPlayer.getId(), game).canActivate()) { Ability newManaAbility = manaAbility.copy(); computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); @@ -651,7 +658,7 @@ public class TestPlayer implements Player { command = command.substring(command.indexOf("addCounters:") + 12); String[] groups = command.split("\\$"); for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { - if (isObjectHaveTargetNameOrAlias(permanent, groups[0])) { + if (hasObjectTargetNameOrAlias(permanent, groups[0])) { CounterType counterType = CounterType.findByName(groups[1]); Assert.assertNotNull("Invalid counter type " + groups[1], counterType); Counter counter = counterType.createInstance(Integer.parseInt(groups[2])); @@ -661,12 +668,18 @@ public class TestPlayer implements Player { } } } else if (action.getAction().startsWith("waitStackResolved")) { + boolean skipOneStackObjectOnly = action.getAction().equals("waitStackResolved:1"); if (game.getStack().isEmpty()) { - // can use next command + // all done, can use next command actions.remove(action); + continue; } else { - // need pass to empty stack - break; + // need to wait (don't remove command, except one skip only) + tryToPlayPriority(game); + if (skipOneStackObjectOnly) { + actions.remove(action); + } + return true; } } else if (action.getAction().startsWith("playerAction:")) { String command = action.getAction(); @@ -675,17 +688,22 @@ public class TestPlayer implements Player { String[] groups = command.split("\\$"); if (groups.length > 0) { if (groups[0].equals("Rollback")) { - if (groups.length > 1 && groups[1].startsWith("turns=")) { + if (groups.length > 2 && groups[1].startsWith("turns=") && groups[2].startsWith("rollbackBlock=")) { int turns = Integer.parseInt(groups[1].substring(6)); + int rollbackBlockNumber = Integer.parseInt(groups[2].substring(14)); game.rollbackTurns(turns); actions.remove(action); + addActionsAfterRollback(game, rollbackBlockNumber); return true; + } else { + Assert.fail("Rollback command misses parameter: " + command); } } if (groups[0].equals("Concede")) { game.concede(getId()); ((GameImpl) game).checkConcede(); actions.remove(action); + return true; } } } else if (action.getAction().startsWith(AI_PREFIX)) { @@ -703,7 +721,7 @@ public class TestPlayer implements Player { // play step if (command.equals(AI_COMMAND_PLAY_STEP)) { AICanChooseInStrictMode = true; // disable on action's remove - actionsToRemovesLater.put(action, game.getStep().getType()); + actionsToRemoveLater.put(action, game.getStep().getType()); computerPlayer.priority(game); return true; } @@ -766,6 +784,13 @@ public class TestPlayer implements Player { wasProccessed = true; } + // check permanent tapped count: target player, card name, tapped status, count + if (params[0].equals(CHECK_COMMAND_PERMANENT_TAPPED) && params.length == 5) { + assertPermanentTapped(action, game, game.getPlayer(UUID.fromString(params[1])), params[2], Boolean.parseBoolean(params[3]), Integer.parseInt(params[4])); + actions.remove(action); + wasProccessed = true; + } + // check permanent counters: card name, counter type, count if (params[0].equals(CHECK_COMMAND_PERMANENT_COUNTERS) && params.length == 4) { assertPermanentCounters(action, game, computerPlayer, params[1], CounterType.findByName(params[2]), Integer.parseInt(params[3])); @@ -780,6 +805,13 @@ public class TestPlayer implements Player { wasProccessed = true; } + // check graveyard count: card name, count + if (params[0].equals(CHECK_COMMAND_GRAVEYARD_COUNT) && params.length == 3) { + assertGraveyardCount(action, game, computerPlayer, params[1], Integer.parseInt(params[2])); + actions.remove(action); + wasProccessed = true; + } + // check hand count: count if (params[0].equals(CHECK_COMMAND_HAND_COUNT) && params.length == 2) { assertHandCount(action, game, computerPlayer, Integer.parseInt(params[1])); @@ -835,8 +867,24 @@ public class TestPlayer implements Player { actions.remove(action); wasProccessed = true; } + + // check stack size: need size + if (params[0].equals(CHECK_COMMAND_STACK_SIZE) && params.length == 2) { + assertStackSize(action, game, Integer.parseInt(params[1])); + actions.remove(action); + wasProccessed = true; + } + + // check stack object: stack ability name, amount + if (params[0].equals(CHECK_COMMAND_STACK_OBJECT) && params.length == 3) { + assertStackObject(action, game, params[1], Integer.parseInt(params[2])); + actions.remove(action); + wasProccessed = true; + } } - if (!wasProccessed) { + if (wasProccessed) { + return true; + } else { Assert.fail("Unknow check command or params: " + command); } } else if (action.getAction().startsWith(SHOW_PREFIX)) { @@ -930,19 +978,34 @@ public class TestPlayer implements Player { actions.remove(action); wasProccessed = true; } + + // show stack + if (params[0].equals(SHOW_COMMAND_STACK) && params.length == 1) { + printStart(action.getActionName()); + printStack(game); + printEnd(); + actions.remove(action); + wasProccessed = true; + } } - if (!wasProccessed) { + if (wasProccessed) { + return true; + } else { Assert.fail("Unknow show command or params: " + command); } } - } - } - if (AIPlayer) { - computerPlayer.priority(game); - } else { - computerPlayer.pass(game); + + // you don't need to use stack command all the time, so some cast commands can be skiped to next check + if (game.getStack().isEmpty()) { + this.chooseStrictModeFailed("cast/activate", game, + "Can't find available command - " + action.getAction() + " (use checkPlayableAbility for \"non available\" checks)", true); + } + } // turn/step } + + tryToPlayPriority(game); + // check to prevent endless loops if (numberOfActions == actions.size()) { foundNoAction++; @@ -956,6 +1019,40 @@ public class TestPlayer implements Player { return false; } + /** + * Adds actions to the player actions after an executed rollback Actions + * have to be added after the rollback becauuse otherwise the actions are + * not valid because otehr ot the same actions are already taken before the + * rollback. + * + * @param game + * @param rollbackBlock rollback block to add the actions for + */ + private void addActionsAfterRollback(Game game, int rollbackBlockNumber) { + Map> rollbackBlock = rollbackActions.get(rollbackBlockNumber); + if (rollbackBlock != null && !rollbackBlock.isEmpty()) { + for (Map.Entry> entry : rollbackBlock.entrySet()) { + TestPlayer testPlayer = (TestPlayer) game.getPlayer(entry.getKey()); + if (testPlayer != null) { + // Add the actions at the start of the action list + int pos = 0; + for (PlayerAction playerAction : entry.getValue()) { + testPlayer.getActions().add(pos, playerAction); + pos++; + } + } + } + } + } + + private void tryToPlayPriority(Game game) { + if (AIPlayer) { + computerPlayer.priority(game); + } else { + computerPlayer.pass(game); + } + } + private Permanent findPermanentWithAssert(PlayerAction action, Game game, Player player, String cardName) { for (Permanent perm : game.getBattlefield().getAllPermanents()) { // need by controller @@ -964,7 +1061,7 @@ public class TestPlayer implements Player { } // need by alias or by name - if (!isObjectHaveTargetNameOrAlias(perm, cardName)) { + if (!hasObjectTargetNameOrAlias(perm, cardName)) { continue; } @@ -1043,7 +1140,7 @@ public class TestPlayer implements Player { }); } - private void printAbilities(Game game, List abilities) { + private void printAbilities(Game game, List abilities) { System.out.println("Total abilities: " + (abilities != null ? abilities.size() : 0)); if (abilities == null) { return; @@ -1052,8 +1149,9 @@ public class TestPlayer implements Player { List data = abilities.stream() .map(a -> (a.getZone() + " -> " + a.getSourceObject(game).getIdName() + " -> " + + (a.toString().startsWith("Cast ") ? "[" + a.getManaCostsToPay().getText() + "] -> " : "") // printed cost, not modified + (a.toString().length() > 0 - ? a.toString().substring(0, Math.min(20, a.toString().length()) - 1) + ? a.toString().substring(0, Math.min(20, a.toString().length())) : a.getClass().getSimpleName()) + "...")) .sorted() @@ -1095,6 +1193,13 @@ public class TestPlayer implements Player { } } + private void printStack(Game game) { + System.out.println("Stack objects: " + game.getStack().size()); + game.getStack().forEach(stack -> { + System.out.println(stack.getStackAbility().toString()); + }); + } + private void assertPT(PlayerAction action, Game game, Player player, String permanentName, int Power, int Toughness) { Permanent perm = findPermanentWithAssert(action, game, player, permanentName); @@ -1139,9 +1244,9 @@ public class TestPlayer implements Player { } if (mustHave) { - Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must have ability " + abilityClass, true, founded); + Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must have the ability " + abilityClass, true, founded); } else { - Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must have not ability " + abilityClass, false, founded); + Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must not have the ability " + abilityClass, false, founded); } } @@ -1155,6 +1260,8 @@ public class TestPlayer implements Player { } if (mustHave && !founded) { + printStart("Available mana for " + computerPlayer.getName()); + printMana(game, computerPlayer.getManaAvailable(game)); printStart(action.getActionName()); printAbilities(game, computerPlayer.getPlayable(game, true)); printEnd(); @@ -1162,6 +1269,8 @@ public class TestPlayer implements Player { } if (!mustHave && founded) { + printStart("Available mana for " + computerPlayer.getName()); + printMana(game, computerPlayer.getManaAvailable(game)); printStart(action.getActionName()); printAbilities(game, computerPlayer.getPlayable(game, true)); printEnd(); @@ -1172,18 +1281,42 @@ public class TestPlayer implements Player { private void assertPermanentCount(PlayerAction action, Game game, Player player, String permanentName, int count) { int foundedCount = 0; for (Permanent perm : game.getBattlefield().getAllPermanents()) { - if (isObjectHaveTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) { + if (hasObjectTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) { foundedCount++; } } - Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must exists in " + count + " instances", count, foundedCount); + if (foundedCount != count) { + printStart("Permanents of " + player.getName()); + printPermanents(game, game.getBattlefield().getAllActivePermanents(player.getId())); + printEnd(); + Assert.fail(action.getActionName() + " - permanent " + permanentName + " must exists in " + count + " instances"); + } + } + + private void assertPermanentTapped(PlayerAction action, Game game, Player player, String permanentName, boolean tapped, int count) { + int foundedCount = 0; + for (Permanent perm : game.getBattlefield().getAllPermanents()) { + if (hasObjectTargetNameOrAlias(perm, permanentName) + && perm.getControllerId().equals(player.getId()) + && perm.isTapped() == tapped) { + foundedCount++; + } + } + + if (foundedCount != count) { + printStart("Permanents of " + player.getName()); + printPermanents(game, game.getBattlefield().getAllActivePermanents(player.getId())); + printEnd(); + Assert.fail(action.getActionName() + " - must have " + count + (tapped ? " tapped " : " untapped ") + + "permanents with name " + permanentName + ", but founded " + foundedCount); + } } private void assertPermanentCounters(PlayerAction action, Game game, Player player, String permanentName, CounterType counterType, int count) { int foundedCount = 0; for (Permanent perm : game.getBattlefield().getAllPermanents()) { - if (isObjectHaveTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) { + if (hasObjectTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) { foundedCount = perm.getCounters(game).getCount(counterType); } } @@ -1194,7 +1327,7 @@ public class TestPlayer implements Player { private void assertExileCount(PlayerAction action, Game game, Player player, String permanentName, int count) { int foundedCount = 0; for (Card card : game.getExile().getAllCards(game)) { - if (isObjectHaveTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) { + if (hasObjectTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) { foundedCount++; } } @@ -1202,6 +1335,17 @@ public class TestPlayer implements Player { Assert.assertEquals(action.getActionName() + " - card " + permanentName + " must exists in exile zone with " + count + " instances", count, foundedCount); } + private void assertGraveyardCount(PlayerAction action, Game game, Player player, String permanentName, int count) { + int foundedCount = 0; + for (Card card : player.getGraveyard().getCards(game)) { + if (hasObjectTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) { + foundedCount++; + } + } + + Assert.assertEquals(action.getActionName() + " - card " + permanentName + " must exists in graveyard zone with " + count + " instances", count, foundedCount); + } + private void assertHandCount(PlayerAction action, Game game, Player player, int count) { Assert.assertEquals(action.getActionName() + " - hand must contain " + count, count, player.getHand().size()); } @@ -1210,7 +1354,7 @@ public class TestPlayer implements Player { int realCount = 0; for (UUID cardId : player.getHand()) { Card card = game.getCard(cardId); - if (isObjectHaveTargetNameOrAlias(card, cardName)) { + if (hasObjectTargetNameOrAlias(card, cardName)) { realCount++; } } @@ -1222,7 +1366,7 @@ public class TestPlayer implements Player { int realCount = 0; for (UUID cardId : game.getCommandersIds(player)) { Card card = game.getCard(cardId); - if (isObjectHaveTargetNameOrAlias(card, cardName) && Zone.COMMAND.equals(game.getState().getZone(cardId))) { + if (hasObjectTargetNameOrAlias(card, cardName) && Zone.COMMAND.equals(game.getState().getZone(cardId))) { realCount++; } } @@ -1320,6 +1464,21 @@ public class TestPlayer implements Player { } } + private void assertStackSize(PlayerAction action, Game game, int needStackSize) { + Assert.assertEquals(action.getActionName() + " - stack size must be " + needStackSize + " but is " + game.getStack().size(), needStackSize, game.getStack().size()); + } + + private void assertStackObject(PlayerAction action, Game game, String stackAbilityName, int needAmount) { + long foundedAmount = game.getStack() + .stream() + .filter(stack -> stack.getStackAbility().toString().startsWith(stackAbilityName)) + .count(); + if (needAmount != foundedAmount) { + printStack(game); + Assert.fail(action.getActionName() + " - stack must have " + needAmount + " objects with ability [" + stackAbilityName + "] but have " + foundedAmount); + } + } + private void assertManaPoolInner(PlayerAction action, Player player, ManaType manaType, Integer amount) { Integer normal = player.getManaPool().getMana().get(manaType); Integer conditional = player.getManaPool().getConditionalMana().stream().mapToInt(a -> a.get(manaType)).sum(); // calcs FULL conditional mana, not real conditions @@ -1411,6 +1570,16 @@ public class TestPlayer implements Player { boolean madeAttackByAction = false; for (Iterator it = actions.iterator(); it.hasNext(); ) { PlayerAction action = it.next(); + + // aiXXX commands + if (action.getTurnNum() == game.getTurnNum() && action.getAction().equals(AI_PREFIX + AI_COMMAND_PLAY_STEP)) { + mustAttackByAction = true; + madeAttackByAction = true; + this.computerPlayer.selectAttackers(game, attackingPlayerId); + it.remove(); + break; + } + if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) { mustAttackByAction = true; String command = action.getAction(); @@ -1429,7 +1598,7 @@ public class TestPlayer implements Player { if (group.startsWith("planeswalker=")) { String planeswalkerName = group.substring(group.indexOf("planeswalker=") + 13); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, game)) { - if (isObjectHaveTargetNameOrAlias(permanent, planeswalkerName)) { + if (hasObjectTargetNameOrAlias(permanent, planeswalkerName)) { defenderId = permanent.getId(); } } @@ -1473,7 +1642,7 @@ public class TestPlayer implements Player { this.chooseStrictModeFailed("attacker", game, "select attackers must use attack command but don't"); } - // AI play if no actions available + // AI FULL play if no actions available if (!mustAttackByAction && this.AIPlayer) { this.computerPlayer.selectAttackers(game, attackingPlayerId); } @@ -1486,14 +1655,22 @@ public class TestPlayer implements Player { @Override public void selectBlockers(Game game, UUID defendingPlayerId) { - List tempActions = new ArrayList<>(actions); UUID opponentId = game.getOpponents(computerPlayer.getId()).iterator().next(); - // Map of Blocker reference -> list of creatures blocked - Map> blockedCreaturesByCreature = new HashMap<>(); + Map> blockedCreaturesList = getBlockedCreaturesByCreatureList(game); + boolean mustBlockByAction = false; for (PlayerAction action : tempActions) { + + // aiXXX commands + if (action.getTurnNum() == game.getTurnNum() && action.getAction().equals(AI_PREFIX + AI_COMMAND_PLAY_STEP)) { + mustBlockByAction = true; + this.computerPlayer.selectBlockers(game, defendingPlayerId); + actions.remove(action); + break; + } + if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("block:")) { mustBlockByAction = true; String command = action.getAction(); @@ -1510,7 +1687,8 @@ public class TestPlayer implements Player { String attackerName = groups[1]; Permanent attacker = findPermanent(new FilterAttackingCreature(), attackerName, opponentId, game); Permanent blocker = findPermanent(new FilterControlledPermanent(), blockerName, computerPlayer.getId(), game); - if (canBlockAnother(game, blocker, attacker, blockedCreaturesByCreature)) { + + if (canBlockAnother(game, blocker, attacker, blockedCreaturesList)) { computerPlayer.declareBlocker(defendingPlayerId, blocker.getId(), attacker.getId(), game); actions.remove(action); } else { @@ -1518,40 +1696,74 @@ public class TestPlayer implements Player { } } } - checkMultipleBlockers(game, blockedCreaturesByCreature); + checkMultipleBlockers(game, blockedCreaturesList); - // AI play if no actions available + // AI FULL play if no actions available if (!mustBlockByAction && this.AIPlayer) { this.computerPlayer.selectBlockers(game, defendingPlayerId); } } - // Checks if a creature can block at least one more creature - private boolean canBlockAnother(Game game, Permanent blocker, Permanent attacker, Map> blockedCreaturesByCreature) { + private Map> getBlockedCreaturesByCreatureList(Game game) { + // collect already declared blockers info (e.g. after auto-adding on block requirements) + // blocker -> blocked attackers + Map> blockedCreaturesByCreature = new HashMap<>(); + for (CombatGroup combatGroup : game.getCombat().getGroups()) { + for (UUID combatBlockerId : combatGroup.getBlockers()) { + Permanent blocker = game.getPermanent(combatBlockerId); + if (blocker != null) { + // collect all blocked attackers + List blocked = getBlockedAttackers(game, blocker, blockedCreaturesByCreature); + for (UUID combatAttackerId : combatGroup.getAttackers()) { + Permanent combatAttacker = game.getPermanent(combatAttackerId); + if (combatAttacker != null) { + blocked.add(new MageObjectReference(combatAttacker, game)); + } + } + } + } + } + return blockedCreaturesByCreature; + } + + private List getBlockedAttackers(Game game, Permanent blocker, Map> blockedCreaturesByCreature) { + // finds creatures list blocked by blocker permanent MageObjectReference blockerRef = new MageObjectReference(blocker, game); - // See if we already reference this blocker for (MageObjectReference r : blockedCreaturesByCreature.keySet()) { if (r.equals(blockerRef)) { - // Use the existing reference if we do + // already exist blockerRef = r; } } + + if (!blockedCreaturesByCreature.containsKey(blockerRef)) { + blockedCreaturesByCreature.put(blockerRef, new ArrayList<>()); + } List blocked = blockedCreaturesByCreature.getOrDefault(blockerRef, new ArrayList<>()); + return blocked; + } + + private boolean canBlockAnother(Game game, Permanent blocker, Permanent attacker, Map> blockedCreaturesByCreature) { + // check if blocker can block one more attacker and adds it + List blocked = getBlockedAttackers(game, blocker, blockedCreaturesByCreature); int numBlocked = blocked.size(); + // Can't block any more creatures if (++numBlocked > blocker.getMaxBlocks()) { return false; } + // Add the attacker reference to the list of creatures this creature is blocking blocked.add(new MageObjectReference(attacker, game)); - blockedCreaturesByCreature.put(blockerRef, blocked); return true; } - // Check for Menace type abilities - if creatures can be blocked by >X or > blockedCreaturesByCreature) { + // Check for Menace type abilities - if creatures can be blocked by >X or blockersForAttacker = new HashMap<>(); + // Calculate the number of blockers each attacker has for (List attackers : blockedCreaturesByCreature.values()) { for (MageObjectReference mr : attackers) { @@ -1559,6 +1771,7 @@ public class TestPlayer implements Player { blockersForAttacker.put(mr, blockers + 1); } } + // Check each attacker is blocked by an allowed amount of creatures for (Map.Entry entry : blockersForAttacker.entrySet()) { Permanent attacker = entry.getKey().getPermanent(game); @@ -1608,8 +1821,19 @@ public class TestPlayer implements Player { } private void chooseStrictModeFailed(String choiceType, Game game, String reason) { + chooseStrictModeFailed(choiceType, game, reason, false); + } + + private void chooseStrictModeFailed(String choiceType, Game game, String reason, boolean printAbilities) { if (strictChooseMode && !AICanChooseInStrictMode) { - Assert.fail("Missing " + choiceType + " def for" + if (printAbilities) { + printStart("Available mana for " + computerPlayer.getName()); + printMana(game, computerPlayer.getManaAvailable(game)); + printStart("Available abilities for " + computerPlayer.getName()); + printAbilities(game, computerPlayer.getPlayable(game, true)); + printEnd(); + } + Assert.fail("Missing " + choiceType.toUpperCase(Locale.ENGLISH) + " def for" + " turn " + game.getTurnNum() + ", step " + (game.getStep() != null ? game.getStep().getType().name() : "not started") + ", " + this.getName() @@ -1625,10 +1849,10 @@ public class TestPlayer implements Player { modesSet.remove(0); return null; } - int selectedMode = Integer.parseInt(modesSet.get(0)); + int needMode = Integer.parseInt(modesSet.get(0)); int i = 1; for (Mode mode : modes.getAvailableModes(source, game)) { - if (i == selectedMode) { + if (i == needMode) { modesSet.remove(0); return mode; } @@ -1693,7 +1917,17 @@ public class TestPlayer implements Player { assertAliasSupportInChoices(true); if (!choices.isEmpty()) { - List usedChoices = new ArrayList<>(); + + // skip choices + if (choices.get(0).equals(CHOICE_SKIP)) { + Assert.assertTrue("found skip choice, but it require more choices, needs " + + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more", + target.getTargets().size() >= target.getMinNumberOfTargets()); + choices.remove(0); + return true; + } + + List usedChoices = new ArrayList<>(); List usedTargets = new ArrayList<>(); Ability source = null; @@ -1730,7 +1964,7 @@ public class TestPlayer implements Player { if (target.getTargets().contains(permanent.getId())) { continue; } - if (isObjectHaveTargetNameOrAlias(permanent, targetName)) { + if (hasObjectTargetNameOrAlias(permanent, targetName)) { if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.add(permanent.getId(), game); @@ -1781,7 +2015,8 @@ public class TestPlayer implements Player { boolean targetCompleted = false; CheckAllChoices: - for (String choiceRecord : choices) { + for (int choiceIndex = 0; choiceIndex < choices.size(); choiceIndex++) { + String choiceRecord = choices.get(choiceIndex); if (targetCompleted) { break CheckAllChoices; } @@ -1795,7 +2030,7 @@ public class TestPlayer implements Player { CheckTargetsList: for (UUID targetId : possibleCards) { MageObject targetObject = game.getObject(targetId); - if (isObjectHaveTargetNameOrAlias(targetObject, possibleChoice)) { + if (hasObjectTargetNameOrAlias(targetObject, possibleChoice)) { if (target.canTarget(targetObject.getId(), game)) { // only unique targets if (usedTargets.contains(targetObject.getId())) { @@ -1821,14 +2056,19 @@ public class TestPlayer implements Player { } if (targetFound) { - usedChoices.add(choiceRecord); + usedChoices.add(choiceIndex); } } // apply only on ALL targets or revert if (usedChoices.size() > 0) { if (target.isChosen()) { - choices.removeAll(usedChoices); + // remove all used choices + for (int i = choices.size(); i >= 0; i--) { + if (usedChoices.contains(i)) { + choices.remove(i); + } + } return true; } else { Assert.fail("Not full targets list."); @@ -1848,7 +2088,7 @@ public class TestPlayer implements Player { for (UUID targetId : possibleTargets) { MageObject targetObject = game.getObject(targetId); if (targetObject != null) { - if (isObjectHaveTargetNameOrAlias(targetObject, targetName)) { + if (hasObjectTargetNameOrAlias(targetObject, targetName)) { List alreadyTargetted = target.getTargets(); if (t.canTarget(targetObject.getId(), game)) { if (alreadyTargetted != null && !alreadyTargetted.contains(targetObject.getId())) { @@ -1878,7 +2118,7 @@ public class TestPlayer implements Player { // ignore player select if (!target.getMessage().equals("Select a starting player")) { - this.chooseStrictModeFailed("choice", game, getInfo(game.getObject(sourceId)) + "; " + getInfo(target)); + this.chooseStrictModeFailed("choice", game, getInfo(game.getObject(sourceId)) + ";\n" + getInfo(target)); } return computerPlayer.choose(outcome, target, sourceId, game, options); } @@ -1932,16 +2172,17 @@ public class TestPlayer implements Player { || target.getOriginalTarget() instanceof TargetPermanentOrPlayer || target.getOriginalTarget() instanceof TargetDefender) { for (String targetDefinition : targets) { + if (!targetDefinition.startsWith("targetPlayer=")) { + continue; + } checkTargetDefinitionMarksSupport(target, targetDefinition, "="); - if (targetDefinition.startsWith("targetPlayer=")) { - String playerName = targetDefinition.substring(targetDefinition.indexOf("targetPlayer=") + 13); - for (Player player : game.getPlayers().values()) { - if (player.getName().equals(playerName) - && target.canTarget(computerPlayer.getId(), player.getId(), source, game)) { - target.addTarget(player.getId(), source, game); - targets.remove(targetDefinition); - return true; - } + String playerName = targetDefinition.substring(targetDefinition.indexOf("targetPlayer=") + 13); + for (Player player : game.getPlayers().values()) { + if (player.getName().equals(playerName) + && target.canTarget(computerPlayer.getId(), player.getId(), source, game)) { + target.addTarget(player.getId(), source, game); + targets.remove(targetDefinition); + return true; } } } @@ -1954,10 +2195,14 @@ public class TestPlayer implements Player { || (target.getOriginalTarget() instanceof TargetCreatureOrPlayer) || (target.getOriginalTarget() instanceof TargetDefender)) { for (String targetDefinition : targets) { + if (targetDefinition.startsWith("targetPlayer=")) { + continue; + } checkTargetDefinitionMarksSupport(target, targetDefinition, "^[]"); String[] targetList = targetDefinition.split("\\^"); boolean targetFound = false; for (String targetName : targetList) { + targetFound = false; // must have all valid targets from list boolean originOnly = false; boolean copyOnly = false; if (targetName.endsWith("]")) { @@ -1984,12 +2229,12 @@ public class TestPlayer implements Player { filter = ((FilterPlaneswalkerOrPlayer) filter).getFilterPermanent(); } for (Permanent permanent : game.getBattlefield().getActivePermanents((FilterPermanent) filter, abilityControllerId, sourceId, game)) { - if (isObjectHaveTargetNameOrAlias(permanent, targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove exp code search? + if (hasObjectTargetNameOrAlias(permanent, targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove exp code search? if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.addTarget(permanent.getId(), source, game); targetFound = true; - break; // return to for (String targetName + break; // return to next targetName } } } @@ -2003,18 +2248,19 @@ public class TestPlayer implements Player { } // card in hand - if (target.getOriginalTarget() instanceof TargetCardInHand) { + if (target.getOriginalTarget() instanceof TargetCardInHand + || target.getOriginalTarget() instanceof TargetDiscard) { for (String targetDefinition : targets) { checkTargetDefinitionMarksSupport(target, targetDefinition, "^"); String[] targetList = targetDefinition.split("\\^"); boolean targetFound = false; for (String targetName : targetList) { - for (Card card : computerPlayer.getHand().getCards(((TargetCardInHand) target.getOriginalTarget()).getFilter(), game)) { - if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? + for (Card card : computerPlayer.getHand().getCards(((TargetCard) target.getOriginalTarget()).getFilter(), game)) { + if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { target.addTarget(card.getId(), source, game); targetFound = true; - break; // return to for (String targetName + break; // return to next targetName } } } @@ -2035,11 +2281,11 @@ public class TestPlayer implements Player { boolean targetFound = false; for (String targetName : targetList) { for (Card card : game.getExile().getCards(targetFull.getFilter(), game)) { - if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? + if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { target.addTarget(card.getId(), source, game); targetFound = true; - break; // return to for (String targetName + break; // return to next targetName } } } @@ -2060,11 +2306,11 @@ public class TestPlayer implements Player { boolean targetFound = false; for (String targetName : targetList) { for (Card card : game.getBattlefield().getAllActivePermanents()) { - if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? + if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? if (targetFull.canTarget(abilityControllerId, card.getId(), source, game) && !targetFull.getTargets().contains(card.getId())) { targetFull.add(card.getId(), game); targetFound = true; - break; // return to for (String targetName + break; // return to next targetName } } } @@ -2110,11 +2356,11 @@ public class TestPlayer implements Player { for (UUID playerId : needPlayers) { Player player = game.getPlayer(playerId); for (Card card : player.getGraveyard().getCards(targetFull.getFilter(), game)) { - if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? + if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { target.addTarget(card.getId(), source, game); targetFound = true; - break IterateGraveyards; // return to for (String targetName + break IterateGraveyards; // return to next targetName } } } @@ -2137,11 +2383,11 @@ public class TestPlayer implements Player { boolean targetFound = false; for (String targetName : targetList) { for (StackObject stackObject : game.getStack()) { - if (isObjectHaveTargetNameOrAlias(stackObject, targetName)) { + if (hasObjectTargetNameOrAlias(stackObject, targetName)) { if (target.canTarget(abilityControllerId, stackObject.getId(), source, game) && !target.getTargets().contains(stackObject.getId())) { target.addTarget(stackObject.getId(), source, game); targetFound = true; - break; // return to for (String targetName + break; // return to next targetName } } } @@ -2187,7 +2433,7 @@ public class TestPlayer implements Player { boolean targetFound = false; for (String targetName : targetList) { for (Card card : cards.getCards(game)) { - if (isObjectHaveTargetNameOrAlias(card, targetName) && !target.getTargets().contains(card.getId())) { + if (hasObjectTargetNameOrAlias(card, targetName) && !target.getTargets().contains(card.getId())) { target.addTarget(card.getId(), source, game); targetFound = true; break; @@ -2204,7 +2450,7 @@ public class TestPlayer implements Player { //Assert.fail("Wrong target"); } - this.chooseStrictModeFailed("target", game, getInfo(source, game) + "; " + getInfo(target)); + this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target)); return computerPlayer.chooseTarget(outcome, cards, target, source, game); } @@ -2339,16 +2585,7 @@ public class TestPlayer implements Player { @Override public void restore(Player player) { - this.modesSet.clear(); - this.modesSet.addAll(((TestPlayer) player).modesSet); - this.actions.clear(); - this.actions.addAll(((TestPlayer) player).actions); - this.choices.clear(); - this.choices.addAll(((TestPlayer) player).choices); - this.targets.clear(); - this.targets.addAll(((TestPlayer) player).targets); - this.aliases.clear(); - this.aliases.putAll(((TestPlayer) player).aliases); + // no rollback for test player meta data (modesSet, actions, choices, targets, aliases) computerPlayer.restore(player); } @@ -2451,13 +2688,13 @@ public class TestPlayer implements Player { } @Override - public int drawCards(int num, Game game) { - return computerPlayer.drawCards(num, game); + public int drawCards(int num, UUID sourceId, Game game) { + return computerPlayer.drawCards(num, sourceId, game); } @Override - public int drawCards(int num, Game game, List appliedEffects) { - return computerPlayer.drawCards(num, game, appliedEffects); + public int drawCards(int num, UUID sourceId, Game game, List appliedEffects) { + return computerPlayer.drawCards(num, sourceId, game, appliedEffects); } @Override @@ -2490,6 +2727,11 @@ public class TestPlayer implements Player { return computerPlayer.discard(amount, random, source, game); } + @Override + public Cards discard(Cards cards, Ability source, Game game) { + return computerPlayer.discard(cards, source, game); + } + @Override public boolean discard(Card card, Ability source, Game game) { return computerPlayer.discard(card, source, game); @@ -2545,6 +2787,21 @@ public class TestPlayer implements Player { return computerPlayer.putCardsOnTopOfLibrary(cards, game, source, anyOrder); } + @Override + public boolean putCardsOnTopOfLibrary(Card card, Game game, Ability source, boolean anyOrder) { + return computerPlayer.putCardsOnTopOfLibrary(card, game, source, anyOrder); + } + + @Override + public boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source) { + return computerPlayer.shuffleCardsToLibrary(cards, game, source); + } + + @Override + public boolean shuffleCardsToLibrary(Card card, Game game, Ability source) { + return computerPlayer.shuffleCardsToLibrary(card, game, source); + } + @Override public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts manaCosts, Costs costs) { computerPlayer.setCastSourceIdWithAlternateMana(sourceId, manaCosts, costs); @@ -2598,8 +2855,8 @@ public class TestPlayer implements Player { } @Override - public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { - return computerPlayer.getUseableActivatedAbilities(object, zone, game); + public LinkedHashMap getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) { + return computerPlayer.getPlayableActivatedAbilities(object, zone, game); } @Override @@ -2887,11 +3144,6 @@ public class TestPlayer implements Player { computerPlayer.pass(game); } - @Override - public boolean isEmptyDraw() { - return computerPlayer.isEmptyDraw(); - } - @Override public void resetPassed() { computerPlayer.resetPassed(); @@ -3062,12 +3314,18 @@ public class TestPlayer implements Player { return computerPlayer.getManaAvailable(game); } - public List getAvailableManaProducersWithCost(Game game) { - return computerPlayer.getAvailableManaProducersWithCost(game); + @Override + public void addAvailableTriggeredMana(List availableTriggeredMana) { + computerPlayer.addAvailableTriggeredMana(availableTriggeredMana); } @Override - public List getPlayable(Game game, boolean hidden) { + public List> getAvailableTriggeredMana() { + return computerPlayer.getAvailableTriggeredMana(); + } + + @Override + public List getPlayable(Game game, boolean hidden) { return computerPlayer.getPlayable(game, hidden); } @@ -3122,8 +3380,8 @@ public class TestPlayer implements Player { } @Override - public boolean canPayLifeCost() { - return computerPlayer.canPayLifeCost(); + public boolean canPayLifeCost(Ability ability) { + return computerPlayer.canPayLifeCost(ability); } @Override @@ -3301,6 +3559,11 @@ public class TestPlayer implements Player { return computerPlayer.moveCardToCommandWithInfo(card, sourceId, game, fromZone); } + @Override + public Cards millCards(int toMill, Ability source, Game game) { + return computerPlayer.millCards(toMill, source, game); + } + @Override public boolean hasOpponent(UUID playerToCheckId, Game game) { return computerPlayer.hasOpponent(playerToCheckId, game); @@ -3391,12 +3654,6 @@ public class TestPlayer implements Player { computerPlayer.cleanUpOnMatchEnd(); } - @Override - public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { - Assert.fail("That's method must calls only from computerPlayer->cast(), see TestComputerPlayerXXX"); - return computerPlayer.chooseSpellAbilityForCast(ability, game, noMana); - } - @Override public void skip() { computerPlayer.skip(); @@ -3425,7 +3682,7 @@ public class TestPlayer implements Player { if (target.getTargets().contains(card.getId())) { continue; } - if (isObjectHaveTargetNameOrAlias(card, targetName)) { + if (hasObjectTargetNameOrAlias(card, targetName)) { if (target.isNotTarget() || target.canTarget(card.getId(), game)) { target.add(card.getId(), game); targetFound = true; @@ -3503,7 +3760,7 @@ public class TestPlayer implements Player { } } - this.chooseStrictModeFailed("target", game, getInfo(source, game) + "; " + getInfo(target)); + this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target)); return computerPlayer.chooseTargetAmount(outcome, target, source, game); } @@ -3528,38 +3785,83 @@ public class TestPlayer implements Player { groupsForTargetHandling = null; if (!computerPlayer.getManaPool().isAutoPayment()) { - // manual pay by mana clicks/commands if (!choices.isEmpty()) { - String needColor = choices.get(0); - switch (needColor) { + // manual pay by mana clicks/commands + + // simulate cancel on mana payment (e.g. user press on cancel button) + if (choices.get(0).equals(MANA_CANCEL)) { + choices.remove(0); + return false; + } + + String choice = choices.get(0); + boolean choiceUsed = false; + boolean choiceRemoved = false; + switch (choice) { case "White": Assert.assertTrue("pool must have white mana", computerPlayer.getManaPool().getWhite() > 0); computerPlayer.getManaPool().unlockManaType(ManaType.WHITE); + choiceUsed = true; break; case "Blue": Assert.assertTrue("pool must have blue mana", computerPlayer.getManaPool().getBlue() > 0); computerPlayer.getManaPool().unlockManaType(ManaType.BLUE); + choiceUsed = true; break; case "Black": Assert.assertTrue("pool must have black mana", computerPlayer.getManaPool().getBlack() > 0); computerPlayer.getManaPool().unlockManaType(ManaType.BLACK); + choiceUsed = true; break; case "Red": Assert.assertTrue("pool must have red mana", computerPlayer.getManaPool().getRed() > 0); computerPlayer.getManaPool().unlockManaType(ManaType.RED); + choiceUsed = true; break; case "Green": Assert.assertTrue("pool must have green mana", computerPlayer.getManaPool().getGreen() > 0); computerPlayer.getManaPool().unlockManaType(ManaType.GREEN); + choiceUsed = true; + break; + case "Colorless": + Assert.assertTrue("pool must have colorless mana", computerPlayer.getManaPool().getColorless() > 0); + computerPlayer.getManaPool().unlockManaType(ManaType.COLORLESS); + choiceUsed = true; break; default: - Assert.fail("Unknown choice command for mana unlock: " + needColor); + // go to special block + //Assert.fail("Unknown choice command for mana unlock: " + needColor); break; } - choices.remove(0); - return true; + + // manual pay by special actions like convoke + if (!choiceUsed) { + Map specialActions = game.getState().getSpecialActions().getControlledBy(this.getId(), true); + for (SpecialAction specialAction : specialActions.values()) { + if (specialAction.getRule(true).startsWith(choice)) { + if (specialAction.canActivate(this.getId(), game).canActivate()) { + choices.remove(0); + choiceRemoved = true; + specialAction.setUnpaidMana(unpaid); + if (activateAbility(specialAction, game)) { + choiceUsed = true; + } + } + } + } + } + + if (choiceUsed) { + if (!choiceRemoved) { + choices.remove(0); + } + return true; + } else { + Assert.fail("Can't use choice in play mana: " + choice); + } } - Assert.fail(this.getName() + " disabled mana auto-payment, but no choices found for color unlock in pool for unpaid cost: " + unpaid.getText()); + + Assert.fail(this.getName() + " disabled mana auto-payment, but no choices found for color unlock in pool or special action for unpaid cost: " + unpaid.getText()); } return computerPlayer.playMana(ability, unpaid, promptText, game); @@ -3731,11 +4033,6 @@ public class TestPlayer implements Player { computerPlayer.addPhyrexianToColors(colors); } - @Override - public void removePhyrexianFromColors(FilterMana colors) { - computerPlayer.removePhyrexianFromColors(colors); - } - @Override public FilterMana getPhyrexianColors() { return computerPlayer.getPhyrexianColors(); @@ -3743,12 +4040,14 @@ public class TestPlayer implements Player { @Override public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) { - String allInfo = ""; + assertAliasSupportInChoices(false); Map useable = PlayerImpl.getSpellAbilities(this.getId(), card, game.getState().getZone(card.getId()), game); - allInfo = useable.values().stream().map(Object::toString).collect(Collectors.joining("\n")); + String allInfo = useable.values().stream().map(Object::toString).collect(Collectors.joining("\n")); + if (useable.size() == 1) { + return (SpellAbility) useable.values().iterator().next(); + } - assertAliasSupportInChoices(false); if (!choices.isEmpty()) { for (ActivatedAbility ability : useable.values()) { if (ability.toString().startsWith(choices.get(0))) { @@ -3772,4 +4071,9 @@ public class TestPlayer implements Player { public void setAICanChooseInStrictMode(boolean AICanChooseInStrictMode) { this.AICanChooseInStrictMode = AICanChooseInStrictMode; } + + public Map>> getRollbackActions() { + return rollbackActions; + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/rollback/CopyAnthemEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/rollback/CopyAnthemEffectTest.java new file mode 100644 index 0000000000..787559a83c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/rollback/CopyAnthemEffectTest.java @@ -0,0 +1,38 @@ +package org.mage.test.rollback; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class CopyAnthemEffectTest extends CardTestPlayerBase { + + // Addresses issue #6618 - Creatures get +1/+1 when we cancel the cast of a spell + + // Creatures of one player get +1/+1 every time someone cancel the cast of a spell. + // Looks like the repeating effect was Force Of Virtue's static ability + // There was also a Mirrormade cast to copy Force of Virtue + + // Further investigation shown that the problem could be also reduced to any anthem effect copied by other permanent, then rollbacking + @Test + public void copyAnthemEffect() { + addCard(Zone.BATTLEFIELD, playerA, "Runeclaw Bear"); // 2/2 vanilla creature + addCard(Zone.BATTLEFIELD, playerA, "Glorious Anthem"); // creatures you control have +1/+1 + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + addCard(Zone.HAND, playerA, "Copy Enchantment"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Copy Enchantment"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Glorious Anthem"); + + rollbackTurns(3, PhaseStep.UPKEEP, playerA, 0); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Runeclaw Bear", 4, 4); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/rollback/DemonicPactTest.java b/Mage.Tests/src/test/java/org/mage/test/rollback/DemonicPactTest.java index 06e5b4791e..9124fac54a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/rollback/DemonicPactTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/rollback/DemonicPactTest.java @@ -1,77 +1,126 @@ - -package org.mage.test.rollback; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class DemonicPactTest extends CardTestPlayerBase { - - @Test - public void testModes() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); - // At the beginning of your upkeep, choose one that hasn't been chosen - // (1) - Demonic Pact deals 4 damage to any target and you gain 4 life; - // (2) - Target opponent discards two cards - // (3) - Draw two cards - // (4) - You lose the game. - addCard(Zone.HAND, playerA, "Demonic Pact"); // Enchantment {2}{B}{B} - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Demonic Pact"); - - setModeChoice(playerA, "3"); - - setModeChoice(playerA, "2"); - addTarget(playerA, playerB); - - setModeChoice(playerA, "1"); - addTarget(playerA, playerB); - - setStopAt(7, PhaseStep.PRECOMBAT_MAIN); - execute(); - - assertPermanentCount(playerA, "Demonic Pact", 1); - assertLife(playerA, 24); - assertLife(playerB, 16); - assertHandCount(playerB, 1); // discard 2 + 3 from regular draws - - assertHandCount(playerA, 5); // two from Demonic Pact + 3 from regular draws - - } - - /* - The rollback to the start of the turn does not correctly reset the already selected choices from Demonic Pact. They are not available again. - */ - @Test - public void testModeAfterRollback() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); - // At the beginning of your upkeep, choose one that hasn't been chosen - // (1) - Demonic Pact deals 4 damage to any target and you gain 4 life; - // (2) - Target opponent discards two cards - // (3) - Draw two cards - // (4) - You lose the game. - addCard(Zone.HAND, playerA, "Demonic Pact"); // Enchantment {2}{B}{B} - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Demonic Pact"); - - setModeChoice(playerA, "1"); - addTarget(playerA, playerB); - - rollbackTurns(3, PhaseStep.PRECOMBAT_MAIN, playerA, 0); - - setStopAt(3, PhaseStep.BEGIN_COMBAT); - execute(); - - assertHandCount(playerA, 1); // 1 from regular draws - assertHandCount(playerB, 1); // 1 from regular draw - - assertLife(playerA, 24); - assertLife(playerB, 16); - - } -} +package org.mage.test.rollback; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class DemonicPactTest extends CardTestPlayerBase { + + @Test + public void testModes() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + // At the beginning of your upkeep, choose one that hasn't been chosen + // (1) - Demonic Pact deals 4 damage to any target and you gain 4 life; + // (2) - Target opponent discards two cards + // (3) - Draw two cards + // (4) - You lose the game. + addCard(Zone.HAND, playerA, "Demonic Pact"); // Enchantment {2}{B}{B} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Demonic Pact"); + + setModeChoice(playerA, "3"); + + setModeChoice(playerA, "2"); + addTarget(playerA, playerB); + + setModeChoice(playerA, "1"); + addTarget(playerA, playerB); + + setStopAt(7, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Demonic Pact", 1); + assertLife(playerA, 24); + assertLife(playerB, 16); + assertHandCount(playerB, 1); // discard 2 + 3 from regular draws + + assertHandCount(playerA, 5); // two from Demonic Pact + 3 from regular draws + + } + + /* + The rollback to the start of the turn does not correctly reset the already selected choices from Demonic Pact. They are not available again. + */ + @Test + public void testModeAfterRollback() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + // At the beginning of your upkeep, choose one that hasn't been chosen + // (1) - Demonic Pact deals 4 damage to any target and you gain 4 life; + // (2) - Target opponent discards two cards + // (3) - Draw two cards + // (4) - You lose the game. + addCard(Zone.HAND, playerA, "Demonic Pact"); // Enchantment {2}{B}{B} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Demonic Pact"); + + setModeChoice(playerA, "1"); + addTarget(playerA, playerB); + + rollbackTurns(3, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, 1); // 1 from regular draws + assertHandCount(playerB, 1); // 1 from regular draw + + assertLife(playerA, 24); + assertLife(playerB, 16); + + } + + /** + * Rollback problem with Pact of Negation etc [cards] #4659 + * + * Potential bug here for [Pact of Negation] and similar cards with 0 cost + * and demand to pay or lose the next turn. + * + * So can you check the following scenario where I think the game is buggy: + * an opponent casts pact of negation on my turn, his next turn he requests + * a rollback to beginning of his turn -- bingo I'm a winner and he loses + * the game. The log says I'm the winner and the opponent lost and that is + * immediately after rollback request. + */ + @Test + public void testPactOfNegationRollback() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + // Counter target spell. + // At the beginning of your next upkeep, pay {3}{U}{U}. If you don't, you lose the game. + addCard(Zone.HAND, playerB, "Pact of Negation"); // Instant {0} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Pact of Negation", "Silvercoat Lion", "Silvercoat Lion"); + + setChoice(playerB, "Yes"); + + rollbackTurns(2, PhaseStep.PRECOMBAT_MAIN, playerB, 0); + + setChoice(playerB, "Yes"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerB, "Pact of Negation", 1); + + Assert.assertTrue("Player A is still in game", playerA.isInGame()); + Assert.assertTrue("Player B is still in game", playerB.isInGame()); + + assertTappedCount("Island", true, 5); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/rollback/NewCreaturesAreRemovedTest.java b/Mage.Tests/src/test/java/org/mage/test/rollback/NewCreaturesAreRemovedTest.java index 1036836343..f2cd3eecaf 100644 --- a/Mage.Tests/src/test/java/org/mage/test/rollback/NewCreaturesAreRemovedTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/rollback/NewCreaturesAreRemovedTest.java @@ -1,108 +1,117 @@ - -package org.mage.test.rollback; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * @author LevelX2 - */ -public class NewCreaturesAreRemovedTest extends CardTestPlayerBase { - - /** - * I was playing with a Tamiyo's Journal in the battlefield. - *

- * During my turn I rollbacked. The clue generated by Tamiyo's Journal - * stayed on battlefield and when my turn started again, it re-investigated - * for another one. - */ - @Test - public void testTamiyosJournal() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); - // At the beginning of your upkeep, investigate (Create a colorless Clue artifact token onto the battlefield with \"{2}, Sacrifice this artifact: Draw a card.\"). - // {T}, Sacrifice three Clues: Search your library for a card and put that card into your hand. Then shuffle your library. - addCard(Zone.HAND, playerA, "Tamiyo's Journal"); // Artifact {5} - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 3); - addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 3); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tamiyo's Journal"); - - // As Port Town enters the battlefield, you may reveal a Plains or Island card from your hand. If you don't, Port Town enters the battlefield tapped. - // {T}: Add {W} or {U}. - addCard(Zone.HAND, playerA, "Port Town"); // Land - addCard(Zone.HAND, playerA, "Island"); // Land - - attack(2, playerB, "Pillarfield Ox"); - - attack(3, playerA, "Silvercoat Lion"); - - attack(4, playerB, "Pillarfield Ox"); - - attack(5, playerA, "Silvercoat Lion"); - - attack(6, playerB, "Pillarfield Ox"); - - playLand(7, PhaseStep.PRECOMBAT_MAIN, playerA, "Port Town"); - attack(7, playerA, "Silvercoat Lion"); - - setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); - execute(); - - assertLife(playerA, 14); - assertLife(playerB, 14); - assertPermanentCount(playerA, "Port Town", 1); - assertTapped("Port Town", false); - assertPermanentCount(playerA, "Clue", 3); - - } - - @Test - public void testTamiyosJournalAndRollback() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); - // At the beginning of your upkeep, investigate (Create a colorless Clue artifact token onto the battlefield with \"{2}, Sacrifice this artifact: Draw a card.\"). - // {T}, Sacrifice three Clues: Search your library for a card and put that card into your hand. Then shuffle your library. - addCard(Zone.HAND, playerA, "Tamiyo's Journal"); // Artifact {5} - - // As Port Town enters the battlefield, you may reveal a Plains or Island card from your hand. If you don't, Port Town enters the battlefield tapped. - // {T}: Add {W} or {U}. - addCard(Zone.HAND, playerA, "Port Town"); // Land - addCard(Zone.HAND, playerA, "Island"); // Land - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 3); - addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 3); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tamiyo's Journal"); - - attack(2, playerB, "Pillarfield Ox"); - - attack(3, playerA, "Silvercoat Lion"); - rollbackTurns(3, PhaseStep.END_TURN, playerA, 0); - - attack(4, playerB, "Pillarfield Ox"); - - attack(5, playerA, "Silvercoat Lion"); - - rollbackTurns(5, PhaseStep.END_TURN, playerA, 0); - - attack(6, playerB, "Pillarfield Ox"); - - playLand(7, PhaseStep.PRECOMBAT_MAIN, playerA, "Port Town"); - attack(7, playerA, "Silvercoat Lion"); - - rollbackTurns(7, PhaseStep.POSTCOMBAT_MAIN, playerA, 0); - - setStopAt(7, PhaseStep.END_TURN); - execute(); - - assertPermanentCount(playerA, "Port Town", 1); - assertTapped("Port Town", false); - assertPermanentCount(playerA, "Clue", 3); - - assertLife(playerA, 14); - assertLife(playerB, 14); - - } - -} +package org.mage.test.rollback; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author LevelX2 + */ +public class NewCreaturesAreRemovedTest extends CardTestPlayerBase { + + /** + * I was playing with a Tamiyo's Journal in the battlefield. + *

+ * During my turn I rollbacked. The clue generated by Tamiyo's Journal + * stayed on battlefield and when my turn started again, it re-investigated + * for another one. + */ + @Test + public void testTamiyosJournal() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + // At the beginning of your upkeep, investigate (Create a colorless Clue artifact token onto the battlefield with \"{2}, Sacrifice this artifact: Draw a card.\"). + // {T}, Sacrifice three Clues: Search your library for a card and put that card into your hand. Then shuffle your library. + addCard(Zone.HAND, playerA, "Tamiyo's Journal"); // Artifact {5} + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 3); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tamiyo's Journal"); + + // As Port Town enters the battlefield, you may reveal a Plains or Island card from your hand. If you don't, Port Town enters the battlefield tapped. + // {T}: Add {W} or {U}. + addCard(Zone.HAND, playerA, "Port Town"); // Land + addCard(Zone.HAND, playerA, "Island"); // Land + + attack(2, playerB, "Pillarfield Ox"); + + attack(3, playerA, "Silvercoat Lion"); + + attack(4, playerB, "Pillarfield Ox"); + + attack(5, playerA, "Silvercoat Lion"); + + attack(6, playerB, "Pillarfield Ox"); + + playLand(7, PhaseStep.PRECOMBAT_MAIN, playerA, "Port Town"); + attack(7, playerA, "Silvercoat Lion"); + + setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 14); + assertLife(playerB, 14); + assertPermanentCount(playerA, "Port Town", 1); + assertTapped("Port Town", false); + assertPermanentCount(playerA, "Clue", 3); + + } + + @Test + public void testTamiyosJournalAndRollback() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + // At the beginning of your upkeep, investigate (Create a colorless Clue artifact token onto the battlefield with \"{2}, Sacrifice this artifact: Draw a card.\"). + // {T}, Sacrifice three Clues: Search your library for a card and put that card into your hand. Then shuffle your library. + addCard(Zone.HAND, playerA, "Tamiyo's Journal"); // Artifact {5} + + // As Port Town enters the battlefield, you may reveal a Plains or Island card from your hand. If you don't, Port Town enters the battlefield tapped. + // {T}: Add {W} or {U}. + addCard(Zone.HAND, playerA, "Port Town"); // Land + addCard(Zone.HAND, playerA, "Island"); // Land + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); // TODO: Check why the test fails (related to rollback?) if the number is set to 3 + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tamiyo's Journal"); + + attack(2, playerB, "Pillarfield Ox"); // A = 18 + + attack(3, playerA, "Silvercoat Lion"); // B = 18 + + rollbackTurns(3, PhaseStep.END_TURN, playerA, 0); + rollbackAfterActionsStart(); + attack(3, playerA, "Silvercoat Lion"); // B = 18 + rollbackAfterActionsEnd(); + + attack(4, playerB, "Pillarfield Ox"); // A =16 + + attack(5, playerA, "Silvercoat Lion"); // B = 16 + rollbackTurns(5, PhaseStep.END_TURN, playerA, 0); + rollbackAfterActionsStart(); + attack(5, playerA, "Silvercoat Lion"); // B = 16 + rollbackAfterActionsEnd(); + + attack(6, playerB, "Pillarfield Ox"); // A = 14 + + playLand(7, PhaseStep.PRECOMBAT_MAIN, playerA, "Port Town"); + attack(7, playerA, "Silvercoat Lion"); // B = 14 + + rollbackTurns(7, PhaseStep.POSTCOMBAT_MAIN, playerA, 0); + rollbackAfterActionsStart(); + playLand(7, PhaseStep.PRECOMBAT_MAIN, playerA, "Port Town"); + attack(7, playerA, "Silvercoat Lion"); // B = 14 + rollbackAfterActionsEnd(); + + setStopAt(7, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Port Town", 1); + assertTapped("Port Town", false); + assertPermanentCount(playerA, "Clue", 3); + + assertLife(playerB, 14); + assertLife(playerA, 14); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/rollback/StateValuesTest.java b/Mage.Tests/src/test/java/org/mage/test/rollback/StateValuesTest.java index 0ec79277b6..c8c309da1b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/rollback/StateValuesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/rollback/StateValuesTest.java @@ -1,6 +1,7 @@ - package org.mage.test.rollback; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlyingAbility; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; @@ -29,20 +30,29 @@ public class StateValuesTest extends CardTestPlayerBase { attack(3, playerA, "Dragon Whelp"); rollbackTurns(3, PhaseStep.BEGIN_COMBAT, playerA, 0); + rollbackAfterActionsStart(); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + + attack(3, playerA, "Dragon Whelp"); + rollbackAfterActionsEnd(); setStopAt(4, PhaseStep.UPKEEP); execute(); - assertLife(playerA, 20); - assertLife(playerB, 12); - assertPermanentCount(playerA, "Dragon Whelp", 1); assertGraveyardCount(playerA, "Dragon Whelp", 0); + assertLife(playerA, 20); + assertLife(playerB, 12); + } @Test public void testBriarbridgePatrol() { + setStrictChooseMode(true); + // Whenever Briarbridge Patrol deals damage to one or more creatures, investigate (Create a colorless Clue artifact token onto the battlefield with "{2}, Sacrifice this artifact: Draw a card."). // At the beginning of each end step, if you sacrificed three or more Clues this turn, you may put a creature card from your hand onto the battlefield. addCard(Zone.BATTLEFIELD, playerA, "Briarbridge Patrol", 1); // 3/3 @@ -54,15 +64,100 @@ public class StateValuesTest extends CardTestPlayerBase { attack(3, playerA, "Briarbridge Patrol"); block(3, playerB, "Pillarfield Ox", "Briarbridge Patrol"); + rollbackTurns(3, PhaseStep.POSTCOMBAT_MAIN, playerA, 0); + rollbackAfterActionsStart(); + attack(3, playerA, "Briarbridge Patrol"); + block(3, playerB, "Pillarfield Ox", "Briarbridge Patrol"); + rollbackAfterActionsEnd(); + setStopAt(3, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); + assertLife(playerA, 20); assertLife(playerB, 20); assertPermanentCount(playerA, "Clue", 2); } + + @Test + public void rollbackTokenCreationTest() { + // Create two 1/1 white Bird creature tokens with flying. + // Flashback—Tap three untapped white creatures you control. (You may cast this card from your graveyard for its flashback cost. Then exile it.) + addCard(Zone.LIBRARY, playerA, "Battle Screech", 1); // Sorcery {2}{W}{W} + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Battle Screech"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback"); + setChoice(playerA, "Bird"); + setChoice(playerA, "Bird"); + setChoice(playerA, "Silvercoat Lion"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + + execute(); + assertAllCommandsUsed(); + + // Before rollback + assertTappedCount("Plains", true, 4); + assertTappedCount("Bird", true, 2); + assertTappedCount("Silvercoat Lion", true, 1); + assertPermanentCount(playerA, "Bird", 4); + assertHandCount(playerA, 0); + assertExileCount(playerA, "Battle Screech", 1); + + currentGame.rollbackTurns(2); + + // After rollback to turn 1 + assertTappedCount("Plains", true, 0); + assertTappedCount("Silvercoat Lion", true, 0); + assertPermanentCount(playerA, "Bird", 0); + assertLibraryCount(playerA, "Battle Screech", 1); + assertHandCount(playerA, 0); + } + + @Test + public void rollbackBuffUntilEndOfTurnTest() { + // Target creature gets +1/+1 and gains flying and first strike until end of turn. + addCard(Zone.LIBRARY, playerA, "Aerial Maneuver", 1); // Instant {1}{W} + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Aerial Maneuver", "Silvercoat Lion"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.BEGIN_COMBAT); + + execute(); + assertAllCommandsUsed(); + + // Before rollback + assertTappedCount("Plains", true, 2); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); + assertAbility(playerA, "Silvercoat Lion", FlyingAbility.getInstance(), true); + assertAbility(playerA, "Silvercoat Lion", FirstStrikeAbility.getInstance(), true); + assertHandCount(playerA, 0); + assertGraveyardCount(playerA, "Aerial Maneuver", 1); + + currentGame.rollbackTurns(0); + + // After rollback to begin turn 3 + assertTappedCount("Plains", true, 0); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + assertAbility(playerA, "Silvercoat Lion", FlyingAbility.getInstance(), false); + assertAbility(playerA, "Silvercoat Lion", FirstStrikeAbility.getInstance(), false); + assertLibraryCount(playerA, "Aerial Maneuver", 1); + assertHandCount(playerA, 0); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/rollback/TransformTest.java b/Mage.Tests/src/test/java/org/mage/test/rollback/TransformTest.java index bc92484c3f..878db38a62 100644 --- a/Mage.Tests/src/test/java/org/mage/test/rollback/TransformTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/rollback/TransformTest.java @@ -1,73 +1,78 @@ - -package org.mage.test.rollback; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class TransformTest extends CardTestPlayerBase { - - /** - * - */ - @Test - public void testTransform() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); - // First strike, lifelink - // At the beginning of the end step, if you gained 3 or more life this turn, transform Lone Rider. - // BACK: It That Rides as One - // Creature 4/4 First strike, lifelink - addCard(Zone.HAND, playerA, "Lone Rider"); // Creature {1}{W} 1/1 - // When Venerable Monk enters the battlefield, you gain 2 life. - addCard(Zone.HAND, playerA, "Venerable Monk"); // Creature {2}{W} 2/2 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lone Rider"); - castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Venerable Monk"); - - attack(3, playerA, "Lone Rider"); - - setStopAt(4, PhaseStep.PRECOMBAT_MAIN); - execute(); - - assertLife(playerA, 23); - assertLife(playerB, 19); - - assertPermanentCount(playerA, "Venerable Monk", 1); - assertPermanentCount(playerA, "It That Rides as One", 1); - - } - - @Test - public void testRollbackWithTransform() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); - // First strike, lifelink - // At the beginning of the end step, if you gained 3 or more life this turn, transform Lone Rider. - // BACK: It That Rides as One - // Creature 4/4 First strike, lifelink - addCard(Zone.HAND, playerA, "Lone Rider"); // Creature {1}{W} 1/1 - // When Venerable Monk enters the battlefield, you gain 2 life. - addCard(Zone.HAND, playerA, "Venerable Monk"); // Creature {2}{W} 2/2 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lone Rider"); - castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Venerable Monk"); - - attack(3, playerA, "Lone Rider"); - - rollbackTurns(3, PhaseStep.END_TURN, playerA, 0); - setStopAt(4, PhaseStep.PRECOMBAT_MAIN); - execute(); - - assertLife(playerA, 23); - assertLife(playerB, 19); - - assertPermanentCount(playerA, "Venerable Monk", 1); - assertPermanentCount(playerA, "It That Rides as One", 1); - - } - -} +package org.mage.test.rollback; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class TransformTest extends CardTestPlayerBase { + + /** + * + */ + @Test + public void testTransform() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + // First strike, lifelink + // At the beginning of the end step, if you gained 3 or more life this turn, transform Lone Rider. + // BACK: It That Rides as One + // Creature 4/4 First strike, lifelink + addCard(Zone.HAND, playerA, "Lone Rider"); // Creature {1}{W} 1/1 + // When Venerable Monk enters the battlefield, you gain 2 life. + addCard(Zone.HAND, playerA, "Venerable Monk"); // Creature {2}{W} 2/2 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lone Rider"); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Venerable Monk"); + + attack(3, playerA, "Lone Rider"); + + setStopAt(4, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertLife(playerA, 23); + assertLife(playerB, 19); + + assertPermanentCount(playerA, "Venerable Monk", 1); + assertPermanentCount(playerA, "It That Rides as One", 1); + + } + + @Test + public void testRollbackWithTransform() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + // First strike, lifelink + // At the beginning of the end step, if you gained 3 or more life this turn, transform Lone Rider. + // BACK: It That Rides as One + // Creature 4/4 First strike, lifelink + addCard(Zone.HAND, playerA, "Lone Rider"); // Creature {1}{W} 1/1 + + // When Venerable Monk enters the battlefield, you gain 2 life. + addCard(Zone.HAND, playerA, "Venerable Monk"); // Creature {2}{W} 2/2 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lone Rider"); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Venerable Monk"); + + attack(3, playerA, "Lone Rider"); + + rollbackTurns(3, PhaseStep.END_TURN, playerA, 0); + + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Venerable Monk"); + + attack(3, playerA, "Lone Rider"); + + setStopAt(4, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertLife(playerA, 23); + assertLife(playerB, 19); + + assertPermanentCount(playerA, "Venerable Monk", 1); + assertPermanentCount(playerA, "It That Rides as One", 1); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/AbilityPickerTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/AbilityPickerTest.java new file mode 100644 index 0000000000..a064f280e4 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/AbilityPickerTest.java @@ -0,0 +1,87 @@ +package org.mage.test.serverside; + +import mage.abilities.Abilities; +import mage.abilities.Ability; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.game.permanent.PermanentCard; +import mage.game.permanent.PermanentImpl; +import mage.view.AbilityPickerView; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class AbilityPickerTest extends CardTestPlayerBase { + + @Test + public void test_PickerChoices_FusedSpells() { + // must be 3 spells for choices + Abilities abilities = getAbilitiesFromCard("Armed // Dangerous"); + Assert.assertEquals(3, abilities.size()); + + AbilityPickerView view = new AbilityPickerView("test name", abilities, "test message"); + Assert.assertEquals(3, view.getChoices().size()); + view.getChoices().values().forEach(c -> { + Assert.assertTrue("Must start with Cast text, but found: " + c, c.contains("Cast ")); + }); + } + + @Test + public void test_PickerChoices_AdventureSpells() { + // must be 2 spells for choices and 1 static ability + Abilities abilities = getAbilitiesFromCard("Foulmire Knight"); + Assert.assertEquals(3, abilities.size()); + + AbilityPickerView view = new AbilityPickerView("test name", abilities, "test message"); + Assert.assertEquals(3, view.getChoices().size()); + view.getChoices().values().forEach(c -> { + if (c.contains("Deathtouch")) { + return; + } + Assert.assertTrue("Must start with Cast text, but found: " + c, c.contains("Cast ")); + }); + } + + @Test + public void test_PickerChoices_ActivatedAbilities() { + // must be 1 cast + 3 abilities + Abilities abilities = getAbilitiesFromCard("Dimir Cluestone"); + Assert.assertEquals(4, abilities.size()); + + AbilityPickerView view = new AbilityPickerView("test name", abilities, "test message"); + Assert.assertEquals(4, view.getChoices().size()); + int castCount = 0; + int abilsCount = 0; + for (String c : view.getChoices().values()) { + if (c.contains("Cast ")) { + castCount++; + } else { + abilsCount++; + } + } + Assert.assertEquals(1, castCount); + Assert.assertEquals(3, abilsCount); + } + + @Test + public void test_PickerChoices_AdditionalSpells() { + // must be 2 cast + Abilities abilities = getAbilitiesFromCard("Cling to Dust"); + Assert.assertEquals(2, abilities.size()); + + AbilityPickerView view = new AbilityPickerView("test name", abilities, "test message"); + Assert.assertEquals(2, view.getChoices().size()); + view.getChoices().values().forEach(c -> { + Assert.assertTrue("Must start with Cast text, but found: " + c, c.contains("Cast ")); + }); + } + + private Abilities getAbilitiesFromCard(String cardName) { + CardInfo info = CardRepository.instance.findCard(cardName); + PermanentImpl permanent = new PermanentCard(info.getCard(), playerA.getId(), currentGame); + return permanent.getAbilities(currentGame); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java new file mode 100644 index 0000000000..9875fd05cb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java @@ -0,0 +1,38 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.serverside.base; + +import java.io.FileNotFoundException; +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.Game; +import mage.game.GameException; +import mage.game.OathbreakerFreeForAll; +import mage.game.mulligan.MulliganType; +import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; + +/** + * @author LevelX2 + */ +public abstract class CardTestOathbreaker3PlayersFFA extends CardTestPlayerAPIImpl { + + public CardTestOathbreaker3PlayersFFA() { + super(); + this.deckNameA = "Oathbreaker_UR.dck"; // PW: Saheeli, Sublime Artificer SS: Thoughtcast + this.deckNameB = "Oathbreaker_UR.dck"; + this.deckNameC = "Oathbreaker_UR.dck"; + } + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new OathbreakerFreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 20); + playerA = createPlayer(game, playerA, "PlayerA", deckNameA); + playerB = createPlayer(game, playerB, "PlayerB", deckNameB); + playerC = createPlayer(game, playerC, "PlayerC", deckNameC); + return game; + } + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseWithMonteCarloAIHelps.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseWithMonteCarloAIHelps.java new file mode 100644 index 0000000000..30a4f16f87 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseWithMonteCarloAIHelps.java @@ -0,0 +1,22 @@ +package org.mage.test.serverside.base; + +import mage.constants.RangeOfInfluence; +import org.mage.test.player.TestComputerPlayerMonteCarlo; +import org.mage.test.player.TestPlayer; + +/** + * Base class but with Monte Carlo computer player to test single AI commands (it's different from full AI simulation from CardTestPlayerBaseAI): + * 1. AI don't play normal priorities (you must use ai*** commands to play it); + * 2. AI will choose in non strict mode (it's simulated ComputerPlayerMCTS, not simple ComputerPlayer from basic tests) + * + * @author JayDi85 + */ +public abstract class CardTestPlayerBaseWithMonteCarloAIHelps extends CardTestPlayerBase { + + @Override + protected TestPlayer createPlayer(String name, RangeOfInfluence rangeOfInfluence) { + TestPlayer testPlayer = new TestPlayer(new TestComputerPlayerMonteCarlo(name, RangeOfInfluence.ONE, 6)); + testPlayer.setAIPlayer(false); // AI can't play it by itself, use AI commands + return testPlayer; + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java index 5b5b758af4..4efe7837f2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -351,6 +351,13 @@ public abstract class MageTestPlayerBase { return new TestPlayer(new TestComputerPlayer(name, rangeOfInfluence)); } + /** + * Raise error on any miss choices/targets setup in tests (if AI try to make decision itself instead of user defined actions) + * If you want to disable mana auto-payment (e.g. to simulate user clicks on mana pool or special mana) then call + * disableManaAutoPayment() + * + * @param enable + */ protected void setStrictChooseMode(boolean enable) { if (playerA != null) playerA.setChooseStrictMode(enable); if (playerB != null) playerB.setChooseStrictMode(enable); @@ -403,8 +410,8 @@ public abstract class MageTestPlayerBase { // custom card with global abilities list to init (can contains abilities per card name) class CustomTestCard extends CardImpl { - static private Map> abilitiesList = new HashMap<>(); // card name -> abilities - static private Map spellAbilitiesList = new HashMap<>(); // card name -> spell ability + static private final Map> abilitiesList = new HashMap<>(); // card name -> abilities + static private final Map spellAbilitiesList = new HashMap<>(); // card name -> spell ability static void addCustomAbility(String cardName, SpellAbility spellAbility, Ability ability) { if (!abilitiesList.containsKey(cardName)) { diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index ef4695b34c..506afe2b16 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -24,6 +24,7 @@ import mage.game.command.CommandObject; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.player.ai.ComputerPlayer7; +import mage.player.ai.ComputerPlayerMCTS; import mage.players.ManaPool; import mage.players.Player; import mage.util.CardUtil; @@ -35,10 +36,7 @@ import org.mage.test.serverside.base.CardTestAPI; import org.mage.test.serverside.base.MageTestPlayerBase; import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -50,7 +48,8 @@ import java.util.stream.Collectors; */ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implements CardTestAPI { - private static final boolean FAST_SCAN_WITHOUT_DATABASE_CREATE = false; // DEBUG only, enable it to fast startup tests without database create + // DEBUG only, enable it to fast startup tests without database create (delete \db\ folder to force db recreate) + private static final boolean FAST_SCAN_WITHOUT_DATABASE_CREATE = false; public static final String ALIAS_PREFIX = "@"; // don't change -- it uses in user's tests public static final String CHECK_PARAM_DELIMETER = "#"; @@ -90,8 +89,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public static final String CHECK_COMMAND_ABILITY = "ABILITY"; public static final String CHECK_COMMAND_PLAYABLE_ABILITY = "PLAYABLE_ABILITY"; public static final String CHECK_COMMAND_PERMANENT_COUNT = "PERMANENT_COUNT"; + public static final String CHECK_COMMAND_PERMANENT_TAPPED = "PERMANENT_TAPPED"; public static final String CHECK_COMMAND_PERMANENT_COUNTERS = "PERMANENT_COUNTERS"; public static final String CHECK_COMMAND_EXILE_COUNT = "EXILE_COUNT"; + public static final String CHECK_COMMAND_GRAVEYARD_COUNT = "GRAVEYARD_COUNT"; public static final String CHECK_COMMAND_HAND_COUNT = "HAND_COUNT"; public static final String CHECK_COMMAND_HAND_CARD_COUNT = "HAND_CARD_COUNT"; public static final String CHECK_COMMAND_COMMAND_CARD_COUNT = "COMMAND_CARD_COUNT"; @@ -101,6 +102,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public static final String CHECK_COMMAND_MANA_POOL = "MANA_POOL"; public static final String CHECK_COMMAND_ALIAS_ZONE = "ALIAS_ZONE"; public static final String CHECK_COMMAND_PLAYER_IN_GAME = "PLAYER_IN_GAME"; + public static final String CHECK_COMMAND_STACK_SIZE = "STACK_SIZE"; + public static final String CHECK_COMMAND_STACK_OBJECT = "STACK_OBJECT"; // TODO: add target player param to commands public static final String SHOW_COMMAND_LIBRARY = "LIBRARY"; @@ -112,6 +115,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public static final String SHOW_COMMAND_AVAILABLE_ABILITIES = "AVAILABLE_ABILITIES"; public static final String SHOW_COMMAND_AVAILABLE_MANA = "AVAILABLE_MANA"; public static final String SHOW_COMMAND_ALIASES = "ALIASES"; + public static final String SHOW_COMMAND_STACK = "STACK"; // TODO: add target player param to commands public static final String ALIAS_COMMAND_ADD = "ADD"; @@ -123,6 +127,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement protected String deckNameC; protected String deckNameD; + private int rollbackBlock = 0; // used to handle actions that have to be added aufter a rollback + private boolean rollbackBlockActive = false; + private TestPlayer rollbackPlayer = null; + protected enum ExpectedType { TURN_NUMBER, RESULT, @@ -135,7 +143,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public CardTestPlayerAPIImpl() { // load all cards to db from class list ArrayList errorsList = new ArrayList<>(); - if (FAST_SCAN_WITHOUT_DATABASE_CREATE) { + if (FAST_SCAN_WITHOUT_DATABASE_CREATE && CardRepository.instance.findCard("Mountain") != null) { CardScanner.scanned = true; } CardScanner.scan(errorsList); @@ -190,6 +198,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement addCard(Zone.LIBRARY, playerB, "Plains", 10); } + /** + * @throws GameException + * @throws FileNotFoundException + */ @Before public void reset() throws GameException, FileNotFoundException { if (currentGame != null) { @@ -216,6 +228,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement gameOptions = new GameOptions(); + rollbackBlock = 0; + rollbackBlockActive = false; + } abstract protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException; @@ -275,12 +290,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")", (maxTurn > this.stopOnTurn) || (maxTurn == this.stopOnTurn && maxPhase > this.stopAtStep.getIndex())); - - for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer) player; - currentGame.cheat(player.getId(), getCommands(testPlayer)); - currentGame.cheat(player.getId(), activePlayer.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer), - getBattlefieldCards(testPlayer), getGraveCards(testPlayer), getCommandCards(testPlayer)); + if (!currentGame.isPaused()) { + for (Player player : currentGame.getPlayers().values()) { + TestPlayer testPlayer = (TestPlayer) player; + currentGame.cheat(player.getId(), getCommands(testPlayer)); + currentGame.cheat(player.getId(), activePlayer.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer), + getBattlefieldCards(testPlayer), getGraveCards(testPlayer), getCommandCards(testPlayer)); + } } long t1 = System.nanoTime(); @@ -289,7 +305,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement gameOptions.stopOnTurn = stopOnTurn; gameOptions.stopAtStep = stopAtStep; currentGame.setGameOptions(gameOptions); + if (currentGame.isPaused()) { + currentGame.resume();// needed if execute() is performed multiple times + } currentGame.start(activePlayer.getId()); + currentGame.setGameStopped(true); // used for rollback handling long t2 = System.nanoTime(); logger.debug("Winner: " + currentGame.getWinner()); logger.info(Thread.currentThread().getStackTrace()[2].getMethodName() + " has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms"); @@ -323,14 +343,34 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement return player; } - // check commands + private void addPlayerAction(TestPlayer player, int turnNum, PhaseStep step, String action) { + PlayerAction playerAction = new PlayerAction("", turnNum, step, action); + addPlayerAction(player, playerAction); + } + private void addPlayerAction(TestPlayer player, String actionName, int turnNum, PhaseStep step, String action) { + PlayerAction playerAction = new PlayerAction(actionName, turnNum, step, action); + addPlayerAction(player, playerAction); + } + + private void addPlayerAction(TestPlayer player, PlayerAction playerAction) { + if (rollbackBlockActive) { + rollbackPlayer.getRollbackActions() + .computeIfAbsent(rollbackBlock, block -> new HashMap<>()) + .computeIfAbsent(player.getId(), playerId -> new ArrayList<>()) + .add(playerAction); + } else { + player.addAction(playerAction); + } + } + + // check commands private void check(String checkName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) { String res = CHECK_PREFIX + command; for (String param : params) { res += CHECK_PARAM_DELIMETER + param; } - player.addAction(checkName, turnNum, step, res); + addPlayerAction(player, checkName, turnNum, step, res); } public void checkPT(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, Integer power, Integer toughness) { @@ -370,6 +410,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement check(checkName, turnNum, step, player, CHECK_COMMAND_PERMANENT_COUNT, targetPlayer.getId().toString(), permanentName, count.toString()); } + public void checkPermanentTapped(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, Boolean tapped, Integer count) { + checkPermanentTapped(checkName, turnNum, step, player, player, permanentName, tapped, count); + } + + public void checkPermanentTapped(String checkName, int turnNum, PhaseStep step, TestPlayer player, TestPlayer targetPlayer, String permanentName, Boolean tapped, Integer count) { + check(checkName, turnNum, step, player, CHECK_COMMAND_PERMANENT_TAPPED, targetPlayer.getId().toString(), permanentName, tapped.toString(), count.toString()); + } + public void checkPermanentCounters(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, CounterType counterType, Integer count) { check(checkName, turnNum, step, player, CHECK_COMMAND_PERMANENT_COUNTERS, permanentName, counterType.toString(), count.toString()); } @@ -379,6 +427,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement check(checkName, turnNum, step, player, CHECK_COMMAND_EXILE_COUNT, permanentName, count.toString()); } + public void checkGraveyardCount(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, Integer count) { + //Assert.assertNotEquals("", permanentName); + check(checkName, turnNum, step, player, CHECK_COMMAND_GRAVEYARD_COUNT, permanentName, count.toString()); + } + public void checkHandCount(String checkName, int turnNum, PhaseStep step, TestPlayer player, Integer count) { check(checkName, turnNum, step, player, CHECK_COMMAND_HAND_COUNT, count.toString()); } @@ -420,14 +473,21 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement check(checkName, turnNum, step, player, CHECK_COMMAND_ALIAS_ZONE, alias, zone.toString(), mustHave.toString()); } - // show commands + public void checkStackSize(String checkName, int turnNum, PhaseStep step, TestPlayer player, Integer needStackSize) { + check(checkName, turnNum, step, player, CHECK_COMMAND_STACK_SIZE, needStackSize.toString()); + } + public void checkStackObject(String checkName, int turnNum, PhaseStep step, TestPlayer player, String spellAbilityOnStack, Integer needAmount) { + check(checkName, turnNum, step, player, CHECK_COMMAND_STACK_OBJECT, spellAbilityOnStack, needAmount.toString()); + } + + // show commands private void show(String showName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) { String res = "show:" + command; for (String param : params) { res += CHECK_PARAM_DELIMETER + param; } - player.addAction(showName, turnNum, step, res); + addPlayerAction(player, showName, turnNum, step, res); } public void showLibrary(String showName, int turnNum, PhaseStep step, TestPlayer player) { @@ -454,11 +514,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement show(showName, turnNum, step, player, SHOW_COMMAND_EXILE); } - public void showAvaileableAbilities(String showName, int turnNum, PhaseStep step, TestPlayer player) { + public void showAvailableAbilities(String showName, int turnNum, PhaseStep step, TestPlayer player) { show(showName, turnNum, step, player, SHOW_COMMAND_AVAILABLE_ABILITIES); } - public void showAvaileableMana(String showName, int turnNum, PhaseStep step, TestPlayer player) { + public void showAvailableMana(String showName, int turnNum, PhaseStep step, TestPlayer player) { show(showName, turnNum, step, player, SHOW_COMMAND_AVAILABLE_MANA); } @@ -466,10 +526,17 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement show(showName, turnNum, step, player, SHOW_COMMAND_ALIASES); } + public void showStack(String showName, int turnNum, PhaseStep step, TestPlayer player) { + show(showName, turnNum, step, player, SHOW_COMMAND_STACK); + } + /** * Removes all cards from player's library from the game. Usually this * should be used once before initialization to form the library in certain * order. + *

+ * Warning, if you doesn't add cards to hand then player will lose the game + * on draw and test return unused actions (game ended too early) * * @param player {@link Player} to remove all library cards from. */ @@ -489,8 +556,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } /** - * Disable auto-payment from mana pool, you must manually fill pool by activateManaAbility and unlock color by setChoice - * Use it for pay color order testing (e.g. simulate user clicks on mana pool to pay) + * Disable auto-payment from mana pool, you must manually fill pool by + * activateManaAbility and unlock color by setChoice Use it for pay color + * order testing (e.g. simulate user clicks on mana pool to pay) * * @param player */ @@ -534,8 +602,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param cardName Card name in string format. * @param count Amount of cards to be added. * @param tapped In case gameZone is Battlefield, determines whether - * permanent should be tapped. In case gameZone is other than Battlefield, - * {@link IllegalArgumentException} is thrown + * permanent should be tapped. In case gameZone is other + * than Battlefield, {@link IllegalArgumentException} is + * thrown */ @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) { @@ -714,10 +783,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param cardName Card name to compare with. * @param power Expected power to compare with. * @param toughness Expected toughness to compare with. - * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you - * want "at least one creature with given name should have specified p\t" - * Use ALL, if you want "all creature with gived name should have specified - * p\t" + * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if + * you want "at least one creature with given name should + * have specified p\t" Use ALL, if you want "all creature + * with gived name should have specified p\t" */ @Override public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) @@ -806,8 +875,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param player * @param cardName * @param ability - * @param mustHave true if creature should contain ability, false if it should - * NOT contain it instead + * @param mustHave true if creature should contain ability, false if it + * should NOT contain it instead * @param count number of permanents with that ability * @throws AssertionError */ @@ -837,6 +906,21 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } } + public void assertAbilityCount(Player player, String cardName, Class searchedAbility, int amount) { + Permanent found = null; + for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) { + if (isObjectHaveTargetNameOrAlias(player, permanent, cardName)) { + found = permanent; + break; + } + } + Assert.assertNotNull("There is no such permanent under player's control, player=" + player.getName() + + ", cardName=" + cardName, found); + + Assert.assertEquals(amount, found.getAbilities(currentGame).stream() + .filter(a -> searchedAbility.isAssignableFrom(a.getClass())).collect(Collectors.toList()).size()); + } + /** * Assert permanent count under player's control. * @@ -984,7 +1068,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement Assert.assertNotNull("There is no such permanent on the battlefield, cardName=" + cardName, found); - Assert.assertTrue("(Battlefield) card type not found (" + cardName + ':' + type + ')', (found.getCardType().contains(type) == mustHave)); + Assert.assertTrue("(Battlefield) card type " + (mustHave ? "not " : "") + + "found (" + cardName + ':' + type + ')', (found.getCardType().contains(type) == mustHave)); } @@ -1175,7 +1260,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement Assert.assertEquals("(Hand) Card counts for card " + cardName + " for " + player.getName() + " are not equal ", count, actual); } - public void assertManaPool(Player player, ManaType color, int amount) { ManaPool manaPool = currentGame.getPlayer(player.getId()).getManaPool(); switch (color) { @@ -1346,6 +1430,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement Assert.assertEquals("(Targets of " + player.getName() + ") Count are not equal (found " + player.getTargets() + ")", count, player.getTargets().size()); } + /** + * Raise error on any unused commands, choices or targets If you want to + * test that ability can't be activated then use call checkPlayableAbility() + * + * @throws AssertionError + */ public void assertAllCommandsUsed() throws AssertionError { for (Player player : currentGame.getPlayers().values()) { TestPlayer testPlayer = (TestPlayer) player; @@ -1359,6 +1449,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement Assert.assertEquals("message", currentGame.getState().getActivePlayerId(), player.getId()); } + public void assertTopCardRevealed(TestPlayer player, boolean isRevealed) { + Assert.assertEquals(isRevealed, player.isTopCardRevealed()); + } + public Permanent getPermanent(String cardName, UUID controller) { assertAliaseSupportInActivateCommand(cardName, false); Permanent found = null; @@ -1396,44 +1490,50 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public void playLand(int turnNum, PhaseStep step, TestPlayer player, String cardName) { //Assert.assertNotEquals("", cardName); assertAliaseSupportInActivateCommand(cardName, false); - player.addAction(turnNum, step, ACTIVATE_PLAY + cardName); + addPlayerAction(player, turnNum, step, ACTIVATE_PLAY + cardName); } public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName) { //Assert.assertNotEquals("", cardName); assertAliaseSupportInActivateCommand(cardName, false); - player.addAction(turnNum, step, ACTIVATE_CAST + cardName); + addPlayerAction(player, turnNum, step, ACTIVATE_CAST + cardName); } public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, Player target) { //Assert.assertNotEquals("", cardName); // warning, target in spell cast command setups without choose target call assertAliaseSupportInActivateCommand(cardName, false); - player.addAction(turnNum, step, ACTIVATE_CAST + cardName + "$targetPlayer=" + target.getName()); + addPlayerAction(player, turnNum, step, ACTIVATE_CAST + cardName + "$targetPlayer=" + target.getName()); } public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, Player target, int manaInPool) { //Assert.assertNotEquals("", cardName); assertAliaseSupportInActivateCommand(cardName, false); - player.addAction(turnNum, step, ACTIVATE_CAST + cardName + "$targetPlayer=" + target.getName() + "$manaInPool=" + manaInPool); + addPlayerAction(player, turnNum, step, ACTIVATE_CAST + cardName + "$targetPlayer=" + target.getName() + "$manaInPool=" + manaInPool); } /** - * AI play one PRIORITY with multi game simulations (calcs and play ONE best action, can be called with stack) - * All choices must be made by AI (e.g. strict mode possible) + * AI play one PRIORITY with multi game simulations (calcs and play ONE best + * action, can be called with stack) All choices must be made by AI + * (e.g.strict mode possible) + * + * @param turnNum + * @param step + * @param player */ public void aiPlayPriority(int turnNum, PhaseStep step, TestPlayer player) { assertAiPlayAndGameCompatible(player); - player.addAction(createAIPlayerAction(turnNum, step, AI_COMMAND_PLAY_PRIORITY)); + addPlayerAction(player, createAIPlayerAction(turnNum, step, AI_COMMAND_PLAY_PRIORITY)); } /** - * AI play STEP to the end with multi game simulations (calcs and play best actions until step ends, can be called in the middle of the step) - * All choices must be made by AI (e.g. strict mode possible) + * AI play STEP to the end with multi game simulations (calcs and play best + * actions until step ends, can be called in the middle of the step) All + * choices must be made by AI (e.g. strict mode possible) */ public void aiPlayStep(int turnNum, PhaseStep step, TestPlayer player) { assertAiPlayAndGameCompatible(player); - player.addAction(createAIPlayerAction(turnNum, step, AI_COMMAND_PLAY_STEP)); + addPlayerAction(player, createAIPlayerAction(turnNum, step, AI_COMMAND_PLAY_STEP)); } public PlayerAction createAIPlayerAction(int turnNum, PhaseStep step, String aiCommand) { @@ -1447,18 +1547,26 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } private void assertAiPlayAndGameCompatible(TestPlayer player) { - if (player.isAIPlayer() || !(player.getComputerPlayer() instanceof ComputerPlayer7)) { - Assert.fail("AI commands supported by CardTestPlayerBaseWithAIHelps only"); + boolean aiCompatible = (player.getComputerPlayer() instanceof ComputerPlayer7 || player.getComputerPlayer() instanceof ComputerPlayerMCTS); + if (player.isAIPlayer() || !aiCompatible) { + Assert.fail("AI commands supported by CardTestPlayerBaseWith***AIHelps only"); } } public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player) { - player.addAction(turnNum, step, "waitStackResolved"); + waitStackResolved(1, step, player, false); + } + + public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player, boolean skipOneStackObjectOnly) { + String command = "waitStackResolved" + (skipOneStackObjectOnly ? ":1" : ""); + addPlayerAction(player, turnNum, step, command); } /** * Rollback the number of given turns: 0 = rollback to the start of the - * current turn + * current turn. Use the commands rollbackAfterActionsStart() and + * rollbackAfterActionsEnd() to define a block of actions, that will be + * added and executed after the rollback. * * @param turnNum * @param step @@ -1466,7 +1574,33 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param turns */ public void rollbackTurns(int turnNum, PhaseStep step, TestPlayer player, int turns) { - player.addAction(turnNum, step, "playerAction:Rollback" + "$turns=" + turns); + rollbackBlock++; + addPlayerAction(player, turnNum, step, "playerAction:Rollback" + "$turns=" + turns + "$rollbackBlock=" + rollbackBlock); + rollbackPlayer = player; + } + + /** + * Adds a number of actions that will be added to the to the start of the + * list of actions of the players but only after the rollback is executed + * because otherwis the actions are executed to early and would lead to + * invalid actions (e.g. casting the same spell twice). + */ + public void rollbackAfterActionsStart() throws IllegalStateException { + if (rollbackPlayer == null || rollbackBlock < 1) { + throw new IllegalStateException("There was no rollback action defined before. You can use this command only after a rollback action."); + } + rollbackBlockActive = true; + } + + /** + * Ends a block of actions to be added after an rollback action + */ + public void rollbackAfterActionsEnd() throws IllegalStateException { + if (rollbackBlockActive = false || rollbackPlayer == null) { + throw new IllegalStateException("There was no rollback action defined before or no rollback block started. You can use this command only after a rollback action."); + } + rollbackBlockActive = false; + rollbackPlayer = null; } /** @@ -1477,7 +1611,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param player */ public void concede(int turnNum, PhaseStep step, TestPlayer player) { - player.addAction(turnNum, step, "playerAction:Concede"); + addPlayerAction(player, turnNum, step, "playerAction:Concede"); } /** @@ -1485,14 +1619,16 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param step * @param player * @param cardName - * @param targetName for modes you can add "mode=3" before target name, - * multiple targets can be seperated by ^, not target marks as TestPlayer.NO_TARGET + * @param targetName for modes you can add "mode=3" before target name; + * multiple targets can be seperated by ^; + * no target marks as TestPlayer.NO_TARGET; + * warning, do not support cards with target adjusters - use addTarget instead */ public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) { //Assert.assertNotEquals("", cardName); assertAliaseSupportInActivateCommand(cardName, true); assertAliaseSupportInActivateCommand(targetName, true); - player.addAction(turnNum, step, ACTIVATE_CAST + cardName + "$target=" + targetName); + addPlayerAction(player, turnNum, step, ACTIVATE_CAST + cardName + "$target=" + targetName); } public enum StackClause { @@ -1535,11 +1671,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement assertAliaseSupportInActivateCommand(targetName, true); assertAliaseSupportInActivateCommand(spellOnStack, false); if (StackClause.WHILE_ON_STACK == clause) { - player.addAction(turnNum, step, ACTIVATE_CAST + cardName + addPlayerAction(player, turnNum, step, ACTIVATE_CAST + cardName + '$' + (targetName != null && targetName.startsWith("target") ? targetName : "target=" + targetName) + "$spellOnStack=" + spellOnStack); } else { - player.addAction(turnNum, step, ACTIVATE_CAST + cardName + addPlayerAction(player, turnNum, step, ACTIVATE_CAST + cardName + '$' + (targetName != null && targetName.startsWith("target") ? targetName : "target=" + targetName) + "$!spellOnStack=" + spellOnStack); } @@ -1558,7 +1694,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement if (spellOnTopOfStack != null && !spellOnTopOfStack.isEmpty()) { action += "$spellOnTopOfStack=" + spellOnTopOfStack; } - player.addAction(turnNum, step, action); + addPlayerAction(player, turnNum, step, action); } public void activateManaAbility(int turnNum, PhaseStep step, TestPlayer player, String ability) { @@ -1567,20 +1703,20 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public void activateManaAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, int timesToActivate) { for (int i = 0; i < timesToActivate; i++) { - player.addAction(turnNum, step, ACTIVATE_MANA + ability); + addPlayerAction(player, turnNum, step, ACTIVATE_MANA + ability); } } public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability) { // TODO: it's uses computerPlayer to execute, only ability target will work, but choices and targets commands aren't assertAliaseSupportInActivateCommand(ability, false); - player.addAction(turnNum, step, ACTIVATE_ABILITY + ability); + addPlayerAction(player, turnNum, step, ACTIVATE_ABILITY + ability); } public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, Player target) { // TODO: it's uses computerPlayer to execute, only ability target will work, but choices and targets commands aren't assertAliaseSupportInActivateCommand(ability, false); - player.addAction(turnNum, step, ACTIVATE_ABILITY + ability + "$targetPlayer=" + target.getName()); + addPlayerAction(player, turnNum, step, ACTIVATE_ABILITY + ability + "$targetPlayer=" + target.getName()); } public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String... targetNames) { @@ -1589,9 +1725,17 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement Arrays.stream(targetNames).forEach(n -> { assertAliaseSupportInActivateCommand(n, true); }); - player.addAction(turnNum, step, ACTIVATE_ABILITY + ability + "$target=" + String.join("^", targetNames)); + addPlayerAction(player, turnNum, step, ACTIVATE_ABILITY + ability + "$target=" + String.join("^", targetNames)); } + /** + * @param turnNum + * @param step + * @param player + * @param ability + * @param targetName use NO_TARGET if there is no target to set + * @param spellOnStack + */ public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) { // TODO: it's uses computerPlayer to execute, only ability target will work, but choices and targets commands aren't this.activateAbility(turnNum, step, player, ability, targetName, spellOnStack, StackClause.WHILE_ON_STACK); @@ -1629,27 +1773,31 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } sb.append(spellOnStack); } - player.addAction(turnNum, step, sb.toString()); + addPlayerAction(player, turnNum, step, sb.toString()); } public void addCounters(int turnNum, PhaseStep step, TestPlayer player, String cardName, CounterType type, int count) { //Assert.assertNotEquals("", cardName); - player.addAction(turnNum, step, "addCounters:" + cardName + '$' + type.getName() + '$' + count); + addPlayerAction(player, turnNum, step, "addCounters:" + cardName + '$' + type.getName() + '$' + count); } public void attack(int turnNum, TestPlayer player, String attacker) { //Assert.assertNotEquals("", attacker); - player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:" + attacker); + assertAliaseSupportInActivateCommand(attacker, false); // it uses old special notation like card_name:index + addPlayerAction(player, turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:" + attacker); } public void attack(int turnNum, TestPlayer player, String attacker, TestPlayer defendingPlayer) { //Assert.assertNotEquals("", attacker); - player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:" + attacker + "$defendingPlayer=" + defendingPlayer.getName()); + assertAliaseSupportInActivateCommand(attacker, false); // it uses old special notation like card_name:index + addPlayerAction(player, turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:" + attacker + "$defendingPlayer=" + defendingPlayer.getName()); } public void attack(int turnNum, TestPlayer player, String attacker, String planeswalker) { //Assert.assertNotEquals("", attacker); - player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, new StringBuilder("attack:").append(attacker).append("$planeswalker=").append(planeswalker).toString()); + assertAliaseSupportInActivateCommand(attacker, false); // it uses old special notation like card_name:index + assertAliaseSupportInActivateCommand(planeswalker, false); + addPlayerAction(player, turnNum, PhaseStep.DECLARE_ATTACKERS, new StringBuilder("attack:").append(attacker).append("$planeswalker=").append(planeswalker).toString()); } public void attackSkip(int turnNum, TestPlayer player) { @@ -1659,7 +1807,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public void block(int turnNum, TestPlayer player, String blocker, String attacker) { //Assert.assertNotEquals("", blocker); //Assert.assertNotEquals("", attacker); - player.addAction(turnNum, PhaseStep.DECLARE_BLOCKERS, "block:" + blocker + '$' + attacker); + assertAliaseSupportInActivateCommand(blocker, false); // it uses old special notation like card_name:index + assertAliaseSupportInActivateCommand(attacker, false); + addPlayerAction(player, turnNum, PhaseStep.DECLARE_BLOCKERS, "block:" + blocker + '$' + attacker); } public void blockSkip(int turnNum, TestPlayer player) { @@ -1667,9 +1817,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } /** - * For use choices set "Yes" or "No" the the choice string. For X values set - * "X=[xValue]" example: for X=3 set choice string to "X=3". - *
For ColorChoice use "Red", "Green", "Blue", "Black" or "White" + * For use choices set "Yes" or "No" the the choice string.
+ * For X values set "X=[xValue]" example: for X=3 set choice string to + * "X=3".
+ * For ColorChoice use "Red", "Green", "Blue", "Black" or "White"
+ * use command setModeChoice if you have to set a mode from modal + * ability
* * @param player * @param choice @@ -1689,10 +1842,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * * @param player * @param choice starting with "1" for mode 1, "2" for mode 2 and so on (to - * set multiple modes call the command multiple times). If a spell mode can - * be used only once like Demonic Pact, the value has to be set to the - * number of the remaining modes (e.g. if only 2 are left the number need to - * be 1 or 2). + * set multiple modes call the command multiple times). If a + * spell mode can be used only once like Demonic Pact, the + * value has to be set to the number of the remaining modes + * (e.g. if only 2 are left the number need to be 1 or 2). */ public void setModeChoice(TestPlayer player, String choice) { player.addModeChoice(choice); @@ -1703,16 +1856,24 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * * @param player * @param target you can add multiple targets by separating them by the "^" - * character e.g. "creatureName1^creatureName2" you can qualify the target - * additional by setcode e.g. "creatureName-M15" you can add [no copy] to - * the end of the target name to prohibit targets that are copied you can - * add [only copy] to the end of the target name to allow only targets that - * are copies. For modal spells use a prefix with the mode number: - * mode=1Lightning Bolt^mode=2Silvercoat Lion + * character e.g. "creatureName1^creatureName2" you can + * qualify the target additional by setcode e.g. + * "creatureName-M15" you can add [no copy] to the end of the + * target name to prohibit targets that are copied you can add + * [only copy] to the end of the target name to allow only + * targets that are copies. For modal spells use a prefix with + * the mode number: mode=1Lightning Bolt^mode=2Silvercoat Lion */ // TODO: mode options doesn't work here (see BrutalExpulsionTest) public void addTarget(TestPlayer player, String target) { - player.addTarget(target); + addTarget(player, target, 1); + } + + public void addTarget(TestPlayer player, String target, int timesToChoose) { + for (int i = 0; i < timesToChoose; i++) { + assertAliaseSupportInActivateCommand(target, true); + player.addTarget(target); + } } /** @@ -1722,12 +1883,19 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param targetPlayer */ public void addTarget(TestPlayer player, TestPlayer targetPlayer) { - player.addTarget("targetPlayer=" + targetPlayer.getName()); + addTarget(player, targetPlayer, 1); + } + + public void addTarget(TestPlayer player, TestPlayer targetPlayer, int timesToChoose) { + for (int i = 0; i < timesToChoose; i++) { + player.addTarget("targetPlayer=" + targetPlayer.getName()); + } } /** * @param player - * @param target use TestPlayer.TARGET_SKIP to 0 targets selects or to stop "up two xxx" selection + * @param target use TestPlayer.TARGET_SKIP to 0 targets selects or to stop + * "up two xxx" selection * @param amount */ public void addTargetAmount(TestPlayer player, String target, int amount) { @@ -1742,7 +1910,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement addTargetAmount(player, "targetPlayer=" + targetPlayer.getName(), amount); } - public void addTargetAmount(TestPlayer player, String target) { Assert.assertTrue("Only skip command allows here", target.equals(TestPlayer.TARGET_SKIP)); addTargetAmount(player, target, 0); @@ -1777,10 +1944,22 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } public void waitStackResolved(int turnNum, PhaseStep step) { - if (playerA != null) waitStackResolved(turnNum, step, playerA); - if (playerB != null) waitStackResolved(turnNum, step, playerB); - if (playerC != null) waitStackResolved(turnNum, step, playerC); - if (playerD != null) waitStackResolved(turnNum, step, playerD); + waitStackResolved(turnNum, step, false); + } + + public void waitStackResolved(int turnNum, PhaseStep step, boolean skipOneStackObjectOnly) { + if (playerA != null) { + waitStackResolved(turnNum, step, playerA, skipOneStackObjectOnly); + } + if (playerB != null) { + waitStackResolved(turnNum, step, playerB, skipOneStackObjectOnly); + } + if (playerC != null) { + waitStackResolved(turnNum, step, playerC, skipOneStackObjectOnly); + } + if (playerD != null) { + waitStackResolved(turnNum, step, playerD, skipOneStackObjectOnly); + } } private void assertAliaseSupportInActivateCommand(String targetName, boolean methodSupportAliases) { @@ -1795,7 +1974,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement private boolean isObjectHaveTargetNameOrAlias(Player player, MageObject object, String nameOrAlias) { TestPlayer testPlayer = (TestPlayer) player; if (player != null) { // TODO: remove null check and replace all null-player calls in tests by player - return testPlayer.isObjectHaveTargetNameOrAlias(object, nameOrAlias); + return testPlayer.hasObjectTargetNameOrAlias(object, nameOrAlias); } else { return object.getName().equals(nameOrAlias); } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java index 317176a718..06a1251441 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java @@ -1,8 +1,5 @@ - package org.mage.test.serverside.deck; -import java.util.ArrayList; -import java.util.List; import mage.cards.decks.Deck; import mage.cards.decks.DeckValidator; import mage.cards.repository.CardInfo; @@ -14,8 +11,10 @@ import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.MageTestBase; +import java.util.ArrayList; +import java.util.List; + /** - * * @author LevelX2 */ public class DeckValidatorTest extends MageTestBase { @@ -65,7 +64,7 @@ public class DeckValidatorTest extends MageTestBase { DeckValidator validator = new Standard(); boolean validationSuccessful = testDeckValid(validator, deck); - Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful); + Assert.assertTrue(validator.getErrorsListInfo(), validationSuccessful); } @Test @@ -79,7 +78,7 @@ public class DeckValidatorTest extends MageTestBase { DeckValidator validator = new Standard(); testDeckValid(validator, deck, sideboard); Assert.assertEquals("invalid message not correct", - "{Sideboard=Must contain no more than 15 cards : has 16 cards, Deck=Must contain at least 60 cards: has only 59 cards}", validator.getInvalid().toString()); + "Deck=Must contain at least 60 cards: has only 59 cards, Sideboard=Must contain no more than 15 cards : has 16 cards", validator.getErrorsListInfo()); } @Test @@ -224,119 +223,119 @@ public class DeckValidatorTest extends MageTestBase { deckList.add(new CardNameAmount("Ancestral Vision", 4)); deckList.add(new CardNameAmount("Mountain", 56)); boolean validationSuccessful = testDeckValid(validator, deckList); - Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertTrue(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Ancient Den", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.add(new CardNameAmount("Birthing Pod", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Blazing Shoal", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Bloodbraid Elf", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertTrue(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Chrome Mox", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Cloudpost", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Dark Depths", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Deathrite Shaman", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Dig Through Time", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Dread Return", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Glimpse of Nature", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Great Furnace", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Green Sun's Zenith", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Hypergenesis", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Jace, the Mind Sculptor", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertTrue(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); deckList.clear(); deckList.add(new CardNameAmount("Mental Misstep", 4)); deckList.add(new CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); - Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); - validator.getInvalid().clear(); + Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); + validator.getErrorsList().clear(); } private boolean testDeckValid(DeckValidator validator, List cards) { diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java index b3739a9383..40f8e4fe83 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java @@ -3,7 +3,11 @@ package org.mage.test.serverside.performance; import mage.abilities.keyword.InfectAbility; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.constants.PhaseStep; +import mage.constants.Zone; import mage.counters.CounterType; +import mage.game.Game; +import mage.game.mulligan.LondonMulligan; import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentImpl; import mage.remote.traffic.ZippedObjectImpl; @@ -21,7 +25,7 @@ public class SerializationTest extends CardTestPlayerBase { public void test_PermanentImpl_Simple() { CardInfo cardInfo = CardRepository.instance.findCard("Balduvian Bears"); PermanentImpl permanent = new PermanentCard(cardInfo.getCard(), playerA.getId(), currentGame); - currentGame.addPermanent(permanent); + currentGame.addPermanent(permanent, 0); Object compressed = CompressUtil.compress(permanent); Assert.assertTrue("Must be zip", compressed instanceof ZippedObjectImpl); @@ -33,10 +37,10 @@ public class SerializationTest extends CardTestPlayerBase { public void test_PermanentImpl_MarkedDamageInfo() { CardInfo cardInfo = CardRepository.instance.findCard("Balduvian Bears"); PermanentImpl permanent = new PermanentCard(cardInfo.getCard(), playerA.getId(), currentGame); - currentGame.addPermanent(permanent); + currentGame.addPermanent(permanent, 0); // mark damage from infected ability - permanent.addAbility(InfectAbility.getInstance(), currentGame); + permanent.addAbility(InfectAbility.getInstance(), null, currentGame); permanent.markDamage(1, permanent.getId(), currentGame, false, false); // test compress (it uses default java serialization) @@ -50,4 +54,25 @@ public class SerializationTest extends CardTestPlayerBase { Assert.assertEquals("Must get infected counter", 1, permanent.getCounters(currentGame).getCount(CounterType.M1M1)); } + @Test + public void test_LondonMulligan() { + LondonMulligan mulligan = new LondonMulligan(15); + Object compressed = CompressUtil.compress(mulligan); + Assert.assertTrue("Must be zip", compressed instanceof ZippedObjectImpl); + LondonMulligan uncompressed = (LondonMulligan) CompressUtil.decompress(compressed); + Assert.assertEquals("Must be same", mulligan.getFreeMulligans(), uncompressed.getFreeMulligans()); + } + + @Test + public void test_Game() { + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + Object compressed = CompressUtil.compress(currentGame); + Assert.assertTrue("Must be zip", compressed instanceof ZippedObjectImpl); + Game uncompressed = (Game) CompressUtil.decompress(compressed); + Assert.assertEquals("Must be same", 1, uncompressed.getBattlefield().getAllActivePermanents().size()); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java index f5b42ee552..0a4dad592d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java +++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java @@ -1,7 +1,5 @@ package org.mage.test.stub; -import java.io.Serializable; -import java.util.*; import mage.MageObject; import mage.MageObjectReference; import mage.abilities.*; @@ -41,6 +39,10 @@ import mage.target.TargetAmount; import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; +import java.io.Serializable; +import java.util.*; +import mage.Mana; + /** * @author Quercitron */ @@ -179,7 +181,7 @@ public class PlayerStub implements Player { } @Override - public boolean canPayLifeCost() { + public boolean canPayLifeCost(Ability ability) { return false; } @@ -287,11 +289,6 @@ public class PlayerStub implements Player { return false; } - @Override - public boolean isEmptyDraw() { - return false; - } - @Override public void pass(Game game) { @@ -528,12 +525,12 @@ public class PlayerStub implements Player { } @Override - public int drawCards(int num, Game game) { + public int drawCards(int num, UUID sourceId, Game game) { return 0; } @Override - public int drawCards(int num, Game game, List appliedEffects) { + public int drawCards(int num, UUID sourceId, Game game, List appliedEffects) { return 0; } @@ -542,11 +539,6 @@ public class PlayerStub implements Player { return false; } - @Override - public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { - return null; - } - @Override public boolean putInHand(Card card, Game game) { return false; @@ -656,6 +648,11 @@ public class PlayerStub implements Player { return 1; } + @Override + public Cards discard(Cards cards, Ability source, Game game) { + return null; + } + @Override public Card discardOne(boolean random, Ability source, Game game) { return null; @@ -896,6 +893,21 @@ public class PlayerStub implements Player { return false; } + @Override + public boolean putCardsOnTopOfLibrary(Card card, Game game, Ability source, boolean anyOrder) { + return false; + } + + @Override + public boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source) { + return false; + } + + @Override + public boolean shuffleCardsToLibrary(Card card, Game game, Ability source) { + return false; + } + @Override public boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop) { return true; @@ -1027,7 +1039,22 @@ public class PlayerStub implements Player { } @Override - public List getPlayable(Game game, boolean hidden) { + public void addAvailableTriggeredMana(List availableTriggeredMan) { + + } + + @Override + public List> getAvailableTriggeredMana() { + return null; + } + + @Override + public int announceXMana(int min, int max, String message, Game game, Ability ability) { + return 0; + } + + @Override + public List getPlayable(Game game, boolean hidden) { return null; } @@ -1042,7 +1069,7 @@ public class PlayerStub implements Player { } @Override - public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { + public LinkedHashMap getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) { return null; } @@ -1191,6 +1218,11 @@ public class PlayerStub implements Player { return false; } + @Override + public Cards millCards(int toMill, Ability source, Game game) { + return null; + } + @Override public boolean hasOpponent(UUID playerToCheckId, Game game) { return false; @@ -1376,11 +1408,6 @@ public class PlayerStub implements Player { } - @Override - public void removePhyrexianFromColors(FilterMana colors) { - - } - @Override public FilterMana getPhyrexianColors() { return (new FilterMana()); diff --git a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java index 25c5a66b22..f1a70c19b5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java +++ b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java @@ -1,8 +1,11 @@ package org.mage.test.testapi; +import mage.cards.Card; +import mage.cards.repository.CardRepository; import mage.constants.EmptyNames; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.game.stack.Spell; import mage.util.CardUtil; import org.junit.Assert; import org.junit.Test; @@ -44,6 +47,34 @@ public class TestAliases extends CardTestPlayerBase { Assert.assertFalse(CardUtil.haveSameNames("Name", "123", true)); Assert.assertFalse(CardUtil.haveSameNames("Name", EmptyNames.FACE_DOWN_CREATURE.toString(), true)); Assert.assertFalse(CardUtil.haveSameNames("Name1", "Name2", true)); + + // name with split card + Card splitCard1 = CardRepository.instance.findCard("Armed // Dangerous").getCard(); + Card splitCard2 = CardRepository.instance.findCard("Alive // Well").getCard(); + Assert.assertTrue(CardUtil.haveSameNames(splitCard1, "Armed", currentGame)); + Assert.assertTrue(CardUtil.haveSameNames(splitCard1, "Dangerous", currentGame)); + Assert.assertTrue(CardUtil.haveSameNames(splitCard1, "Armed // Dangerous", currentGame)); + Assert.assertTrue(CardUtil.haveSameNames(splitCard1, splitCard1)); + Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Other", currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Other // Dangerous", currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Armed // Other", currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(splitCard1, splitCard2)); + + // name with face down spells: face down spells don't have names, see https://github.com/magefree/mage/issues/6569 + Card bearCard = CardRepository.instance.findCard("Balduvian Bears").getCard(); + Spell normalSpell = new Spell(bearCard, bearCard.getSpellAbility(), playerA.getId(), Zone.HAND); + Spell faceDownSpell = new Spell(bearCard, bearCard.getSpellAbility(), playerA.getId(), Zone.HAND); + faceDownSpell.setFaceDown(true, currentGame); + // normal spell + Assert.assertFalse(CardUtil.haveSameNames(normalSpell, "", currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(normalSpell, "Other", currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(normalSpell, EmptyNames.FACE_DOWN_CREATURE.toString(), currentGame)); + Assert.assertTrue(CardUtil.haveSameNames(normalSpell, "Balduvian Bears", currentGame)); + // face down spell + Assert.assertFalse(CardUtil.haveSameNames(faceDownSpell, "", currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(faceDownSpell, "Other", currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(faceDownSpell, EmptyNames.FACE_DOWN_CREATURE.toString(), currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(faceDownSpell, "Balduvian Bears", currentGame)); } @Test @@ -159,7 +190,7 @@ public class TestAliases extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Embermage Goblin", 2); addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); - showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + showAvailableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Silvercoat Lion"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Silvercoat Lion"); @@ -177,7 +208,7 @@ public class TestAliases extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Embermage Goblin@goblin", 2); addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion@lion", 1); - showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + showAvailableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "@lion"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "@lion"); diff --git a/Mage.Tests/src/test/java/org/mage/test/testapi/WaitStackResolvedTest.java b/Mage.Tests/src/test/java/org/mage/test/testapi/WaitStackResolvedTest.java new file mode 100644 index 0000000000..75effe8401 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/testapi/WaitStackResolvedTest.java @@ -0,0 +1,100 @@ +package org.mage.test.testapi; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class WaitStackResolvedTest extends CardTestPlayerBase { + + @Test + public void test_Spells() { + addCard(Zone.HAND, playerA, "Firebolt", 1); // sorcery + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); // instant + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + checkStackSize("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + + // prepare - cast 3 spells + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Firebolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + checkStackSize("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 3); + checkStackObject("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Firebolt", 1); + checkStackObject("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", 2); + + // skip 1 (bolt) + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkStackSize("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + checkStackObject("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Firebolt", 1); + checkStackObject("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", 1); + + // skip 2 (bolt) + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkStackSize("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1); + checkStackObject("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Firebolt", 1); + checkStackObject("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", 0); + + // skip 3 (firebolt) + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkStackSize("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + checkStackObject("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Firebolt", 0); + checkStackObject("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", 0); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Abilities() { + // {1}{R}, Sacrifice Pyromania: Pyromania deals 1 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Pyromania", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + // {2}: Goblin Cannon deals 1 damage to any target. Sacrifice Goblin Cannon. + addCard(Zone.BATTLEFIELD, playerA, "Goblin Cannon", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 * 2); + + String goblinAbility = "{2}:"; + String pyroAbility = "{1}{R}, Sacrifice"; + + checkStackSize("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + + // prepare - activate 3 abilities + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, playerB); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroAbility, playerB); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, playerB); + checkStackSize("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 3); + checkStackObject("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroAbility, 1); + checkStackObject("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, 2); + showStack("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + + // skip 1 (goblin) + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkStackSize("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + checkStackObject("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroAbility, 1); + checkStackObject("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, 1); + + // skip 2 (pyro) + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkStackSize("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1); + checkStackObject("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroAbility, 0); + checkStackObject("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, 1); + + // skip 3 (goblin) + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkStackSize("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + checkStackObject("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroAbility, 0); + checkStackObject("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, 0); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/turnmod/ExtraTurnsTest.java b/Mage.Tests/src/test/java/org/mage/test/turnmod/ExtraTurnsTest.java index 32b27689a1..2a07f34a25 100644 --- a/Mage.Tests/src/test/java/org/mage/test/turnmod/ExtraTurnsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/turnmod/ExtraTurnsTest.java @@ -1,4 +1,3 @@ - package org.mage.test.turnmod; import mage.constants.PhaseStep; @@ -97,4 +96,50 @@ public class ExtraTurnsTest extends CardTestPlayerBase { Assert.assertEquals("For turn " + currentGame.getTurnNum() + ", playerB has to be the active player but active player is: " + currentGame.getPlayer(currentGame.getActivePlayerId()).getName(), currentGame.getActivePlayerId(), playerB.getId()); } + + /** + * https://github.com/magefree/mage/issues/6824 + * + * When you cast miracled Temporal Mastery with God-Eternal Kefnet on the + * battlefield and copy it with it's ability you get only 1 extra turn. It + * should be 2, since you cast Temporal Mastery with it's miracle ability + + * you get a copy from Kefnet's ability. Still after first extra turn game + * proceeds to next player. + */ + @Test + public void testCopyMiracledTemporalMastery4TwoExtraTurns() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 7); + // Flying + // You may reveal the first card you draw each turn as you draw it. Whenever you reveal an instant or sorcery card this way, + // copy that card and you may cast the copy. That copy costs {2} less to cast. + // When God-Eternal Kefnet dies or is put into exile from the battlefield, you may put it into its owner’s library third from the top. + addCard(Zone.BATTLEFIELD, playerB, "God-Eternal Kefnet", 1); + // Take an extra turn after this one. Exile Temporal Mastery. + // Miracle {1}{U} (You may cast this card for its miracle cost when you draw it if it's the first card you drew this turn.) + addCard(Zone.LIBRARY, playerB, "Temporal Mastery", 1); // Sorcery {5}{U}{U} + skipInitShuffling(); + + setChoice(playerB, "Yes"); // Would you like to reveal first drawn card (Temporal Mastery, you can copy it and cast {2} less)? + setChoice(playerB, "Yes"); // Would you like to copy Temporal Mastery and cast it {2} less? + setChoice(playerB, "Yes"); // Reveal Temporal Mastery to be able to use Miracle? + setChoice(playerB, "Yes"); // Miracle {1}{U} (You may cast this card for its miracle cost when you draw it if it's the first card you drew this turn.) + + setChoice(playerB, "No"); // Would you like to reveal first drawn card? (Turn 3) + setChoice(playerB, "No"); // Would you like to reveal first drawn card? (Turn 4) + + // Turn 3 + 4 are extra turns + setStopAt(4, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + + assertExileCount(playerB, "Temporal Mastery", 1); + + Assert.assertTrue("Turn 4 is an extra turn ", currentGame.getState().isExtraTurn()); + Assert.assertEquals("For turn " + currentGame.getTurnNum() + ", playerB has to be the active player but active player is: " + + currentGame.getPlayer(currentGame.getActivePlayerId()).getName(), currentGame.getActivePlayerId(), playerB.getId()); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/utils/BoostCountTest.java b/Mage.Tests/src/test/java/org/mage/test/utils/BoostCountTest.java new file mode 100644 index 0000000000..6121fb2bad --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/utils/BoostCountTest.java @@ -0,0 +1,24 @@ +package org.mage.test.utils; + +import mage.util.CardUtil; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author JayDi85 + */ +public class BoostCountTest { + + @Test + public void test_BoostCountSigns() { + Assert.assertEquals(CardUtil.getBoostCountAsStr(0, 0), "+0/+0"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(1, 0), "+1/+0"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(0, 1), "+0/+1"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(1, 1), "+1/+1"); + + Assert.assertEquals(CardUtil.getBoostCountAsStr(-1, 0), "-1/-0"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(0, -1), "-0/-1"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(-1, 1), "-1/+1"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(1, -1), "+1/-1"); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java b/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java index e7d2c5e946..632e2b4d26 100644 --- a/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java @@ -209,11 +209,32 @@ public class ManaOptionsTest extends CardTestPlayerBase { assertManaOptions("{C}{G}{Any}", manaOptions); } + // Nykthos, Shrine to Nyx + @Test + public void testNykthos4a() { + addCard(Zone.BATTLEFIELD, playerA, "Sedge Scorpion", 4); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + // {T}: Add {C}. + // {2}, {T}: Choose a color. Add an amount of mana of that color equal to your devotion to that color. (Your devotion to a color is the number of mana symbols of that color in the mana costs of permanents you control.) + addCard(Zone.BATTLEFIELD, playerA, "Nykthos, Shrine to Nyx", 1); + + setStopAt(1, PhaseStep.UPKEEP); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + assertDuplicatedManaOptions(manaOptions); + + Assert.assertEquals("mana variations don't fit", 2, manaOptions.size()); + assertManaOptions("{C}{G}{G}{G}", manaOptions); + assertManaOptions("{G}{G}{G}{G}{G}", manaOptions); + + } + // Nykthos, Shrine to Nyx // {T}: Add {C}. // {2}, {T}: Choose a color. Add an amount of mana of that color equal to your devotion to that color. (Your devotion to a color is the number of mana symbols of that color in the mana costs of permanents you control.) @Test - public void testNykthos4() { + public void testNykthos4b() { // If a land is tapped for two or more mana, it produces {C} instead of any other type and amount. // Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn. addCard(Zone.BATTLEFIELD, playerA, "Damping Sphere", 1); @@ -300,9 +321,11 @@ public class ManaOptionsTest extends CardTestPlayerBase { execute(); ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); - Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + Assert.assertEquals("mana variations don't fit", 3, manaOptions.size()); assertDuplicatedManaOptions(manaOptions); + assertManaOptions("{C}{C}", manaOptions); assertManaOptions("{Any}{Any}", manaOptions); + assertManaOptions("{C}{Any}", manaOptions); } @Test @@ -399,7 +422,6 @@ public class ManaOptionsTest extends CardTestPlayerBase { } @Test - @Ignore // TriggeredManaAbilities not supported yet for getAvailableMana public void testCryptGhast() { //Extort (Whenever you cast a spell, you may pay {WB}. If you do, each opponent loses 1 life and you gain that much life.) // Whenever you tap a Swamp for mana, add {B} (in addition to the mana the land produces). diff --git a/Mage.Verify/pom.xml b/Mage.Verify/pom.xml index 43b3ce0a3b..77f0c934ea 100644 --- a/Mage.Verify/pom.xml +++ b/Mage.Verify/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 mage-verify @@ -44,12 +44,12 @@ org.reflections reflections - 0.9.11 + 0.9.12 org.mage mage-client - 1.4.42 + 1.4.44 diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index fa6ed0a7ce..d821855a24 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -1,9 +1,14 @@ package mage.verify; import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.MultikickerAbility; import mage.cards.*; import mage.cards.basiclands.BasicLand; +import mage.cards.decks.DeckCardLists; +import mage.cards.decks.importer.DeckImporter; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.cards.repository.CardScanner; @@ -11,6 +16,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SubType; import mage.constants.SuperType; +import mage.game.command.Plane; import mage.game.draft.RateCard; import mage.game.permanent.token.Token; import mage.game.permanent.token.TokenImpl; @@ -30,9 +36,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Paths; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -105,7 +110,6 @@ public class VerifyCardDataTest { skipListAddName(SKIP_LIST_COST, "WC00", "Erase"); skipListAddName(SKIP_LIST_COST, "H17", "Grimlock, Dinobot Leader"); skipListAddName(SKIP_LIST_COST, "UST", "Everythingamajig"); - skipListAddName(SKIP_LIST_COST, "ANA", "Shrine Keeper"); // until it's been corrected in MTGJSON // supertype skipListCreate(SKIP_LIST_SUPERTYPE); @@ -119,17 +123,6 @@ public class VerifyCardDataTest { // subtype skipListCreate(SKIP_LIST_SUBTYPE); skipListAddName(SKIP_LIST_SUBTYPE, "UGL", "Miss Demeanor"); - // the following are temporary - subtypesToIgnore.add("Noble"); - skipListAddName(SKIP_LIST_SUBTYPE, "AER", "Ridgescale Tusker"); - skipListAddName(SKIP_LIST_SUBTYPE, "ME3", "Lady Caleria"); - skipListAddName(SKIP_LIST_SUBTYPE, "LEG", "Lady Caleria"); - // Errata: Chicken -> Bird (2020). Remove these, when it's been corrected in MTGJSON - skipListAddName(SKIP_LIST_SUBTYPE, "UGL", "Chicken a la King"); - skipListAddName(SKIP_LIST_SUBTYPE, "UGL", "Free-Range Chicken"); - skipListAddName(SKIP_LIST_SUBTYPE, "UND", "Free-Range Chicken"); - skipListAddName(SKIP_LIST_SUBTYPE, "UGL", "Poultrygeist"); - skipListAddName(SKIP_LIST_SUBTYPE, "UND", "Poultrygeist"); // number skipListCreate(SKIP_LIST_NUMBER); @@ -218,7 +211,7 @@ public class VerifyCardDataTest { private final ArrayList outputMessages = new ArrayList<>(); @Test - public void verifyCards() throws IOException { + public void test_verifyCards() throws IOException { int cardIndex = 0; for (Card card : CardScanner.getAllCards()) { cardIndex++; @@ -233,12 +226,12 @@ public class VerifyCardDataTest { printMessages(outputMessages); if (failed > 0) { - Assert.fail(failed + " errors in verify"); + Assert.fail("found " + failed + " errors in cards verify (see errors list above)"); } } @Test - public void checkDuplicateCardNumbersInDB() { + public void test_checkDuplicateCardNumbersInDB() { Collection doubleErrors = new ArrayList<>(); Collection sets = Sets.getInstance().values(); @@ -290,7 +283,7 @@ public class VerifyCardDataTest { @Test @Ignore // TODO: enable it after THB set will be completed - public void checkDoubleRareCardsInSets() { + public void test_checkDoubleRareCardsInSets() { // all basic sets after THB must have double rare cards (one normal, one bonus) // ELD can have same rules, but xmage stores it as different sets (ELD and CELD) Date startCheck = TherosBeyondDeath.getInstance().getReleaseDate(); @@ -349,7 +342,7 @@ public class VerifyCardDataTest { } @Test - public void checkWrongCardClasses() { + public void test_checkWrongCardClasses() { Collection errorsList = new ArrayList<>(); Map classesIndex = new HashMap<>(); int totalCards = 0; @@ -390,8 +383,7 @@ public class VerifyCardDataTest { } @Test - public void checkMissingSets() { - + public void test_checkMissingSets() { Collection errorsList = new ArrayList<>(); int totalMissingSets = 0; @@ -405,7 +397,7 @@ public class VerifyCardDataTest { if (skipListHaveName(SKIP_LIST_INVALID_SETS, searchSet)) continue; - ExpansionSet mageSet = Sets.findSet(searchSet.toUpperCase()); + ExpansionSet mageSet = Sets.findSet(searchSet.toUpperCase(Locale.ENGLISH)); if (mageSet == null) { totalMissingSets = totalMissingSets + 1; totalMissingCards = totalMissingCards + refSet.cards.size(); @@ -422,6 +414,55 @@ public class VerifyCardDataTest { } } + @Test + @Ignore // TODO: enable and fix broken decks after promo sets merge https://github.com/magefree/mage/pull/6190 + public void test_checkSampleDecks() { + Collection errorsList = new ArrayList<>(); + + // collect all files + final String rootPath = "..\\Mage.Client\\release\\sample-decks\\"; + Collection filesList = new ArrayList<>(); + try { + Files.walkFileTree(Paths.get(rootPath), new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + filesList.add(file); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + e.printStackTrace(); + errorsList.add("Error: sample deck - can't get folder content - " + e.getMessage()); + } + Assert.assertTrue("Sample decks: can't find any deck files", filesList.size() > 0); + + // try to open deck files + int totalErrorFiles = 0; + for (Path deckFile : filesList) { + String deckName = deckFile.toString().replace(rootPath, ""); + StringBuilder deckErrors = new StringBuilder(); + DeckCardLists deckCards = DeckImporter.importDeckFromFile(deckFile.toString(), deckErrors); + + if (!deckErrors.toString().isEmpty()) { + errorsList.add("Error: sample contains errors " + deckName); + System.out.println("Errors in sample file " + deckName + ":\n" + deckErrors.toString()); + totalErrorFiles++; + continue; + } + + if ((deckCards.getCards().size() + deckCards.getSideboard().size()) < 10) { + errorsList.add("Error: sample deck contains too little cards " + deckName); + totalErrorFiles++; + continue; + } + } + + printMessages(errorsList); + if (errorsList.size() > 0) { + Assert.fail("Found sample decks: " + filesList.size() + "; with errors: " + totalErrorFiles); + } + } + private Object createNewObject(Class clazz) { try { Constructor cons = clazz.getConstructor(); @@ -464,13 +505,13 @@ public class VerifyCardDataTest { } @Test - public void checkMissingSetData() { + public void test_checkMissingSetData() { Collection errorsList = new ArrayList<>(); Collection warningsList = new ArrayList<>(); Collection sets = Sets.getInstance().values(); - // 1. wrong set class names + // CHECK: wrong set class names for (ExpansionSet set : sets) { String className = extractShortClass(set.getClass()); String needClassName = set.getName() @@ -493,7 +534,7 @@ public class VerifyCardDataTest { } } - // 2. wrong basic lands settings (it's for lands search, not booster construct) + // CHECK: wrong basic lands settings (it's for lands search, not booster construct) Map skipLandCheck = new HashMap<>(); for (ExpansionSet set : sets) { if (skipLandCheck.containsKey(set.getName())) { @@ -526,7 +567,7 @@ public class VerifyCardDataTest { // TODO: add test to check num cards (hasBasicLands and numLand > 0) } - // 3. wrong snow land info + // CHECK: wrong snow land info for (ExpansionSet set : sets) { boolean needSnow = CardRepository.instance.haveSnowLands(set.getCode()); boolean haveSnow = false; @@ -552,7 +593,7 @@ public class VerifyCardDataTest { } @Test - public void checkMissingCardData() { + public void test_checkMissingCardData() { Collection errorsList = new ArrayList<>(); Collection warningsList = new ArrayList<>(); @@ -605,8 +646,7 @@ public class VerifyCardDataTest { } @Test - //@Ignore // TODO: enable it on copy() methods removing - public void checkWatcherCopyMethods() { + public void test_checkWatcherCopyMethods() { Collection errorsList = new ArrayList<>(); Collection warningsList = new ArrayList<>(); @@ -683,7 +723,7 @@ public class VerifyCardDataTest { @Test @Ignore // TODO: enable test after massive token fixes - public void checkMissingTokenData() { + public void test_checkMissingTokenData() { Collection errorsList = new ArrayList<>(); Collection warningsList = new ArrayList<>(); @@ -759,9 +799,8 @@ public class VerifyCardDataTest { for (Class tokenClass : publicTokens) { String className = extractShortClass(tokenClass); Token token = (Token) createNewObject(tokenClass); - //Assert.assertNotNull("Can't create token by default constructor", token); if (token == null) { - Assert.fail("Can't create token by default constructor: " + className); + errorsList.add("error, token must have default constructor with zero params: " + tokenClass.getName()); } else if (tokDataNamesIndex.getOrDefault(token.getName(), "").isEmpty()) { errorsList.add("error, can't find data in card-pictures-tok.txt for token: " + tokenClass.getName() + " -> " + token.getName()); } @@ -778,6 +817,57 @@ public class VerifyCardDataTest { if (errorsList.size() > 0) { Assert.fail("Found token errors: " + errorsList.size()); } + + // TODO: all token must have correct availableImageSetCodes (all sets with that token) + // Some sets have original card, but don't have token card at all. So you must use scryfall tokens list above to find + // all token's sets and compare with xmage + } + + @Test + public void test_checkMissingPlanesData() { + Collection errorsList = new ArrayList<>(); + + Reflections reflections = new Reflections("mage."); + Set> planesClassesList = reflections.getSubTypesOf(Plane.class); + + + // 1. correct class name + for (Class planeClass : planesClassesList) { + if (!planeClass.getName().endsWith("Plane")) { + String className = extractShortClass(planeClass); + errorsList.add("error, plane class must ends with Plane: " + className + " from " + planeClass.getName()); + } + } + + // 2. correct package + for (Class planeClass : planesClassesList) { + String fullClass = planeClass.getName(); + if (!fullClass.startsWith("mage.game.command.planes.")) { + String className = extractShortClass(planeClass); + errorsList.add("error, plane must be stored in mage.game.command.planes package: " + className + " from " + planeClass.getName()); + } + } + + // 3. correct constructor + for (Class planeClass : planesClassesList) { + String className = extractShortClass(planeClass); + Plane plane; + try { + plane = (Plane) createNewObject(planeClass); + + // 4. must have type/name + if (plane.getPlaneType() == null) { + errorsList.add("error, plane must have plane type: " + className + " from " + planeClass.getName()); + } + } catch (Throwable e) { + errorsList.add("error, can't create plane with default constructor: " + className + " from " + planeClass.getName()); + } + } + + printMessages(errorsList); + if (errorsList.size() > 0) { + Assert.fail("Found plane errors: " + errorsList.size()); + } } private static final Pattern SHORT_JAVA_STRING = Pattern.compile("(?<=\")[A-Z][a-z]+(?=\")"); @@ -934,6 +1024,28 @@ public class VerifyCardDataTest { fail(card, "abilities", "card have Multikicker ability, but missing it in rules text"); } + // special check: missing or wrong ability/effect hints + Map hints = new HashMap<>(); + hints.put(MenaceAbility.class, "can't be blocked except by two or more"); + hints.put(ScryEffect.class, "Look at the top card of your library"); + for (Class objectClass : hints.keySet()) { + String objectHint = hints.get(objectClass); + // ability/effect must have description or not + boolean mustCheck = card.getAbilities().containsClass(objectClass) + || card.getAbilities().stream() + .map(Ability::getAllEffects) + .flatMap(Collection::stream) + .anyMatch(effect -> effect.getClass().isAssignableFrom(objectClass)); + mustCheck = false; // TODO: enable and fix all problems with effect and ability hints + if (mustCheck) { + boolean needHint = ref.text.contains(objectHint); + boolean haveHint = card.getRules().stream().anyMatch(rule -> rule.contains(objectHint)); + if (needHint != haveHint) { + fail(card, "abilities", "card have " + objectClass.getSimpleName() + " but hint is wrong (it must be " + (needHint ? "enabled" : "disabled") + ")"); + } + } + } + // spells have only 1 ability if (card.isSorcery() || card.isInstant()) { return; @@ -995,20 +1107,32 @@ public class VerifyCardDataTest { } @Test - @Ignore - public void showCardInfo() throws Exception { + public void test_showCardInfo() throws Exception { // debug only: show direct card info (takes it from class file, not from db repository) - String cardName = "Essence Capture"; + // can check multiple cards at once, example: name1;name2;name3 + String cardNames = "Armed // Dangerous;Beacon Behemoth;Grizzly Bears"; CardScanner.scan(); - CardSetInfo testSet = new CardSetInfo(cardName, "test", "123", Rarity.COMMON); - CardInfo cardInfo = CardRepository.instance.findCard(cardName); - Card card = CardImpl.createCard(cardInfo.getClassName(), testSet); - System.out.println(card.getName()); - if (card instanceof SplitCard) { - card.getAbilities().getRules(card.getName()).stream().forEach(System.out::println); - } else { - card.getRules().stream().forEach(System.out::println); - } + Arrays.stream(cardNames.split(";")).forEach(cardName -> { + cardName = cardName.trim(); + CardSetInfo testSet = new CardSetInfo(cardName, "test", "123", Rarity.COMMON); + CardInfo cardInfo = CardRepository.instance.findCard(cardName); + if (cardInfo == null) { + Assert.fail("Can't find card name: " + cardName); + } + Card card = CardImpl.createCard(cardInfo.getClassName(), testSet); + System.out.println(); + System.out.println(card.getName() + " " + card.getManaCost().getText()); + if (card instanceof SplitCard) { + card.getAbilities().getRules(card.getName()).forEach(this::printAbilityText); + } else { + card.getRules().forEach(this::printAbilityText); + } + }); + } + + private void printAbilityText(String text) { + text = text.replace("
", "\n"); + System.out.println(text); } private void checkWrongAbilitiesText(Card card, JsonCard ref, int cardIndex) { @@ -1258,7 +1382,7 @@ public class VerifyCardDataTest { } @Test - public void testCardRatingConsistency() { + public void test_checkCardRatingConsistency() { // all cards with same name must have same rating (see RateCard.rateCard) // cards rating must be consistency (same) for card sorting List cardsList = new ArrayList<>(CardScanner.getAllCards()); @@ -1277,7 +1401,7 @@ public class VerifyCardDataTest { } @Test - public void testCardsCreatingAndConstructorErrors() { + public void test_CardsCreatingAndConstructorErrors() { int errorsCount = 0; Collection sets = Sets.getInstance().values(); for (ExpansionSet set : sets) { diff --git a/Mage/pom.xml b/Mage/pom.xml index 0ad03fa743..40cc7b98ca 100644 --- a/Mage/pom.xml +++ b/Mage/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 mage @@ -14,12 +14,16 @@ Mage Framework + + com.googlecode.json-simple + json-simple + 1.1.1 + log4j log4j jar - com.h2database h2 @@ -42,7 +46,7 @@ com.google.protobuf protobuf-java - 3.7.0-rc1 + 4.0.0-rc-2 @@ -91,7 +95,7 @@ org.codehaus.mojo build-helper-maven-plugin - 1.12 + 3.2.0 add-classes diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java index 744ec43f07..53bb7d9cef 100644 --- a/Mage/src/main/java/mage/MageObject.java +++ b/Mage/src/main/java/mage/MageObject.java @@ -1,5 +1,9 @@ package mage; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCost; @@ -15,12 +19,6 @@ import mage.game.Game; import mage.game.events.ZoneChangeEvent; import mage.util.SubTypeList; -import java.io.Serializable; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; - public interface MageObject extends MageItem, Serializable { String getName(); @@ -33,7 +31,7 @@ public interface MageObject extends MageItem, Serializable { void setName(String name); - Set getCardType(); + ArrayList getCardType(); SubTypeList getSubtype(Game game); @@ -41,9 +39,15 @@ public interface MageObject extends MageItem, Serializable { Set getSuperType(); + /** + * For cards: return basic abilities (without dynamic added) For permanents: + * return all abilities (dynamic ability inserts into permanent) + * + * @return + */ Abilities getAbilities(); - boolean hasAbility(UUID abilityId, Game game); + boolean hasAbility(Ability ability, Game game); ObjectColor getColor(Game game); @@ -180,9 +184,9 @@ public interface MageObject extends MageItem, Serializable { } if (this.isCreature() && otherCard.isCreature()) { - if (this.getAbilities().contains(ChangelingAbility.getInstance()) + if (this.hasAbility(ChangelingAbility.getInstance(), game) || this.isAllCreatureTypes() - || otherCard.getAbilities().contains(ChangelingAbility.getInstance()) + || otherCard.hasAbility(ChangelingAbility.getInstance(), game) || otherCard.isAllCreatureTypes()) { return true; } @@ -200,7 +204,7 @@ public interface MageObject extends MageItem, Serializable { void setIsAllCreatureTypes(boolean value); - default void addCardTypes(Set cardType) { + default void addCardTypes(ArrayList cardType) { getCardType().addAll(cardType); } diff --git a/Mage/src/main/java/mage/MageObjectImpl.java b/Mage/src/main/java/mage/MageObjectImpl.java index b68592bb6b..2183f20377 100644 --- a/Mage/src/main/java/mage/MageObjectImpl.java +++ b/Mage/src/main/java/mage/MageObjectImpl.java @@ -1,5 +1,6 @@ package mage; +import java.util.*; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; @@ -21,8 +22,6 @@ import mage.game.permanent.Permanent; import mage.util.GameLog; import mage.util.SubTypeList; -import java.util.*; - public abstract class MageObjectImpl implements MageObject { protected UUID objectId; @@ -32,7 +31,7 @@ public abstract class MageObjectImpl implements MageObject { protected ObjectColor color; protected ObjectColor frameColor; protected FrameStyle frameStyle; - protected Set cardType = EnumSet.noneOf(CardType.class); + protected ArrayList cardType = new ArrayList<>(); protected SubTypeList subtype = new SubTypeList(); protected boolean isAllCreatureTypes; protected Set supertype = EnumSet.noneOf(SuperType.class); @@ -112,7 +111,7 @@ public abstract class MageObjectImpl implements MageObject { } @Override - public Set getCardType() { + public ArrayList getCardType() { return cardType; } @@ -132,12 +131,12 @@ public abstract class MageObjectImpl implements MageObject { } @Override - public boolean hasAbility(UUID abilityId, Game game) { - if (this.getAbilities().containsKey(abilityId)) { + public boolean hasAbility(Ability ability, Game game) { + if (this.getAbilities().contains(ability)) { return true; } Abilities otherAbilities = game.getState().getAllOtherAbilities(getId()); - return otherAbilities != null && otherAbilities.containsKey(abilityId); + return otherAbilities != null && otherAbilities.contains(ability); } @Override @@ -171,39 +170,22 @@ public abstract class MageObjectImpl implements MageObject { // its frame colors. if (this.isLand()) { ObjectColor cl = frameColor.copy(); + Set manaTypes = EnumSet.noneOf(ManaType.class); for (Ability ab : getAbilities()) { if (ab instanceof ActivatedManaAbilityImpl) { - ActivatedManaAbilityImpl mana = (ActivatedManaAbilityImpl) ab; - try { - List manaAdded = mana.getNetMana(game); - for (Mana m : manaAdded) { - if (m.getAny() > 0) { - return new ObjectColor("WUBRG"); - } - if (m.getWhite() > 0) { - cl.setWhite(true); - } - if (m.getBlue() > 0) { - cl.setBlue(true); - } - if (m.getBlack() > 0) { - cl.setBlack(true); - } - if (m.getRed() > 0) { - cl.setRed(true); - } - if (m.getGreen() > 0) { - cl.setGreen(true); - } - } - } catch (NullPointerException e) { - // Ability depends on game - // but no game passed - // All such abilities are 5-color ones - return new ObjectColor("WUBRG"); - } + manaTypes.addAll(((ActivatedManaAbilityImpl) ab).getProducableManaTypes(game)); } } + cl.setWhite(manaTypes.contains(ManaType.WHITE)); + cl.setBlue(manaTypes.contains(ManaType.BLUE)); + cl.setBlack(manaTypes.contains(ManaType.BLACK)); + cl.setRed(manaTypes.contains(ManaType.RED)); + cl.setGreen(manaTypes.contains(ManaType.GREEN)); + +// // Ability depends on game +// // but no game passed +// // All such abilities are 5-color ones +// return new ObjectColor("WUBRG"); return cl; } else { // For everything else, just return the frame colors @@ -329,7 +311,7 @@ public abstract class MageObjectImpl implements MageObject { */ @Override public void removePTCDA() { - for (Iterator iter = this.getAbilities().iterator(); iter.hasNext(); ) { + for (Iterator iter = this.getAbilities().iterator(); iter.hasNext();) { Ability ability = iter.next(); for (Effect effect : ability.getEffects()) { if (effect instanceof ContinuousEffect && ((ContinuousEffect) effect).getSublayer() == SubLayer.CharacteristicDefining_7a) { diff --git a/Mage/src/main/java/mage/Mana.java b/Mage/src/main/java/mage/Mana.java index f6a594292f..883ec29e2a 100644 --- a/Mage/src/main/java/mage/Mana.java +++ b/Mage/src/main/java/mage/Mana.java @@ -43,13 +43,13 @@ public class Mana implements Comparable, Serializable, Copyable { * Creates a {@link Mana} object with the passed in values. Values can not * be less than 0. Any values less than 0 will be logged and set to 0. * - * @param red total Red mana to have. - * @param green total Green mana to have. - * @param blue total Blue mana to have. - * @param white total White mana to have. - * @param black total Black mana to have. - * @param generic total Generic mana to have. - * @param any total Any mana to have. + * @param red total Red mana to have. + * @param green total Green mana to have. + * @param blue total Blue mana to have. + * @param white total White mana to have. + * @param black total Black mana to have. + * @param generic total Generic mana to have. + * @param any total Any mana to have. * @param colorless total Colorless mana to have. */ public Mana(final int red, final int green, final int blue, final int white, final int black, final int generic, final int any, final int colorless) { @@ -142,6 +142,35 @@ public class Mana implements Comparable, Serializable, Copyable { } } + public Mana(final ManaType manaType, int num) { + Objects.requireNonNull(manaType, "The passed in ManaType can not be null"); + switch (manaType) { + case GREEN: + green = num; + break; + case RED: + red = num; + break; + case BLACK: + black = num; + break; + case BLUE: + blue = num; + break; + case WHITE: + white = num; + break; + case COLORLESS: + colorless = num; + break; + case GENERIC: + generic = num; + break; + default: + throw new IllegalArgumentException("Unknown manaType: " + manaType); + } + } + /** * Creates a {@link Mana} object with the passed in {@code num} of Red mana. * {@code num} can not be a negative value. Negative values will be logged @@ -161,7 +190,7 @@ public class Mana implements Comparable, Serializable, Copyable { * * @param num value of Green mana to create. * @return a {@link Mana} object with the passed in {@code num} of Green - * mana. + * mana. */ public static Mana GreenMana(int num) { return new Mana(0, notNegative(num, "Green"), 0, 0, 0, 0, 0, 0); @@ -174,7 +203,7 @@ public class Mana implements Comparable, Serializable, Copyable { * * @param num value of Blue mana to create. * @return a {@link Mana} object with the passed in {@code num} of Blue - * mana. + * mana. */ public static Mana BlueMana(int num) { return new Mana(0, 0, notNegative(num, "Blue"), 0, 0, 0, 0, 0); @@ -187,7 +216,7 @@ public class Mana implements Comparable, Serializable, Copyable { * * @param num value of White mana to create. * @return a {@link Mana} object with the passed in {@code num} of White - * mana. + * mana. */ public static Mana WhiteMana(int num) { return new Mana(0, 0, 0, notNegative(num, "White"), 0, 0, 0, 0); @@ -200,7 +229,7 @@ public class Mana implements Comparable, Serializable, Copyable { * * @param num value of Black mana to create. * @return a {@link Mana} object with the passed in {@code num} of Black - * mana. + * mana. */ public static Mana BlackMana(int num) { return new Mana(0, 0, 0, 0, notNegative(num, "Black"), 0, 0, 0); @@ -213,7 +242,7 @@ public class Mana implements Comparable, Serializable, Copyable { * * @param num value of Generic mana to create. * @return a {@link Mana} object with the passed in {@code num} of Generic - * mana. + * mana. */ public static Mana GenericMana(int num) { return new Mana(0, 0, 0, 0, 0, notNegative(num, "Generic"), 0, 0); @@ -226,7 +255,7 @@ public class Mana implements Comparable, Serializable, Copyable { * * @param num value of Colorless mana to create. * @return a {@link Mana} object with the passed in {@code num} of Colorless - * mana. + * mana. */ public static Mana ColorlessMana(int num) { return new Mana(0, 0, 0, 0, 0, 0, 0, notNegative(num, "Colorless")); @@ -444,7 +473,7 @@ public class Mana implements Comparable, Serializable, Copyable { * * @param filter the colors of mana to return the count for. * @return the count of filtered mana provided by the passed in - * {@link FilterMana}. + * {@link FilterMana}. */ public int count(final FilterMana filter) { if (filter == null) { @@ -898,10 +927,10 @@ public class Mana implements Comparable, Serializable, Copyable { * Returns if this objects mana contains any coloured mana the same as the * passed in {@link Mana}'s mana. * - * @param mana the mana to check for + * @param mana the mana to check for * @param includeColorless also check for colorless * @return true if this contains any of the same type of coloured mana that - * this has + * this has */ public boolean containsAny(final Mana mana, boolean includeColorless) { if (mana.black > 0 && this.black > 0) { @@ -929,7 +958,7 @@ public class Mana implements Comparable, Serializable, Copyable { * * @param color the color to return the count for. * @return the total count of mana in this object as specified by the passed - * in {@link ColoredManaSymbol}. + * in {@link ColoredManaSymbol}. */ public int getColor(final ColoredManaSymbol color) { if (color == ColoredManaSymbol.G) { @@ -956,7 +985,7 @@ public class Mana implements Comparable, Serializable, Copyable { * * @param manaType the type to return the count for. * @return the total count of mana in this object as specified by the passed - * in {@link ManaType}. + * in {@link ManaType}. */ public int get(final ManaType manaType) { switch (manaType) { @@ -981,7 +1010,7 @@ public class Mana implements Comparable, Serializable, Copyable { * {@code amount} . * * @param manaType the color of the mana to set - * @param amount the value to set the mana too + * @param amount the value to set the mana too */ public void set(final ManaType manaType, final int amount) { switch (manaType) { @@ -1056,7 +1085,7 @@ public class Mana implements Comparable, Serializable, Copyable { * * @param mana the mana to compare with * @return if this object has more than or equal mana to the passed in - * {@link Mana}. + * {@link Mana}. */ public boolean includesMana(Mana mana) { return this.green >= mana.green @@ -1123,10 +1152,7 @@ public class Mana implements Comparable, Serializable, Copyable { } } if (lessMana.getColorless() > moreMana.getColorless()) { - anyDiff -= lessMana.getColorless() - moreMana.getColorless(); - if (anyDiff < 0) { - return null; - } + return null; // Any (color) can't produce colorless mana } if (lessMana.getAny() > moreMana.getAny()) { return null; @@ -1217,10 +1243,10 @@ public class Mana implements Comparable, Serializable, Copyable { * is negative, it is logged and 0 is returned. * * @param value the value to check. - * @param name the name of the value to check. Used to make logging of the - * {@code value} easier + * @param name the name of the value to check. Used to make logging of the + * {@code value} easier * @return the {@code value} passed in, unless it is minus, in which case 0 - * is returned. + * is returned. */ private static int notNegative(int value, final String name) { if (value < 0) { diff --git a/Mage/src/main/java/mage/abilities/Abilities.java b/Mage/src/main/java/mage/abilities/Abilities.java index 0d1000d172..cbceb72bc3 100644 --- a/Mage/src/main/java/mage/abilities/Abilities.java +++ b/Mage/src/main/java/mage/abilities/Abilities.java @@ -2,9 +2,12 @@ package mage.abilities; import java.io.Serializable; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.function.Predicate; + import mage.abilities.keyword.ProtectionAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.constants.Zone; @@ -255,7 +258,8 @@ public interface Abilities extends List, Serializable { boolean containsAll(Abilities abilities); /** - * Searches this set of abilities for the existence of the give class + * Searches this set of abilities for the existence of the given class + * Warning, it doesn't work with inherited classes (e.g. it's not equal to instanceOf command) * * @param classObject * @return True if the passed in class is also in this set of abilities. @@ -271,4 +275,13 @@ public interface Abilities extends List, Serializable { Abilities copy(); String getValue(); + + @Deprecated // use permanent.removeAbility instead + boolean remove(Object o); + + @Deprecated // use permanent.removeAbility instead + boolean removeAll(Collection c); + + @Deprecated // use permanent.removeAbility instead + boolean removeIf(Predicate filter); } diff --git a/Mage/src/main/java/mage/abilities/AbilitiesImpl.java b/Mage/src/main/java/mage/abilities/AbilitiesImpl.java index d389a5ac15..d46348ac00 100644 --- a/Mage/src/main/java/mage/abilities/AbilitiesImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilitiesImpl.java @@ -73,10 +73,14 @@ public class AbilitiesImpl extends ArrayList implements Ab StringBuilder sbRule = threadLocalBuilder.get(); for (Cost cost : ability.getCosts()) { if (cost.getText() != null && !cost.getText().isEmpty()) { + String costText = cost.getText(); if (!cost.getText().startsWith("As an additional cost")) { sbRule.append("As an additional cost to cast this spell, "); + if (!costText.isEmpty()) { + costText = Character.toLowerCase(costText.charAt(0)) + costText.substring(1); + } } - sbRule.append(cost.getText()).append(".
"); + sbRule.append(costText).append(".
"); } } rules.add(sbRule.toString()); @@ -228,7 +232,8 @@ public class AbilitiesImpl extends ArrayList implements Ab if (ability.getId().equals(test.getId())) { return true; } - if (ability.getOriginalId().equals(test.getId())) { + if (ability.getOriginalId().equals(test.getOriginalId())) { + // on ability resolve: engine creates ability's copy and generates newId(), so you must use originalId to find that ability in card later return true; } if (ability instanceof MageSingleton && test instanceof MageSingleton && ability.getRule().equals(test.getRule())) { @@ -239,7 +244,7 @@ public class AbilitiesImpl extends ArrayList implements Ab } @Override - public boolean containsRule(T ability) { + public boolean containsRule(T ability) { // TODO: remove return stream().anyMatch(rule -> rule.getRule().equals(ability.getRule())); } @@ -258,7 +263,7 @@ public class AbilitiesImpl extends ArrayList implements Ab } @Override - public boolean containsKey(UUID abilityId) { + public boolean containsKey(UUID abilityId) { // TODO: remove return stream().anyMatch(ability -> abilityId.equals(ability.getId())); } diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index dd5b811f84..66dd169e6a 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -23,6 +23,7 @@ import mage.watchers.Watcher; import java.io.Serializable; import java.util.List; import java.util.UUID; +import mage.abilities.costs.common.TapSourceCost; /** * Practically everything in the game is started from an Ability. This interface @@ -190,13 +191,19 @@ public interface Ability extends Controllable, Serializable { /** * Retrieves all targets that must be satisfied before this ability is put - * onto the stack. + * onto the stack. Warning, return targets from first/current mode only. * * @return All {@link Targets} that must be satisfied before this ability is * put onto the stack. */ Targets getTargets(); + /** + * Retrieves all selected targets, read only. Multi-modes return different targets. + * Works on stack only (after real cast/activate) + */ + Targets getAllSelectedTargets(); + /** * Retrieves the {@link Target} located at the 0th index in the * {@link Targets}. A call to the method is equivalent to @@ -360,6 +367,19 @@ public interface Ability extends Controllable, Serializable { * @return */ boolean hasSourceObjectAbility(Game game, MageObject source, GameEvent event); + + /** + * Returns true if the ability has a tap itself in their costs + * @return + */ + default boolean hasTapCost() { + for (Cost cost : this.getCosts()) { + if (cost instanceof TapSourceCost) { + return true; + } + } + return false; + } /** * Returns true if this ability has to be shown as topmost of all the rules @@ -447,7 +467,7 @@ public interface Ability extends Controllable, Serializable { * * @param abilityWord */ - void setAbilityWord(AbilityWord abilityWord); + Ability setAbilityWord(AbilityWord abilityWord); /** * Creates the message about the ability casting/triggering/activating to @@ -522,4 +542,12 @@ public interface Ability extends Controllable, Serializable { Ability addCustomOutcome(Outcome customOutcome); Outcome getCustomOutcome(); + + /** + * For mtg's instances search, see rules example in 112.10b + * + * @param ability + * @return + */ + boolean isSameInstance(Ability ability); } diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 361a6c4ec4..c9f951defa 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -8,6 +8,7 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ManaEffect; import mage.abilities.hint.Hint; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.Card; @@ -24,6 +25,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.Targets; import mage.target.targetadjustment.TargetAdjuster; +import mage.util.CardUtil; import mage.util.GameLog; import mage.util.ThreadLocalStringBuilder; import mage.watchers.Watcher; @@ -33,6 +35,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.UUID; +import mage.abilities.costs.common.TapSourceCost; /** * @author BetaSteward_at_googlemail.com @@ -52,7 +55,7 @@ public abstract class AbilityImpl implements Ability { protected ManaCosts manaCostsToPay; protected Costs costs; protected Costs optionalCosts; - protected Modes modes; // access to it by GetModes only (it's can be override by some abilities) + protected Modes modes; // access to it by GetModes only (it can be overridden by some abilities) protected Zone zone; protected String name; protected AbilityWord abilityWord; @@ -63,7 +66,7 @@ public abstract class AbilityImpl implements Ability { protected boolean activated = false; protected boolean worksFaceDown = false; protected int sourceObjectZoneChangeCounter; - protected List watchers = new ArrayList<>(); // access to it by GetWatchers only (it's can be override by some abilities) + protected List watchers = new ArrayList<>(); // access to it by GetWatchers only (it can be overridden by some abilities) protected List subAbilities = null; protected boolean canFizzle = true; protected TargetAdjuster targetAdjuster = null; @@ -152,6 +155,9 @@ public abstract class AbilityImpl implements Ability { boolean result = true; //20100716 - 117.12 if (checkIfClause(game)) { + // Ability has started resolving. Fire event. + // Used for abilities counting the number of resolutions like Ashling the Pilgrim. + game.fireEvent(new GameEvent(GameEvent.EventType.RESOLVING_ABILITY, this.getOriginalId(), this.getSourceId(), this.getControllerId())); if (this instanceof TriggeredAbility) { for (UUID modeId : this.getModes().getSelectedModes()) { this.getModes().setActiveMode(modeId); @@ -167,6 +173,9 @@ public abstract class AbilityImpl implements Ability { private boolean resolveMode(Game game) { boolean result = true; for (Effect effect : getEffects()) { + if (game.inCheckPlayableState() && !(effect instanceof ManaEffect)) { + continue; // Ignored non mana effects - see GameEvent.TAPPED_FOR_MANA + } if (effect instanceof OneShotEffect) { boolean effectResult = effect.apply(game, this); result &= effectResult; @@ -325,8 +334,9 @@ public abstract class AbilityImpl implements Ability { } if (!getTargets().isEmpty()) { Outcome outcome = getEffects().getOutcome(this); - // only activated abilities can be canceled by user (not triggered) - if (!getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game, this instanceof ActivatedAbility)) { + // only activated abilities can be canceled by human user (not triggered) + boolean canCancel = this instanceof ActivatedAbility && controller.isHuman(); + if (!getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game, canCancel)) { // was canceled during targer selection return false; } @@ -349,8 +359,15 @@ public abstract class AbilityImpl implements Ability { return false; } + // fused spell contains 3 abilities (fused, left, right) + // fused cost added to fused ability, so no need cost modification for other parts + boolean needCostModification = true; + if (CardUtil.isFusedPartAbility(this, game)) { + needCostModification = false; + } + //20101001 - 601.2e - if (sourceObject != null) { + if (needCostModification && sourceObject != null) { sourceObject.adjustCosts(this, game); // still needed game.getContinuousEffects().costModification(this, game); } @@ -414,36 +431,28 @@ public abstract class AbilityImpl implements Ability { } } - boolean alternativeCostisUsed = false; + boolean alternativeCostUsed = false; if (sourceObject != null && !(sourceObject instanceof Permanent)) { - Abilities abilities = null; - if (sourceObject instanceof Card) { - abilities = ((Card) sourceObject).getAbilities(game); - } else { - sourceObject.getAbilities(); - } - - if (abilities != null) { - for (Ability ability : abilities) { - // if cast for noMana no Alternative costs are allowed - if (canUseAlternativeCost && !noMana && ability instanceof AlternativeSourceCosts) { - AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability; - if (alternativeSpellCosts.isAvailable(this, game)) { - if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) { - // only one alternative costs may be activated - alternativeCostisUsed = true; - break; - } + Abilities abilities = CardUtil.getAbilities(sourceObject, game); + for (Ability ability : abilities) { + // if cast for noMana no Alternative costs are allowed + if (canUseAlternativeCost && !noMana && ability instanceof AlternativeSourceCosts) { + AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability; + if (alternativeSpellCosts.isAvailable(this, game)) { + if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) { + // only one alternative costs may be activated + alternativeCostUsed = true; + break; } } - if (canUseAdditionalCost && ability instanceof OptionalAdditionalSourceCosts) { - ((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game); - } + } + if (canUseAdditionalCost && ability instanceof OptionalAdditionalSourceCosts) { + ((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game); } } // controller specific alternate spell costs - if (canUseAlternativeCost && !noMana && !alternativeCostisUsed) { + if (canUseAlternativeCost && !noMana && !alternativeCostUsed) { if (this.getAbilityType() == AbilityType.SPELL // 117.9a Only one alternative cost can be applied to any one spell as it's being cast. // So an alternate spell ability can't be paid with Omniscience @@ -452,7 +461,7 @@ public abstract class AbilityImpl implements Ability { if (alternativeSourceCosts.isAvailable(this, game)) { if (alternativeSourceCosts.askToActivateAlternativeCosts(this, game)) { // only one alternative costs may be activated - alternativeCostisUsed = true; + alternativeCostUsed = true; break; } } @@ -461,7 +470,7 @@ public abstract class AbilityImpl implements Ability { } } - return alternativeCostisUsed; + return alternativeCostUsed; } /** @@ -706,7 +715,6 @@ public abstract class AbilityImpl implements Ability { @Override public void addWatcher(Watcher watcher) { - watcher.setSourceId(this.sourceId); watcher.setControllerId(this.controllerId); getWatchers().add(watcher); @@ -766,7 +774,7 @@ public abstract class AbilityImpl implements Ability { if (ruleStart.length() > 1) { String end = ruleStart.substring(ruleStart.length() - 2).trim(); if (end.isEmpty() || end.equals(":") || end.equals(".")) { - rule = ruleStart + Character.toUpperCase(text.charAt(0)) + text.substring(1); + rule = ruleStart + CardUtil.getTextWithFirstCharUpperCase(text); } else { rule = ruleStart + text; } @@ -844,6 +852,18 @@ public abstract class AbilityImpl implements Ability { return new Targets(); } + @Override + public Targets getAllSelectedTargets() { + Targets res = new Targets(); + for (UUID modeId : this.getModes().getSelectedModes()) { + Mode mode = this.getModes().get(modeId); + if (mode != null) { + res.addAll(mode.getTargets()); + } + } + return res; + } + @Override public UUID getFirstTarget() { return getTargets().getFirstTarget(); @@ -935,6 +955,10 @@ public abstract class AbilityImpl implements Ability { @Override public boolean hasSourceObjectAbility(Game game, MageObject source, GameEvent event) { + // if source object have this ability + // uses for ability.isInUseableZone + // replacement and other continues effects can be without source, but active (must return true) + MageObject object = source; // for singleton abilities like Flying we can't rely on abilities' source because it's only once in continuous effects // so will use the sourceId of the object itself that came as a parameter if it is not null @@ -946,16 +970,10 @@ public abstract class AbilityImpl implements Ability { } if (object != null) { if (object instanceof Permanent) { - if (!((Permanent) object).getAbilities(game).contains(this)) { - return false; - } - return ((Permanent) object).isPhasedIn(); - } else if (object instanceof Card) { - return ((Card) object).getAbilities(game).contains(this); - } else if (!object.getAbilities().contains(this)) { // not sure which object it can still be - // check if it's an ability that is temporary gained to a card - Abilities otherAbilities = game.getState().getAllOtherAbilities(this.getSourceId()); - return otherAbilities != null && otherAbilities.contains(this); + return object.hasAbility(this, game) && ((Permanent) object).isPhasedIn(); + } else { + // cards and other objects + return object.hasAbility(this, game); } } return true; @@ -1005,8 +1023,9 @@ public abstract class AbilityImpl implements Ability { } @Override - public void setAbilityWord(AbilityWord abilityWord) { + public Ability setAbilityWord(AbilityWord abilityWord) { this.abilityWord = abilityWord; + return this; } @Override @@ -1115,6 +1134,7 @@ public abstract class AbilityImpl implements Ability { String usedVerb = null; for (Target target : targets) { if (!target.getTargets().isEmpty()) { + String targetHintInfo = target.getChooseHint() == null ? "" : " (" + target.getChooseHint() + ")"; if (!target.isNotTarget()) { if (usedVerb == null || usedVerb.equals(" choosing ")) { usedVerb = " targeting "; @@ -1125,6 +1145,7 @@ public abstract class AbilityImpl implements Ability { sb.append(usedVerb); } sb.append(target.getTargetedName(game)); + sb.append(targetHintInfo); } } } @@ -1223,6 +1244,17 @@ public abstract class AbilityImpl implements Ability { } } + /** + * Dynamic cost modification for ability.
+ * Example: if it need stack related info (like real targets) then must + * check two states (game.inCheckPlayableState):
+ * 1. In playable state it must check all possible use cases (e.g. allow to + * reduce on any available target and modes)
+ * 2. In real cast state it must check current use case (e.g. real selected + * targets and modes) + * + * @param costAdjuster + */ @Override public void setCostAdjuster(CostAdjuster costAdjuster) { this.costAdjuster = costAdjuster; @@ -1261,4 +1293,17 @@ public abstract class AbilityImpl implements Ability { public Outcome getCustomOutcome() { return this.customOutcome; } + + @Override + public boolean isSameInstance(Ability ability) { + // same instance (by mtg rules) = same object, ID or class+text (you can't check class only cause it can be different by params/text) + if (ability == null) { + return false; + } + + return (this == ability) + || (this.getId().equals(ability.getId())) + || (this.getOriginalId().equals(ability.getOriginalId())) + || (this.getClass() == ability.getClass() && this.getRule(true).equals(ability.getRule(true))); + } } diff --git a/Mage/src/main/java/mage/abilities/ActivatedAbility.java b/Mage/src/main/java/mage/abilities/ActivatedAbility.java index 4601964969..95e97d2bfd 100644 --- a/Mage/src/main/java/mage/abilities/ActivatedAbility.java +++ b/Mage/src/main/java/mage/abilities/ActivatedAbility.java @@ -1,18 +1,19 @@ package mage.abilities; -import java.util.UUID; +import mage.MageObject; import mage.MageObjectReference; import mage.abilities.mana.ManaOptions; import mage.constants.TargetController; import mage.game.Game; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public interface ActivatedAbility extends Ability { - final public class ActivationStatus { + final class ActivationStatus { private final boolean canActivate; private final MageObjectReference permittingObject; @@ -34,8 +35,13 @@ public interface ActivatedAbility extends Ability { return new ActivationStatus(false, null); } - public static ActivationStatus getTrue() { - return new ActivationStatus(true, null); + /** + * @param permittingObjectAbility card or permanent that allows to activate current ability + */ + public static ActivationStatus getTrue(Ability permittingObjectAbility, Game game) { + MageObject object = permittingObjectAbility == null ? null : permittingObjectAbility.getSourceObject(game); + MageObjectReference ref = object == null ? null : new MageObjectReference(object, game); + return new ActivationStatus(true, ref); } } diff --git a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java index d54e2f27eb..4f98229b5b 100644 --- a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java @@ -1,6 +1,5 @@ package mage.abilities; -import java.util.UUID; import mage.MageObject; import mage.MageObjectReference; import mage.abilities.condition.Condition; @@ -12,11 +11,7 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; import mage.abilities.mana.ManaOptions; import mage.cards.Card; -import mage.constants.AbilityType; -import mage.constants.AsThoughEffectType; -import mage.constants.TargetController; -import mage.constants.TimingRule; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.command.Commander; import mage.game.command.Emblem; @@ -24,8 +19,9 @@ import mage.game.command.Plane; import mage.game.permanent.Permanent; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public abstract class ActivatedAbilityImpl extends AbilityImpl implements ActivatedAbility { @@ -148,6 +144,34 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa @Override public abstract ActivatedAbilityImpl copy(); + protected boolean checkTargetController(UUID playerId, Game game) { + switch (mayActivate) { + case ANY: + return true; + case ACTIVE: + return game.getActivePlayerId() == playerId; + case NOT_YOU: + return !controlsAbility(playerId, game); + case TEAM: + return !game.getPlayer(controllerId).hasOpponent(playerId, game); + case OPPONENT: + return game.getPlayer(controllerId).hasOpponent(playerId, game); + case OWNER: + Permanent permanent = game.getPermanent(getSourceId()); + return permanent != null && permanent.isOwnedBy(playerId); + case YOU: + return controlsAbility(playerId, game); + case CONTROLLER_ATTACHED_TO: + Permanent enchantment = game.getPermanent(getSourceId()); + if (enchantment == null || enchantment.getAttachedTo() == null) { + return false; + } + Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); + return enchanted != null && enchanted.isControlledBy(playerId); + } + return true; + } + @Override public ActivationStatus canActivate(UUID playerId, Game game) { //20091005 - 602.2 @@ -156,49 +180,8 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa || condition.apply(game, this)))) { return ActivationStatus.getFalse(); } - switch (mayActivate) { - case ANY: - break; - case ACTIVE: - if (game.getActivePlayerId() != playerId) { - return ActivationStatus.getFalse(); - } - break; - case NOT_YOU: - if (controlsAbility(playerId, game)) { - return ActivationStatus.getFalse(); - } - break; - case TEAM: - if (game.getPlayer(controllerId).hasOpponent(playerId, game)) { - return ActivationStatus.getFalse(); - } - break; - case OPPONENT: - if (!game.getPlayer(controllerId).hasOpponent(playerId, game)) { - return ActivationStatus.getFalse(); - } - break; - case OWNER: - Permanent permanent = game.getPermanent(getSourceId()); - if (!permanent.isOwnedBy(playerId)) { - return ActivationStatus.getFalse(); - } - break; - case YOU: - if (!controlsAbility(playerId, game)) { - return ActivationStatus.getFalse(); - } - break; - case CONTROLLER_ATTACHED_TO: - Permanent enchantment = game.getPermanent(getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); - if (enchanted != null && enchanted.isControlledBy(playerId)) { - break; - } - } - return ActivationStatus.getFalse(); + if (!this.checkTargetController(playerId, game)) { + return ActivationStatus.getFalse(); } //20091005 - 602.5d/602.5e MageObjectReference permittingObject = game.getContinuousEffects() @@ -227,17 +210,16 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa protected boolean controlsAbility(UUID playerId, Game game) { if (this.controllerId != null && this.controllerId.equals(playerId)) { return true; - } else { - MageObject mageObject = game.getObject(this.sourceId); - if (mageObject instanceof Emblem) { - return ((Emblem) mageObject).isControlledBy(playerId); - } else if (mageObject instanceof Plane) { - return ((Plane) mageObject).isControlledBy(playerId); - } else if (mageObject instanceof Commander) { - return ((Commander) mageObject).isControlledBy(playerId); - } else if (game.getState().getZone(this.sourceId) != Zone.BATTLEFIELD) { - return ((Card) mageObject).isOwnedBy(playerId); - } + } + MageObject mageObject = game.getObject(this.sourceId); + if (mageObject instanceof Emblem) { + return ((Emblem) mageObject).isControlledBy(playerId); + } else if (mageObject instanceof Plane) { + return ((Plane) mageObject).isControlledBy(playerId); + } else if (mageObject instanceof Commander) { + return ((Commander) mageObject).isControlledBy(playerId); + } else if (game.getState().getZone(this.sourceId) != Zone.BATTLEFIELD) { + return ((Card) mageObject).isOwnedBy(playerId); } return false; } @@ -271,22 +253,20 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa @Override public boolean activate(Game game, boolean noMana) { - if (hasMoreActivationsThisTurn(game)) { - if (super.activate(game, noMana)) { - ActivationInfo activationInfo = getActivationInfo(game); - if (activationInfo == null) { - activationInfo = new ActivationInfo(game.getTurnNum(), 1); - } else if (activationInfo.turnNum != game.getTurnNum()) { - activationInfo.turnNum = game.getTurnNum(); - activationInfo.activationCounter = 1; - } else { - activationInfo.activationCounter++; - } - setActivationInfo(activationInfo, game); - return true; - } + if (!hasMoreActivationsThisTurn(game) || !super.activate(game, noMana)) { + return false; } - return false; + ActivationInfo activationInfo = getActivationInfo(game); + if (activationInfo == null) { + activationInfo = new ActivationInfo(game.getTurnNum(), 1); + } else if (activationInfo.turnNum != game.getTurnNum()) { + activationInfo.turnNum = game.getTurnNum(); + activationInfo.activationCounter = 1; + } else { + activationInfo.activationCounter++; + } + setActivationInfo(activationInfo, game); + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/ActivationInfo.java b/Mage/src/main/java/mage/abilities/ActivationInfo.java index bed1acb2fe..0ca371695b 100644 --- a/Mage/src/main/java/mage/abilities/ActivationInfo.java +++ b/Mage/src/main/java/mage/abilities/ActivationInfo.java @@ -1,62 +1,62 @@ - -package mage.abilities; - -import java.util.UUID; -import mage.game.Game; - -/** - * The ActivationInfo class holds the information how often an ability of an - * object was activated during a turn. It handles the check, if the object is - * still the same, so for example if a permanent left battlefield and returns, - * the counting of activations happens for each object. - * - * @author LevelX2 - */ -public class ActivationInfo { - - protected int turnNum = 0; - protected int activationCounter = 0; - protected String key; - - public static ActivationInfo getInstance(Game game, UUID sourceId) { - return ActivationInfo.getInstance(game, sourceId, game.getState().getZoneChangeCounter(sourceId)); - } - - public static ActivationInfo getInstance(Game game, UUID sourceId, int zoneChangeCounter) { - String key = "ActivationInfo" + sourceId.toString() + zoneChangeCounter; - Integer activations = (Integer) game.getState().getValue(key); - ActivationInfo activationInfo; - if (activations != null) { - Integer turnNum = (Integer) game.getState().getValue(key + 'T'); - activationInfo = new ActivationInfo(game, turnNum, activations); - } else { - activationInfo = new ActivationInfo(game, game.getTurnNum(), 0); - } - activationInfo.setKey(key); - return activationInfo; - } - - public void setKey(String key) { - this.key = key; - } - - protected ActivationInfo(Game game, int turnNum, int activationCounter) { - this.turnNum = turnNum; - this.activationCounter = activationCounter; - } - - public void addActivation(Game game) { - if (game.getTurnNum() != turnNum) { - activationCounter = 1; - turnNum = game.getTurnNum(); - } else { - activationCounter++; - } - game.getState().setValue(key, activationCounter); - game.getState().setValue(key + 'T', turnNum); - } - - public int getActivationCounter() { - return activationCounter; - } -} + +package mage.abilities; + +import java.util.UUID; +import mage.game.Game; + +/** + * The ActivationInfo class holds the information how often an ability of an + * object was activated during a turn. It handles the check, if the object is + * still the same, so for example if a permanent left battlefield and returns, + * the counting of activations happens for each object. + * + * @author LevelX2 + */ +public class ActivationInfo { + + protected int turnNum = 0; + protected int activationCounter = 0; + protected String key; + + public static ActivationInfo getInstance(Game game, UUID sourceId) { + return ActivationInfo.getInstance(game, sourceId, game.getState().getZoneChangeCounter(sourceId)); + } + + public static ActivationInfo getInstance(Game game, UUID sourceId, int zoneChangeCounter) { + String key = "ActivationInfo" + sourceId.toString() + zoneChangeCounter; + Integer activations = (Integer) game.getState().getValue(key); + ActivationInfo activationInfo; + if (activations != null) { + Integer turnNum = (Integer) game.getState().getValue(key + 'T'); + activationInfo = new ActivationInfo(game, turnNum, activations); + } else { + activationInfo = new ActivationInfo(game, game.getTurnNum(), 0); + } + activationInfo.setKey(key); + return activationInfo; + } + + public void setKey(String key) { + this.key = key; + } + + protected ActivationInfo(Game game, int turnNum, int activationCounter) { + this.turnNum = turnNum; + this.activationCounter = activationCounter; + } + + public void addActivation(Game game) { + if (game.getTurnNum() != turnNum) { + activationCounter = 1; + turnNum = game.getTurnNum(); + } else { + activationCounter++; + } + game.getState().setValue(key, activationCounter); + game.getState().setValue(key + 'T', turnNum); + } + + public int getActivationCounter() { + return activationCounter; + } +} diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index f05cb990d9..b06707d34c 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -23,9 +23,9 @@ public class Modes extends LinkedHashMap { public static final UUID CHOOSE_OPTION_CANCEL_ID = UUID.fromString("0125bd0c-5610-4eba-bc80-fc6d0a7b9de6"); private Mode currentMode; // the current mode of the selected modes - private final List selectedModes = new ArrayList<>(); // all selected modes (this + duplicate) - private final Map duplicateModes = new LinkedHashMap<>(); // for 2x selects: copy mode and put it to duplicate list - private final Map duplicateToOriginalModeRefs = new LinkedHashMap<>(); // for 2x selects: stores ref from duplicate to original mode + private final List selectedModes = new ArrayList<>(); // all selected modes (this + duplicate), use getSelectedModes all the time to keep modes order + private final Map selectedDuplicateModes = new LinkedHashMap<>(); // for 2x selects: copy mode and put it to duplicate list + private final Map selectedDuplicateToOriginalModeRefs = new LinkedHashMap<>(); // for 2x selects: stores ref from duplicate to original mode private int minModes; private int maxModes; @@ -52,10 +52,10 @@ public class Modes extends LinkedHashMap { for (Map.Entry entry : modes.entrySet()) { this.put(entry.getKey(), entry.getValue().copy()); } - for (Map.Entry entry : modes.duplicateModes.entrySet()) { - duplicateModes.put(entry.getKey(), entry.getValue().copy()); + for (Map.Entry entry : modes.selectedDuplicateModes.entrySet()) { + selectedDuplicateModes.put(entry.getKey(), entry.getValue().copy()); } - duplicateToOriginalModeRefs.putAll(modes.duplicateToOriginalModeRefs); + selectedDuplicateToOriginalModeRefs.putAll(modes.selectedDuplicateToOriginalModeRefs); this.minModes = modes.minModes; this.maxModes = modes.maxModes; @@ -72,7 +72,7 @@ public class Modes extends LinkedHashMap { if (modes.getSelectedModes().isEmpty()) { this.currentMode = values().iterator().next(); } else { - this.currentMode = get(modes.getMode().getId()); + this.currentMode = get(modes.getMode().getId()); // need fix? } } @@ -84,7 +84,7 @@ public class Modes extends LinkedHashMap { public Mode get(Object key) { Mode modeToGet = super.get(key); if (modeToGet == null && eachModeMoreThanOnce) { - modeToGet = duplicateModes.get(key); + modeToGet = selectedDuplicateModes.get(key); } return modeToGet; } @@ -103,7 +103,7 @@ public class Modes extends LinkedHashMap { public UUID getModeId(int index) { int idx = 0; if (eachModeMoreThanOnce) { - for (UUID modeId : this.selectedModes) { + for (UUID modeId : this.getSelectedModes()) { idx++; if (idx == index) { return modeId; @@ -121,7 +121,25 @@ public class Modes extends LinkedHashMap { } public List getSelectedModes() { - return selectedModes; + // sorted as original modes + List res = new ArrayList<>(); + for (Mode mode : this.values()) { + for (UUID selectedId : this.selectedModes) { + // selectedModes contains original mode and 2+ selected as duplicates (new modes) + UUID selectedOriginalId = this.selectedDuplicateToOriginalModeRefs.get(selectedId); + if (Objects.equals(mode.getId(), selectedId) + || Objects.equals(mode.getId(), selectedOriginalId)) { + res.add(selectedId); + } + } + } + return res; + } + + public void clearSelectedModes() { + this.selectedModes.clear(); + this.selectedDuplicateModes.clear(); + this.selectedDuplicateToOriginalModeRefs.clear(); } public int getSelectedStats(UUID modeId) { @@ -133,14 +151,14 @@ public class Modes extends LinkedHashMap { // multiple select (all 2x select generate new duplicate mode) UUID originalId; - if (this.duplicateModes.containsKey(modeId)) { + if (this.selectedDuplicateModes.containsKey(modeId)) { // modeId is duplicate - originalId = this.duplicateToOriginalModeRefs.get(modeId); + originalId = this.selectedDuplicateToOriginalModeRefs.get(modeId); } else { // modeId is original originalId = modeId; } - for (UUID id : this.duplicateToOriginalModeRefs.values()) { + for (UUID id : this.selectedDuplicateToOriginalModeRefs.values()) { if (id.equals(originalId)) { count++; } @@ -183,9 +201,7 @@ public class Modes extends LinkedHashMap { } public void setActiveMode(Mode mode) { - if (selectedModes.contains(mode.getId())) { - this.currentMode = mode; - } + setActiveMode(mode.getId()); } public void setActiveMode(UUID modeId) { @@ -200,9 +216,7 @@ public class Modes extends LinkedHashMap { public boolean choose(Game game, Ability source) { if (this.size() > 1) { - this.selectedModes.clear(); - this.duplicateModes.clear(); - this.duplicateToOriginalModeRefs.clear(); + this.clearSelectedModes(); if (this.isRandom) { List modes = getAvailableModes(source, game); this.addSelectedMode(modes.get(RandomUtil.nextInt(modes.size())).getId()); @@ -230,7 +244,7 @@ public class Modes extends LinkedHashMap { } } if (isEachModeOnlyOnce()) { - setAlreadySelectedModes(selectedModes, source, game); + setAlreadySelectedModes(source, game); } return !selectedModes.isEmpty(); } @@ -274,7 +288,7 @@ public class Modes extends LinkedHashMap { Mode choice = player.chooseMode(this, source, game); if (choice == null) { if (isEachModeOnlyOnce()) { - setAlreadySelectedModes(selectedModes, source, game); + setAlreadySelectedModes(source, game); } return this.selectedModes.size() >= this.getMinModes(); } @@ -284,12 +298,13 @@ public class Modes extends LinkedHashMap { } } if (isEachModeOnlyOnce()) { - setAlreadySelectedModes(selectedModes, source, game); + setAlreadySelectedModes(source, game); } return true; - } else { // only one mode + } else { + // only one mode available if (currentMode == null) { - this.selectedModes.clear(); + this.clearSelectedModes(); Mode mode = this.values().iterator().next(); this.addSelectedMode(mode.getId()); this.setActiveMode(mode); @@ -301,12 +316,11 @@ public class Modes extends LinkedHashMap { /** * Saves the already selected modes to the state value * - * @param selectedModes * @param source * @param game */ - private void setAlreadySelectedModes(List selectedModes, Ability source, Game game) { - for (UUID modeId : selectedModes) { + private void setAlreadySelectedModes(Ability source, Game game) { + for (UUID modeId : getSelectedModes()) { String key = getKey(source, game, modeId); game.getState().setValue(key, true); } @@ -318,19 +332,29 @@ public class Modes extends LinkedHashMap { * * @param modeId */ - private void addSelectedMode(UUID modeId) { + public void addSelectedMode(UUID modeId) { + if (!this.containsKey(modeId)) { + throw new IllegalArgumentException("Unknown modeId to select"); + } + if (selectedModes.contains(modeId) && eachModeMoreThanOnce) { Mode duplicateMode = get(modeId).copy(); UUID originalId = modeId; duplicateMode.setRandomId(); modeId = duplicateMode.getId(); - duplicateModes.put(modeId, duplicateMode); - duplicateToOriginalModeRefs.put(duplicateMode.getId(), originalId); + selectedDuplicateModes.put(modeId, duplicateMode); + selectedDuplicateToOriginalModeRefs.put(duplicateMode.getId(), originalId); } this.selectedModes.add(modeId); } + public void removeSelectedMode(UUID modeId) { + this.selectedModes.remove(modeId); + this.selectedDuplicateModes.remove(modeId); + this.selectedDuplicateToOriginalModeRefs.remove(modeId); + } + // The already once selected modes for a modal card are stored as a state value // That's important for modal abilities with modes that can only selected once while the object stays in its zone @SuppressWarnings("unchecked") diff --git a/Mage/src/main/java/mage/abilities/SpecialAction.java b/Mage/src/main/java/mage/abilities/SpecialAction.java index b4cc07c731..fd0a85092f 100644 --- a/Mage/src/main/java/mage/abilities/SpecialAction.java +++ b/Mage/src/main/java/mage/abilities/SpecialAction.java @@ -1,18 +1,22 @@ - - package mage.abilities; +import mage.abilities.costs.mana.AlternateManaPaymentAbility; import mage.abilities.costs.mana.ManaCost; +import mage.abilities.mana.ManaOptions; import mage.constants.AbilityType; import mage.constants.Zone; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; + +import java.util.UUID; /** - * - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ public abstract class SpecialAction extends ActivatedAbilityImpl { - private boolean manaAction; + private final AlternateManaPaymentAbility manaAbility; // mana actions generates on every pay cycle, no need to copy it protected ManaCost unpaidMana; public SpecialAction() { @@ -20,22 +24,23 @@ public abstract class SpecialAction extends ActivatedAbilityImpl { } public SpecialAction(Zone zone) { - this(zone, false); + this(zone, null); } - public SpecialAction(Zone zone, boolean manaAction) { + + public SpecialAction(Zone zone, AlternateManaPaymentAbility manaAbility) { super(AbilityType.SPECIAL_ACTION, zone); this.usesStack = false; - this.manaAction = manaAction; + this.manaAbility = manaAbility; } public SpecialAction(final SpecialAction action) { super(action); - this.manaAction = action.manaAction; this.unpaidMana = action.unpaidMana; + this.manaAbility = action.manaAbility; } public boolean isManaAction() { - return manaAction; + return manaAbility != null; } public void setUnpaidMana(ManaCost manaCost) { @@ -45,4 +50,29 @@ public abstract class SpecialAction extends ActivatedAbilityImpl { public ManaCost getUnpaidMana() { return unpaidMana; } + + public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) { + if (manaAbility != null) { + return manaAbility.getManaOptions(source, game, unpaid); + } + return null; + } + + @Override + public ActivationStatus canActivate(UUID playerId, Game game) { + if (isManaAction()) { + // limit play mana abilities by steps + int currentStepOrder = 0; + if (!game.getStack().isEmpty()) { + StackObject stackObject = game.getStack().getFirst(); + if (stackObject instanceof Spell) { + currentStepOrder = ((Spell) stackObject).getCurrentActivatingManaAbilitiesStep().getStepOrder(); + } + } + if (currentStepOrder > manaAbility.useOnActivationManaAbilityStep().getStepOrder()) { + return ActivationStatus.getFalse(); + } + } + return super.canActivate(playerId, game); + } } diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java index 9bd506f34a..d76b52e478 100644 --- a/Mage/src/main/java/mage/abilities/SpellAbility.java +++ b/Mage/src/main/java/mage/abilities/SpellAbility.java @@ -1,7 +1,5 @@ package mage.abilities; -import java.util.Optional; -import java.util.UUID; import mage.MageObject; import mage.MageObjectReference; import mage.abilities.costs.Cost; @@ -14,9 +12,12 @@ import mage.cards.SplitCard; import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.stack.Spell; import mage.players.Player; +import java.util.Optional; +import java.util.UUID; +import javax.naming.directory.InvalidAttributesException; + /** * @author BetaSteward_at_googlemail.com */ @@ -70,7 +71,7 @@ public class SpellAbility extends ActivatedAbilityImpl { } return null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase || timing == TimingRule.INSTANT - || object.hasAbility(FlashAbility.getInstance().getId(), game) + || object.hasAbility(FlashAbility.getInstance(), game) || game.canPlaySorcery(playerId); } @@ -104,12 +105,16 @@ public class SpellAbility extends ActivatedAbilityImpl { return ActivationStatus.getFalse(); } } - if (costs.canPay(this, sourceId, controllerId, game)) { + if (costs.canPay(this, sourceId, playerId, game)) { if (getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { SplitCard splitCard = (SplitCard) game.getCard(getSourceId()); if (splitCard != null) { - return new ActivationStatus(splitCard.getLeftHalfCard().getSpellAbility().canChooseTarget(game) - && splitCard.getRightHalfCard().getSpellAbility().canChooseTarget(game), null); + // fused can be called from hand only, so not permitting object allows or other zones checks + // see https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/251926-snapcaster-mage-and-fuse + if (game.getState().getZone(splitCard.getId()) == Zone.HAND) { + return new ActivationStatus(splitCard.getLeftHalfCard().getSpellAbility().canChooseTarget(game) + && splitCard.getRightHalfCard().getSpellAbility().canChooseTarget(game), null); + } } return ActivationStatus.getFalse(); @@ -140,9 +145,13 @@ public class SpellAbility extends ActivatedAbilityImpl { this.costs.clearPaid(); } + public String getName() { + return this.name; + } + @Override public String toString() { - return this.name; + return getName(); } @Override @@ -227,12 +236,25 @@ public class SpellAbility extends ActivatedAbilityImpl { return this; } + /** + * Returns a card object with the spell characteristics like calor, types, + * subtypes etc. E.g. if you cast a Bestow card as enchantment, the + * characteristics don't include the creature type. + * + * @param game + * @return card object with the spell characteristics + */ public Card getCharacteristics(Game game) { - Spell spell = game.getSpell(this.getId()); - if (spell != null) { - return spell; + Card spellCharacteristics = game.getSpell(this.getId()); + if (spellCharacteristics == null) { + spellCharacteristics = game.getCard(this.getSourceId()); } - return game.getCard(this.getSourceId()); + if (spellCharacteristics != null) { + if (getSpellAbilityCastMode() != SpellAbilityCastMode.NORMAL) { + spellCharacteristics = getSpellAbilityCastMode().getTypeModifiedCardObjectCopy(spellCharacteristics, game); + } + } + return spellCharacteristics; } public static SpellAbility getSpellAbilityFromEvent(GameEvent event, Game game) { diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index e037461998..80a26c2f80 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -6,7 +6,6 @@ import mage.constants.AbilityType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.players.Player; import mage.util.CardUtil; @@ -171,21 +170,24 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge } } } + if (isLeavesTheBattlefieldTrigger()) { + source = zce.getTarget(); + } + break; case DESTROYED_PERMANENT: if (isLeavesTheBattlefieldTrigger()) { - if (event.getType() == EventType.DESTROYED_PERMANENT) { - source = game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD); - } else if (((ZoneChangeEvent) event).getTarget() != null) { - source = ((ZoneChangeEvent) event).getTarget(); - } else { - source = game.getLastKnownInformation(getSourceId(), event.getZone()); - } + source = game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD); } + break; case PHASED_OUT: case PHASED_IN: + if (isLeavesTheBattlefieldTrigger()) { + source = game.getLastKnownInformation(getSourceId(), event.getZone()); + } if (this.zone == Zone.ALL || game.getLastKnownInformation(getSourceId(), zone) != null) { return this.hasSourceObjectAbility(game, source, event); } + break; } return super.isInUseableZone(game, source, event); } diff --git a/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java b/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java index 5af021988b..1c5a4b23f5 100644 --- a/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java +++ b/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java @@ -53,7 +53,7 @@ public class ConstellationAbility extends TriggeredAbilityImpl { return false; } Permanent permanent = game.getPermanent(event.getTargetId()); - return permanent != null && permanent.isEnchantment(); + return permanent != null && ((thisOr && permanent.getId().equals(getSourceId())) || permanent.isEnchantment()); } @Override diff --git a/Mage/src/main/java/mage/abilities/abilityword/StriveAbility.java b/Mage/src/main/java/mage/abilities/abilityword/StriveAbility.java index 389e2cee0e..48d4d112c8 100644 --- a/Mage/src/main/java/mage/abilities/abilityword/StriveAbility.java +++ b/Mage/src/main/java/mage/abilities/abilityword/StriveAbility.java @@ -1,5 +1,3 @@ - - package mage.abilities.abilityword; import mage.abilities.Ability; @@ -16,14 +14,13 @@ import mage.target.Target; import mage.util.ManaUtil; /** - * * @author LevelX2 */ public class StriveAbility extends SimpleStaticAbility { private final String striveCost; - + public StriveAbility(String manaString) { super(Zone.STACK, new StriveCostIncreasingEffect(new ManaCostsImpl(manaString))); setRuleAtTheTop(true); @@ -63,7 +60,7 @@ class StriveCostIncreasingEffect extends CostModificationEffectImpl { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { for (Target target : abilityToModify.getTargets()) { - if (target.getMaxNumberOfTargets() == Integer.MAX_VALUE) { + if (target.getMaxNumberOfTargets() == Integer.MAX_VALUE) { // strive works with "any number of target" only int additionalTargets = target.getTargets().size() - 1; StringBuilder sb = new StringBuilder(); for (int i = 0; i < additionalTargets; i++) { diff --git a/Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java new file mode 100644 index 0000000000..ba4bd5c48b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java @@ -0,0 +1,61 @@ +package mage.abilities.common; + +import mage.abilities.LoyaltyAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackAbility; + +public class ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility extends TriggeredAbilityImpl { + + private final SubType planeswalkerSubType; + + public ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility(Effect effect, SubType planeswalkerSubType) { + super(Zone.BATTLEFIELD, effect, false); + this.planeswalkerSubType = planeswalkerSubType; + } + + private ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility(final ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility ability) { + super(ability); + this.planeswalkerSubType = ability.planeswalkerSubType; + } + + @Override + public ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility copy() { + return new ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getPlayerId().equals(getControllerId())) { + return false; + } + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility == null || !(stackAbility.getStackAbility() instanceof LoyaltyAbility)) { + return false; + } + Permanent permanent = stackAbility.getSourcePermanentOrLKI(game); + if (permanent == null || !permanent.isPlaneswalker() + || !permanent.hasSubtype(planeswalkerSubType, game)) { + return false; + } + Effect effect = this.getEffects().get(0); + effect.setValue("stackAbility", stackAbility); + return true; + } + + @Override + public String getRule() { + return "Whenever you activate a loyalty ability of a " + planeswalkerSubType.getDescription() + " planeswalker, " + + this.getEffects().get(0).getText(getModes().getMode()) + "."; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/AttachedToCreatureSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttachedToCreatureSourceTriggeredAbility.java new file mode 100644 index 0000000000..1387a45d9c --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/AttachedToCreatureSourceTriggeredAbility.java @@ -0,0 +1,48 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import static mage.constants.CardType.CREATURE; + +/** + * + * @author htrajan + */ +public class AttachedToCreatureSourceTriggeredAbility extends TriggeredAbilityImpl { + + public AttachedToCreatureSourceTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + public AttachedToCreatureSourceTriggeredAbility(final AttachedToCreatureSourceTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ATTACHED + && event.getSourceId() != null + && event.getSourceId().equals(this.getSourceId()); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent attachedPermanent = game.getPermanent(event.getTargetId()); + return attachedPermanent != null && attachedPermanent.getCardType().contains(CREATURE); + } + + @Override + public String getRule() { + return "As {this} becomes attached to a creature, " + super.getRule(); + } + + @Override + public AttachedToCreatureSourceTriggeredAbility copy() { + return new AttachedToCreatureSourceTriggeredAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java index c290a39cc2..b5991cf2e4 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java @@ -77,7 +77,7 @@ public class AttacksAllTriggeredAbility extends TriggeredAbilityImpl { switch (setTargetPointer) { case PERMANENT: for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); } break; case PLAYER: diff --git a/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java index 5dc5cec55c..6f67313da9 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java @@ -76,12 +76,12 @@ public class AttacksCreatureYouControlTriggeredAbility extends TriggeredAbilityI public String getRule() { String an; String who = filter.getMessage(); - if (who.startsWith("another")) { + if (who.startsWith("another") || who.startsWith("a ")) { an = ""; - } else if (who.startsWith("a")) { - an = "an"; + } else if (who.length() > 0 && "aeiou".contains(who.charAt(0) + "")) { + an = "an "; } else { - an = "a"; + an = "a "; } return "When" + (once ? "" : "ever") diff --git a/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java index bc4d9626b7..0045bb8292 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java @@ -45,7 +45,9 @@ public class BecomesBlockedByCreatureTriggeredAbility extends TriggeredAbilityIm if (!filter.match(blocker, game)) { return false; } - this.getEffects().setTargetPointer(new FixedTarget(blocker, game)); + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getSourceId())); + } return true; } diff --git a/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java new file mode 100644 index 0000000000..8fc710f1f5 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java @@ -0,0 +1,43 @@ + +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author North + */ +public class BecomesBlockedSourceTriggeredAbility extends TriggeredAbilityImpl { + + public BecomesBlockedSourceTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + public BecomesBlockedSourceTriggeredAbility(final BecomesBlockedSourceTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CREATURE_BLOCKED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getTargetId().equals(this.getSourceId()); + } + + @Override + public String getRule() { + return "Whenever {this} becomes blocked, " + super.getRule(); + } + + @Override + public BecomesBlockedSourceTriggeredAbility copy() { + return new BecomesBlockedSourceTriggeredAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesMonstrousTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesMonstrousTriggeredAbility.java index 70eeed7514..02f922b6c4 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesMonstrousTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesMonstrousTriggeredAbility.java @@ -1,55 +1,55 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author Styxo - */ -public class BecomesMonstrousTriggeredAbility extends TriggeredAbilityImpl { - - public BecomesMonstrousTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, false); - } - - public BecomesMonstrousTriggeredAbility(final BecomesMonstrousTriggeredAbility ability) { - super(ability); - } - - @Override - public BecomesMonstrousTriggeredAbility copy() { - return new BecomesMonstrousTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.BECOMES_MONSTROUS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature() - && (permanent.isControlledBy(getControllerId()))) { - this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature you control becomes monstrous, " + super.getRule(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author Styxo + */ +public class BecomesMonstrousTriggeredAbility extends TriggeredAbilityImpl { + + public BecomesMonstrousTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect, false); + } + + public BecomesMonstrousTriggeredAbility(final BecomesMonstrousTriggeredAbility ability) { + super(ability); + } + + @Override + public BecomesMonstrousTriggeredAbility copy() { + return new BecomesMonstrousTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.BECOMES_MONSTROUS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.isCreature() + && (permanent.isControlledBy(getControllerId()))) { + this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever a creature you control becomes monstrous, " + super.getRule(); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java index 1833985830..7c945d0a19 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java @@ -28,7 +28,11 @@ public class BecomesTargetTriggeredAbility extends TriggeredAbilityImpl { } public BecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer) { - super(Zone.BATTLEFIELD, effect); + this(effect, filter, setTargetPointer, false); + } + + public BecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); this.filter = filter.copy(); this.setTargetPointer = setTargetPointer; } diff --git a/Mage/src/main/java/mage/abilities/common/BeginningOfEndStepTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BeginningOfEndStepTriggeredAbility.java index 3ece2bd954..4e96afcf76 100644 --- a/Mage/src/main/java/mage/abilities/common/BeginningOfEndStepTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BeginningOfEndStepTriggeredAbility.java @@ -103,6 +103,7 @@ public class BeginningOfEndStepTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { StringBuilder sb = new StringBuilder(getEffects().getText(modes.getMode())); + if (this.optional) { if (sb.substring(0, 6).toLowerCase(Locale.ENGLISH).equals("target")) { sb.insert(0, "you may have "); @@ -114,6 +115,7 @@ public class BeginningOfEndStepTriggeredAbility extends TriggeredAbilityImpl { if (abilityWord != null) { abilityWordRule = "" + abilityWord.toString() + " &mdash "; } + switch (targetController) { case YOU: return sb.insert(0, generateConditionString()).insert(0, abilityWordRule + "At the beginning of your end step, ").toString(); @@ -134,6 +136,13 @@ public class BeginningOfEndStepTriggeredAbility extends TriggeredAbilityImpl { private String generateConditionString() { if (interveningIfClauseCondition != null) { if (interveningIfClauseCondition.toString().startsWith("if")) { + + //Fixes punctuation on multiple sentence if-then construction + // see -- Colfenor's Urn + if (interveningIfClauseCondition.toString().endsWith(".")){ + return interveningIfClauseCondition.toString() + " "; + } + return interveningIfClauseCondition.toString() + ", "; } else { return "if {this} is " + interveningIfClauseCondition.toString() + ", "; diff --git a/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java index 7969e308b5..021787292e 100644 --- a/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -137,7 +136,7 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { case OPPONENT: return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each opponent's upkeep, ").toString(); case ANY: - return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each upkeep, ").toString(); + return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each player's upkeep, ").toString(); case ACTIVE: return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each player's upkeep, ").toString(); case CONTROLLER_ATTACHED_TO: diff --git a/Mage/src/main/java/mage/abilities/common/BeginningOfYourEndStepTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BeginningOfYourEndStepTriggeredAbility.java index eb221d6ebb..04854e58b0 100644 --- a/Mage/src/main/java/mage/abilities/common/BeginningOfYourEndStepTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BeginningOfYourEndStepTriggeredAbility.java @@ -15,6 +15,10 @@ public class BeginningOfYourEndStepTriggeredAbility extends TriggeredAbilityImpl super(Zone.BATTLEFIELD, effect, optional); } + public BeginningOfYourEndStepTriggeredAbility(Zone zone, Effect effect, boolean optional) { + super(zone, effect, optional); + } + public BeginningOfYourEndStepTriggeredAbility(final BeginningOfYourEndStepTriggeredAbility ability) { super(ability); } diff --git a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java new file mode 100644 index 0000000000..3863d19045 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java @@ -0,0 +1,91 @@ + +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author North, Loki + */ +public class BlocksOrBecomesBlockedSourceTriggeredAbility extends TriggeredAbilityImpl { + + protected FilterPermanent filter; + protected String rule; + protected boolean setTargetPointer; + + public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, boolean optional) { + this(effect, StaticFilters.FILTER_PERMANENT_CREATURE, optional, null, true); + } + + public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) { + this(effect, filter, optional, null, true); + } + + public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, String rule) { + this(effect, filter, optional, rule, true); + } + + public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, String rule, boolean setTargetPointer) { + super(Zone.BATTLEFIELD, effect, optional); + this.filter = filter; + this.rule = rule; + this.setTargetPointer = setTargetPointer; + } + + public BlocksOrBecomesBlockedSourceTriggeredAbility(final BlocksOrBecomesBlockedSourceTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + this.rule = ability.rule; + this.setTargetPointer = ability.setTargetPointer; + + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.BLOCKER_DECLARED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getSourceId().equals(this.getSourceId())) { + Permanent blocked = game.getPermanent(event.getTargetId()); + if (blocked != null && filter.match(blocked, game)) { + if (setTargetPointer) { + this.getEffects().setTargetPointer(new FixedTarget(blocked, game)); + } + return true; + } + } + if (event.getTargetId().equals(this.getSourceId())) { + Permanent blocker = game.getPermanent(event.getSourceId()); + if (blocker != null && filter.match(blocker, game)) { + if (setTargetPointer) { + this.getEffects().setTargetPointer(new FixedTarget(blocker, game)); + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + if (rule != null) { + return rule; + } + return "Whenever {this} blocks or becomes blocked" + (setTargetPointer ? " by a " + filter.getMessage() : "") + ", " + super.getRule(); + } + + @Override + public BlocksOrBecomesBlockedSourceTriggeredAbility copy() { + return new BlocksOrBecomesBlockedSourceTriggeredAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/CantBeCounteredSourceAbility.java b/Mage/src/main/java/mage/abilities/common/CantBeCounteredSourceAbility.java new file mode 100644 index 0000000000..1d1fdaf76b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/CantBeCounteredSourceAbility.java @@ -0,0 +1,32 @@ + + +package mage.abilities.common; + +import mage.abilities.StaticAbility; +import mage.abilities.effects.common.CantBeCounteredSourceEffect; +import mage.constants.Zone; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class CantBeCounteredSourceAbility extends StaticAbility { + + public CantBeCounteredSourceAbility() { + super(Zone.STACK, new CantBeCounteredSourceEffect()); + } + + public CantBeCounteredSourceAbility(CantBeCounteredSourceAbility ability) { + super(ability); + } + + @Override + public String getRule() { + return "{this} can't be countered."; + } + + @Override + public CantBeCounteredSourceAbility copy() { + return new CantBeCounteredSourceAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/CantHaveMoreThanAmountCountersSourceAbility.java b/Mage/src/main/java/mage/abilities/common/CantHaveMoreThanAmountCountersSourceAbility.java index ddb331c104..0a826b7a17 100644 --- a/Mage/src/main/java/mage/abilities/common/CantHaveMoreThanAmountCountersSourceAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CantHaveMoreThanAmountCountersSourceAbility.java @@ -1,104 +1,104 @@ - -package mage.abilities.common; - -import mage.abilities.Ability; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.util.CardUtil; - -/** - * - * @author emerald000 - */ -public class CantHaveMoreThanAmountCountersSourceAbility extends SimpleStaticAbility { - - private final CounterType counterType; - private final int amount; - - public CantHaveMoreThanAmountCountersSourceAbility(CounterType counterType, int amount) { - super(Zone.BATTLEFIELD, new CantHaveMoreThanAmountCountersSourceEffect(counterType, amount)); - this.counterType = counterType; - this.amount = amount; - } - - private CantHaveMoreThanAmountCountersSourceAbility(CantHaveMoreThanAmountCountersSourceAbility ability) { - super(ability); - this.counterType = ability.counterType; - this.amount = ability.amount; - } - - @Override - public String getRule() { - return "Rasputin can't have more than " + CardUtil.numberToText(this.amount) + ' ' + this.counterType.getName() + " counters on it."; - } - - @Override - public CantHaveMoreThanAmountCountersSourceAbility copy() { - return new CantHaveMoreThanAmountCountersSourceAbility(this); - } - - public CounterType getCounterType() { - return this.counterType; - } - - public int getAmount() { - return this.amount; - } -} - -class CantHaveMoreThanAmountCountersSourceEffect extends ReplacementEffectImpl { - - private final CounterType counterType; - private final int amount; - - CantHaveMoreThanAmountCountersSourceEffect(CounterType counterType, int amount) { - super(Duration.WhileOnBattlefield, Outcome.Detriment, false); - this.counterType = counterType; - this.amount = amount; - } - - CantHaveMoreThanAmountCountersSourceEffect(final CantHaveMoreThanAmountCountersSourceEffect effect) { - super(effect); - this.counterType = effect.counterType; - this.amount = effect.amount; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.ADD_COUNTER; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent == null) { - permanent = game.getPermanentEntering(event.getTargetId()); - } - return permanent != null - && permanent.getId().equals(source.getSourceId()) - && event.getData().equals(this.counterType.getName()) - && permanent.getCounters(game).getCount(this.counterType) == this.amount; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public CantHaveMoreThanAmountCountersSourceEffect copy() { - return new CantHaveMoreThanAmountCountersSourceEffect(this); - } -} + +package mage.abilities.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.util.CardUtil; + +/** + * + * @author emerald000 + */ +public class CantHaveMoreThanAmountCountersSourceAbility extends SimpleStaticAbility { + + private final CounterType counterType; + private final int amount; + + public CantHaveMoreThanAmountCountersSourceAbility(CounterType counterType, int amount) { + super(Zone.BATTLEFIELD, new CantHaveMoreThanAmountCountersSourceEffect(counterType, amount)); + this.counterType = counterType; + this.amount = amount; + } + + private CantHaveMoreThanAmountCountersSourceAbility(CantHaveMoreThanAmountCountersSourceAbility ability) { + super(ability); + this.counterType = ability.counterType; + this.amount = ability.amount; + } + + @Override + public String getRule() { + return "Rasputin can't have more than " + CardUtil.numberToText(this.amount) + ' ' + this.counterType.getName() + " counters on it."; + } + + @Override + public CantHaveMoreThanAmountCountersSourceAbility copy() { + return new CantHaveMoreThanAmountCountersSourceAbility(this); + } + + public CounterType getCounterType() { + return this.counterType; + } + + public int getAmount() { + return this.amount; + } +} + +class CantHaveMoreThanAmountCountersSourceEffect extends ReplacementEffectImpl { + + private final CounterType counterType; + private final int amount; + + CantHaveMoreThanAmountCountersSourceEffect(CounterType counterType, int amount) { + super(Duration.WhileOnBattlefield, Outcome.Detriment, false); + this.counterType = counterType; + this.amount = amount; + } + + CantHaveMoreThanAmountCountersSourceEffect(final CantHaveMoreThanAmountCountersSourceEffect effect) { + super(effect); + this.counterType = effect.counterType; + this.amount = effect.amount; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.ADD_COUNTER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null) { + permanent = game.getPermanentEntering(event.getTargetId()); + } + return permanent != null + && permanent.getId().equals(source.getSourceId()) + && event.getData().equals(this.counterType.getName()) + && permanent.getCounters(game).getCount(this.counterType) == this.amount; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public CantHaveMoreThanAmountCountersSourceEffect copy() { + return new CantHaveMoreThanAmountCountersSourceEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java b/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java index 68ceca8ea6..f2777d8b8b 100644 --- a/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java @@ -9,7 +9,7 @@ import mage.constants.Zone; */ public class CastCommanderAbility extends SpellAbility { - private String ruleText; + private final String ruleText; public CastCommanderAbility(Card card, SpellAbility spellTemplate) { super(spellTemplate); diff --git a/Mage/src/main/java/mage/abilities/common/ControllerPlaysLandTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ControllerPlaysLandTriggeredAbility.java index f741ee9feb..42a69c0246 100644 --- a/Mage/src/main/java/mage/abilities/common/ControllerPlaysLandTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ControllerPlaysLandTriggeredAbility.java @@ -1,49 +1,49 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; - -/** - * @author jeffwadsworth - */ -public class ControllerPlaysLandTriggeredAbility extends TriggeredAbilityImpl { - - public ControllerPlaysLandTriggeredAbility(Zone zone, Effect effect, Boolean optional) { - super(zone, effect, optional); - } - - public ControllerPlaysLandTriggeredAbility(ControllerPlaysLandTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.LAND_PLAYED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent land = game.getPermanent(event.getTargetId()); - return land != null && land.getControllerId().equals(controllerId); - } - - @Override - public ControllerPlaysLandTriggeredAbility copy() { - return new ControllerPlaysLandTriggeredAbility(this); - } - - @Override - public String getRule() { - return "Whenever you play a land, "; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; + +/** + * @author jeffwadsworth + */ +public class ControllerPlaysLandTriggeredAbility extends TriggeredAbilityImpl { + + public ControllerPlaysLandTriggeredAbility(Zone zone, Effect effect, Boolean optional) { + super(zone, effect, optional); + } + + public ControllerPlaysLandTriggeredAbility(ControllerPlaysLandTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.LAND_PLAYED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent land = game.getPermanent(event.getTargetId()); + return land != null && land.getControllerId().equals(controllerId); + } + + @Override + public ControllerPlaysLandTriggeredAbility copy() { + return new ControllerPlaysLandTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever you play a land, "; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/CycleControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/CycleControllerTriggeredAbility.java new file mode 100644 index 0000000000..5911d5c445 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/CycleControllerTriggeredAbility.java @@ -0,0 +1,63 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.keyword.CyclingAbility; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.StackAbility; +import mage.game.stack.StackObject; + +/** + * @author TheElk801 + */ +public class CycleControllerTriggeredAbility extends TriggeredAbilityImpl { + + private final boolean excludeSource; + + public CycleControllerTriggeredAbility(Effect effect) { + this(effect, false); + } + + public CycleControllerTriggeredAbility(Effect effect, boolean optional) { + this(effect, optional, false); + } + + public CycleControllerTriggeredAbility(Effect effect, boolean optional, boolean excludeSource) { + super(Zone.BATTLEFIELD, effect, optional); + this.excludeSource = excludeSource; + } + + private CycleControllerTriggeredAbility(final CycleControllerTriggeredAbility ability) { + super(ability); + this.excludeSource = ability.excludeSource; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (game.getState().getStack().isEmpty() + || !event.getPlayerId().equals(this.getControllerId()) + || (event.getSourceId().equals(this.getSourceId()) && excludeSource)) { + return false; + } + StackObject item = game.getState().getStack().getFirst(); + return item instanceof StackAbility + && item.getStackAbility() instanceof CyclingAbility; + } + + @Override + public String getRule() { + return "Whenever you cycle " + (excludeSource ? "another" : "a") + " card, " + super.getRule(); + } + + @Override + public CycleControllerTriggeredAbility copy() { + return new CycleControllerTriggeredAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java index fabbf901df..051b491457 100644 --- a/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java @@ -1,5 +1,3 @@ - - package mage.abilities.common; import mage.abilities.effects.Effect; @@ -10,7 +8,6 @@ import mage.game.events.GameEvent; import mage.game.stack.StackObject; /** - * * @author Plopman */ public class CycleTriggeredAbility extends ZoneChangeTriggeredAbility { @@ -26,7 +23,7 @@ public class CycleTriggeredAbility extends ZoneChangeTriggeredAbility { public CycleTriggeredAbility(CycleTriggeredAbility ability) { super(ability); } - + @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; @@ -34,10 +31,11 @@ public class CycleTriggeredAbility extends ZoneChangeTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - if(event.getSourceId().equals(this.getSourceId())) { + if (event.getSourceId().equals(this.getSourceId())) { StackObject object = game.getStack().getStackObject(event.getSourceId()); - if(object != null && object.getStackAbility() instanceof CyclingAbility){ - return true; + if (object != null && object.getStackAbility() instanceof CyclingAbility) { + this.getEffects().setValue("cycleCosts", object.getStackAbility().getCosts()); + return true; } } return false; diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAllTriggeredAbility.java index 87f28e4dfc..35e66327e8 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAllTriggeredAbility.java @@ -1,104 +1,104 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.SetTargetPointer; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.events.DamagedCreatureEvent; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author LevelX2 - */ -public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityImpl { - - private final boolean combatDamageOnly; - private final FilterPermanent filterPermanent; - private final SetTargetPointer setTargetPointer; - - /** - * This ability works only for permanents doing damage. - * - * @param effect - * @param optional - * @param filterPermanent The filter that restricts which permanets have to - * trigger - * @param setTargetPointer The target to be set to target pointer of the - * effect.
- * - PLAYER = player controlling the damage source.
- * - PERMANENT = source permanent.
- * - PERMANENT_TARGET = damaged creature. - * @param combatDamageOnly The flag to determine if only combat damage has - * to trigger - */ - public DealsDamageToACreatureAllTriggeredAbility(Effect effect, boolean optional, FilterPermanent filterPermanent, SetTargetPointer setTargetPointer, boolean combatDamageOnly) { - super(Zone.BATTLEFIELD, effect, optional); - this.combatDamageOnly = combatDamageOnly; - this.setTargetPointer = setTargetPointer; - this.filterPermanent = filterPermanent; - } - - public DealsDamageToACreatureAllTriggeredAbility(final DealsDamageToACreatureAllTriggeredAbility ability) { - super(ability); - this.combatDamageOnly = ability.combatDamageOnly; - this.filterPermanent = ability.filterPermanent; - this.setTargetPointer = ability.setTargetPointer; - } - - @Override - public DealsDamageToACreatureAllTriggeredAbility copy() { - return new DealsDamageToACreatureAllTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DAMAGED_CREATURE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!combatDamageOnly || ((DamagedCreatureEvent) event).isCombatDamage()) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (permanent != null && filterPermanent.match(permanent, getSourceId(), getControllerId(), game)) { - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - effect.setValue("sourceId", event.getSourceId()); - switch (setTargetPointer) { - case PLAYER: - effect.setTargetPointer(new FixedTarget(permanent.getControllerId())); - break; - case PERMANENT: - effect.setTargetPointer(new FixedTarget(permanent, game)); - break; - case PERMANENT_TARGET: - Permanent permanent_target = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent_target != null) { - effect.setTargetPointer(new FixedTarget(permanent_target, game)); - } - break; - } - - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever " + filterPermanent.getMessage() + " deals " - + (combatDamageOnly ? "combat " : "") + "damage to a creature, " + super.getRule(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.DamagedCreatureEvent; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author LevelX2 + */ +public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityImpl { + + private final boolean combatDamageOnly; + private final FilterPermanent filterPermanent; + private final SetTargetPointer setTargetPointer; + + /** + * This ability works only for permanents doing damage. + * + * @param effect + * @param optional + * @param filterPermanent The filter that restricts which permanets have to + * trigger + * @param setTargetPointer The target to be set to target pointer of the + * effect.
+ * - PLAYER = player controlling the damage source.
+ * - PERMANENT = source permanent.
+ * - PERMANENT_TARGET = damaged creature. + * @param combatDamageOnly The flag to determine if only combat damage has + * to trigger + */ + public DealsDamageToACreatureAllTriggeredAbility(Effect effect, boolean optional, FilterPermanent filterPermanent, SetTargetPointer setTargetPointer, boolean combatDamageOnly) { + super(Zone.BATTLEFIELD, effect, optional); + this.combatDamageOnly = combatDamageOnly; + this.setTargetPointer = setTargetPointer; + this.filterPermanent = filterPermanent; + } + + public DealsDamageToACreatureAllTriggeredAbility(final DealsDamageToACreatureAllTriggeredAbility ability) { + super(ability); + this.combatDamageOnly = ability.combatDamageOnly; + this.filterPermanent = ability.filterPermanent; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public DealsDamageToACreatureAllTriggeredAbility copy() { + return new DealsDamageToACreatureAllTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGED_CREATURE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!combatDamageOnly || ((DamagedCreatureEvent) event).isCombatDamage()) { + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (permanent != null && filterPermanent.match(permanent, getSourceId(), getControllerId(), game)) { + for (Effect effect : this.getEffects()) { + effect.setValue("damage", event.getAmount()); + effect.setValue("sourceId", event.getSourceId()); + switch (setTargetPointer) { + case PLAYER: + effect.setTargetPointer(new FixedTarget(permanent.getControllerId())); + break; + case PERMANENT: + effect.setTargetPointer(new FixedTarget(permanent, game)); + break; + case PERMANENT_TARGET: + Permanent permanent_target = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent_target != null) { + effect.setTargetPointer(new FixedTarget(permanent_target, game)); + } + break; + } + + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever " + filterPermanent.getMessage() + " deals " + + (combatDamageOnly ? "combat " : "") + "damage to a creature, " + super.getRule(); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java new file mode 100644 index 0000000000..1d8eabc2bd --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java @@ -0,0 +1,62 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +public class DestroyPlaneswalkerWhenDamagedTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterPermanent filter; + + public DestroyPlaneswalkerWhenDamagedTriggeredAbility() { + this((FilterPermanent) null); + } + + public DestroyPlaneswalkerWhenDamagedTriggeredAbility(FilterPermanent filter) { + super(Zone.BATTLEFIELD, null); + this.filter = filter; + } + + private DestroyPlaneswalkerWhenDamagedTriggeredAbility(final DestroyPlaneswalkerWhenDamagedTriggeredAbility effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public DestroyPlaneswalkerWhenDamagedTriggeredAbility copy() { + return new DestroyPlaneswalkerWhenDamagedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = getSourcePermanentIfItStillExists(game); + if (permanent != null) { + boolean applies = filter != null ? + filter.match(permanent, game) : event.getSourceId().equals(getSourceId()); + if (applies) { + Effect effect = new DestroyTargetEffect(); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); + this.getEffects().clear(); + this.addEffect(effect); + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever " + (filter != null ? filter.getMessage() : "this creature") + " deals damage to a planeswalker, destroy that planeswalker."; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java index d6dc69c5c6..1928b30063 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java @@ -69,7 +69,7 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; if (zEvent.isDiesEvent()) { - if (filter.match(zEvent.getTarget(), sourceId, controllerId, game) && zEvent.getTarget().isCreature()) { + if (filter.match(zEvent.getTarget(), sourceId, controllerId, game)) { if (setTargetPointer) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); diff --git a/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java new file mode 100644 index 0000000000..9a9d219669 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java @@ -0,0 +1,79 @@ +package mage.abilities.common; + +import mage.MageObject; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; + +/** + * @author BetaSteward_at_googlemail.com + */ +public class DiesSourceTriggeredAbility extends ZoneChangeTriggeredAbility { + + public DiesSourceTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, Zone.GRAVEYARD, effect, "When {this} dies, ", optional); + } + + public DiesSourceTriggeredAbility(Effect effect) { + this(effect, false); + } + + public DiesSourceTriggeredAbility(DiesSourceTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { + // check it was previously on battlefield + Permanent before = ((ZoneChangeEvent) event).getTarget(); + if (before == null) { + return false; + } + if (!this.hasSourceObjectAbility(game, before, event)) { // the permanent does not have the ability so no trigger + return false; + } + // check now it is in graveyard if it is no token + if (!(before instanceof PermanentToken) && before.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(sourceId)) { + Zone after = game.getState().getZone(sourceId); + return after != null && Zone.GRAVEYARD.match(after); + } else { + // Already moved to another zone, so guess it's ok + return true; + } + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + if (super.checkEventType(event, game)) { + return ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD; + } + return false; + } + + @Override + public DiesSourceTriggeredAbility copy() { + return new DiesSourceTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (super.checkTrigger(event, game)) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getTarget().isTransformable()) { + if (!zEvent.getTarget().getAbilities().contains(this)) { + return false; + } + } + for (Effect effect : getEffects()) { + effect.setValue("permanentLeftBattlefield", zEvent.getTarget()); + } + return true; + } + return false; + } + +} diff --git a/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureTriggeredAbility.java index c0d241039d..61478cf9ba 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureTriggeredAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common; import mage.MageObject; @@ -17,6 +16,7 @@ import mage.game.permanent.Permanent; public class DiesThisOrAnotherCreatureTriggeredAbility extends TriggeredAbilityImpl { protected FilterCreaturePermanent filter; + private boolean applyFilterOnSource = false; public DiesThisOrAnotherCreatureTriggeredAbility(Effect effect, boolean optional) { this(effect, optional, new FilterCreaturePermanent()); @@ -30,6 +30,12 @@ public class DiesThisOrAnotherCreatureTriggeredAbility extends TriggeredAbilityI public DiesThisOrAnotherCreatureTriggeredAbility(DiesThisOrAnotherCreatureTriggeredAbility ability) { super(ability); this.filter = ability.filter; + this.applyFilterOnSource = ability.applyFilterOnSource; + } + + public DiesThisOrAnotherCreatureTriggeredAbility setApplyFilterOnSource(boolean applyFilterOnSource) { + this.applyFilterOnSource = applyFilterOnSource; + return this; } @Override @@ -68,7 +74,7 @@ public class DiesThisOrAnotherCreatureTriggeredAbility extends TriggeredAbilityI if (zEvent.isDiesEvent()) { if (zEvent.getTarget() != null) { - if (zEvent.getTarget().getId().equals(this.getSourceId())) { + if (!applyFilterOnSource && zEvent.getTarget().getId().equals(this.getSourceId())) { return true; } else { if (filter.match(zEvent.getTarget(), getSourceId(), getControllerId(), game)) { diff --git a/Mage/src/main/java/mage/abilities/common/DiscardedByOpponentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiscardedByOpponentTriggeredAbility.java index 97f7c15618..397ead5d6d 100644 --- a/Mage/src/main/java/mage/abilities/common/DiscardedByOpponentTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiscardedByOpponentTriggeredAbility.java @@ -1,54 +1,54 @@ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.stack.StackObject; - -/** - * - * @author Styxo - */ -public class DiscardedByOpponentTriggeredAbility extends TriggeredAbilityImpl { - - public DiscardedByOpponentTriggeredAbility(Effect effect) { - this(effect, false); - } - - public DiscardedByOpponentTriggeredAbility(Effect effect, boolean optional) { - super(Zone.GRAVEYARD, effect, optional); - } - - public DiscardedByOpponentTriggeredAbility(final DiscardedByOpponentTriggeredAbility ability) { - super(ability); - } - - @Override - public DiscardedByOpponentTriggeredAbility copy() { - return new DiscardedByOpponentTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DISCARDED_CARD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (getSourceId().equals(event.getTargetId())) { - StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); - if (stackObject != null) { - return game.getOpponents(this.getControllerId()).contains(stackObject.getControllerId()); - } - } - return false; - } - - @Override - public String getRule() { - return "When a spell or ability an opponent controls causes you to discard this card, " + super.getRule(); - } -} +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.stack.StackObject; + +/** + * + * @author Styxo + */ +public class DiscardedByOpponentTriggeredAbility extends TriggeredAbilityImpl { + + public DiscardedByOpponentTriggeredAbility(Effect effect) { + this(effect, false); + } + + public DiscardedByOpponentTriggeredAbility(Effect effect, boolean optional) { + super(Zone.GRAVEYARD, effect, optional); + } + + public DiscardedByOpponentTriggeredAbility(final DiscardedByOpponentTriggeredAbility ability) { + super(ability); + } + + @Override + public DiscardedByOpponentTriggeredAbility copy() { + return new DiscardedByOpponentTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DISCARDED_CARD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (getSourceId().equals(event.getTargetId())) { + StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); + if (stackObject != null) { + return game.getOpponents(this.getControllerId()).contains(stackObject.getControllerId()); + } + } + return false; + } + + @Override + public String getRule() { + return "When a spell or ability an opponent controls causes you to discard this card, " + super.getRule(); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java index 5f907b63a3..a55686155c 100644 --- a/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -43,7 +42,7 @@ public class EnchantedCreatureBlockedTriggeredAbility extends TriggeredAbilityIm @Override public String getRule() { - return "Whenever enchanted creature becomes blocked by a creature, " + super.getRule(); + return "Whenever enchanted creature becomes blocked, " + super.getRule(); } @Override diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java index 3a7125f87a..41fb6e3d6d 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java @@ -1,7 +1,6 @@ package mage.abilities.common; -import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.SetTargetPointer; @@ -12,8 +11,9 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl { @@ -22,6 +22,7 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl { protected String rule; protected boolean controlledText; protected SetTargetPointer setTargetPointer; + protected final boolean thisOrAnother; /** * zone = BATTLEFIELD optional = false @@ -54,11 +55,16 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl { } public EntersBattlefieldAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, String rule, boolean controlledText) { + this(zone, effect, filter, optional, setTargetPointer, rule, controlledText, false); + } + + protected EntersBattlefieldAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, String rule, boolean controlledText, boolean thisOrAnother) { super(zone, effect, optional); this.filter = filter; this.rule = rule; this.controlledText = controlledText; this.setTargetPointer = setTargetPointer; + this.thisOrAnother = thisOrAnother; } public EntersBattlefieldAllTriggeredAbility(final EntersBattlefieldAllTriggeredAbility ability) { @@ -67,6 +73,7 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl { this.rule = ability.rule; this.controlledText = ability.controlledText; this.setTargetPointer = ability.setTargetPointer; + this.thisOrAnother = ability.thisOrAnother; } @Override @@ -105,7 +112,11 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl { if (rule != null && !rule.isEmpty()) { return rule; } - StringBuilder sb = new StringBuilder("Whenever ").append(filter.getMessage()); + StringBuilder sb = new StringBuilder("Whenever "); + if (thisOrAnother) { + sb.append("{this} or another "); + } + sb.append(filter.getMessage()); sb.append(" enters the battlefield"); if (controlledText) { sb.append(" under your control, "); diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldThisOrAnotherTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldThisOrAnotherTriggeredAbility.java new file mode 100644 index 0000000000..ce631a0a3b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldThisOrAnotherTriggeredAbility.java @@ -0,0 +1,62 @@ +package mage.abilities.common; + +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * @author TheElk801 + */ +public class EntersBattlefieldThisOrAnotherTriggeredAbility extends EntersBattlefieldAllTriggeredAbility { + + private final boolean onlyControlled; + + public EntersBattlefieldThisOrAnotherTriggeredAbility(Effect effect, FilterPermanent filter) { + this(effect, filter, false, false); + } + + public EntersBattlefieldThisOrAnotherTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, boolean onlyControlled) { + this(effect, filter, optional, SetTargetPointer.NONE, onlyControlled); + } + + public EntersBattlefieldThisOrAnotherTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyControlled) { + this(Zone.BATTLEFIELD, effect, filter, optional, setTargetPointer, onlyControlled); + } + + public EntersBattlefieldThisOrAnotherTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyControlled) { + super(zone, effect, filter, optional, setTargetPointer, null, onlyControlled, true); + this.onlyControlled = onlyControlled; + } + + private EntersBattlefieldThisOrAnotherTriggeredAbility(final EntersBattlefieldThisOrAnotherTriggeredAbility ability) { + super(ability); + this.onlyControlled = ability.onlyControlled; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!super.checkTrigger(event, game)) { + return false; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null) { + return false; + } + if (permanent.getId().equals(getSourceId())) { + return true; + } + if (onlyControlled && !permanent.isControlledBy(this.getControllerId())) { + return false; + } + return filter.match(permanent, getSourceId(), getControllerId(), game); + } + + @Override + public EntersBattlefieldThisOrAnotherTriggeredAbility copy() { + return new EntersBattlefieldThisOrAnotherTriggeredAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/GoadAttachedAbility.java b/Mage/src/main/java/mage/abilities/common/GoadAttachedAbility.java new file mode 100644 index 0000000000..2111294b5e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/GoadAttachedAbility.java @@ -0,0 +1,72 @@ +package mage.abilities.common; + +import mage.abilities.Ability; +import mage.abilities.StaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.combat.AttacksIfAbleAttachedEffect; +import mage.constants.AttachmentType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class GoadAttachedAbility extends StaticAbility { + + public GoadAttachedAbility(Effect... effects) { + super(Zone.BATTLEFIELD, null); + for (Effect effect : effects) { + this.addEffect(effect); + } + this.addEffect(new AttacksIfAbleAttachedEffect( + Duration.WhileOnBattlefield, AttachmentType.AURA + ).setText(", and is goaded. ")); + this.addEffect(new GoadAttackEffect()); + } + + private GoadAttachedAbility(final GoadAttachedAbility ability) { + super(ability); + } + + @Override + public GoadAttachedAbility copy() { + return new GoadAttachedAbility(this); + } +} + +class GoadAttackEffect extends RestrictionEffect { + + GoadAttackEffect() { + super(Duration.WhileOnBattlefield); + staticText = "(It attacks each combat if able and attacks a player other than you if able.)"; + } + + private GoadAttackEffect(final GoadAttackEffect effect) { + super(effect); + } + + @Override + public GoadAttackEffect copy() { + return new GoadAttackEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + Permanent attachment = game.getPermanent(source.getSourceId()); + return attachment != null && attachment.getAttachedTo() != null + && permanent.getId().equals(attachment.getAttachedTo()); + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { + if (defenderId == null) { + return true; + } + return !defenderId.equals(source.getControllerId()); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java index 7ce2028a91..83d6d92f95 100644 --- a/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java @@ -1,88 +1,88 @@ - -package mage.abilities.common; - -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.SetTargetPointer; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author Styxo - */ -public class LeavesBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl { - - protected FilterPermanent filter; - protected SetTargetPointer setTargetPointer; - - public LeavesBattlefieldAllTriggeredAbility(Effect effect, FilterPermanent filter) { - this(effect, filter, false); - } - - public LeavesBattlefieldAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) { - this(Zone.BATTLEFIELD, effect, filter, optional); - } - - public LeavesBattlefieldAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional) { - this(zone, effect, filter, optional, SetTargetPointer.NONE); - } - - public LeavesBattlefieldAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer) { - super(zone, effect, optional); - this.filter = filter; - this.setTargetPointer = setTargetPointer; - } - - private LeavesBattlefieldAllTriggeredAbility(final LeavesBattlefieldAllTriggeredAbility ability) { - super(ability); - filter = ability.filter; - setTargetPointer = ability.setTargetPointer; - } - - @Override - public LeavesBattlefieldAllTriggeredAbility copy() { - return new LeavesBattlefieldAllTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - UUID targetId = event.getTargetId(); - Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); - if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { - if (setTargetPointer != SetTargetPointer.NONE) { - for (Effect effect : this.getEffects()) { - switch (setTargetPointer) { - case PERMANENT: - effect.setTargetPointer(new FixedTarget(permanent.getId())); - break; - case PLAYER: - effect.setTargetPointer(new FixedTarget(permanent.getControllerId())); - break; - } - } - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever " + filter.getMessage() + " leaves the battlefield, " + super.getRule(); - } -} + +package mage.abilities.common; + +import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author Styxo + */ +public class LeavesBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl { + + protected FilterPermanent filter; + protected SetTargetPointer setTargetPointer; + + public LeavesBattlefieldAllTriggeredAbility(Effect effect, FilterPermanent filter) { + this(effect, filter, false); + } + + public LeavesBattlefieldAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) { + this(Zone.BATTLEFIELD, effect, filter, optional); + } + + public LeavesBattlefieldAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional) { + this(zone, effect, filter, optional, SetTargetPointer.NONE); + } + + public LeavesBattlefieldAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer) { + super(zone, effect, optional); + this.filter = filter; + this.setTargetPointer = setTargetPointer; + } + + protected LeavesBattlefieldAllTriggeredAbility(final LeavesBattlefieldAllTriggeredAbility ability) { + super(ability); + filter = ability.filter; + setTargetPointer = ability.setTargetPointer; + } + + @Override + public LeavesBattlefieldAllTriggeredAbility copy() { + return new LeavesBattlefieldAllTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getFromZone() == Zone.BATTLEFIELD) { + UUID targetId = event.getTargetId(); + Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); + if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { + if (setTargetPointer != SetTargetPointer.NONE) { + for (Effect effect : this.getEffects()) { + switch (setTargetPointer) { + case PERMANENT: + effect.setTargetPointer(new FixedTarget(permanent, game)); + break; + case PLAYER: + effect.setTargetPointer(new FixedTarget(permanent.getControllerId())); + break; + } + } + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever " + filter.getMessage() + " leaves the battlefield, " + super.getRule(); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/LicidAbility.java b/Mage/src/main/java/mage/abilities/common/LicidAbility.java index f37f715e26..e5aae7688f 100644 --- a/Mage/src/main/java/mage/abilities/common/LicidAbility.java +++ b/Mage/src/main/java/mage/abilities/common/LicidAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common; import java.util.ArrayList; @@ -78,7 +77,7 @@ class LicidEffect extends OneShotEffect { if (licid != null) { UUID messageId = UUID.randomUUID(); LicidContinuousEffect effect = new LicidContinuousEffect(messageId); - effect.setTargetPointer(new FixedTarget(licid.getId())); + effect.setTargetPointer(new FixedTarget(licid, game)); game.addEffect(effect, source); new AttachEffect(Outcome.Neutral).apply(game, source); SpecialAction specialAction = new LicidSpecialAction(this.specialActionCost, messageId, licid.getIdName()); @@ -130,7 +129,8 @@ class LicidContinuousEffect extends ContinuousEffectImpl { } } } - licid.getAbilities(game).removeAll(toRemove); + licid.removeAbilities(toRemove, source.getSourceId(), game); + Ability ability = new EnchantAbility("creature"); ability.setRuleAtTheTop(true); licid.addAbility(ability, source.getSourceId(), game); diff --git a/Mage/src/main/java/mage/abilities/common/MutatesSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/MutatesSourceTriggeredAbility.java new file mode 100644 index 0000000000..7416eab26f --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/MutatesSourceTriggeredAbility.java @@ -0,0 +1,47 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * @author TheElk801 + */ +public class MutatesSourceTriggeredAbility extends TriggeredAbilityImpl { + + public MutatesSourceTriggeredAbility(Effect effect) { + this(effect, false); + } + + public MutatesSourceTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + private MutatesSourceTriggeredAbility(final MutatesSourceTriggeredAbility ability) { + super(ability); + } + + @Override + public MutatesSourceTriggeredAbility copy() { + return new MutatesSourceTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + // TODO: implement this + return false; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + // TODO: implement this + return false; + } + + @Override + public String getRule() { + return "Whenever this creature mutates, " + super.getRule(); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/OpponentPlaysLandTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/OpponentPlaysLandTriggeredAbility.java index d8c7ba42e0..86b230daed 100644 --- a/Mage/src/main/java/mage/abilities/common/OpponentPlaysLandTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/OpponentPlaysLandTriggeredAbility.java @@ -1,48 +1,48 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; - -/** - * @author jeffwadsworth - */ -public class OpponentPlaysLandTriggeredAbility extends TriggeredAbilityImpl { - - public OpponentPlaysLandTriggeredAbility(Zone zone, Effect effect, Boolean optional) { - super(zone, effect, optional); - } - - public OpponentPlaysLandTriggeredAbility(OpponentPlaysLandTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.LAND_PLAYED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent land = game.getPermanent(event.getTargetId()); - return land != null && game.getOpponents(controllerId).contains(land.getControllerId()); - } - - @Override - public OpponentPlaysLandTriggeredAbility copy() { - return new OpponentPlaysLandTriggeredAbility(this); - } - - @Override - public String getRule() { - return "Whenever an opponent plays a land, "; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * @author jeffwadsworth + */ +public class OpponentPlaysLandTriggeredAbility extends TriggeredAbilityImpl { + + public OpponentPlaysLandTriggeredAbility(Zone zone, Effect effect, Boolean optional) { + super(zone, effect, optional); + } + + public OpponentPlaysLandTriggeredAbility(OpponentPlaysLandTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LAND_PLAYED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent land = game.getPermanent(event.getTargetId()); + return land != null && game.getOpponents(controllerId).contains(land.getControllerId()); + } + + @Override + public OpponentPlaysLandTriggeredAbility copy() { + return new OpponentPlaysLandTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever an opponent plays a land, "; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/PassAbility.java b/Mage/src/main/java/mage/abilities/common/PassAbility.java index 08ebb1608a..e49cc4ca4a 100644 --- a/Mage/src/main/java/mage/abilities/common/PassAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PassAbility.java @@ -1,14 +1,13 @@ - package mage.abilities.common; -import java.util.UUID; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.effects.common.PassEffect; import mage.constants.Zone; import mage.game.Game; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public class PassAbility extends ActivatedAbilityImpl { @@ -29,7 +28,7 @@ public class PassAbility extends ActivatedAbilityImpl { @Override public ActivationStatus canActivate(UUID playerId, Game game) { - return ActivationStatus.getTrue(); + return ActivationStatus.getTrue(this, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/common/PutCardIntoGraveFromAnywhereAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/PutCardIntoGraveFromAnywhereAllTriggeredAbility.java index aaf1597ab7..c23396919e 100644 --- a/Mage/src/main/java/mage/abilities/common/PutCardIntoGraveFromAnywhereAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PutCardIntoGraveFromAnywhereAllTriggeredAbility.java @@ -85,7 +85,7 @@ public class PutCardIntoGraveFromAnywhereAllTriggeredAbility extends TriggeredAb switch (setTargetPointer) { case CARD: for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); } break; case PLAYER: diff --git a/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredAbility.java index 6d9cf2e45f..570487225f 100644 --- a/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredAbility.java @@ -41,6 +41,9 @@ public class TapForManaAllTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { + if (game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA + return false; + } Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { ManaEvent mEvent = (ManaEvent) event; @@ -49,7 +52,7 @@ public class TapForManaAllTriggeredAbility extends TriggeredAbilityImpl { } switch(setTargetPointer) { case PERMANENT: - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); break; case PLAYER: getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getControllerId())); diff --git a/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredManaAbility.java b/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredManaAbility.java index 608e2f358b..9949441de1 100644 --- a/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredManaAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TapForManaAllTriggeredManaAbility.java @@ -49,7 +49,7 @@ public class TapForManaAllTriggeredManaAbility extends TriggeredManaAbility { effect.setValue("mana", mEvent.getMana()); switch(setTargetPointer) { case PERMANENT: - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); break; case PLAYER: effect.setTargetPointer(new FixedTarget(permanent.getControllerId())); diff --git a/Mage/src/main/java/mage/abilities/common/TapLandForManaAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TapLandForManaAllTriggeredAbility.java index f6cba6af6b..da10a22b40 100644 --- a/Mage/src/main/java/mage/abilities/common/TapLandForManaAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TapLandForManaAllTriggeredAbility.java @@ -34,6 +34,9 @@ public class TapLandForManaAllTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { + if (game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA + return false; + } Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); if (permanent != null && permanent.isLand()) { if (setTargetPointer) { diff --git a/Mage/src/main/java/mage/abilities/common/TapLandForManaAllTriggeredManaAbility.java b/Mage/src/main/java/mage/abilities/common/TapLandForManaAllTriggeredManaAbility.java index d236c19ce5..eedcc0c7ce 100644 --- a/Mage/src/main/java/mage/abilities/common/TapLandForManaAllTriggeredManaAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TapLandForManaAllTriggeredManaAbility.java @@ -38,7 +38,7 @@ public class TapLandForManaAllTriggeredManaAbility extends TriggeredManaAbility Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); if (permanent != null && permanent.isLand()) { if (setTargetPointer) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); } return true; } diff --git a/Mage/src/main/java/mage/abilities/common/TurnFaceUpAbility.java b/Mage/src/main/java/mage/abilities/common/TurnFaceUpAbility.java index de4daaa7ef..7a76fc7955 100644 --- a/Mage/src/main/java/mage/abilities/common/TurnFaceUpAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TurnFaceUpAbility.java @@ -40,6 +40,7 @@ public class TurnFaceUpAbility extends SpecialAction { this.usesStack = false; this.abilityType = AbilityType.SPECIAL_ACTION; this.setRuleVisible(false); // will be made visible only to controller in CardView + this.setWorksFaceDown(true); } public TurnFaceUpAbility(final TurnFaceUpAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/UnattachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/UnattachedTriggeredAbility.java index 83fe71cfbc..95a6d8431c 100644 --- a/Mage/src/main/java/mage/abilities/common/UnattachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/UnattachedTriggeredAbility.java @@ -31,7 +31,7 @@ public class UnattachedTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getSourceId().equals(this.getSourceId()) ) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId())); + getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java index 9d97496682..10ee16f538 100644 --- a/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java @@ -27,6 +27,9 @@ public class ZoneChangeAllTriggeredAbility extends TriggeredAbilityImpl { public ZoneChangeAllTriggeredAbility(Zone zone, Zone fromZone, Zone toZone, Effect effect, FilterPermanent filter, String rule, boolean optional) { super(zone, effect, optional); + if (fromZone == Zone.BATTLEFIELD) { + setLeavesTheBattlefieldTrigger(true); + } this.fromZone = fromZone; this.toZone = toZone; this.rule = rule; diff --git a/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java index 1bb565531a..5329f3f438 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common.delayed; import mage.constants.Outcome; @@ -19,7 +18,6 @@ public class PactDelayedTriggeredAbility extends DelayedTriggeredAbility { super(new PactEffect(cost)); } - public PactDelayedTriggeredAbility(PactDelayedTriggeredAbility ability) { super(ability); } @@ -39,8 +37,6 @@ public class PactDelayedTriggeredAbility extends DelayedTriggeredAbility { return game.isActivePlayer(this.getControllerId()); } - - @Override public String getRule() { return "At the beginning of your next upkeep " + modes.getText(); @@ -49,8 +45,7 @@ public class PactDelayedTriggeredAbility extends DelayedTriggeredAbility { class PactEffect extends OneShotEffect { - private ManaCosts cost; - + private final ManaCosts cost; public PactEffect(ManaCosts cost) { super(Outcome.Neutral); @@ -60,7 +55,7 @@ class PactEffect extends OneShotEffect { public PactEffect(final PactEffect effect) { super(effect); - this.cost = effect.cost; + this.cost = effect.cost.copy(); } @Override @@ -71,10 +66,10 @@ class PactEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + '?', source, game)) { + if (player != null) { + if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + '?', source, game)) { cost.clearPaid(); - if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)){ + if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { return true; } } @@ -83,7 +78,4 @@ class PactEffect extends OneShotEffect { } return false; } - - - } diff --git a/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java new file mode 100644 index 0000000000..e8202aa834 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java @@ -0,0 +1,52 @@ +package mage.abilities.common.delayed; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; + +import java.util.Locale; + +/** + * @author TheElk801 + */ +public class ReflexiveTriggeredAbility extends DelayedTriggeredAbility { + + private final String text; + + public ReflexiveTriggeredAbility(Effect effect, boolean optional, String text) { + super(effect, Duration.EndOfTurn, true, optional); + this.text = text; + } + + protected ReflexiveTriggeredAbility(final ReflexiveTriggeredAbility ability) { + super(ability); + this.text = ability.text; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.OPTION_USED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals(this.getControllerId()) + && event.getSourceId().equals(this.getSourceId()); + } + + @Override + public String getRule() { + return text.substring(0, 1).toUpperCase(Locale.ENGLISH) + text.substring(1) + '.'; + } + + public String getText() { + return text; + } + + @Override + public ReflexiveTriggeredAbility copy() { + return new ReflexiveTriggeredAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/OrCondition.java b/Mage/src/main/java/mage/abilities/condition/OrCondition.java index 0d5bb3f6ca..621edbd993 100644 --- a/Mage/src/main/java/mage/abilities/condition/OrCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/OrCondition.java @@ -1,38 +1,38 @@ - -package mage.abilities.condition; - -import java.util.ArrayList; -import java.util.Arrays; -import mage.abilities.Ability; -import mage.game.Game; - -/** - * Combines conditions to one compound conditon, one condition must be - * true to return true for the compound condtion. - * - * @author emerald000 - */ -public class OrCondition implements Condition { - - private final ArrayList conditions = new ArrayList<>(); - private final String text; - - public OrCondition(Condition... conditions) { - this("", conditions); - } - - public OrCondition(String text, Condition... conditions) { - this.conditions.addAll(Arrays.asList(conditions)); - this.text = text; - } - - @Override - public boolean apply(Game game, Ability source) { - return conditions.stream().anyMatch(condition -> condition.apply(game, source)); - } - - @Override - public String toString() { - return text; - } -} + +package mage.abilities.condition; + +import java.util.ArrayList; +import java.util.Arrays; +import mage.abilities.Ability; +import mage.game.Game; + +/** + * Combines conditions to one compound conditon, one condition must be + * true to return true for the compound condtion. + * + * @author emerald000 + */ +public class OrCondition implements Condition { + + private final ArrayList conditions = new ArrayList<>(); + private final String text; + + public OrCondition(Condition... conditions) { + this("", conditions); + } + + public OrCondition(String text, Condition... conditions) { + this.conditions.addAll(Arrays.asList(conditions)); + this.text = text; + } + + @Override + public boolean apply(Game game, Ability source) { + return conditions.stream().anyMatch(condition -> condition.apply(game, source)); + } + + @Override + public String toString() { + return text; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/AttachedToPermanentCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AttachedToPermanentCondition.java new file mode 100644 index 0000000000..fcb235271d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/AttachedToPermanentCondition.java @@ -0,0 +1,33 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * Source must be attached to permanent + * + * @author JayDi85 + */ +public class AttachedToPermanentCondition implements Condition { + + final UUID permanentId; + + public AttachedToPermanentCondition(UUID permanentId) { + this.permanentId = permanentId; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent attachment = game.getPermanent(source.getSourceId()); + Permanent permanent = game.getPermanent(this.permanentId); + if (attachment != null && permanent != null) { + return permanent.getAttachments().contains(attachment.getId()); + } + return false; + } + +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/AttackedThisTurnSourceCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AttackedThisTurnSourceCondition.java index 0bf152ec76..54d20484f4 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/AttackedThisTurnSourceCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/AttackedThisTurnSourceCondition.java @@ -1,24 +1,24 @@ - -package mage.abilities.condition.common; - -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.watchers.common.AttackedThisTurnWatcher; - -/** - * - * @author LevelX2 - */ -public enum AttackedThisTurnSourceCondition implements Condition { - instance; - - @Override - public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - AttackedThisTurnWatcher watcher = game.getState().getWatcher(AttackedThisTurnWatcher.class); - return sourcePermanent != null && watcher.getAttackedThisTurnCreatures().contains(new MageObjectReference(sourcePermanent, game)); - } -} + +package mage.abilities.condition.common; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.watchers.common.AttackedThisTurnWatcher; + +/** + * + * @author LevelX2 + */ +public enum AttackedThisTurnSourceCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + AttackedThisTurnWatcher watcher = game.getState().getWatcher(AttackedThisTurnWatcher.class); + return sourcePermanent != null && watcher.getAttackedThisTurnCreatures().contains(new MageObjectReference(sourcePermanent, game)); + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/BuybackCondition.java b/Mage/src/main/java/mage/abilities/condition/common/BuybackCondition.java index f61f4ead82..e565ea72f3 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/BuybackCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/BuybackCondition.java @@ -17,7 +17,7 @@ public enum BuybackCondition implements Condition { public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getSourceId()); if (card != null) { - return card.getAbilities().stream() + return card.getAbilities(game).stream() .filter(a -> a instanceof BuybackAbility) .anyMatch(a -> ((BuybackAbility) a).isBuybackActivated(game)); } diff --git a/Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourceCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourcePermanentCondition.java similarity index 95% rename from Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourceCondition.java rename to Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourcePermanentCondition.java index 1a584533ec..e65dc1f59a 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourceCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourcePermanentCondition.java @@ -13,7 +13,7 @@ import mage.watchers.common.CastFromHandWatcher; * * @author Loki */ -public enum CastFromHandSourceCondition implements Condition { +public enum CastFromHandSourcePermanentCondition implements Condition { instance; diff --git a/Mage/src/main/java/mage/abilities/condition/common/CitysBlessingCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CitysBlessingCondition.java index 158d41ae2d..11a283338a 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/CitysBlessingCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/CitysBlessingCondition.java @@ -1,26 +1,26 @@ - -package mage.abilities.condition.common; - -import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.designations.DesignationType; -import mage.game.Game; - -/** - * - * @author LvelX2 - */ -public enum CitysBlessingCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - return game.getPlayer(source.getControllerId()).hasDesignation(DesignationType.CITYS_BLESSING); - } - - @Override - public String toString() { - return "you have the city's blessing"; - } -} + +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.designations.DesignationType; +import mage.game.Game; + +/** + * + * @author LvelX2 + */ +public enum CitysBlessingCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.getPlayer(source.getControllerId()).hasDesignation(DesignationType.CITYS_BLESSING); + } + + @Override + public String toString() { + return "you have the city's blessing"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/ControlACommanderCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ControlACommanderCondition.java new file mode 100644 index 0000000000..9cd23bbef9 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/ControlACommanderCondition.java @@ -0,0 +1,36 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.constants.CommanderCardType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.Collection; +import java.util.Objects; + +/** + * @author TheElk801 + */ +public enum ControlACommanderCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.getPlayerList() + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(player -> game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER)) + .flatMap(Collection::stream) + .map(game::getPermanent) + .filter(Objects::nonNull) + .map(Permanent::getControllerId) + .anyMatch(source.getControllerId()::equals); + } + + @Override + public String toString() { + return "If you control a commander"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/CreatureCountCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CreatureCountCondition.java index 6b2686030d..6cb7afd8e2 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/CreatureCountCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/CreatureCountCondition.java @@ -1,88 +1,88 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.condition.common; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; - -/** - * - * @author Styxo - */ -public class CreatureCountCondition implements Condition { - - private FilterCreaturePermanent filter; - private int creatureCount; - private TargetController targetController; - - public CreatureCountCondition(FilterCreaturePermanent filter, int creatureCount, TargetController targetController) { - this.filter = filter; - this.creatureCount = creatureCount; - this.targetController = targetController; - } - - public CreatureCountCondition(int creatureCount, TargetController targetController) { - this.filter = new FilterCreaturePermanent(); - this.creatureCount = creatureCount; - this.targetController = targetController; - - } - - @Override - public boolean apply(Game game, Ability source) { - switch (targetController) { - case YOU: - return game.getBattlefield().countAll(filter, source.getControllerId(), game) == creatureCount; - case OPPONENT: - for (UUID opponent : game.getOpponents(source.getControllerId())) { - if (game.getBattlefield().countAll(filter, opponent, game) != creatureCount) { - return false; - } - } - return true; - case ANY: - return game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) == creatureCount; - default: - throw new UnsupportedOperationException("Value for targetController not supported: " + targetController.toString()); - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - switch (targetController) { - case YOU: - sb.append("you"); - break; - case OPPONENT: - sb.append("your opponents"); - break; - case ANY: - sb.append("if "); - sb.append(creatureCount); - sb.append(' '); - sb.append(filter.getMessage()); - sb.append(" are on the battlefield"); - return sb.toString(); - } - sb.append(" control"); - if (creatureCount == 0) { - sb.append(" no "); - } else { - sb.append(" exactly "); - sb.append(creatureCount); - sb.append(' '); - } - sb.append(filter.getMessage()); - sb.append(creatureCount != 1 ? "s" : ""); - - return sb.toString(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.condition.common; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; + +/** + * + * @author Styxo + */ +public class CreatureCountCondition implements Condition { + + private FilterCreaturePermanent filter; + private int creatureCount; + private TargetController targetController; + + public CreatureCountCondition(FilterCreaturePermanent filter, int creatureCount, TargetController targetController) { + this.filter = filter; + this.creatureCount = creatureCount; + this.targetController = targetController; + } + + public CreatureCountCondition(int creatureCount, TargetController targetController) { + this.filter = new FilterCreaturePermanent(); + this.creatureCount = creatureCount; + this.targetController = targetController; + + } + + @Override + public boolean apply(Game game, Ability source) { + switch (targetController) { + case YOU: + return game.getBattlefield().countAll(filter, source.getControllerId(), game) == creatureCount; + case OPPONENT: + for (UUID opponent : game.getOpponents(source.getControllerId())) { + if (game.getBattlefield().countAll(filter, opponent, game) != creatureCount) { + return false; + } + } + return true; + case ANY: + return game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) == creatureCount; + default: + throw new UnsupportedOperationException("Value for targetController not supported: " + targetController.toString()); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + switch (targetController) { + case YOU: + sb.append("you"); + break; + case OPPONENT: + sb.append("your opponents"); + break; + case ANY: + sb.append("if "); + sb.append(creatureCount); + sb.append(' '); + sb.append(filter.getMessage()); + sb.append(" are on the battlefield"); + return sb.toString(); + } + sb.append(" control"); + if (creatureCount == 0) { + sb.append(" no "); + } else { + sb.append(" exactly "); + sb.append(creatureCount); + sb.append(' '); + } + sb.append(filter.getMessage()); + sb.append(creatureCount != 1 ? "s" : ""); + + return sb.toString(); + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/DashedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/DashedCondition.java index 72b75e44c8..dd7c1e8790 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/DashedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/DashedCondition.java @@ -19,9 +19,9 @@ public enum DashedCondition implements Condition { public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getSourceId()); if (card != null) { - return card.getAbilities().stream() + return card.getAbilities(game).stream() .filter(a -> a instanceof DashAbility) - .anyMatch(d -> ((DashAbility)d).isActivated(source, game)); + .anyMatch(d -> ((DashAbility) d).isActivated(source, game)); } return false; diff --git a/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java index 82103a9f05..7733f475f7 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java @@ -22,7 +22,7 @@ public enum EvokedCondition implements Condition { public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getSourceId()); if (card != null) { - return card.getAbilities().stream() + return card.getAbilities(game).stream() .filter(ab -> ab instanceof EvokeAbility) .anyMatch(evoke -> ((EvokeAbility) evoke).isActivated(source, game)); } diff --git a/Mage/src/main/java/mage/abilities/condition/common/HateCondition.java b/Mage/src/main/java/mage/abilities/condition/common/HateCondition.java index 1db22ed9f8..4d42a696c4 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/HateCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/HateCondition.java @@ -1,29 +1,29 @@ - -package mage.abilities.condition.common; - -import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.game.Game; -import mage.watchers.common.LifeLossOtherFromCombatWatcher; - -/** - * Describes condition when an opponent has been dealt any amount of non-combat - * damage - * - * @author Styxo - */ -public enum HateCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - LifeLossOtherFromCombatWatcher watcher = game.getState().getWatcher(LifeLossOtherFromCombatWatcher.class); - return watcher != null && watcher.opponentLostLifeOtherFromCombat(source.getControllerId(), game); - } - - @Override - public String toString() { - return "if an opponent lost life from source other than combat damage this turn"; - } -} + +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; +import mage.watchers.common.LifeLossOtherFromCombatWatcher; + +/** + * Describes condition when an opponent has been dealt any amount of non-combat + * damage + * + * @author Styxo + */ +public enum HateCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + LifeLossOtherFromCombatWatcher watcher = game.getState().getWatcher(LifeLossOtherFromCombatWatcher.class); + return watcher != null && watcher.opponentLostLifeOtherFromCombat(source.getControllerId(), game); + } + + @Override + public String toString() { + return "if an opponent lost life from source other than combat damage this turn"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/LiveLostLastTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/LiveLostLastTurnCondition.java index 7025aca0bc..fc625230ab 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/LiveLostLastTurnCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/LiveLostLastTurnCondition.java @@ -1,28 +1,28 @@ - -package mage.abilities.condition.common; - -import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.game.Game; -import mage.watchers.WatcherUtils; -import mage.watchers.common.PlayerLostLifeWatcher; - -/** - * - * @author LevelX - */ -public enum LiveLostLastTurnCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); - if (watcher != null) { - return watcher.getLifeLostLastTurn(source.getControllerId()) > 0; - } else { - WatcherUtils.logMissingWatcher(game, source, PlayerLostLifeWatcher.class, this.getClass()); - } - return false; - } -} + +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; +import mage.watchers.WatcherUtils; +import mage.watchers.common.PlayerLostLifeWatcher; + +/** + * + * @author LevelX + */ +public enum LiveLostLastTurnCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); + if (watcher != null) { + return watcher.getLifeLostLastTurn(source.getControllerId()) > 0; + } else { + WatcherUtils.logMissingWatcher(game, source, PlayerLostLifeWatcher.class, this.getClass()); + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/OathbreakerOnBattlefieldCondition.java b/Mage/src/main/java/mage/abilities/condition/common/OathbreakerOnBattlefieldCondition.java index ad26f8a79d..ba27bdfbbc 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/OathbreakerOnBattlefieldCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/OathbreakerOnBattlefieldCondition.java @@ -22,9 +22,9 @@ import java.util.UUID; */ public class OathbreakerOnBattlefieldCondition implements Condition { - private UUID playerId; - private FilterControlledPermanent filter; - private String compatibleNames; + private final UUID playerId; + private final FilterControlledPermanent filter; + private final String compatibleNames; public OathbreakerOnBattlefieldCondition(Game game, UUID playerId, UUID signatureSpellId, Set oathbreakersToSearch) { this.playerId = playerId; @@ -35,17 +35,17 @@ public class OathbreakerOnBattlefieldCondition implements Condition { // spell can be casted by any compatible oathbreakers List compatibleList = new ArrayList<>(); - List compatibleNames = new ArrayList<>(); + List compatibleNamesList = new ArrayList<>(); if (oathbreakersToSearch != null && !oathbreakersToSearch.isEmpty()) { for (UUID id : oathbreakersToSearch) { Card commander = game.getCard(id); if (commander != null && ManaUtil.isColorIdentityCompatible(commander.getColorIdentity(), spellColors)) { compatibleList.add(new PermanentIdPredicate(id)); - compatibleNames.add(commander.getName()); + compatibleNamesList.add(commander.getName()); } } } - this.compatibleNames = String.join("; ", compatibleNames); + this.compatibleNames = String.join("; ", compatibleNamesList); if (compatibleList.isEmpty()) { // random id to disable condition diff --git a/Mage/src/main/java/mage/abilities/condition/common/OpponentHasNoCardsInHandCondition.java b/Mage/src/main/java/mage/abilities/condition/common/OpponentHasNoCardsInHandCondition.java new file mode 100644 index 0000000000..3eaed97bd9 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/OpponentHasNoCardsInHandCondition.java @@ -0,0 +1,36 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author JayDi85 + */ + +public enum OpponentHasNoCardsInHandCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(playerId); + if (opponent != null && opponent.getHand().isEmpty()) { + return true; + } + } + } + return false; + } + + @Override + public String toString() { + return "an opponent has no cards in hand"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/ProwlCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ProwlCondition.java index 638dbbf33b..053e17e777 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/ProwlCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/ProwlCondition.java @@ -1,16 +1,16 @@ - package mage.abilities.condition.common; import mage.abilities.Ability; import mage.abilities.condition.Condition; -import mage.abilities.keyword.ProwlAbility; import mage.cards.Card; +import mage.constants.SubType; import mage.game.Game; +import mage.watchers.common.ProwlWatcher; /** - * Checks if a the spell was cast with the alternate prowl costs + * Is it able to activate prowl cost (damage was made) * - * @author LevelX2 + * @author JayDi85 */ public enum ProwlCondition implements Condition { @@ -18,22 +18,15 @@ public enum ProwlCondition implements Condition { @Override public boolean apply(Game game, Ability source) { + ProwlWatcher watcher = game.getState().getWatcher(ProwlWatcher.class); Card card = game.getCard(source.getSourceId()); - if (card != null) { - for (Ability ability : card.getAbilities()) { - if (ability instanceof ProwlAbility) { - if (((ProwlAbility) ability).isActivated(source, game)) { - return true; - } + if (watcher != null && card != null) { + for (SubType subtype : card.getSubtype(game)) { + if (watcher.hasSubtypeMadeCombatDamage(source.getControllerId(), subtype)) { + return true; } } } return false; } - - @Override - public String toString() { - return "{source}'s prowl cost was paid"; - } - } diff --git a/Mage/src/main/java/mage/abilities/condition/common/ProwlCostWasPaidCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ProwlCostWasPaidCondition.java new file mode 100644 index 0000000000..5b6879fcbf --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/ProwlCostWasPaidCondition.java @@ -0,0 +1,38 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.keyword.ProwlAbility; +import mage.cards.Card; +import mage.game.Game; + +/** + * Checks if a the spell was cast with the alternate prowl costs + * + * @author LevelX2 + */ +public enum ProwlCostWasPaidCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(source.getSourceId()); + if (card != null) { + for (Ability ability : card.getAbilities()) { + if (ability instanceof ProwlAbility) { + if (((ProwlAbility) ability).isActivated(source, game)) { + return true; + } + } + } + } + return false; + } + + @Override + public String toString() { + return "{source}'s prowl cost was paid"; + } + +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/RaidCondition.java b/Mage/src/main/java/mage/abilities/condition/common/RaidCondition.java index efb29099f6..8c03dd92b3 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/RaidCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/RaidCondition.java @@ -21,6 +21,6 @@ public enum RaidCondition implements Condition { @Override public String toString() { - return "if you attacked with a creature this turn"; + return "if you attacked this turn"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java index b4d46d2ade..64f600e444 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java @@ -1,31 +1,43 @@ - package mage.abilities.condition.common; -import java.util.Objects; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.watchers.common.LostControlWatcher; /** - * This condition remembers controller on the first apply. - * As long as this controller keeps unchanged and the source is - * on the battlefield, the condition is true. + * This condition checks if ever since first call of the apply method the + * controller of the source has changed + * + * Monitoring the LOST_CONTROL event has the advantage that also all layered + * effects can correctly check for controller change because comparing old and + * new controller during their apply time does not take into account layered + * change control effects that will be applied later. + * + * This condition needs the LostControlWatcher, so be sure to add it to the card + * that uses the condition. * * @author LevelX2 */ public class SourceOnBattlefieldControlUnchangedCondition implements Condition { - - private UUID controllerId; + + private Long checkingSince; + private int startingZoneChangeCounter; @Override public boolean apply(Game game, Ability source) { - if (controllerId == null) { - controllerId = source.getControllerId(); + if (checkingSince == null) { + checkingSince = System.currentTimeMillis() - 1; + startingZoneChangeCounter = game.getState().getZoneChangeCounter(source.getSourceId()); } - Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); - return (permanent != null && Objects.equals(controllerId, source.getControllerId())); + if (game.getState().getZoneChangeCounter(source.getSourceId()) > startingZoneChangeCounter) { + return false; + } + LostControlWatcher watcher = game.getState().getWatcher(LostControlWatcher.class); + if (watcher != null) { + return checkingSince > watcher.getOrderOfLastLostControl(source.getSourceId()); + } + throw new UnsupportedOperationException("LostControlWatcher not found!"); } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java new file mode 100644 index 0000000000..67fe507a4a --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java @@ -0,0 +1,40 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.constants.Zone; +import mage.game.Game; + +/** + * + * @author LevelX2 + */ +public class SourceRemainsInZoneCondition implements Condition { + + private final Zone zone; + private int timesChangedZones = -1; + + public SourceRemainsInZoneCondition(Zone zone) { + this.zone = zone; + this.timesChangedZones = -1; + } + + @Override + public boolean apply(Game game, Ability source) { + if (timesChangedZones == -1) { // Only changed on first execution + timesChangedZones = game.getState().getZoneChangeCounter(source.getSourceId()); + } + return (timesChangedZones == game.getState().getZoneChangeCounter(source.getSourceId()) + && zone.equals(game.getState().getZone(source.getSourceId()))); + } + + @Override + public String toString() { + return "for as long as {this} remains on the " + zone.toString(); + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/SuspendedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SuspendedCondition.java index dcdd6a8162..ba7dc69d1b 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SuspendedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SuspendedCondition.java @@ -27,10 +27,10 @@ public enum SuspendedCondition implements Condition { public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getSourceId()); if (card != null) { - boolean found = card.getAbilities().stream().anyMatch(ability -> ability instanceof SuspendAbility); + boolean found = card.getAbilities(game).containsClass(SuspendAbility.class); if (!found) { - found = game.getState().getAllOtherAbilities(source.getSourceId()).stream().anyMatch(ability -> ability instanceof SuspendAbility); + found = game.getState().getAllOtherAbilities(source.getSourceId()).containsClass(SuspendAbility.class); } if (found) { diff --git a/Mage/src/main/java/mage/abilities/condition/common/TargetHasCounterCondition.java b/Mage/src/main/java/mage/abilities/condition/common/TargetHasCounterCondition.java index 469a2bede0..da48962f3f 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/TargetHasCounterCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/TargetHasCounterCondition.java @@ -1,72 +1,72 @@ - -package mage.abilities.condition.common; - -import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.cards.Card; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author Styxo - */ -public class TargetHasCounterCondition implements Condition { - - private final CounterType counterType; - private int amount = 1; - private int from = -1; - private int to; - - public TargetHasCounterCondition(CounterType type) { - this.counterType = type; - } - - public TargetHasCounterCondition(CounterType type, int amount) { - this.counterType = type; - this.amount = amount; - } - - public TargetHasCounterCondition(CounterType type, int from, int to) { - this.counterType = type; - this.from = from; - this.to = to; - } - - @Override - @SuppressWarnings("null") - public boolean apply(Game game, Ability source) { - Card card = null; - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); - if (permanent == null) { - card = game.getCard(source.getFirstTarget()); - if (card == null) { - return false; - } - } - if (from != -1) { //range compare - int count; - if (card != null) { - count = card.getCounters(game).getCount(counterType); - } else { - count = permanent.getCounters(game).getCount(counterType); - } - if (to == Integer.MAX_VALUE) { - return count >= from; - } - return count >= from && count <= to; - } else { // single compare (lte) - if (card != null) { - return card.getCounters(game).getCount(counterType) >= amount; - } else { - return permanent.getCounters(game).getCount(counterType) >= amount; - } - } - } - - @Override - public String toString() { - return "if it has a " + counterType.getName() + " on it"; - } -} + +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.cards.Card; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author Styxo + */ +public class TargetHasCounterCondition implements Condition { + + private final CounterType counterType; + private int amount = 1; + private int from = -1; + private int to; + + public TargetHasCounterCondition(CounterType type) { + this.counterType = type; + } + + public TargetHasCounterCondition(CounterType type, int amount) { + this.counterType = type; + this.amount = amount; + } + + public TargetHasCounterCondition(CounterType type, int from, int to) { + this.counterType = type; + this.from = from; + this.to = to; + } + + @Override + @SuppressWarnings("null") + public boolean apply(Game game, Ability source) { + Card card = null; + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + if (permanent == null) { + card = game.getCard(source.getFirstTarget()); + if (card == null) { + return false; + } + } + if (from != -1) { //range compare + int count; + if (card != null) { + count = card.getCounters(game).getCount(counterType); + } else { + count = permanent.getCounters(game).getCount(counterType); + } + if (to == Integer.MAX_VALUE) { + return count >= from; + } + return count >= from && count <= to; + } else { // single compare (lte) + if (card != null) { + return card.getCounters(game).getCount(counterType) >= amount; + } else { + return permanent.getCounters(game).getCount(counterType) >= amount; + } + } + } + + @Override + public String toString() { + return "if it has a " + counterType.getName() + " on it"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java b/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java index d8ab429719..48c0ae17a6 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java @@ -1,8 +1,8 @@ package mage.abilities.condition.common; import mage.abilities.Ability; -import mage.constants.ComparisonType; import mage.abilities.condition.IntCompareCondition; +import mage.constants.ComparisonType; import mage.game.Game; import mage.watchers.common.PlayerGainedLifeWatcher; @@ -27,6 +27,6 @@ public class YouGainedLifeCondition extends IntCompareCondition { @Override public String toString() { - return String.format("if you gained %s or more life this turn ", value + 1); + return String.format("if you gained %s or more life this turn", value + 1); } } diff --git a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java index 25ba6f8f4f..7fc082a423 100644 --- a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java +++ b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java @@ -151,11 +151,11 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter for (AlternativeCost2 alternateCost : alternativeCostsToCheck) { alternateCost.activate(); for (Iterator it = ((Costs) alternateCost).iterator(); it.hasNext(); ) { - Cost costDeailed = (Cost) it.next(); - if (costDeailed instanceof ManaCost) { - ability.getManaCostsToPay().add((ManaCost) costDeailed.copy()); - } else { - ability.getCosts().add(costDeailed.copy()); + Cost costDetailed = (Cost) it.next(); + if (costDetailed instanceof ManaCost) { + ability.getManaCostsToPay().add((ManaCost) costDetailed.copy()); + } else if (costDetailed != null) { + ability.getCosts().add(costDetailed.copy()); } } } @@ -222,7 +222,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter sb.append("pay "); } String text = alternativeCost.getText(true); - sb.append(Character.toLowerCase(text.charAt(0)) + text.substring(1)); + sb.append(Character.toLowerCase(text.charAt(0))).append(text.substring(1)); } ++numberCosts; } diff --git a/Mage/src/main/java/mage/abilities/costs/CostAdjuster.java b/Mage/src/main/java/mage/abilities/costs/CostAdjuster.java index 7ac35fe979..b7bc0d23a5 100644 --- a/Mage/src/main/java/mage/abilities/costs/CostAdjuster.java +++ b/Mage/src/main/java/mage/abilities/costs/CostAdjuster.java @@ -8,5 +8,14 @@ import mage.game.Game; */ public interface CostAdjuster { + /** + * Must check playable and real cast states. + * Example: if it need stack related info (like real targets) then must check two states (game.inCheckPlayableState): + * 1. In playable state it must check all possible use cases (e.g. allow to reduce on any available target and modes) + * 2. In real cast state it must check current use case (e.g. real selected targets and modes) + * + * @param ability + * @param game + */ void adjustCosts(Ability ability, Game game); } diff --git a/Mage/src/main/java/mage/abilities/costs/common/CyclingDiscardCost.java b/Mage/src/main/java/mage/abilities/costs/common/CyclingDiscardCost.java index 522b5943fb..5c846b0bdc 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/CyclingDiscardCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/CyclingDiscardCost.java @@ -1,58 +1,63 @@ - - -/** - * - * @author jeffwadsworth - */ - -package mage.abilities.costs.common; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; -import mage.cards.Card; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; - -public class CyclingDiscardCost extends CostImpl { - - public CyclingDiscardCost() { - } - - public CyclingDiscardCost(CyclingDiscardCost cost) { - super(cost); - } - - @Override - public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - return game.getPlayer(controllerId).getHand().contains(sourceId); - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { - Player player = game.getPlayer(controllerId); - if (player != null) { - Card card = player.getHand().get(sourceId, game); - if (card != null) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CYCLE_CARD, card.getId(), card.getId(), card.getOwnerId())); - paid = player.discard(card, null, game); - if (paid) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CYCLED_CARD, card.getId(), card.getId(), card.getOwnerId())); - } - } - } - return paid; - } - - @Override - public String getText() { - return "Discard this card"; - } - - @Override - public CyclingDiscardCost copy() { - return new CyclingDiscardCost(this); - } -} +package mage.abilities.costs.common; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.cards.Card; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public class CyclingDiscardCost extends CostImpl { + + private MageObjectReference cycledCard = null; + + public CyclingDiscardCost() { + } + + private CyclingDiscardCost(CyclingDiscardCost cost) { + super(cost); + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + return game.getPlayer(controllerId).getHand().contains(sourceId); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Player player = game.getPlayer(controllerId); + if (player != null) { + Card card = player.getHand().get(sourceId, game); + if (card != null) { + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CYCLE_CARD, card.getId(), card.getId(), card.getOwnerId())); + paid = player.discard(card, null, game); + if (paid) { + cycledCard = new MageObjectReference(card, game); + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CYCLED_CARD, card.getId(), card.getId(), card.getOwnerId())); + } + } + } + return paid; + } + + @Override + public String getText() { + return "Discard this card"; + } + + @Override + public CyclingDiscardCost copy() { + return new CyclingDiscardCost(this); + } + + public MageObjectReference getCycledCard() { + return cycledCard; + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/common/DiscardCardCost.java b/Mage/src/main/java/mage/abilities/costs/common/DiscardCardCost.java index e93004e4e2..a20bf922bf 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/DiscardCardCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/DiscardCardCost.java @@ -1,11 +1,9 @@ - package mage.abilities.costs.common; import mage.filter.FilterCard; import mage.target.common.TargetCardInHand; /** - * * @author magenoxx_at_googlemail.com */ public class DiscardCardCost extends DiscardTargetCost { @@ -15,7 +13,7 @@ public class DiscardCardCost extends DiscardTargetCost { } public DiscardCardCost(boolean randomDiscard) { - this(new FilterCard(randomDiscard ?"a card at random":"a card"), randomDiscard); + this(new FilterCard(randomDiscard ? "a card at random" : "a card"), randomDiscard); } public DiscardCardCost(FilterCard filter) { @@ -23,7 +21,7 @@ public class DiscardCardCost extends DiscardTargetCost { } public DiscardCardCost(FilterCard filter, boolean randomDiscard) { - super(new TargetCardInHand(filter), randomDiscard); + super(new TargetCardInHand(filter).withChooseHint("discard cost"), randomDiscard); } public DiscardCardCost(final DiscardCardCost cost) { diff --git a/Mage/src/main/java/mage/abilities/costs/common/DiscardHandCost.java b/Mage/src/main/java/mage/abilities/costs/common/DiscardHandCost.java index 4023d942b4..32a76e3a79 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/DiscardHandCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/DiscardHandCost.java @@ -1,27 +1,23 @@ - - package mage.abilities.costs.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; -import mage.cards.Card; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public class DiscardHandCost extends CostImpl { public DiscardHandCost() { - } - public DiscardHandCost(final DiscardHandCost cost) { + private DiscardHandCost(final DiscardHandCost cost) { super(cost); } @@ -38,12 +34,11 @@ public class DiscardHandCost extends CostImpl { @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { Player player = game.getPlayer(controllerId); - if (player != null) { - for (Card card : player.getHand().getCards(game)) { - player.discard(card, ability, game); - } - paid = true; + if (player == null) { + return paid; } + player.discard(player.getHand(), ability, game); + paid = true; return paid; } diff --git a/Mage/src/main/java/mage/abilities/costs/common/DiscardTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/DiscardTargetCost.java index ff190e15cb..6c786f6ff1 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/DiscardTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/DiscardTargetCost.java @@ -1,20 +1,21 @@ - package mage.abilities.costs.common; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInHand; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public class DiscardTargetCost extends CostImpl { @@ -50,13 +51,11 @@ public class DiscardTargetCost extends CostImpl { if (randomDiscard) { this.cards.addAll(player.discard(amount, true, ability, game).getCards(game)); } else if (targets.choose(Outcome.Discard, controllerId, sourceId, game)) { - for (UUID targetId : targets.get(0).getTargets()) { - Card card = player.getHand().get(targetId, game); - if (card == null) { - return false; - } - player.discard(card, ability, game); - this.cards.add(card); + Cards toDiscard = new CardsImpl(); + toDiscard.addAll(targets.get(0).getTargets()); + Cards discarded = player.discard(toDiscard, ability, game); + if (!discarded.isEmpty()) { + cards.addAll(discarded.getCards(game)); } } paid = cards.size() >= amount; diff --git a/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java index be5c3ba367..8f65b0782f 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java @@ -1,4 +1,3 @@ - package mage.abilities.costs.common; import mage.abilities.Ability; @@ -10,7 +9,6 @@ import mage.players.Player; import mage.target.common.TargetCardInHand; /** - * * @author LevelX2 */ public class DiscardXTargetCost extends VariableCostImpl { diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExertSourceCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExertSourceCost.java index 957921b67f..f17236eb5b 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExertSourceCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExertSourceCost.java @@ -1,54 +1,54 @@ - -package mage.abilities.costs.common; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author emerald000 - */ -public class ExertSourceCost extends CostImpl { - - public ExertSourceCost() { - this.text = "Exert {this}"; - } - - public ExertSourceCost(ExertSourceCost cost) { - super(cost); - } - - @Override - public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - return true; - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { - Player player = game.getPlayer(controllerId); - Permanent permanent = game.getPermanent(sourceId); - if (player != null && permanent != null) { - game.fireEvent(GameEvent.getEvent(EventType.BECOMES_EXERTED, permanent.getId(), permanent.getId(), permanent.getControllerId())); - ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("", permanent.getControllerId()); - effect.setTargetPointer(new FixedTarget(permanent, game)); - game.addEffect(effect, ability); - paid = true; - } - return paid; - } - - @Override - public ExertSourceCost copy() { - return new ExertSourceCost(this); - } -} + +package mage.abilities.costs.common; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author emerald000 + */ +public class ExertSourceCost extends CostImpl { + + public ExertSourceCost() { + this.text = "Exert {this}"; + } + + public ExertSourceCost(ExertSourceCost cost) { + super(cost); + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + return true; + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Player player = game.getPlayer(controllerId); + Permanent permanent = game.getPermanent(sourceId); + if (player != null && permanent != null) { + game.fireEvent(GameEvent.getEvent(EventType.BECOMES_EXERTED, permanent.getId(), permanent.getId(), permanent.getControllerId())); + ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("", permanent.getControllerId()); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, ability); + paid = true; + } + return paid; + } + + @Override + public ExertSourceCost copy() { + return new ExertSourceCost(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java index dd70b3866b..e83cb0045a 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java @@ -36,7 +36,9 @@ public class ExileFromGraveCost extends CostImpl { + CardUtil.numberToText(target.getMaxNumberOfTargets())) + ' ' + target.getTargetName(); } else { - this.text = "Exile " + target.getTargetName(); + this.text = "Exile " + + (target.getTargetName().startsWith("card ") ? "a ":"") + + target.getTargetName(); } if (!this.text.endsWith(" from your graveyard")) { this.text = this.text + " from your graveyard"; diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromGraveCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromGraveCost.java index 0cf07fb3bd..2fcc873a16 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromGraveCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromGraveCost.java @@ -17,7 +17,7 @@ import mage.players.Player; public class ExileSourceFromGraveCost extends CostImpl { public ExileSourceFromGraveCost() { - this.text = "Exile this card from your graveyard"; + this.text = "Exile {this} from your graveyard"; } public ExileSourceFromGraveCost(ExileSourceFromGraveCost cost) { diff --git a/Mage/src/main/java/mage/abilities/costs/common/PayLifeCost.java b/Mage/src/main/java/mage/abilities/costs/common/PayLifeCost.java index 18c902d5af..79f68bb0c6 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PayLifeCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PayLifeCost.java @@ -40,7 +40,7 @@ public class PayLifeCost extends CostImpl { //life total; in other words, the player loses that much life. (Players can always pay 0 life.) int lifeToPayAmount = amount.calculate(game, ability, null); // Paying 0 life is not considered paying any life. - if (lifeToPayAmount > 0 && !game.getPlayer(controllerId).canPayLifeCost()) { + if (lifeToPayAmount > 0 && !game.getPlayer(controllerId).canPayLifeCost(ability)) { return false; } return game.getPlayer(controllerId).getLife() >= lifeToPayAmount || lifeToPayAmount == 0; diff --git a/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java b/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java index 4da6753e7e..6e69eead36 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java @@ -42,7 +42,7 @@ public class PayVariableLifeCost extends VariableCostImpl { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { // Paying 0 life is not considered paying any life, so paying 0 is still allowed - if (game.getPlayer(source.getControllerId()).canPayLifeCost()) { + if (game.getPlayer(source.getControllerId()).canPayLifeCost(source)) { maxValue = controller.getLife(); } } diff --git a/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java b/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java index bd23597969..cd2820b391 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java @@ -1,60 +1,60 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.costs.common; - -import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; -import mage.cards.Card; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetCardInHand; - -import java.util.UUID; - -/** - * @author jeffwadsworth - */ - -public class PutCardFromHandOnTopOfLibraryCost extends CostImpl { - - public PutCardFromHandOnTopOfLibraryCost() { - this.text = "Put a card from your hand on top of your library"; - } - - public PutCardFromHandOnTopOfLibraryCost(PutCardFromHandOnTopOfLibraryCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { - Player controller = game.getPlayer(controllerId); - TargetCardInHand targetCardInHand = new TargetCardInHand(); - targetCardInHand.setRequired(false); - Card card; - if (targetCardInHand.canChoose(controllerId, game) - && controller.choose(Outcome.PreventDamage, targetCardInHand, sourceId, game)) { - card = game.getCard(targetCardInHand.getFirstTarget()); - paid = card != null && controller.moveCardToLibraryWithInfo(card, sourceId, game, Zone.HAND, true, true); - } - return paid; - } - - @Override - public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - Player controller = game.getPlayer(controllerId); - return (controller != null - && !controller.getHand().isEmpty()); - } - - @Override - public PutCardFromHandOnTopOfLibraryCost copy() { - return new PutCardFromHandOnTopOfLibraryCost(this); - } -} - +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.costs.common; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author jeffwadsworth + */ + +public class PutCardFromHandOnTopOfLibraryCost extends CostImpl { + + public PutCardFromHandOnTopOfLibraryCost() { + this.text = "Put a card from your hand on top of your library"; + } + + public PutCardFromHandOnTopOfLibraryCost(PutCardFromHandOnTopOfLibraryCost cost) { + super(cost); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Player controller = game.getPlayer(controllerId); + TargetCardInHand targetCardInHand = new TargetCardInHand(); + targetCardInHand.setRequired(false); + Card card; + if (targetCardInHand.canChoose(controllerId, game) + && controller.choose(Outcome.PreventDamage, targetCardInHand, sourceId, game)) { + card = game.getCard(targetCardInHand.getFirstTarget()); + paid = card != null && controller.moveCardToLibraryWithInfo(card, sourceId, game, Zone.HAND, true, true); + } + return paid; + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + return (controller != null + && !controller.getHand().isEmpty()); + } + + @Override + public PutCardFromHandOnTopOfLibraryCost copy() { + return new PutCardFromHandOnTopOfLibraryCost(this); + } +} + diff --git a/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java b/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java index 232961e449..821e46283f 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java @@ -1,18 +1,17 @@ - package mage.abilities.costs.common; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; import mage.cards.Card; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; + public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl { private final int numberOfCards; @@ -27,7 +26,7 @@ public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl { this.text = setText(); } - public PutTopCardOfYourLibraryToGraveyardCost(PutTopCardOfYourLibraryToGraveyardCost cost) { + private PutTopCardOfYourLibraryToGraveyardCost(final PutTopCardOfYourLibraryToGraveyardCost cost) { super(cost); this.numberOfCards = cost.numberOfCards; this.cardsMovedToGraveyard.addAll(cost.getCardsMovedToGraveyard()); @@ -38,8 +37,7 @@ public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl { Player player = game.getPlayer(controllerId); if (player != null && player.getLibrary().size() >= numberOfCards) { paid = true; - this.cardsMovedToGraveyard.addAll(player.getLibrary().getTopCards(game, numberOfCards)); - player.moveCards(player.getLibrary().getTopCards(game, numberOfCards), Zone.GRAVEYARD, ability, game); + this.cardsMovedToGraveyard.addAll(player.millCards(numberOfCards, ability, game).getCards(game)); } return paid; } @@ -60,13 +58,6 @@ public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl { } private String setText() { - StringBuilder sb = new StringBuilder("Put the top "); - if (numberOfCards == 1) { - sb.append("card"); - } else { - sb.append(CardUtil.numberToText(numberOfCards)).append(" cards"); - } - sb.append(" of your library into your graveyard"); - return sb.toString(); + return "mill " + (numberOfCards == 1 ? "a card" : CardUtil.numberToText(numberOfCards) + " cards"); } } diff --git a/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java b/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java index ab1196345f..4ebaf11923 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java @@ -23,10 +23,10 @@ import java.util.UUID; */ public class RemoveCounterCost extends CostImpl { - private TargetPermanent target; + protected TargetPermanent target; private String name; private CounterType counterTypeToRemove; - private int countersToRemove; + protected int countersToRemove; public RemoveCounterCost(TargetPermanent target) { this(target, null); diff --git a/Mage/src/main/java/mage/abilities/costs/common/ReturnToHandChosenControlledPermanentCost.java b/Mage/src/main/java/mage/abilities/costs/common/ReturnToHandChosenControlledPermanentCost.java index b88a5e445e..c32ba68b22 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ReturnToHandChosenControlledPermanentCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ReturnToHandChosenControlledPermanentCost.java @@ -26,7 +26,7 @@ public class ReturnToHandChosenControlledPermanentCost extends CostImpl { target.setNotTarget(true); this.addTarget(target); if (target.getMaxNumberOfTargets() > 1 && target.getMaxNumberOfTargets() == target.getNumberOfTargets()) { - this.text = "return " + CardUtil.numberToText(target.getMaxNumberOfTargets()) + ' ' + this.text = "Return " + CardUtil.numberToText(target.getMaxNumberOfTargets()) + ' ' + target.getTargetName() + (target.getTargetName().endsWith(" you control") ? "" : " you control") + " to their owner's hand"; diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ActivationManaAbilityStep.java b/Mage/src/main/java/mage/abilities/costs/mana/ActivationManaAbilityStep.java new file mode 100644 index 0000000000..27a89c4766 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/mana/ActivationManaAbilityStep.java @@ -0,0 +1,25 @@ +package mage.abilities.costs.mana; + +/** + * Some special AlternateManaPaymentAbility must be restricted to pay before or after mana abilities. + * Game logic: if you use special mana ability then normal mana abilities must be restricted and vice versa, + * see Convoke for more info and rules + * + * @author JayDi85 + */ + +public enum ActivationManaAbilityStep { + BEFORE(0), // assist + NORMAL(1), // all activated mana abilities + AFTER(2); // convoke, delve, improvise + + private final int stepOrder; + + ActivationManaAbilityStep(int stepOrder) { + this.stepOrder = stepOrder; + } + + public int getStepOrder() { + return stepOrder; + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/mana/AlternateManaPaymentAbility.java b/Mage/src/main/java/mage/abilities/costs/mana/AlternateManaPaymentAbility.java index d9df07ba03..e3769cbcec 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/AlternateManaPaymentAbility.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/AlternateManaPaymentAbility.java @@ -1,18 +1,17 @@ - package mage.abilities.costs.mana; import mage.abilities.Ability; +import mage.abilities.mana.ManaOptions; import mage.game.Game; /** * Interface for abilities that allow the player to pay mana costs of a spell in alternate ways. * For the payment SpecialActions are used. - * + *

* Example of such an alternate payment ability: {@link mage.abilities.keyword.DelveAbility} * - * @author LevelX2 + * @author LevelX2, JayDi85 */ -@FunctionalInterface public interface AlternateManaPaymentAbility { /** * Adds the special action to the state, that allows the user to do the alternate mana payment @@ -22,4 +21,21 @@ public interface AlternateManaPaymentAbility { * @param unpaid unapaid mana costs of the spell */ void addSpecialAction(Ability source, Game game, ManaCost unpaid); + + /** + * All possible mana payments that can make that ability (uses to find playable abilities) + * + * @param source + * @param game + * @param unpaid + * @return + */ + ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid); + + /** + * Mana payment step where you can use it + * + * @return + */ + ActivationManaAbilityStep useOnActivationManaAbilityStep(); } \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java index c92df834dd..9b2848230e 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java @@ -120,7 +120,7 @@ public class ManaCostsImpl extends ArrayList implements M } Player player = game.getPlayer(controllerId); - handleKrrikPhyrexianManaCosts(controllerId, ability, game); + handleLikePhyrexianManaCosts(controllerId, ability, game); // e.g. K'rrik, Son of Yawgmoth if (!player.getManaPool().isForcedToPay()) { assignPayment(game, ability, player.getManaPool(), this); } @@ -150,13 +150,16 @@ public class ManaCostsImpl extends ArrayList implements M */ @Override public boolean payOrRollback(Ability ability, Game game, UUID sourceId, UUID payingPlayerId) { - int bookmark = game.bookmarkState(); - handlePhyrexianManaCosts(payingPlayerId, ability, game); - if (pay(ability, game, sourceId, payingPlayerId, false, null)) { - game.removeBookmark(bookmark); - return true; + Player player = game.getPlayer(payingPlayerId); + if (player != null) { + int bookmark = game.bookmarkState(); + handlePhyrexianManaCosts(payingPlayerId, ability, game); + if (pay(ability, game, sourceId, payingPlayerId, false, null)) { + game.removeBookmark(bookmark); + return true; + } + player.restoreState(bookmark, ability.getRule(), game); } - game.restoreState(bookmark, ability.getRule()); return false; } @@ -170,11 +173,6 @@ public class ManaCostsImpl extends ArrayList implements M while (manaCostIterator.hasNext()) { ManaCost manaCost = manaCostIterator.next(); - PhyrexianManaCost tempPhyrexianCost = null; - Mana mana = manaCost.getMana(); - - FilterMana phyrexianColors = player.getPhyrexianColors(); - if (manaCost instanceof PhyrexianManaCost) { PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost) manaCost; PayLifeCost payLifeCost = new PayLifeCost(2); @@ -189,7 +187,7 @@ public class ManaCostsImpl extends ArrayList implements M tempCosts.pay(source, game, source.getSourceId(), player.getId(), false, null); } - private void handleKrrikPhyrexianManaCosts(UUID payingPlayerId, Ability source, Game game) { + private void handleLikePhyrexianManaCosts(UUID payingPlayerId, Ability source, Game game) { Player player = game.getPlayer(payingPlayerId); if (this == null || player == null) { return; // nothing to be done without any mana costs. prevents NRE from occurring here diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java index 1d0403aa9f..357d41588e 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java @@ -177,6 +177,7 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { /** * Return all effects list, for tests only + * @return */ public List getAllEffects() { List res = new ArrayList<>(); diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.java new file mode 100644 index 0000000000..d5a4ad4095 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.java @@ -0,0 +1,56 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.dynamicvalue; + +import mage.abilities.Ability; +import mage.abilities.effects.Effect; +import mage.game.Game; + +/** + * + * @author LevelX2 + */ +public class LimitedDynamicValue implements DynamicValue { + + private final DynamicValue value; + private final int limit; + + /** + * Returns a dynamic but with an upper limit. + * + * @param limit the max value the dynamic value will return + * @param value the dynamic value to calculate the row dynamic value + */ + public LimitedDynamicValue(int limit, DynamicValue value) { + this.value = value; + this.limit = limit; + } + + LimitedDynamicValue(final LimitedDynamicValue dynamicValue) { + this.value = dynamicValue.value; + this.limit = dynamicValue.limit; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return Math.min(limit, value.calculate(game, sourceAbility, effect)); + } + + @Override + public LimitedDynamicValue copy() { + return new LimitedDynamicValue(this); + } + + @Override + public String toString() { + return value.toString(); + } + + @Override + public String getMessage() { + return value.getMessage(); + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/LockedInDynamicValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/LockedInDynamicValue.java index a156036ac3..54a902dab1 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/LockedInDynamicValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/LockedInDynamicValue.java @@ -1,51 +1,51 @@ - -package mage.abilities.dynamicvalue; - -import mage.abilities.Ability; -import mage.abilities.effects.Effect; -import mage.game.Game; - -/** - * The first calculated value is used as long as the class instance is in use - * - * IMPORTANT: If used the ability / effect that uses a locked in dynamic value - * has to really copy the dnamic value in its copy method (not reference) - * - * @author LevelX2 - */ -public class LockedInDynamicValue implements DynamicValue { - - private boolean valueChecked = false; - private int lockedInValue; - private final DynamicValue basicDynamicValue; - - public LockedInDynamicValue(DynamicValue dynamicValue) { - this.basicDynamicValue = dynamicValue; - } - - public LockedInDynamicValue(LockedInDynamicValue dynamicValue, final boolean copy) { - this.basicDynamicValue = dynamicValue.basicDynamicValue; - this.lockedInValue = dynamicValue.lockedInValue; - this.valueChecked = dynamicValue.valueChecked; - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - if (!valueChecked) { - lockedInValue = basicDynamicValue.calculate(game, sourceAbility, effect); - valueChecked = true; - } - return lockedInValue; - } - - @Override - public LockedInDynamicValue copy() { - return new LockedInDynamicValue(this, true); - } - - @Override - public String getMessage() { - return basicDynamicValue.getMessage(); - } - -} + +package mage.abilities.dynamicvalue; + +import mage.abilities.Ability; +import mage.abilities.effects.Effect; +import mage.game.Game; + +/** + * The first calculated value is used as long as the class instance is in use + * + * IMPORTANT: If used the ability / effect that uses a locked in dynamic value + * has to really copy the dnamic value in its copy method (not reference) + * + * @author LevelX2 + */ +public class LockedInDynamicValue implements DynamicValue { + + private boolean valueChecked = false; + private int lockedInValue; + private final DynamicValue basicDynamicValue; + + public LockedInDynamicValue(DynamicValue dynamicValue) { + this.basicDynamicValue = dynamicValue; + } + + public LockedInDynamicValue(LockedInDynamicValue dynamicValue, final boolean copy) { + this.basicDynamicValue = dynamicValue.basicDynamicValue; + this.lockedInValue = dynamicValue.lockedInValue; + this.valueChecked = dynamicValue.valueChecked; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + if (!valueChecked) { + lockedInValue = basicDynamicValue.calculate(game, sourceAbility, effect); + valueChecked = true; + } + return lockedInValue; + } + + @Override + public LockedInDynamicValue copy() { + return new LockedInDynamicValue(this, true); + } + + @Override + public String getMessage() { + return basicDynamicValue.getMessage(); + } + +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java new file mode 100644 index 0000000000..b2fcb80e22 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java @@ -0,0 +1,39 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.game.Game; +import mage.watchers.common.AbilityResolvedWatcher; + +/** + * @author emerald000 + */ +public enum AbilityResolutionCount implements DynamicValue { + + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class); + if (watcher != null) { + return watcher.getResolutionCount(game, sourceAbility); + } + return 0; + } + + @Override + public AbilityResolutionCount copy() { + return instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "permanents you control"; + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactsYouControlCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactYouControlCount.java similarity index 76% rename from Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactsYouControlCount.java rename to Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactYouControlCount.java index 93613cf69a..4c626a97a9 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactsYouControlCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactYouControlCount.java @@ -9,7 +9,7 @@ import mage.game.Game; /** * @author JayDi85 */ -public enum ArtifactsYouControlCount implements DynamicValue { +public enum ArtifactYouControlCount implements DynamicValue { instance; @@ -19,17 +19,17 @@ public enum ArtifactsYouControlCount implements DynamicValue { } @Override - public ArtifactsYouControlCount copy() { + public ArtifactYouControlCount copy() { return instance; } @Override public String toString() { - return "X"; + return "1"; // uses "for each" effects, so must be 1, not X } @Override public String getMessage() { - return "artifacts you control"; + return "artifact you control"; } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java index fa4ba981a5..5b19df74a5 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java @@ -1,38 +1,60 @@ - package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; +import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public class AttackingCreatureCount implements DynamicValue { private String message; + private FilterCreaturePermanent filter; public AttackingCreatureCount() { this("attacking creature"); } + public AttackingCreatureCount(FilterCreaturePermanent filter) { + this(filter, "attacking " + filter.getMessage()); + } + public AttackingCreatureCount(String message) { + this(null, message); + } + + public AttackingCreatureCount(FilterCreaturePermanent filter, String message) { this.message = message; + this.filter = filter; } public AttackingCreatureCount(final AttackingCreatureCount dynamicValue) { super(); this.message = dynamicValue.message; + this.filter = dynamicValue.filter; } @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { int count = 0; for (CombatGroup combatGroup : game.getCombat().getGroups()) { - count += combatGroup.getAttackers().size(); + for (UUID permId : combatGroup.getAttackers()) { + if (filter != null) { + Permanent attacker = game.getPermanent(permId); + if (attacker != null && filter.match(attacker, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game)) { + count++; + } + } else { + count++; + } + } } return count; } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingFilterCreatureCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingFilterCreatureCount.java deleted file mode 100644 index 5cee8d7544..0000000000 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingFilterCreatureCount.java +++ /dev/null @@ -1,65 +0,0 @@ - -package mage.abilities.dynamicvalue.common; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.combat.CombatGroup; -import mage.game.permanent.Permanent; - -/** - * - * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) - */ -public class AttackingFilterCreatureCount implements DynamicValue { - - private FilterCreaturePermanent filter; - private String message; - - public AttackingFilterCreatureCount(FilterCreaturePermanent filter) { - this(filter, "attacking creature"); - } - - public AttackingFilterCreatureCount(FilterCreaturePermanent filter, String message) { - this.filter = filter; - this.message = message; - } - - public AttackingFilterCreatureCount(final AttackingFilterCreatureCount dynamicValue) { - super(); - this.message = dynamicValue.message; - this.filter = dynamicValue.filter; - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - int count = 0; - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - for (UUID permId : combatGroup.getAttackers()) { - Permanent attacker = game.getPermanent(permId); - if (filter.match(attacker, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game)) { - count++; - } - } - } - return count; - } - - @Override - public AttackingFilterCreatureCount copy() { - return new AttackingFilterCreatureCount(this); - } - - @Override - public String getMessage() { - return message; - } - - @Override - public String toString() { - return "X"; - } -} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java index 32b2d55318..aba073e3f7 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java @@ -1,15 +1,16 @@ package mage.abilities.dynamicvalue.common; +import java.util.HashSet; +import java.util.Set; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.cards.Card; import mage.constants.CardType; import mage.game.Game; +import mage.game.permanent.PermanentToken; import mage.players.Player; -import java.util.EnumSet; - /** * @author JayDi85 */ @@ -21,9 +22,12 @@ public enum CardTypesInGraveyardCount implements DynamicValue { public int calculate(Game game, Ability sourceAbility, Effect effect) { Player controller = game.getPlayer(sourceAbility.getControllerId()); if (controller != null) { - EnumSet foundCardTypes = EnumSet.noneOf(CardType.class); + Set foundCardTypes = new HashSet<>(); for (Card card : controller.getGraveyard().getCards(game)) { - foundCardTypes.addAll(card.getCardType()); + // 4/8/2016 In some rare cases, you can have a token or a copy of a spell in your graveyard at the moment that an object’s delirium ability counts the card types among cards in your graveyard, before that token or copy ceases to exist. Because tokens and copies of spells are not cards, even if they are copies of cards, their types will never be counted. + if (!card.isCopy() && !(card instanceof PermanentToken)) { + foundCardTypes.addAll(card.getCardType()); + } } return foundCardTypes.size(); } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInControllerGraveyardCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInControllerGraveyardCount.java index 3242e4d845..6702e42e1d 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInControllerGraveyardCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInControllerGraveyardCount.java @@ -9,13 +9,12 @@ import mage.game.Game; import mage.players.Player; /** - * * @author North */ public class CardsInControllerGraveyardCount implements DynamicValue { - private FilterCard filter; - private Integer amount; + private final FilterCard filter; + private final Integer amount; public CardsInControllerGraveyardCount() { this(StaticFilters.FILTER_CARD, 1); @@ -39,7 +38,9 @@ public class CardsInControllerGraveyardCount implements DynamicValue { public int calculate(Game game, Ability sourceAbility, Effect effect) { Player player = game.getPlayer(sourceAbility.getControllerId()); if (player != null) { - return amount * player.getGraveyard().count(filter, game); + return amount * player.getGraveyard().count( + filter, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game + ); } return 0; } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderCastCountValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderCastCountValue.java new file mode 100644 index 0000000000..652bb4bb2d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderCastCountValue.java @@ -0,0 +1,35 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.game.Game; +import mage.watchers.common.CommanderPlaysCountWatcher; + +/** + * @author TheElk801 + */ +public enum CommanderCastCountValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability source, Effect effect) { + CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); + return watcher != null ? watcher.getPlayerCount(source.getControllerId()) : 0; + } + + @Override + public CommanderCastCountValue copy() { + return CommanderCastCountValue.instance; + } + + @Override + public String toString() { + return "for each"; + } + + @Override + public String getMessage() { + return "time you've cast a commander from the command zone this game"; + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControllerLifeDividedValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControllerLifeDividedValue.java new file mode 100644 index 0000000000..d6a3138440 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControllerLifeDividedValue.java @@ -0,0 +1,48 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class ControllerLifeDividedValue implements DynamicValue { + + private final Integer divider; + + public ControllerLifeDividedValue(Integer divider) { + this.divider = divider; + } + + public ControllerLifeDividedValue(final ControllerLifeDividedValue dynamicValue) { + this.divider = dynamicValue.divider; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Player p = game.getPlayer(sourceAbility.getControllerId()); + if (p != null) { + return p.getLife() / divider; + } + return 0; + } + + @Override + public ControllerLifeDividedValue copy() { + return new ControllerLifeDividedValue(this); + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return ""; + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CountersCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CountersCount.java index 488ea62955..5922c1608c 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CountersCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CountersCount.java @@ -1,58 +1,58 @@ - -package mage.abilities.dynamicvalue.common; - -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author Styxo - */ -public class CountersCount implements DynamicValue { - - private CounterType counter; - private FilterPermanent filter; - - public CountersCount(CounterType counterType) { - this(counterType, new FilterPermanent()); - } - - public CountersCount(CounterType counter, FilterPermanent filter) { - this.counter = counter; - this.filter = filter; - } - - public CountersCount(final CountersCount countersCount) { - this.counter = countersCount.counter; - this.filter = countersCount.filter; - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - int count = 0; - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { - count += permanent.getCounters(game).getCount(counter); - } - return count; - } - - @Override - public CountersCount copy() { - return new CountersCount(this); - } - - @Override - public String toString() { - return "1"; - } - - @Override - public String getMessage() { - return counter.getName() + " counter on " + filter.getMessage(); - } -} + +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author Styxo + */ +public class CountersCount implements DynamicValue { + + private CounterType counter; + private FilterPermanent filter; + + public CountersCount(CounterType counterType) { + this(counterType, new FilterPermanent()); + } + + public CountersCount(CounterType counter, FilterPermanent filter) { + this.counter = counter; + this.filter = filter; + } + + public CountersCount(final CountersCount countersCount) { + this.counter = countersCount.counter; + this.filter = countersCount.filter; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int count = 0; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { + count += permanent.getCounters(game).getCount(counter); + } + return count; + } + + @Override + public CountersCount copy() { + return new CountersCount(this); + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return counter.getName() + " counter on " + filter.getMessage(); + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/DomainValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/DomainValue.java index cdb7d1360b..c4bc6caaaa 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/DomainValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/DomainValue.java @@ -99,6 +99,6 @@ public class DomainValue implements DynamicValue { @Override public String getMessage() { - return "basic land type among lands " + (countTargetPlayer ? "they control" : "you control"); + return "basic land types among lands " + (countTargetPlayer ? "they control" : "you control"); } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GateYouControlCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GateYouControlCount.java index 2045cdfac2..61a06c39c9 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GateYouControlCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GateYouControlCount.java @@ -32,11 +32,11 @@ public enum GateYouControlCount implements DynamicValue { @Override public String toString() { - return "X"; + return "1"; // uses "for each" effects, so must be 1, not X } @Override public String getMessage() { - return "gate you control"; + return "Gate you control"; } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestPowerAmongControlledCreaturesValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestPowerAmongControlledCreaturesValue.java index 8989267cc2..98ad23a0ea 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestPowerAmongControlledCreaturesValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestPowerAmongControlledCreaturesValue.java @@ -1,44 +1,44 @@ - -package mage.abilities.dynamicvalue.common; - -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * @author Styxo - */ -public enum GreatestPowerAmongControlledCreaturesValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - int amount = 0; - for (Permanent p : game.getBattlefield().getActivePermanents( - StaticFilters.FILTER_CONTROLLED_CREATURE, sourceAbility.getControllerId(), game - )) { - amount = Math.max(p.getPower().getValue(), amount); - } - return amount; - } - - @Override - public GreatestPowerAmongControlledCreaturesValue copy() { - return GreatestPowerAmongControlledCreaturesValue.instance; - } - - @Override - public String getMessage() { - return "the greatest power among creatures you control"; - } - - @Override - public String toString() { - return "X"; - } - -} + +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * @author Styxo + */ +public enum GreatestPowerAmongControlledCreaturesValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int amount = 0; + for (Permanent p : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, sourceAbility.getControllerId(), game + )) { + amount = Math.max(p.getPower().getValue(), amount); + } + return amount; + } + + @Override + public GreatestPowerAmongControlledCreaturesValue copy() { + return GreatestPowerAmongControlledCreaturesValue.instance; + } + + @Override + public String getMessage() { + return "the greatest power among creatures you control"; + } + + @Override + public String toString() { + return "X"; + } + +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestToughnessAmongControlledCreaturesValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestToughnessAmongControlledCreaturesValue.java index 2ffddc55f0..6889642901 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestToughnessAmongControlledCreaturesValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestToughnessAmongControlledCreaturesValue.java @@ -1,43 +1,43 @@ - -package mage.abilities.dynamicvalue.common; - -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * @author TheElk801 - */ -public enum GreatestToughnessAmongControlledCreaturesValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - int amount = 0; - for (Permanent p : game.getBattlefield().getActivePermanents( - StaticFilters.FILTER_CONTROLLED_CREATURE, sourceAbility.getControllerId(), game - )) { - amount = Math.max(p.getToughness().getValue(), amount); - } - return amount; - } - - @Override - public GreatestToughnessAmongControlledCreaturesValue copy() { - return GreatestToughnessAmongControlledCreaturesValue.instance; - } - - @Override - public String getMessage() { - return "the greatest toughness among creatures you control"; - } - - @Override - public String toString() { - return "X"; - } - -} + +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * @author TheElk801 + */ +public enum GreatestToughnessAmongControlledCreaturesValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int amount = 0; + for (Permanent p : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, sourceAbility.getControllerId(), game + )) { + amount = Math.max(p.getToughness().getValue(), amount); + } + return amount; + } + + @Override + public GreatestToughnessAmongControlledCreaturesValue copy() { + return GreatestToughnessAmongControlledCreaturesValue.instance; + } + + @Override + public String getMessage() { + return "the greatest toughness among creatures you control"; + } + + @Override + public String toString() { + return "X"; + } + +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestCMCOfPermanentValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestCMCOfPermanentValue.java new file mode 100644 index 0000000000..c83a523b29 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestCMCOfPermanentValue.java @@ -0,0 +1,67 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class HighestCMCOfPermanentValue implements DynamicValue { + + private final FilterPermanent filter; + private final boolean onlyIfCanBeSacrificed; + + public HighestCMCOfPermanentValue(FilterPermanent filter, boolean onlyIfCanBeSacrificed) { + super(); + this.filter = filter; + this.onlyIfCanBeSacrificed = onlyIfCanBeSacrificed; + } + + public HighestCMCOfPermanentValue(final HighestCMCOfPermanentValue dynamicValue) { + this.filter = dynamicValue.filter; + this.onlyIfCanBeSacrificed = dynamicValue.onlyIfCanBeSacrificed; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int value = 0; + Player controller = game.getPlayer(sourceAbility.getControllerId()); + if (controller != null) { + for (Permanent permanent : game.getBattlefield() + .getActivePermanents(filter, sourceAbility.getControllerId(), sourceAbility.getSourceId(), game)) { + if ((!onlyIfCanBeSacrificed || controller.canPaySacrificeCost(permanent, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game)) + && permanent.getConvertedManaCost() > value) { + value = permanent.getConvertedManaCost(); + } + + } + } + return value; + } + + @Override + public HighestCMCOfPermanentValue copy() { + return new HighestCMCOfPermanentValue(this); + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return filter.getMessage(); + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java index 087f375dc0..a9a85c54a3 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java @@ -24,6 +24,11 @@ public class PermanentsOnBattlefieldCount implements DynamicValue { this(filter, 1); } + /** + * + * @param filter + * @param multiplier + */ public PermanentsOnBattlefieldCount(FilterPermanent filter, Integer multiplier) { this.filter = filter; this.multiplier = multiplier; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceMutatedCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceMutatedCount.java new file mode 100644 index 0000000000..349828a0a5 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceMutatedCount.java @@ -0,0 +1,35 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public enum SourceMutatedCount implements DynamicValue { + + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + // TODO: Implement this + return 0; + } + + @Override + public SourceMutatedCount copy() { + return instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "the number of times this creature has mutated"; + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentPowerCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentPowerCount.java index af4effc0f0..e48bfa0451 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentPowerCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourcePermanentPowerCount.java @@ -31,7 +31,9 @@ public class SourcePermanentPowerCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { Permanent sourcePermanent = game.getPermanent(sourceAbility.getSourceId()); - if (sourcePermanent == null || sourcePermanent.getZoneChangeCounter(game) > sourceAbility.getSourceObjectZoneChangeCounter()) { + if (sourcePermanent == null + || (sourceAbility.getSourceObjectZoneChangeCounter() > 0 + && sourcePermanent.getZoneChangeCounter(game) > sourceAbility.getSourceObjectZoneChangeCounter())) { sourcePermanent = (Permanent) game.getLastKnownInformation(sourceAbility.getSourceId(), Zone.BATTLEFIELD); } if (sourcePermanent != null diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/UrzaTerrainValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/UrzaTerrainValue.java index d5d6fbbdc0..06168a722d 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/UrzaTerrainValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/UrzaTerrainValue.java @@ -4,49 +4,56 @@ import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; -public class UrzaTerrainValue implements DynamicValue { +public enum UrzaTerrainValue implements DynamicValue { + MINE(2, SubType.MINE), + TOWER(3, SubType.TOWER), + POWER_PLANT(2, SubType.POWER_PLANT); private final int value; + private final SubType subType; - public UrzaTerrainValue(int value) { + private static final FilterPermanent mineFilter = new FilterControlledPermanent(SubType.MINE); + private static final FilterPermanent towerFilter = new FilterControlledPermanent(SubType.TOWER); + private static final FilterPermanent powerPlantFilter = new FilterControlledPermanent(SubType.POWER_PLANT); + + static { + mineFilter.add(SubType.URZAS.getPredicate()); + towerFilter.add(SubType.URZAS.getPredicate()); + powerPlantFilter.add(SubType.URZAS.getPredicate()); + } + + UrzaTerrainValue(int value, SubType subType) { this.value = value; + this.subType = subType; } @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - FilterControlledPermanent pp = new FilterControlledPermanent("Urza's Power Plant"); - pp.add(SubType.URZAS.getPredicate()); - pp.add(SubType.POWER_PLANT.getPredicate()); - PermanentsOnBattlefieldCount ppP = new PermanentsOnBattlefieldCount(pp); - if (ppP.calculate(game, sourceAbility, effect) < 1) { + if (subType != SubType.MINE && game.getBattlefield().count( + mineFilter, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game + ) < 1) { return 1; } - - FilterControlledPermanent to = new FilterControlledPermanent("Urza's Tower"); - to.add(SubType.URZAS.getPredicate()); - to.add(SubType.TOWER.getPredicate()); - PermanentsOnBattlefieldCount toP = new PermanentsOnBattlefieldCount(to); - if (toP.calculate(game, sourceAbility, effect) < 1) { + if (subType != SubType.TOWER && game.getBattlefield().count( + towerFilter, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game + ) < 1) { return 1; } - - FilterControlledPermanent mi = new FilterControlledPermanent("Urza's Mine"); - mi.add(SubType.URZAS.getPredicate()); - mi.add(SubType.MINE.getPredicate()); - PermanentsOnBattlefieldCount miP = new PermanentsOnBattlefieldCount(mi); - if (miP.calculate(game, sourceAbility, effect) < 1) { + if (subType != SubType.POWER_PLANT && game.getBattlefield().count( + powerPlantFilter, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game + ) < 1) { return 1; } - return value; } @Override public UrzaTerrainValue copy() { - return new UrzaTerrainValue(value); + return this; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/ApplyCountersEffect.java b/Mage/src/main/java/mage/abilities/effects/ApplyCountersEffect.java index 61d76e0215..3d287de846 100644 --- a/Mage/src/main/java/mage/abilities/effects/ApplyCountersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/ApplyCountersEffect.java @@ -1,45 +1,57 @@ - - package mage.abilities.effects; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; import mage.abilities.Ability; +import mage.constants.*; +import mage.counters.AbilityCounter; import mage.counters.BoostCounter; import mage.game.Game; import mage.game.permanent.Permanent; /** - * * @author BetaSteward_at_googlemail.com */ public class ApplyCountersEffect extends ContinuousEffectImpl { - public ApplyCountersEffect() { - super(Duration.EndOfGame, Layer.PTChangingEffects_7, SubLayer.Counters_7d, Outcome.BoostCreature); + ApplyCountersEffect() { + super(Duration.EndOfGame, Outcome.BoostCreature); } - public ApplyCountersEffect(ApplyCountersEffect effect) { + private ApplyCountersEffect(ApplyCountersEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - for (Permanent permanent: game.getBattlefield().getAllActivePermanents(CardType.CREATURE)) { - for (BoostCounter counter: permanent.getCounters(game).getBoostCounters()) { - permanent.addPower(counter.getPower() * counter.getCount()); - permanent.addToughness(counter.getToughness() * counter.getCount()); + return false; + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + if (layer == Layer.AbilityAddingRemovingEffects_6) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { + for (AbilityCounter counter : permanent.getCounters(game).getAbilityCounters()) { + permanent.addAbility(counter.getAbility(), source == null ? permanent.getId() : source.getSourceId(), game); + } + } + } + if (layer == Layer.PTChangingEffects_7 && sublayer == SubLayer.Counters_7d) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(CardType.CREATURE)) { + for (BoostCounter counter : permanent.getCounters(game).getBoostCounters()) { + permanent.addPower(counter.getPower() * counter.getCount()); + permanent.addToughness(counter.getToughness() * counter.getCount()); + } } } return true; } + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.PTChangingEffects_7 || layer == Layer.AbilityAddingRemovingEffects_6; + } + @Override public ApplyCountersEffect copy() { return new ApplyCountersEffect(this); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java index d17ca0e263..52bf7f4436 100644 --- a/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java @@ -7,6 +7,9 @@ import mage.constants.*; import mage.game.Game; import java.util.UUID; +import mage.cards.SplitCard; +import mage.cards.SplitCardHalf; +import mage.players.Player; /** * @author BetaSteward_at_googlemail.com @@ -42,7 +45,14 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements } /** - * Helper to check that affectedAbility is compatible for alternative cast modifications by setCastSourceIdWithAlternateMana + * Helper to check that affectedAbility is compatible for alternative cast + * modifications by setCastSourceIdWithAlternateMana + * + * @param cardToCheck + * @param affectedAbilityToCheck + * @param playerToCheck + * @param source + * @return */ public boolean isAbilityAppliedForAlternateCast(Card cardToCheck, Ability affectedAbilityToCheck, UUID playerToCheck, Ability source) { return cardToCheck != null @@ -52,4 +62,35 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements && (affectedAbilityToCheck.getAbilityType() == AbilityType.SPELL || affectedAbilityToCheck.getAbilityType() == AbilityType.PLAY_LAND); } + + /** + * Internal method to do the neccessary to allow the card from objectId to be cast or played (if it's a land) without paying any mana. + * Additional costs (like sacrificing or discarding) have still to be payed. + * Checks if the card is of the correct type or in the correct zone have to be done before. + * + * @param objectId sourceId of the card to play + * @param source source ability that allows this effect + * @param affectedControllerId player allowed to play the card + * @param game + * @return + */ + protected boolean allowCardToPlayWithoutMana(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + Player player = game.getPlayer(affectedControllerId); + Card card = game.getCard(objectId); + if (card == null || player == null) { + return false; + } + if (!card.isLand()) { + if (card instanceof SplitCard) { + SplitCardHalf leftCard = ((SplitCard) card).getLeftHalfCard(); + player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts()); + SplitCardHalf rightCard = ((SplitCard) card).getRightHalfCard(); + player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCard.getSpellAbility().getCosts()); + } else { + player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); + } + } + return true; + } + } diff --git a/Mage/src/main/java/mage/abilities/effects/AuraReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/AuraReplacementEffect.java index 2369835049..8ad5ee3acd 100644 --- a/Mage/src/main/java/mage/abilities/effects/AuraReplacementEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/AuraReplacementEffect.java @@ -1,5 +1,6 @@ package mage.abilities.effects; +import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -17,8 +18,6 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInGraveyard; -import java.util.UUID; - /** * Cards with the Aura subtype don't change the zone they are in, if there is no * valid target on the battlefield. Also, when entering the battlefield and it @@ -163,7 +162,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { PermanentCard permanent = new PermanentCard(card, (controllingPlayer == null ? card.getOwnerId() : controllingPlayer.getId()), game); ZoneChangeEvent zoneChangeEvent = new ZoneChangeEvent(permanent, controllerId, fromZone, Zone.BATTLEFIELD); permanent.updateZoneChangeCounter(game, zoneChangeEvent); - game.getBattlefield().addPermanent(permanent); + game.addPermanent(permanent, 0); card.setZone(Zone.BATTLEFIELD, game); if (permanent.entersBattlefield(event.getSourceId(), game, fromZone, true)) { if (targetCard != null) { @@ -196,9 +195,9 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { return card != null && (card.isEnchantment() && card.hasSubtype(SubType.AURA, game) || // in case of transformable enchantments (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId()) != null - && card.getSecondCardFace() != null - && card.getSecondCardFace().isEnchantment() - && card.getSecondCardFace().hasSubtype(SubType.AURA, game))); + && card.getSecondCardFace() != null + && card.getSecondCardFace().isEnchantment() + && card.getSecondCardFace().hasSubtype(SubType.AURA, game))); } return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java b/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java new file mode 100644 index 0000000000..cf38222a36 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java @@ -0,0 +1,115 @@ +package mage.abilities.effects; + +import mage.abilities.Ability; +import mage.cards.Card; +import mage.constants.AsThoughEffectType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +public class CastCardFromGraveyardThenExileItEffect extends OneShotEffect { + + public CastCardFromGraveyardThenExileItEffect() { + super(Outcome.Benefit); + } + + CastCardFromGraveyardThenExileItEffect(final CastCardFromGraveyardThenExileItEffect effect) { + super(effect); + } + + @Override + public CastCardFromGraveyardThenExileItEffect copy() { + return new CastCardFromGraveyardThenExileItEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); + if (card != null) { + ContinuousEffect effect = new CastCardFromGraveyardEffect(); + effect.setTargetPointer(new FixedTarget(card, game)); + game.addEffect(effect, source); + effect = new ExileReplacementEffect(card.getId()); + game.addEffect(effect, source); + return true; + } + return false; + } +} + +class CastCardFromGraveyardEffect extends AsThoughEffectImpl { + + CastCardFromGraveyardEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); + this.staticText = "You may cast target card from your graveyard"; + } + + CastCardFromGraveyardEffect(final CastCardFromGraveyardEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public CastCardFromGraveyardEffect copy() { + return new CastCardFromGraveyardEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + return objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId()); + } +} + +class ExileReplacementEffect extends ReplacementEffectImpl { + + private final UUID cardId; + + ExileReplacementEffect(UUID cardId) { + super(Duration.EndOfTurn, Outcome.Exile); + this.cardId = cardId; + this.staticText = "If that card would be put into your graveyard this turn, exile it instead"; + } + + ExileReplacementEffect(final ExileReplacementEffect effect) { + super(effect); + this.cardId = effect.cardId; + } + + @Override + public ExileReplacementEffect copy() { + return new ExileReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(this.cardId); + if (controller != null && card != null) { + return controller.moveCards(card, Zone.EXILED, source, game); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.getToZone() == Zone.GRAVEYARD + && zEvent.getTargetId().equals(this.cardId); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java index cdea7ccc33..11a001c527 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java @@ -1,5 +1,9 @@ package mage.abilities.effects; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; import mage.constants.DependencyType; @@ -9,11 +13,6 @@ import mage.constants.SubLayer; import mage.game.Game; import mage.target.targetpointer.TargetPointer; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; - /** * @author BetaSteward_at_googlemail.com */ @@ -67,8 +66,6 @@ public interface ContinuousEffect extends Effect { UUID getStartingController(); - void incYourTurnNumPlayed(); - boolean isYourNextTurn(Game game); @Override diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java index fd3f20e163..37671eed41 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java @@ -4,6 +4,7 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.CompoundAbility; import mage.abilities.MageSingleton; +import mage.abilities.costs.mana.ActivationManaAbilityStep; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.DomainValue; import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; @@ -47,9 +48,9 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu protected boolean characterDefining = false; // until your next turn or until end of your next turn - private UUID startingControllerId; // player to checkss turns (can't different with real controller ability) - private boolean startingTurnWasActive; - private int yourTurnNumPlayed = 0; // turnes played after effect was created + private UUID startingControllerId; // player to check for turn duration (can't different with real controller ability) + private boolean startingTurnWasActive; // effect started during related players turn and related players turn was already active + private int effectStartingOnTurn = 0; // turn the effect started public ContinuousEffectImpl(Duration duration, Outcome outcome) { super(outcome); @@ -79,7 +80,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu this.temporary = effect.temporary; this.startingControllerId = effect.startingControllerId; this.startingTurnWasActive = effect.startingTurnWasActive; - this.yourTurnNumPlayed = effect.yourTurnNumPlayed; + this.effectStartingOnTurn = effect.effectStartingOnTurn; this.dependencyTypes = effect.dependencyTypes; this.dependendToTypes = effect.dependendToTypes; this.characterDefining = effect.characterDefining; @@ -191,23 +192,13 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu this.startingControllerId = startingController; this.startingTurnWasActive = activePlayerId != null && activePlayerId.equals(startingController); // you can't use "game" for active player cause it's called from tests/cheat too - this.yourTurnNumPlayed = 0; - } - - @Override - public void incYourTurnNumPlayed() { - yourTurnNumPlayed++; + this.effectStartingOnTurn = game.getTurnNum(); } @Override public boolean isYourNextTurn(Game game) { - if (this.startingTurnWasActive) { - return yourTurnNumPlayed == 1 - && game.isActivePlayer(startingControllerId); - } else { - return yourTurnNumPlayed == 0 - && game.isActivePlayer(startingControllerId); - } + return effectStartingOnTurn < game.getTurnNum() + && game.isActivePlayer(startingControllerId); } @Override @@ -367,6 +358,9 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu /** * Auto-generates dependencies on different effects (what's apply first and * what's apply second) + * + * @param abilityToGain + * @param filterToSearch */ public void generateGainAbilityDependencies(Ability abilityToGain, Filter filterToSearch) { this.addDependencyType(DependencyType.AddingAbility); @@ -424,7 +418,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu StackObject stackObject = game.getStack().getFirst(); return !(stackObject instanceof Spell) || !Zone.LIBRARY.equals(((Spell) stackObject).getFromZone()) - || ((Spell) stackObject).isDoneActivatingManaAbilities(); + || ((Spell) stackObject).getCurrentActivatingManaAbilitiesStep() == ActivationManaAbilityStep.AFTER; // mana payment finished } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index d7993b3fa9..6e85bc4e22 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -4,7 +4,6 @@ import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.MageSingleton; -import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect; import mage.abilities.effects.common.continuous.CommanderReplacementEffect; @@ -24,6 +23,7 @@ import mage.game.stack.Spell; import mage.players.ManaPoolItem; import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import org.apache.log4j.Logger; import java.io.Serializable; @@ -52,27 +52,21 @@ public class ContinuousEffects implements Serializable { private ContinuousEffectsList spliceCardEffects = new ContinuousEffectsList<>(); private final Map> asThoughEffectsMap = new EnumMap<>(AsThoughEffectType.class); - public final List> allEffectsLists = new ArrayList<>(); + public final List> allEffectsLists = new ArrayList<>(); // contains refs to real effect's list private final ApplyCountersEffect applyCounters; - // private final PlaneswalkerRedirectionEffect planeswalkerRedirectionEffect; private final AuraReplacementEffect auraReplacementEffect; - private final List previous = new ArrayList<>(); - - // note all effect/abilities that were only added temporary - private final Map> temporaryEffects = new HashMap<>(); + private final Map> lastEffectsListOnLayer = new HashMap<>(); // helps to find out new effect timestamps on layers public ContinuousEffects() { applyCounters = new ApplyCountersEffect(); -// planeswalkerRedirectionEffect = new PlaneswalkerRedirectionEffect(); auraReplacementEffect = new AuraReplacementEffect(); collectAllEffects(); } public ContinuousEffects(final ContinuousEffects effect) { - this.applyCounters = effect.applyCounters.copy(); -// this.planeswalkerRedirectionEffect = effect.planeswalkerRedirectionEffect.copy(); - this.auraReplacementEffect = effect.auraReplacementEffect.copy(); + applyCounters = effect.applyCounters.copy(); + auraReplacementEffect = effect.auraReplacementEffect.copy(); layeredEffects = effect.layeredEffects.copy(); continuousRuleModifyingEffects = effect.continuousRuleModifyingEffects.copy(); replacementEffects = effect.replacementEffects.copy(); @@ -86,8 +80,8 @@ public class ContinuousEffects implements Serializable { costModificationEffects = effect.costModificationEffects.copy(); spliceCardEffects = effect.spliceCardEffects.copy(); - for (Map.Entry> entry : effect.temporaryEffects.entrySet()) { - temporaryEffects.put(entry.getKey().copy(), entry.getValue()); + for (Map.Entry> entry : effect.lastEffectsListOnLayer.entrySet()) { + lastEffectsListOnLayer.put(entry.getKey(), entry.getValue().copy()); } collectAllEffects(); order = effect.order; @@ -163,21 +157,20 @@ public class ContinuousEffects implements Serializable { spliceCardEffects.removeInactiveEffects(game); } - public synchronized void incYourTurnNumPlayed(Game game) { - layeredEffects.incYourTurnNumPlayed(game); - continuousRuleModifyingEffects.incYourTurnNumPlayed(game); - replacementEffects.incYourTurnNumPlayed(game); - preventionEffects.incYourTurnNumPlayed(game); - requirementEffects.incYourTurnNumPlayed(game); - restrictionEffects.incYourTurnNumPlayed(game); - for (ContinuousEffectsList asThoughtlist : asThoughEffectsMap.values()) { - asThoughtlist.incYourTurnNumPlayed(game); - } - costModificationEffects.incYourTurnNumPlayed(game); - spliceCardEffects.incYourTurnNumPlayed(game); + public synchronized List getLayeredEffects(Game game) { + return getLayeredEffects(game, "main"); } - public synchronized List getLayeredEffects(Game game) { + /** + * Return effects list ordered by timestamps (timestamps are automaticity + * generates from new/old lists on same layer) + * + * @param game + * @param timestampGroupName workaround to fix broken timestamps on effect's + * add/remove between different layers + * @return effects list ordered by timestamp + */ + public synchronized List getLayeredEffects(Game game, String timestampGroupName) { List layerEffects = new ArrayList<>(); for (ContinuousEffect effect : layeredEffects) { switch (effect.getDuration()) { @@ -202,9 +195,14 @@ public class ContinuousEffects implements Serializable { } } - updateTimestamps(layerEffects); + updateTimestamps(timestampGroupName, layerEffects); + layerEffects.sort(Comparator.comparingLong(ContinuousEffect::getOrder)); + /* debug effects apply order: + if (game.getStep() != null) System.out.println("layr - " + game.getTurnNum() + "." + game.getStep().getType() + ": layers " + layerEffects.size() + + " - " + layerEffects.stream().map(l -> l.getClass().getSimpleName()).collect(Collectors.joining(", ")) + + " - " + callName); + //*/ - Collections.sort(layerEffects, Comparator.comparingLong(ContinuousEffect::getOrder)); return layerEffects; } @@ -214,18 +212,26 @@ public class ContinuousEffects implements Serializable { * "actual" meaning it becomes turned on that is defined by * Ability.#isInUseableZone(Game, boolean) method in * #getLayeredEffects(Game). + *

+ * It must be called with different timestamp group name (otherwise sort + * order will be changed for add/remove effects, see Urborg and Bloodmoon + * test) * * @param layerEffects */ - private synchronized void updateTimestamps(List layerEffects) { + private synchronized void updateTimestamps(String timestampGroupName, List layerEffects) { + if (!lastEffectsListOnLayer.containsKey(timestampGroupName)) { + lastEffectsListOnLayer.put(timestampGroupName, new ContinuousEffectsList<>()); + } + ContinuousEffectsList prevs = lastEffectsListOnLayer.get(timestampGroupName); for (ContinuousEffect continuousEffect : layerEffects) { // check if it's new, then set order - if (!previous.contains(continuousEffect)) { + if (!prevs.contains(continuousEffect)) { setOrder(continuousEffect); } } - previous.clear(); - previous.addAll(layerEffects); + prevs.clear(); + prevs.addAll(layerEffects); } public void setOrder(ContinuousEffect effect) { @@ -330,15 +336,12 @@ public class ContinuousEffects implements Serializable { */ private Map> getApplicableReplacementEffects(GameEvent event, Game game) { Map> replaceEffects = new HashMap<>(); -// if (planeswalkerRedirectionEffect.checksEventType(event, game) && planeswalkerRedirectionEffect.applies(event, null, game)) { -// replaceEffects.put(planeswalkerRedirectionEffect, null); -// } if (auraReplacementEffect.checksEventType(event, game) && auraReplacementEffect.applies(event, null, game)) { replaceEffects.put(auraReplacementEffect, null); } // boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT); //get all applicable transient Replacement effects - for (Iterator iterator = replacementEffects.iterator(); iterator.hasNext();) { + for (Iterator iterator = replacementEffects.iterator(); iterator.hasNext(); ) { ReplacementEffect effect = iterator.next(); if (!effect.checksEventType(event, game)) { continue; @@ -371,7 +374,7 @@ public class ContinuousEffects implements Serializable { } } - for (Iterator iterator = preventionEffects.iterator(); iterator.hasNext();) { + for (Iterator iterator = preventionEffects.iterator(); iterator.hasNext(); ) { PreventionEffect effect = iterator.next(); if (!effect.checksEventType(event, game)) { continue; @@ -425,12 +428,12 @@ public class ContinuousEffects implements Serializable { return false; } boolean exists = true; - if (!object.getAbilities().contains(ability)) { + if (!object.hasAbility(ability, game)) { exists = false; if (object instanceof PermanentCard) { PermanentCard permanent = (PermanentCard) object; if (permanent.isTransformable() && event.getType() == GameEvent.EventType.TRANSFORMED) { - exists = permanent.getCard().getAbilities().contains(ability); + exists = permanent.getCard().hasAbility(ability, game); } } } else if (object instanceof PermanentCard) { @@ -682,10 +685,17 @@ public class ContinuousEffects implements Serializable { * @param game */ public void applySpliceEffects(Ability abilityToModify, Game game) { - if (((SpellAbility) abilityToModify).getSpellAbilityType() == SpellAbilityType.SPLICE) { - // on a spliced ability of a spell can't be spliced again + // add effects from splice card to spell ability on activate/cast + + // splice spell - spell can't be spliced again + if (CardUtil.isSpliceAbility(abilityToModify, game)) { return; } + // fused spell - can be spliced only to main fused ability, not to parts + if (CardUtil.isFusedPartAbility(abilityToModify, game)) { + return; + } + List spliceEffects = getApplicableSpliceCardEffects(game, abilityToModify.getControllerId()); // get the applyable splice abilities List spliceAbilities = new ArrayList<>(); @@ -740,8 +750,8 @@ public class ContinuousEffects implements Serializable { * @param event * @param targetAbility ability the event is attached to. can be null. * @param game - * @param silentMode true if the event does not really happen but it's - * checked if the event would be replaced + * @param silentMode true if the event does not really happen but it's + * checked if the event would be replaced * @return */ public boolean preventedByRuleModification(GameEvent event, Ability targetAbility, Game game, boolean silentMode) { @@ -789,7 +799,7 @@ public class ContinuousEffects implements Serializable { do { Map> rEffects = getApplicableReplacementEffects(event, game); // Remove all consumed effects (ability dependant) - for (Iterator it1 = rEffects.keySet().iterator(); it1.hasNext();) { + for (Iterator it1 = rEffects.keySet().iterator(); it1.hasNext(); ) { ReplacementEffect entry = it1.next(); if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9. Set consumedAbilitiesIds = consumed.get(entry.getId()); @@ -903,7 +913,7 @@ public class ContinuousEffects implements Serializable { //20091005 - 613 public synchronized void apply(Game game) { removeInactiveEffects(game); - List activeLayerEffects = getLayeredEffects(game); + List activeLayerEffects = getLayeredEffects(game); // main call List layer = filterLayeredEffects(activeLayerEffects, Layer.CopyEffects_1); for (ContinuousEffect effect : layer) { @@ -914,7 +924,7 @@ public class ContinuousEffects implements Serializable { } //Reload layerEffect if copy effects were applied if (!layer.isEmpty()) { - activeLayerEffects = getLayeredEffects(game); + activeLayerEffects = getLayeredEffects(game, "layer_1"); } layer = filterLayeredEffects(activeLayerEffects, Layer.ControlChangingEffects_2); @@ -936,73 +946,101 @@ public class ContinuousEffects implements Serializable { game.getBattlefield().resetPermanentsControl(); } - applyLayer(activeLayerEffects, Layer.TextChangingEffects_3, game); - applyLayer(activeLayerEffects, Layer.TypeChangingEffects_4, game); - applyLayer(activeLayerEffects, Layer.ColorChangingEffects_5, game); + applyLayer(activeLayerEffects, Layer.TextChangingEffects_3, game, "layer_3"); + applyLayer(activeLayerEffects, Layer.TypeChangingEffects_4, game, "layer_4"); + applyLayer(activeLayerEffects, Layer.ColorChangingEffects_5, game, "layer_5"); Map> appliedEffectAbilities = new HashMap<>(); boolean done = false; Map> waitingEffects = new LinkedHashMap<>(); Set appliedEffects = new HashSet<>(); + applyCounters.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, null, game); + activeLayerEffects = getLayeredEffects(game, "layer_6"); + while (!done) { // loop needed if a added effect adds again an effect (e.g. Level 5- of Joraga Treespeaker) done = true; layer = filterLayeredEffects(activeLayerEffects, Layer.AbilityAddingRemovingEffects_6); // debug /* - System.out.println(game.getTurn() + ", " + game.getPhase() + ": " + "need apply " + layer.stream() - .map((eff) -> {return eff.getClass().getName().replaceAll(".+\\.(.+)", "$1");}) - .collect(Collectors.joining(", "))); + System.out.println( + game.getTurn() + + ", " + game.getPhase() + + ": need to apply " + + layer.stream() + .map(Object::getClass) + .map(Class::getName) + .map(s -> s.replaceAll(".+\\.(.+)", "$1")) + .collect(Collectors.joining(", ")) + ); */ for (ContinuousEffect effect : layer) { - if (activeLayerEffects.contains(effect) && !appliedEffects.contains(effect.getId())) { // Effect does still exist and was not applied yet - Set dependentTo = effect.isDependentTo(layer); - if (!appliedEffects.containsAll(dependentTo)) { + if (!activeLayerEffects.contains(effect) || appliedEffects.contains(effect.getId())) { + continue; + } // Effect does still exist and was not applied yet + Set dependentTo = effect.isDependentTo(layer); + if (!appliedEffects.containsAll(dependentTo)) { + waitingEffects + .entrySet() + .stream() + .filter(entry -> dependentTo.contains(entry.getKey().getId()) + && entry.getValue().contains(effect.getId())) + .forEach(entry -> { + entry.getValue().remove(effect.getId()); + dependentTo.remove(entry.getKey().getId()); + }); + waitingEffects.entrySet().removeIf(x -> x.getValue() == null || x.getValue().isEmpty()); + if (!dependentTo.isEmpty() && !waitingEffects.containsKey(effect)) { + // make sure circular dependencies weren't the only dependencies waitingEffects.put(effect, dependentTo); continue; } - List appliedAbilities = appliedEffectAbilities.get(effect); - Set abilities = layeredEffects.getAbility(effect.getId()); - for (Ability ability : abilities) { - if (appliedAbilities == null || !appliedAbilities.contains(ability)) { - if (appliedAbilities == null) { - appliedAbilities = new ArrayList<>(); - appliedEffectAbilities.put(effect, appliedAbilities); - } - appliedAbilities.add(ability); - effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game); - done = false; - // list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities) - activeLayerEffects = getLayeredEffects(game); - } + } + List appliedAbilities = appliedEffectAbilities.get(effect); + Set abilities = layeredEffects.getAbility(effect.getId()); + for (Ability ability : abilities) { + if (appliedAbilities != null && appliedAbilities.contains(ability)) { + continue; } - appliedEffects.add(effect.getId()); + if (appliedAbilities == null) { + appliedAbilities = new ArrayList<>(); + appliedEffectAbilities.put(effect, appliedAbilities); + } + appliedAbilities.add(ability); + effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game); + done = false; + // list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities) + activeLayerEffects = getLayeredEffects(game, "apply"); + } + appliedEffects.add(effect.getId()); - if (!waitingEffects.isEmpty()) { - // check if waiting effects can be applied now - for (Iterator>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) { - Map.Entry> entry = iterator.next(); - if (appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself - appliedAbilities = appliedEffectAbilities.get(entry.getKey()); - abilities = layeredEffects.getAbility(entry.getKey().getId()); - for (Ability ability : abilities) { - if (appliedAbilities == null || !appliedAbilities.contains(ability)) { - if (appliedAbilities == null) { - appliedAbilities = new ArrayList<>(); - appliedEffectAbilities.put(entry.getKey(), appliedAbilities); - } - appliedAbilities.add(ability); - entry.getKey().apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game); - done = false; - // list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities) - activeLayerEffects = getLayeredEffects(game); - } - } - appliedEffects.add(entry.getKey().getId()); - iterator.remove(); - } - } + if (waitingEffects.isEmpty()) { + continue; + } + // check if waiting effects can be applied now + for (Iterator>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) { + Map.Entry> entry = iterator.next(); + if (!appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself + continue; } + appliedAbilities = appliedEffectAbilities.get(entry.getKey()); + abilities = layeredEffects.getAbility(entry.getKey().getId()); + for (Ability ability : abilities) { + if (appliedAbilities != null && appliedAbilities.contains(ability)) { + continue; + } + if (appliedAbilities == null) { + appliedAbilities = new ArrayList<>(); + appliedEffectAbilities.put(entry.getKey(), appliedAbilities); + } + appliedAbilities.add(ability); + entry.getKey().apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game); + done = false; + // list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities) + activeLayerEffects = getLayeredEffects(game, "apply"); + } + appliedEffects.add(entry.getKey().getId()); + iterator.remove(); } } } @@ -1055,10 +1093,10 @@ public class ContinuousEffects implements Serializable { private boolean abilityActive(Ability ability, Game game) { MageObject object = game.getObject(ability.getSourceId()); - return object != null && object.hasAbility(ability.getId(), game); + return object != null && object.hasAbility(ability, game); } - private void applyLayer(List activeLayerEffects, Layer currentLayer, Game game) { + private void applyLayer(List activeLayerEffects, Layer currentLayer, Game game, String timestampGroupName) { List layer = filterLayeredEffects(activeLayerEffects, currentLayer); // layer is a list of all effects at the current layer if (!layer.isEmpty()) { @@ -1081,7 +1119,7 @@ public class ContinuousEffects implements Serializable { applyContinuousEffect(effect, currentLayer, game); // add it to the applied effects list appliedEffects.add(effect.getId()); - layer = getLayeredEffects(game); + layer = getLayeredEffects(game, timestampGroupName); // check waiting effects to see if it has anything to check if (!waitingEffects.isEmpty()) { @@ -1092,7 +1130,7 @@ public class ContinuousEffects implements Serializable { applyContinuousEffect(entry.getKey(), currentLayer, game); // add it to the applied effects list appliedEffects.add(entry.getKey().getId()); - layer = getLayeredEffects(game); + layer = getLayeredEffects(game, timestampGroupName); } } } @@ -1103,7 +1141,7 @@ public class ContinuousEffects implements Serializable { applyContinuousEffect(entry.getKey(), currentLayer, game); // add it to the applied effects list appliedEffects.add(entry.getKey().getId()); - layer = getLayeredEffects(game); + layer = getLayeredEffects(game, timestampGroupName); } } } @@ -1126,7 +1164,7 @@ public class ContinuousEffects implements Serializable { if (!(effect instanceof BecomesFaceDownCreatureEffect) && (effect != null && !effect.getDuration().equals(Duration.Custom))) { // Custom effects do not depend on the creating permanent if (card != null) { - return card.getAbilities(game).contains(ability); + return card.hasAbility(ability, game); } } @@ -1149,17 +1187,6 @@ public class ContinuousEffects implements Serializable { public synchronized void addEffect(ContinuousEffect effect, UUID sourceId, Ability source) { if (!(source instanceof MageSingleton)) { // because MageSingletons may never be removed by removing the temporary effecs they are not added to the temporaryEffects to prevent this effect.setTemporary(true); - Set abilities = temporaryEffects.get(effect); - if (abilities == null) { - abilities = new HashSet<>(); - temporaryEffects.put(effect, abilities); - } else if (abilities.contains(source)) { - // this ability (for the continuous effect) is already added - return; - } - abilities.add(source); - - // add the effect itself } addEffect(effect, source); } @@ -1227,18 +1254,19 @@ public class ContinuousEffects implements Serializable { } private void setControllerForEffect(ContinuousEffectsList effects, UUID sourceId, UUID controllerId) { - for (Effect effect : effects) { - Set abilities = effects.getAbility(effect.getId()); - for (Ability ability : abilities) { - if (ability.getSourceId() != null) { - if (ability.getSourceId().equals(sourceId)) { - ability.setControllerId(controllerId); + for (ContinuousEffect effect : effects) { + if (!effect.getDuration().isFixedController()) { + Set abilities = effects.getAbility(effect.getId()); + for (Ability ability : abilities) { + if (ability.getSourceId() != null) { + if (ability.getSourceId().equals(sourceId)) { + ability.setControllerId(controllerId); + } + } else if (ability.getZone() != Zone.COMMAND) { + logger.fatal("Continuous effect for ability with no sourceId Ability: " + ability); } - } else if (ability.getZone() != Zone.COMMAND) { - logger.fatal("Continuous effect for ability with no sourceId Ability: " + ability); } } - } } @@ -1246,51 +1274,12 @@ public class ContinuousEffects implements Serializable { for (ContinuousEffectsList effectsList : allEffectsLists) { effectsList.clear(); } - temporaryEffects.clear(); } public synchronized void removeAllTemporaryEffects() { - for (Map.Entry> entry : temporaryEffects.entrySet()) { - switch (entry.getKey().getEffectType()) { - case REPLACEMENT: - case REDIRECTION: - replacementEffects.removeEffects(entry.getKey().getId(), entry.getValue()); - break; - case PREVENTION: - preventionEffects.removeEffects(entry.getKey().getId(), entry.getValue()); - break; - case RESTRICTION: - restrictionEffects.removeEffects(entry.getKey().getId(), entry.getValue()); - break; - case RESTRICTION_UNTAP_NOT_MORE_THAN: - restrictionUntapNotMoreThanEffects.removeEffects(entry.getKey().getId(), entry.getValue()); - break; - case REQUIREMENT: - requirementEffects.removeEffects(entry.getKey().getId(), entry.getValue()); - break; - case ASTHOUGH: - AsThoughEffect newAsThoughEffect = (AsThoughEffect) entry.getKey(); - if (!asThoughEffectsMap.containsKey(newAsThoughEffect.getAsThoughEffectType())) { - break; - } - asThoughEffectsMap.get(newAsThoughEffect.getAsThoughEffectType()).removeEffects(entry.getKey().getId(), entry.getValue()); - break; - case COSTMODIFICATION: - costModificationEffects.removeEffects(entry.getKey().getId(), entry.getValue()); - break; - case SPLICE: - spliceCardEffects.removeEffects(entry.getKey().getId(), entry.getValue()); - break; - case CONTINUOUS_RULE_MODIFICATION: - continuousRuleModifyingEffects.removeEffects(entry.getKey().getId(), entry.getValue()); - break; - default: - layeredEffects.removeEffects(entry.getKey().getId(), entry.getValue()); - break; - } - + for (ContinuousEffectsList effectsList : allEffectsLists) { + effectsList.removeTemporaryEffects(); } - temporaryEffects.clear(); } public Map getReplacementEffectsTexts(Map> rEffects, Game game) { @@ -1300,14 +1289,13 @@ public class ContinuousEffects implements Serializable { for (Ability ability : entry.getValue()) { MageObject object = game.getObject(ability.getSourceId()); if (object != null) { - texts.put(ability.getId().toString() + '_' + entry.getKey().getId().toString(), object.getName() + ": " + ability.getRule(object.getName())); + texts.put(ability.getId().toString() + '_' + entry.getKey().getId().toString(), object.getIdName() + ": " + ability.getRule(object.getName())); } else { texts.put(ability.getId().toString() + '_' + entry.getKey().getId().toString(), entry.getKey().getText(null)); } } } else { if (!(entry.getKey() instanceof AuraReplacementEffect)) { -// && !(entry.getKey() instanceof PlaneswalkerRedirectionEffect)) { logger.error("Replacement effect without ability: " + entry.getKey().toString()); } } diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java index 26e9288654..c3e2cd3429 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java @@ -66,7 +66,6 @@ public class ContinuousEffectsList extends ArrayList } public void removeEndOfCombatEffects() { - for (Iterator i = this.iterator(); i.hasNext(); ) { T entry = i.next(); if (entry.getDuration() == Duration.EndOfCombat) { @@ -86,15 +85,6 @@ public class ContinuousEffectsList extends ArrayList } } - public void incYourTurnNumPlayed(Game game) { - for (Iterator i = this.iterator(); i.hasNext(); ) { - T entry = i.next(); - if (game.isActivePlayer(entry.getStartingController())) { - entry.incYourTurnNumPlayed(); - } - } - } - private boolean isInactive(T effect, Game game) { // ends all inactive effects -- calls on player leave or apply new effect if (game.getState().isGameOver()) { @@ -109,9 +99,8 @@ public class ContinuousEffectsList extends ArrayList those objects are exiled. This is not a state-based action. It happens as soon as the player leaves the game. If the player who left the game had priority at the time they left, priority passes to the next player in turn order who’s still in the game. - */ + */ // objects removes doing in player.leave() call... effects removes is here - Set set = effectAbilityMap.get(effect.getId()); if (set == null) { logger.debug("No abilities for effect found: " + effect.toString()); @@ -218,20 +207,13 @@ public class ContinuousEffectsList extends ArrayList return effectAbilityMap.getOrDefault(effectId, new HashSet<>()); } - public void removeEffects(UUID effectIdToRemove, Set abilitiesToRemove) { - Set abilities = effectAbilityMap.get(effectIdToRemove); - if (abilitiesToRemove != null && abilities != null) { - abilities.removeAll(abilitiesToRemove); - } - if (abilities == null || abilities.isEmpty()) { - for (Iterator iterator = this.iterator(); iterator.hasNext(); ) { - ContinuousEffect effect = iterator.next(); - if (effect.getId().equals(effectIdToRemove)) { - iterator.remove(); - break; - } + public void removeTemporaryEffects() { + for (Iterator i = this.iterator(); i.hasNext(); ) { + T entry = i.next(); + if (entry.isTemporary()) { + i.remove(); + effectAbilityMap.remove(entry.getId()); } - effectAbilityMap.remove(effectIdToRemove); } } @@ -240,4 +222,24 @@ public class ContinuousEffectsList extends ArrayList super.clear(); effectAbilityMap.clear(); } + + @Override + public boolean contains(Object object) { + if (object == null || !(object instanceof ContinuousEffect)) { + return false; + } + + // search by id + ContinuousEffect need = (ContinuousEffect) object; + for (Iterator iterator = this.iterator(); iterator.hasNext(); ) { + T test = iterator.next(); + if (need.equals(test)) { + return true; + } + if (need.getId().equals(test.getId())) { + return true; + } + } + return false; + } } diff --git a/Mage/src/main/java/mage/abilities/effects/Effects.java b/Mage/src/main/java/mage/abilities/effects/Effects.java index 8d3d81db54..fe11c2b90d 100644 --- a/Mage/src/main/java/mage/abilities/effects/Effects.java +++ b/Mage/src/main/java/mage/abilities/effects/Effects.java @@ -1,11 +1,12 @@ package mage.abilities.effects; +import java.util.ArrayList; +import java.util.Arrays; import mage.abilities.Ability; import mage.abilities.Mode; import mage.constants.Outcome; import mage.target.targetpointer.TargetPointer; - -import java.util.ArrayList; +import mage.util.CardUtil; /** * @author BetaSteward_at_googlemail.com @@ -13,9 +14,7 @@ import java.util.ArrayList; public class Effects extends ArrayList { public Effects(Effect... effects) { - for (Effect effect : effects) { - this.add(effect); - } + this.addAll(Arrays.asList(effects)); } public Effects(final Effects effects) { @@ -31,7 +30,7 @@ public class Effects extends ArrayList { public String getTextStartingUpperCase(Mode mode) { String text = getText(mode); if (text.length() > 3) { - return Character.toUpperCase(text.charAt(0)) + text.substring(1); + return CardUtil.getTextWithFirstCharUpperCase(text); } return text; } @@ -52,17 +51,27 @@ public class Effects extends ArrayList { // concat effects (default: each effect with a new sentence) String concatPrefix = effect.getConcatPrefix(); + if (effectNum > 1 && !concatPrefix.isEmpty() && !concatPrefix.equals(".")) { nextRule = concatPrefix + " " + nextRule; } if (nextRule != null) { + //check if nextRule is a new sentence or not. if (nextRule.startsWith("and ") || nextRule.startsWith("with ") || nextRule.startsWith("then ")) { endString = " "; } else if (nextRule.startsWith(",") || nextRule.startsWith(" ")) { endString = ""; + // nextRule determined to be a new sentence, now check ending of lastRule } else if (lastRule != null && lastRule.length() > 3) { - if (!lastRule.endsWith(".") && !lastRule.endsWith("
")) { + //check if lastRule already has appropriate punctuation, if so, add a space. + if (lastRule.endsWith(".\"") + || lastRule.endsWith(".)") + || lastRule.endsWith(".)
") + || lastRule.endsWith(".")) { + endString = " "; + // if lastRule does not have appropriate punctuation, add the default ". " + } else if (!lastRule.endsWith(".") && !lastRule.endsWith("
")) { endString = ". "; } if (nextRule.length() > 3) { @@ -82,9 +91,12 @@ public class Effects extends ArrayList { sbText.append(currentRule); } + lastRule = nextRule; + } + //add punctuation to very last rule. if (lastRule != null && lastRule.length() > 3 && !lastRule.endsWith(".") && !lastRule.endsWith("\"") @@ -95,6 +107,7 @@ public class Effects extends ArrayList { } return sbText.toString(); + } public boolean hasOutcome(Ability source, Outcome outcome) { @@ -167,4 +180,7 @@ public class Effects extends ArrayList { } } + public void setValue(String key, Object value) { + this.stream().forEach(effect -> effect.setValue(key, value)); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java b/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java index 11545d7d27..0d96f2a918 100644 --- a/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java @@ -64,7 +64,7 @@ public class GainAbilitySpellsEffect extends ContinuousEffectImpl { if (stackObject.isControlledBy(source.getControllerId())) { Card card = game.getCard(stackObject.getSourceId()); if (card != null && filter.match(card, game)) { - if (!card.getAbilities().contains(ability)) { + if (!card.hasAbility(ability, game)) { game.getState().addOtherAbility(card, ability); } } diff --git a/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java b/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java index 813f1ad790..14c4968017 100644 --- a/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java @@ -1,66 +1,66 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects; - -import mage.abilities.Ability; -import mage.constants.Duration; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; - -/** - * @author antoni-g - */ -public class PreventDamageAndRemoveCountersEffect extends PreventionEffectImpl { - private final boolean thatMany; - - public PreventDamageAndRemoveCountersEffect(boolean thatMany) { - super(Duration.WhileOnBattlefield, Integer.MAX_VALUE, false, false); - this.thatMany = thatMany; - staticText = "If damage would be dealt to {this} while it has a +1/+1 counter on it, " + - "prevent that damage and remove " + (thatMany ? "that many +1/+1 counters" : "a +1/+1 counter") + " from it"; - } - - private PreventDamageAndRemoveCountersEffect(final PreventDamageAndRemoveCountersEffect effect) { - super(effect); - this.thatMany = effect.thatMany; - } - - @Override - public PreventDamageAndRemoveCountersEffect copy() { - return new PreventDamageAndRemoveCountersEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - int damage = event.getAmount(); - preventDamageAction(event, source, game); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent == null) { - return false; - } - if (!thatMany) { - damage = 1; - } - permanent.removeCounters(CounterType.P1P1.createInstance(damage), game); //MTG ruling (this) loses counters even if the damage isn't prevented - return false; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - return super.applies(event, source, game) - && permanent != null - && event.getTargetId().equals(source.getSourceId()) - && permanent.getCounters(game).containsKey(CounterType.P1P1); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects; + +import mage.abilities.Ability; +import mage.constants.Duration; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * @author antoni-g + */ +public class PreventDamageAndRemoveCountersEffect extends PreventionEffectImpl { + private final boolean thatMany; + + public PreventDamageAndRemoveCountersEffect(boolean thatMany) { + super(Duration.WhileOnBattlefield, Integer.MAX_VALUE, false, false); + this.thatMany = thatMany; + staticText = "If damage would be dealt to {this} while it has a +1/+1 counter on it, " + + "prevent that damage and remove " + (thatMany ? "that many +1/+1 counters" : "a +1/+1 counter") + " from it"; + } + + private PreventDamageAndRemoveCountersEffect(final PreventDamageAndRemoveCountersEffect effect) { + super(effect); + this.thatMany = effect.thatMany; + } + + @Override + public PreventDamageAndRemoveCountersEffect copy() { + return new PreventDamageAndRemoveCountersEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + int damage = event.getAmount(); + preventDamageAction(event, source, game); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return false; + } + if (!thatMany) { + damage = 1; + } + permanent.removeCounters(CounterType.P1P1.createInstance(damage), game); //MTG ruling (this) loses counters even if the damage isn't prevented + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + return super.applies(event, source, game) + && permanent != null + && event.getTargetId().equals(source.getSourceId()) + && permanent.getCounters(game).containsKey(CounterType.P1P1); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/AdditionalCombatPhaseEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AdditionalCombatPhaseEffect.java index 26f30dd7ac..041067f8cb 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/AdditionalCombatPhaseEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/AdditionalCombatPhaseEffect.java @@ -10,8 +10,8 @@ import mage.game.turn.TurnMod; public class AdditionalCombatPhaseEffect extends OneShotEffect { public AdditionalCombatPhaseEffect() { - super(Outcome.Benefit); - staticText = "After this phase, there is an additional combat phase"; + super(Outcome.Benefit); + staticText = "After this phase, there is an additional combat phase"; } public AdditionalCombatPhaseEffect(String staticText) { @@ -20,17 +20,18 @@ public class AdditionalCombatPhaseEffect extends OneShotEffect { } public AdditionalCombatPhaseEffect(final AdditionalCombatPhaseEffect effect) { - super(effect); + super(effect); } @Override public AdditionalCombatPhaseEffect copy() { - return new AdditionalCombatPhaseEffect(this); + return new AdditionalCombatPhaseEffect(this); } @Override public boolean apply(Game game, Ability source) { - game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), TurnPhase.COMBAT, null, false)); - return true; + game.getState().getTurnMods().add(new TurnMod(game.getState().getActivePlayerId(), + TurnPhase.COMBAT, null, false)); + return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java index f3d6e14290..f44dfcf545 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java @@ -1,6 +1,5 @@ package mage.abilities.effects.common; -import mage.Mana; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.effects.common.cost.CostModificationEffectImpl; @@ -9,9 +8,10 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; +import mage.util.CardUtil; public class AffinityEffect extends CostModificationEffectImpl { - + private final FilterControlledPermanent filter; public AffinityEffect(FilterControlledPermanent affinityFilter) { @@ -27,20 +27,10 @@ public class AffinityEffect extends CostModificationEffectImpl { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility)abilityToModify; - Mana mana = spellAbility.getManaCostsToPay().getMana(); - if (mana.getGeneric() > 0) { - // the following works with Sen Triplets and in multiplayer games - int count = game.getBattlefield().getActivePermanents(filter, abilityToModify.getControllerId(), source.getId(), game).size(); - int newCount = mana.getGeneric() - count; - if (newCount < 0) { - newCount = 0; - } - mana.setGeneric(newCount); - spellAbility.getManaCostsToPay().load(mana.toString()); - return true; - } - return false; + // abilityToModify.getControllerId() works with Sen Triplets and in multiplayer games, see https://github.com/magefree/mage/issues/5931 + int count = game.getBattlefield().getActivePermanents(filter, abilityToModify.getControllerId(), source.getId(), game).size(); + CardUtil.reduceCost(abilityToModify, count); + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/AmplifyEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AmplifyEffect.java index 81eab7e767..bbe980d2b8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/AmplifyEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/AmplifyEffect.java @@ -16,6 +16,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardIdPredicate; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; @@ -108,6 +109,13 @@ public class AmplifyEffect extends ReplacementEffectImpl { } else if (filterSubtypes.size() == 1) { filter.add(filterSubtypes.get(0)); } + + // You can’t reveal this card or any other cards that are entering the battlefield at the same time as this card. + filter.add(Predicates.not(new CardIdPredicate(source.getSourceId()))); + for (Permanent enteringPermanent : game.getPermanentsEntering().values()) { + filter.add(Predicates.not(new CardIdPredicate(enteringPermanent.getId()))); + } + if (controller.getHand().count(filter, source.getSourceId(), source.getControllerId(), game) > 0) { if (controller.chooseUse(outcome, "Reveal cards to Amplify?", source, game)) { TargetCardInHand target = new TargetCardInHand(0, Integer.MAX_VALUE, filter); diff --git a/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java new file mode 100644 index 0000000000..3f6a41bb02 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java @@ -0,0 +1,152 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetControlledPermanent; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author emerald000 + */ +public class BalanceEffect extends OneShotEffect { + + public BalanceEffect() { + super(Outcome.Sacrifice); + staticText = "each player chooses a number of lands they control " + + "equal to the number of lands controlled by the player " + + "who controls the fewest, then sacrifices the rest. " + + "Players discard cards and sacrifice creatures the same way"; + } + + private BalanceEffect(final BalanceEffect effect) { + super(effect); + } + + @Override + public BalanceEffect copy() { + return new BalanceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + + choosePermanentsToKeep(game, source, controller, StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, new FilterControlledLandPermanent("lands to keep")); + choosePermanentsToKeep(game, source, controller, StaticFilters.FILTER_CONTROLLED_CREATURE, new FilterControlledCreaturePermanent("creatures to keep")); + + //Cards in hand + int lowestHandSize = Integer.MAX_VALUE; + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + + lowestHandSize = Math.min(lowestHandSize, player.getHand().size()); + } + + Map cardsToDiscard = new HashMap<>(); + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + + TargetCardInHand target = new TargetCardInHand(lowestHandSize, new FilterCard("cards to keep")); + if (!target.choose(Outcome.Protect, player.getId(), source.getSourceId(), game)) { + continue; + } + + Set allCardsInHand = player.getHand().getCards(game); + Set cardsToKeep = new LinkedHashSet<>(); + + for (Card card : allCardsInHand) { + if (card != null && target.getTargets().contains(card.getId())) { + cardsToKeep.add(card); + } + } + + cardsToDiscard.put(playerId, allCardsInHand.stream() + .filter(e -> !cardsToKeep.contains(e)) + .collect(CardsImpl::new, CardsImpl::add, CardsImpl::addAll)); + } + + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null && cardsToDiscard.get(playerId) != null) { + player.discard(cardsToDiscard.get(playerId), source, game); + } + } + + return true; + } + + private void choosePermanentsToKeep(Game game, Ability source, Player controller, + FilterControlledPermanent filterPermanent, FilterControlledPermanent filterPermanentDialog) { + int lowestPermanentsCount = Integer.MAX_VALUE; + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + + lowestPermanentsCount = Math.min(lowestPermanentsCount, + game.getBattlefield().countAll(filterPermanent, player.getId(), game)); + } + + List permanentsToSacrifice = new ArrayList<>(); + + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + + TargetControlledPermanent target = new TargetControlledPermanent(lowestPermanentsCount, lowestPermanentsCount, filterPermanentDialog, true); + if (!target.choose(Outcome.Protect, player.getId(), source.getSourceId(), game)) { + continue; + } + + List allPermanentsOfType = game.getBattlefield().getActivePermanents(filterPermanent, player.getId(), source.getSourceId(), game); + List permanentsToKeep = new ArrayList<>(); + + for (Permanent permanent : allPermanentsOfType) { + if (permanent != null && target.getTargets().contains(permanent.getId())) { + permanentsToKeep.add(permanent); + } + } + + List playerPermanentsToSacrifice = allPermanentsOfType.stream().filter(e -> !permanentsToKeep.contains(e)).collect(Collectors.toList()); + permanentsToSacrifice.addAll(playerPermanentsToSacrifice); + + if (playerPermanentsToSacrifice.isEmpty()) { + game.informPlayers(player.getLogName() + " chose permanents to be sacrificed: " + + playerPermanentsToSacrifice.stream().map(Permanent::getLogName).collect(Collectors.joining(", "))); + } + } + + for (Permanent permanent : permanentsToSacrifice) { + if (permanent != null) { + permanent.sacrifice(source.getSourceId(), game); + } + } + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/BecomeBlockedTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/BecomeBlockedTargetEffect.java new file mode 100644 index 0000000000..a398d066ea --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/BecomeBlockedTargetEffect.java @@ -0,0 +1,93 @@ +package mage.abilities.effects.common; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.util.CardUtil; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class BecomeBlockedTargetEffect extends OneShotEffect { + + public BecomeBlockedTargetEffect() { + super(Outcome.Benefit); + } + + private BecomeBlockedTargetEffect(final BecomeBlockedTargetEffect effect) { + super(effect); + } + + @Override + public BecomeBlockedTargetEffect copy() { + return new BecomeBlockedTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Set morSet = new HashSet<>(); + for (UUID targetId : targetPointer.getTargets(game, source)) { + Permanent permanent = game.getPermanent(targetId); + if (permanent == null) { + continue; + } + CombatGroup combatGroup = game.getCombat().findGroup(permanent.getId()); + if (combatGroup == null) { + continue; + } + boolean alreadyBlocked = combatGroup.getBlocked(); + combatGroup.setBlocked(true); // non-banded creatures + combatGroup.setBlocked(true, game); // this only works for banded creatures and needs to be checked out + if (alreadyBlocked) { + continue; + } + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.CREATURE_BLOCKED, permanent.getId(), + source.getSourceId(), null + )); + morSet.add(new MageObjectReference(permanent, game)); + } + String key = UUID.randomUUID().toString(); + game.getState().setValue("becameBlocked_" + key, morSet); + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.BATCH_BLOCK_NONCOMBAT, + source.getSourceId(), source.getSourceId(), + source.getControllerId(), key, 0) + ); + return true; + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder(); + if (mode.getTargets().isEmpty()) { + return "that creature becomes blocked"; + } + Target target = mode.getTargets().get(0); + if (target.getNumberOfTargets() == 1) { + String targetName = target.getTargetName(); + sb.append("target ").append(targetName).append(" becomes blocked"); + return sb.toString(); + } + if (target.getMaxNumberOfTargets() != target.getMinNumberOfTargets()) { + sb.append("up to "); + } + sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())); + sb.append(" target ").append(target.getTargetName()).append(" become blocked"); + return sb.toString(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/BrainstormEffect.java b/Mage/src/main/java/mage/abilities/effects/common/BrainstormEffect.java index cf8e4b6558..5dd98633e8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/BrainstormEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/BrainstormEffect.java @@ -29,7 +29,7 @@ public class BrainstormEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - player.drawCards(3, game); + player.drawCards(3, source.getSourceId(), game); putOnLibrary(player, source, game); putOnLibrary(player, source, game); return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedCardsGraveyardsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedCardsGraveyardsEffect.java index 67b64383c8..9ba6c55c46 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedCardsGraveyardsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedCardsGraveyardsEffect.java @@ -1,56 +1,56 @@ - -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.cards.Card; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.StackObject; - -/** - * - * @author LevelX2 - */ -public class CantBeTargetedCardsGraveyardsEffect extends ContinuousRuleModifyingEffectImpl { - - public CantBeTargetedCardsGraveyardsEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Cards in graveyards can't be the targets of spells or abilities"; - } - - public CantBeTargetedCardsGraveyardsEffect(final CantBeTargetedCardsGraveyardsEffect effect) { - super(effect); - } - - @Override - public CantBeTargetedCardsGraveyardsEffect copy() { - return new CantBeTargetedCardsGraveyardsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGET; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Card targetCard = game.getCard(event.getTargetId()); - StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); - if (targetCard != null && stackObject != null) { - Zone zone = game.getState().getZone(targetCard.getId()); - if (zone != null && zone == Zone.GRAVEYARD) { - return true; - } - } - return false; - } -} + +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.cards.Card; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.StackObject; + +/** + * + * @author LevelX2 + */ +public class CantBeTargetedCardsGraveyardsEffect extends ContinuousRuleModifyingEffectImpl { + + public CantBeTargetedCardsGraveyardsEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Cards in graveyards can't be the targets of spells or abilities"; + } + + public CantBeTargetedCardsGraveyardsEffect(final CantBeTargetedCardsGraveyardsEffect effect) { + super(effect); + } + + @Override + public CantBeTargetedCardsGraveyardsEffect copy() { + return new CantBeTargetedCardsGraveyardsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TARGET; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Card targetCard = game.getCard(event.getTargetId()); + StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); + if (targetCard != null && stackObject != null) { + Zone zone = game.getState().getZone(targetCard.getId()); + if (zone != null && zone == Zone.GRAVEYARD) { + return true; + } + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseCardTypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseCardTypeEffect.java new file mode 100644 index 0000000000..52d0b8e466 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseCardTypeEffect.java @@ -0,0 +1,67 @@ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.choices.Choice; +import mage.choices.ChoiceCardType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author emerald000 + */ +public class ChooseCardTypeEffect extends OneShotEffect { + + List cardTypes = Arrays.stream(CardType.values()).collect(Collectors.toList()); + + public ChooseCardTypeEffect(Outcome outcome) { + this(outcome, Arrays.stream(CardType.values()).collect(Collectors.toList())); + } + + public ChooseCardTypeEffect(Outcome outcome, List cardTypes) { + super(outcome); + this.staticText = "choose a card type"; + this.cardTypes = new ArrayList<>(cardTypes); + } + + private ChooseCardTypeEffect(final ChooseCardTypeEffect effect) { + super(effect); + this.cardTypes = new ArrayList<>(effect.cardTypes); + } + + @Override + public ChooseCardTypeEffect copy() { + return new ChooseCardTypeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (controller != null && mageObject != null) { + Choice typeChoice = new ChoiceCardType(); + if (controller.choose(outcome, typeChoice, game)) { + game.informPlayers(mageObject.getLogName() + ": " + controller.getLogName() + " has chosen: " + typeChoice.getChoice()); + game.getState().setValue(source.getSourceId() + "_type", typeChoice.getChoice()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("chosen type", CardUtil.addToolTipMarkTags("Chosen type: " + typeChoice.getChoice()), game); + } + return true; + } + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseColorEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseColorEffect.java index 50a1c928bf..ddda5e3e10 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseColorEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseColorEffect.java @@ -11,19 +11,28 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.CardUtil; +import java.util.Locale; + /** - * * @author Plopman */ public class ChooseColorEffect extends OneShotEffect { + private final String exceptColor; + public ChooseColorEffect(Outcome outcome) { + this(outcome, null); + } + + public ChooseColorEffect(Outcome outcome, String exceptColor) { super(outcome); - staticText = "choose a color"; + this.exceptColor = exceptColor; + staticText = "choose a color" + (exceptColor != null ? " other than " + exceptColor.toLowerCase(Locale.ENGLISH) : ""); } public ChooseColorEffect(final ChooseColorEffect effect) { super(effect); + this.exceptColor = effect.exceptColor; } @Override @@ -34,17 +43,18 @@ public class ChooseColorEffect extends OneShotEffect { mageObject = game.getObject(source.getSourceId()); } ChoiceColor choice = new ChoiceColor(); - if (controller != null && mageObject != null && controller.choose(outcome, choice, game)) { - if (!game.isSimulation()) { - game.informPlayers(mageObject.getLogName() + ": " + controller.getLogName() + " has chosen " + choice.getChoice()); - } - game.getState().setValue(mageObject.getId() + "_color", choice.getColor()); - if (mageObject instanceof Permanent) { - ((Permanent) mageObject).addInfo("chosen color", CardUtil.addToolTipMarkTags("Chosen color: " + choice.getChoice()), game); - } - return true; + if (exceptColor != null) { + choice.removeColorFromChoices(exceptColor); } - return false; + if (controller == null || mageObject == null || !controller.choose(outcome, choice, game)) { + return false; + } + game.informPlayers(mageObject.getLogName() + ": " + controller.getLogName() + " has chosen " + choice.getChoice()); + game.getState().setValue(mageObject.getId() + "_color", choice.getColor()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("chosen color", CardUtil.addToolTipMarkTags("Chosen color: " + choice.getChoice()), game); + } + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java index 6c1a8ba5c6..e8b893706c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java @@ -9,6 +9,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; import mage.util.CardUtil; + /** * * @author LevelX2 @@ -19,50 +20,49 @@ public class ChooseSecretOpponentEffect extends OneShotEffect { public static final String SECRET_OPPONENT = "_secOpp"; public static final String SECRET_OWNER = "_secOwn"; - public ChooseSecretOpponentEffect() { - super(Outcome.Neutral); - staticText = "secretly choose an opponent"; - } + public ChooseSecretOpponentEffect() { + super(Outcome.Neutral); + staticText = "secretly choose an opponent"; + } - public ChooseSecretOpponentEffect(final ChooseSecretOpponentEffect effect) { - super(effect); - } + public ChooseSecretOpponentEffect(final ChooseSecretOpponentEffect effect) { + super(effect); + } - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject mageObject = game.getPermanentEntering(source.getSourceId()); - if (mageObject == null) { - mageObject = game.getObject(source.getSourceId()); - } - if (controller != null && mageObject != null) { - TargetOpponent targetOpponent = new TargetOpponent(); - targetOpponent.setTargetName("opponent (secretly)"); - while (!controller.choose(outcome, targetOpponent, source.getSourceId(), game)) { - if (!controller.canRespond()) { - return false; - } - } - if (targetOpponent.getTargets().isEmpty()) { + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (controller != null && mageObject != null) { + TargetOpponent targetOpponent = new TargetOpponent(true); + targetOpponent.setTargetName("opponent (secretly)"); + while (!controller.choose(outcome, targetOpponent, source.getSourceId(), game)) { + if (!controller.canRespond()) { return false; } - if (!game.isSimulation()) { - game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has secretly chosen an opponent."); - } - game.getState().setValue(mageObject.getId() + SECRET_OPPONENT, targetOpponent.getTargets().get(0)); - game.getState().setValue(mageObject.getId() + SECRET_OWNER, controller.getId()); - if (mageObject instanceof Permanent) { - ((Permanent) mageObject).addInfo(SECRET_OPPONENT, - CardUtil.addToolTipMarkTags(controller.getLogName() + " has secretly chosen an opponent."), game); - } } - return false; - } - - @Override - public ChooseSecretOpponentEffect copy() { - return new ChooseSecretOpponentEffect(this); + if (targetOpponent.getTargets().isEmpty()) { + return false; + } + if (!game.isSimulation()) { + game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has secretly chosen an opponent."); + } + game.getState().setValue(mageObject.getId() + SECRET_OPPONENT, targetOpponent.getTargets().get(0)); + game.getState().setValue(mageObject.getId() + SECRET_OWNER, controller.getId()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo(SECRET_OPPONENT, + CardUtil.addToolTipMarkTags(controller.getLogName() + " has secretly chosen an opponent."), game); + } } + return false; + } + @Override + public ChooseSecretOpponentEffect copy() { + return new ChooseSecretOpponentEffect(this); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java index d75fda572e..ae3f6c5e86 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java @@ -115,11 +115,11 @@ public class CopyEffect extends ContinuousEffectImpl { permanent.removeAllAbilities(source.getSourceId(), game); if (copyFromObject instanceof Permanent) { for (Ability ability : ((Permanent) copyFromObject).getAbilities(game)) { - permanent.addAbility(ability, getSourceId(), game, false); // no new Id so consumed replacement effects are known while new continuousEffects.apply happen. + permanent.addAbility(ability, getSourceId(), game); } } else { for (Ability ability : copyFromObject.getAbilities()) { - permanent.addAbility(ability, getSourceId(), game, false); // no new Id so consumed replacement effects are known while new continuousEffects.apply happen. + permanent.addAbility(ability, getSourceId(), game); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java index 3485c7c4f9..e50ba8830e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyPermanentEffect.java @@ -6,11 +6,11 @@ import mage.abilities.SpellAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.EnchantAbility; +import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -26,13 +26,14 @@ import java.util.UUID; */ public class CopyPermanentEffect extends OneShotEffect { - private FilterPermanent filter; - private ApplyToPermanent applier; + private final FilterPermanent filter; + private final ApplyToPermanent applier; + private final boolean useTargetOfAbility; private Permanent bluePrintPermanent; - private boolean useTargetOfAbility; + private Duration duration = Duration.Custom; public CopyPermanentEffect() { - this(new FilterCreaturePermanent()); + this(StaticFilters.FILTER_PERMANENT_CREATURE); } public CopyPermanentEffect(ApplyToPermanent applier) { @@ -63,6 +64,7 @@ public class CopyPermanentEffect extends OneShotEffect { this.bluePrintPermanent = effect.bluePrintPermanent.copy(); } this.useTargetOfAbility = effect.useTargetOfAbility; + this.duration = effect.duration; } @Override @@ -72,88 +74,95 @@ public class CopyPermanentEffect extends OneShotEffect { if (sourcePermanent == null) { sourcePermanent = game.getObject(source.getSourceId()); } - if (controller != null && sourcePermanent != null) { - Permanent copyFromPermanent = null; - if (useTargetOfAbility) { - copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - } else { - Target target = new TargetPermanent(filter); - target.setNotTarget(true); - if (target.canChoose(source.getSourceId(), controller.getId(), game)) { - controller.choose(Outcome.Copy, target, source.getSourceId(), game); - copyFromPermanent = game.getPermanent(target.getFirstTarget()); - } - } - if (copyFromPermanent != null) { - bluePrintPermanent = game.copyPermanent(copyFromPermanent, sourcePermanent.getId(), source, applier); - if (bluePrintPermanent == null) { - return false; - } - - // if object is a copy of an aura, it needs to attach again for new target - if (bluePrintPermanent.hasSubtype(SubType.AURA, game)) { - //copied from mage.cards.c.CopyEnchantment.java - - // permanent can be attached (Estrid's Mask) or enchant (Utopia Sprawl) - // TODO: fix Animate Dead -- it's can't be copied (can't retarget) - Outcome auraOutcome = Outcome.BoostCreature; - Target auraTarget = null; - - // attach - search effect in spell ability (example: cast Utopia Sprawl, cast Estrid's Invocation on it) - for (Ability ability : bluePrintPermanent.getAbilities()) { - if (ability instanceof SpellAbility) { - auraOutcome = ability.getEffects().getOutcome(ability); - for (Effect effect : ability.getEffects()) { - if (effect instanceof AttachEffect) { - if (bluePrintPermanent.getSpellAbility().getTargets().size() > 0) { - auraTarget = bluePrintPermanent.getSpellAbility().getTargets().get(0); - } - } - } - } - } - - // enchant - search in all abilities (example: cast Estrid's Invocation on enchanted creature by Estrid, the Masked second ability, cast Estrid's Invocation on it) - if (auraTarget == null) { - for (Ability ability : bluePrintPermanent.getAbilities()) { - if (ability instanceof EnchantAbility) { - auraOutcome = ability.getEffects().getOutcome(ability); - if (ability.getTargets().size() > 0) { // Animate Dead don't have targets - auraTarget = ability.getTargets().get(0); - } - } - } - } - - /* if this is a copy of a copy, the copy's target has been - * copied and needs to be cleared - */ - if (auraTarget != null) { - // clear selected target - if (auraTarget.getFirstTarget() != null) { - auraTarget.remove(auraTarget.getFirstTarget()); - } - - // select new target - auraTarget.setNotTarget(true); - if (controller.choose(auraOutcome, auraTarget, source.getSourceId(), game)) { - UUID targetId = auraTarget.getFirstTarget(); - Permanent targetPermanent = game.getPermanent(targetId); - Player targetPlayer = game.getPlayer(targetId); - if (targetPermanent != null) { - targetPermanent.addAttachment(sourcePermanent.getId(), game); - } else if (targetPlayer != null) { - targetPlayer.addAttachment(sourcePermanent.getId(), game); - } else { - return false; - } - } - } - } + if (controller == null || sourcePermanent == null) { + return false; + } + Permanent copyFromPermanent = null; + if (useTargetOfAbility) { + copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + } else { + Target target = new TargetPermanent(filter); + target.setNotTarget(true); + if (target.canChoose(source.getSourceId(), controller.getId(), game)) { + controller.choose(Outcome.Copy, target, source.getSourceId(), game); + copyFromPermanent = game.getPermanent(target.getFirstTarget()); } + } + if (copyFromPermanent == null) { return true; } - return false; + bluePrintPermanent = game.copyPermanent(duration, copyFromPermanent, sourcePermanent.getId(), source, applier); + if (bluePrintPermanent == null) { + return false; + } + + // if object is a copy of an aura, it needs to attach again for new target + if (!bluePrintPermanent.hasSubtype(SubType.AURA, game)) { + return true; + } + //copied from mage.cards.c.CopyEnchantment.java + + // permanent can be attached (Estrid's Mask) or enchant (Utopia Sprawl) + // TODO: fix Animate Dead -- it's can't be copied (can't retarget) + Outcome auraOutcome = Outcome.BoostCreature; + Target auraTarget = null; + + // attach - search effect in spell ability (example: cast Utopia Sprawl, cast Estrid's Invocation on it) + for (Ability ability : bluePrintPermanent.getAbilities()) { + if (!(ability instanceof SpellAbility)) { + continue; + } + auraOutcome = ability.getEffects().getOutcome(ability); + for (Effect effect : ability.getEffects()) { + if (!(effect instanceof AttachEffect)) { + continue; + } + if (bluePrintPermanent.getSpellAbility().getTargets().size() > 0) { + auraTarget = bluePrintPermanent.getSpellAbility().getTargets().get(0); + } + } + } + + // enchant - search in all abilities (example: cast Estrid's Invocation on enchanted creature by Estrid, the Masked second ability, cast Estrid's Invocation on it) + if (auraTarget == null) { + for (Ability ability : bluePrintPermanent.getAbilities()) { + if (!(ability instanceof EnchantAbility)) { + continue; + } + auraOutcome = ability.getEffects().getOutcome(ability); + if (ability.getTargets().size() > 0) { // Animate Dead don't have targets + auraTarget = ability.getTargets().get(0); + } + } + } + + /* if this is a copy of a copy, the copy's target has been + * copied and needs to be cleared + */ + if (auraTarget == null) { + return true; + } + // clear selected target + if (auraTarget.getFirstTarget() != null) { + auraTarget.remove(auraTarget.getFirstTarget()); + } + + // select new target + auraTarget.setNotTarget(true); + if (!controller.choose(auraOutcome, auraTarget, source.getSourceId(), game)) { + return true; + } + UUID targetId = auraTarget.getFirstTarget(); + Permanent targetPermanent = game.getPermanent(targetId); + Player targetPlayer = game.getPlayer(targetId); + if (targetPermanent != null) { + targetPermanent.addAttachment(sourcePermanent.getId(), game); + } else if (targetPlayer != null) { + targetPlayer.addAttachment(sourcePermanent.getId(), game); + } else { + return false; + } + return true; } public Permanent getBluePrintPermanent() { @@ -164,4 +173,9 @@ public class CopyPermanentEffect extends OneShotEffect { public CopyPermanentEffect copy() { return new CopyPermanentEffect(this); } + + public CopyPermanentEffect setDuration(Duration duration) { + this.duration = duration; + return this; + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java index dccf10bc17..3da5053047 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java @@ -450,6 +450,6 @@ class TargetWithAdditionalFilter extends TargetImpl { } } } - return sb.toString(); + return sb.toString().trim(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java index 63b3018d23..d5de78070d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java @@ -41,7 +41,7 @@ public class CopyTokenEffect extends ContinuousEffectImpl { } permanent.getAbilities().clear(); for (Ability ability : token.getAbilities()) { - permanent.addAbility(ability, game); + permanent.addAbility(ability, source.getSourceId(), game); } permanent.getPower().setValue(token.getPower().getValue()); permanent.getToughness().setValue(token.getToughness().getValue()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index acc93941dc..7ffcd7f0ee 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -1,5 +1,9 @@ package mage.abilities.effects.common; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; @@ -20,10 +24,6 @@ import mage.util.CardUtil; import mage.util.functions.ApplyToPermanent; import mage.util.functions.EmptyApplyToPermanent; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - /** * @author LevelX2 */ @@ -46,6 +46,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { private ObjectColor color; private boolean useLKI = false; private boolean isntLegendary = false; + private final List additionalAbilities = new ArrayList(); public CreateTokenCopyTargetEffect(boolean useLKI) { this(); @@ -69,8 +70,8 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { } /** - * @param playerId null the token is controlled/owned by the controller of - * the source ability + * @param playerId null the token is controlled/owned by the + * controller of the source ability * @param additionalCardType the token gains this card type in addition * @param hasHaste the token gains haste * @param number number of tokens to put into play @@ -131,7 +132,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { } Permanent permanent; if (useLKI) { - permanent = ((FixedTarget) getTargetPointer()).getTargetedPermanentOrLKIBattlefield(game); + permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); } else { permanent = game.getPermanentOrLKIBattlefield(targetId); } @@ -202,6 +203,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { if (color != null) { token.getColor(game).setColor(color); } + additionalAbilities.stream().forEach(token::addAbility); token.putOntoBattlefield(number, game, source.getSourceId(), playerId == null ? source.getControllerId() : playerId, tapped, attacking, attackedPlayer); for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield @@ -302,4 +304,8 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source); } } + + public void addAdditionalAbilities(Ability... abilities) { + Arrays.stream(abilities).forEach(this.additionalAbilities::add); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageTargetControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageTargetControllerEffect.java index 48e1cdf3b1..18ccef09a6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DamageTargetControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DamageTargetControllerEffect.java @@ -49,7 +49,7 @@ public class DamageTargetControllerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { Player targetController = game.getPlayer(permanent.getControllerId()); if (targetController != null) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java index d4db66bb45..ea60a6e866 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java @@ -122,8 +122,7 @@ public class DamageTargetEffect extends OneShotEffect { permanent.damage(amount.calculate(game, source, this), source.getSourceId(), game, false, preventable); } Player player = game.getPlayer(targetId); - if (player != null - && player.isInGame()) { + if (player != null) { player.damage(amount.calculate(game, source, this), source.getSourceId(), game, false, preventable); } } @@ -136,8 +135,7 @@ public class DamageTargetEffect extends OneShotEffect { permanent.damage(amount.calculate(game, source, this), source.getSourceId(), game, false, preventable); } else { Player player = game.getPlayer(targetId); - if (player != null - && player.isInGame()) { + if (player != null) { player.damage(amount.calculate(game, source, this), source.getSourceId(), game, false, preventable); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DestroyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DestroyTargetEffect.java index 6ac8f7ab1c..22fb9acf03 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DestroyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DestroyTargetEffect.java @@ -39,7 +39,7 @@ public class DestroyTargetEffect extends OneShotEffect { } public DestroyTargetEffect(boolean noRegen, boolean multitargetHandling) { - super(Outcome.DestroyPermanent); + super(Outcome.Detriment); this.noRegen = noRegen; this.multitargetHandling = multitargetHandling; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java index d2d8a6a7d2..553bf2b6c1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java @@ -88,19 +88,19 @@ public class DevourEffect extends ReplacementEffectImpl { controller.chooseTarget(Outcome.Detriment, target, source, game); if (!target.getTargets().isEmpty()) { List cardSubtypes = new ArrayList<>(); - int devouredCreatures = target.getTargets().size(); + int devouredCreatures = 0; + for (UUID targetId : target.getTargets()) { + Permanent targetCreature = game.getPermanent(targetId); + if (targetCreature != null && targetCreature.sacrifice(source.getSourceId(), game)) { + cardSubtypes.add(targetCreature.getSubtype(game)); + devouredCreatures++; + } + } if (!game.isSimulation()) { game.informPlayers(creature.getLogName() + " devours " + devouredCreatures + " creatures"); } - for (UUID targetId : target.getTargets()) { - Permanent targetCreature = game.getPermanent(targetId); - if (targetCreature != null) { - cardSubtypes.add(targetCreature.getSubtype(game)); - } - if (targetCreature == null || !targetCreature.sacrifice(source.getSourceId(), game)) { - return false; - } - } + game.getState().processAction(game); // need for multistep effects + int amountCounters; if (devourFactor == DevourFactor.DevourX) { amountCounters = devouredCreatures * devouredCreatures; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DiscardCardControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/effects/common/DiscardCardControllerTriggeredAbility.java index 71482262d6..3e96e54f71 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DiscardCardControllerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DiscardCardControllerTriggeredAbility.java @@ -1,53 +1,53 @@ -package mage.abilities.effects.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; - -/** - * @author TheElk801 - */ - -public class DiscardCardControllerTriggeredAbility extends TriggeredAbilityImpl { - - private final FilterCard filter; - - public DiscardCardControllerTriggeredAbility(Effect effect, boolean isOptional) { - this(effect, isOptional, StaticFilters.FILTER_CARD); - } - - public DiscardCardControllerTriggeredAbility(Effect effect, boolean isOptional, FilterCard filter) { - super(Zone.BATTLEFIELD, effect, isOptional); - this.filter = filter; - } - - private DiscardCardControllerTriggeredAbility(final DiscardCardControllerTriggeredAbility ability) { - super(ability); - this.filter = ability.filter; - } - - @Override - public DiscardCardControllerTriggeredAbility copy() { - return new DiscardCardControllerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DISCARDED_CARD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(getControllerId()) - && filter.match(game.getCard(event.getTargetId()), getId(), getControllerId(), game); - } - - @Override - public String getRule() { - return "Whenever you discard " + filter.getMessage() + ", " + super.getRule(); - } +package mage.abilities.effects.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * @author TheElk801 + */ + +public class DiscardCardControllerTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterCard filter; + + public DiscardCardControllerTriggeredAbility(Effect effect, boolean isOptional) { + this(effect, isOptional, StaticFilters.FILTER_CARD_A); + } + + public DiscardCardControllerTriggeredAbility(Effect effect, boolean isOptional, FilterCard filter) { + super(Zone.BATTLEFIELD, effect, isOptional); + this.filter = filter; + } + + private DiscardCardControllerTriggeredAbility(final DiscardCardControllerTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + } + + @Override + public DiscardCardControllerTriggeredAbility copy() { + return new DiscardCardControllerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DISCARDED_CARD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals(getControllerId()) + && filter.match(game.getCard(event.getTargetId()), getSourceId(), getControllerId(), game); + } + + @Override + public String getRule() { + return "Whenever you discard " + filter.getMessage() + ", " + super.getRule(); + } } \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/common/DiscardCardPlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/effects/common/DiscardCardPlayerTriggeredAbility.java index a977e752fd..a97f6dc9a4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DiscardCardPlayerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DiscardCardPlayerTriggeredAbility.java @@ -1,51 +1,51 @@ -package mage.abilities.effects.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.SetTargetPointer; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; - -/** - * @author jeffwadsworth - */ - -public class DiscardCardPlayerTriggeredAbility extends TriggeredAbilityImpl { - - private SetTargetPointer setTargetPointer; - - public DiscardCardPlayerTriggeredAbility(Effect effect, boolean isOptional) { - this(effect, isOptional, SetTargetPointer.NONE); - } - - public DiscardCardPlayerTriggeredAbility(Effect effect, boolean isOptional, SetTargetPointer setTargetPointer) { - super(Zone.BATTLEFIELD, effect, isOptional); - this.setTargetPointer = setTargetPointer; - } - - private DiscardCardPlayerTriggeredAbility(final DiscardCardPlayerTriggeredAbility ability) { - super(ability); - this.setTargetPointer = ability.setTargetPointer; - } - - @Override - public DiscardCardPlayerTriggeredAbility copy() { - return new DiscardCardPlayerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DISCARDED_CARD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return true; - } - - @Override - public String getRule() { - return "Whenever a player discards a card, " + super.getRule(); - } +package mage.abilities.effects.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * @author jeffwadsworth + */ + +public class DiscardCardPlayerTriggeredAbility extends TriggeredAbilityImpl { + + private SetTargetPointer setTargetPointer; + + public DiscardCardPlayerTriggeredAbility(Effect effect, boolean isOptional) { + this(effect, isOptional, SetTargetPointer.NONE); + } + + public DiscardCardPlayerTriggeredAbility(Effect effect, boolean isOptional, SetTargetPointer setTargetPointer) { + super(Zone.BATTLEFIELD, effect, isOptional); + this.setTargetPointer = setTargetPointer; + } + + private DiscardCardPlayerTriggeredAbility(final DiscardCardPlayerTriggeredAbility ability) { + super(ability); + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public DiscardCardPlayerTriggeredAbility copy() { + return new DiscardCardPlayerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DISCARDED_CARD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return true; + } + + @Override + public String getRule() { + return "Whenever a player discards a card, " + super.getRule(); + } } \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java index d231598bf0..7f6d13dc60 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java @@ -108,7 +108,7 @@ public class DoIfCostPaid extends OneShotEffect { player.resetStoredBookmark(game); // otherwise you can e.g. undo card drawn with Mentor of the Meek } else { // Paying cost was cancels so try to undo payment so far - game.restoreState(bookmark, DoIfCostPaid.class.getName()); + player.restoreState(bookmark, DoIfCostPaid.class.getName(), game); if (!otherwiseEffects.isEmpty()) { for (Effect effect : otherwiseEffects) { effect.setTargetPointer(this.targetPointer); @@ -167,6 +167,13 @@ public class DoIfCostPaid extends OneShotEffect { return sb.append(costText).toString(); } + @Override + public void setValue(String key, Object value) { + super.setValue(key, value); + this.executingEffects.setValue(key, value); + this.otherwiseEffects.setValue(key, value); + } + @Override public DoIfCostPaid copy() { return new DoIfCostPaid(this); diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java new file mode 100644 index 0000000000..0697ec3bec --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java @@ -0,0 +1,105 @@ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.Locale; + +public class DoWhenCostPaid extends OneShotEffect { + + private final ReflexiveTriggeredAbility ability; + private final Cost cost; + private final String chooseUseText; + private final boolean optional; + + public DoWhenCostPaid(ReflexiveTriggeredAbility ability, Cost cost, String chooseUseText) { + this(ability, cost, chooseUseText, true); + } + + public DoWhenCostPaid(ReflexiveTriggeredAbility ability, Cost cost, String chooseUseText, boolean optional) { + super(Outcome.Benefit); + this.ability = ability; + this.cost = cost; + this.chooseUseText = chooseUseText; + this.optional = optional; + } + + private DoWhenCostPaid(final DoWhenCostPaid effect) { + super(effect); + this.ability = effect.ability.copy(); + this.cost = effect.cost.copy(); + this.chooseUseText = effect.chooseUseText; + this.optional = effect.optional; + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getObject(source.getSourceId()); + if (player == null || mageObject == null) { + return false; + } + String message = CardUtil.replaceSourceName(chooseUseText, mageObject.getLogName()); + Outcome payOutcome = ability.getEffects().getOutcome(source, this.outcome); + if (!cost.canPay(source, source.getSourceId(), player.getId(), game) + || (optional && !player.chooseUse(payOutcome, message, source, game))) { + return false; + } + cost.clearPaid(); + int bookmark = game.bookmarkState(); + if (cost.pay(source, game, source.getSourceId(), player.getId(), false)) { + game.fireReflexiveTriggeredAbility(ability, source); + player.resetStoredBookmark(game); + return true; + } + player.restoreState(bookmark, DoWhenCostPaid.class.getName(), game); + return true; + } + + public Cost getCost() { + return cost; + } + + @Override + public String getText(Mode mode) { + if (!staticText.isEmpty()) { + return staticText; + } + return (optional ? "you may " : "") + getCostText() + ". When you do, " + ability.getText(); + } + + private String getCostText() { + StringBuilder sb = new StringBuilder(); + String costText = cost.getText(); + if (costText != null + && !costText.toLowerCase(Locale.ENGLISH).startsWith("put") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("return") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("exile") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("discard") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("sacrifice") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("remove") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("pay")) { + sb.append("pay "); + } + return sb.append(costText).toString(); + } + + @Override + public void setValue(String key, Object value) { + super.setValue(key, value); + ability.getEffects().setValue(key, value); + } + + @Override + public DoWhenCostPaid copy() { + return new DoWhenCostPaid(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java index dbc5aa6ad1..ea1aaf6a44 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java @@ -71,7 +71,7 @@ public class DontUntapInControllersUntapStepTargetEffect extends ContinuousRuleM if (staticText != null) { return staticText; } - return "Target " + mode.getTargets().get(0).getTargetName() + return "target " + mode.getTargets().get(0).getTargetName() + " doesn't untap during its controller's untap step" + (getDuration().toString().isEmpty() ? "" : " " + getDuration()); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawCardAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawCardAllEffect.java index 0e87c451e8..772d25845a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DrawCardAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DrawCardAllEffect.java @@ -59,7 +59,7 @@ public class DrawCardAllEffect extends OneShotEffect { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(amount.calculate(game, source, this), game); + player.drawCards(amount.calculate(game, source, this), source.getSourceId(), game); } } break; @@ -67,7 +67,7 @@ public class DrawCardAllEffect extends OneShotEffect { for (UUID playerId : game.getOpponents(controller.getId())) { Player player = game.getPlayer(playerId); if (player != null) { - player.drawCards(amount.calculate(game, source, this), game); + player.drawCards(amount.calculate(game, source, this), source.getSourceId(), game); } } break; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawCardForEachColorAmongControlledPermanentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawCardForEachColorAmongControlledPermanentsEffect.java new file mode 100644 index 0000000000..951d788acb --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/DrawCardForEachColorAmongControlledPermanentsEffect.java @@ -0,0 +1,57 @@ +package mage.abilities.effects.common; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.HashSet; +import java.util.Set; + +public class DrawCardForEachColorAmongControlledPermanentsEffect extends OneShotEffect { + + public DrawCardForEachColorAmongControlledPermanentsEffect() { + super(Outcome.DrawCard); + this.staticText = "Draw a card for each color among permanents you control"; + } + + public DrawCardForEachColorAmongControlledPermanentsEffect(final DrawCardForEachColorAmongControlledPermanentsEffect effect) { + super(effect); + } + + @Override + public DrawCardForEachColorAmongControlledPermanentsEffect copy() { + return new DrawCardForEachColorAmongControlledPermanentsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Set colors = new HashSet<>(); + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) { + if (permanent.getColor(game).isBlack()) { + colors.add(ObjectColor.BLACK); + } + if (permanent.getColor(game).isBlue()) { + colors.add(ObjectColor.BLUE); + } + if (permanent.getColor(game).isRed()) { + colors.add(ObjectColor.RED); + } + if (permanent.getColor(game).isGreen()) { + colors.add(ObjectColor.GREEN); + } + if (permanent.getColor(game).isWhite()) { + colors.add(ObjectColor.WHITE); + } + } + controller.drawCards(colors.size(), source.getSourceId(), game); + return true; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawCardSourceControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawCardSourceControllerEffect.java index 8e260bd88c..7dd857656f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DrawCardSourceControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DrawCardSourceControllerEffect.java @@ -41,7 +41,6 @@ public class DrawCardSourceControllerEffect extends OneShotEffect { super(effect); this.amount = effect.amount.copy(); this.whoDrawCard = effect.whoDrawCard; - setText(); } @Override @@ -54,7 +53,7 @@ public class DrawCardSourceControllerEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); if (player != null && player.canRespond()) { - player.drawCards(amount.calculate(game, source, this), game); + player.drawCards(amount.calculate(game, source, this), source.getSourceId(), game); return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawCardTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawCardTargetEffect.java index c56ae3840b..15adc05b82 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DrawCardTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DrawCardTargetEffect.java @@ -70,7 +70,7 @@ public class DrawCardTargetEffect extends OneShotEffect { } if (!optional || player.chooseUse(outcome, "Use draw effect?", source, game)) { - player.drawCards(cardsToDraw, game); + player.drawCards(cardsToDraw, source.getSourceId(), game); } } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardControllerEffect.java index f682be6e67..7823789af5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardControllerEffect.java @@ -58,7 +58,7 @@ public class DrawDiscardControllerEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); if (player != null) { if (!optional || player.chooseUse(outcome, "Use draw, then discard effect?", source, game)) { - player.drawCards(cardsToDraw, game); + player.drawCards(cardsToDraw, source.getSourceId(), game); player.discard(cardsToDiscard, false, source, game); } return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardOneOfThemEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardOneOfThemEffect.java index 2731eebd39..2738e12505 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardOneOfThemEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardOneOfThemEffect.java @@ -46,7 +46,7 @@ public class DrawDiscardOneOfThemEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Cards initialHand = controller.getHand().copy(); - controller.drawCards(cardsToDraw, game); + controller.drawCards(cardsToDraw, source.getSourceId(), game); Cards drawnCards = new CardsImpl(controller.getHand().copy()); drawnCards.removeAll(initialHand); if (!drawnCards.isEmpty()) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardTargetEffect.java index 5c134042e1..9a8f21ae21 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardTargetEffect.java @@ -49,7 +49,7 @@ public class DrawDiscardTargetEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); if (player != null) { - player.drawCards(cardsToDraw, game); + player.drawCards(cardsToDraw, source.getSourceId(), game); player.discard(cardsToDiscard, false, source, game); return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java index 2d3a8c5639..5b1b4f4e94 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java @@ -1,72 +1,73 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.GainControlTargetEffect; -import mage.constants.Duration; -import mage.constants.Outcome; -import static mage.constants.Outcome.Benefit; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetOpponent; -import mage.target.targetpointer.FixedTarget; - -/** - * Use this effect only with EntersBattlefieldAbility like abilities - * - * @author LevelX2 - */ - -public class EntersBattlefieldUnderControlOfOpponentOfChoiceEffect extends OneShotEffect { - - public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect() { - super(Benefit); - staticText = "under the control of an opponent of your choice"; - } - - private EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(final EntersBattlefieldUnderControlOfOpponentOfChoiceEffect effect) { - super(effect); - } - - @Override - public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect copy() { - return new EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - Target target = new TargetOpponent(); - target.setNotTarget(true); - if (!controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) { - return false; - } - Player opponent = game.getPlayer(target.getFirstTarget()); - if (opponent == null) { - return false; - } - Permanent permanent = game.getPermanentEntering(source.getSourceId()); - if (permanent != null) { - game.informPlayers(permanent.getLogName() + " enters the battlefield under the control of " + opponent.getLogName()); - } - ContinuousEffect continuousEffect = new GainControlTargetEffect( - Duration.Custom, true, opponent.getId() - ); - continuousEffect.setTargetPointer(new FixedTarget( - source.getSourceId(), source.getSourceObjectZoneChangeCounter() - )); - game.addEffect(continuousEffect, source); - return true; - } -} \ No newline at end of file +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.constants.Duration; +import mage.constants.Outcome; +import static mage.constants.Outcome.Benefit; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetOpponent; +import mage.target.targetpointer.FixedTarget; + +/** + * Use this effect only with EntersBattlefieldAbility like abilities + * + * @author LevelX2 + */ +public class EntersBattlefieldUnderControlOfOpponentOfChoiceEffect extends OneShotEffect { + + public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect() { + super(Benefit); + staticText = "under the control of an opponent of your choice"; + } + + private EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(final EntersBattlefieldUnderControlOfOpponentOfChoiceEffect effect) { + super(effect); + } + + @Override + public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect copy() { + return new EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Target target = new TargetOpponent(); + target.setNotTarget(true); + if (!controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) { + return false; + } + Player opponent = game.getPlayer(target.getFirstTarget()); + if (opponent == null) { + return false; + } + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + if (permanent != null) { + permanent.setOriginalControllerId(opponent.getId()); // permanent was controlled by this player since the existance of this object so original controller has to be set to the first controller + permanent.setControllerId(opponent.getId()); // neccessary to set already here because spell caster never controlled the permanent (important for rule 800.4a) + game.informPlayers(permanent.getLogName() + " enters the battlefield under the control of " + opponent.getLogName()); + } + ContinuousEffect continuousEffect = new GainControlTargetEffect( + Duration.Custom, true, opponent.getId() + ); + continuousEffect.setTargetPointer(new FixedTarget( + source.getSourceId(), source.getSourceObjectZoneChangeCounter() + )); + game.addEffect(continuousEffect, source); + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeTargetEffect.java index beaf72ba8c..725072c7dc 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExchangeLifeTargetEffect.java @@ -1,65 +1,65 @@ - -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; - -/** - * - * @author Styxo - */ -public class ExchangeLifeTargetEffect extends OneShotEffect { - - public ExchangeLifeTargetEffect() { - super(Outcome.Neutral); - } - - public ExchangeLifeTargetEffect(final ExchangeLifeTargetEffect effect) { - super(effect); - } - - @Override - public ExchangeLifeTargetEffect copy() { - return new ExchangeLifeTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Player player = game.getPlayer(source.getFirstTarget()); - if (controller != null && player != null) { - int lifeController = controller.getLife(); - int lifePlayer = player.getLife(); - - if (lifeController == lifePlayer) { - return false; - } - - if (!controller.isLifeTotalCanChange() || !player.isLifeTotalCanChange()) { - return false; - } - - if (lifeController < lifePlayer && (!controller.isCanGainLife() || !player.isCanLoseLife())) { - return false; - } - - if (lifeController > lifePlayer && (!controller.isCanLoseLife() || !player.isCanGainLife())) { - return false; - } - - controller.setLife(lifePlayer, game, source); - player.setLife(lifeController, game, source); - return true; - } - return false; - } - - @Override - public String getText(Mode mode) { - return "Exchange life totals with target " + mode.getTargets().get(0).getTargetName(); - } -} + +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author Styxo + */ +public class ExchangeLifeTargetEffect extends OneShotEffect { + + public ExchangeLifeTargetEffect() { + super(Outcome.Neutral); + } + + public ExchangeLifeTargetEffect(final ExchangeLifeTargetEffect effect) { + super(effect); + } + + @Override + public ExchangeLifeTargetEffect copy() { + return new ExchangeLifeTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); + if (controller != null && player != null) { + int lifeController = controller.getLife(); + int lifePlayer = player.getLife(); + + if (lifeController == lifePlayer) { + return false; + } + + if (!controller.isLifeTotalCanChange() || !player.isLifeTotalCanChange()) { + return false; + } + + if (lifeController < lifePlayer && (!controller.isCanGainLife() || !player.isCanLoseLife())) { + return false; + } + + if (lifeController > lifePlayer && (!controller.isCanLoseLife() || !player.isCanGainLife())) { + return false; + } + + controller.setLife(lifePlayer, game, source); + player.setLife(lifeController, game, source); + return true; + } + return false; + } + + @Override + public String getText(Mode mode) { + return "Exchange life totals with target " + mode.getTargets().get(0).getTargetName(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAdventureSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAdventureSpellEffect.java index 9c664bbf25..d6a5c84953 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAdventureSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAdventureSpellEffect.java @@ -1,5 +1,6 @@ package mage.abilities.effects.common; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.MageSingleton; import mage.abilities.effects.AsThoughEffectImpl; @@ -17,8 +18,6 @@ import mage.players.Player; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; -import java.util.UUID; - /** * @author phulin */ @@ -58,7 +57,7 @@ public class ExileAdventureSpellEffect extends OneShotEffect implements MageSing Card parentCard = adventureSpellCard.getParentCard(); if (controller.moveCardsToExile(parentCard, source, game, true, exileId, "On an Adventure from " + controller.getName())) { ContinuousEffect effect = new AdventureCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(parentCard.getId(), game)); + effect.setTargetPointer(new FixedTarget(parentCard, game)); game.addEffect(effect, source); } } @@ -104,4 +103,4 @@ class AdventureCastFromExileEffect extends AsThoughEffectImpl { } return false; } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java index efa3ecd82d..64ceb7543e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java @@ -35,7 +35,7 @@ public class ExileAndGainLifeEqualPowerTargetEffect extends OneShotEffect { if (player != null) { int creaturePower = permanent.getPower().getValue(); permanent.moveToExile(null, null, source.getSourceId(), game); - game.applyEffects(); + game.getState().processAction(game); player.gainLife(creaturePower, game, source); } return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAttachedEffect.java index 26b09fb3ae..02a599c06f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAttachedEffect.java @@ -1,47 +1,51 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * - * @author Styxo - */ -public class ExileAttachedEffect extends OneShotEffect { - - public ExileAttachedEffect() { - super(Outcome.Exile); - staticText = "Exile enchanted creature"; - } - - public ExileAttachedEffect(final ExileAttachedEffect effect) { - super(effect); - } - - @Override - public ExileAttachedEffect copy() { - return new ExileAttachedEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null && enchantment != null && enchantment.getAttachedTo() != null) { - Permanent creature = game.getPermanent(enchantment.getAttachedTo()); - if (creature != null) { - controller.moveCardsToExile(creature, source, game, true, null, ""); - } - } - return false; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author Styxo + */ +public class ExileAttachedEffect extends OneShotEffect { + + public ExileAttachedEffect() { + super(Outcome.Exile); + staticText = "Exile enchanted creature"; + } + + public ExileAttachedEffect(final ExileAttachedEffect effect) { + super(effect); + } + + @Override + public ExileAttachedEffect copy() { + return new ExileAttachedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + // The LKI must be used for this step. 608.2g + Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + if (controller != null + && enchantment != null + && enchantment.getAttachedTo() != null) { + Permanent creature = game.getPermanent(enchantment.getAttachedTo()); + if (creature != null) { + controller.moveCardsToExile(creature, source, game, true, null, ""); + } + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java index 0d1285a3f9..f7dc493b35 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java @@ -1,8 +1,5 @@ package mage.abilities.effects.common; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; @@ -20,13 +17,16 @@ import mage.target.targetpointer.FirstTargetPointer; import mage.target.targetpointer.SecondTargetPointer; import mage.util.CardUtil; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public class ExileTargetEffect extends OneShotEffect { - private Zone onlyFromZone; + private final Zone onlyFromZone; private String exileZone = null; private UUID exileId = null; protected boolean multitargetHandling; @@ -42,6 +42,10 @@ public class ExileTargetEffect extends OneShotEffect { this.multitargetHandling = multitargetHandling; } + /** + * Exile cards to normal exile window (but it can exile to source's exile + * window after toSourceExileZone change) + */ public ExileTargetEffect() { this(null, ""); } @@ -88,21 +92,27 @@ public class ExileTargetEffect extends OneShotEffect { Set toExile = new LinkedHashSet<>(); if (multitargetHandling && targetPointer instanceof FirstTargetPointer - && (source.getTargets().size() > 1 || (source.getTargets().size() > 0 && source.getTargets().get(0).getTargets().size() > 1))) { + && (source.getTargets().size() > 1 + || (source.getTargets().size() > 0 + && source.getTargets().get(0).getTargets().size() > 1))) { for (Target target : source.getTargets()) { for (UUID targetId : target.getTargets()) { Permanent permanent = game.getPermanent(targetId); if (permanent != null && permanent.isPhasedIn()) { Zone currentZone = game.getState().getZone(permanent.getId()); - if (currentZone != Zone.EXILED && (onlyFromZone == null || onlyFromZone == Zone.BATTLEFIELD)) { + if (currentZone != Zone.EXILED + && (onlyFromZone == null + || onlyFromZone == Zone.BATTLEFIELD)) { toExile.add(permanent); } } else { Card card = game.getCard(targetId); if (card != null) { Zone currentZone = game.getState().getZone(card.getId()); - if (currentZone != Zone.EXILED && (onlyFromZone == null || onlyFromZone == currentZone)) { + if (currentZone != Zone.EXILED + && (onlyFromZone == null + || onlyFromZone == currentZone)) { toExile.add(card); } } else { @@ -120,14 +130,18 @@ public class ExileTargetEffect extends OneShotEffect { if (permanent != null && permanent.isPhasedIn()) { Zone currentZone = game.getState().getZone(permanent.getId()); - if (currentZone != Zone.EXILED && (onlyFromZone == null || onlyFromZone == Zone.BATTLEFIELD)) { + if (currentZone != Zone.EXILED + && (onlyFromZone == null + || onlyFromZone == Zone.BATTLEFIELD)) { toExile.add(permanent); } } else { Card card = game.getCard(targetId); if (card != null) { Zone currentZone = game.getState().getZone(card.getId()); - if (currentZone != Zone.EXILED && (onlyFromZone == null || onlyFromZone == currentZone)) { + if (currentZone != Zone.EXILED + && (onlyFromZone == null + || onlyFromZone == currentZone)) { toExile.add(card); } } else { diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java index bee973d554..6be4f53124 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java @@ -21,6 +21,10 @@ import java.util.UUID; */ public class ExileTargetForSourceEffect extends OneShotEffect { + /** + * Exile cards to source's exile window (e.g. if it have another effect like return from exile later) + * TODO: delete that effect and replace it by ExileTargetEffect (it have special param for same purpose) + */ public ExileTargetForSourceEffect() { super(Outcome.Exile); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetIfDiesEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetIfDiesEffect.java index bc82697355..ceb64907ca 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetIfDiesEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetIfDiesEffect.java @@ -1,41 +1,41 @@ - -package mage.abilities.effects.common; - -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.replacement.DiesReplacementEffect; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author LevelX2 - */ -public class ExileTargetIfDiesEffect extends OneShotEffect { - - public ExileTargetIfDiesEffect() { - super(Outcome.Damage); - this.staticText = "If that creature would die this turn, exile it instead"; - } - - public ExileTargetIfDiesEffect(final ExileTargetIfDiesEffect effect) { - super(effect); - } - - @Override - public ExileTargetIfDiesEffect copy() { - return new ExileTargetIfDiesEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (targetCreature != null) { - game.addEffect(new DiesReplacementEffect(new MageObjectReference(targetCreature, game), Duration.EndOfTurn), source); - } - return true; - } -} + +package mage.abilities.effects.common; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.replacement.DiesReplacementEffect; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class ExileTargetIfDiesEffect extends OneShotEffect { + + public ExileTargetIfDiesEffect() { + super(Outcome.Damage); + this.staticText = "If that creature would die this turn, exile it instead"; + } + + public ExileTargetIfDiesEffect(final ExileTargetIfDiesEffect effect) { + super(effect); + } + + @Override + public ExileTargetIfDiesEffect copy() { + return new ExileTargetIfDiesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (targetCreature != null) { + game.addEffect(new DiesReplacementEffect(new MageObjectReference(targetCreature, game), Duration.EndOfTurn), source); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTop3MayPlayUntilEndOfTurnEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTop3MayPlayUntilEndOfTurnEffect.java new file mode 100644 index 0000000000..3ad21482f6 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTop3MayPlayUntilEndOfTurnEffect.java @@ -0,0 +1,56 @@ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.cards.Card; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTargets; + +import java.util.HashSet; +import java.util.Set; + +public class ExileTop3MayPlayUntilEndOfTurnEffect extends OneShotEffect { + + public ExileTop3MayPlayUntilEndOfTurnEffect() { + super(Outcome.Benefit); + this.staticText = "exile the top three cards of your library. Until end of turn, you may play cards exiled this way"; + } + + public ExileTop3MayPlayUntilEndOfTurnEffect(final ExileTop3MayPlayUntilEndOfTurnEffect effect) { + super(effect); + } + + @Override + public ExileTop3MayPlayUntilEndOfTurnEffect copy() { + return new ExileTop3MayPlayUntilEndOfTurnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { + Set cards = new HashSet<>(controller.getLibrary().getTopCards(game, 3)); + if (!cards.isEmpty()) { + controller.moveCardsToExile(cards, source, game, true, source.getSourceId(), sourceObject.getIdName()); + // remove cards that could not be moved to exile + cards.removeIf(card -> !Zone.EXILED.equals(game.getState().getZone(card.getId()))); + if (!cards.isEmpty()) { + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTargets(cards, game)); + game.addEffect(effect, source); + } + } + return true; + } + return false; + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java index bd54104237..22a73d8e0e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java @@ -1,43 +1,47 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.util.CardUtil; - -/** - * - * @author Styxo - */ -public class ExileUntilSourceLeavesEffect extends OneShotEffect { - - public ExileUntilSourceLeavesEffect(String targetName) { - super(Outcome.Removal); - this.staticText = "exile target " + targetName + " an opponent controls until {this} leaves the battlefield"; - } - - public ExileUntilSourceLeavesEffect(final ExileUntilSourceLeavesEffect effect) { - super(effect); - } - - @Override - public ExileUntilSourceLeavesEffect copy() { - return new ExileUntilSourceLeavesEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - return new ExileTargetEffect(CardUtil.getCardExileZoneId(game, source), permanent.getIdName()).apply(game, source); - } - return false; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.util.CardUtil; + +/** + * + * @author Styxo + */ +public class ExileUntilSourceLeavesEffect extends OneShotEffect { + + public ExileUntilSourceLeavesEffect(String targetName) { + super(Outcome.Removal); + this.staticText = "exile target " + targetName + " an opponent controls until {this} leaves the battlefield"; + } + + public ExileUntilSourceLeavesEffect(final ExileUntilSourceLeavesEffect effect) { + super(effect); + } + + @Override + public ExileUntilSourceLeavesEffect copy() { + return new ExileUntilSourceLeavesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + ExileTargetEffect effect = new ExileTargetEffect(CardUtil.getCardExileZoneId(game, source), permanent.getIdName()); + if (targetPointer != null) { // Grasping Giant + effect.setTargetPointer(targetPointer); + } + return effect.apply(game, source); + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java index 22fdb166fe..6661533d8e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java @@ -34,6 +34,7 @@ public class FightTargetsEffect extends OneShotEffect { if (card != null) { UUID target1Id = null; UUID target2Id = null; + boolean secondTargetOptional = false; // first target is in target pointer, second target is a normal target if (source.getTargets().size() < 2) { if (!source.getTargets().get(0).isLegal(source, game)) { @@ -48,6 +49,7 @@ public class FightTargetsEffect extends OneShotEffect { } else if (source.getTargets().get(0).isLegal(source, game) && source.getTargets().get(1).isLegal(source, game)) { target1Id = source.getTargets().get(0).getFirstTarget(); target2Id = source.getTargets().get(1).getFirstTarget(); + secondTargetOptional = source.getTargets().get(1).getMinNumberOfTargets() == 0; } Permanent creature1 = game.getPermanent(target1Id); Permanent creature2 = game.getPermanent(target2Id); @@ -57,7 +59,7 @@ public class FightTargetsEffect extends OneShotEffect { return creature1.fight(creature2, source, game); } } - if (!game.isSimulation()) { + if (!game.isSimulation() && !secondTargetOptional) { game.informPlayers(card.getName() + " has been fizzled."); } } @@ -77,7 +79,7 @@ public class FightTargetsEffect extends OneShotEffect { return staticText; } - return "Target " + mode + return "target " + mode .getTargets().get(0).getTargetName() + " fights another target " + mode .getTargets().get(1).getTargetName(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/FlipCoinEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FlipCoinEffect.java index 32b1b7940d..afa980f039 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/FlipCoinEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/FlipCoinEffect.java @@ -1,92 +1,92 @@ - -package mage.abilities.effects.common; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.Effects; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; - -/** - * - * @author LevelX2 - */ -public class FlipCoinEffect extends OneShotEffect { - - protected Effects executingEffectsWon = new Effects(); - protected Effects executingEffectsLost = new Effects(); - - public FlipCoinEffect(Effect effectWon) { - this(effectWon, null); - } - - public FlipCoinEffect(Effect effectWon, Effect effectLost) { - this(effectWon, effectLost, Outcome.Benefit); - - } - - public FlipCoinEffect(Effect effectWon, Effect effectLost, Outcome outcome) { - super(outcome); - addEffectWon(effectWon); - addEffectLost(effectLost); - } - - public FlipCoinEffect(final FlipCoinEffect effect) { - super(effect); - this.executingEffectsWon = effect.executingEffectsWon.copy(); - this.executingEffectsLost = effect.executingEffectsLost.copy(); - } - - public void addEffectWon(Effect effect) { - if (effect != null) { - executingEffectsWon.add(effect); - } - } - - public void addEffectLost(Effect effect) { - if (effect != null) { - executingEffectsLost.add(effect); - } - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject mageObject = game.getObject(source.getSourceId()); - if (controller != null && mageObject != null) { - boolean result = true; - for (Effect effect : controller.flipCoin(source, game, true) ? executingEffectsWon : executingEffectsLost) { - effect.setTargetPointer(this.targetPointer); - if (effect instanceof OneShotEffect) { - result &= effect.apply(game, source); - } else { - game.addEffect((ContinuousEffect) effect, source); - } - } - return result; - } - return false; - } - - @Override - public String getText(Mode mode) { - if (!staticText.isEmpty()) { - return staticText; - } - StringBuilder sb = new StringBuilder("Flip a coin. If you win the flip, ").append(executingEffectsWon.getText(mode)); - if (!executingEffectsLost.isEmpty()) { - sb.append(". If you lose the flip, ").append(executingEffectsLost.getText(mode)); - } - return sb.toString(); - } - - @Override - public FlipCoinEffect copy() { - return new FlipCoinEffect(this); - } -} + +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.Effects; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class FlipCoinEffect extends OneShotEffect { + + protected Effects executingEffectsWon = new Effects(); + protected Effects executingEffectsLost = new Effects(); + + public FlipCoinEffect(Effect effectWon) { + this(effectWon, null); + } + + public FlipCoinEffect(Effect effectWon, Effect effectLost) { + this(effectWon, effectLost, Outcome.Benefit); + + } + + public FlipCoinEffect(Effect effectWon, Effect effectLost, Outcome outcome) { + super(outcome); + addEffectWon(effectWon); + addEffectLost(effectLost); + } + + public FlipCoinEffect(final FlipCoinEffect effect) { + super(effect); + this.executingEffectsWon = effect.executingEffectsWon.copy(); + this.executingEffectsLost = effect.executingEffectsLost.copy(); + } + + public void addEffectWon(Effect effect) { + if (effect != null) { + executingEffectsWon.add(effect); + } + } + + public void addEffectLost(Effect effect) { + if (effect != null) { + executingEffectsLost.add(effect); + } + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getObject(source.getSourceId()); + if (controller != null && mageObject != null) { + boolean result = true; + for (Effect effect : controller.flipCoin(source, game, true) ? executingEffectsWon : executingEffectsLost) { + effect.setTargetPointer(this.targetPointer); + if (effect instanceof OneShotEffect) { + result &= effect.apply(game, source); + } else { + game.addEffect((ContinuousEffect) effect, source); + } + } + return result; + } + return false; + } + + @Override + public String getText(Mode mode) { + if (!staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder("Flip a coin. If you win the flip, ").append(executingEffectsWon.getText(mode)); + if (!executingEffectsLost.isEmpty()) { + sb.append(" If you lose the flip, ").append(executingEffectsLost.getText(mode)); + } + return sb.toString(); + } + + @Override + public FlipCoinEffect copy() { + return new FlipCoinEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java b/Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java new file mode 100644 index 0000000000..2ea7fe132c --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java @@ -0,0 +1,56 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.Card; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +public class GainActivatedAbilitiesOfTopCardEffect extends ContinuousEffectImpl { + + private final FilterCard filter; + + public GainActivatedAbilitiesOfTopCardEffect(FilterCard filter) { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "As long as the top card of your library is " + filter.getMessage() + ", {this} has all activated abilities of that card"; + this.filter = filter; + } + + public GainActivatedAbilitiesOfTopCardEffect(final GainActivatedAbilitiesOfTopCardEffect effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public GainActivatedAbilitiesOfTopCardEffect copy() { + return new GainActivatedAbilitiesOfTopCardEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + Card card = player.getLibrary().getFromTop(game); + if (card != null && filter.match(card, game)) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + for (Ability ability : card.getAbilities(game)) { + if (ability instanceof ActivatedAbility) { + permanent.addAbility(ability, source.getSourceId(), game); + } + } + return true; + } + } + } + return false; + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/GainLifeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/GainLifeTargetEffect.java index 98210835fb..25579ab5b6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/GainLifeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/GainLifeTargetEffect.java @@ -1,18 +1,15 @@ - - package mage.abilities.effects.common; -import mage.constants.Outcome; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; -import java.util.UUID; - /** * * @author BetaSteward_at_googlemail.com @@ -42,7 +39,7 @@ public class GainLifeTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (UUID playerId: targetPointer.getTargets(game, source)) { + for (UUID playerId : targetPointer.getTargets(game, source)) { Player player = game.getPlayer(playerId); if (player != null) { player.gainLife(life.calculate(game, source, this), game, source); @@ -59,12 +56,13 @@ public class GainLifeTargetEffect extends OneShotEffect { StringBuilder sb = new StringBuilder(); String message = life.getMessage(); - if (!mode.getTargets().isEmpty()) { - sb.append("target ").append(mode.getTargets().get(0).getTargetName()); + if (!mode.getTargets().isEmpty() && mode.getTargets().get(0).getMaxNumberOfTargets() == Integer.MAX_VALUE) { + sb.append("any number of target players each gain "); + } else if (!mode.getTargets().isEmpty()) { + sb.append("target ").append(mode.getTargets().get(0).getTargetName()).append(" gains "); } else { - sb.append("that player"); + sb.append("that player gains "); } - sb.append(" gains "); if (message.isEmpty() || !message.equals("1")) { sb.append(life.toString()).append(' '); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java index a99b93a6f3..eb9aa7b52e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java @@ -53,6 +53,6 @@ public class GetEmblemTargetPlayerEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - return "Target " + mode.getTargets().get(0).getTargetName() + " gets an emblem with \"" + emblem.getAbilities().getRules(null).stream().collect(Collectors.joining("; ")) + "\""; + return "target " + mode.getTargets().get(0).getTargetName() + " gets an emblem with \"" + emblem.getAbilities().getRules(null).stream().collect(Collectors.joining("; ")) + "\""; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java b/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java new file mode 100644 index 0000000000..21814f17ec --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java @@ -0,0 +1,52 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.util.CardUtil; +import mage.watchers.common.AbilityResolvedWatcher; + +/** + * @author emerald000 + */ +public class IfAbilityHasResolvedXTimesEffect extends OneShotEffect { + + private final int resolutionNumber; + private final Effect effect; + + public IfAbilityHasResolvedXTimesEffect(Outcome outcome, int resolutionNumber, Effect effect) { + super(outcome); + this.resolutionNumber = resolutionNumber; + this.effect = effect; + this.staticText = "If this is the " + CardUtil.numberToOrdinalText(resolutionNumber) + " time this ability has resolved this turn, " + + effect.getText(null); + } + + private IfAbilityHasResolvedXTimesEffect(final IfAbilityHasResolvedXTimesEffect effect) { + super(effect); + this.resolutionNumber = effect.resolutionNumber; + this.effect = effect.effect; + } + + @Override + public IfAbilityHasResolvedXTimesEffect copy() { + return new IfAbilityHasResolvedXTimesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class); + if (watcher != null && watcher.getResolutionCount(game, source) == resolutionNumber) { + if (effect instanceof OneShotEffect) { + return effect.apply(game, source); + } else { + game.addEffect((ContinuousEffect) effect, source); + return true; + } + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/InfoEffect.java b/Mage/src/main/java/mage/abilities/effects/common/InfoEffect.java index 7ac754b609..d7150029d9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/InfoEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/InfoEffect.java @@ -2,8 +2,11 @@ package mage.abilities.effects.common; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.Hint; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; @@ -12,7 +15,7 @@ import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; /** - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class InfoEffect extends OneShotEffect { @@ -36,10 +39,66 @@ public class InfoEffect extends OneShotEffect { } public static void addInfoToPermanent(Game game, Ability source, Permanent permanent, String info) { + addInfoToPermanent(game, source, permanent, info, Duration.WhileOnBattlefield); + } + + /** + * Add temporary information string to permanent (visible in rules list) + * + * @param game + * @param source + * @param permanent + * @param info + * @param duration + */ + public static void addInfoToPermanent(Game game, Ability source, Permanent permanent, String info, Duration duration) { // add simple static info to permanent's rules SimpleStaticAbility ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect(info)); - GainAbilityTargetEffect gainAbilityEffect = new GainAbilityTargetEffect(ability, Duration.WhileOnBattlefield); - gainAbilityEffect.setTargetPointer(new FixedTarget(permanent.getId())); - game.addEffect(gainAbilityEffect, source); + + GainAbilityTargetEffect gainEffect = new GainAbilityTargetEffect(ability, duration); + gainEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(gainEffect, source); + } + + /** + * Add temporary card hint to permanent (visible in rules list) + * + * @param game + * @param source + * @param permanent + * @param cardHint + * @param duration + */ + public static void addCardHintToPermanent(Game game, Ability source, Permanent permanent, Hint cardHint, Duration duration) { + SimpleStaticAbility ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("hint")); + ability.setRuleVisible(false); + ability.addHint(cardHint); + + GainAbilityTargetEffect gainEffect = new GainAbilityTargetEffect(ability, duration); + gainEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(gainEffect, source); + } + + /** + * Add temporary card hint to permanent (visible in rules list) + * Will be visible on conditional only + * + * @param game + * @param source + * @param permanent + * @param cardHint + * @param duration + * @param condition + */ + public static void addCardHintToPermanentConditional(Game game, Ability source, Permanent permanent, Hint cardHint, Duration duration, Condition condition) { + SimpleStaticAbility ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("hint")); + ability.setRuleVisible(false); + ability.addHint(cardHint); + + GainAbilityTargetEffect gainEffect = new GainAbilityTargetEffect(ability, duration); + gainEffect.setTargetPointer(new FixedTarget(permanent, game)); + ConditionalContinuousEffect conditionalEffect = new ConditionalContinuousEffect(gainEffect, condition, "test"); + conditionalEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(conditionalEffect, source); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/LivingDeathEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LivingDeathEffect.java new file mode 100644 index 0000000000..21ba872a1c --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/LivingDeathEffect.java @@ -0,0 +1,76 @@ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.*; + +public class LivingDeathEffect extends OneShotEffect { + + public LivingDeathEffect() { + super(Outcome.Benefit); + this.staticText = "Each player exiles all creature cards from their graveyard, then sacrifices all creatures they control, then puts all cards they exiled this way onto the battlefield"; + } + + public LivingDeathEffect(final LivingDeathEffect effect) { + super(effect); + } + + @Override + public LivingDeathEffect copy() { + return new LivingDeathEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { + Map> exiledCards = new HashMap<>(); + + // Move creature cards from graveyard to exile + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + Set cardsPlayer = player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game); + if (!cardsPlayer.isEmpty()) { + exiledCards.put(player.getId(), cardsPlayer); + player.moveCards(cardsPlayer, Zone.EXILED, source, game); + } + } + } + game.getState().processAction(game); + + // Sacrifice all creatures + for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { + permanent.sacrifice(source.getSourceId(), game); + } + + game.getState().processAction(game); + + // Exiled cards are put onto the battlefield at the same time under their owner's control + Set cardsToReturnFromExile = new HashSet<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + Set cardsPlayer = exiledCards.get(playerId); + if (cardsPlayer != null + && !cardsPlayer.isEmpty()) { + cardsToReturnFromExile.addAll(cardsPlayer); + } + } + } + controller.moveCards(cardsToReturnFromExile, Zone.BATTLEFIELD, source, game, false, false, true, null); + return true; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java index f2d8b038fd..4718bfdcb8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java @@ -39,7 +39,7 @@ public class LookLibraryMayPutToBottomEffect extends OneShotEffect { if (sourceObject == null || controller == null) { return false; } - if (!controller.getLibrary().isEmptyDraw()) { + if (controller.getLibrary().hasCards()) { Card card = controller.getLibrary().getFromTop(game); if (card == null) { return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/LoseHalfLifeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LoseHalfLifeTargetEffect.java index 196612959d..230c26baf8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/LoseHalfLifeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/LoseHalfLifeTargetEffect.java @@ -1,42 +1,42 @@ - -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; - -/** - * - * @author Styxo - */ -public class LoseHalfLifeTargetEffect extends OneShotEffect { - - public LoseHalfLifeTargetEffect() { - super(Outcome.Damage); - staticText = "that player loses half their life, rounded up"; - } - - public LoseHalfLifeTargetEffect(final LoseHalfLifeTargetEffect effect) { - super(effect); - } - - @Override - public LoseHalfLifeTargetEffect copy() { - return new LoseHalfLifeTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - Integer amount = (int) Math.ceil(player.getLife() / 2f); - if (amount > 0) { - player.loseLife(amount, game, false); - return true; - } - } - return false; - } -} + +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author Styxo + */ +public class LoseHalfLifeTargetEffect extends OneShotEffect { + + public LoseHalfLifeTargetEffect() { + super(Outcome.Damage); + staticText = "that player loses half their life, rounded up"; + } + + public LoseHalfLifeTargetEffect(final LoseHalfLifeTargetEffect effect) { + super(effect); + } + + @Override + public LoseHalfLifeTargetEffect copy() { + return new LoseHalfLifeTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(targetPointer.getFirst(game, source)); + if (player != null) { + Integer amount = (int) Math.ceil(player.getLife() / 2f); + if (amount > 0) { + player.loseLife(amount, game, false); + return true; + } + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ManaEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ManaEffect.java index 4acd0a564e..cc3ea6300b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ManaEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ManaEffect.java @@ -13,23 +13,23 @@ import mage.game.events.ManaEvent; import mage.players.Player; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import mage.abilities.TriggeredAbility; +import mage.constants.ManaType; /** * @author BetaSteward_at_googlemail.com */ public abstract class ManaEffect extends OneShotEffect { - protected Mana createdMana; - public ManaEffect() { super(Outcome.PutManaInPool); - createdMana = null; } public ManaEffect(final ManaEffect effect) { super(effect); - this.createdMana = effect.createdMana == null ? null : effect.createdMana.copy(); } @Override @@ -38,6 +38,15 @@ public abstract class ManaEffect extends OneShotEffect { if (player == null) { return false; } + if (game.inCheckPlayableState()) { + // During calculation of the available mana for a player the "TappedForMana" event is fired to simulate triggered mana production. + // By checking the inCheckPlayableState these events are handled to give back only the available mana of instead really producing mana + // So it's important if ManaEffects overwrite the apply method to take care for this. + if (source instanceof TriggeredAbility) { + player.addAvailableTriggeredMana(getNetMana(game, source)); + } + return true; // No need to add mana to pool during checkPlayable + } Mana manaToAdd = produceMana(game, source); if (manaToAdd != null && manaToAdd.count() > 0) { checkToFirePossibleEvents(manaToAdd, game, source); @@ -72,11 +81,60 @@ public abstract class ManaEffect extends OneShotEffect { } /** - * Produced the mana the effect can produce (DO NOT add it to mana pool -- return all added as mana object to process by replace events) + * The type of mana a permanent "could produce" is the type of mana that any + * ability of that permanent can generate, taking into account any + * applicable replacement effects. If the type of mana can’t be defined, + * there’s no type of mana that that permanent could produce. The "type" of + * mana is its color, or lack thereof (for colorless mana). + * + * @param game + * @param source + * @return + */ + public Set getProducableManaTypes(Game game, Ability source) { + return getManaTypesFromManaList(getNetMana(game, source)); + } + + public static Set getManaTypesFromManaList(List manaList) { + Set manaTypes = new HashSet<>(); + for (Mana mana : manaList) { + if (mana.getAny() > 0) { + manaTypes.add(ManaType.BLACK); + manaTypes.add(ManaType.BLUE); + manaTypes.add(ManaType.GREEN); + manaTypes.add(ManaType.WHITE); + manaTypes.add(ManaType.RED); + } + if (mana.getBlack() > 0) { + manaTypes.add(ManaType.BLACK); + } + if (mana.getBlue() > 0) { + manaTypes.add(ManaType.BLUE); + } + if (mana.getGreen() > 0) { + manaTypes.add(ManaType.GREEN); + } + if (mana.getWhite() > 0) { + manaTypes.add(ManaType.WHITE); + } + if (mana.getRed() > 0) { + manaTypes.add(ManaType.RED); + } + if (mana.getColorless() > 0) { + manaTypes.add(ManaType.COLORLESS); + } + } + return manaTypes; + } + + /** + * Produced the mana the effect can produce (DO NOT add it to mana pool -- + * return all added as mana object to process by replace events) *

- * WARNING, produceMana can be called multiple times for mana and spell available calculations - * if you don't want it then overide getNetMana to return max possible mana values - * (if you have choose dialogs or extra effects like new counters in produceMana) + * WARNING, produceMana can be called multiple times for mana and spell + * available calculations if you don't want it then overide getNetMana to + * return max possible mana values (if you have choose dialogs or extra + * effects like new counters in produceMana) * * @param game warning, can be NULL for AI score calcs (game == null) * @param source @@ -93,14 +151,10 @@ public abstract class ManaEffect extends OneShotEffect { * @param source */ public void checkToFirePossibleEvents(Mana mana, Game game, Ability source) { - if (source.getAbilityType() == AbilityType.MANA) { - for (Cost cost : source.getCosts()) { - if (cost instanceof TapSourceCost) { - ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, source.getSourceId(), source.getSourceId(), source.getControllerId(), mana); - if (!game.replaceEvent(event)) { - game.fireEvent(event); - } - } + if (source.getAbilityType() == AbilityType.MANA && source.hasTapCost()) { + ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, source.getSourceId(), source.getSourceId(), source.getControllerId(), mana); + if (!game.replaceEvent(event)) { + game.fireEvent(event); } } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java index 7713284c96..28eb5727ea 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import java.util.HashSet; @@ -80,7 +79,7 @@ public class MeldEffect extends OneShotEffect { meldCard.setOwnerId(controller.getId()); meldCard.setTopHalfCard(meldWithCard, game); meldCard.setBottomHalfCard(sourceCard, game); - meldCard.setMelded(true); + meldCard.setMelded(true, game); game.addMeldCard(meldCard.getId(), meldCard); game.getState().addCard(meldCard); meldCard.setZone(Zone.EXILED, game); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PopulateEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PopulateEffect.java index ed49adc476..4b1518252e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PopulateEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PopulateEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import mage.abilities.Ability; @@ -7,6 +6,7 @@ import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.constants.TargetController; import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -30,7 +30,7 @@ import mage.target.targetpointer.FixedTarget; public class PopulateEffect extends OneShotEffect { private final boolean tappedAndAttacking; - private static final FilterPermanent filter = new FilterPermanent("token for populate"); + private static final FilterPermanent filter = new FilterCreaturePermanent("creature token for populate"); static { filter.add(TokenPredicate.instance); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageByAllObjectsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageByAllObjectsEffect.java index 93e0777b21..c8984f75d8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageByAllObjectsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageByAllObjectsEffect.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.common; import mage.MageObject; @@ -10,13 +5,13 @@ import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.PreventionEffectImpl; import mage.constants.Duration; +import mage.filter.FilterInPlay; import mage.filter.FilterObject; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; /** - * * @author LevelX2 */ public class PreventAllDamageByAllObjectsEffect extends PreventionEffectImpl { @@ -36,7 +31,7 @@ public class PreventAllDamageByAllObjectsEffect extends PreventionEffectImpl { this.filter = filter; } - public PreventAllDamageByAllObjectsEffect(final PreventAllDamageByAllObjectsEffect effect) { + private PreventAllDamageByAllObjectsEffect(final PreventAllDamageByAllObjectsEffect effect) { super(effect); if (effect.filter != null) { this.filter = effect.filter.copy(); @@ -50,19 +45,24 @@ public class PreventAllDamageByAllObjectsEffect extends PreventionEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game) && event instanceof DamageEvent && event.getAmount() > 0) { - DamageEvent damageEvent = (DamageEvent) event; - if (damageEvent.isCombatDamage() || !onlyCombat) { - if (filter == null) { - return true; - } - MageObject damageSource = game.getObject(damageEvent.getSourceId()); - if (damageSource != null && filter.match(damageSource, game)) { - return true; - } - } + if (!super.applies(event, source, game) || !(event instanceof DamageEvent) || event.getAmount() <= 0) { + return false; } - return false; + DamageEvent damageEvent = (DamageEvent) event; + if (!damageEvent.isCombatDamage() && onlyCombat) { + return false; + } + if (filter == null) { + return true; + } + MageObject damageSource = game.getObject(damageEvent.getSourceId()); + if (damageSource == null) { + return false; + } + if (filter instanceof FilterInPlay) { + return ((FilterInPlay) filter).match(damageSource, source.getSourceId(), source.getControllerId(), game); + } + return filter.match(damageSource, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageToSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageToSourceEffect.java index dfc149a587..22cd8c14e1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageToSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageToSourceEffect.java @@ -16,7 +16,12 @@ public class PreventAllDamageToSourceEffect extends PreventionEffectImpl { public PreventAllDamageToSourceEffect(Duration duration) { super(duration, Integer.MAX_VALUE, false); - staticText = "Prevent all damage that would be dealt to {this} " + duration.toString(); + //Some durations have no text + if ( duration.toString().length()>0){ + staticText = "Prevent all damage that would be dealt to {this} " + duration.toString(); + } else { + staticText = "Prevent all damage that would be dealt to {this}"; + } } public PreventAllDamageToSourceEffect(final PreventAllDamageToSourceEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java index 51cbe53711..8edf712f0c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java @@ -1,5 +1,3 @@ - - package mage.abilities.effects.common; import mage.abilities.Ability; @@ -27,7 +25,11 @@ public class PreventAllNonCombatDamageToAllEffect extends PreventionEffectImpl { super(duration, Integer.MAX_VALUE, false); this.filter = filter; this.andToYou = andToYou; - staticText = "Prevent all non combat damage that would be dealt to " + (andToYou ? "you and " : "") + filter.getMessage() + ' ' + duration.toString(); + staticText = "Prevent all noncombat damage that would be dealt to " + (andToYou ? "you and " : "") + filter.getMessage(); + + if (duration != Duration.WhileOnBattlefield) { + staticText += ' ' + duration.toString(); + } } private PreventAllNonCombatDamageToAllEffect(final PreventAllNonCombatDamageToAllEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java index 665a930257..fe77559134 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java @@ -6,7 +6,6 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -45,7 +44,7 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(targetPointer.getFirst(game, source)); if (player != null) { - player.moveCards(player.getLibrary().getTopCards(game, amount.calculate(game, source, this)), Zone.GRAVEYARD, source, game); + player.millCards(amount.calculate(game, source, this), source, game); return true; } return false; @@ -65,23 +64,20 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect { sb.append("that target"); } - sb.append(" puts the top "); + sb.append(" mills "); if (message.isEmpty()) { if (amount.toString().equals("1")) { - sb.append("card "); + sb.append("a card"); } else { - sb.append(CardUtil.numberToText(amount.toString())).append(" cards "); + sb.append(CardUtil.numberToText(amount.toString())).append(" cards"); } } else { - sb.append(" X cards "); + sb.append("X cards, where X is the number of "); } - sb.append("of their library into their graveyard"); if (!message.isEmpty()) { - sb.append(", where X is the number of "); sb.append(message); } return sb.toString(); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java index cd7f43bddb..28e5b10ba5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java @@ -54,7 +54,6 @@ public class PutOnLibrarySourceEffect extends OneShotEffect { } else if (sourceObject instanceof Card && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { for (Player player : game.getPlayers().values()) { if (player.getGraveyard().contains(sourceObject.getId())) { - player.getGraveyard().remove(((Card) sourceObject)); ((Card) sourceObject).moveToZone(Zone.LIBRARY, source.getSourceId(), game, onTop); return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java index 08f6763ab8..423daa126e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java @@ -3,7 +3,6 @@ package mage.abilities.effects.common; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -36,21 +35,12 @@ public class PutTopCardOfLibraryIntoGraveControllerEffect extends OneShotEffect public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - return controller.moveCards(controller.getLibrary().getTopCards(game, numberCards), Zone.GRAVEYARD, source, game); + return !controller.millCards(numberCards, source, game).isEmpty(); } return false; } private String setText() { - StringBuilder sb = new StringBuilder("put the top"); - if (numberCards == 1) { - sb.append(" card"); - } else { - sb.append(" "); - sb.append(CardUtil.numberToText(numberCards)); - sb.append(" cards"); - } - sb.append(" of your library into your graveyard"); - return sb.toString(); + return "mill " + (numberCards == 1 ? "a card" : CardUtil.numberToText(numberCards) + " cards"); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java index 9cd5fa8702..fb29279aa9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java @@ -1,20 +1,18 @@ - package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.constants.TargetController; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 */ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect { @@ -33,7 +31,7 @@ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect this.staticText = setText(); } - public PutTopCardOfLibraryIntoGraveEachPlayerEffect(final PutTopCardOfLibraryIntoGraveEachPlayerEffect effect) { + private PutTopCardOfLibraryIntoGraveEachPlayerEffect(final PutTopCardOfLibraryIntoGraveEachPlayerEffect effect) { super(effect); this.numberCards = effect.numberCards; this.targetController = effect.targetController; @@ -77,7 +75,7 @@ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect private void putCardsToGravecard(UUID playerId, Ability source, Game game) { Player player = game.getPlayer(playerId); if (player != null) { - player.moveCards(player.getLibrary().getTopCards(game, numberCards.calculate(game, source, this)), Zone.GRAVEYARD, source, game); + player.millCards(numberCards.calculate(game, source, this), source, game); } } @@ -96,14 +94,13 @@ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect default: throw new UnsupportedOperationException("TargetController type not supported."); } - sb.append("puts the top "); - if(numberCards.toString().equals("1")) { - sb.append("card"); + sb.append("mills "); + if (numberCards.toString().equals("1")) { + sb.append("a card"); } else { sb.append(CardUtil.numberToText(numberCards.toString())); sb.append(" cards"); } - sb.append(" of their library into their graveyard"); return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java index d45f62ad2c..5c33b2f936 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import mage.abilities.Ability; @@ -6,7 +5,6 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -42,21 +40,20 @@ public class PutTopCardOfLibraryIntoGraveTargetEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(targetPointer.getFirst(game, source)); if (player != null) { - player.moveCards(player.getLibrary().getTopCards(game, numberCards.calculate(game, source, this)), Zone.GRAVEYARD, source, game); + player.millCards(numberCards.calculate(game, source, this), source, game); return true; } return false; } private String setText() { - StringBuilder sb = new StringBuilder("target player puts the top "); + StringBuilder sb = new StringBuilder("target player mills "); if (numberCards.toString().equals("1")) { - sb.append(" card"); + sb.append("a card"); } else { sb.append(CardUtil.numberToText(numberCards.toString())); sb.append(" cards"); } - sb.append(" of their library into their graveyard"); return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java index 01a4daf46f..009d38db17 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import mage.abilities.Ability; @@ -10,7 +9,6 @@ import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; /** - * * @author LevelX2 */ public class RegenerateAllEffect extends OneShotEffect { @@ -18,7 +16,7 @@ public class RegenerateAllEffect extends OneShotEffect { private final FilterPermanent filter; public RegenerateAllEffect(FilterPermanent filter) { - super(Outcome.DestroyPermanent); + super(Outcome.Regenerate); this.filter = filter; staticText = "Regenerate each " + filter.getMessage(); } @@ -42,5 +40,4 @@ public class RegenerateAllEffect extends OneShotEffect { } return true; } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RegenerateAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RegenerateAttachedEffect.java index 8e259eba9e..6d16d6415b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RegenerateAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RegenerateAttachedEffect.java @@ -1,47 +1,49 @@ - package mage.abilities.effects.common; -import java.util.Locale; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.OneShotEffect; import mage.constants.AttachmentType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.Locale; /** * @author jeff */ -public class RegenerateAttachedEffect extends ReplacementEffectImpl { +public class RegenerateAttachedEffect extends OneShotEffect { protected AttachmentType attachmentType; public RegenerateAttachedEffect(AttachmentType attachmentType) { - super(Duration.EndOfTurn, Outcome.Regenerate); + super(Outcome.Regenerate); this.attachmentType = attachmentType; this.setText(); } public RegenerateAttachedEffect(final RegenerateAttachedEffect effect) { super(effect); + this.attachmentType = effect.attachmentType; } @Override public boolean apply(Game game, Ability source) { - //20110204 - 701.11 - Permanent permanent = game.getPermanent(source.getSourceId()); + // must use lki cause attachment can be sacrificed to activate regen + Permanent attachment = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (attachment == null) { + return false; + } + Permanent permanent = game.getPermanent(attachment.getAttachedTo()); if (permanent == null) { return false; } - Permanent equipped = game.getPermanent(permanent.getAttachedTo()); - if (equipped != null && equipped.regenerate(this.getId(), game)) { - this.used = true; - return true; - } - return false; + + RegenerateTargetEffect regenEffect = new RegenerateTargetEffect(); + regenEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(regenEffect, source); + return true; } @Override @@ -49,32 +51,6 @@ public class RegenerateAttachedEffect extends ReplacementEffectImpl { return new RegenerateAttachedEffect(this); } - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return apply(game, source); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DESTROY_PERMANENT; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - //20110204 - 701.11c - event.getAmount() is used to signal if regeneration is allowed - Permanent equipment = game.getPermanent(source.getSourceId()); - if (equipment != null) { - Permanent equipped = game.getPermanent(equipment.getAttachedTo()); - if (equipped != null) { - UUID equippedID = equipped.getId(); - if (event.getAmount() == 0 && event.getTargetId().equals(equippedID) && !this.used) { - return true; - } - } - } - return false; - } - private void setText() { staticText = "Regenerate " + attachmentType.verb().toLowerCase(Locale.ENGLISH) + " creature"; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java index 9bd10c4645..cc2f548c88 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java @@ -1,18 +1,20 @@ - - package mage.abilities.effects.common; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.hint.Hint; +import mage.abilities.hint.HintUtils; +import mage.constants.Duration; +import mage.constants.Outcome; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.util.CardUtil; + +import java.util.UUID; /** - * - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class RegenerateSourceEffect extends ReplacementEffectImpl { @@ -21,6 +23,11 @@ public class RegenerateSourceEffect extends ReplacementEffectImpl { staticText = "Regenerate {this}"; } + public RegenerateSourceEffect(String targetName) { + super(Duration.EndOfTurn, Outcome.Regenerate); + staticText = "Regenerate " + targetName; + } + public RegenerateSourceEffect(final RegenerateSourceEffect effect) { super(effect); } @@ -29,13 +36,22 @@ public class RegenerateSourceEffect extends ReplacementEffectImpl { public boolean apply(Game game, Ability source) { //20110204 - 701.11 Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null && permanent.regenerate(this.getId(), game)) { + if (permanent != null + && permanent.regenerate(source, game)) { this.used = true; + discard(); return true; } return false; } + @Override + public void init(Ability source, Game game) { + super.init(source, game); + + RegenerateSourceEffect.initRegenerationShieldInfo(game, source, source.getSourceId()); + } + @Override public RegenerateSourceEffect copy() { return new RegenerateSourceEffect(this); @@ -45,14 +61,91 @@ public class RegenerateSourceEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { return apply(game, source); } + @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DESTROY_PERMANENT; - } - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - //20110204 - 701.11c - event.getAmount() is used to signal if regeneration is allowed - return event.getAmount() == 0 && event.getTargetId().equals(source.getSourceId()) && !this.used; + return event.getType() == GameEvent.EventType.DESTROY_PERMANENT + || event.getType() == GameEvent.EventType.ZONE_CHANGE; } + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + // The regeneration effect is discarded if the permanent is blinked or changes zone + if (event.getType() == GameEvent.EventType.ZONE_CHANGE + && event.getTargetId() == source.getSourceId()) { + discard(); + return false; + } + //20110204 - 701.11c - event.getAmount() is used to signal if regeneration is allowed + return event.getAmount() == 0 + && event.getTargetId().equals(source.getSourceId()) + && !this.used; + } + + /** + * Add info about new regen shield. + * Warning, it's a workaround to show regen shields info, real effects will be use replacement logic. + * + * @param game + * @param source + * @param permanentId + */ + public static void initRegenerationShieldInfo(Game game, Ability source, UUID permanentId) { + Permanent permanent = game.getPermanent(permanentId); + if (permanent != null) { + // add one regen shield + RegenerateSourceEffect.incRegenerationShieldsAmount(game, permanent.getId()); + // add regen info + InfoEffect.addCardHintToPermanent(game, source, permanent, + RegenerationShieldsHint.instance, Duration.EndOfTurn); + } + } + + public static int getRegenerationShieldsAmount(Game game, UUID permanentId) { + // info must be reset on new turn + Integer amount = (Integer) game.getState().getValue( + CardUtil.getCardZoneString("RegenerationShieldsAmount_turn" + game.getTurnNum(), permanentId, game)); + if (amount != null) { + return amount; + } + return 0; + } + + private static void setRegenerationShieldsAmount(Game game, UUID permanentId, int amount) { + game.getState().setValue( + CardUtil.getCardZoneString("RegenerationShieldsAmount_turn" + game.getTurnNum(), permanentId, game), amount); + } + + public static int incRegenerationShieldsAmount(Game game, UUID permanentId) { + int amount = getRegenerationShieldsAmount(game, permanentId) + 1; + setRegenerationShieldsAmount(game, permanentId, amount); + return amount; + } + + public static int decRegenerationShieldsAmount(Game game, UUID permanentId) { + int amount = Math.max(0, getRegenerationShieldsAmount(game, permanentId) - 1); + setRegenerationShieldsAmount(game, permanentId, amount); + return amount; + } +} + +enum RegenerationShieldsHint implements Hint { + + instance; + + @Override + public String getText(Game game, Ability ability) { + int amount = RegenerateSourceEffect.getRegenerationShieldsAmount(game, ability.getSourceId()); + String info = "Regeneration shields: " + amount + " (permanent will be regenerated instead of destroyed)"; + if (amount > 0) { + return HintUtils.prepareText(info, null, HintUtils.HINT_ICON_GOOD); + } else { + return HintUtils.prepareText(info, null, HintUtils.HINT_ICON_BAD); + } + } + + @Override + public Hint copy() { + return instance; + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RegenerateTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RegenerateTargetEffect.java index 8d1fc9ba7a..f672ce1164 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RegenerateTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RegenerateTargetEffect.java @@ -1,7 +1,5 @@ - package mage.abilities.effects.common; -import java.util.Locale; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.ReplacementEffectImpl; @@ -13,8 +11,9 @@ import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.Target; +import java.util.Locale; + /** - * * @author maurer.it_at_gmail.com */ public class RegenerateTargetEffect extends ReplacementEffectImpl { @@ -31,13 +30,20 @@ public class RegenerateTargetEffect extends ReplacementEffectImpl { public boolean apply(Game game, Ability source) { //20110204 - 701.11 Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); - if (permanent != null && permanent.regenerate(this.getId(), game)) { + if (permanent != null && permanent.regenerate(source, game)) { this.used = true; return true; } return false; } + @Override + public void init(Ability source, Game game) { + super.init(source, game); + + RegenerateSourceEffect.initRegenerationShieldInfo(game, source, targetPointer.getFirst(game, source)); + } + @Override public RegenerateTargetEffect copy() { return new RegenerateTargetEffect(this); @@ -76,5 +82,4 @@ public class RegenerateTargetEffect extends ReplacementEffectImpl { } return sb.toString(); } - -} +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java new file mode 100644 index 0000000000..fb3075a7fa --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java @@ -0,0 +1,53 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +public class ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect extends OneShotEffect { + + public ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "put target creature card from a graveyard onto the battlefield under your control. It gains haste"; + } + + public ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect(final ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect effect) { + super(effect); + } + + @Override + public ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect copy() { + return new ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Card card = game.getCard(source.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null) { + ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + } + return true; + } + + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnCreaturesFromExileEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnCreaturesFromExileEffect.java index a33ef0f2d4..76594aa066 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnCreaturesFromExileEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnCreaturesFromExileEffect.java @@ -1,46 +1,46 @@ -package mage.abilities.effects.common; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; -import mage.game.ExileZone; -import mage.game.Game; -import mage.players.Player; - -public class ReturnCreaturesFromExileEffect extends OneShotEffect { - - private UUID exileId; - private boolean byOwner; - - public ReturnCreaturesFromExileEffect(UUID exileId, boolean byOwner, String description) { - super(Outcome.PutCardInPlay); - this.exileId = exileId; - this.setText(description); - this.byOwner = byOwner; - } - - - public ReturnCreaturesFromExileEffect(final ReturnCreaturesFromExileEffect effect) { - super(effect); - this.exileId = effect.exileId; - } - - @Override - public ReturnCreaturesFromExileEffect copy() { - return new ReturnCreaturesFromExileEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - ExileZone exile = game.getExile().getExileZone(exileId); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && exile != null) { - controller.moveCards(exile.getCards(new FilterCreatureCard(), game), Zone.BATTLEFIELD, source, game, false, false, this.byOwner, null); - return true; - } - return false; - } -} +package mage.abilities.effects.common; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.common.FilterCreatureCard; +import mage.game.ExileZone; +import mage.game.Game; +import mage.players.Player; + +public class ReturnCreaturesFromExileEffect extends OneShotEffect { + + private UUID exileId; + private boolean byOwner; + + public ReturnCreaturesFromExileEffect(UUID exileId, boolean byOwner, String description) { + super(Outcome.PutCardInPlay); + this.exileId = exileId; + this.setText(description); + this.byOwner = byOwner; + } + + + public ReturnCreaturesFromExileEffect(final ReturnCreaturesFromExileEffect effect) { + super(effect); + this.exileId = effect.exileId; + } + + @Override + public ReturnCreaturesFromExileEffect copy() { + return new ReturnCreaturesFromExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + ExileZone exile = game.getExile().getExileZone(exileId); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && exile != null) { + controller.moveCards(exile.getCards(new FilterCreatureCard(), game), Zone.BATTLEFIELD, source, game, false, false, this.byOwner, null); + return true; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlTargetEffect.java index db66028de2..092c444780 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlTargetEffect.java @@ -8,10 +8,8 @@ import mage.cards.CardsImpl; import mage.cards.MeldCard; import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; -import mage.util.CardUtil; import java.util.UUID; @@ -21,22 +19,19 @@ import java.util.UUID; public class ReturnToBattlefieldUnderOwnerControlTargetEffect extends OneShotEffect { private boolean tapped; - protected boolean fromExileZone; + protected boolean returnFromExileZoneOnly; private String returnName = "that card"; private String returnUnderControlName = "its owner's"; - public ReturnToBattlefieldUnderOwnerControlTargetEffect() { - this(false); - } - - public ReturnToBattlefieldUnderOwnerControlTargetEffect(boolean tapped) { - this(tapped, false); - } - - public ReturnToBattlefieldUnderOwnerControlTargetEffect(boolean tapped, boolean fromExileZone) { + /** + * @param returnFromExileZoneOnly see https://github.com/magefree/mage/issues/5151 + * return it or that card - false + * return exiled card - true + */ + public ReturnToBattlefieldUnderOwnerControlTargetEffect(boolean tapped, boolean returnFromExileZoneOnly) { super(Outcome.Benefit); this.tapped = tapped; - this.fromExileZone = fromExileZone; + this.returnFromExileZoneOnly = returnFromExileZoneOnly; updateText(); } @@ -44,7 +39,7 @@ public class ReturnToBattlefieldUnderOwnerControlTargetEffect extends OneShotEff public ReturnToBattlefieldUnderOwnerControlTargetEffect(final ReturnToBattlefieldUnderOwnerControlTargetEffect effect) { super(effect); this.tapped = effect.tapped; - this.fromExileZone = effect.fromExileZone; + this.returnFromExileZoneOnly = effect.returnFromExileZoneOnly; this.returnName = effect.returnName; this.returnUnderControlName = effect.returnUnderControlName; @@ -52,7 +47,7 @@ public class ReturnToBattlefieldUnderOwnerControlTargetEffect extends OneShotEff } private void updateText() { - this.staticText = "return " + this.returnName + this.staticText = "then return " + this.returnName + " to the battlefield" + (tapped ? " tapped" : "") + " under " + this.returnUnderControlName + " control"; } @@ -67,27 +62,21 @@ public class ReturnToBattlefieldUnderOwnerControlTargetEffect extends OneShotEff Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Cards cardsToBattlefield = new CardsImpl(); - if (fromExileZone) { - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - if (exileZoneId != null) { - ExileZone exileZone = game.getExile().getExileZone(exileZoneId); - if (exileZone != null) { - for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { - if (exileZone.contains(targetId)) { - cardsToBattlefield.add(targetId); - } else { - Card card = game.getCard(targetId); - if (card instanceof MeldCard) { - MeldCard meldCard = (MeldCard) card; - Card topCard = meldCard.getTopHalfCard(); - Card bottomCard = meldCard.getBottomHalfCard(); - if (topCard.getZoneChangeCounter(game) == meldCard.getTopLastZoneChangeCounter() && exileZone.contains(topCard.getId())) { - cardsToBattlefield.add(topCard); - } - if (bottomCard.getZoneChangeCounter(game) == meldCard.getBottomLastZoneChangeCounter() && exileZone.contains(bottomCard.getId())) { - cardsToBattlefield.add(bottomCard); - } - } + if (returnFromExileZoneOnly) { + for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { + if (game.getExile().containsId(targetId, game)) { + cardsToBattlefield.add(targetId); + } else { + Card card = game.getCard(targetId); + if (card instanceof MeldCard) { + MeldCard meldCard = (MeldCard) card; + Card topCard = meldCard.getTopHalfCard(); + Card bottomCard = meldCard.getBottomHalfCard(); + if (topCard.getZoneChangeCounter(game) == meldCard.getTopLastZoneChangeCounter() && game.getExile().containsId(topCard.getId(), game)) { + cardsToBattlefield.add(topCard); + } + if (bottomCard.getZoneChangeCounter(game) == meldCard.getBottomLastZoneChangeCounter() && game.getExile().containsId(bottomCard.getId(), game)) { + cardsToBattlefield.add(bottomCard); } } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java index 25936223dd..839a7ba304 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java @@ -8,10 +8,8 @@ import mage.cards.CardsImpl; import mage.cards.MeldCard; import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; -import mage.util.CardUtil; import java.util.UUID; @@ -20,7 +18,7 @@ import java.util.UUID; */ public class ReturnToBattlefieldUnderYourControlTargetEffect extends OneShotEffect { - private boolean fromExileZone; + private boolean returnFromExileZoneOnly; private boolean tapped; private boolean attacking; private String returnName = "that card"; @@ -30,17 +28,18 @@ public class ReturnToBattlefieldUnderYourControlTargetEffect extends OneShotEffe this(false); } - public ReturnToBattlefieldUnderYourControlTargetEffect(boolean fromExileZone) { - this(fromExileZone, false, false); + public ReturnToBattlefieldUnderYourControlTargetEffect(boolean returnFromExileZoneOnly) { + this(returnFromExileZoneOnly, false, false); } /** - * @param fromExileZone - the card will only be returned if it's still in - * the source object specific exile zone + * @param returnFromExileZoneOnly see https://github.com/magefree/mage/issues/5151 + * return it or that card - false + * return exiled card - true */ - public ReturnToBattlefieldUnderYourControlTargetEffect(boolean fromExileZone, boolean tapped, boolean attacking) { + public ReturnToBattlefieldUnderYourControlTargetEffect(boolean returnFromExileZoneOnly, boolean tapped, boolean attacking) { super(Outcome.Benefit); - this.fromExileZone = fromExileZone; + this.returnFromExileZoneOnly = returnFromExileZoneOnly; this.tapped = tapped; this.attacking = attacking; @@ -49,7 +48,7 @@ public class ReturnToBattlefieldUnderYourControlTargetEffect extends OneShotEffe public ReturnToBattlefieldUnderYourControlTargetEffect(final ReturnToBattlefieldUnderYourControlTargetEffect effect) { super(effect); - this.fromExileZone = effect.fromExileZone; + this.returnFromExileZoneOnly = effect.returnFromExileZoneOnly; this.tapped = effect.tapped; this.attacking = effect.attacking; this.returnName = effect.returnName; @@ -75,27 +74,21 @@ public class ReturnToBattlefieldUnderYourControlTargetEffect extends OneShotEffe Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Cards cardsToBattlefield = new CardsImpl(); - if (fromExileZone) { - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - if (exileZoneId != null) { - ExileZone exileZone = game.getExile().getExileZone(exileZoneId); - if (exileZone != null) { - for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { - if (exileZone.contains(targetId)) { - cardsToBattlefield.add(targetId); - } else { - Card card = game.getCard(targetId); - if (card instanceof MeldCard) { - MeldCard meldCard = (MeldCard) card; - Card topCard = meldCard.getTopHalfCard(); - Card bottomCard = meldCard.getBottomHalfCard(); - if (topCard.getZoneChangeCounter(game) == meldCard.getTopLastZoneChangeCounter() && exileZone.contains(topCard.getId())) { - cardsToBattlefield.add(topCard); - } - if (bottomCard.getZoneChangeCounter(game) == meldCard.getBottomLastZoneChangeCounter() && exileZone.contains(bottomCard.getId())) { - cardsToBattlefield.add(bottomCard); - } - } + if (returnFromExileZoneOnly) { + for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { + if (game.getExile().containsId(targetId, game)) { + cardsToBattlefield.add(targetId); + } else { + Card card = game.getCard(targetId); + if (card instanceof MeldCard) { + MeldCard meldCard = (MeldCard) card; + Card topCard = meldCard.getTopHalfCard(); + Card bottomCard = meldCard.getBottomHalfCard(); + if (topCard.getZoneChangeCounter(game) == meldCard.getTopLastZoneChangeCounter() && game.getExile().containsId(topCard.getId(), game)) { + cardsToBattlefield.add(topCard); + } + if (bottomCard.getZoneChangeCounter(game) == meldCard.getBottomLastZoneChangeCounter() && game.getExile().containsId(bottomCard.getId(), game)) { + cardsToBattlefield.add(bottomCard); } } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java index b719eaca60..2d381520ed 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java @@ -1,149 +1,149 @@ - -package mage.abilities.effects.common; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Library; -import mage.players.Player; - -/** - * - * @author Styxo - */ -public class RevealCardsFromLibraryUntilEffect extends OneShotEffect { - - private FilterCard filter; - private Zone zoneToPutRest; - private Zone zoneToPutCard; - private boolean shuffleRestInto; - private boolean anyOrder; - - public RevealCardsFromLibraryUntilEffect(FilterCard filter, Zone zoneToPutCard, Zone zoneToPutRest) { - this(filter, zoneToPutCard, zoneToPutRest, false, false); - } - - public RevealCardsFromLibraryUntilEffect(FilterCard filter, Zone zoneToPutCard, Zone zoneToPutRest, boolean shuffleRestInto) { - this(filter, zoneToPutCard, zoneToPutRest, shuffleRestInto, false); - } - - public RevealCardsFromLibraryUntilEffect(FilterCard filter, Zone zoneToPutCard, Zone zoneToPutRest, boolean shuffleRestInto, boolean anyOrder) { - super(Outcome.Benefit); - this.filter = filter; - this.zoneToPutCard = zoneToPutCard; - this.zoneToPutRest = zoneToPutRest; - this.shuffleRestInto = shuffleRestInto; - this.anyOrder = anyOrder; - setText(); - } - - public RevealCardsFromLibraryUntilEffect(final RevealCardsFromLibraryUntilEffect effect) { - super(effect); - this.filter = effect.filter; - this.zoneToPutCard = effect.zoneToPutCard; - this.zoneToPutRest = effect.zoneToPutRest; - this.shuffleRestInto = effect.shuffleRestInto; - this.anyOrder = effect.anyOrder; - setText(); - } - - @Override - public RevealCardsFromLibraryUntilEffect copy() { - return new RevealCardsFromLibraryUntilEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && controller.getLibrary().hasCards()) { - Cards cards = new CardsImpl(); - Library library = controller.getLibrary(); - Card card = null; - do { - card = library.removeFromTop(game); - if (card != null) { - cards.add(card); - } - } while (library.hasCards() && card != null && !filter.match(card, game)); - // reveal cards - if (!cards.isEmpty()) { - controller.revealCards(sourceObject.getIdName(), cards, game); - if (filter.match(card, game)) { - // put card in correct zone - controller.moveCards(card, zoneToPutCard, source, game); - // remove it from revealed card list - cards.remove(card); - } - // Put the rest in correct zone - switch (zoneToPutRest) { - case LIBRARY: { - if (!cards.isEmpty()) { - if (shuffleRestInto) { - library.addAll(cards.getCards(game), game); - } else { - controller.putCardsOnBottomOfLibrary(cards, game, source, anyOrder); - } - } - break; - } - default: - if (!cards.isEmpty()) { - controller.moveCards(cards, zoneToPutRest, source, game); - } - } - } - return true; - } - return false; - } - - private void setText() { - StringBuilder sb = new StringBuilder("reveal cards from the top of your library until you reveal a " + filter.getMessage() + ". Put that card "); - - switch (zoneToPutCard) { - case HAND: { - sb.append("into your hand "); - break; - } - case BATTLEFIELD: { - sb.append("onto the battlefield"); - break; - } - } - - switch (zoneToPutRest) { - case GRAVEYARD: { - sb.append(" and put all other cards revealed this way into your graveyard."); - break; - } - case LIBRARY: { - if (shuffleRestInto) { - sb.append(", then shuffles the rest into their library."); - } else { - sb.append(" and the rest on the bottom of your library in "); - if (anyOrder) { - sb.append("any"); - } else { - sb.append("a random"); - - } - sb.append(" order."); - } - break; - } - case EXILED: { - sb.append(" and exile all other cards revealed this way."); - break; - } - } - staticText = sb.toString(); - } -} + +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Library; +import mage.players.Player; + +/** + * + * @author Styxo + */ +public class RevealCardsFromLibraryUntilEffect extends OneShotEffect { + + private FilterCard filter; + private Zone zoneToPutRest; + private Zone zoneToPutCard; + private boolean shuffleRestInto; + private boolean anyOrder; + + public RevealCardsFromLibraryUntilEffect(FilterCard filter, Zone zoneToPutCard, Zone zoneToPutRest) { + this(filter, zoneToPutCard, zoneToPutRest, false, false); + } + + public RevealCardsFromLibraryUntilEffect(FilterCard filter, Zone zoneToPutCard, Zone zoneToPutRest, boolean shuffleRestInto) { + this(filter, zoneToPutCard, zoneToPutRest, shuffleRestInto, false); + } + + public RevealCardsFromLibraryUntilEffect(FilterCard filter, Zone zoneToPutCard, Zone zoneToPutRest, boolean shuffleRestInto, boolean anyOrder) { + super(Outcome.Benefit); + this.filter = filter; + this.zoneToPutCard = zoneToPutCard; + this.zoneToPutRest = zoneToPutRest; + this.shuffleRestInto = shuffleRestInto; + this.anyOrder = anyOrder; + setText(); + } + + public RevealCardsFromLibraryUntilEffect(final RevealCardsFromLibraryUntilEffect effect) { + super(effect); + this.filter = effect.filter; + this.zoneToPutCard = effect.zoneToPutCard; + this.zoneToPutRest = effect.zoneToPutRest; + this.shuffleRestInto = effect.shuffleRestInto; + this.anyOrder = effect.anyOrder; + setText(); + } + + @Override + public RevealCardsFromLibraryUntilEffect copy() { + return new RevealCardsFromLibraryUntilEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && controller.getLibrary().hasCards()) { + Cards cards = new CardsImpl(); + Library library = controller.getLibrary(); + Card card = null; + do { + card = library.removeFromTop(game); + if (card != null) { + cards.add(card); + } + } while (library.hasCards() && card != null && !filter.match(card, game)); + // reveal cards + if (!cards.isEmpty()) { + controller.revealCards(sourceObject.getIdName(), cards, game); + if (filter.match(card, game)) { + // put card in correct zone + controller.moveCards(card, zoneToPutCard, source, game); + // remove it from revealed card list + cards.remove(card); + } + // Put the rest in correct zone + switch (zoneToPutRest) { + case LIBRARY: { + if (!cards.isEmpty()) { + if (shuffleRestInto) { + library.addAll(cards.getCards(game), game); + } else { + controller.putCardsOnBottomOfLibrary(cards, game, source, anyOrder); + } + } + break; + } + default: + if (!cards.isEmpty()) { + controller.moveCards(cards, zoneToPutRest, source, game); + } + } + } + return true; + } + return false; + } + + private void setText() { + StringBuilder sb = new StringBuilder("reveal cards from the top of your library until you reveal a " + filter.getMessage() + ". Put that card "); + + switch (zoneToPutCard) { + case HAND: { + sb.append("into your hand "); + break; + } + case BATTLEFIELD: { + sb.append("onto the battlefield"); + break; + } + } + + switch (zoneToPutRest) { + case GRAVEYARD: { + sb.append(" and put all other cards revealed this way into your graveyard."); + break; + } + case LIBRARY: { + if (shuffleRestInto) { + sb.append(", then shuffles the rest into their library."); + } else { + sb.append(" and the rest on the bottom of your library in "); + if (anyOrder) { + sb.append("any"); + } else { + sb.append("a random"); + + } + sb.append(" order"); + } + break; + } + case EXILED: { + sb.append(" and exile all other cards revealed this way."); + break; + } + } + staticText = sb.toString(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java index dcd4a797d3..dbfd982bb9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java @@ -1,7 +1,5 @@ package mage.abilities.effects.common; -import java.util.List; -import java.util.stream.Collectors; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; @@ -18,8 +16,11 @@ import mage.players.Player; import mage.target.Target; import mage.target.targetpointer.FixedTarget; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + /** - * * @author spjspj */ public class RollPlanarDieEffect extends OneShotEffect { @@ -98,8 +99,8 @@ public class RollPlanarDieEffect extends OneShotEffect { // Steps: 1) Remove the last plane and set its effects to discarded for (CommandObject cobject : game.getState().getCommand()) { if (cobject instanceof Plane) { - if (((Plane) cobject).getAbilities() != null) { - for (Ability ability : ((Plane) cobject).getAbilities()) { + if (cobject.getAbilities() != null) { + for (Ability ability : cobject.getAbilities()) { for (Effect effect : ability.getEffects()) { if (effect instanceof ContinuousEffect) { ((ContinuousEffect) effect).discard(); @@ -107,7 +108,7 @@ public class RollPlanarDieEffect extends OneShotEffect { } } } - game.getState().removeTriggersOfSourceId(((Plane) cobject).getId()); + game.getState().removeTriggersOfSourceId(cobject.getId()); game.getState().getCommand().remove(cobject); break; } @@ -123,7 +124,7 @@ public class RollPlanarDieEffect extends OneShotEffect { boolean foundNextPlane = false; while (!foundNextPlane) { - Plane plane = Plane.getRandomPlane(); + Plane plane = Plane.createRandomPlane(); try { if (plane != null && !planesVisited.contains(plane.getName())) { foundNextPlane = true; @@ -150,7 +151,7 @@ public class RollPlanarDieEffect extends OneShotEffect { if (effect != null) { try { String emode = effect.getText(mode); - emode = emode.substring(0, 1).toLowerCase() + emode.substring(1); + emode = emode.substring(0, 1).toLowerCase(Locale.ENGLISH) + emode.substring(1); sb.append(emode); } catch (Exception e) { sb.append("perform the CHAOS action"); diff --git a/Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java deleted file mode 100644 index 462f650bae..0000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java +++ /dev/null @@ -1,42 +0,0 @@ - -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; - -/** - * - * @author LevelX2 - */ -public class SendOptionUsedEventEffect extends OneShotEffect { - - private final int value; - - public SendOptionUsedEventEffect() { - this(0); - } - - public SendOptionUsedEventEffect(int value) { - super(Outcome.Detriment); - this.value = value; - } - - public SendOptionUsedEventEffect(final SendOptionUsedEventEffect effect) { - super(effect); - this.value = effect.value; - } - - @Override - public SendOptionUsedEventEffect copy() { - return new SendOptionUsedEventEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId(), value)); - return true; - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeTargetEffect.java index cb79f6e1f6..1ac1c70900 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SetPlayerLifeTargetEffect.java @@ -1,55 +1,55 @@ - -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; - -/** - * - * @author Styxo - */ -public class SetPlayerLifeTargetEffect extends OneShotEffect { - - protected DynamicValue amount; - - public SetPlayerLifeTargetEffect(int amount) { - this(StaticValue.get(amount)); - } - - public SetPlayerLifeTargetEffect(DynamicValue amount) { - super(Outcome.Neutral); - this.amount = amount; - this.staticText = setText(); - } - - public SetPlayerLifeTargetEffect(final SetPlayerLifeTargetEffect effect) { - super(effect); - this.amount = effect.amount; - } - - @Override - public SetPlayerLifeTargetEffect copy() { - return new SetPlayerLifeTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - player.setLife(amount.calculate(game, source, this), game, source); - return true; - } - return false; - } - - private String setText() { - StringBuilder sb = new StringBuilder("Target player's life total becomes "); - sb.append(amount.toString()); - return sb.toString(); - } + +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author Styxo + */ +public class SetPlayerLifeTargetEffect extends OneShotEffect { + + protected DynamicValue amount; + + public SetPlayerLifeTargetEffect(int amount) { + this(StaticValue.get(amount)); + } + + public SetPlayerLifeTargetEffect(DynamicValue amount) { + super(Outcome.Neutral); + this.amount = amount; + this.staticText = setText(); + } + + public SetPlayerLifeTargetEffect(final SetPlayerLifeTargetEffect effect) { + super(effect); + this.amount = effect.amount; + } + + @Override + public SetPlayerLifeTargetEffect copy() { + return new SetPlayerLifeTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(targetPointer.getFirst(game, source)); + if (player != null) { + player.setLife(amount.calculate(game, source, this), game, source); + return true; + } + return false; + } + + private String setText() { + StringBuilder sb = new StringBuilder("Target player's life total becomes "); + sb.append(amount.toString()); + return sb.toString(); + } } \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/common/ShuffleHandIntoLibraryDrawThatManySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ShuffleHandIntoLibraryDrawThatManySourceEffect.java index 05c1908d48..edc6e83545 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ShuffleHandIntoLibraryDrawThatManySourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ShuffleHandIntoLibraryDrawThatManySourceEffect.java @@ -36,8 +36,8 @@ public class ShuffleHandIntoLibraryDrawThatManySourceEffect extends OneShotEffec if (cardsHand > 0) { controller.moveCards(controller.getHand(), Zone.LIBRARY, source, game); controller.shuffleLibrary(source, game); - game.applyEffects(); // then - controller.drawCards(cardsHand, game); + game.getState().processAction(game); // then + controller.drawCards(cardsHand, source.getSourceId(), game); } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java index 5166946dfa..1ff0f8b936 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java @@ -1,62 +1,67 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * - * @author Styxo - */ -public class ShuffleIntoLibraryTargetEffect extends OneShotEffect { - - public ShuffleIntoLibraryTargetEffect() { - super(Outcome.Detriment); - } - - public ShuffleIntoLibraryTargetEffect(String effectText) { - super(Outcome.Detriment); - this.staticText = effectText; - } - - public ShuffleIntoLibraryTargetEffect(final ShuffleIntoLibraryTargetEffect effect) { - super(effect); - } - - @Override - public ShuffleIntoLibraryTargetEffect copy() { - return new ShuffleIntoLibraryTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (permanent != null && controller != null) { - if (controller.moveCards(permanent, Zone.LIBRARY, source, game)) { - game.getPlayer(permanent.getOwnerId()).shuffleLibrary(source, game); - } - return true; - } - return false; - } - - @Override - public String getText(Mode mode) { - if (staticText != null && !staticText.isEmpty()) { - return staticText; - } else { - return "choose target " + mode.getTargets().get(0).getTargetName() + ". Its owner shuffles it into their library"; - } - } -} +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author Styxo + */ +public class ShuffleIntoLibraryTargetEffect extends OneShotEffect { + + boolean optional; + + public ShuffleIntoLibraryTargetEffect() { + this(false); + } + + public ShuffleIntoLibraryTargetEffect(boolean optional) { + super(Outcome.Detriment); + this.optional = optional; + } + + public ShuffleIntoLibraryTargetEffect(final ShuffleIntoLibraryTargetEffect effect) { + super(effect); + this.optional = effect.optional; + } + + @Override + public ShuffleIntoLibraryTargetEffect copy() { + return new ShuffleIntoLibraryTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject cardObject = game.getObject(getTargetPointer().getFirst(game, source)); + Player controller = game.getPlayer(source.getControllerId()); + if (cardObject != null && controller != null && cardObject instanceof Card) { + if (!optional + || controller.chooseUse(Outcome.Benefit, "Do you wish to shuffle " + cardObject.getIdName() + " into " + + (((Card) cardObject).getOwnerId().equals(source.getControllerId()) ? "your" : "its owners") + + " library?", source, game)) { + Player owner = game.getPlayer(((Card) cardObject).getOwnerId()); + if (owner != null) { + return owner.shuffleCardsToLibrary(((Card) cardObject), game, source); + } + return true; + } + } + return false; + } + + @Override + public String getText(Mode mode + ) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } else { + return "choose target " + mode.getTargets().get(0).getTargetName() + ". Its owner shuffles it into their library"; + } + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java index 4d72fe19fd..dee8ec1f45 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java @@ -1,55 +1,55 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; - -/** - * - * @author jeffwadsworth - */ - -public class SkipCombatStepEffect extends ReplacementEffectImpl { - - public SkipCombatStepEffect(Duration duration) { - super(duration, Outcome.Detriment); - staticText = "that player skips their next combat phase"; - } - - public SkipCombatStepEffect(final SkipCombatStepEffect effect) { - super(effect); - } - - @Override - public SkipCombatStepEffect copy() { - return new SkipCombatStepEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.COMBAT_PHASE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(targetPointer.getFirst(game, source)); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author jeffwadsworth + */ + +public class SkipCombatStepEffect extends ReplacementEffectImpl { + + public SkipCombatStepEffect(Duration duration) { + super(duration, Outcome.Detriment); + staticText = "that player skips their next combat phase"; + } + + public SkipCombatStepEffect(final SkipCombatStepEffect effect) { + super(effect); + } + + @Override + public SkipCombatStepEffect copy() { + return new SkipCombatStepEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COMBAT_PHASE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(targetPointer.getFirst(game, source)); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java index e0d819beaf..17dc0aba8a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java @@ -1,55 +1,55 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; - -/** - * - * @author jeffwadsworth - */ - -public class SkipNextDrawStepTargetEffect extends ReplacementEffectImpl { - - public SkipNextDrawStepTargetEffect() { - super(Duration.OneUse, Outcome.Detriment); - staticText = "Target player skips their next draw step"; - } - - public SkipNextDrawStepTargetEffect(final SkipNextDrawStepTargetEffect effect) { - super(effect); - } - - @Override - public SkipNextDrawStepTargetEffect copy() { - return new SkipNextDrawStepTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DRAW_STEP; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(source.getFirstTarget()); - } +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author jeffwadsworth + */ + +public class SkipNextDrawStepTargetEffect extends ReplacementEffectImpl { + + public SkipNextDrawStepTargetEffect() { + super(Duration.OneUse, Outcome.Detriment); + staticText = "Target player skips their next draw step"; + } + + public SkipNextDrawStepTargetEffect(final SkipNextDrawStepTargetEffect effect) { + super(effect); + } + + @Override + public SkipNextDrawStepTargetEffect copy() { + return new SkipNextDrawStepTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DRAW_STEP; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(source.getFirstTarget()); + } } \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/common/TurnFaceUpTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TurnFaceUpTargetEffect.java index e3fbd63110..edcf579fa4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/TurnFaceUpTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/TurnFaceUpTargetEffect.java @@ -1,7 +1,5 @@ - package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; @@ -9,8 +7,9 @@ import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author cg5 */ public class TurnFaceUpTargetEffect extends OneShotEffect { @@ -42,6 +41,6 @@ public class TurnFaceUpTargetEffect extends OneShotEffect { @Override public String getText(Mode mode) { - return "turn target " + mode.getTargets().toString() + " face-up"; + return "turn target " + mode.getTargets().get(0).getTargetName() + " face-up"; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java index f1ab28e27a..c5a4cb6f28 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java @@ -17,7 +17,7 @@ import mage.util.CardUtil; */ public class UntapLandsEffect extends OneShotEffect { - private static final FilterLandPermanent filter = new FilterLandPermanent("untapped lands"); + private static final FilterLandPermanent filter = new FilterLandPermanent("land(s) to untap"); static { filter.add(TappedPredicate.instance); diff --git a/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java b/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java index 074459a385..001dd53aaf 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java @@ -1,120 +1,120 @@ -package mage.abilities.effects.common; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; - -import java.util.List; -import java.util.Set; - -/** - * @author Styxo - */ -public class WishEffect extends OneShotEffect { - - private final FilterCard filter; - private final boolean reveal; - private final boolean alsoFromExile; - private final String choiceText; - - public WishEffect() { - this(new FilterCard()); - } - - public WishEffect(FilterCard filter) { - this(filter, true); - } - - public WishEffect(FilterCard filter, boolean reveal) { - this(filter, reveal, false); - } - - public WishEffect(FilterCard filter, boolean reveal, boolean alsoFromExile) { - super(Outcome.DrawCard); - this.filter = filter; - this.alsoFromExile = alsoFromExile; - this.reveal = reveal; - choiceText = "Choose " + filter.getMessage() + " you own from outside the game" - + (alsoFromExile ? " or in exile" : "") - + (reveal ? ", reveal that card," : "") - + " and put it into your hand."; - staticText = "You may c" + choiceText.substring(1, choiceText.length() - 1); - - } - - public WishEffect(final WishEffect effect) { - super(effect); - this.filter = effect.filter; - this.alsoFromExile = effect.alsoFromExile; - this.reveal = effect.reveal; - this.choiceText = effect.choiceText; - } - - @Override - public WishEffect copy() { - return new WishEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - if (controller.chooseUse(Outcome.Benefit, choiceText, source, game)) { - Cards cards = controller.getSideboard(); - List exile = game.getExile().getAllCards(game); - boolean noTargets = cards.isEmpty() && (!alsoFromExile || exile.isEmpty()); - if (noTargets) { - game.informPlayer(controller, "You have no cards outside the game" + (alsoFromExile ? " or in exile" : "") + '.'); - return true; - } - - Set filtered = cards.getCards(filter, game); - Cards filteredCards = new CardsImpl(); - for (Card card : filtered) { - filteredCards.add(card.getId()); - } - if (alsoFromExile) { - for (Card exileCard : exile) { - if (exileCard.isOwnedBy(source.getControllerId()) && filter.match(exileCard, game)) { - filteredCards.add(exileCard); - } - } - } - if (filteredCards.isEmpty()) { - game.informPlayer(controller, "You don't have " + filter.getMessage() + " outside the game" + (alsoFromExile ? " or in exile" : "") + '.'); - return true; - } - - TargetCard target = new TargetCard(Zone.ALL, filter); - target.setNotTarget(true); - if (controller.choose(Outcome.Benefit, filteredCards, target, game)) { - Card card = controller.getSideboard().get(target.getFirstTarget(), game); - if (card == null && alsoFromExile) { - card = game.getCard(target.getFirstTarget()); - } - if (card != null) { - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); - if (reveal) { - Cards revealCard = new CardsImpl(); - revealCard.add(card); - controller.revealCards(sourceObject.getIdName(), revealCard, game); - } - } - } - } - return true; - } - return false; - } - -} +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; + +import java.util.List; +import java.util.Set; + +/** + * @author Styxo + */ +public class WishEffect extends OneShotEffect { + + private final FilterCard filter; + private final boolean reveal; + private final boolean alsoFromExile; + private final String choiceText; + + public WishEffect() { + this(new FilterCard()); + } + + public WishEffect(FilterCard filter) { + this(filter, true); + } + + public WishEffect(FilterCard filter, boolean reveal) { + this(filter, reveal, false); + } + + public WishEffect(FilterCard filter, boolean reveal, boolean alsoFromExile) { + super(Outcome.DrawCard); + this.filter = filter; + this.alsoFromExile = alsoFromExile; + this.reveal = reveal; + choiceText = "Choose " + filter.getMessage() + " you own from outside the game" + + (alsoFromExile ? " or in exile" : "") + + (reveal ? ", reveal that card," : "") + + " and put it into your hand."; + staticText = "You may c" + choiceText.substring(1, choiceText.length() - 1); + + } + + public WishEffect(final WishEffect effect) { + super(effect); + this.filter = effect.filter; + this.alsoFromExile = effect.alsoFromExile; + this.reveal = effect.reveal; + this.choiceText = effect.choiceText; + } + + @Override + public WishEffect copy() { + return new WishEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller != null && sourceObject != null) { + if (controller.chooseUse(Outcome.Benefit, choiceText, source, game)) { + Cards cards = controller.getSideboard(); + List exile = game.getExile().getAllCards(game); + boolean noTargets = cards.isEmpty() && (!alsoFromExile || exile.isEmpty()); + if (noTargets) { + game.informPlayer(controller, "You have no cards outside the game" + (alsoFromExile ? " or in exile" : "") + '.'); + return true; + } + + Set filtered = cards.getCards(filter, game); + Cards filteredCards = new CardsImpl(); + for (Card card : filtered) { + filteredCards.add(card.getId()); + } + if (alsoFromExile) { + for (Card exileCard : exile) { + if (exileCard.isOwnedBy(source.getControllerId()) && filter.match(exileCard, game)) { + filteredCards.add(exileCard); + } + } + } + if (filteredCards.isEmpty()) { + game.informPlayer(controller, "You don't have " + filter.getMessage() + " outside the game" + (alsoFromExile ? " or in exile" : "") + '.'); + return true; + } + + TargetCard target = new TargetCard(Zone.ALL, filter); + target.setNotTarget(true); + if (controller.choose(Outcome.Benefit, filteredCards, target, game)) { + Card card = controller.getSideboard().get(target.getFirstTarget(), game); + if (card == null && alsoFromExile) { + card = game.getCard(target.getFirstTarget()); + } + if (card != null) { + card.moveToZone(Zone.HAND, source.getSourceId(), game, false); + if (reveal) { + Cards revealCard = new CardsImpl(); + revealCard.add(card); + controller.revealCards(sourceObject.getIdName(), revealCard, game); + } + } + } + } + return true; + } + return false; + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java index ec878eb5b1..4c438da8c1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java @@ -1,15 +1,23 @@ package mage.abilities.effects.common.asthought; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.cards.Card; import mage.constants.AsThoughEffectType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; /** * @@ -19,6 +27,7 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl { private final Zone fromZone; private final TargetController allowedCaster; + private final boolean withoutMana; public PlayFromNotOwnHandZoneTargetEffect() { this(Duration.EndOfTurn); @@ -33,15 +42,21 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl { } public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, TargetController allowedCaster, Duration duration) { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit); + this(fromZone, allowedCaster, duration, false); + } + + public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, TargetController allowedCaster, Duration duration, boolean withoutMana) { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, withoutMana ? Outcome.PlayForFree : Outcome.PutCardInPlay); this.fromZone = fromZone; this.allowedCaster = allowedCaster; + this.withoutMana = withoutMana; } public PlayFromNotOwnHandZoneTargetEffect(final PlayFromNotOwnHandZoneTargetEffect effect) { super(effect); this.fromZone = effect.fromZone; this.allowedCaster = effect.allowedCaster; + this.withoutMana = effect.withoutMana; } @Override @@ -56,27 +71,96 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + return applies(objectId, null, source, game, affectedControllerId); + } + + @Override + public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + + List targets = getTargetPointer().getTargets(game, source); + if (targets.isEmpty()) { + this.discard(); + return false; + } switch (allowedCaster) { case YOU: - if (affectedControllerId != source.getControllerId()) { + if (playerId != source.getControllerId()) { return false; } break; case OPPONENT: - if (!game.getOpponents(source.getControllerId()).contains(affectedControllerId)) { + if (!game.getOpponents(source.getControllerId()).contains(playerId)) { + return false; + } + break; + case OWNER: + if (playerId != game.getCard(objectId).getOwnerId()) { return false; } break; case ANY: break; } - List targets = getTargetPointer().getTargets(game, source); - if (targets.isEmpty()) { - this.discard(); + UUID objectIdToCast = CardUtil.getMainCardId(game, objectId); + if (targets.contains(objectIdToCast) + && playerId.equals(source.getControllerId()) + && game.getState().getZone(objectId).match(fromZone)) { + if (withoutMana) { + if (affectedAbility != null) { + objectIdToCast = affectedAbility.getSourceId(); + } + return allowCardToPlayWithoutMana(objectIdToCast, source, playerId, game); + } + return true; + } + return false; + } + + public static boolean exileAndPlayFromExile(Game game, Ability source, Card card, TargetController allowedCaster, Duration duration, boolean withoutMana) { + if (card == null) { + return true; + } + Set cards = new HashSet<>(); + cards.add(card); + return exileAndPlayFromExile(game, source, cards, allowedCaster, duration, withoutMana); + } + /** + * Exiles the cards and let the allowed player play them from exile for the given duration + * + * @param game + * @param source + * @param cards + * @param allowedCaster + * @param duration + * @param withoutMana + * @return + */ + public static boolean exileAndPlayFromExile(Game game, Ability source, Set cards, TargetController allowedCaster, Duration duration, boolean withoutMana) { + if (cards == null || cards.isEmpty()) { + return true; + } + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { return false; } - return targets.contains(objectId) - && affectedControllerId.equals(source.getControllerId()) - && game.getState().getZone(objectId).match(fromZone); + UUID exileId = CardUtil.getExileZoneId( + controller.getId().toString() + + "-" + game.getState().getTurnNum() + + "-" + sourceObject.getIdName(), game + ); + String exileName = sourceObject.getIdName() + " free play" + + (Duration.EndOfTurn.equals(duration) ? " on turn " + game.getState().getTurnNum():"") + + " for " + controller.getName(); + if (Duration.EndOfTurn.equals(duration)) { + game.getExile().createZone(exileId, exileName).setCleanupOnEndTurn(true); + } + if (!controller.moveCardsToExile(cards, source, game, true, exileId, exileName)) { + return false; + } + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, allowedCaster, duration, withoutMana); + effect.setTargetPointer(new FixedTargets(cards, game)); + game.addEffect(effect, source); + return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java index 2a30cb35f3..c545cd2488 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java @@ -48,9 +48,9 @@ public class AttacksIfAbleTargetEffect extends RequirementEffect { return staticText; } if (this.duration == Duration.EndOfTurn) { - return "Target " + mode.getTargets().get(0).getTargetName() + " attacks this turn if able"; + return "target " + mode.getTargets().get(0).getTargetName() + " attacks this turn if able"; } else { - return "Target " + mode.getTargets().get(0).getTargetName() + " attacks each turn if able"; + return "target " + mode.getTargets().get(0).getTargetName() + " attacks each turn if able"; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/BlocksIfAbleTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/BlocksIfAbleTargetEffect.java index 85963adf2f..bdf7f4b016 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/BlocksIfAbleTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/BlocksIfAbleTargetEffect.java @@ -54,10 +54,10 @@ public class BlocksIfAbleTargetEffect extends RequirementEffect { return staticText; } if (this.duration == Duration.EndOfTurn) { - return "Target " + mode.getTargets().get(0).getTargetName() + " blocks this turn if able"; + return "target " + mode.getTargets().get(0).getTargetName() + " blocks this turn if able"; } else { - return "Target " + mode.getTargets().get(0).getTargetName() + " blocks each turn if able"; + return "target " + mode.getTargets().get(0).getTargetName() + " blocks each turn if able"; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderTargetEffect.java index f21719649a..8c2e07016e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderTargetEffect.java @@ -47,9 +47,9 @@ public class CanAttackAsThoughItDidntHaveDefenderTargetEffect extends AsThoughEf } if (!mode.getTargets().isEmpty()) { if (this.duration == Duration.EndOfTurn) { - return "Target " + mode.getTargets().get(0).getTargetName() + " can attack this turn as though it didn't have defender"; + return "target " + mode.getTargets().get(0).getTargetName() + " can attack this turn as though it didn't have defender"; } else { - return "Target " + mode.getTargets().get(0).getTargetName() + " can attack as though it didn't have defender"; + return "target " + mode.getTargets().get(0).getTargetName() + " can attack as though it didn't have defender"; } } else { throw new UnsupportedOperationException("No target defined"); diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java index ecb95414c9..a3b94d6f0a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java @@ -33,7 +33,7 @@ public class CanBlockOnlyFlyingAttachedEffect extends RestrictionEffect { if (attacker == null) { return true; } - return attacker.getAbilities().contains(FlyingAbility.getInstance()); + return attacker.hasAbility(FlyingAbility.getInstance(), game); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingEffect.java index c39e9d3188..4d41916cb4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingEffect.java @@ -33,7 +33,7 @@ public class CanBlockOnlyFlyingEffect extends RestrictionEffect { if (attacker == null) { return true; } - return attacker.getAbilities().contains(FlyingAbility.getInstance()); + return attacker.hasAbility(FlyingAbility.getInstance(), game); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysSourceEffect.java index d1dc071f22..60051d3a23 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysSourceEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common.combat; import mage.abilities.Ability; @@ -12,22 +11,19 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; /** - * * @author LevelX2 */ public class CantAttackBlockUnlessPaysSourceEffect extends PayCostToAttackBlockEffectImpl { public CantAttackBlockUnlessPaysSourceEffect(Cost cost, RestrictType restrictType) { super(Duration.WhileOnBattlefield, Outcome.Detriment, restrictType, cost); - staticText = "{this} can't " + restrictType.toString() + " unless you " - + cost == null ? "" : cost.getText() - + (restrictType == RestrictType.ATTACK ? " (This cost is paid as attackers are declared.)" : ""); + staticText = "{this} can't " + restrictType.toString() + " unless you " + (cost == null ? "" : cost.getText()) + + (restrictType == RestrictType.ATTACK ? " (This cost is paid as attackers are declared.)" : ""); } public CantAttackBlockUnlessPaysSourceEffect(ManaCosts manaCosts, RestrictType restrictType) { super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK_AND_BLOCK, manaCosts); - staticText = "{this} can't " + restrictType.toString() + " unless you pay " - + manaCosts == null ? "" : manaCosts.getText(); + staticText = "{this} can't " + restrictType.toString() + " unless you pay " + (manaCosts == null ? "" : manaCosts.getText()); } public CantAttackBlockUnlessPaysSourceEffect(CantAttackBlockUnlessPaysSourceEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackControllerDueToGoadEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackControllerDueToGoadEffect.java new file mode 100644 index 0000000000..964bcf9a59 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackControllerDueToGoadEffect.java @@ -0,0 +1,46 @@ +package mage.abilities.effects.common.combat; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.RestrictionEffect; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * @author TheElk801 + */ +public class CantAttackControllerDueToGoadEffect extends RestrictionEffect { + + public CantAttackControllerDueToGoadEffect(Duration duration) { + super(duration); + } + + public CantAttackControllerDueToGoadEffect(final CantAttackControllerDueToGoadEffect effect) { + super(effect); + } + + @Override + public CantAttackControllerDueToGoadEffect copy() { + return new CantAttackControllerDueToGoadEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return this.getTargetPointer().getTargets(game, source).contains(permanent.getId()); + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { + if (defenderId == null + || game.getState().getPlayersInRange(attacker.getControllerId(), game).size() == 2) { // just 2 players left, so it may attack you + return true; + } + // A planeswalker controlled by the controller is the defender + if (game.getPermanent(defenderId) != null) { + return !game.getPermanent(defenderId).getControllerId().equals(source.getControllerId()); + } + // The controller is the defender + return !defenderId.equals(source.getControllerId()); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java index cdd9e6b0b2..a4dfb5227d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java @@ -1,13 +1,12 @@ package mage.abilities.effects.common.combat; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.RestrictionEffect; import mage.constants.Duration; import mage.game.Game; import mage.game.permanent.Permanent; -import java.util.UUID; - /** * @author TheElk801 */ @@ -33,9 +32,15 @@ public class CantAttackYouEffect extends RestrictionEffect { @Override public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { - if (defenderId == null) { + if (defenderId == null + || game.getState().getPlayersInRange(attacker.getControllerId(), game).size() == 2) { // just 2 players left, so it may attack you return true; } + // A planeswalker controlled by the controller is the defender + if (game.getPermanent(defenderId) != null) { + return !game.getPermanent(defenderId).getControllerId().equals(source.getControllerId()); + } + // The controller is the defender return !defenderId.equals(source.getControllerId()); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java index e953e8b058..578536f1d7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java @@ -41,18 +41,18 @@ public class GoadTargetEffect extends OneShotEffect { Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); if (targetCreature != null && controller != null) { - // TODO: impoves goad to allows to target controller, current AttacksIfAbleTargetEffect is not support it + // TODO: Allow goad to target controller, current AttacksIfAbleTargetEffect does not support it // https://github.com/magefree/mage/issues/5283 /* If the creature doesn’t meet any of the above exceptions and can attack, it must attack a player other than - the controller of the spell or ability that goaded it if able. It the creature can’t attack any of those + the controller of the spell or ability that goaded it if able. If the creature can’t attack any of those players but could otherwise attack, it must attack an opposing planeswalker (controlled by any opponent) or the player that goaded it. (2016-08-23) */ ContinuousEffect effect = new AttacksIfAbleTargetEffect(Duration.UntilYourNextTurn); effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source))); game.addEffect(effect, source); - effect = new CantAttackYouEffect(Duration.UntilYourNextTurn); + effect = new CantAttackControllerDueToGoadEffect(Duration.UntilYourNextTurn); // remember current controller effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source))); game.addEffect(effect, source); game.informPlayers(controller.getLogName() + " is goading " + targetCreature.getLogName()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/ActivateAbilitiesAnyTimeYouCouldCastInstantEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/ActivateAbilitiesAnyTimeYouCouldCastInstantEffect.java index d15adb8048..5a19776e0d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/ActivateAbilitiesAnyTimeYouCouldCastInstantEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/ActivateAbilitiesAnyTimeYouCouldCastInstantEffect.java @@ -19,7 +19,7 @@ import mage.game.Game; */ public class ActivateAbilitiesAnyTimeYouCouldCastInstantEffect extends AsThoughEffectImpl { - private Class activatedAbility; + private final Class activatedAbility; public ActivateAbilitiesAnyTimeYouCouldCastInstantEffect(Class activatedAbility, String activatedAbilityName) { super(AsThoughEffectType.ACTIVATE_AS_INSTANT, Duration.EndOfGame, Outcome.Benefit); @@ -45,7 +45,7 @@ public class ActivateAbilitiesAnyTimeYouCouldCastInstantEffect extends AsThoughE @Override public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { return affectedAbility.isControlledBy(source.getControllerId()) - && activatedAbility.isInstance(affectedAbility); + && activatedAbility.isAssignableFrom(affectedAbility.getClass()); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java index cf3e77643c..e013d1b60e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java @@ -47,13 +47,21 @@ public class AddCardSubTypeTargetEffect extends ContinuousEffectImpl { @Override public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } StringBuilder sb = new StringBuilder(); if (!mode.getTargets().isEmpty()) { sb.append("Target ").append(mode.getTargets().get(0).getTargetName()); } else { sb.append("It "); } - sb.append(" becomes ").append(addedSubType).append(" in addition to its other types ").append(duration.toString()); + if (addedSubType.toString().matches("(?i)^[AEIOUYaeiouy].*$")) { + sb.append(" becomes an "); + } else { + sb.append(" becomes a "); + } + sb.append(addedSubType).append(" in addition to its other types ").append(duration.toString()); return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect.java index 16b27519ee..e7727fe2b6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect.java @@ -1,165 +1,165 @@ -package mage.abilities.effects.common.continuous; - -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.Token; - -/** - * @author jeffwadsworth - */ -public class BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect extends ContinuousEffectImpl { - - public enum LoseType { - NONE, ALL, ALL_BUT_COLOR, ABILITIES, ABILITIES_SUBTYPE_AND_PT - } - - protected Token token; - protected String type; - protected LoseType loseType; // what attributes are lost - - public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(Token token, String text, Duration duration) { - this(token, text, duration, LoseType.NONE); - } - - public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(Token token, String text, Duration duration, LoseType loseType) { - super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature); - this.token = token; - this.loseType = loseType; - staticText = text; - } - - public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(final BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect effect) { - super(effect); - this.token = effect.token.copy(); - this.type = effect.type; - this.loseType = effect.loseType; - } - - @Override - public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect copy() { - return new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - Permanent attachedPermanent = game.getPermanent(source.getSourceId()); - if (attachedPermanent != null) { - Permanent permanentAttachedTo = game.getPermanent(attachedPermanent.getAttachedTo()); - if (permanentAttachedTo != null) { - affectedObjectList.add(new MageObjectReference(permanentAttachedTo, game)); - } - } - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - boolean attachedExists = false; - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null) { - for (MageObjectReference mageObjectReference : affectedObjectList) { - Permanent permanentAttachedTo = mageObjectReference.getPermanent(game); - if (permanentAttachedTo != null) { - attachedExists = true; - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - for (SuperType superType : token.getSuperType()) { - permanentAttachedTo.addSuperType(superType); - - } - // card type - switch (loseType) { - case ALL: - case ALL_BUT_COLOR: - permanentAttachedTo.getCardType().clear(); - break; - } - for (CardType cardType : token.getCardType()) { - permanentAttachedTo.addCardType(cardType); - } - - // sub type - switch (loseType) { - case ALL: - case ALL_BUT_COLOR: - case ABILITIES_SUBTYPE_AND_PT: - permanentAttachedTo.getSubtype(game).retainAll(SubType.getLandTypes()); - break; - } - for (SubType subType : token.getSubtype(game)) { - if (!permanentAttachedTo.hasSubtype(subType, game)) { - permanentAttachedTo.getSubtype(game).add(subType); - } - } - - } - break; - - case ColorChangingEffects_5: - if (sublayer == SubLayer.NA) { - if (loseType == LoseType.ALL) { - permanentAttachedTo.getColor(game).setBlack(false); - permanentAttachedTo.getColor(game).setGreen(false); - permanentAttachedTo.getColor(game).setBlue(false); - permanentAttachedTo.getColor(game).setWhite(false); - permanentAttachedTo.getColor(game).setRed(false); - } - if (token.getColor(game).hasColor()) { - permanentAttachedTo.getColor(game).setColor(token.getColor(game)); - } - } - break; - - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - switch (loseType) { - case ALL: - case ALL_BUT_COLOR: - case ABILITIES: - case ABILITIES_SUBTYPE_AND_PT: - permanentAttachedTo.removeAllAbilities(source.getSourceId(), game); - break; - } - for (Ability ability : token.getAbilities()) { - permanentAttachedTo.addAbility(ability, source.getSourceId(), game); - } - - } - break; - - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - permanentAttachedTo.getPower().setValue(token.getPower().getValue()); - permanentAttachedTo.getToughness().setValue(token.getToughness().getValue()); - } - break; - } - } - if (!attachedExists) { - discard(); - } - return true; - } - } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.PTChangingEffects_7 - || layer == Layer.AbilityAddingRemovingEffects_6 - || layer == Layer.ColorChangingEffects_5 - || layer == Layer.TypeChangingEffects_4; - } - -} +package mage.abilities.effects.common.continuous; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.Token; + +/** + * @author jeffwadsworth + */ +public class BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect extends ContinuousEffectImpl { + + public enum LoseType { + NONE, ALL, ALL_BUT_COLOR, ABILITIES, ABILITIES_SUBTYPE_AND_PT + } + + protected Token token; + protected String type; + protected LoseType loseType; // what attributes are lost + + public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(Token token, String text, Duration duration) { + this(token, text, duration, LoseType.NONE); + } + + public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(Token token, String text, Duration duration, LoseType loseType) { + super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature); + this.token = token; + this.loseType = loseType; + staticText = text; + } + + public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(final BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect effect) { + super(effect); + this.token = effect.token.copy(); + this.type = effect.type; + this.loseType = effect.loseType; + } + + @Override + public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect copy() { + return new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + Permanent attachedPermanent = game.getPermanent(source.getSourceId()); + if (attachedPermanent != null) { + Permanent permanentAttachedTo = game.getPermanent(attachedPermanent.getAttachedTo()); + if (permanentAttachedTo != null) { + affectedObjectList.add(new MageObjectReference(permanentAttachedTo, game)); + } + } + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + boolean attachedExists = false; + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment != null) { + for (MageObjectReference mageObjectReference : affectedObjectList) { + Permanent permanentAttachedTo = mageObjectReference.getPermanent(game); + if (permanentAttachedTo != null) { + attachedExists = true; + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == SubLayer.NA) { + for (SuperType superType : token.getSuperType()) { + permanentAttachedTo.addSuperType(superType); + + } + // card type + switch (loseType) { + case ALL: + case ALL_BUT_COLOR: + permanentAttachedTo.getCardType().clear(); + break; + } + for (CardType cardType : token.getCardType()) { + permanentAttachedTo.addCardType(cardType); + } + + // sub type + switch (loseType) { + case ALL: + case ALL_BUT_COLOR: + case ABILITIES_SUBTYPE_AND_PT: + permanentAttachedTo.getSubtype(game).retainAll(SubType.getLandTypes()); + break; + } + for (SubType subType : token.getSubtype(game)) { + if (!permanentAttachedTo.hasSubtype(subType, game)) { + permanentAttachedTo.getSubtype(game).add(subType); + } + } + + } + break; + + case ColorChangingEffects_5: + if (sublayer == SubLayer.NA) { + if (loseType == LoseType.ALL) { + permanentAttachedTo.getColor(game).setBlack(false); + permanentAttachedTo.getColor(game).setGreen(false); + permanentAttachedTo.getColor(game).setBlue(false); + permanentAttachedTo.getColor(game).setWhite(false); + permanentAttachedTo.getColor(game).setRed(false); + } + if (token.getColor(game).hasColor()) { + permanentAttachedTo.getColor(game).setColor(token.getColor(game)); + } + } + break; + + case AbilityAddingRemovingEffects_6: + if (sublayer == SubLayer.NA) { + switch (loseType) { + case ALL: + case ALL_BUT_COLOR: + case ABILITIES: + case ABILITIES_SUBTYPE_AND_PT: + permanentAttachedTo.removeAllAbilities(source.getSourceId(), game); + break; + } + for (Ability ability : token.getAbilities()) { + permanentAttachedTo.addAbility(ability, source.getSourceId(), game); + } + + } + break; + + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanentAttachedTo.getPower().setValue(token.getPower().getValue()); + permanentAttachedTo.getToughness().setValue(token.getToughness().getValue()); + } + break; + } + } + if (!attachedExists) { + discard(); + } + return true; + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.PTChangingEffects_7 + || layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.ColorChangingEffects_5 + || layer == Layer.TypeChangingEffects_4; + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java index 07151c6fa4..dfcabb361b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java @@ -22,6 +22,7 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { protected boolean addStillALandText; protected boolean loseName; protected boolean keepAbilities; + protected boolean removeSubtypes = false; public BecomesCreatureTargetEffect(Token token, boolean loseAllAbilities, boolean stillALand, Duration duration) { @@ -41,7 +42,7 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { * @param duration */ public BecomesCreatureTargetEffect(Token token, boolean loseAllAbilities, boolean stillALand, Duration duration, boolean loseName, - boolean keepAbilities) { + boolean keepAbilities) { super(duration, Outcome.BecomeCreature); this.token = token; this.loseAllAbilities = loseAllAbilities; @@ -86,6 +87,9 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { permanent.getCardType().clear(); // remove all CardTypes permanent.getSubtype(game).addAll(token.getSubtype(game)); } else { + if (removeSubtypes) { + permanent.getSubtype(game).clear(); + } for (SubType t : token.getSubtype(game)) { if (!permanent.hasSubtype(t, game)) { permanent.getSubtype(game).add(t); @@ -149,6 +153,11 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { return result; } + public BecomesCreatureTargetEffect setRemoveSubtypes(boolean removeSubtypes) { + this.removeSubtypes = removeSubtypes; + return this; + } + @Override public boolean apply(Game game, Ability source) { return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java index e16f3d033a..064e11c4a0 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java @@ -1,79 +1,79 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common.continuous; - -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author jeffwadsworth - */ -public class BecomesEnchantmentSourceEffect extends ContinuousEffectImpl implements SourceEffect { - - public BecomesEnchantmentSourceEffect() { - super(Duration.Custom, Outcome.AddAbility); - staticText = "{this} becomes an Enchantment"; - dependencyTypes.add(DependencyType.EnchantmentAddingRemoving); - - } - - public BecomesEnchantmentSourceEffect(final BecomesEnchantmentSourceEffect effect) { - super(effect); - } - - @Override - public BecomesEnchantmentSourceEffect copy() { - return new BecomesEnchantmentSourceEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - affectedObjectList.add(new MageObjectReference(source.getSourceId(), game)); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent permanent = affectedObjectList.get(0).getPermanent(game); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.getCardType().clear(); - permanent.getSubtype(game).clear(); - if (!permanent.getCardType().contains(CardType.ENCHANTMENT)) { - permanent.getCardType().add(CardType.ENCHANTMENT); - } - } - break; - } - return true; - } - this.discard(); - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return Layer.TypeChangingEffects_4 == layer; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common.continuous; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.CardType; +import mage.constants.DependencyType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author jeffwadsworth + */ +public class BecomesEnchantmentSourceEffect extends ContinuousEffectImpl implements SourceEffect { + + public BecomesEnchantmentSourceEffect() { + super(Duration.Custom, Outcome.AddAbility); + staticText = "{this} becomes an Enchantment"; + dependencyTypes.add(DependencyType.EnchantmentAddingRemoving); + + } + + public BecomesEnchantmentSourceEffect(final BecomesEnchantmentSourceEffect effect) { + super(effect); + } + + @Override + public BecomesEnchantmentSourceEffect copy() { + return new BecomesEnchantmentSourceEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + affectedObjectList.add(new MageObjectReference(source.getSourceId(), game)); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = affectedObjectList.get(0).getPermanent(game); + if (permanent != null) { + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == SubLayer.NA) { + permanent.getCardType().clear(); + permanent.getSubtype(game).clear(); + if (!permanent.getCardType().contains(CardType.ENCHANTMENT)) { + permanent.getCardType().add(CardType.ENCHANTMENT); + } + } + break; + } + return true; + } + this.discard(); + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return Layer.TypeChangingEffects_4 == layer; + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java index d91a9a4d3d..202788360b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java @@ -1,11 +1,5 @@ - package mage.abilities.effects.common.continuous; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; import mage.MageObjectReference; import mage.ObjectColor; import mage.abilities.Ability; @@ -13,23 +7,20 @@ import mage.abilities.common.TurnFaceUpAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.MorphAbility; import mage.cards.Card; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; +import mage.constants.*; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.*; + /** - * * @author LevelX2 */ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl implements SourceEffect { - protected Map turnFaceUpAbilityMap = new HashMap<>(); + protected Map turnFaceUpAbilityMap = new HashMap<>(); protected FilterPermanent filter; public BecomesFaceDownCreatureAllEffect(FilterPermanent filter) { @@ -40,7 +31,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple public BecomesFaceDownCreatureAllEffect(final BecomesFaceDownCreatureAllEffect effect) { super(effect); - for (Map.Entry entry: effect.turnFaceUpAbilityMap.entrySet()) { + for (Map.Entry entry : effect.turnFaceUpAbilityMap.entrySet()) { this.turnFaceUpAbilityMap.put(entry.getKey(), entry.getValue()); } this.filter = effect.filter.copy(); @@ -54,16 +45,16 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple @Override public void init(Ability source, Game game) { super.init(source, game); - for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { if (!perm.isFaceDown(game) && !perm.isTransformable()) { affectedObjectList.add(new MageObjectReference(perm, game)); perm.setFaceDown(true, game); // check for Morph Card card = game.getCard(perm.getId()); if (card != null) { - for (Ability ability: card.getAbilities()) { + for (Ability ability : card.getAbilities()) { if (ability instanceof MorphAbility) { - this.turnFaceUpAbilityMap.put(card.getId(), new TurnFaceUpAbility(((MorphAbility)ability).getMorphCosts())); + this.turnFaceUpAbilityMap.put(card.getId(), new TurnFaceUpAbility(((MorphAbility) ability).getMorphCosts())); } } } @@ -74,7 +65,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { boolean targetExists = false; - for (MageObjectReference mor: affectedObjectList) { + for (MageObjectReference mor : affectedObjectList) { Permanent permanent = mor.getPermanent(game); if (permanent != null && permanent.isFaceDown(game)) { targetExists = true; @@ -92,27 +83,35 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple break; case AbilityAddingRemovingEffects_6: Card card = game.getCard(permanent.getId()); // - List abilities = new ArrayList<>(); + List abilitiesToRemove = new ArrayList<>(); for (Ability ability : permanent.getAbilities()) { + + // keep gained abilities from other sources, removes only own (card text) if (card != null && !card.getAbilities().contains(ability)) { - // gained abilities from other sources won't be removed continue; } - // TODO: Add flag "works also face down" to ability and use it to control ability removement instead of instanceof check + + // 701.33c + // If a card with morph is manifested, its controller may turn that card face up using + // either the procedure described in rule 702.36e to turn a face-down permanent with morph face up + // or the procedure described above to turn a manifested permanent face up. + // + // so keep all tune face up abilities and other face down compatible if (ability.getWorksFaceDown()) { ability.setRuleVisible(false); continue; } + if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) { if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureAllEffect) { continue; } } - abilities.add(ability); + abilitiesToRemove.add(ability); } - permanent.getAbilities().removeAll(abilities); + permanent.removeAbilities(abilitiesToRemove, source.getSourceId(), game); if (turnFaceUpAbilityMap.containsKey(permanent.getId())) { - permanent.addAbility(turnFaceUpAbilityMap.get(permanent.getId()), game); + permanent.addAbility(turnFaceUpAbilityMap.get(permanent.getId()), source.getSourceId(), game); } break; case PTChangingEffects_7: diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java index 3471377788..06e5f1c012 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java @@ -1,8 +1,5 @@ - package mage.abilities.effects.common.continuous; -import java.util.ArrayList; -import java.util.List; import mage.MageObjectReference; import mage.ObjectColor; import mage.abilities.Ability; @@ -12,14 +9,13 @@ import mage.abilities.costs.Costs; import mage.abilities.costs.CostsImpl; import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.Card; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.ArrayList; +import java.util.List; + /** * This effect lets the card be a 2/2 face-down creature, with no text, no name, * no subtypes, and no mana cost, if it's face down on the battlefield. And it @@ -149,21 +145,31 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen Card card = game.getCard(permanent.getId()); // List abilitiesToRemove = new ArrayList<>(); for (Ability ability : permanent.getAbilities()) { + + // keep gained abilities from other sources, removes only own (card text) if (card != null && !card.getAbilities().contains(ability)) { - // gained abilities from other sources won't be removed continue; } + + // 701.33c + // If a card with morph is manifested, its controller may turn that card face up using + // either the procedure described in rule 702.36e to turn a face-down permanent with morph face up + // or the procedure described above to turn a manifested permanent face up. + // + // so keep all tune face up abilities and other face down compatible if (ability.getWorksFaceDown()) { ability.setRuleVisible(false); continue; - } else if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) { + } + + if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) { if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) { continue; } } abilitiesToRemove.add(ability); } - permanent.getAbilities().removeAll(abilitiesToRemove); + permanent.removeAbilities(abilitiesToRemove, source.getSourceId(), game); if (turnFaceUpAbility != null) { permanent.addAbility(turnFaceUpAbility, source.getSourceId(), game); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java index 901fd63335..8466445ba7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java @@ -1,7 +1,5 @@ - package mage.abilities.effects.common.continuous; -import java.util.Iterator; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; @@ -16,8 +14,9 @@ import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.Iterator; + /** - * * @author BetaSteward_at_googlemail.com */ public class BoostControlledEffect extends ContinuousEffectImpl { @@ -56,10 +55,10 @@ public class BoostControlledEffect extends ContinuousEffectImpl { * @param power * @param toughness * @param duration - * @param filter AnotherPredicate is not working, you need to use the - * excludeSource option - * @param lockedIn if true, power and toughness will be calculated only - * once, when the ability resolves + * @param filter AnotherPredicate is not working, you need to use the + * excludeSource option + * @param lockedIn if true, power and toughness will be calculated only + * once, when the ability resolves * @param excludeSource */ public BoostControlledEffect(DynamicValue power, DynamicValue toughness, Duration duration, FilterCreaturePermanent filter, boolean excludeSource, boolean lockedIn) { @@ -105,7 +104,7 @@ public class BoostControlledEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { if (this.affectedObjectsSet) { - for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { + for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { Permanent permanent = it.next().getPermanent(game); if (permanent != null) { permanent.addPower(power.calculate(game, source, this)); @@ -126,7 +125,6 @@ public class BoostControlledEffect extends ContinuousEffectImpl { } private void setText() { - String message = null; StringBuilder sb = new StringBuilder(); if (excludeSource) { sb.append("other "); @@ -150,6 +148,9 @@ public class BoostControlledEffect extends ContinuousEffectImpl { sb.append(t); sb.append((duration == Duration.EndOfTurn ? " until end of turn" : "")); + + // where X + String message = null; if (t.equals("X")) { message = toughness.getMessage(); } else if (p.equals("X")) { @@ -158,6 +159,17 @@ public class BoostControlledEffect extends ContinuousEffectImpl { if (message != null && !message.isEmpty()) { sb.append(", where X is ").append(message); } + + // for each + if (message == null) { + message = toughness.getMessage(); + if (message.isEmpty()) { + message = power.getMessage(); + } + if (!message.isEmpty()) { + sb.append(" for each " + message); + } + } staticText = sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostOpponentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostOpponentsEffect.java index 2220838308..449534640e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostOpponentsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostOpponentsEffect.java @@ -1,6 +1,6 @@ package mage.abilities.effects.common.continuous; -import java.util.Iterator; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.constants.Duration; @@ -10,10 +10,11 @@ import mage.constants.SubLayer; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; +import java.util.Iterator; import java.util.Set; import java.util.UUID; -import mage.MageObjectReference; public class BoostOpponentsEffect extends ContinuousEffectImpl { protected int power; @@ -49,7 +50,7 @@ public class BoostOpponentsEffect extends ContinuousEffectImpl { super.init(source, game); if (this.affectedObjectsSet) { Set opponents = game.getOpponents(source.getControllerId()); - for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { if (opponents.contains(perm.getControllerId())) { affectedObjectList.add(new MageObjectReference(perm, game)); } @@ -61,7 +62,7 @@ public class BoostOpponentsEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Set opponents = game.getOpponents(source.getControllerId()); if (this.affectedObjectsSet) { - for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost + for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost Permanent perm = it.next().getPermanent(game); if (perm != null) { if (opponents.contains(perm.getControllerId())) { @@ -73,7 +74,7 @@ public class BoostOpponentsEffect extends ContinuousEffectImpl { } } } else { - for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { if (opponents.contains(perm.getControllerId())) { perm.addPower(power); perm.addToughness(toughness); @@ -86,8 +87,8 @@ public class BoostOpponentsEffect extends ContinuousEffectImpl { private void setText() { StringBuilder sb = new StringBuilder(); sb.append(filter.getMessage()); - sb.append(" your opponents control get ").append(String.format("%1$+d/%2$+d", power, toughness)); - sb.append((duration== Duration.EndOfTurn?" until end of turn":"")); + sb.append(" your opponents control get ").append(CardUtil.getBoostCountAsStr(power, toughness)); + sb.append((duration == Duration.EndOfTurn ? " until end of turn" : "")); staticText = sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceWhileControlsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceWhileControlsEffect.java index 4bc2ec8b96..ddc30d4fd1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceWhileControlsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceWhileControlsEffect.java @@ -1,20 +1,18 @@ - - package mage.abilities.effects.common.continuous; +import mage.abilities.Ability; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.WhileConditionContinuousEffect; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; -import mage.abilities.Ability; -import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.effects.WhileConditionContinuousEffect; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; /** - * * @author BetaSteward_at_googlemail.com */ public class BoostSourceWhileControlsEffect extends WhileConditionContinuousEffect { @@ -28,10 +26,10 @@ public class BoostSourceWhileControlsEffect extends WhileConditionContinuousEffe this.power = power; this.toughness = toughness; this.filterDescription = filter.getMessage(); - staticText = "{this} gets " - + String.format("%1$+d/%2$+d", power, toughness) + staticText = "{this} gets " + + CardUtil.getBoostCountAsStr(power, toughness) + " as long as you control " - + (filterDescription.startsWith("an ") ? "":"a ") + + (filterDescription.startsWith("an ") ? "" : "a ") + filterDescription; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java index a330989828..c72a601922 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java @@ -28,10 +28,14 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp } public CastFromHandWithoutPayingManaCostEffect(FilterCard filter, boolean fromHand) { - super(Duration.WhileOnBattlefield, Outcome.Detriment); + this(filter, fromHand, Duration.WhileOnBattlefield); + } + + public CastFromHandWithoutPayingManaCostEffect(FilterCard filter, boolean fromHand, Duration duration) { + super(duration, Outcome.Detriment); this.filter = filter; this.fromHand = fromHand; - staticText = "You may cast " + filter.getMessage() + this.staticText = "You may cast " + filter.getMessage() + (fromHand ? " from your hand" : "") + " without paying their mana costs"; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java index 399cd4c72a..621c9b434c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java @@ -1,5 +1,7 @@ package mage.abilities.effects.common.continuous; +import java.util.Locale; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; @@ -13,9 +15,6 @@ import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; -import java.util.Locale; -import java.util.UUID; - /** * @author Plopman, JayDi85 */ @@ -27,10 +26,15 @@ import java.util.UUID; 903.9a If a commander is a melded permanent and its owner chooses to put it into the command zone this way, that permanent and the card representing it that isn’t a commander are put into the appropriate zone, and the card that represents it and is a commander is put into the command zone. + + The new state-based action that helps accomplish this rules change is worded as follows: (again, quoting from the announcement) + “If a commander is in a graveyard or in exile and that card was put into that zone since the last time state-based actions were checked, + its owner may put it into the command zone. + + If a commander would be put into its owner’s hand or library from anywhere, its owner may put it into the command zone instead. + This replacement effect may apply more than once to the same event.” */ - // Oathbreaker mode: If your Oathbreaker changes zones, you may return it to the Command Zone. The Signature Spell must return to the Command Zone. - public class CommanderReplacementEffect extends ReplacementEffectImpl { private final UUID commanderId; @@ -39,6 +43,18 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl { private final boolean forceToMove; private final String commanderTypeName; + /** + * + * @param commanderId + * @param alsoHand is the replacement effect also applied if + * commander object goes to hand zone + * @param alsoLibrary is the replacement effect also applied if + * commander object goes to library zone + * @param forceToMove used for signature spell of Oathbreaker format + * (spell is mandatory moved to command zone + * instead) + * @param commanderTypeName type of commander object to set the correct text + */ public CommanderReplacementEffect(UUID commanderId, boolean alsoHand, boolean alsoLibrary, boolean forceToMove, String commanderTypeName) { super(Duration.WhileOnBattlefield, Outcome.Benefit); String mayStr = forceToMove ? " " : " may "; @@ -89,12 +105,12 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - - if (!game.isSimulation() && commanderId.equals(zEvent.getTargetId())) { - //System.out.println("applies " + game.getTurnNum() + ": " + game.getObject(event.getTargetId()).getName() + ": " + zEvent.getFromZone() + " -> " + zEvent.getToZone() + "; " + game.getObject(zEvent.getSourceId())); + if (!commanderId.equals(event.getTargetId())) { + return false; } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getToZone().equals(Zone.HAND) && !alsoHand) { return false; } @@ -106,12 +122,15 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl { switch (zEvent.getToZone()) { case LIBRARY: case HAND: - case GRAVEYARD: - case EXILED: - if (commanderId.equals(zEvent.getTargetId())) { + return true; + } + if (forceToMove) { + switch (zEvent.getToZone()) { // Normal commander movement is handled in state-based actions in GameImpl + case BATTLEFIELD: + case GRAVEYARD: + case EXILED: return true; - } - break; + } } return false; } @@ -121,10 +140,6 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; String originToZone = zEvent.getToZone().toString().toLowerCase(Locale.ENGLISH); - if (!game.isSimulation()) { - //System.out.println("replace " + game.getTurnNum() + ": " + game.getObject(event.getTargetId()).getName() + ": " + zEvent.getFromZone() + " -> " + zEvent.getToZone() + "; " + game.getObject(zEvent.getSourceId())); - } - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { Permanent permanent = zEvent.getTarget(); if (permanent != null) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/ControlEnchantedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/ControlEnchantedEffect.java index 3b8d579a0c..d11d5c3947 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/ControlEnchantedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/ControlEnchantedEffect.java @@ -8,6 +8,7 @@ import mage.constants.Outcome; import mage.constants.SubLayer; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; /** * @author nantuko @@ -19,7 +20,7 @@ public class ControlEnchantedEffect extends ContinuousEffectImpl { } public ControlEnchantedEffect(String targetDescription) { - super(Duration.WhileOnBattlefield, Outcome.GainControl); + super(Duration.WhileOnBattlefield, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); staticText = "You control enchanted " + targetDescription; } @@ -35,8 +36,10 @@ public class ControlEnchantedEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Permanent enchantment = game.getPermanent(source.getSourceId()); + Player controllerOfEnchantment = game.getPlayer(source.getControllerId()); if (enchantment != null - && enchantment.getAttachedTo() != null) { + && enchantment.getAttachedTo() != null + && controllerOfEnchantment != null) { Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); if (permanent != null) { switch (layer) { @@ -50,6 +53,8 @@ public class ControlEnchantedEffect extends ContinuousEffectImpl { break; } return true; + } else { //remove effect if the aura or attachedTo permanent or controller of the enchantment is null + discard(); } } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java new file mode 100644 index 0000000000..e2513cac56 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java @@ -0,0 +1,67 @@ +package mage.abilities.effects.common.continuous; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +public class CreaturesBecomeOtherTypeEffect extends ContinuousEffectImpl { + + protected final FilterPermanent filter; + private final SubType subType; + + public CreaturesBecomeOtherTypeEffect(FilterPermanent filter, SubType subType, Duration duration) { + super(duration, Outcome.Neutral); + this.filter = filter; + this.subType = subType; + + this.dependendToTypes.add(DependencyType.BecomeCreature); // Opalescence and Starfield of Nyx + } + + protected CreaturesBecomeOtherTypeEffect(final CreaturesBecomeOtherTypeEffect effect) { + super(effect); + this.filter = effect.filter; + this.subType = effect.subType; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public CreaturesBecomeOtherTypeEffect copy() { + return new CreaturesBecomeOtherTypeEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + if (layer == Layer.TypeChangingEffects_4) { + for (Permanent object: game.getBattlefield().getActivePermanents(this.filter, source.getControllerId(), game)) { + if (!object.hasSubtype(this.subType, game)) { + object.getSubtype(game).add(this.subType); + } + } + } + + return true; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4; + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + + return this.filter.getMessage() + " is " + this.subType.getIndefiniteArticle() + + " " + this.subType.toString() + " in addition to its other types"; + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesCantGetOrHaveAbilityEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesCantGetOrHaveAbilityEffect.java index 51fb06870d..6ea1aabfbe 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesCantGetOrHaveAbilityEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesCantGetOrHaveAbilityEffect.java @@ -45,9 +45,7 @@ public class CreaturesCantGetOrHaveAbilityEffect extends ContinuousEffectImpl { if (controller != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { if (permanent != null) { - while (permanent.getAbilities().remove(ability)) { - // repeat as long as ability can be removed - } + permanent.removeAbility(ability, source.getSourceId(), game); } } return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java index 56d08fdbab..9259d2904a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java @@ -8,6 +8,7 @@ import mage.game.Game; import mage.game.events.GameEvent; public class DamageCantBePreventedEffect extends ContinuousRuleModifyingEffectImpl { + public DamageCantBePreventedEffect(Duration duration, String staticText, boolean messageToUser, boolean messageToLog) { super(duration, Outcome.Benefit, messageToUser, messageToLog); this.staticText = staticText; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java index ad96eedd26..36d96f04b6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java @@ -12,7 +12,10 @@ import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import java.util.*; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; /** * @author Loki @@ -22,6 +25,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl { protected Ability ability; protected boolean excludeSource; protected FilterPermanent filter; + protected boolean forceQuotes = false; public GainAbilityAllEffect(Ability ability, Duration duration) { this(ability, duration, new FilterPermanent()); @@ -56,6 +60,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl { ability.newId(); // This is needed if the effect is copied e.g. by a clone so the ability can be added multiple times to permanents this.filter = effect.filter.copy(); this.excludeSource = effect.excludeSource; + this.forceQuotes = effect.forceQuotes; } @Override @@ -82,7 +87,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl { for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost Permanent permanent = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets (e.g. Showstopper) if (permanent != null) { - permanent.addAbility(ability, source.getSourceId(), game, false); + permanent.addAbility(ability, source.getSourceId(), game); } else { it.remove(); // no longer on the battlefield, remove reference to object if (affectedObjectList.isEmpty()) { @@ -94,7 +99,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl { setRuntimeData(source, game); for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { if (!(excludeSource && perm.getId().equals(source.getSourceId())) && selectedByRuntimeData(perm, source, game)) { - perm.addAbility(ability, source.getSourceId(), game, false); + perm.addAbility(ability, source.getSourceId(), game); } } // still as long as the prev. permanent is known to the LKI (e.g. Mikaeus, the Unhallowed) so gained dies triggered ability will trigger @@ -104,7 +109,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl { Permanent perm = (Permanent) mageObject; if (!(excludeSource && perm.getId().equals(source.getSourceId())) && selectedByRuntimeData(perm, source, game)) { if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) { - perm.addAbility(ability, source.getSourceId(), game, false); + perm.addAbility(ability, source.getSourceId(), game); } } } @@ -143,7 +148,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl { StringBuilder sb = new StringBuilder(); - boolean quotes = (ability instanceof SimpleActivatedAbility) || (ability instanceof TriggeredAbility); + boolean quotes = forceQuotes || (ability instanceof SimpleActivatedAbility) || (ability instanceof TriggeredAbility); if (excludeSource) { sb.append("Other "); } @@ -171,4 +176,12 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl { } return sb.toString(); } + + /** + * Add quotes to gains abilities (by default static abilities don't have it) + */ + public GainAbilityAllEffect withForceQuotes() { + this.forceQuotes = true; + return this; + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllOfChosenSubtypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllOfChosenSubtypeEffect.java index 17e459fdc7..e9351a24e8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllOfChosenSubtypeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllOfChosenSubtypeEffect.java @@ -1,51 +1,51 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common.continuous; - -import mage.abilities.Ability; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author LevelX2 - */ -public class GainAbilityAllOfChosenSubtypeEffect extends GainAbilityAllEffect { - - SubType subtype = null; - - public GainAbilityAllOfChosenSubtypeEffect(Ability ability, Duration duration, FilterPermanent filter) { - super(ability, duration, filter); - } - - public GainAbilityAllOfChosenSubtypeEffect(final GainAbilityAllOfChosenSubtypeEffect effect) { - super(effect); - this.subtype = effect.subtype; - } - - @Override - public GainAbilityAllOfChosenSubtypeEffect copy() { - return new GainAbilityAllOfChosenSubtypeEffect(this); - } - - @Override - protected boolean selectedByRuntimeData(Permanent permanent, Ability source, Game game) { - if (subtype != null) { - return permanent.hasSubtype(subtype, game); - } - return false; - } - - @Override - protected void setRuntimeData(Ability source, Game game) { - subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type"); - - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common.continuous; + +import mage.abilities.Ability; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class GainAbilityAllOfChosenSubtypeEffect extends GainAbilityAllEffect { + + SubType subtype = null; + + public GainAbilityAllOfChosenSubtypeEffect(Ability ability, Duration duration, FilterPermanent filter) { + super(ability, duration, filter); + } + + public GainAbilityAllOfChosenSubtypeEffect(final GainAbilityAllOfChosenSubtypeEffect effect) { + super(effect); + this.subtype = effect.subtype; + } + + @Override + public GainAbilityAllOfChosenSubtypeEffect copy() { + return new GainAbilityAllOfChosenSubtypeEffect(this); + } + + @Override + protected boolean selectedByRuntimeData(Permanent permanent, Ability source, Game game) { + if (subtype != null) { + return permanent.hasSubtype(subtype, game); + } + return false; + } + + @Override + protected void setRuntimeData(Ability source, Game game) { + subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type"); + + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java index 1b6395382b..959f3cddd5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java @@ -88,7 +88,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { } } if (permanent != null) { - permanent.addAbility(ability, source.getSourceId(), game, false); + permanent.addAbility(ability, source.getSourceId(), game); } return true; } @@ -102,7 +102,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { } else { sb.append("gains "); } - sb.append('"').append(ability.getRule("this creature")).append('"'); + sb.append(ability.getRule("this creature")); if (!duration.toString().isEmpty()) { sb.append(' ').append(duration.toString()); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java index 886919d655..1dcc227e8d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java @@ -11,7 +11,6 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.UUID; @@ -24,13 +23,14 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl { protected CompoundAbility ability; protected boolean excludeSource; protected FilterPermanent filter; + protected boolean forceQuotes = false; public GainAbilityControlledEffect(Ability ability, Duration duration) { - this(ability, duration, StaticFilters.FILTER_PERMANENT); + this(ability, duration, StaticFilters.FILTER_PERMANENTS); } public GainAbilityControlledEffect(CompoundAbility ability, Duration duration) { - this(ability, duration, StaticFilters.FILTER_PERMANENT); + this(ability, duration, StaticFilters.FILTER_PERMANENTS); } public GainAbilityControlledEffect(Ability ability, Duration duration, FilterPermanent filter) { @@ -60,7 +60,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl { this.ability = effect.ability.copy(); this.filter = effect.filter.copy(); this.excludeSource = effect.excludeSource; - + this.forceQuotes = effect.forceQuotes; } @Override @@ -87,7 +87,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl { Permanent perm = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets (e.g. Showstopper) if (perm != null) { for (Ability abilityToAdd : ability) { - perm.addAbility(abilityToAdd, source.getSourceId(), game, false); + perm.addAbility(abilityToAdd, source.getSourceId(), game); } } else { it.remove(); @@ -100,7 +100,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl { for (Permanent perm : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { if (!(excludeSource && perm.getId().equals(source.getSourceId()))) { for (Ability abilityToAdd : ability) { - perm.addAbility(abilityToAdd, source.getSourceId(), game, false); + perm.addAbility(abilityToAdd, source.getSourceId(), game); } } } @@ -112,7 +112,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl { if (!(excludeSource && perm.getId().equals(source.getSourceId()))) { if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) { for (Ability abilityToAdd : ability) { - perm.addAbility(abilityToAdd, source.getSourceId(), game, false); + perm.addAbility(abilityToAdd, source.getSourceId(), game); } } } @@ -139,7 +139,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl { sb.append(filter.getMessage()).append(" you control "); if (duration == Duration.WhileOnBattlefield || duration == Duration.EndOfGame) { sb.append("have "); - if (gainedAbility.startsWith("Whenever ") || gainedAbility.startsWith("{T}")) { + if (forceQuotes || gainedAbility.startsWith("When") || gainedAbility.startsWith("{T}")) { gainedAbility = '"' + gainedAbility + '"'; } } else { @@ -149,7 +149,17 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl { if (!duration.toString().isEmpty() && duration != Duration.EndOfGame) { sb.append(' ').append(duration.toString()); } - staticText = sb.toString() + "."; + staticText = sb.toString(); + } + + /** + * Add quotes to gains abilities (by default static abilities don't have it) + * @return + */ + public GainAbilityControlledEffect withForceQuotes() { + this.forceQuotes = true; + setText(); + return this; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java index e58f88cda6..a5d48f26ac 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java @@ -1,64 +1,88 @@ - -package mage.abilities.effects.common.continuous; - -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.filter.FilterSpell; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; -import mage.players.Player; - -/** - * - * @author Styxo - */ -public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl { - - private final Ability ability; - private final FilterSpell filter; - - public GainAbilityControlledSpellsEffect(Ability ability, FilterSpell filter) { - super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - this.ability = ability; - this.filter = filter; - staticText = filter.getMessage() + " you cast have " + ability.getRule() + '.'; - } - - public GainAbilityControlledSpellsEffect(final GainAbilityControlledSpellsEffect effect) { - super(effect); - this.ability = effect.ability; - this.filter = effect.filter; - } - - @Override - public GainAbilityControlledSpellsEffect copy() { - return new GainAbilityControlledSpellsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null && permanent != null) { - for (StackObject stackObject : game.getStack()) { - // only spells cast, so no copies of spells - if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.isControlledBy(source.getControllerId())) { - Spell spell = (Spell) stackObject; - if (filter.match(spell, game)) { - if (!spell.getAbilities().contains(ability)) { - game.getState().addOtherAbility(spell.getCard(), ability); - } - } - } - } - return true; - } - return false; - } -} +package mage.abilities.effects.common.continuous; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.Card; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.players.Player; + +/** + * @author Styxo + */ +public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl { + + private final Ability ability; + private final FilterCard filter; + + public GainAbilityControlledSpellsEffect(Ability ability, FilterCard filter) { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.ability = ability; + this.filter = filter; + staticText = filter.getMessage() + " you cast have " + ability.getRule() + '.'; + } + + public GainAbilityControlledSpellsEffect(final GainAbilityControlledSpellsEffect effect) { + super(effect); + this.ability = effect.ability; + this.filter = effect.filter; + } + + @Override + public GainAbilityControlledSpellsEffect copy() { + return new GainAbilityControlledSpellsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (player != null + && permanent != null) { + for (Card card : game.getExile().getAllCards(game)) { + if (card.isOwnedBy(source.getControllerId()) + && filter.match(card, game)) { + game.getState().addOtherAbility(card, ability); + } + } + for (Card card : player.getLibrary().getCards(game)) { + if (filter.match(card, game)) { + game.getState().addOtherAbility(card, ability); + } + } + for (Card card : player.getHand().getCards(game)) { + if (filter.match(card, game)) { + game.getState().addOtherAbility(card, ability); + } + } + for (Card card : player.getGraveyard().getCards(game)) { + if (filter.match(card, game)) { + game.getState().addOtherAbility(card, ability); + } + } + for (StackObject stackObject : game.getStack()) { + // only spells cast, so no copies of spells + if ((stackObject instanceof Spell) + && !stackObject.isCopy() + && stackObject.isControlledBy(source.getControllerId())) { + Card card = game.getCard(stackObject.getSourceId()); + if (card != null + && filter.match(card, game)) { + if (!card.hasAbility(ability, game)) { + game.getState().addOtherAbility(card, ability); + return true; + } + } + } + } + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java index 6c8c4b0daa..a1c5554330 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java @@ -43,7 +43,7 @@ public class GainAbilityPairedEffect extends ContinuousEffectImpl { Permanent paired = permanent.getPairedCard().getPermanent(game); if (paired != null && paired.getPairedCard() != null && paired.getPairedCard().equals(new MageObjectReference(permanent, game))) { permanent.addAbility(ability, source.getSourceId(), game); - paired.addAbility(ability, source.getSourceId(), game, false); + paired.addAbility(ability, source.getSourceId(), game); return true; } else { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java index ddf5809187..c3a3507f5e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java @@ -103,7 +103,7 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl implements Sou permanent = game.getPermanent(source.getSourceId()); } if (permanent != null) { - permanent.addAbility(ability, source.getSourceId(), game, false); + permanent.addAbility(ability, source.getSourceId(), game); return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java index 5647423c5d..adff4f92a4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java @@ -112,7 +112,7 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl { for (UUID permanentId : targetPointer.getTargets(game, source)) { Permanent permanent = game.getPermanentOrLKIBattlefield(permanentId); if (permanent != null) { - permanent.addAbility(ability, source.getSourceId(), game, false); + permanent.addAbility(ability, source.getSourceId(), game); affectedTargets++; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java index c418dadd40..aee19ee2ba 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java @@ -1,6 +1,5 @@ package mage.abilities.effects.common.continuous; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.Mode; @@ -16,8 +15,9 @@ import mage.players.Player; import mage.target.Target; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public class GainControlTargetEffect extends ContinuousEffectImpl { @@ -31,17 +31,15 @@ public class GainControlTargetEffect extends ContinuousEffectImpl { } /** - * * @param duration * @param fixedControl Controlling player is fixed even if the controller of - * the ability changes later + * the ability changes later */ public GainControlTargetEffect(Duration duration, boolean fixedControl) { this(duration, fixedControl, null); } /** - * * @param duration * @param controllingPlayerId Player that controls the target creature */ @@ -112,8 +110,7 @@ public class GainControlTargetEffect extends ContinuousEffectImpl { } } // no valid target exists and the controller is no longer in the game, effect can be discarded - if (!oneTargetStillExists - || !controller.isInGame()) { + if (!oneTargetStillExists || !controller.isInGame()) { discard(); } firstControlChange = false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainProtectionFromTypeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainProtectionFromTypeTargetEffect.java index d2e00db536..c620c0c4b3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainProtectionFromTypeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainProtectionFromTypeTargetEffect.java @@ -38,7 +38,7 @@ public class GainProtectionFromTypeTargetEffect extends GainAbilityTargetEffect public boolean apply(Game game, Ability source) { Permanent creature = game.getPermanent(source.getFirstTarget()); if (creature != null) { - creature.addAbility(ability, game); + creature.addAbility(ability, source.getSourceId(), game); return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java index f9a6f40117..dc8daf168c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java @@ -85,9 +85,7 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl { for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost Permanent perm = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets (e.g. Showstopper) if (perm != null) { - for (Ability ability : ability) { - perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId())); - } + perm.removeAbilities(ability, source.getSourceId(), game); } else { it.remove(); if (affectedObjectList.isEmpty()) { @@ -99,9 +97,7 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl { for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { if (!(excludeSource && perm.getId().equals(source.getSourceId()))) { System.out.println(game.getTurn() + ", " + game.getPhase() + ": " + "remove from size " + perm.getAbilities().size()); - for (Ability ability : ability) { - perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId())); - } + perm.removeAbilities(ability, source.getSourceId(), game); } } // still as long as the prev. permanent is known to the LKI (e.g. Mikaeus, the Unhallowed) so gained dies triggered ability will trigger @@ -111,9 +107,7 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl { Permanent perm = (Permanent) mageObject; if (!(excludeSource && perm.getId().equals(source.getSourceId()))) { if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) { - for (Ability ability : ability) { - perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId())); - } + perm.removeAbilities(ability, source.getSourceId(), game); } } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAttachedEffect.java index 9c43b5c4c3..5007c45494 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAttachedEffect.java @@ -43,12 +43,7 @@ public class LoseAbilityAttachedEffect extends ContinuousEffectImpl { if (equipment != null && equipment.getAttachedTo() != null) { Permanent creature = game.getPermanent(equipment.getAttachedTo()); if (creature != null) { - while (creature.getAbilities().contains(ability)) { - if (!creature.getAbilities().remove(ability)) { - // Something went wrong - ability has an other id? - logger.warn("ability" + ability.getRule() + "couldn't be removed."); - } - } + creature.removeAbility(ability, source.getSourceId(), game); } } return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityOrAnotherAbilityTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityOrAnotherAbilityTargetEffect.java index 44f6031387..108f6f9054 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityOrAnotherAbilityTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityOrAnotherAbilityTargetEffect.java @@ -63,13 +63,9 @@ public class LoseAbilityOrAnotherAbilityTargetEffect extends LoseAbilityTargetEf if (player.choose(outcome, chooseAbility, game)) { String chosenAbility = chooseAbility.getChoice(); if (chosenAbility.equals(ability.getRule())) { - while (permanent.getAbilities().contains(ability)) { - permanent.getAbilities().remove(ability); - } + permanent.removeAbility(ability, source.getSourceId(), game); } else if (chosenAbility.equals(ability2.getRule())) { - while (permanent.getAbilities().contains(ability2)) { - permanent.getAbilities().remove(ability2); - } + permanent.removeAbility(ability2, source.getSourceId(), game); } } else { return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java index 6f5d03f33b..f55e2fea5a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java @@ -56,10 +56,7 @@ public class LoseAbilitySourceEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { - // 112.10 - while (permanent.getAbilities().remove(ability)) { - - } + permanent.removeAbility(ability, source.getSourceId(), game); } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityTargetEffect.java index f8fa800f08..a3195035c3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityTargetEffect.java @@ -45,18 +45,7 @@ public class LoseAbilityTargetEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent != null) { - if (ability instanceof MageSingleton) { - while (permanent.getAbilities().contains(ability)) { - permanent.getAbilities().remove(ability); - } - } else { - for (Iterator iter = permanent.getAbilities().iterator(); iter.hasNext();) { - Ability ab = iter.next(); - if (ab.getClass().equals(ability.getClass())) { - iter.remove(); - } - } - } + permanent.removeAbility(ability, source.getSourceId(), game); } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java index e913d661ab..7be4c3ef7b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java @@ -40,6 +40,6 @@ public class LoseAllCreatureTypesTargetEffect extends ContinuousEffectImpl { if (staticText != null && !staticText.isEmpty()) { return staticText; } - return "Target " + mode.getTargets().get(0).getTargetName() + " loses all creature types " + duration.toString(); + return "target " + mode.getTargets().get(0).getTargetName() + " loses all creature types " + duration.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayTheTopCardEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayTheTopCardEffect.java index 2d369f0f4d..b0119f9485 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayTheTopCardEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayTheTopCardEffect.java @@ -7,6 +7,7 @@ import mage.constants.AsThoughEffectType; import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -18,17 +19,17 @@ import java.util.UUID; */ public class PlayTheTopCardEffect extends AsThoughEffectImpl { - private FilterCard filter; + private final FilterCard filter; public PlayTheTopCardEffect() { - this(new FilterCard()); - staticText = "You may play the top card of your library"; + this(StaticFilters.FILTER_CARD); + staticText = "You may play lands and cast spells from the top of your library"; } public PlayTheTopCardEffect(FilterCard filter) { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); this.filter = filter; - staticText = "You may play the top card of your library if it's a " + filter.getMessage(); + staticText = "You may " + filter.getMessage() + " from the top of your library"; } public PlayTheTopCardEffect(final PlayTheTopCardEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessAllEffect.java index edb7b8e459..ac2b5127a9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessAllEffect.java @@ -98,6 +98,10 @@ public class SetPowerToughnessAllEffect extends ContinuousEffectImpl { @Override public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder(); sb.append(filter.getMessage()); if (filter.getMessage().toLowerCase(Locale.ENGLISH).startsWith("Each ")) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessEnchantedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessEnchantedEffect.java index a5f1cf72e3..cabb1b5de3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessEnchantedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessEnchantedEffect.java @@ -1,57 +1,57 @@ -package mage.abilities.effects.common.continuous; - -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author LevelX2 - */ -public class SetPowerToughnessEnchantedEffect extends ContinuousEffectImpl { - - private final int power; - private final int toughness; - - public SetPowerToughnessEnchantedEffect() { - this(0, 2); - } - - public SetPowerToughnessEnchantedEffect(int power, int toughness) { - super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.BoostCreature); - staticText = "Enchanted creature has base power and toughness " + power + "/" + toughness; - this.power = power; - this.toughness = toughness; - } - - public SetPowerToughnessEnchantedEffect(final SetPowerToughnessEnchantedEffect effect) { - super(effect); - this.power = effect.power; - this.toughness = effect.toughness; - } - - @Override - public SetPowerToughnessEnchantedEffect copy() { - return new SetPowerToughnessEnchantedEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); - if (enchanted != null) { - enchanted.getPower().setValue(power); - enchanted.getToughness().setValue(toughness); - } - return true; - } - return false; - } - -} +package mage.abilities.effects.common.continuous; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class SetPowerToughnessEnchantedEffect extends ContinuousEffectImpl { + + private final int power; + private final int toughness; + + public SetPowerToughnessEnchantedEffect() { + this(0, 2); + } + + public SetPowerToughnessEnchantedEffect(int power, int toughness) { + super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.BoostCreature); + staticText = "Enchanted creature has base power and toughness " + power + "/" + toughness; + this.power = power; + this.toughness = toughness; + } + + public SetPowerToughnessEnchantedEffect(final SetPowerToughnessEnchantedEffect effect) { + super(effect); + this.power = effect.power; + this.toughness = effect.toughness; + } + + @Override + public SetPowerToughnessEnchantedEffect copy() { + return new SetPowerToughnessEnchantedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment != null && enchantment.getAttachedTo() != null) { + Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); + if (enchanted != null) { + enchanted.getPower().setValue(power); + enchanted.getToughness().setValue(toughness); + } + return true; + } + return false; + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/AbilitiesCostReductionControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/AbilitiesCostReductionControllerEffect.java index b0b53d2d82..86a4c0de5b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/AbilitiesCostReductionControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/AbilitiesCostReductionControllerEffect.java @@ -1,50 +1,50 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common.cost; - -import mage.abilities.Ability; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.util.CardUtil; - -/** - * - * @author Styxo - */ -public class AbilitiesCostReductionControllerEffect extends CostModificationEffectImpl { - - private Class activatedAbility; - - public AbilitiesCostReductionControllerEffect(Class activatedAbility, String activatedAbilityName) { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); - this.activatedAbility = activatedAbility; - staticText = activatedAbilityName + " costs you pay cost {1} less"; - } - - public AbilitiesCostReductionControllerEffect(AbilitiesCostReductionControllerEffect effect) { - super(effect); - this.activatedAbility = effect.activatedAbility; - } - - @Override - public AbilitiesCostReductionControllerEffect copy() { - return new AbilitiesCostReductionControllerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - CardUtil.reduceCost(abilityToModify, 1); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify.isControlledBy(source.getControllerId()) - && activatedAbility.isInstance(abilityToModify); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common.cost; + +import mage.abilities.Ability; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.util.CardUtil; + +/** + * + * @author Styxo + */ +public class AbilitiesCostReductionControllerEffect extends CostModificationEffectImpl { + + private final Class activatedAbility; + + public AbilitiesCostReductionControllerEffect(Class activatedAbility, String activatedAbilityName) { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); + this.activatedAbility = activatedAbility; + staticText = activatedAbilityName + " costs you pay cost {1} less"; + } + + public AbilitiesCostReductionControllerEffect(AbilitiesCostReductionControllerEffect effect) { + super(effect); + this.activatedAbility = effect.activatedAbility; + } + + @Override + public AbilitiesCostReductionControllerEffect copy() { + return new AbilitiesCostReductionControllerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.reduceCost(abilityToModify, 1); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify.isControlledBy(source.getControllerId()) + && activatedAbility.isInstance(abilityToModify); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java index c043b741f9..a24c93acf8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java @@ -1,27 +1,34 @@ - - package mage.abilities.effects.common.cost; -import mage.constants.Duration; -import mage.constants.EffectType; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.CostModificationEffect; import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.EffectType; +import mage.constants.Outcome; import mage.game.Game; /** * Simple implementation of a {@link CostModificationEffect} offering simplified * construction to setup the object for use by the mage framework. - + *

+ * WARNING, if you implement custom effect and it can works on stack only (e.g. it need spell's targets to check) then + * use different apply code: + * - one for get playable mode before spell puts on stack (apply maximum possible cost reduction, use game.inCheckPlayableState()). + * - one for normal mode after spell puts on stack (apply real cost reduction) + * Example: TorgaarFamineIncarnate + * * @author maurer.it_at_gmail.com */ public abstract class CostModificationEffectImpl extends ContinuousEffectImpl implements CostModificationEffect { private final CostModificationType modificationType; - - public CostModificationEffectImpl ( Duration duration, Outcome outcome, CostModificationType type) { + + // if effect need real stack object to check then mark it as stack only (example: apply cost reduction if you target human creature) + private boolean worksOnStackOnly = false; + + public CostModificationEffectImpl(Duration duration, Outcome outcome, CostModificationType type) { super(duration, outcome); this.effectType = EffectType.COSTMODIFICATION; this.modificationType = type; @@ -30,23 +37,33 @@ public abstract class CostModificationEffectImpl extends ContinuousEffectImpl im public CostModificationEffectImpl(final CostModificationEffectImpl effect) { super(effect); this.modificationType = effect.modificationType; + this.worksOnStackOnly = effect.worksOnStackOnly; } /** * Overridden and 'no-op' implementation put in place. * - * @see #apply(mage.game.Game, mage.abilities.Ability, mage.abilities.Ability) - * * @param game * @param source * @return + * @see #apply(mage.game.Game, mage.abilities.Ability, mage.abilities.Ability) */ @Override - public final boolean apply ( Game game, Ability source ) { return false; } - + public final boolean apply(Game game, Ability source) { + return false; + } + @Override - public CostModificationType getModificationType(){ + public CostModificationType getModificationType() { return this.modificationType; } + public CostModificationEffectImpl setCanWorksOnStackOnly(boolean worksOnStackOnly) { + this.worksOnStackOnly = worksOnStackOnly; + return this; + } + + public boolean canWorksOnStackOnly() { + return this.worksOnStackOnly; + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationSourceEffect.java new file mode 100644 index 0000000000..607f180aee --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationSourceEffect.java @@ -0,0 +1,62 @@ +package mage.abilities.effects.common.cost; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +public class CostModificationSourceEffect extends CostModificationEffectImpl { + + private final Class abilityType; + private final DynamicValue value; + private final boolean reducesCost; + + public CostModificationSourceEffect(Duration duration, Outcome outcome, Class abilityType, DynamicValue value, boolean reducesCost) { + super(duration, outcome, reducesCost ? CostModificationType.REDUCE_COST : CostModificationType.INCREASE_COST); + this.abilityType = abilityType; + this.value = value; + this.reducesCost = reducesCost; + this.staticText = "this ability costs {1} " + (reducesCost ? "less" : "more") + " to activate for each " + value.getMessage(); + } + + private CostModificationSourceEffect(CostModificationSourceEffect effect) { + super(effect); + this.abilityType = effect.abilityType; + this.value = effect.value; + this.reducesCost = effect.reducesCost; + } + + @Override + public CostModificationSourceEffect copy() { + return new CostModificationSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int count = value.calculate(game, source, this); + if (reducesCost) { + CardUtil.reduceCost(abilityToModify, count); + } else { + CardUtil.increaseCost(abilityToModify, count); + } + } + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return (abilityToModify.getClass().isAssignableFrom(abilityType)) && abilityToModify.getSourceId().equals(source.getSourceId()); + } + + @Override + public String getText(Mode mode) { + return super.getText(mode); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/PlanarDieRollCostIncreasingEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/PlanarDieRollCostIncreasingEffect.java new file mode 100644 index 0000000000..0adb9dd283 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/PlanarDieRollCostIncreasingEffect.java @@ -0,0 +1,53 @@ +package mage.abilities.effects.common.cost; + +import mage.abilities.Ability; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; +import mage.watchers.common.PlanarRollWatcher; + +import java.util.UUID; + +/** + * @author JayDi85 + */ +public class PlanarDieRollCostIncreasingEffect extends CostModificationEffectImpl { + + private final UUID originalId; + + public PlanarDieRollCostIncreasingEffect(UUID originalId) { + super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.INCREASE_COST); + this.originalId = originalId; + } + + private PlanarDieRollCostIncreasingEffect(final PlanarDieRollCostIncreasingEffect effect) { + super(effect); + this.originalId = effect.originalId; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + Player activePlayer = game.getPlayer(game.getActivePlayerId()); + PlanarRollWatcher watcher = game.getState().getWatcher(PlanarRollWatcher.class); + if (activePlayer == null && watcher == null) { + return false; + } + + int rolledCounter = watcher.getNumberTimesPlanarDieRolled(activePlayer.getId()); + CardUtil.increaseCost(abilityToModify, rolledCounter); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify.getOriginalId().equals(originalId); + } + + @Override + public PlanarDieRollCostIncreasingEffect copy() { + return new PlanarDieRollCostIncreasingEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SourceCostReductionForEachCardInGraveyardEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SourceCostReductionForEachCardInGraveyardEffect.java deleted file mode 100644 index ab50288e85..0000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SourceCostReductionForEachCardInGraveyardEffect.java +++ /dev/null @@ -1,59 +0,0 @@ -package mage.abilities.effects.common.cost; - -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.filter.FilterCard; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.players.Player; -import mage.util.CardUtil; - -/** - * @author Styxo - */ -public class SourceCostReductionForEachCardInGraveyardEffect extends CostModificationEffectImpl { - - private FilterCard filter; - - public SourceCostReductionForEachCardInGraveyardEffect() { - this(StaticFilters.FILTER_CARD); - } - - public SourceCostReductionForEachCardInGraveyardEffect(FilterCard filter) { - super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - this.filter = filter; - staticText = "{this} costs {1} less to cast for each " + filter.getMessage() + " in your graveyard"; - } - - private SourceCostReductionForEachCardInGraveyardEffect(SourceCostReductionForEachCardInGraveyardEffect effect) { - super(effect); - this.filter = effect.filter.copy(); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - int reductionAmount = player.getGraveyard().count(filter, game); - CardUtil.reduceCost(abilityToModify, reductionAmount); - return true; - } - return false; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if ((abilityToModify instanceof SpellAbility) && abilityToModify.getSourceId().equals(source.getSourceId())) { - return game.getCard(abilityToModify.getSourceId()) != null; - } - return false; - } - - @Override - public SourceCostReductionForEachCardInGraveyardEffect copy() { - return new SourceCostReductionForEachCardInGraveyardEffect(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java new file mode 100644 index 0000000000..f7c1b9e95d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java @@ -0,0 +1,92 @@ +package mage.abilities.effects.common.cost; + +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.util.CardUtil; + +/** + * @author JayDi85 + */ +public class SpellCostReductionForEachSourceEffect extends CostModificationEffectImpl { + + private final DynamicValue eachAmount; + private final ManaCosts reduceManaCosts; + private final int reduceGenericMana; + + public SpellCostReductionForEachSourceEffect(int reduceGenericMana, DynamicValue eachAmount) { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); + this.eachAmount = eachAmount; + this.reduceManaCosts = null; + this.reduceGenericMana = reduceGenericMana; + + StringBuilder sb = new StringBuilder(); + sb.append("this spell costs {") + .append(this.reduceGenericMana) + .append("} less to cast for each ") + .append(this.eachAmount.getMessage()); + this.staticText = sb.toString(); + } + + public SpellCostReductionForEachSourceEffect(ManaCosts reduceManaCosts, DynamicValue eachAmount) { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); + this.eachAmount = eachAmount; + this.reduceManaCosts = reduceManaCosts; + this.reduceGenericMana = 0; + + StringBuilder sb = new StringBuilder(); + sb.append("this spell costs "); + for (String manaSymbol : reduceManaCosts.getSymbols()) { + sb.append(manaSymbol); + } + sb.append(" less to cast for each ").append(this.eachAmount.getMessage()); + this.staticText = sb.toString(); + } + + protected SpellCostReductionForEachSourceEffect(final SpellCostReductionForEachSourceEffect effect) { + super(effect); + this.eachAmount = effect.eachAmount; + this.reduceManaCosts = effect.reduceManaCosts; + this.reduceGenericMana = effect.reduceGenericMana; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + int needReduceAmount = eachAmount.calculate(game, source, this); + if (needReduceAmount > 0) { + if (reduceManaCosts != null) { + // color reduce + ManaCosts needReduceMana = new ManaCostsImpl<>(); + for (int i = 0; i <= needReduceAmount; i++) { + needReduceMana.add(reduceManaCosts.copy()); + } + CardUtil.adjustCost((SpellAbility) abilityToModify, needReduceMana, false); + } else { + // generic reduce + CardUtil.reduceCost(abilityToModify, needReduceAmount * this.reduceGenericMana); + } + } + + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) { + return true; + } + return false; + } + + @Override + public SpellCostReductionForEachSourceEffect copy() { + return new SpellCostReductionForEachSourceEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java index e280df5a06..c6bf32504e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java @@ -5,6 +5,8 @@ import mage.abilities.SpellAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.constants.CostModificationType; import mage.constants.Duration; import mage.constants.Outcome; @@ -16,7 +18,7 @@ import mage.util.CardUtil; */ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl { - private final int amount; + private final DynamicValue amount; private ManaCosts manaCostsToReduce = null; private Condition condition; @@ -26,7 +28,7 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl { public SpellCostReductionSourceEffect(ManaCosts manaCostsToReduce, Condition condition) { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); - this.amount = 0; + this.amount = StaticValue.get(0); this.manaCostsToReduce = manaCostsToReduce; this.condition = condition; @@ -35,28 +37,38 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl { for (String manaSymbol : manaCostsToReduce.getSymbols()) { sb.append(manaSymbol); } - sb.append(" less"); + sb.append(" less to cast"); if (this.condition != null) { - sb.append(" to if ").append(this.condition.toString()); + sb.append(" if ").append(this.condition.toString()); } - this.staticText = sb.toString(); } public SpellCostReductionSourceEffect(int amount) { + this(StaticValue.get(amount), null); + } + + public SpellCostReductionSourceEffect(DynamicValue amount) { this(amount, null); } public SpellCostReductionSourceEffect(int amount, Condition condition) { + this(StaticValue.get(amount), condition); + } + + public SpellCostReductionSourceEffect(DynamicValue amount, Condition condition) { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); this.amount = amount; this.condition = condition; StringBuilder sb = new StringBuilder(); - sb.append("this spell costs {").append(amount).append("} less to cast"); + sb.append("this spell costs {").append(this.amount).append("} less to cast"); if (this.condition != null) { sb.append(" ").append(this.condition.toString().startsWith("if ") ? "" : "if "); sb.append(this.condition.toString()); } + if (this.amount.toString().equals("X")) { + sb.append(", where {X} is ").append(this.amount.getMessage()); + } this.staticText = sb.toString(); } @@ -72,7 +84,7 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl { if (manaCostsToReduce != null) { CardUtil.adjustCost((SpellAbility) abilityToModify, manaCostsToReduce, false); } else { - CardUtil.reduceCost(abilityToModify, this.amount); + CardUtil.reduceCost(abilityToModify, this.amount.calculate(game, source, this)); } return true; } @@ -80,7 +92,9 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) { - return condition == null || condition.apply(game, source); + // some conditions can works after put on stack, so skip it in get playable (allows user to put card on stack anyway) + boolean skipCondition = game.inCheckPlayableState() && canWorksOnStackOnly(); + return condition == null || skipCondition || condition.apply(game, source); } return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java deleted file mode 100644 index 4391755fa3..0000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java +++ /dev/null @@ -1,52 +0,0 @@ -package mage.abilities.effects.common.cost; - -import mage.Mana; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; - -public class SpellCostReductionSourceForOpponentsEffect extends CostModificationEffectImpl { - - public SpellCostReductionSourceForOpponentsEffect() { - this("undaunted (This spell costs {1} less to cast for each opponent.)"); - } - - public SpellCostReductionSourceForOpponentsEffect(String newStaticText) { - super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = newStaticText; - } - - public SpellCostReductionSourceForOpponentsEffect(final SpellCostReductionSourceForOpponentsEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - Mana mana = spellAbility.getManaCostsToPay().getMana(); - if (mana.getGeneric() > 0) { - int count = game.getOpponents(source.getControllerId()).size(); - int newCount = mana.getGeneric() - count; - if (newCount < 0) { - newCount = 0; - } - mana.setGeneric(newCount); - spellAbility.getManaCostsToPay().load(mana.toString()); - return true; - } - return false; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify instanceof SpellAbility && abilityToModify.getSourceId().equals(source.getSourceId()); - } - - @Override - public SpellCostReductionSourceForOpponentsEffect copy() { - return new SpellCostReductionSourceForOpponentsEffect(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java deleted file mode 100644 index cceda07b55..0000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java +++ /dev/null @@ -1,66 +0,0 @@ - -package mage.abilities.effects.common.cost; - -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.cards.Card; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.util.CardUtil; - -/** - * - * @author Plopman - */ -public class SpellsCostIncreasementAllEffect extends CostModificationEffectImpl { - - private FilterCard filter; - private int amount; - - public SpellsCostIncreasementAllEffect(int amount) { - this(new FilterCard("Spells"), amount); - } - - public SpellsCostIncreasementAllEffect(FilterCard filter, int amount) { - super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); - this.filter = filter; - this.amount = amount; - this.staticText = new StringBuilder(filter.getMessage()).append(" cost {").append(amount).append("} more to cast").toString(); - } - - protected SpellsCostIncreasementAllEffect(SpellsCostIncreasementAllEffect effect) { - super(effect); - this.filter = effect.filter; - this.amount = effect.amount; - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - CardUtil.increaseCost(abilityToModify, this.amount); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); - if (spell != null) { - return this.filter.match(spell, game); - } else { - // used at least for flashback ability because Flashback ability doesn't use stack - Card sourceCard = game.getCard(abilityToModify.getSourceId()); - return sourceCard != null && this.filter.match(sourceCard, game); - } - } - return false; - } - - @Override - public SpellsCostIncreasementAllEffect copy() { - return new SpellsCostIncreasementAllEffect(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementControllerEffect.java deleted file mode 100644 index 7023fc7884..0000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementControllerEffect.java +++ /dev/null @@ -1,90 +0,0 @@ - -package mage.abilities.effects.common.cost; - -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.abilities.costs.mana.ManaCost; -import mage.abilities.costs.mana.ManaCosts; -import mage.cards.Card; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.util.CardUtil; - -/** - * - * @author Quercitron - */ -public class SpellsCostIncreasementControllerEffect extends CostModificationEffectImpl { - - private final FilterCard filter; - private final int amount; - private ManaCosts manaCostsToIncrease = null; - - public SpellsCostIncreasementControllerEffect(FilterCard filter, ManaCosts manaCostsToReduce) { - super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST); - this.filter = filter; - this.amount = 0; - this.manaCostsToIncrease = manaCostsToReduce; - - StringBuilder sb = new StringBuilder(); - sb.append(filter.getMessage()).append(" you cast cost "); - for (String manaSymbol : manaCostsToReduce.getSymbols()) { - sb.append(manaSymbol); - } - sb.append(" more to cast"); - this.staticText = sb.toString(); - } - - public SpellsCostIncreasementControllerEffect(FilterCard filter, int amount) { - super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST); - this.filter = filter; - this.amount = amount; - - StringBuilder sb = new StringBuilder(); - sb.append(filter.getMessage()).append(" you cast cost {").append(amount).append("} more to cast"); - this.staticText = sb.toString(); - } - - protected SpellsCostIncreasementControllerEffect(SpellsCostIncreasementControllerEffect effect) { - super(effect); - this.filter = effect.filter; - this.amount = effect.amount; - this.manaCostsToIncrease = effect.manaCostsToIncrease; - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - if (manaCostsToIncrease != null) { - CardUtil.increaseCost((SpellAbility) abilityToModify, manaCostsToIncrease); - } else { - CardUtil.increaseCost(abilityToModify, this.amount); - } - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility) { - if (abilityToModify.isControlledBy(source.getControllerId())) { - Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); - if (spell != null) { - return this.filter.match(spell, game); - } else { - // used at least for flashback ability because Flashback ability doesn't use stack - Card sourceCard = game.getCard(abilityToModify.getSourceId()); - return sourceCard != null && this.filter.match(sourceCard, game); - } - } - } - return false; - } - - @Override - public SpellsCostIncreasementControllerEffect copy() { - return new SpellsCostIncreasementControllerEffect(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java new file mode 100644 index 0000000000..fdec9bfdd3 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java @@ -0,0 +1,139 @@ +package mage.abilities.effects.common.cost; + +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.cards.Card; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * @author JayDi85 + */ +public class SpellsCostIncreasingAllEffect extends CostModificationEffectImpl { + + private final FilterCard filter; + private final TargetController targetController; + private final int increaseGenericCost; + private final ManaCosts increaseManaCosts; + + public SpellsCostIncreasingAllEffect(int increaseGenericCost, FilterCard filter, TargetController targetController) { + super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST); + this.filter = filter; + this.targetController = targetController; + this.increaseGenericCost = increaseGenericCost; + this.increaseManaCosts = null; + + setText(); + } + + public SpellsCostIncreasingAllEffect(ManaCosts increaseManaCosts, FilterCard filter, TargetController targetController) { + super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST); + this.filter = filter; + this.targetController = targetController; + this.increaseGenericCost = 0; + this.increaseManaCosts = increaseManaCosts; + + setText(); + } + + private void setText() { + StringBuilder sb = new StringBuilder(); + sb.append(filter.getMessage()); + switch (this.targetController) { + case YOU: + sb.append(" you cast"); + break; + case OPPONENT: + sb.append(" your opponents cast"); + break; + case ANY: + break; + default: + throw new IllegalArgumentException("Unsupported TargetController " + this.targetController); + } + + sb.append(" cost "); + if (this.increaseManaCosts != null) { + for (String manaSymbol : this.increaseManaCosts.getSymbols()) { + sb.append(manaSymbol); + } + } else { + sb.append("{").append(increaseGenericCost).append("}"); + } + + sb.append(" more to cast"); + this.staticText = sb.toString(); + } + + protected SpellsCostIncreasingAllEffect(SpellsCostIncreasingAllEffect effect) { + super(effect); + this.filter = effect.filter; + this.targetController = effect.targetController; + this.increaseGenericCost = effect.increaseGenericCost; + this.increaseManaCosts = effect.increaseManaCosts; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + if (increaseManaCosts != null) { + CardUtil.increaseCost((SpellAbility) abilityToModify, increaseManaCosts); + } else { + CardUtil.increaseCost(abilityToModify, this.increaseGenericCost); + } + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + if (abilityToModify instanceof SpellAbility) { + Player abilityController = game.getPlayer(abilityToModify.getControllerId()); + Player sourceController = game.getPlayer(source.getControllerId()); + if (abilityController == null || sourceController == null) { + return false; + } + + switch (this.targetController) { + case YOU: + if (!sourceController.getId().equals(abilityController.getId())) { + return false; + } + break; + case OPPONENT: + if (!sourceController.hasOpponent(abilityController.getId(), game)) { + return false; + } + break; + case ANY: + break; + default: + throw new IllegalArgumentException("Unsupported TargetController " + this.targetController); + } + + Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); + if (spell != null) { + // real cast with put on stack + return this.filter.match(spell, game); + } else { + // get playable and other staff without put on stack + // used at least for flashback ability because Flashback ability doesn't use stack + Card sourceCard = game.getCard(abilityToModify.getSourceId()); + return sourceCard != null && this.filter.match(sourceCard, game); + } + } + return false; + } + + @Override + public SpellsCostIncreasingAllEffect copy() { + return new SpellsCostIncreasingAllEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java new file mode 100644 index 0000000000..e8fbeb1c1d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java @@ -0,0 +1,145 @@ +package mage.abilities.effects.common.cost; + +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.cards.Card; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; + +/** + * @author JayDi85 + */ +public class SpellsCostModificationThatTargetSourceEffect extends CostModificationEffectImpl { + + private final FilterCard spellFilter; + private final int modificationAmount; + private String targetName = "{this}"; + private final TargetController targetController; + + public SpellsCostModificationThatTargetSourceEffect(int modificationAmount, FilterCard spellFilter, TargetController targetController) { + super(Duration.WhileOnBattlefield, Outcome.Neutral, modificationAmount < 0 ? CostModificationType.REDUCE_COST : CostModificationType.INCREASE_COST); + this.spellFilter = spellFilter; + this.modificationAmount = modificationAmount; + this.targetController = targetController; + + setText(); + } + + private void setText() { + // example: Spells your opponents cast that target Accursed Witch cost {1} less to cast. + StringBuilder sb = new StringBuilder(); + sb.append(this.spellFilter.getMessage()); + switch (this.targetController) { + case ANY: + break; + case YOU: + sb.append(" you cast"); + break; + case OPPONENT: + sb.append(" your opponents cast"); + break; + default: + throw new IllegalArgumentException("Unsupported target controller " + this.targetController); + } + + sb.append(" that target ").append(this.targetName); + if (this.modificationAmount < 0) { + sb.append(" cost {").append(-1 * this.modificationAmount).append("} less to cast"); + } else { + sb.append(" cost {").append(this.modificationAmount).append("} more to cast"); + } + this.staticText = sb.toString(); + } + + private SpellsCostModificationThatTargetSourceEffect(SpellsCostModificationThatTargetSourceEffect effect) { + super(effect); + this.spellFilter = effect.spellFilter; + this.modificationAmount = effect.modificationAmount; + this.targetName = effect.targetName; + this.targetController = effect.targetController; + } + + @Override + public SpellsCostModificationThatTargetSourceEffect copy() { + return new SpellsCostModificationThatTargetSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + if (this.modificationAmount >= 0) { + CardUtil.increaseCost(abilityToModify, this.modificationAmount); + } else { + CardUtil.reduceCost(abilityToModify, -1 * this.modificationAmount); + } + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + if (!(abilityToModify instanceof SpellAbility)) { + return false; + } + + Player sourceController = game.getPlayer(source.getControllerId()); + Player abilityController = game.getPlayer(abilityToModify.getControllerId()); + if (sourceController == null || abilityController == null) { + return false; + } + + switch (this.targetController) { + case ANY: + break; + case YOU: + if (!sourceController.getId().equals(abilityController.getId())) { + return false; + } + break; + case OPPONENT: + if (!sourceController.hasOpponent(abilityController.getId(), game)) { + return false; + } + break; + default: + return false; + } + + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + if (spellCard == null || !this.spellFilter.match(spellCard, game)) { + return false; + } + + if (game.getStack().getStackObject(abilityToModify.getId()) != null) { + // real cast + Set allTargets = CardUtil.getAllSelectedTargets(abilityToModify, game); + return allTargets.contains(source.getSourceId()); + } else { + // playable + Set allTargets = CardUtil.getAllPossibleTargets(abilityToModify, game); + + switch (this.getModificationType()) { + case REDUCE_COST: + // must reduce all the time + return allTargets.contains(source.getSourceId()); + case INCREASE_COST: + // must increase if can't target another (e.g. user can choose another target without cost increase) + return allTargets.contains(source.getSourceId()) && allTargets.size() <= 1; + } + } + return false; + } + + public SpellsCostModificationThatTargetSourceEffect withTargetName(String targetName) { + this.targetName = targetName; + setText(); + return this; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java index 3c79a57825..5bfc2e76a6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java @@ -3,6 +3,7 @@ package mage.abilities.effects.common.cost; import java.util.LinkedHashSet; import java.util.Set; import java.util.UUID; +import javax.naming.directory.InvalidAttributesException; import mage.Mana; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -13,12 +14,10 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.FilterCard; import mage.game.Game; -import mage.game.stack.Spell; import mage.players.Player; import mage.util.CardUtil; /** - * * @author LevelX2 */ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl { @@ -123,13 +122,9 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl { return false; } if (abilityToModify instanceof SpellAbility) { - Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); - if (spell != null) { - return this.filter.match(spell, game) && selectedByRuntimeData(spell, source, game); - } else { - // used at least for flashback ability because Flashback ability doesn't use stack - Card sourceCard = game.getCard(abilityToModify.getSourceId()); - return sourceCard != null && this.filter.match(sourceCard, game) && selectedByRuntimeData(sourceCard, source, game); + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + if (spellCard != null) { + return this.filter.match(spellCard, game) && selectedByRuntimeData(spellCard, source, game); } } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenCardTypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenCardTypeEffect.java new file mode 100644 index 0000000000..35d89abd51 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenCardTypeEffect.java @@ -0,0 +1,39 @@ +package mage.abilities.effects.common.cost; + +import mage.abilities.Ability; +import mage.cards.Card; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.game.Game; + +/** + * @author emerald000 + */ +public class SpellsCostReductionAllOfChosenCardTypeEffect extends SpellsCostReductionAllEffect { + + public SpellsCostReductionAllOfChosenCardTypeEffect(FilterCard filter, int amount) { + this(filter, amount, false); + } + + public SpellsCostReductionAllOfChosenCardTypeEffect(FilterCard filter, int amount, boolean onlyControlled) { + super(filter, amount, false, onlyControlled); + } + + public SpellsCostReductionAllOfChosenCardTypeEffect(final SpellsCostReductionAllOfChosenCardTypeEffect effect) { + super(effect); + } + + @Override + public SpellsCostReductionAllOfChosenCardTypeEffect copy() { + return new SpellsCostReductionAllOfChosenCardTypeEffect(this); + } + + @Override + protected boolean selectedByRuntimeData(Card card, Ability source, Game game) { + Object savedType = game.getState().getValue(source.getSourceId() + "_type"); + if (savedType instanceof String) { + return card.getCardType().contains(CardType.fromString((String) savedType)); + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java index 1fd38caa06..8819933e7d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java @@ -13,14 +13,13 @@ import mage.choices.ChoiceImpl; import mage.constants.CostModificationType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SpellAbilityCastMode; import mage.filter.FilterCard; import mage.game.Game; -import mage.game.stack.Spell; import mage.players.Player; import mage.util.CardUtil; /** - * * @author North */ public class SpellsCostReductionControllerEffect extends CostModificationEffectImpl { @@ -114,13 +113,9 @@ public class SpellsCostReductionControllerEffect extends CostModificationEffectI public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify instanceof SpellAbility) { if (abilityToModify.isControlledBy(source.getControllerId())) { - Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); - if (spell != null) { - return this.filter.match(spell, source.getSourceId(), source.getControllerId(), game); - } else { - // used at least for flashback ability because Flashback ability doesn't use stack or for getPlayables where spell is not cast yet - Card sourceCard = game.getCard(abilityToModify.getSourceId()); - return sourceCard != null && this.filter.match(sourceCard, source.getSourceId(), source.getControllerId(), game); + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game);; + if (spellCard != null) { + return this.filter.match(spellCard, source.getSourceId(), source.getControllerId(), game); } } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/AddCounterChoiceSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/AddCounterChoiceSourceEffect.java new file mode 100644 index 0000000000..f181e21aa8 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/AddCounterChoiceSourceEffect.java @@ -0,0 +1,62 @@ +package mage.abilities.effects.common.counter; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.counters.Counter; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.Locale; + +/** + * @author TheElk801 + */ +public class AddCounterChoiceSourceEffect extends OneShotEffect { + + private final CounterType counterType1; + private final CounterType counterType2; + + public AddCounterChoiceSourceEffect(CounterType counterType1, CounterType counterType2) { + super(Outcome.Benefit); + this.counterType1 = counterType1; + this.counterType2 = counterType2; + staticText = "with your choice of a " + counterType1 + " counter or a " + counterType2 + " counter on it"; + } + + private AddCounterChoiceSourceEffect(final AddCounterChoiceSourceEffect effect) { + super(effect); + this.counterType1 = effect.counterType1; + this.counterType2 = effect.counterType2; + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + if (player == null || permanent == null) { + return false; + } + Counter counter; + if (player.chooseUse( + Outcome.Neutral, "Choose " + counterType1 + " or " + counterType2, null, + cap(counterType1.getName()), cap(counterType2.getName()), source, game + )) { + counter = counterType1.createInstance(); + } else { + counter = counterType2.createInstance(); + } + return permanent.addCounters(counter, source, game); + } + + private static final String cap(String string) { + return string != null ? string.substring(0, 1).toUpperCase(Locale.ENGLISH) + string.substring(1) : null; + } + + @Override + public AddCounterChoiceSourceEffect copy() { + return new AddCounterChoiceSourceEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/AddPoisonCounterTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/AddPoisonCounterTargetEffect.java index ba836e76ad..69d595db61 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/AddPoisonCounterTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/AddPoisonCounterTargetEffect.java @@ -51,6 +51,6 @@ public class AddPoisonCounterTargetEffect extends OneShotEffect { if(staticText != null && !staticText.isEmpty()) { return staticText; } - return "Target " + mode.getTargets().get(0).getTargetName() + " gets " + Integer.toString(amount) + " poison counter(s)."; + return "target " + mode.getTargets().get(0).getTargetName() + " gets " + Integer.toString(amount) + " poison counter(s)."; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/DistributeCountersEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/DistributeCountersEffect.java index 39315588d4..3be17d3df7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/DistributeCountersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/DistributeCountersEffect.java @@ -76,7 +76,7 @@ public class DistributeCountersEffect extends OneShotEffect { } String name = counterType.getName(); - String text = "distribute " + CardUtil.numberToText(amount) + ' ' + name + " counters among " + targetDescription + '.'; + String text = "distribute " + CardUtil.numberToText(amount) + ' ' + name + " counters among " + targetDescription; if (removeAtEndOfTurn) { text += " For each " + name + " counter you put on a creature this way, remove a " + name + " counter from that creature at the beginning of the next cleanup step."; diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCountersTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCountersTargetsEffect.java index bd6ff6892c..1c6f410266 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCountersTargetsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCountersTargetsEffect.java @@ -1,78 +1,78 @@ - -package mage.abilities.effects.common.counter; - -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author Styxo - */ -public class MoveCountersTargetsEffect extends OneShotEffect { - - private final CounterType counterType; - private final int amount; - - public MoveCountersTargetsEffect(CounterType counterType, int amount) { - super(Outcome.Detriment); - this.counterType = counterType; - this.amount = amount; - - } - - public MoveCountersTargetsEffect(final MoveCountersTargetsEffect effect) { - super(effect); - this.counterType = effect.counterType; - this.amount = effect.amount; - } - - @Override - public MoveCountersTargetsEffect copy() { - return new MoveCountersTargetsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent removeTargetCreature = game.getPermanent(targetPointer.getTargets(game, source).get(0)); - Permanent addTargetCreature = game.getPermanent(targetPointer.getTargets(game, source).get(1)); - if (removeTargetCreature != null && addTargetCreature != null && removeTargetCreature.getCounters(game).getCount(counterType) >= amount) { - removeTargetCreature.removeCounters(counterType.createInstance(amount), game); - addTargetCreature.addCounters(counterType.createInstance(amount), source, game); - if (!game.isSimulation()) { - game.informPlayers("Moved " + amount + ' ' + counterType.getName() + " counter" + (amount > 1 ? "s" : "") + " from " + removeTargetCreature.getLogName() + " to " + addTargetCreature.getLogName()); - } - return true; - } - return false; - } - - @Override - public String getText(Mode mode) { - if (!staticText.isEmpty()) { - return staticText; - } - - StringBuilder sb = new StringBuilder("move "); - if (amount > 1) { - sb.append(amount); - } else { - sb.append('a'); - } - sb.append(' '); - sb.append(counterType.getName()); - sb.append(" counter"); - if (amount > 1) { - sb.append("s "); - } else { - sb.append(' '); - } - sb.append("from one target creature to another target creature"); - - return sb.toString(); - } -} + +package mage.abilities.effects.common.counter; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author Styxo + */ +public class MoveCountersTargetsEffect extends OneShotEffect { + + private final CounterType counterType; + private final int amount; + + public MoveCountersTargetsEffect(CounterType counterType, int amount) { + super(Outcome.Detriment); + this.counterType = counterType; + this.amount = amount; + + } + + public MoveCountersTargetsEffect(final MoveCountersTargetsEffect effect) { + super(effect); + this.counterType = effect.counterType; + this.amount = effect.amount; + } + + @Override + public MoveCountersTargetsEffect copy() { + return new MoveCountersTargetsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent removeTargetCreature = game.getPermanent(targetPointer.getTargets(game, source).get(0)); + Permanent addTargetCreature = game.getPermanent(targetPointer.getTargets(game, source).get(1)); + if (removeTargetCreature != null && addTargetCreature != null && removeTargetCreature.getCounters(game).getCount(counterType) >= amount) { + removeTargetCreature.removeCounters(counterType.createInstance(amount), game); + addTargetCreature.addCounters(counterType.createInstance(amount), source, game); + if (!game.isSimulation()) { + game.informPlayers("Moved " + amount + ' ' + counterType.getName() + " counter" + (amount > 1 ? "s" : "") + " from " + removeTargetCreature.getLogName() + " to " + addTargetCreature.getLogName()); + } + return true; + } + return false; + } + + @Override + public String getText(Mode mode) { + if (!staticText.isEmpty()) { + return staticText; + } + + StringBuilder sb = new StringBuilder("move "); + if (amount > 1) { + sb.append(amount); + } else { + sb.append('a'); + } + sb.append(' '); + sb.append(counterType.getName()); + sb.append(" counter"); + if (amount > 1) { + sb.append("s "); + } else { + sb.append(' '); + } + sb.append("from one target creature to another target creature"); + + return sb.toString(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java index d48b78dc8e..4ba0596c82 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java @@ -33,7 +33,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect { private static final FilterCard filterOneCard = new FilterCard("one card"); public DiscardCardYouChooseTargetEffect() { - this(new FilterCard("a card")); + this(StaticFilters.FILTER_CARD_A); } public DiscardCardYouChooseTargetEffect(TargetController targetController) { @@ -144,12 +144,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect { if (!controller.choose(Outcome.Benefit, revealedCards, target, game)) { return result; } - for (UUID targetId : target.getTargets()) { - Card card = revealedCards.get(targetId, game); - if (!player.discard(card, source, game)) { - result = false; - } - } + result=!player.discard(new CardsImpl(target.getTargets()),source,game).isEmpty(); return result; } @@ -175,7 +170,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect { } else { if (numberCardsToReveal instanceof StaticValue) { sb.append(" reveals "); - sb.append(CardUtil.numberToText(((StaticValue) numberCardsToReveal).getValue()) + " cards"); + sb.append(CardUtil.numberToText(((StaticValue) numberCardsToReveal).getValue())).append(" cards"); sb.append(" from their hand"); } else { sb.append(" reveals a number of cards from their hand equal to "); diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardControllerEffect.java index 278a4747c8..88d1f95ade 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardControllerEffect.java @@ -1,11 +1,9 @@ - package mage.abilities.effects.common.discard; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; @@ -16,8 +14,8 @@ import mage.util.CardUtil; */ public class DiscardControllerEffect extends OneShotEffect { - protected DynamicValue amount; - protected boolean randomDiscard; + protected final DynamicValue amount; + protected final boolean randomDiscard; public DiscardControllerEffect(int amount) { this(StaticValue.get(amount)); @@ -51,22 +49,8 @@ public class DiscardControllerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - boolean result = false; Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - if (randomDiscard) { - int maxAmount = Math.min(amount.calculate(game, source, this), player.getHand().size()); - for (int i = 0; i < maxAmount; i++) { - Card card = player.getHand().getRandom(game); - result |= player.discard(card, source, game); - - } - } else { - player.discard(amount.calculate(game, source, this), false, source, game); - result = true; - } - } - return result; + return player != null && !player.discard(amount.calculate(game, source, this), randomDiscard, source, game).isEmpty(); } private void setText() { diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java index fba1fef2f9..7fee7da644 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java @@ -5,12 +5,11 @@ import mage.abilities.Mode; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.TargetController; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.Target; @@ -62,54 +61,49 @@ public class DiscardEachPlayerEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); // Store for each player the cards to discard, that's important because all discard shall happen at the same time Map cardsToDiscard = new HashMap<>(); - if (controller != null) { - // choose cards to discard - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - switch (targetController) { - case NOT_YOU: - if (playerId.equals(source.getControllerId())) { - continue; - } - break; - case OPPONENT: - if (!game.getOpponents(source.getControllerId()).contains(playerId)) { - continue; - } - break; - } - int numberOfCardsToDiscard = Math.min(amount.calculate(game, source, this), player.getHand().size()); - Cards cards = new CardsImpl(); - if (randomDiscard) { - while (player.isInGame() && cards.size() < numberOfCardsToDiscard) { - Card card = player.getHand().getRandom(game); - if (!cards.contains(card.getId())) { - cards.add(card); - } - } - } else { - Target target = new TargetDiscard(numberOfCardsToDiscard, numberOfCardsToDiscard, new FilterCard(), playerId); - player.chooseTarget(outcome, target, source, game); - cards.addAll(target.getTargets()); - } - cardsToDiscard.put(playerId, cards); - } + if (controller == null) { + return true; + } + int toDiscard = amount.calculate(game, source, this); + // choose cards to discard + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; } - // discard all choosen cards - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - Cards cardsPlayer = cardsToDiscard.get(playerId); - if (cardsPlayer != null) { - for (UUID cardId : cardsPlayer) { - Card card = game.getCard(cardId); - player.discard(card, source, game); - - } + switch (targetController) { + case NOT_YOU: + if (playerId.equals(source.getControllerId())) { + continue; } - } + break; + case OPPONENT: + if (!game.getOpponents(source.getControllerId()).contains(playerId)) { + continue; + } + break; } + if (randomDiscard) { + player.discard(toDiscard, true, source, game); + continue; + } + int numberOfCardsToDiscard = Math.min(toDiscard, player.getHand().size()); + Cards cards = new CardsImpl(); + Target target = new TargetDiscard(numberOfCardsToDiscard, numberOfCardsToDiscard, StaticFilters.FILTER_CARD, playerId); + player.chooseTarget(outcome, target, source, game); + cards.addAll(target.getTargets()); + cardsToDiscard.put(playerId, cards); + } + if (randomDiscard) { + return true; + } + // discard all choosen cards + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + player.discard(cardsToDiscard.get(playerId), source, game); } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandAllEffect.java index d6cb2f4eb7..60cc0bd342 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandAllEffect.java @@ -2,12 +2,10 @@ package mage.abilities.effects.common.discard; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; -import java.util.Set; import java.util.UUID; /** @@ -33,12 +31,10 @@ public class DiscardHandAllEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); - if (player != null) { - Set cards = player.getHand().getCards(game); - for (Card card : cards) { - player.discard(card, source, game); - } + if (player == null) { + continue; } + player.discard(player.getHand(), source, game); } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandControllerEffect.java index e3393eda66..cfc2f09e5d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandControllerEffect.java @@ -18,7 +18,7 @@ public class DiscardHandControllerEffect extends OneShotEffect { public DiscardHandControllerEffect() { super(Outcome.Discard); - this.staticText = "Discard your hand"; + this.staticText = "discard your hand"; } public DiscardHandControllerEffect(final DiscardHandControllerEffect effect) { @@ -33,12 +33,10 @@ public class DiscardHandControllerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - for (Card card : player.getHand().getCards(game)) { - player.discard(card, source, game); - } - return true; + if (player == null) { + return false; } - return false; + player.discard(player.getHand().size(),false,source,game); + return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandDrawSameNumberSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandDrawSameNumberSourceEffect.java index 8cda876856..8fb68238ef 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandDrawSameNumberSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandDrawSameNumberSourceEffect.java @@ -28,7 +28,7 @@ public class DiscardHandDrawSameNumberSourceEffect extends OneShotEffect { if (player != null) { int amount = player.getHand().getCards(game).size(); player.discard(amount, false, source, game); - player.drawCards(amount, game); + player.drawCards(amount, source.getSourceId(), game); return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java index b70c8bb181..1f6ca305ad 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java @@ -1,46 +1,46 @@ - -package mage.abilities.effects.common.enterAttribute; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * IMPORTANT: This only adds the chosen subtype while the source permanent is entering the battlefield. - * You should also use @link{mage.abilities.effects.common.continuous.AddChosenSubtypeEffect} to make the subtype persist. - * @author LevelX2 - */ -public class EnterAttributeAddChosenSubtypeEffect extends OneShotEffect { - - public EnterAttributeAddChosenSubtypeEffect() { - super(Outcome.Benefit); - this.staticText = "{this} is the chosen type in addition to its other types"; - } - - public EnterAttributeAddChosenSubtypeEffect(final EnterAttributeAddChosenSubtypeEffect effect) { - super(effect); - } - - @Override - public EnterAttributeAddChosenSubtypeEffect copy() { - return new EnterAttributeAddChosenSubtypeEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentEntering(source.getSourceId()); - SubType subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type"); - if (permanent != null && subtype != null) { - if (!permanent.getSubtype(game).contains(subtype)) { - permanent.getSubtype(game).add(subtype); - } - return true; - } - return false; - } -} + +package mage.abilities.effects.common.enterAttribute; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * IMPORTANT: This only adds the chosen subtype while the source permanent is entering the battlefield. + * You should also use @link{mage.abilities.effects.common.continuous.AddChosenSubtypeEffect} to make the subtype persist. + * @author LevelX2 + */ +public class EnterAttributeAddChosenSubtypeEffect extends OneShotEffect { + + public EnterAttributeAddChosenSubtypeEffect() { + super(Outcome.Benefit); + this.staticText = "{this} is the chosen type in addition to its other types"; + } + + public EnterAttributeAddChosenSubtypeEffect(final EnterAttributeAddChosenSubtypeEffect effect) { + super(effect); + } + + @Override + public EnterAttributeAddChosenSubtypeEffect copy() { + return new EnterAttributeAddChosenSubtypeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + SubType subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type"); + if (permanent != null && subtype != null) { + if (!permanent.getSubtype(game).contains(subtype)) { + permanent.getSubtype(game).add(subtype); + } + return true; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java b/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java index ed699d4ecd..799881bca8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java @@ -1,47 +1,47 @@ - -package mage.abilities.effects.common.replacement; - -import mage.abilities.Ability; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; - -/** - * - * @author LevelX2 - */ -public class CreateTwiceThatManyTokensEffect extends ReplacementEffectImpl { - - public CreateTwiceThatManyTokensEffect() { - super(Duration.WhileOnBattlefield, Outcome.Copy); - staticText = "If an effect would create one or more tokens under your control, it creates twice that many of those tokens instead"; - } - - public CreateTwiceThatManyTokensEffect(final CreateTwiceThatManyTokensEffect effect) { - super(effect); - } - - @Override - public CreateTwiceThatManyTokensEffect copy() { - return new CreateTwiceThatManyTokensEffect(this); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.CREATE_TOKEN; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(source.getControllerId()); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(event.getAmount() * 2); - return false; - } - -} + +package mage.abilities.effects.common.replacement; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author LevelX2 + */ +public class CreateTwiceThatManyTokensEffect extends ReplacementEffectImpl { + + public CreateTwiceThatManyTokensEffect() { + super(Duration.WhileOnBattlefield, Outcome.Copy); + staticText = "If an effect would create one or more tokens under your control, it creates twice that many of those tokens instead"; + } + + public CreateTwiceThatManyTokensEffect(final CreateTwiceThatManyTokensEffect effect) { + super(effect); + } + + @Override + public CreateTwiceThatManyTokensEffect copy() { + return new CreateTwiceThatManyTokensEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CREATE_TOKEN; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(source.getControllerId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(event.getAmount() * 2); + return false; + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java index 12ece0c7b5..3f5051ae44 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java @@ -1,72 +1,72 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common.replacement; - -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * - * @author LevelX2 - */ -public class DiesReplacementEffect extends ReplacementEffectImpl { - - private final MageObjectReference objectRef; - - public DiesReplacementEffect(MageObjectReference objectRef, Duration duration) { - super(duration, Outcome.Exile); - this.objectRef = objectRef; - staticText = "If that creature would die " + (duration == Duration.EndOfTurn ? "this turn" : "") + ", exile it instead"; - } - - public DiesReplacementEffect(final DiesReplacementEffect effect) { - super(effect); - this.objectRef = effect.objectRef; - } - - @Override - public DiesReplacementEffect copy() { - return new DiesReplacementEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent permanent = ((ZoneChangeEvent) event).getTarget(); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && permanent != null) { - return controller.moveCardToExileWithInfo(permanent, null, "", source.getSourceId(), game, Zone.BATTLEFIELD, true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.ZONE_CHANGE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - ZoneChangeEvent zce = (ZoneChangeEvent) event; - return zce.isDiesEvent() - && objectRef.equals(new MageObjectReference(zce.getTarget(), game)); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common.replacement; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class DiesReplacementEffect extends ReplacementEffectImpl { + + private final MageObjectReference objectRef; + + public DiesReplacementEffect(MageObjectReference objectRef, Duration duration) { + super(duration, Outcome.Exile); + this.objectRef = objectRef; + staticText = "If that creature would die " + (duration == Duration.EndOfTurn ? "this turn" : "") + ", exile it instead"; + } + + public DiesReplacementEffect(final DiesReplacementEffect effect) { + super(effect); + this.objectRef = effect.objectRef; + } + + @Override + public DiesReplacementEffect copy() { + return new DiesReplacementEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent permanent = ((ZoneChangeEvent) event).getTarget(); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && permanent != null) { + return controller.moveCards(permanent, Zone.EXILED, source, game); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zce = (ZoneChangeEvent) event; + return zce.isDiesEvent() + && objectRef.equals(new MageObjectReference(zce.getTarget(), game)); + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java index 7af7131b28..ce9a090df1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java @@ -12,6 +12,7 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInYourGraveyard; /** * @author Styxo @@ -54,6 +55,7 @@ public class SearchLibraryGraveyardPutInHandEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); Card cardFound = null; + boolean needShuffle = false; if (controller != null && sourceObject != null) { if (forceToSearchBoth || controller.chooseUse(outcome, "Search your library for a card named " + filter.getMessage() + '?', source, game)) { TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter); @@ -63,11 +65,11 @@ public class SearchLibraryGraveyardPutInHandEffect extends OneShotEffect { cardFound = game.getCard(target.getFirstTarget()); } } - controller.shuffleLibrary(source, game); + needShuffle = true; } if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a card named " + filter.getMessage() + '?', source, game)) { - TargetCard target = new TargetCard(0, 1, Zone.GRAVEYARD, filter); + TargetCard target = new TargetCardInYourGraveyard(0, 1, filter, true); target.clearChosen(); if (controller.choose(outcome, controller.getGraveyard(), target, game)) { if (!target.getTargets().isEmpty()) { @@ -81,6 +83,10 @@ public class SearchLibraryGraveyardPutInHandEffect extends OneShotEffect { controller.moveCards(cardFound, Zone.HAND, source, game); } + if (needShuffle) { + controller.shuffleLibrary(source, game); + } + return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java index 627244f47b..1afa6c7f27 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java @@ -1,91 +1,92 @@ -package mage.abilities.effects.common.search; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardsImpl; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; -import mage.target.common.TargetCardInLibrary; - -/** - * - * @author Styxo - */ -public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffect { - - private FilterCard filter; - private boolean forceToSearchBoth; - - public SearchLibraryGraveyardPutOntoBattlefieldEffect(FilterCard filter) { - this(filter, false); - } - - public SearchLibraryGraveyardPutOntoBattlefieldEffect(FilterCard filter, boolean forceToSearchBoth) { - this(filter, forceToSearchBoth, false); - } - - public SearchLibraryGraveyardPutOntoBattlefieldEffect(FilterCard filter, boolean forceToSearchBoth, boolean youMay) { - super(Outcome.Benefit); - this.filter = filter; - this.forceToSearchBoth = forceToSearchBoth; - staticText = (youMay ? "You may" : "") + " search your library and" + (forceToSearchBoth ? "" : "/or") + " graveyard for a card named " + filter.getMessage() - + ", reveal it, and put it into your hand. " + (forceToSearchBoth ? "Then shuffle your library" : "If you search your library this way, shuffle it"); - } - - public SearchLibraryGraveyardPutOntoBattlefieldEffect(final SearchLibraryGraveyardPutOntoBattlefieldEffect effect) { - super(effect); - this.filter = effect.filter; - this.forceToSearchBoth = effect.forceToSearchBoth; - - } - - @Override - public SearchLibraryGraveyardPutOntoBattlefieldEffect copy() { - return new SearchLibraryGraveyardPutOntoBattlefieldEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - Card cardFound = null; - if (controller != null && sourceObject != null) { - if (forceToSearchBoth || controller.chooseUse(outcome, "Search your library for a card named " + filter.getMessage() + '?', source, game)) { - TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter); - target.clearChosen(); - if (controller.searchLibrary(target, source, game)) { - if (!target.getTargets().isEmpty()) { - cardFound = game.getCard(target.getFirstTarget()); - } - } - controller.shuffleLibrary(source, game); - } - - if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a card named " + filter.getMessage() + '?', source, game)) { - TargetCard target = new TargetCard(0, 1, Zone.GRAVEYARD, filter); - target.clearChosen(); - if (controller.choose(outcome, controller.getGraveyard(), target, game)) { - if (!target.getTargets().isEmpty()) { - cardFound = game.getCard(target.getFirstTarget()); - } - } - } - - if (cardFound != null) { - controller.revealCards(sourceObject.getIdName(), new CardsImpl(cardFound), game); - controller.moveCards(cardFound, Zone.BATTLEFIELD, source, game); - } - - return true; - } - - return false; - } - -} +package mage.abilities.effects.common.search; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * @author Styxo + */ +public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffect { + + private FilterCard filter; + private boolean forceToSearchBoth; + + public SearchLibraryGraveyardPutOntoBattlefieldEffect(FilterCard filter) { + this(filter, false); + } + + public SearchLibraryGraveyardPutOntoBattlefieldEffect(FilterCard filter, boolean forceToSearchBoth) { + this(filter, forceToSearchBoth, false); + } + + public SearchLibraryGraveyardPutOntoBattlefieldEffect(FilterCard filter, boolean forceToSearchBoth, boolean youMay) { + super(Outcome.Benefit); + this.filter = filter; + this.forceToSearchBoth = forceToSearchBoth; + staticText = (youMay ? "You may " : "") + "search your library and" + (forceToSearchBoth ? "" : "/or") + " graveyard for a " + filter.getMessage() + + " and put it onto the battlefield. " + (forceToSearchBoth ? "Then shuffle your library" : "If you search your library this way, shuffle it"); + } + + public SearchLibraryGraveyardPutOntoBattlefieldEffect(final SearchLibraryGraveyardPutOntoBattlefieldEffect effect) { + super(effect); + this.filter = effect.filter; + this.forceToSearchBoth = effect.forceToSearchBoth; + } + + @Override + public SearchLibraryGraveyardPutOntoBattlefieldEffect copy() { + return new SearchLibraryGraveyardPutOntoBattlefieldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + Card cardFound = null; + boolean needShuffle = false; + if (controller != null && sourceObject != null) { + if (forceToSearchBoth || controller.chooseUse(outcome, "Search your library for a " + filter.getMessage() + '?', source, game)) { + TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter); + target.clearChosen(); + if (controller.searchLibrary(target, source, game)) { + if (!target.getTargets().isEmpty()) { + cardFound = game.getCard(target.getFirstTarget()); + } + } + needShuffle = true; + } + + if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a " + filter.getMessage() + '?', source, game)) { + TargetCard target = new TargetCardInYourGraveyard(0, 1, filter, true); + target.clearChosen(); + if (controller.choose(outcome, controller.getGraveyard(), target, game)) { + if (!target.getTargets().isEmpty()) { + cardFound = game.getCard(target.getFirstTarget()); + } + } + } + + if (cardFound != null) { + controller.moveCards(cardFound, Zone.BATTLEFIELD, source, game); + } + + if (needShuffle) { + controller.shuffleLibrary(source, game); + } + + return true; + } + + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardWithLessCMCPutIntoPlay.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardWithLessCMCPutIntoPlay.java index 09754429ba..9a37206fe4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardWithLessCMCPutIntoPlay.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardWithLessCMCPutIntoPlay.java @@ -1,87 +1,87 @@ -package mage.abilities.effects.common.search; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.constants.ComparisonType; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardsImpl; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; -import mage.target.common.TargetCardInLibrary; - -/** - * - * @author antoni-g - */ -public class SearchLibraryGraveyardWithLessCMCPutIntoPlay extends OneShotEffect { - - private final FilterCard filter; - - public SearchLibraryGraveyardWithLessCMCPutIntoPlay() { - this(new FilterCard()); - } - - public SearchLibraryGraveyardWithLessCMCPutIntoPlay(FilterCard filter) { - super(Outcome.PutCreatureInPlay); - this.filter = filter; - staticText = "Search your library or graveyard for a " + filter.getMessage() + " with converted mana cost X or less, put it onto the battlefield, then shuffle your library"; - } - - public SearchLibraryGraveyardWithLessCMCPutIntoPlay(final SearchLibraryGraveyardWithLessCMCPutIntoPlay effect) { - super(effect); - this.filter = effect.filter; - } - - @Override - public SearchLibraryGraveyardWithLessCMCPutIntoPlay copy() { - return new SearchLibraryGraveyardWithLessCMCPutIntoPlay(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - Card cardFound = null; - if (controller != null && sourceObject != null) { - // create x cost filter - FilterCard advancedFilter = filter.copy(); // never change static objects so copy the object here before - advancedFilter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); - - if (controller.chooseUse(outcome, "Search your library for a " + filter.getMessage() + " with CMC X or less" + '?', source, game)) { - TargetCardInLibrary target = new TargetCardInLibrary(advancedFilter); - target.clearChosen(); - if (controller.searchLibrary(target, source, game)) { - if (!target.getTargets().isEmpty()) { - cardFound = game.getCard(target.getFirstTarget()); - } - } - controller.shuffleLibrary(source, game); - } - - if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a " + filter.getMessage() + " with CMC X or less" + '?', source, game)) { - TargetCard target = new TargetCard(0, 1, Zone.GRAVEYARD, advancedFilter); - target.clearChosen(); - if (controller.choose(outcome, controller.getGraveyard(), target, game)) { - if (!target.getTargets().isEmpty()) { - cardFound = game.getCard(target.getFirstTarget()); - } - } - } - - if (cardFound != null) { - controller.revealCards(sourceObject.getIdName(), new CardsImpl(cardFound), game); - controller.moveCards(cardFound, Zone.BATTLEFIELD, source, game); - } - - return true; - } - return false; - } - -} +package mage.abilities.effects.common.search; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.constants.ComparisonType; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author antoni-g + */ +public class SearchLibraryGraveyardWithLessCMCPutIntoPlay extends OneShotEffect { + + private final FilterCard filter; + + public SearchLibraryGraveyardWithLessCMCPutIntoPlay() { + this(new FilterCard()); + } + + public SearchLibraryGraveyardWithLessCMCPutIntoPlay(FilterCard filter) { + super(Outcome.PutCreatureInPlay); + this.filter = filter; + staticText = "Search your library or graveyard for a " + filter.getMessage() + " with converted mana cost X or less, put it onto the battlefield, then shuffle your library"; + } + + public SearchLibraryGraveyardWithLessCMCPutIntoPlay(final SearchLibraryGraveyardWithLessCMCPutIntoPlay effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public SearchLibraryGraveyardWithLessCMCPutIntoPlay copy() { + return new SearchLibraryGraveyardWithLessCMCPutIntoPlay(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + Card cardFound = null; + if (controller != null && sourceObject != null) { + // create x cost filter + FilterCard advancedFilter = filter.copy(); // never change static objects so copy the object here before + advancedFilter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + + if (controller.chooseUse(outcome, "Search your library for a " + filter.getMessage() + " with CMC X or less" + '?', source, game)) { + TargetCardInLibrary target = new TargetCardInLibrary(advancedFilter); + target.clearChosen(); + if (controller.searchLibrary(target, source, game)) { + if (!target.getTargets().isEmpty()) { + cardFound = game.getCard(target.getFirstTarget()); + } + } + controller.shuffleLibrary(source, game); + } + + if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a " + filter.getMessage() + " with CMC X or less" + '?', source, game)) { + TargetCard target = new TargetCard(0, 1, Zone.GRAVEYARD, advancedFilter); + target.clearChosen(); + if (controller.choose(outcome, controller.getGraveyard(), target, game)) { + if (!target.getTargets().isEmpty()) { + cardFound = game.getCard(target.getFirstTarget()); + } + } + } + + if (cardFound != null) { + controller.revealCards(sourceObject.getIdName(), new CardsImpl(cardFound), game); + controller.moveCards(cardFound, Zone.BATTLEFIELD, source, game); + } + + return true; + } + return false; + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryWithLessCMCPutInPlayEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryWithLessCMCPutInPlayEffect.java index c116004e5e..8f4bb5f105 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryWithLessCMCPutInPlayEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryWithLessCMCPutInPlayEffect.java @@ -1,65 +1,65 @@ - -package mage.abilities.effects.common.search; - -import mage.abilities.Ability; -import mage.constants.ComparisonType; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetCardInLibrary; - -/** - * - * @author Styxo - */ -public class SearchLibraryWithLessCMCPutInPlayEffect extends OneShotEffect { - - private final FilterCard filter; - - public SearchLibraryWithLessCMCPutInPlayEffect() { - this(new FilterCard()); - } - - public SearchLibraryWithLessCMCPutInPlayEffect(FilterCard filter) { - super(Outcome.PutCreatureInPlay); - this.filter = filter; - staticText = "Search your library for a " + filter.getMessage() + " with converted mana cost X or less, put it onto the battlefield, then shuffle your library"; - } - - public SearchLibraryWithLessCMCPutInPlayEffect(final SearchLibraryWithLessCMCPutInPlayEffect effect) { - super(effect); - this.filter = effect.filter; - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - FilterCard advancedFilter = filter.copy(); // never change static objects so copy the object here before - advancedFilter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); - TargetCardInLibrary target = new TargetCardInLibrary(advancedFilter); - if (controller.searchLibrary(target, source, game)) { - if (!target.getTargets().isEmpty()) { - Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); - if (card != null) { - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - } - } - controller.shuffleLibrary(source, game); - } - return true; - } - return false; - } - - @Override - public SearchLibraryWithLessCMCPutInPlayEffect copy() { - return new SearchLibraryWithLessCMCPutInPlayEffect(this); - } - -} + +package mage.abilities.effects.common.search; + +import mage.abilities.Ability; +import mage.constants.ComparisonType; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author Styxo + */ +public class SearchLibraryWithLessCMCPutInPlayEffect extends OneShotEffect { + + private final FilterCard filter; + + public SearchLibraryWithLessCMCPutInPlayEffect() { + this(new FilterCard()); + } + + public SearchLibraryWithLessCMCPutInPlayEffect(FilterCard filter) { + super(Outcome.PutCreatureInPlay); + this.filter = filter; + staticText = "Search your library for a " + filter.getMessage() + " with converted mana cost X or less, put it onto the battlefield, then shuffle your library"; + } + + public SearchLibraryWithLessCMCPutInPlayEffect(final SearchLibraryWithLessCMCPutInPlayEffect effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + FilterCard advancedFilter = filter.copy(); // never change static objects so copy the object here before + advancedFilter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + TargetCardInLibrary target = new TargetCardInLibrary(advancedFilter); + if (controller.searchLibrary(target, source, game)) { + if (!target.getTargets().isEmpty()) { + Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + } + } + controller.shuffleLibrary(source, game); + } + return true; + } + return false; + } + + @Override + public SearchLibraryWithLessCMCPutInPlayEffect copy() { + return new SearchLibraryWithLessCMCPutInPlayEffect(this); + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/BolsterEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/BolsterEffect.java index 3bf6a6c9f8..595a00b8d8 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/BolsterEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/BolsterEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.keyword; import mage.abilities.Ability; @@ -78,7 +77,7 @@ public class BolsterEffect extends OneShotEffect { } if (selectedCreature != null) { Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(amount.calculate(game, source, this))); - effect.setTargetPointer(new FixedTarget(selectedCreature.getId())); + effect.setTargetPointer(new FixedTarget(selectedCreature, game)); return effect.apply(game, source); } } diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/CompanionEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/CompanionEffect.java new file mode 100644 index 0000000000..a6c855e2d6 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/keyword/CompanionEffect.java @@ -0,0 +1,39 @@ +package mage.abilities.effects.keyword; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/* + * @author emerald000 + */ +public class CompanionEffect extends OneShotEffect { + + public CompanionEffect() { + super(Outcome.DrawCard); + staticText = "put {this} into your hand"; + } + + private CompanionEffect(final CompanionEffect effect) { + super(effect); + } + + @Override + public CompanionEffect copy() { + return new CompanionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = (Card) source.getSourceObjectIfItStillExists(game); + if (controller != null && card != null && game.getState().getZone(card.getId()) == Zone.OUTSIDE) { + return controller.moveCards(card, Zone.HAND, source, game); + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/ExploreTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/ExploreTargetEffect.java index 1a6ff1285e..2484a0e84d 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/ExploreTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/ExploreTargetEffect.java @@ -1,38 +1,38 @@ - -package mage.abilities.effects.keyword; - -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; - -/** - * - * @author LevelX2 - */ -public class ExploreTargetEffect extends OneShotEffect { - - public ExploreTargetEffect() { - this(true); - } - - public ExploreTargetEffect(boolean showAbilityHint) { - super(Outcome.Benefit); - this.staticText = ExploreSourceEffect.getRuleText(showAbilityHint); - } - - public ExploreTargetEffect(final ExploreTargetEffect effect) { - super(effect); - } - - @Override - public ExploreTargetEffect copy() { - return new ExploreTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return ExploreSourceEffect.explorePermanent(game, getTargetPointer().getFirst(game, source), source); - } - -} + +package mage.abilities.effects.keyword; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; + +/** + * + * @author LevelX2 + */ +public class ExploreTargetEffect extends OneShotEffect { + + public ExploreTargetEffect() { + this(true); + } + + public ExploreTargetEffect(boolean showAbilityHint) { + super(Outcome.Benefit); + this.staticText = ExploreSourceEffect.getRuleText(showAbilityHint); + } + + public ExploreTargetEffect(final ExploreTargetEffect effect) { + super(effect); + } + + @Override + public ExploreTargetEffect copy() { + return new ExploreTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return ExploreSourceEffect.explorePermanent(game, getTargetPointer().getFirst(game, source), source); + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java index 99fd8e360a..d4ebabe2b3 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java @@ -8,22 +8,28 @@ import mage.players.Player; import mage.util.CardUtil; /** - * * @author BetaSteward_at_googlemail.com */ public class ScryEffect extends OneShotEffect { protected int scryNumber; + protected boolean showEffectHint; public ScryEffect(int scryNumber) { + this(scryNumber, true); + } + + public ScryEffect(int scryNumber, boolean showEffectHint) { super(Outcome.Benefit); this.scryNumber = scryNumber; + this.showEffectHint = showEffectHint; this.setText(); } public ScryEffect(final ScryEffect effect) { super(effect); this.scryNumber = effect.scryNumber; + this.showEffectHint = effect.showEffectHint; } @Override @@ -42,12 +48,14 @@ public class ScryEffect extends OneShotEffect { private void setText() { StringBuilder sb = new StringBuilder("scry ").append(scryNumber); - if (scryNumber == 1) { - sb.append(". (Look at the top card of your library. You may put that card on the bottom of your library.)"); - } else { - sb.append(". (Look at the top "); - sb.append(CardUtil.numberToText(scryNumber)); - sb.append(" cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)"); + if (showEffectHint) { + if (scryNumber == 1) { + sb.append(". (Look at the top card of your library. You may put that card on the bottom of your library.)"); + } else { + sb.append(". (Look at the top "); + sb.append(CardUtil.numberToText(scryNumber)); + sb.append(" cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)"); + } } staticText = sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java index 6b152acc44..4c4a23dafd 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java @@ -1,5 +1,7 @@ package mage.abilities.effects.mana; +import java.util.ArrayList; +import java.util.List; import mage.ConditionalMana; import mage.Mana; import mage.abilities.Ability; @@ -13,9 +15,6 @@ import mage.players.Player; import mage.util.CardUtil; import org.apache.log4j.Logger; -import java.util.ArrayList; -import java.util.List; - /** * @author noxx */ @@ -24,20 +23,22 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect { private static final Logger logger = Logger.getLogger(AddConditionalManaOfAnyColorEffect.class); private final DynamicValue amount; + private final DynamicValue netAmount; private final ConditionalManaBuilder manaBuilder; private final boolean oneChoice; public AddConditionalManaOfAnyColorEffect(int amount, ConditionalManaBuilder manaBuilder) { - this(StaticValue.get(amount), manaBuilder); + this(StaticValue.get(amount), StaticValue.get(amount), manaBuilder); } - public AddConditionalManaOfAnyColorEffect(DynamicValue amount, ConditionalManaBuilder manaBuilder) { - this(amount, manaBuilder, true); + public AddConditionalManaOfAnyColorEffect(DynamicValue amount, DynamicValue netAmount, ConditionalManaBuilder manaBuilder) { + this(amount, netAmount, manaBuilder, true); } - public AddConditionalManaOfAnyColorEffect(DynamicValue amount, ConditionalManaBuilder manaBuilder, boolean oneChoice) { + public AddConditionalManaOfAnyColorEffect(DynamicValue amount, DynamicValue netAmount, ConditionalManaBuilder manaBuilder, boolean oneChoice) { super(); this.amount = amount; + this.netAmount = netAmount; this.manaBuilder = manaBuilder; this.oneChoice = oneChoice; // @@ -53,6 +54,7 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect { public AddConditionalManaOfAnyColorEffect(final AddConditionalManaOfAnyColorEffect effect) { super(effect); this.amount = effect.amount; + this.netAmount = effect.netAmount; this.manaBuilder = effect.manaBuilder; this.oneChoice = effect.oneChoice; } @@ -66,9 +68,16 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect { public List getNetMana(Game game, Ability source) { List netMana = new ArrayList<>(); if (game != null) { - int value = amount.calculate(game, source, this); - if (value > 0) { - netMana.add(Mana.AnyMana(value)); + if (game.inCheckPlayableState()) { + int amountAvailableMana = netAmount.calculate(game, source, this); + if (amountAvailableMana > 0) { + netMana.add(manaBuilder.setMana(Mana.AnyMana(amountAvailableMana), source, game).build()); + } + } else { + int amountOfManaLeft = amount.calculate(game, source, this); + if (amountOfManaLeft > 0) { + netMana.add(Mana.AnyMana(amountOfManaLeft)); + } } } return netMana; diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfTwoDifferentColorsEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfTwoDifferentColorsEffect.java index 4c58fda7d6..419a2c25a7 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfTwoDifferentColorsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfTwoDifferentColorsEffect.java @@ -1,61 +1,61 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.mana; - -import mage.ConditionalMana; -import mage.Mana; -import mage.abilities.Ability; -import mage.abilities.effects.common.ManaEffect; -import mage.abilities.mana.builder.ConditionalManaBuilder; -import mage.choices.ManaChoice; -import mage.game.Game; -import mage.players.Player; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author jeffwadsworth - */ -public class AddConditionalManaOfTwoDifferentColorsEffect extends ManaEffect { - - private final ConditionalManaBuilder manaBuilder; - - public AddConditionalManaOfTwoDifferentColorsEffect(ConditionalManaBuilder manaBuilder) { - super(); - this.manaBuilder = manaBuilder; - staticText = "Add two mana of different colors. " + manaBuilder.getRule(); - } - - private AddConditionalManaOfTwoDifferentColorsEffect(final AddConditionalManaOfTwoDifferentColorsEffect effect) { - super(effect); - this.manaBuilder = effect.manaBuilder; - } - - @Override - public List getNetMana(Game game, Ability source) { - List netMana = new ArrayList<>(); - netMana.add(Mana.AnyMana(2)); - return netMana; - } - - @Override - public Mana produceMana(Game game, Ability source) { - if (game != null) { - Player player = getPlayer(game, source); - Mana mana = new ConditionalMana(manaBuilder.setMana( - ManaChoice.chooseTwoDifferentColors( - player, game), source, game).build()); - return mana; - } - return new Mana(); - } - - @Override - public AddConditionalManaOfTwoDifferentColorsEffect copy() { - return new AddConditionalManaOfTwoDifferentColorsEffect(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.mana; + +import mage.ConditionalMana; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.effects.common.ManaEffect; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.choices.ManaChoice; +import mage.game.Game; +import mage.players.Player; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author jeffwadsworth + */ +public class AddConditionalManaOfTwoDifferentColorsEffect extends ManaEffect { + + private final ConditionalManaBuilder manaBuilder; + + public AddConditionalManaOfTwoDifferentColorsEffect(ConditionalManaBuilder manaBuilder) { + super(); + this.manaBuilder = manaBuilder; + staticText = "Add two mana of different colors. " + manaBuilder.getRule(); + } + + private AddConditionalManaOfTwoDifferentColorsEffect(final AddConditionalManaOfTwoDifferentColorsEffect effect) { + super(effect); + this.manaBuilder = effect.manaBuilder; + } + + @Override + public List getNetMana(Game game, Ability source) { + List netMana = new ArrayList<>(); + netMana.add(Mana.AnyMana(2)); + return netMana; + } + + @Override + public Mana produceMana(Game game, Ability source) { + if (game != null) { + Player player = getPlayer(game, source); + Mana mana = new ConditionalMana(manaBuilder.setMana( + ManaChoice.chooseTwoDifferentColors( + player, game), source, game).build()); + return mana; + } + return new Mana(); + } + + @Override + public AddConditionalManaOfTwoDifferentColorsEffect copy() { + return new AddConditionalManaOfTwoDifferentColorsEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java index 22740daf39..1b856a1d7f 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java @@ -1,13 +1,9 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.effects.mana; import mage.Mana; import mage.ObjectColor; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.effects.common.ManaEffect; import mage.constants.ColoredManaSymbol; import mage.game.Game; @@ -17,6 +13,8 @@ import mage.game.Game; */ public class AddManaChosenColorEffect extends ManaEffect { + private ObjectColor chosenColorInfo = null; + public AddManaChosenColorEffect() { super(); staticText = "Add one mana of the chosen color"; @@ -24,6 +22,7 @@ public class AddManaChosenColorEffect extends ManaEffect { public AddManaChosenColorEffect(final AddManaChosenColorEffect effect) { super(effect); + chosenColorInfo = effect.chosenColorInfo; } @Override @@ -31,6 +30,7 @@ public class AddManaChosenColorEffect extends ManaEffect { if (game != null) { ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color"); if (color != null) { + this.chosenColorInfo = color; return new Mana(ColoredManaSymbol.lookup(color.toString().charAt(0))); } } @@ -41,4 +41,10 @@ public class AddManaChosenColorEffect extends ManaEffect { public AddManaChosenColorEffect copy() { return new AddManaChosenColorEffect(this); } + + @Override + public String getText(Mode mode) { + // buggy, but no other way, see comments https://github.com/magefree/mage/commit/cd8d12365f8119dcfe19176a7142e77f80f423b + return super.getText(mode) + (chosenColorInfo == null ? "" : " {" + chosenColorInfo.toString() + "}"); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java index 82b3e1fe5c..bc80fc7fd7 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java @@ -1,19 +1,23 @@ package mage.abilities.effects.mana; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import mage.Mana; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.ManaEffect; +import mage.abilities.mana.ManaOptions; import mage.constants.ColoredManaSymbol; +import mage.constants.ManaType; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - /** * @author LevelX2 */ @@ -21,20 +25,22 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { private ArrayList manaSymbols = new ArrayList<>(); private final DynamicValue amount; + private final DynamicValue netAmount; public AddManaInAnyCombinationEffect(int amount) { - this(StaticValue.get(amount), ColoredManaSymbol.B, ColoredManaSymbol.U, ColoredManaSymbol.R, ColoredManaSymbol.W, ColoredManaSymbol.G); + this(StaticValue.get(amount), StaticValue.get(amount), ColoredManaSymbol.B, ColoredManaSymbol.U, ColoredManaSymbol.R, ColoredManaSymbol.W, ColoredManaSymbol.G); } public AddManaInAnyCombinationEffect(int amount, ColoredManaSymbol... coloredManaSymbols) { - this(StaticValue.get(amount), coloredManaSymbols); + this(StaticValue.get(amount), StaticValue.get(amount), coloredManaSymbols); } - public AddManaInAnyCombinationEffect(DynamicValue amount, ColoredManaSymbol... coloredManaSymbols) { + public AddManaInAnyCombinationEffect(DynamicValue amount, DynamicValue netAmount, ColoredManaSymbol... coloredManaSymbols) { super(); this.manaSymbols.addAll(Arrays.asList(coloredManaSymbols)); this.amount = amount; this.staticText = setText(); + this.netAmount = netAmount; } public AddManaInAnyCombinationEffect(int amount, String text) { @@ -47,8 +53,8 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { this.staticText = text; } - public AddManaInAnyCombinationEffect(DynamicValue amount, String text, ColoredManaSymbol... coloredManaSymbols) { - this(amount, coloredManaSymbols); + public AddManaInAnyCombinationEffect(DynamicValue amount, DynamicValue netAmount, String text, ColoredManaSymbol... coloredManaSymbols) { + this(amount, netAmount, coloredManaSymbols); this.staticText = text; } @@ -56,6 +62,11 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { super(effect); this.manaSymbols = effect.manaSymbols; this.amount = effect.amount; + if (effect.netAmount != null) { + this.netAmount = effect.netAmount.copy(); + } else { + this.netAmount = null; + } } @Override @@ -67,9 +78,26 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { public List getNetMana(Game game, Ability source) { List netMana = new ArrayList<>(); if (game != null) { - int amountOfManaLeft = amount.calculate(game, source, this); - if (amountOfManaLeft > 0) { - netMana.add(Mana.AnyMana(amountOfManaLeft)); + if (game.inCheckPlayableState()) { + int count = netAmount.calculate(game, source, this); + if (count > 0) { + // add color combinations + ManaOptions allPossibleMana = new ManaOptions(); + for (int i = 0; i < count; ++i) { + ManaOptions currentPossibleMana = new ManaOptions(); + for (ColoredManaSymbol coloredManaSymbol : manaSymbols) { + currentPossibleMana.add(new Mana(coloredManaSymbol)); + } + allPossibleMana.addMana(currentPossibleMana); + } + allPossibleMana.removeDuplicated(); + return allPossibleMana.stream().collect(Collectors.toList()); + } + } else { + int amountOfManaLeft = amount.calculate(game, source, this); + if (amountOfManaLeft > 0) { + netMana.add(Mana.AnyMana(amountOfManaLeft)); + } } } return netMana; @@ -104,6 +132,29 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { return null; } + @Override + public Set getProducableManaTypes(Game game, Ability source) { + Set manaTypes = new HashSet<>(); + for(ColoredManaSymbol coloredManaSymbol: manaSymbols) { + if (coloredManaSymbol.equals(ColoredManaSymbol.B)) { + manaTypes.add(ManaType.BLACK); + } + if (coloredManaSymbol.equals(ColoredManaSymbol.R)) { + manaTypes.add(ManaType.RED); + } + if (coloredManaSymbol.equals(ColoredManaSymbol.G)) { + manaTypes.add(ManaType.GREEN); + } + if (coloredManaSymbol.equals(ColoredManaSymbol.U)) { + manaTypes.add(ManaType.BLUE); + } + if (coloredManaSymbol.equals(ColoredManaSymbol.W)) { + manaTypes.add(ManaType.WHITE); + } + } + return manaTypes; + } + private String setText() { StringBuilder sb = new StringBuilder("Add "); sb.append(CardUtil.numberToText(amount.toString())); diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java index 7fc082c33c..987490072e 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java @@ -1,21 +1,22 @@ package mage.abilities.effects.mana; +import java.util.ArrayList; +import java.util.List; import mage.Mana; import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; import mage.choices.ChoiceColor; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; -import java.util.ArrayList; -import java.util.List; - /** * @author BetaSteward_at_googlemail.com */ public class AddManaOfAnyColorEffect extends BasicManaEffect { protected final int amount; + protected final DynamicValue netAmount; protected final ArrayList netMana = new ArrayList<>(); protected final boolean setFlag; @@ -28,8 +29,13 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect { } public AddManaOfAnyColorEffect(int amount, boolean setFlag) { + this(amount, null, setFlag); + } + + public AddManaOfAnyColorEffect(int amount, DynamicValue netAmount, boolean setFlag) { super(new Mana(0, 0, 0, 0, 0, 0, amount, 0)); this.amount = amount; + this.netAmount = netAmount; netMana.add(Mana.AnyMana(amount)); this.staticText = "add " + CardUtil.numberToText(amount) + " mana of any " + (amount > 1 ? "one " : "") + "color"; this.setFlag = setFlag; @@ -40,6 +46,12 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect { this.amount = effect.amount; this.netMana.addAll(effect.netMana); this.setFlag = effect.setFlag; + if (effect.netAmount == null) { + this.netAmount = null; + } else { + this.netAmount = effect.netAmount.copy(); + } + } @Override @@ -49,6 +61,16 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect { @Override public List getNetMana(Game game, Ability source) { + if (game != null && game.inCheckPlayableState()) { + if (netAmount != null) { + int count = netAmount.calculate(game, source, this); + Mana mana = new Mana(); + mana.setAny(count * amount); + ArrayList possibleNetMana = new ArrayList<>(); + possibleNetMana.add(mana); + return possibleNetMana; + } + } return new ArrayList<>(this.netMana); } diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyTypeProducedEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyTypeProducedEffect.java index e80e2f288b..f849c21b69 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyTypeProducedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyTypeProducedEffect.java @@ -38,9 +38,26 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect { @Override public List getNetMana(Game game, Ability source) { List netMana = new ArrayList<>(); - Mana types = (Mana) this.getValue("mana"); // TODO: will not work until TriggeredManaAbility fix (see TriggeredManaAbilityMustGivesExtraManaOptions test) + Mana types = (Mana) this.getValue("mana"); if (types != null) { - netMana.add(types.copy()); + if (types.getBlack() > 0) { + netMana.add(Mana.BlackMana(1)); + } + if (types.getRed() > 0) { + netMana.add(Mana.RedMana(1)); + } + if (types.getBlue() > 0) { + netMana.add(Mana.BlueMana(1)); + } + if (types.getGreen() > 0) { + netMana.add(Mana.GreenMana(1)); + } + if (types.getWhite() > 0) { + netMana.add(Mana.WhiteMana(1)); + } + if (types.getColorless() > 0) { + netMana.add(Mana.ColorlessMana(1)); + } } return netMana; } diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfTwoDifferentColorsEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfTwoDifferentColorsEffect.java index 8a8999b8f8..1bd4bd3f90 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfTwoDifferentColorsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfTwoDifferentColorsEffect.java @@ -1,46 +1,46 @@ -package mage.abilities.effects.mana; - - -import mage.Mana; -import mage.abilities.Ability; -import mage.abilities.effects.common.ManaEffect; -import mage.choices.ManaChoice; -import mage.game.Game; -import mage.players.Player; - -import java.util.ArrayList; -import java.util.List; - -public class AddManaOfTwoDifferentColorsEffect extends ManaEffect { - - public AddManaOfTwoDifferentColorsEffect() { - super(); - staticText = "Add two mana of different colors."; - } - - private AddManaOfTwoDifferentColorsEffect(final AddManaOfTwoDifferentColorsEffect effect) { - super(effect); - } - - @Override - public List getNetMana(Game game, Ability source) { - List netMana = new ArrayList<>(); - netMana.add(Mana.AnyMana(2)); - return netMana; - } - - @Override - public Mana produceMana(Game game, Ability source) { - if (game != null) { - Player player = getPlayer(game, source); - return ManaChoice.chooseTwoDifferentColors(player, game); - } else { - return new Mana(); - } - } - - @Override - public AddManaOfTwoDifferentColorsEffect copy() { - return new AddManaOfTwoDifferentColorsEffect(this); - } -} +package mage.abilities.effects.mana; + + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.effects.common.ManaEffect; +import mage.choices.ManaChoice; +import mage.game.Game; +import mage.players.Player; + +import java.util.ArrayList; +import java.util.List; + +public class AddManaOfTwoDifferentColorsEffect extends ManaEffect { + + public AddManaOfTwoDifferentColorsEffect() { + super(); + staticText = "Add two mana of different colors."; + } + + private AddManaOfTwoDifferentColorsEffect(final AddManaOfTwoDifferentColorsEffect effect) { + super(effect); + } + + @Override + public List getNetMana(Game game, Ability source) { + List netMana = new ArrayList<>(); + netMana.add(Mana.AnyMana(2)); + return netMana; + } + + @Override + public Mana produceMana(Game game, Ability source) { + if (game != null) { + Player player = getPlayer(game, source); + return ManaChoice.chooseTwoDifferentColors(player, game); + } else { + return new Mana(); + } + } + + @Override + public AddManaOfTwoDifferentColorsEffect copy() { + return new AddManaOfTwoDifferentColorsEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/mana/BasicManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/BasicManaEffect.java index c3c0e99633..e50e521ce8 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/BasicManaEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/BasicManaEffect.java @@ -1,34 +1,83 @@ package mage.abilities.effects.mana; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import mage.ConditionalMana; import mage.Mana; import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.common.ManaEffect; import mage.game.Game; -import mage.players.Player; public class BasicManaEffect extends ManaEffect { protected Mana manaTemplate; + private final DynamicValue netAmount; public BasicManaEffect(Mana mana) { + this(mana, null); + this.manaTemplate = mana; + } + + public BasicManaEffect(Mana mana, DynamicValue netAmount) { super(); this.manaTemplate = mana; staticText = "add " + mana.toString(); + this.netAmount = netAmount; } public BasicManaEffect(ConditionalMana conditionalMana) { super(); this.manaTemplate = conditionalMana; staticText = "add " + manaTemplate.toString() + " " + conditionalMana.getDescription(); + this.netAmount = null; } public BasicManaEffect(final BasicManaEffect effect) { super(effect); this.manaTemplate = effect.manaTemplate.copy(); + this.netAmount = effect.netAmount; } + @Override + public List getNetMana(Game game, Ability source) { + if (game != null && game.inCheckPlayableState() && netAmount != null) { + // calculate the maximum available mana + int count = netAmount.calculate(game, source, this); + Mana computedMana = new Mana(); + if (count > 0) { + if (manaTemplate.getBlack() > 0) { + computedMana.setBlack(count * manaTemplate.getBlack()); + } + if (manaTemplate.getBlue() > 0) { + computedMana.setBlue(count * manaTemplate.getBlue()); + } + if (manaTemplate.getGreen() > 0) { + computedMana.setGreen(count * manaTemplate.getGreen()); + } + if (manaTemplate.getRed() > 0) { + computedMana.setRed(count * manaTemplate.getRed()); + } + if (manaTemplate.getWhite() > 0) { + computedMana.setWhite(count * manaTemplate.getWhite()); + } + if (manaTemplate.getColorless() > 0) { + computedMana.setColorless(count * manaTemplate.getColorless()); + } + if (manaTemplate.getAny() > 0) { + throw new IllegalArgumentException("BasicManaEffect does not support {Any} mana!"); + } + if (manaTemplate.getGeneric() > 0) { + computedMana.setGeneric(count * manaTemplate.getGeneric()); + } + } + return new ArrayList<>(Arrays.asList(computedMana)); + } + return super.getNetMana(game, source); + } + @Override public BasicManaEffect copy() { return new BasicManaEffect(this); diff --git a/Mage/src/main/java/mage/abilities/hint/ConditionHint.java b/Mage/src/main/java/mage/abilities/hint/ConditionHint.java index 4df8b1cf59..6347054b2f 100644 --- a/Mage/src/main/java/mage/abilities/hint/ConditionHint.java +++ b/Mage/src/main/java/mage/abilities/hint/ConditionHint.java @@ -3,6 +3,7 @@ package mage.abilities.hint; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.game.Game; +import mage.util.CardUtil; import java.awt.*; @@ -18,15 +19,19 @@ public class ConditionHint implements Hint { private Color falseColor; private Boolean useIcons; + public ConditionHint(Condition condition) { + this(condition, condition.toString()); + } + public ConditionHint(Condition condition, String textWithIcons) { this(condition, textWithIcons, null, textWithIcons, null, true); } public ConditionHint(Condition condition, String trueText, Color trueColor, String falseText, Color falseColor, Boolean useIcons) { this.condition = condition; - this.trueText = trueText; + this.trueText = CardUtil.getTextWithFirstCharUpperCase(trueText); this.trueColor = trueColor; - this.falseText = falseText; + this.falseText = CardUtil.getTextWithFirstCharUpperCase(falseText); this.falseColor = falseColor; this.useIcons = useIcons; } diff --git a/Mage/src/main/java/mage/abilities/hint/Hint.java b/Mage/src/main/java/mage/abilities/hint/Hint.java index 1209214005..f4ba63986c 100644 --- a/Mage/src/main/java/mage/abilities/hint/Hint.java +++ b/Mage/src/main/java/mage/abilities/hint/Hint.java @@ -10,9 +10,15 @@ import java.io.Serializable; */ public interface Hint extends Serializable { + // It's a constant hint for cards/permanents (e.g. visible all the time) + // If you want to use a temporary hint for permanent then possible solutions availeable: + // 1. Add card hint to gained ability (ability create code); + // 2. Add constant text: InfoEffect.addInfoToPermanent + // 3. Add dynamic card hint: InfoEffect.addCardHintToPermanent + // TODO: add card hint for ActivateIfConditionActivatedAbility // * remove my turn condition from cards construction - // * test condition texts (add alternative texts to donditions like getHintText?) + // * test condition texts (add alternative texts to conditions like getHintText?) // * add auto-capitalize of first symbol // * add support of compound conditions // see https://github.com/magefree/mage/issues/5497 diff --git a/Mage/src/main/java/mage/abilities/hint/ValueHint.java b/Mage/src/main/java/mage/abilities/hint/ValueHint.java index 49c03d7c91..656ab0fdb6 100644 --- a/Mage/src/main/java/mage/abilities/hint/ValueHint.java +++ b/Mage/src/main/java/mage/abilities/hint/ValueHint.java @@ -9,8 +9,8 @@ import mage.game.Game; */ public class ValueHint implements Hint { - private String name; - private DynamicValue value; + private final String name; + private final DynamicValue value; public ValueHint(String name, DynamicValue value) { this.name = name; diff --git a/Mage/src/main/java/mage/abilities/hint/common/AbilityResolutionCountHint.java b/Mage/src/main/java/mage/abilities/hint/common/AbilityResolutionCountHint.java new file mode 100644 index 0000000000..9a8f8ff32b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/common/AbilityResolutionCountHint.java @@ -0,0 +1,26 @@ +package mage.abilities.hint.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.AbilityResolutionCount; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.game.Game; + +/** + * @author emerald000 + */ +public enum AbilityResolutionCountHint implements Hint { + + instance; + private static final Hint hint = new ValueHint("Resolution count", AbilityResolutionCount.instance); + + @Override + public String getText(Game game, Ability ability) { + return hint.getText(game, ability); + } + + @Override + public Hint copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/hint/common/ArtifactsYouControlHint.java b/Mage/src/main/java/mage/abilities/hint/common/ArtifactYouControlHint.java similarity index 73% rename from Mage/src/main/java/mage/abilities/hint/common/ArtifactsYouControlHint.java rename to Mage/src/main/java/mage/abilities/hint/common/ArtifactYouControlHint.java index d4b253da8e..960c416385 100644 --- a/Mage/src/main/java/mage/abilities/hint/common/ArtifactsYouControlHint.java +++ b/Mage/src/main/java/mage/abilities/hint/common/ArtifactYouControlHint.java @@ -1,7 +1,7 @@ package mage.abilities.hint.common; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.ArtifactsYouControlCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.hint.Hint; import mage.abilities.hint.ValueHint; import mage.game.Game; @@ -9,10 +9,10 @@ import mage.game.Game; /** * @author JayDi85 */ -public enum ArtifactsYouControlHint implements Hint { +public enum ArtifactYouControlHint implements Hint { instance; - private static final Hint hint = new ValueHint("Artifacts you control", ArtifactsYouControlCount.instance); + private static final Hint hint = new ValueHint("Artifacts you control", ArtifactYouControlCount.instance); @Override public String getText(Game game, Ability ability) { diff --git a/Mage/src/main/java/mage/abilities/hint/common/MetalcraftHint.java b/Mage/src/main/java/mage/abilities/hint/common/MetalcraftHint.java new file mode 100644 index 0000000000..1be7ca6228 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/common/MetalcraftHint.java @@ -0,0 +1,34 @@ +package mage.abilities.hint.common; + +import mage.abilities.Ability; +import mage.abilities.condition.common.MetalcraftCondition; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.game.Game; + +/** + * @author JayDi85 + */ +public enum MetalcraftHint implements Hint { + + instance; + private static final ConditionHint hint = new ConditionHint(MetalcraftCondition.instance, "You control three or more artifacts"); + private static final FilterPermanent filter = new FilterPermanent("artifact"); + + static { + filter.add(CardType.ARTIFACT.getPredicate()); + } + + @Override + public String getText(Game game, Ability ability) { + int amount = game.getBattlefield().countAll(filter, ability.getControllerId(), game); + return hint.getText(game, ability) + " (current: " + amount + ")"; + } + + @Override + public Hint copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/hint/common/MonstrousHint.java b/Mage/src/main/java/mage/abilities/hint/common/MonstrousHint.java new file mode 100644 index 0000000000..6db8fe011b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/common/MonstrousHint.java @@ -0,0 +1,24 @@ +package mage.abilities.hint.common; + +import mage.abilities.Ability; +import mage.abilities.condition.common.MonstrousCondition; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.game.Game; + +public enum MonstrousHint implements Hint { + instance; + private static final ConditionHint hint = new ConditionHint(MonstrousCondition.instance, + "{this} is monstrous", null, + "{this} isn't monstrous", null, true); + + @Override + public String getText(Game game, Ability ability) { + return hint.getText(game, ability); + } + + @Override + public Hint copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/hint/common/ProwlCostWasPaidHint.java b/Mage/src/main/java/mage/abilities/hint/common/ProwlCostWasPaidHint.java new file mode 100644 index 0000000000..d1e3ae8d07 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/common/ProwlCostWasPaidHint.java @@ -0,0 +1,26 @@ +package mage.abilities.hint.common; + +import mage.abilities.Ability; +import mage.abilities.condition.common.ProwlCostWasPaidCondition; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.game.Game; + +/** + * @author JayDi85 + */ +public enum ProwlCostWasPaidHint implements Hint { + + instance; + private static final ConditionHint hint = new ConditionHint(ProwlCostWasPaidCondition.instance, "Prowl cost was paid"); + + @Override + public String getText(Game game, Ability ability) { + return hint.getText(game, ability); + } + + @Override + public Hint copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/hint/common/ProwlHint.java b/Mage/src/main/java/mage/abilities/hint/common/ProwlHint.java new file mode 100644 index 0000000000..27e78ad13b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/common/ProwlHint.java @@ -0,0 +1,26 @@ +package mage.abilities.hint.common; + +import mage.abilities.Ability; +import mage.abilities.condition.common.ProwlCondition; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.game.Game; + +/** + * @author JayDi85 + */ +public enum ProwlHint implements Hint { + + instance; + private static final ConditionHint hint = new ConditionHint(ProwlCondition.instance, "Prowl cost can be activated"); + + @Override + public String getText(Game game, Ability ability) { + return hint.getText(game, ability); + } + + @Override + public Hint copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/hint/common/RaidHint.java b/Mage/src/main/java/mage/abilities/hint/common/RaidHint.java new file mode 100644 index 0000000000..c941746a2b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/common/RaidHint.java @@ -0,0 +1,26 @@ +package mage.abilities.hint.common; + +import mage.abilities.Ability; +import mage.abilities.condition.common.RaidCondition; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.game.Game; + +/** + * @author JayDi85 + */ +public enum RaidHint implements Hint { + + instance; + private static final ConditionHint hint = new ConditionHint(RaidCondition.instance, "You attacked this turn"); + + @Override + public String getText(Game game, Ability ability) { + return hint.getText(game, ability); + } + + @Override + public Hint copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java b/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java index 26cecb80a6..9f35ffadbc 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java @@ -2,7 +2,7 @@ package mage.abilities.keyword; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AffinityEffect; -import mage.abilities.hint.common.ArtifactsYouControlHint; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.constants.Zone; import mage.filter.StaticFilters; @@ -15,7 +15,7 @@ public class AffinityForArtifactsAbility extends SimpleStaticAbility { super(Zone.ALL, new AffinityEffect(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)); setRuleAtTheTop(true); - this.addHint(ArtifactsYouControlHint.instance); + this.addHint(ArtifactYouControlHint.instance); } public AffinityForArtifactsAbility(final AffinityForArtifactsAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/keyword/AfflictAbility.java b/Mage/src/main/java/mage/abilities/keyword/AfflictAbility.java index 2e8feb1cee..0bf1615332 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AfflictAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AfflictAbility.java @@ -1,13 +1,13 @@ package mage.abilities.keyword; import java.util.UUID; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.targetpointer.FixedTarget; -public class AfflictAbility extends BecomesBlockedTriggeredAbility { +public class AfflictAbility extends BecomesBlockedSourceTriggeredAbility { private final int lifeLoss; diff --git a/Mage/src/main/java/mage/abilities/keyword/AfterlifeAbility.java b/Mage/src/main/java/mage/abilities/keyword/AfterlifeAbility.java index 2fa1c7ecf3..9f87d2039b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AfterlifeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AfterlifeAbility.java @@ -1,11 +1,11 @@ package mage.abilities.keyword; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.game.permanent.token.WhiteBlackSpiritToken; import mage.util.CardUtil; -public class AfterlifeAbility extends DiesTriggeredAbility { +public class AfterlifeAbility extends DiesSourceTriggeredAbility { private final int tokenCount; diff --git a/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java b/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java index 7da5d3a447..aec3f99d8b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java @@ -1,24 +1,39 @@ package mage.abilities.keyword; +import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.SpecialAction; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ActivationManaAbilityStep; import mage.abilities.costs.mana.AlternateManaPaymentAbility; import mage.abilities.costs.mana.ManaCost; import mage.abilities.effects.OneShotEffect; +import mage.abilities.mana.ManaOptions; import mage.constants.*; import mage.filter.FilterPlayer; import mage.game.Game; +import mage.game.stack.Spell; import mage.players.ManaPool; import mage.players.Player; import mage.target.Target; import mage.target.TargetPlayer; import mage.util.ManaUtil; -/* - * @author emerald000 +/** + * 702.131. Assist + *

+ * 702.131a Assist is a static ability that modifies the rules of paying for the + * spell with assist (see rules 601.2g-h). If the total cost to cast a spell + * with assist includes a generic mana component, before you activate mana + * abilities while casting it, you may choose another player. That player has a + * chance to activate mana abilities. Once that player chooses not to activate + * any more mana abilities, you have a chance to activate mana abilities. Before + * you begin to pay the total cost of the spell, the player you chose may pay + * for any amount of the generic mana in the spell’s total cost. + * + * @author emerald000, JayDi85 */ public class AssistAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility { @@ -29,7 +44,7 @@ public class AssistAbility extends SimpleStaticAbility implements AlternateManaP } public AssistAbility() { - super(Zone.STACK, null); + super(Zone.ALL, null); this.setRuleAtTheTop(true); } @@ -42,14 +57,24 @@ public class AssistAbility extends SimpleStaticAbility implements AlternateManaP return new AssistAbility(this); } + @Override + public String getRule() { + return "Assist (Another player can help pay the generic mana of this spell's cost.)"; + } + + @Override + public ActivationManaAbilityStep useOnActivationManaAbilityStep() { + return ActivationManaAbilityStep.BEFORE; + } + @Override public void addSpecialAction(Ability source, Game game, ManaCost unpaid) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && source.getAbilityType() == AbilityType.SPELL && unpaid.getMana().getGeneric() >= 1 - && game.getState().getValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId())) == null) { - SpecialAction specialAction = new AssistSpecialAction(unpaid); + && game.getState().getValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()) + "_assisted") == null) { + SpecialAction specialAction = new AssistSpecialAction(unpaid, this); specialAction.setControllerId(source.getControllerId()); specialAction.setSourceId(source.getSourceId()); Target target = new TargetPlayer(1, 1, true, filter); @@ -61,15 +86,47 @@ public class AssistAbility extends SimpleStaticAbility implements AlternateManaP } @Override - public String getRule() { - return "Assist (Another player can help pay the generic mana of this spell's cost.)"; + public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) { + ManaOptions options = new ManaOptions(); + if (unpaid.getMana().getGeneric() == 0) { + // nothing to pay + return options; + } + + // AI can't use assist (can't ask another player to help), maybe in teammode it can be enabled, but tests must works all the time + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && !controller.isTestMode() && !controller.isHuman()) { + return options; + } + + // search opponents who can help with generic pay + int opponentCanPayMax = 0; + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + // basic and pool, but no coditional mana + ManaOptions availableMana = opponent.getManaAvailable(game); +// availableMana.addMana(opponent.getManaPool().getMana()); + for (Mana mana : availableMana) { + if (mana.count() > 0) { + opponentCanPayMax = Math.max(opponentCanPayMax, mana.count()); + } + } + } + } + + if (opponentCanPayMax > 0) { + options.addMana(Mana.GenericMana(Math.min(unpaid.getMana().getGeneric(), opponentCanPayMax))); + } + + return options; } } class AssistSpecialAction extends SpecialAction { - AssistSpecialAction(ManaCost unpaid) { - super(Zone.ALL, true); + AssistSpecialAction(ManaCost unpaid, AlternateManaPaymentAbility manaAbility) { + super(Zone.ALL, manaAbility); setRuleVisible(false); this.addEffect(new AssistEffect(unpaid)); } @@ -108,17 +165,28 @@ class AssistEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (controller != null && targetPlayer != null) { - int amountToPay = targetPlayer.announceXMana(0, unpaid.getMana().getGeneric(), "How much mana to pay?", game, source); + Spell spell = game.getStack().getSpell(source.getSourceId()); + if (controller != null && spell != null && targetPlayer != null) { + // AI can't assist other players, maybe for teammates only (but tests must work as normal) + int amountToPay = 0; + if (targetPlayer.isHuman() || targetPlayer.isTestMode()) { + amountToPay = targetPlayer.announceXMana(0, unpaid.getMana().getGeneric(), + "How much mana to pay as assist for " + controller.getName() + "?", game, source); + } + if (amountToPay > 0) { Cost cost = ManaUtil.createManaCost(amountToPay, false); if (cost.pay(source, game, source.getSourceId(), targetPlayer.getId(), false)) { ManaPool manaPool = controller.getManaPool(); manaPool.addMana(Mana.ColorlessMana(amountToPay), game, source); - manaPool.unlockManaType(ManaType.COLORLESS); - game.informPlayers(targetPlayer.getLogName() + " paid {" + amountToPay + "}."); - game.getState().setValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()), true); + manaPool.unlockManaType(ManaType.COLORLESS); // it's unlock mana for one use/click, but it can gives more + game.informPlayers(targetPlayer.getLogName() + " paid {" + amountToPay + "} for " + controller.getLogName()); + game.getState().setValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()) + "_assisted", true); } + + // assist must be used before activating mana abilities, so no need to switch step after usage + // (mana and other special abilities can be used after assist) + //spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.NORMAL); } return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/BandsWithOtherAbility.java b/Mage/src/main/java/mage/abilities/keyword/BandsWithOtherAbility.java index 528cc744df..28db7a4b53 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BandsWithOtherAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BandsWithOtherAbility.java @@ -6,6 +6,8 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; +import java.util.Locale; + /** * * @author L_J @@ -69,7 +71,7 @@ public class BandsWithOtherAbility extends StaticAbility { if (subtype != null) { return sb.append(' ').append(subtype.getDescription()).append('s').toString(); } else if (supertype != null) { - return sb.append(' ').append(supertype.toString()).append(" creatures").toString(); + return sb.append(' ').append(supertype.toString().toLowerCase(Locale.ENGLISH)).append(" creatures").toString(); } else if (bandingName != null) { return sb.append(" creatures named ").append(bandingName).toString(); } diff --git a/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java b/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java index 757c5bbe2c..e9ce835df9 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.keyword; import mage.MageObject; @@ -12,6 +11,7 @@ import mage.cards.Card; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SpellAbilityCastMode; import mage.constants.SpellAbilityType; import mage.constants.SubType; import mage.constants.TimingRule; @@ -92,6 +92,7 @@ public class BestowAbility extends SpellAbility { public BestowAbility(Card card, String manaString) { super(new ManaCostsImpl(manaString), card.getName() + " using bestow"); this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE; + this.spellAbilityCastMode = SpellAbilityCastMode.BESTOW; this.timing = TimingRule.SORCERY; TargetPermanent auraTarget = new TargetCreaturePermanent(); this.addTarget(auraTarget); @@ -136,6 +137,14 @@ public class BestowAbility extends SpellAbility { } } + + static public void becomeAura(Card card) { + if (card != null) { + card.getSubtype(null).add(SubType.AURA); + card.getCardType().remove(CardType.CREATURE); + card.getCardType().add(CardType.ENCHANTMENT); + } + } } class BestowEntersBattlefieldEffect extends ReplacementEffectImpl { diff --git a/Mage/src/main/java/mage/abilities/keyword/BountyAbility.java b/Mage/src/main/java/mage/abilities/keyword/BountyAbility.java index 38dda22c8d..779fddfa7a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BountyAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BountyAbility.java @@ -1,54 +1,54 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.keyword; - -import mage.abilities.common.DiesCreatureTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.constants.TargetController; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.CounterPredicate; - -/** - * - * @author Styxo - */ -public class BountyAbility extends DiesCreatureTriggeredAbility { - - private static final FilterCreaturePermanent bountyCounterFilter = new FilterCreaturePermanent("creature an opponent controls with a bounty counter on it"); - - static { - bountyCounterFilter.add(TargetController.OPPONENT.getControllerPredicate()); - bountyCounterFilter.add(new CounterPredicate(CounterType.BOUNTY)); - } - - public BountyAbility(Effect effect) { - super(effect, false, bountyCounterFilter); - } - - public BountyAbility(Effect effect, boolean optional) { - super(effect, optional, bountyCounterFilter); - } - - public BountyAbility(Effect effect, boolean optional, boolean setTargetPointer) { - super(effect, optional, bountyCounterFilter, setTargetPointer); - } - - public BountyAbility(final BountyAbility ability) { - super(ability); - } - - @Override - public BountyAbility copy() { - return new BountyAbility(this); - } - - @Override - public String getRule() { - return "Bounty — " + super.getRule(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.keyword; + +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.CounterPredicate; + +/** + * + * @author Styxo + */ +public class BountyAbility extends DiesCreatureTriggeredAbility { + + private static final FilterCreaturePermanent bountyCounterFilter = new FilterCreaturePermanent("creature an opponent controls with a bounty counter on it"); + + static { + bountyCounterFilter.add(TargetController.OPPONENT.getControllerPredicate()); + bountyCounterFilter.add(new CounterPredicate(CounterType.BOUNTY)); + } + + public BountyAbility(Effect effect) { + super(effect, false, bountyCounterFilter); + } + + public BountyAbility(Effect effect, boolean optional) { + super(effect, optional, bountyCounterFilter); + } + + public BountyAbility(Effect effect, boolean optional, boolean setTargetPointer) { + super(effect, optional, bountyCounterFilter, setTargetPointer); + } + + public BountyAbility(final BountyAbility ability) { + super(ability); + } + + @Override + public BountyAbility copy() { + return new BountyAbility(this); + } + + @Override + public String getRule() { + return "Bounty — " + super.getRule(); + } + +} diff --git a/Mage/src/main/java/mage/abilities/keyword/CanBlockSpaceflightAbility.java b/Mage/src/main/java/mage/abilities/keyword/CanBlockSpaceflightAbility.java index 68138e2f0f..7916189057 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CanBlockSpaceflightAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CanBlockSpaceflightAbility.java @@ -1,43 +1,43 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.keyword; - -import java.io.ObjectStreamException; -import mage.abilities.MageSingleton; -import mage.abilities.StaticAbility; -import mage.constants.Zone; - -/** - * - * @author LevelX2 - */ -public class CanBlockSpaceflightAbility extends StaticAbility implements MageSingleton { - - private static final CanBlockSpaceflightAbility instance = new CanBlockSpaceflightAbility(); - - private Object readResolve() throws ObjectStreamException { - return instance; - } - - public static CanBlockSpaceflightAbility getInstance() { - return instance; - } - - private CanBlockSpaceflightAbility() { - super(Zone.ALL, null); - } - - @Override - public String getRule() { - return "{this} can block creatures with spaceflight."; - } - - @Override - public CanBlockSpaceflightAbility copy() { - return instance; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.keyword; + +import java.io.ObjectStreamException; +import mage.abilities.MageSingleton; +import mage.abilities.StaticAbility; +import mage.constants.Zone; + +/** + * + * @author LevelX2 + */ +public class CanBlockSpaceflightAbility extends StaticAbility implements MageSingleton { + + private static final CanBlockSpaceflightAbility instance = new CanBlockSpaceflightAbility(); + + private Object readResolve() throws ObjectStreamException { + return instance; + } + + public static CanBlockSpaceflightAbility getInstance() { + return instance; + } + + private CanBlockSpaceflightAbility() { + super(Zone.ALL, null); + } + + @Override + public String getRule() { + return "{this} can block creatures with spaceflight."; + } + + @Override + public CanBlockSpaceflightAbility copy() { + return instance; + } + +} diff --git a/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java b/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java index d929bc068f..cb51ee3156 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java @@ -86,7 +86,7 @@ class CascadeEffect extends OneShotEffect { if (controller == null) { return false; } - ExileZone exile = game.getExile().createZone(source.getSourceId(), + ExileZone exile = game.getExile().createZone(source.getSourceId(), controller.getName() + " Cascade"); card = game.getCard(source.getSourceId()); if (card == null) { @@ -99,7 +99,7 @@ class CascadeEffect extends OneShotEffect { break; } controller.moveCardsToExile(card, source, game, true, exile.getId(), exile.getName()); - } while (controller.isInGame() + } while (controller.canRespond() && (card.isLand() || !cardThatCostsLess(sourceCost, card, game))); diff --git a/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java b/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java index 7a7e10b740..0ab7c537b6 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java @@ -42,17 +42,18 @@ public class CommanderStormAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(getSourceId())) { - StackObject spell = game.getStack().getStackObject(getSourceId()); - if (spell instanceof Spell) { - for (Effect effect : this.getEffects()) { - effect.setValue("StormSpell", spell); - effect.setValue("StormSpellRef", new MageObjectReference(spell.getId(), game)); - } - return true; - } + if (!event.getSourceId().equals(getSourceId())) { + return false; } - return false; + StackObject spell = game.getStack().getStackObject(getSourceId()); + if (!(spell instanceof Spell)) { + return false; + } + for (Effect effect : this.getEffects()) { + effect.setValue("StormSpell", spell); + effect.setValue("StormSpellRef", new MageObjectReference(spell.getId(), game)); + } + return true; } @Override @@ -65,11 +66,11 @@ public class CommanderStormAbility extends TriggeredAbilityImpl { class CommanderStormEffect extends OneShotEffect { - public CommanderStormEffect() { + CommanderStormEffect() { super(Outcome.Copy); } - public CommanderStormEffect(final CommanderStormEffect effect) { + private CommanderStormEffect(final CommanderStormEffect effect) { super(effect); } @@ -79,14 +80,19 @@ class CommanderStormEffect extends OneShotEffect { if (spellRef == null) { return false; } - int stormCount = 0; Player player = game.getPlayer(source.getControllerId()); if (player == null) { return false; } - stormCount = game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER).stream() - .map((commanderId) -> game.getState().getWatcher(CommanderPlaysCountWatcher.class).getPlaysCount(commanderId)) - .reduce(stormCount, Integer::sum); + CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); + if (watcher == null) { + return false; + } + int stormCount = game + .getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER) + .stream() + .mapToInt(watcher::getPlaysCount) + .sum(); if (stormCount == 0) { return true; } @@ -95,9 +101,7 @@ class CommanderStormEffect extends OneShotEffect { return false; } game.informPlayers(spell.getLogName() + " will be copied " + stormCount + " time" + (stormCount > 1 ? "s" : "")); - for (int i = 0; i < stormCount; i++) { - spell.createCopyOnStack(game, source, source.getControllerId(), true); - } + spell.createCopyOnStack(game, source, source.getControllerId(), true, stormCount); return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java b/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java new file mode 100644 index 0000000000..dff4f8dcfb --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java @@ -0,0 +1,46 @@ +package mage.abilities.keyword; + +import mage.abilities.SpecialAction; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.CompanionEffect; +import mage.cards.Card; +import mage.constants.TimingRule; +import mage.constants.Zone; + +import java.util.Set; + +/* + * @author emerald000 + */ +public class CompanionAbility extends SpecialAction { + + private final CompanionCondition companionCondition; + + public CompanionAbility(CompanionCondition companionCondition) { + super(Zone.OUTSIDE); + this.companionCondition = companionCondition; + this.addCost(new GenericManaCost(3)); + this.addEffect(new CompanionEffect()); + this.setTiming(TimingRule.SORCERY); + } + + private CompanionAbility(final CompanionAbility ability) { + super(ability); + this.companionCondition = ability.companionCondition; + } + + @Override + public CompanionAbility copy() { + return new CompanionAbility(this); + } + + @Override + public String getRule() { + return "Companion — " + companionCondition.getRule(); + } + + public boolean isLegal(Set cards, int startingSize) { + return companionCondition.isLegal(cards, startingSize); + } +} + diff --git a/Mage/src/main/java/mage/abilities/keyword/CompanionCondition.java b/Mage/src/main/java/mage/abilities/keyword/CompanionCondition.java new file mode 100644 index 0000000000..161bd2aad9 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/CompanionCondition.java @@ -0,0 +1,24 @@ +package mage.abilities.keyword; + +import mage.cards.Card; + +import java.io.Serializable; +import java.util.Set; + +/* + * @author emerald000 + */ +public interface CompanionCondition extends Serializable { + + /** + * @return The rule to get added to the card text. (Everything after the dash) + */ + String getRule(); + + /** + * @param deck The set of cards to check. + * @param startingSize + * @return Whether the companion is valid for that deck. + */ + boolean isLegal(Set deck, int startingSize); +} diff --git a/Mage/src/main/java/mage/abilities/keyword/ConvokeAbility.java b/Mage/src/main/java/mage/abilities/keyword/ConvokeAbility.java index 19a50c3e32..3aa290bba5 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ConvokeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ConvokeAbility.java @@ -1,17 +1,17 @@ package mage.abilities.keyword; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.UUID; import mage.Mana; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.SpecialAction; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ActivationManaAbilityStep; import mage.abilities.costs.mana.AlternateManaPaymentAbility; import mage.abilities.costs.mana.ManaCost; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.ValueHint; +import mage.abilities.mana.ManaOptions; import mage.choices.Choice; import mage.choices.ChoiceColor; import mage.constants.AbilityType; @@ -19,62 +19,67 @@ import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.game.stack.Spell; import mage.players.ManaPool; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; -/* +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.UUID; + +/** * 502.46. Convoke - * + *

* 502.46a Convoke is a static ability that functions while the spell is on the stack. "Convoke" * means "As an additional cost to play this spell, you may tap any number of untapped creatures * you control. Each creature tapped this way reduces the cost to play this spell by {1} or by * one mana of any of that creature's colors." Using the convoke ability follows the rules for * paying additional costs in rules 409.1b and 4091f-h. - * + *

* Example: You play Guardian of Vitu-Ghazi, a spell with convoke that costs {3}{G}{W}. You announce * that you're going to tap an artifact creature, a red creature, and a green-and-white creature to * help pay for it. The artifact creature and the red creature each reduce the spell's cost by {1}. * You choose whether the green-white creature reduces the spell's cost by {1}, {G}, or {W}. Then * the creatures become tapped as you pay Guardian of Vitu-Ghazi's cost. - * + *

* 502.46b Convoke can't reduce the cost to play a spell to less than 0. - * + *

* 502.46c Multiple instances of convoke on the same spell are redundant. - * + *

* You can tap only untapped creatures you control to reduce the cost of a spell with convoke * that you play. - * + *

* While playing a spell with convoke, if you control a creature that taps to produce mana, you * can either tap it for mana or tap it to reduce the cost of the spell, but not both. - * + *

* If you tap a multicolored creature to reduce the cost of a spell with convoke, you reduce * the cost by {1} or by one mana of your choice of any of that creature's colors. - * + *

* Convoke doesn't change a spell's mana cost or converted mana cost. * - * - * @author LevelX2 + * @author LevelX2, JayDi85 */ public class ConvokeAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility { - private static final FilterCreaturePermanent filterUntapped = new FilterCreaturePermanent(); + private static final FilterControlledCreaturePermanent filterUntapped = new FilterControlledCreaturePermanent(); static { filterUntapped.add(Predicates.not(TappedPredicate.instance)); } public ConvokeAbility() { - super(Zone.STACK, null); + super(Zone.ALL, null); // all AlternateManaPaymentAbility must use ALL zone to calculate playable abilities this.setRuleAtTheTop(true); + this.addHint(new ValueHint("Untapped creatures you control", new PermanentsOnBattlefieldCount(filterUntapped))); } public ConvokeAbility(final ConvokeAbility ability) { @@ -86,14 +91,25 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana return new ConvokeAbility(this); } + @Override + public String getRule() { + return "Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)"; + } + + @Override + public ActivationManaAbilityStep useOnActivationManaAbilityStep() { + return ActivationManaAbilityStep.AFTER; + } + @Override public void addSpecialAction(Ability source, Game game, ManaCost unpaid) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && game.getBattlefield().contains(filterUntapped, controller.getId(), 1, game)) { if (source.getAbilityType() == AbilityType.SPELL) { - SpecialAction specialAction = new ConvokeSpecialAction(unpaid); + SpecialAction specialAction = new ConvokeSpecialAction(unpaid, this); specialAction.setControllerId(source.getControllerId()); specialAction.setSourceId(source.getSourceId()); + // create filter for possible creatures to tap FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); filter.add(Predicates.not(TappedPredicate.instance)); @@ -117,7 +133,7 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana filter.add(Predicates.or(colorPredicates)); } Target target = new TargetControlledCreaturePermanent(1, 1, filter, true); - target.setTargetName("creature to convoke"); + target.setTargetName("tap creature card as convoke's pay"); specialAction.addTarget(target); if (specialAction.canActivate(source.getControllerId(), game).canActivate()) { game.getState().getSpecialActions().add(specialAction); @@ -127,15 +143,36 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana } @Override - public String getRule() { - return "Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)"; + public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) { + ManaOptions options = new ManaOptions(); + FilterControlledCreaturePermanent filterBasic = new FilterControlledCreaturePermanent(); + + // each creature can give {1} or color mana + game.getBattlefield().getActivePermanents(filterBasic, source.getControllerId(), source.getSourceId(), game) + .stream() + .filter(permanent -> !permanent.isTapped()) + .forEach(permanent -> { + ManaOptions permMana = new ManaOptions(); + permMana.add(Mana.GenericMana(1)); + for (ObjectColor color : permanent.getColor(game).getColors()) { + if (color.isBlack()) permMana.add(Mana.BlackMana(1)); + if (color.isBlue()) permMana.add(Mana.BlueMana(1)); + if (color.isGreen()) permMana.add(Mana.GreenMana(1)); + if (color.isRed()) permMana.add(Mana.RedMana(1)); + if (color.isWhite()) permMana.add(Mana.WhiteMana(1)); + } + options.addMana(permMana); + }); + + options.removeDuplicated(); + return options; } } class ConvokeSpecialAction extends SpecialAction { - public ConvokeSpecialAction(ManaCost unpaid) { - super(Zone.ALL, true); + public ConvokeSpecialAction(ManaCost unpaid, AlternateManaPaymentAbility manaAbility) { + super(Zone.ALL, manaAbility); setRuleVisible(false); this.addEffect(new ConvokeEffect(unpaid)); } @@ -157,7 +194,7 @@ class ConvokeEffect extends OneShotEffect { public ConvokeEffect(ManaCost unpaid) { super(Outcome.Benefit); this.unpaid = unpaid; - this.staticText = "Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {C} or one mana of that creature's color.)"; + this.staticText = "Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)"; } public ConvokeEffect(final ConvokeEffect effect) { @@ -173,7 +210,8 @@ class ConvokeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { + Spell spell = game.getStack().getSpell(source.getSourceId()); + if (controller != null && spell != null) { for (UUID creatureId : this.getTargetPointer().getTargets(game, source)) { Permanent perm = game.getPermanent(creatureId); if (perm == null) { @@ -225,8 +263,10 @@ class ConvokeEffect extends OneShotEffect { } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CONVOKED, perm.getId(), source.getSourceId(), source.getControllerId())); game.informPlayers("Convoke: " + controller.getLogName() + " taps " + perm.getLogName() + " to pay one " + manaName + " mana"); - } + // can't use mana abilities after that (convoke cost must be payed after mana abilities only) + spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.AFTER); + } } return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/CumulativeUpkeepAbility.java b/Mage/src/main/java/mage/abilities/keyword/CumulativeUpkeepAbility.java index 546de34cf2..cafd1a867b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CumulativeUpkeepAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CumulativeUpkeepAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.keyword; import mage.abilities.Ability; @@ -104,7 +103,7 @@ class CumulativeUpkeepEffect extends OneShotEffect { game.fireEvent(new GameEvent(EventType.PAID_CUMULATIVE_UPKEEP, permanent.getId(), permanent.getId(), player.getId(), ageCounter, false)); return true; } else { - game.restoreState(bookmark, source.getRule()); + player.restoreState(bookmark, source.getRule(), game); } } game.fireEvent(new GameEvent(EventType.DIDNT_PAY_CUMULATIVE_UPKEEP, permanent.getId(), permanent.getId(), player.getId(), ageCounter, false)); diff --git a/Mage/src/main/java/mage/abilities/keyword/CyclingAbility.java b/Mage/src/main/java/mage/abilities/keyword/CyclingAbility.java index 2efb919974..e236cd7fb9 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CyclingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CyclingAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.keyword; import mage.abilities.ActivatedAbilityImpl; @@ -12,7 +11,6 @@ import mage.filter.FilterCard; import mage.target.common.TargetCardInLibrary; /** - * * @author BetaSteward_at_googlemail.com */ public class CyclingAbility extends ActivatedAbilityImpl { @@ -56,5 +54,4 @@ public class CyclingAbility extends ActivatedAbilityImpl { rule.append(cost.getText()).append(" (").append(super.getRule(true)).append(")"); return rule.toString(); } - } diff --git a/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java b/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java index 2855262cdb..6f8002ed78 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java @@ -5,9 +5,14 @@ import mage.abilities.Ability; import mage.abilities.SpecialAction; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.mana.ActivationManaAbilityStep; import mage.abilities.costs.mana.AlternateManaPaymentAbility; import mage.abilities.costs.mana.ManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.ValueHint; +import mage.abilities.mana.ManaOptions; import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; @@ -17,6 +22,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; +import mage.game.stack.Spell; import mage.players.ManaPool; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; @@ -25,35 +31,42 @@ import mage.util.CardUtil; import java.util.List; /** - * 702.65. Delve 702.65a Delve is a static ability that functions while the - * spell with delve is on the stack. “Delve” means “For each generic mana in - * this spell's total cost, you may exile a card from your graveyard rather than - * pay that mana.” The delve ability isn't an additional or alternative cost and - * applies only after the total cost of the spell with delve is determined. - * 702.65b Multiple instances of delve on the same spell are redundant. + * 702.65. Delve *

- * The rules for delve have changed slightly since it was last in an expansion. - * Previously, delve reduced the cost to cast a spell. Under the current rules, - * you exile cards from your graveyard at the same time you pay the spell's - * cost. Exiling a card this way is simply another way to pay that cost. * Delve - * doesn't change a spell's mana cost or converted mana cost. For example, Dead - * Drop's converted mana cost is 10 even if you exiled three cards to cast it. * - * You can't exile cards to pay for the colored mana requirements of a spell - * with delve. * You can't exile more cards than the generic mana requirement of - * a spell with delve. For example, you can't exile more than nine cards from - * your graveyard to cast Dead Drop. * Because delve isn't an alternative cost, - * it can be used in conjunction with alternative costs. + * 702.65a Delve is a static ability that functions while the spell with delve is on the stack. “Delve” means “For + * each generic mana in this spell’s total cost, you may exile a card from your graveyard rather than pay that mana.” + *

+ * 702.65b The delve ability isn’t an additional or alternative cost and applies only after the total cost of the spell + * with delve is determined. + *

+ * 702.65c Multiple instances of delve on the same spell are redundant. + *

+ * The rules for delve have changed slightly since it was last in an expansion. Previously, delve reduced the cost + * to cast a spell. Under the current rules, you exile cards from your graveyard at the same time you pay the spell’s + * cost. Exiling a card this way is simply another way to pay that cost. (This is similar to the change made to + * convoke for the Magic 2015 Core Set.) + *

+ * You can’t exile cards to pay for the colored mana requirements of a spell with delve. + *

+ * You can’t exile more cards than the generic mana requirement of a spell with delve. For example, you can’t exile more + * than nine cards from your graveyard to cast Dead Drop. + *

+ * Because delve isn’t an alternative cost, it can be used in conjunction with alternative costs. * - * @author LevelX2 + * @author LevelX2, JayDi85 *

* TODO: Change card exiling to a way to pay mana costs, now it's maybe not * possible to pay costs from effects that increase the mana costs. + * If it real bug then possible fix: choose cards on apply like convoke and improvise, not as cost */ public class DelveAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility { + private static final DynamicValue cardsInGraveyard = new CardsInControllerGraveyardCount(); + public DelveAbility() { - super(Zone.STACK, null); + super(Zone.ALL, null); this.setRuleAtTheTop(true); + this.addHint(new ValueHint("Cards in your graveyard", cardsInGraveyard)); } public DelveAbility(final DelveAbility ability) { @@ -70,12 +83,17 @@ public class DelveAbility extends SimpleStaticAbility implements AlternateManaPa return "Delve (Each card you exile from your graveyard while casting this spell pays for {1}.)"; } + @Override + public ActivationManaAbilityStep useOnActivationManaAbilityStep() { + return ActivationManaAbilityStep.AFTER; + } + @Override public void addSpecialAction(Ability source, Game game, ManaCost unpaid) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && !controller.getGraveyard().isEmpty()) { - if (unpaid.getMana().getGeneric() > 0 && source.getAbilityType() == AbilityType.SPELL) { - SpecialAction specialAction = new DelveSpecialAction(); + if (source.getAbilityType() == AbilityType.SPELL && unpaid.getMana().getGeneric() > 0) { + SpecialAction specialAction = new DelveSpecialAction(this); specialAction.setControllerId(source.getControllerId()); specialAction.setSourceId(source.getSourceId()); int unpaidAmount = unpaid.getMana().getGeneric(); @@ -84,19 +102,30 @@ public class DelveAbility extends SimpleStaticAbility implements AlternateManaPa } specialAction.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard( 0, Math.min(controller.getGraveyard().size(), unpaidAmount), - new FilterCard("cards to exile for delve's pay from your graveyard"), true))); + new FilterCard("cards from your graveyard"), true))); if (specialAction.canActivate(source.getControllerId(), game).canActivate()) { game.getState().getSpecialActions().add(specialAction); } } } } + + @Override + public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) { + ManaOptions options = new ManaOptions(); + Player controller = game.getPlayer(source.getControllerId()); + int graveCount = cardsInGraveyard.calculate(game, source, null); + if (controller != null && graveCount > 0) { + options.addMana(Mana.GenericMana(Math.min(unpaid.getMana().getGeneric(), graveCount))); + } + return options; + } } class DelveSpecialAction extends SpecialAction { - public DelveSpecialAction() { - super(Zone.ALL, true); + public DelveSpecialAction(AlternateManaPaymentAbility manaAbility) { + super(Zone.ALL, manaAbility); this.addEffect(new DelveEffect()); } @@ -129,9 +158,9 @@ class DelveEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { + Spell spell = game.getStack().getSpell(source.getSourceId()); + if (controller != null && spell != null) { ExileFromGraveCost exileFromGraveCost = (ExileFromGraveCost) source.getCosts().get(0); - List exiledCards = exileFromGraveCost.getExiledCards(); if (!exiledCards.isEmpty()) { Cards toDelve = new CardsImpl(); @@ -139,7 +168,7 @@ class DelveEffect extends OneShotEffect { toDelve.add(card); } ManaPool manaPool = controller.getManaPool(); - manaPool.addMana(new Mana(0, 0, 0, 0, 0, 0, 0, toDelve.size()), game, source); + manaPool.addMana(Mana.ColorlessMana(toDelve.size()), game, source); manaPool.unlockManaType(ManaType.COLORLESS); String keyString = CardUtil.getCardZoneString("delvedCards", source.getSourceId(), game); @SuppressWarnings("unchecked") @@ -149,6 +178,9 @@ class DelveEffect extends OneShotEffect { } else { delvedCards.addAll(toDelve); } + + // can't use mana abilities after that (delve cost must be payed after mana abilities only) + spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.AFTER); } return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/DredgeAbility.java b/Mage/src/main/java/mage/abilities/keyword/DredgeAbility.java index c2f2b79f2d..a78a400e47 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DredgeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DredgeAbility.java @@ -4,8 +4,6 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; @@ -13,11 +11,11 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.players.Player; +import mage.util.CardUtil; /** - * If you would draw a card, instead you may put exactly X cards from the top of - * your library into your graveyard. If you do, return this card from your - * graveyard to your hand. Otherwise, draw a card. + * If you would draw a card, you may mill X cards instead. If you do, return + * this card from your graveyard to your hand. * * @author North */ @@ -44,9 +42,9 @@ class DredgeEffect extends ReplacementEffectImpl { public DredgeEffect(int value) { super(Duration.WhileInGraveyard, Outcome.AIDontUseIt); this.amount = value; - this.staticText = ("Dredge ") + Integer.toString(value) + " (If you would draw a card, instead you may put exactly " - + value + " card(s) from the top of your library into your graveyard. If you do, return this card from " - + "your graveyard to your hand. Otherwise, draw a card.)"; + this.staticText = "Dredge " + value + " (If you would draw a card, you may mill " + + CardUtil.numberToText(value, "a") + (value > 1 ? " cards" : " card") + + " instead. If you do, return this card from your graveyard to your hand.)"; } public DredgeEffect(final DredgeEffect effect) { @@ -74,13 +72,11 @@ class DredgeEffect extends ReplacementEffectImpl { if (owner != null && owner.getLibrary().size() >= amount && owner.chooseUse(outcome, new StringBuilder("Dredge ").append(sourceCard.getLogName()). - append("? (").append(amount).append(" cards go from top of library to graveyard)").toString(), source, game)) { + append("? (").append(amount).append(" cards are milled)").toString(), source, game)) { if (!game.isSimulation()) { game.informPlayers(new StringBuilder(owner.getLogName()).append(" dredges ").append(sourceCard.getLogName()).toString()); } - Cards cardsToGrave = new CardsImpl(); - cardsToGrave.addAll(owner.getLibrary().getTopCards(game, amount)); - owner.moveCards(cardsToGrave, Zone.GRAVEYARD, source, game); + owner.millCards(amount, source, game); owner.moveCards(sourceCard, Zone.HAND, source, game); return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java b/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java index 4baac2c65c..cd50dd7120 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EmbalmAbility.java @@ -1,102 +1,102 @@ - -package mage.abilities.keyword; - -import mage.ObjectColor; -import mage.abilities.Ability; -import mage.abilities.ActivatedAbilityImpl; -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.ExileSourceFromGraveCost; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.TimingRule; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.token.EmptyToken; -import mage.players.Player; -import mage.util.CardUtil; - -/** - * - * @author LevelX2 - */ -public class EmbalmAbility extends ActivatedAbilityImpl { - - private String rule; - - public EmbalmAbility(Cost cost, Card card) { - super(Zone.GRAVEYARD, new EmbalmEffect(), cost); - addCost(new ExileSourceFromGraveCost()); - this.rule = setRule(cost, card); - this.timing = TimingRule.SORCERY; - setRule(cost, card); - } - - public EmbalmAbility(final EmbalmAbility ability) { - super(ability); - this.rule = ability.rule; - } - - @Override - public EmbalmAbility copy() { - return new EmbalmAbility(this); - } - - @Override - public String getRule() { - return rule; - } - - private String setRule(Cost cost, Card card) { - StringBuilder sb = new StringBuilder("Embalm ").append(cost.getText()); - sb.append(" (").append(cost.getText()); - sb.append(", Exile this card from your graveyard: Create a token that's a copy of it, except it's a white Zombie "); - for (SubType subtype : card.getSubtype(null)) { - sb.append(subtype).append(" "); - } - sb.append(" with no mana cost. Embalm only as a sorcery.)"); - return sb.toString(); - } -} - -class EmbalmEffect extends OneShotEffect { - - public EmbalmEffect() { - super(Outcome.PutCreatureInPlay); - } - - public EmbalmEffect(final EmbalmEffect effect) { - super(effect); - } - - @Override - public EmbalmEffect copy() { - return new EmbalmEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(source.getSourceId()); - if (card == null) { - return false; - } - Player controller = game.getPlayer(card.getOwnerId()); - if (controller == null) { - return false; - } - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) - token.getColor(game).setColor(ObjectColor.WHITE); - if (!token.hasSubtype(SubType.ZOMBIE, game)) { - token.getSubtype(game).add(0, SubType.ZOMBIE); - } - token.getManaCost().clear(); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.EMBALMED_CREATURE, token.getId(), source.getSourceId(), controller.getId())); - token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId(), false, false, null); - // Probably it makes sense to remove also the Embalm ability (it's not shown on the token cards). - // Also it can never get active or? But it's not mentioned in the reminder text. - return true; - } -} + +package mage.abilities.keyword; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbilityImpl; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TimingRule; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.EmptyToken; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author LevelX2 + */ +public class EmbalmAbility extends ActivatedAbilityImpl { + + private String rule; + + public EmbalmAbility(Cost cost, Card card) { + super(Zone.GRAVEYARD, new EmbalmEffect(), cost); + addCost(new ExileSourceFromGraveCost()); + this.rule = setRule(cost, card); + this.timing = TimingRule.SORCERY; + setRule(cost, card); + } + + public EmbalmAbility(final EmbalmAbility ability) { + super(ability); + this.rule = ability.rule; + } + + @Override + public EmbalmAbility copy() { + return new EmbalmAbility(this); + } + + @Override + public String getRule() { + return rule; + } + + private String setRule(Cost cost, Card card) { + StringBuilder sb = new StringBuilder("Embalm ").append(cost.getText()); + sb.append(" (").append(cost.getText()); + sb.append(", Exile this card from your graveyard: Create a token that's a copy of it, except it's a white Zombie "); + for (SubType subtype : card.getSubtype(null)) { + sb.append(subtype).append(" "); + } + sb.append(" with no mana cost. Embalm only as a sorcery.)"); + return sb.toString(); + } +} + +class EmbalmEffect extends OneShotEffect { + + public EmbalmEffect() { + super(Outcome.PutCreatureInPlay); + } + + public EmbalmEffect(final EmbalmEffect effect) { + super(effect); + } + + @Override + public EmbalmEffect copy() { + return new EmbalmEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(source.getSourceId()); + if (card == null) { + return false; + } + Player controller = game.getPlayer(card.getOwnerId()); + if (controller == null) { + return false; + } + EmptyToken token = new EmptyToken(); + CardUtil.copyTo(token).from(card); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) + token.getColor(game).setColor(ObjectColor.WHITE); + if (!token.hasSubtype(SubType.ZOMBIE, game)) { + token.getSubtype(game).add(0, SubType.ZOMBIE); + } + token.getManaCost().clear(); + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.EMBALMED_CREATURE, token.getId(), source.getSourceId(), controller.getId())); + token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId(), false, false, null); + // Probably it makes sense to remove also the Embalm ability (it's not shown on the token cards). + // Also it can never get active or? But it's not mentioned in the reminder text. + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java index c11ec9da55..0aa1eddbd6 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java @@ -2,7 +2,6 @@ package mage.abilities.keyword; import mage.Mana; import mage.abilities.SpellAbility; -import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.mana.ManaOptions; @@ -11,7 +10,6 @@ import mage.constants.Outcome; import mage.constants.SpellAbilityType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -30,7 +28,8 @@ public class EmergeAbility extends SpellAbility { public EmergeAbility(Card card, ManaCosts emergeCost) { super(card.getSpellAbility()); - this.newId(); + this.emergeCost = emergeCost.copy(); + this.newId(); // Set newId because cards spell ability is copied and needs own id this.setCardName(card.getName() + " with emerge"); zone = Zone.HAND; spellAbilityType = SpellAbilityType.BASE_ALTERNATE; @@ -39,8 +38,7 @@ public class EmergeAbility extends SpellAbility { this.getManaCostsToPay().clear(); this.addManaCost(emergeCost.copy()); - this.setRuleAtTheTop(true); - this.emergeCost = emergeCost.copy(); + this.setRuleAtTheTop(true); } public EmergeAbility(final EmergeAbility ability) { @@ -57,7 +55,7 @@ public class EmergeAbility extends SpellAbility { new FilterControlledCreaturePermanent(), this.getControllerId(), this.getSourceId(), game)) { ManaCost costToPay = CardUtil.reduceCost(emergeCost.copy(), creature.getConvertedManaCost()); if (costToPay.canPay(this, this.getSourceId(), this.getControllerId(), game)) { - return ActivationStatus.getTrue(); + return ActivationStatus.getTrue(this, game); } } } @@ -92,11 +90,16 @@ public class EmergeAbility extends SpellAbility { TargetPermanent target = new TargetControlledCreaturePermanent(new FilterControlledCreaturePermanent("creature to sacrifice for emerge")); if (controller.choose(Outcome.Sacrifice, target, this.getSourceId(), game)) { Permanent creature = game.getPermanent(target.getFirstTarget()); - CardUtil.reduceCost(this, creature.getConvertedManaCost()); - FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(creature.getLogName()); - filter.add(new CardIdPredicate(creature.getId())); - this.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(filter))); - return super.activate(game, false); + if (creature != null) { + CardUtil.reduceCost(this, creature.getConvertedManaCost()); + if (super.activate(game, false)) { + if (creature.sacrifice(getSourceId(), game)) { + return true; + } else { + activated = false; + } + } + } } } return false; diff --git a/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java b/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java index 6b4b95043e..d2ebcbad1c 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.keyword; import mage.abilities.Ability; @@ -83,7 +82,7 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM if (!(ability instanceof SpellAbility)) { return; } - Player player = game.getPlayer(controllerId); + Player player = game.getPlayer(ability.getControllerId()); if (player == null) { return; } @@ -105,7 +104,7 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM @Override public void addOptionalAdditionalModeCosts(Ability ability, Game game) { if (additionalCost.isActivated()) { - for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext(); ) { + for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) { Cost cost = (Cost) it.next(); if (cost instanceof ManaCostsImpl) { ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); diff --git a/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java b/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java index ccb08800bb..aad2d3ae72 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java @@ -14,7 +14,7 @@ import mage.target.common.TargetControlledCreaturePermanent; * @author BetaSteward_at_googlemail.com */ public class EquipAbility extends ActivatedAbilityImpl { - + public EquipAbility(int cost) { this(Outcome.AddAbility, new GenericManaCost(cost)); } @@ -26,7 +26,7 @@ public class EquipAbility extends ActivatedAbilityImpl { public EquipAbility(Outcome outcome, Cost cost, Target target) { super(Zone.BATTLEFIELD, new EquipEffect(outcome), cost); this.addTarget(target); - this.timing = TimingRule.SORCERY; + this.timing = TimingRule.SORCERY; } public EquipAbility(final EquipAbility ability) { @@ -40,7 +40,13 @@ public class EquipAbility extends ActivatedAbilityImpl { @Override public String getRule() { - return "Equip " + costs.getText() + manaCosts.getText() + " (" + manaCosts.getText() + ": Attach to target creature you control. Equip only as a sorcery.)"; + String targetText = getTargets().get(0) != null ? getTargets().get(0).getFilter().getMessage() : "creature"; + + return "Equip " + + (targetText.equals("creature you control") ? "" : targetText + " ") + + costs.getText() + + manaCosts.getText() + + " (" + manaCosts.getText() + ": Attach to target " + targetText + " you control. Equip only as a sorcery. This card enters the battlefield unattached and stays on the battlefield if the creature leaves.)"; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/EquipFilterAbility.java b/Mage/src/main/java/mage/abilities/keyword/EquipFilterAbility.java deleted file mode 100644 index 9941068604..0000000000 --- a/Mage/src/main/java/mage/abilities/keyword/EquipFilterAbility.java +++ /dev/null @@ -1,40 +0,0 @@ -package mage.abilities.keyword; - -import mage.abilities.ActivatedAbilityImpl; -import mage.abilities.costs.Cost; -import mage.abilities.effects.common.AttachEffect; -import mage.constants.Outcome; -import mage.constants.TimingRule; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.target.common.TargetControlledCreaturePermanent; - -/** - * @author TheElk801 - */ -public class EquipFilterAbility extends ActivatedAbilityImpl { - - private final FilterControlledCreaturePermanent filter; - - public EquipFilterAbility(FilterControlledCreaturePermanent filter, Cost cost) { - super(Zone.BATTLEFIELD, new AttachEffect(Outcome.AddAbility, "Equip"), cost); - this.addTarget(new TargetControlledCreaturePermanent(filter)); - this.filter = filter; - this.timing = TimingRule.SORCERY; - } - - private EquipFilterAbility(final EquipFilterAbility ability) { - super(ability); - this.filter = ability.filter; - } - - @Override - public EquipFilterAbility copy() { - return new EquipFilterAbility(this); - } - - @Override - public String getRule() { - return "Equip " + filter.getMessage() + costs.getText() + manaCosts.getText(); - } -} diff --git a/Mage/src/main/java/mage/abilities/keyword/EscalateAbility.java b/Mage/src/main/java/mage/abilities/keyword/EscalateAbility.java index 6d8317b751..4fba5ecc28 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EscalateAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EscalateAbility.java @@ -1,72 +1,72 @@ - -package mage.abilities.keyword; - -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.Cost; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; - -/** - * - * @author emerald000 - */ -public class EscalateAbility extends SimpleStaticAbility { - - public EscalateAbility(Cost cost) { - super(Zone.ALL, new EscalateEffect(cost)); - this.setRuleAtTheTop(true); - } - - public EscalateAbility(final EscalateAbility ability) { - super(ability); - } - - @Override - public EscalateAbility copy() { - return new EscalateAbility(this); - } -} - -class EscalateEffect extends CostModificationEffectImpl { - - private final Cost cost; - - EscalateEffect(Cost cost) { - super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST); - this.cost = cost; - this.staticText = "Escalate " + cost.getText() + " (Pay this cost for each mode chosen beyond the first.)"; - } - - EscalateEffect(final EscalateEffect effect) { - super(effect); - this.cost = effect.cost; - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - int numCosts = abilityToModify.getModes().getSelectedModes().size() - 1; - for (int i = 0; i < numCosts; i++) { - abilityToModify.addCost(cost.copy()); - } - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) { - return abilityToModify.getModes().getSelectedModes().size() > 1; - } - return false; - } - - @Override - public EscalateEffect copy() { - return new EscalateEffect(this); - } -} + +package mage.abilities.keyword; + +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; + +/** + * + * @author emerald000 + */ +public class EscalateAbility extends SimpleStaticAbility { + + public EscalateAbility(Cost cost) { + super(Zone.ALL, new EscalateEffect(cost)); + this.setRuleAtTheTop(true); + } + + public EscalateAbility(final EscalateAbility ability) { + super(ability); + } + + @Override + public EscalateAbility copy() { + return new EscalateAbility(this); + } +} + +class EscalateEffect extends CostModificationEffectImpl { + + private final Cost cost; + + EscalateEffect(Cost cost) { + super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST); + this.cost = cost; + this.staticText = "Escalate " + cost.getText() + " (Pay this cost for each mode chosen beyond the first.)"; + } + + EscalateEffect(final EscalateEffect effect) { + super(effect); + this.cost = effect.cost; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + int numCosts = abilityToModify.getModes().getSelectedModes().size() - 1; + for (int i = 0; i < numCosts; i++) { + abilityToModify.addCost(cost.copy()); + } + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) { + return abilityToModify.getModes().getSelectedModes().size() > 1; + } + return false; + } + + @Override + public EscalateEffect copy() { + return new EscalateEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/FabricateAbility.java b/Mage/src/main/java/mage/abilities/keyword/FabricateAbility.java index e767d1ca96..14584854cf 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FabricateAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FabricateAbility.java @@ -1,79 +1,79 @@ - -package mage.abilities.keyword; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.constants.Outcome; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.token.ServoToken; -import mage.players.Player; -import mage.util.CardUtil; - -/** - * @author emerald000 - */ -public class FabricateAbility extends EntersBattlefieldTriggeredAbility { - - public FabricateAbility(int value) { - super(new FabricateEffect(value), false, true); - } - - public FabricateAbility(final FabricateAbility ability) { - super(ability); - } - - @Override - public FabricateAbility copy() { - return new FabricateAbility(this); - } -} - -class FabricateEffect extends OneShotEffect { - - private final int value; - - FabricateEffect(int value) { - super(Outcome.Benefit); - this.value = value; - this.staticText = "Fabricate " + value - + " (When this creature enters the battlefield, put " + CardUtil.numberToText(value, "a") + " +1/+1 counter" + (value > 1 ? "s" : "") - + " on it or create " + CardUtil.numberToText(value, "a") + " 1/1 colorless Servo artifact creature token" + (value > 1 ? "s" : "") + ".)"; - } - - FabricateEffect(final FabricateEffect effect) { - super(effect); - this.value = effect.value; - } - - @Override - public FabricateEffect copy() { - return new FabricateEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - MageObject sourceObject = source.getSourceObjectIfItStillExists(game); - if (sourceObject != null && controller.chooseUse( - Outcome.BoostCreature, - "Fabricate " + value, - null, - "Put " + CardUtil.numberToText(value, "a") + " +1/+1 counter" + (value > 1 ? "s" : ""), - "Create " + CardUtil.numberToText(value, "a") + " 1/1 token" + (value > 1 ? "s" : ""), - source, - game)) { - ((Card) sourceObject).addCounters(CounterType.P1P1.createInstance(value), source, game); - } - else { - new ServoToken().putOntoBattlefield(value, game, source.getSourceId(), controller.getId()); - } - return true; - } - return false; - } -} + +package mage.abilities.keyword; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.token.ServoToken; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * @author emerald000 + */ +public class FabricateAbility extends EntersBattlefieldTriggeredAbility { + + public FabricateAbility(int value) { + super(new FabricateEffect(value), false, true); + } + + public FabricateAbility(final FabricateAbility ability) { + super(ability); + } + + @Override + public FabricateAbility copy() { + return new FabricateAbility(this); + } +} + +class FabricateEffect extends OneShotEffect { + + private final int value; + + FabricateEffect(int value) { + super(Outcome.Benefit); + this.value = value; + this.staticText = "Fabricate " + value + + " (When this creature enters the battlefield, put " + CardUtil.numberToText(value, "a") + " +1/+1 counter" + (value > 1 ? "s" : "") + + " on it or create " + CardUtil.numberToText(value, "a") + " 1/1 colorless Servo artifact creature token" + (value > 1 ? "s" : "") + ".)"; + } + + FabricateEffect(final FabricateEffect effect) { + super(effect); + this.value = effect.value; + } + + @Override + public FabricateEffect copy() { + return new FabricateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (sourceObject != null && controller.chooseUse( + Outcome.BoostCreature, + "Fabricate " + value, + null, + "Put " + CardUtil.numberToText(value, "a") + " +1/+1 counter" + (value > 1 ? "s" : ""), + "Create " + CardUtil.numberToText(value, "a") + " 1/1 token" + (value > 1 ? "s" : ""), + source, + game)) { + ((Card) sourceObject).addCounters(CounterType.P1P1.createInstance(value), source, game); + } + else { + new ServoToken().putOntoBattlefield(value, game, source.getSourceId(), controller.getId()); + } + return true; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java index 145257ae4b..91143c8978 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java @@ -1,6 +1,5 @@ package mage.abilities.keyword; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.costs.Cost; @@ -9,21 +8,19 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; import mage.cards.SplitCard; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SpellAbilityCastMode; -import mage.constants.SpellAbilityType; -import mage.constants.TimingRule; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; /** * 702.32. Flashback - * + *

* 702.32a. Flashback appears on some instants and sorceries. It represents two * static abilities: one that functions while the card is in a player‘s * graveyard and the other that functions while the card is on the stack. @@ -57,6 +54,7 @@ public class FlashbackAbility extends SpellAbility { @Override public ActivationStatus canActivate(UUID playerId, Game game) { + // flashback ability dynamicly added to all card's parts (split cards) if (super.canActivate(playerId, game).canActivate()) { Card card = game.getCard(getSourceId()); if (card != null) { @@ -69,6 +67,7 @@ public class FlashbackAbility extends SpellAbility { return ActivationStatus.getFalse(); } // Flashback can never cast a split card by Fuse, because Fuse only works from hand + // https://tappedout.net/mtg-questions/snapcaster-mage-and-flashback-on-a-fuse-card-one-or-both-halves-legal-targets/ if (card.isSplitCard()) { if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) { return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game); @@ -213,14 +212,13 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getSourceId()) + UUID cardId = CardUtil.getMainCardId(game, source.getSourceId()); // for split cards + if (cardId.equals(event.getTargetId()) && ((ZoneChangeEvent) event).getFromZone() == Zone.STACK && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) { - int zcc = game.getState().getZoneChangeCounter(source.getSourceId()); - if (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == zcc) { - return true; - } + int zcc = game.getState().getZoneChangeCounter(cardId); + return ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == zcc; } return false; diff --git a/Mage/src/main/java/mage/abilities/keyword/GravestormAbility.java b/Mage/src/main/java/mage/abilities/keyword/GravestormAbility.java index ab4823303e..16f3ef71e4 100644 --- a/Mage/src/main/java/mage/abilities/keyword/GravestormAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/GravestormAbility.java @@ -16,7 +16,6 @@ import mage.game.stack.StackObject; import mage.watchers.common.GravestormWatcher; /** - * * @author emerald000 */ public class GravestormAbility extends TriggeredAbilityImpl { @@ -75,7 +74,7 @@ class GravestormEffect extends OneShotEffect { MageObjectReference spellRef = (MageObjectReference) this.getValue("GravestormSpellRef"); if (spellRef != null) { GravestormWatcher watcher = game.getState().getWatcher(GravestormWatcher.class); - if(watcher != null) { + if (watcher != null) { int gravestormCount = watcher.getGravestormCount(); if (gravestormCount > 0) { Spell spell = (Spell) this.getValue("GravestormSpell"); @@ -83,9 +82,7 @@ class GravestormEffect extends OneShotEffect { if (!game.isSimulation()) { game.informPlayers("Gravestorm: " + spell.getName() + " will be copied " + gravestormCount + " time" + (gravestormCount > 1 ? "s" : "")); } - for (int i = 0; i < gravestormCount; i++) { - spell.createCopyOnStack(game, source, source.getControllerId(), true); - } + spell.createCopyOnStack(game, source, source.getControllerId(), true, gravestormCount); } } return true; diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofAbility.java index 1395823500..eb42f64c80 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofAbility.java @@ -1,9 +1,9 @@ package mage.abilities.keyword; +import mage.MageObject; +import mage.game.Game; + import java.io.ObjectStreamException; -import mage.abilities.MageSingleton; -import mage.abilities.common.SimpleStaticAbility; -import mage.constants.Zone; /** * Hexproof (This creature or player can't be the target of spells or abilities @@ -11,7 +11,7 @@ import mage.constants.Zone; * * @author loki */ -public class HexproofAbility extends SimpleStaticAbility implements MageSingleton { +public class HexproofAbility extends HexproofBaseAbility { private static final HexproofAbility instance; @@ -28,7 +28,12 @@ public class HexproofAbility extends SimpleStaticAbility implements MageSingleto } private HexproofAbility() { - super(Zone.BATTLEFIELD, null); + super(); + } + + @Override + public boolean checkObject(MageObject source, Game game) { + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofBaseAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofBaseAbility.java new file mode 100644 index 0000000000..539ca5d513 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofBaseAbility.java @@ -0,0 +1,21 @@ +package mage.abilities.keyword; + +import mage.MageObject; +import mage.abilities.MageSingleton; +import mage.abilities.common.SimpleStaticAbility; +import mage.constants.Zone; +import mage.game.Game; + +/** + * an abstract base class for hexproof abilities + * + * @author TheElk801 + */ +public abstract class HexproofBaseAbility extends SimpleStaticAbility implements MageSingleton { + + HexproofBaseAbility() { + super(Zone.BATTLEFIELD, null); + } + + public abstract boolean checkObject(MageObject source, Game game); +} diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlackAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlackAbility.java index 39ed14932e..82f2275563 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlackAbility.java @@ -1,9 +1,9 @@ package mage.abilities.keyword; +import mage.MageObject; +import mage.game.Game; + import java.io.ObjectStreamException; -import mage.abilities.MageSingleton; -import mage.abilities.common.SimpleStaticAbility; -import mage.constants.Zone; /** * Hexproof from black (This creature or player can't be the target of black @@ -11,7 +11,7 @@ import mage.constants.Zone; * * @author igoudt */ -public class HexproofFromBlackAbility extends SimpleStaticAbility implements MageSingleton { +public class HexproofFromBlackAbility extends HexproofBaseAbility { private static final HexproofFromBlackAbility instance; @@ -28,7 +28,12 @@ public class HexproofFromBlackAbility extends SimpleStaticAbility implements Mag } private HexproofFromBlackAbility() { - super(Zone.BATTLEFIELD, null); + super(); + } + + @Override + public boolean checkObject(MageObject source, Game game) { + return source.getColor(game).isBlack(); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlueAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlueAbility.java index 4f65271fa0..ac7317f025 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlueAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlueAbility.java @@ -1,8 +1,7 @@ package mage.abilities.keyword; -import mage.abilities.MageSingleton; -import mage.abilities.common.SimpleStaticAbility; -import mage.constants.Zone; +import mage.MageObject; +import mage.game.Game; import java.io.ObjectStreamException; @@ -12,7 +11,7 @@ import java.io.ObjectStreamException; * * @author igoudt */ -public class HexproofFromBlueAbility extends SimpleStaticAbility implements MageSingleton { +public class HexproofFromBlueAbility extends HexproofBaseAbility { private static final HexproofFromBlueAbility instance; @@ -29,7 +28,12 @@ public class HexproofFromBlueAbility extends SimpleStaticAbility implements Mage } private HexproofFromBlueAbility() { - super(Zone.BATTLEFIELD, null); + super(); + } + + @Override + public boolean checkObject(MageObject source, Game game) { + return source.getColor(game).isBlue(); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java index 88fd558921..27a711692a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java @@ -1,8 +1,7 @@ package mage.abilities.keyword; -import mage.abilities.MageSingleton; -import mage.abilities.common.SimpleStaticAbility; -import mage.constants.Zone; +import mage.MageObject; +import mage.game.Game; import java.io.ObjectStreamException; @@ -12,7 +11,7 @@ import java.io.ObjectStreamException; * * @author TheElk801 */ -public class HexproofFromMonocoloredAbility extends SimpleStaticAbility implements MageSingleton { +public class HexproofFromMonocoloredAbility extends HexproofBaseAbility { private static final HexproofFromMonocoloredAbility instance; @@ -29,7 +28,12 @@ public class HexproofFromMonocoloredAbility extends SimpleStaticAbility implemen } private HexproofFromMonocoloredAbility() { - super(Zone.BATTLEFIELD, null); + super(); + } + + @Override + public boolean checkObject(MageObject source, Game game) { + return !source.getColor(game).isMulticolored() && !source.getColor(game).isColorless(); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofFromWhiteAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofFromWhiteAbility.java index fb535e1c50..ec1d60a020 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofFromWhiteAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofFromWhiteAbility.java @@ -1,9 +1,9 @@ package mage.abilities.keyword; +import mage.MageObject; +import mage.game.Game; + import java.io.ObjectStreamException; -import mage.abilities.MageSingleton; -import mage.abilities.common.SimpleStaticAbility; -import mage.constants.Zone; /** * Hexproof from white (This creature or player can't be the target of white @@ -11,7 +11,7 @@ import mage.constants.Zone; * * @author igoudt */ -public class HexproofFromWhiteAbility extends SimpleStaticAbility implements MageSingleton { +public class HexproofFromWhiteAbility extends HexproofBaseAbility { private static final HexproofFromWhiteAbility instance; @@ -28,7 +28,12 @@ public class HexproofFromWhiteAbility extends SimpleStaticAbility implements Mag } private HexproofFromWhiteAbility() { - super(Zone.BATTLEFIELD, null); + super(); + } + + @Override + public boolean checkObject(MageObject source, Game game) { + return source.getColor(game).isWhite(); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/ImproviseAbility.java b/Mage/src/main/java/mage/abilities/keyword/ImproviseAbility.java index 7418145817..5d7d15a4e3 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ImproviseAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ImproviseAbility.java @@ -1,23 +1,21 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.keyword; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.SpecialAction; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ActivationManaAbilityStep; import mage.abilities.costs.mana.AlternateManaPaymentAbility; import mage.abilities.costs.mana.ManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.ValueHint; +import mage.abilities.mana.ManaOptions; import mage.constants.AbilityType; import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterArtifactPermanent; import mage.filter.common.FilterControlledArtifactPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; @@ -29,20 +27,36 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * @author LevelX2 + * 702.125. Improvise + *

+ * 702.125a Improvise is a static ability that functions while the spell with improvise is on the stack. “Improvise” + * means “For each generic mana in this spell’s total cost, you may tap an untapped artifact you control rather + * than pay that mana.” + *

+ * 702.125b The improvise ability isn’t an additional or alternative cost and applies only after the total cost of + * the spell with improvise is determined. + *

+ * 702.125c Multiple instances of improvise on the same spell are redundant. + * + * @author LevelX2, JayDi85 */ public class ImproviseAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility { - private static final FilterArtifactPermanent filterUntapped = new FilterArtifactPermanent(); + private static final FilterControlledArtifactPermanent filterUntapped = new FilterControlledArtifactPermanent("untapped artifact you control"); static { filterUntapped.add(Predicates.not(TappedPredicate.instance)); } + private static final DynamicValue untappedCount = new PermanentsOnBattlefieldCount(filterUntapped); + public ImproviseAbility() { - super(Zone.STACK, null); + super(Zone.ALL, null); this.setRuleAtTheTop(true); + this.addHint(new ValueHint("Untapped artifacts you control", untappedCount)); } public ImproviseAbility(final ImproviseAbility ability) { @@ -54,19 +68,29 @@ public class ImproviseAbility extends SimpleStaticAbility implements AlternateMa return new ImproviseAbility(this); } + + @Override + public String getRule() { + return "Improvise (Your artifacts can help cast this spell. Each artifact you tap after you're done activating mana abilities pays for {1}.)"; + } + + @Override + public ActivationManaAbilityStep useOnActivationManaAbilityStep() { + return ActivationManaAbilityStep.AFTER; + } + @Override public void addSpecialAction(Ability source, Game game, ManaCost unpaid) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && game.getBattlefield().contains(filterUntapped, controller.getId(), 1, game)) { + int canPayCount = untappedCount.calculate(game, source, null); + if (controller != null && canPayCount > 0) { if (source.getAbilityType() == AbilityType.SPELL && unpaid.getMana().getGeneric() > 0) { - SpecialAction specialAction = new ImproviseSpecialAction(unpaid); + SpecialAction specialAction = new ImproviseSpecialAction(unpaid, this); specialAction.setControllerId(source.getControllerId()); specialAction.setSourceId(source.getSourceId()); // create filter for possible artifacts to tap - FilterControlledArtifactPermanent filter = new FilterControlledArtifactPermanent(); - filter.add(Predicates.not(TappedPredicate.instance)); - Target target = new TargetControlledPermanent(1, unpaid.getMana().getGeneric(), filter, true); - target.setTargetName("artifact to Improvise"); + Target target = new TargetControlledPermanent(1, unpaid.getMana().getGeneric(), filterUntapped, true); + target.setTargetName("artifact to tap as Improvise's pay"); specialAction.addTarget(target); if (specialAction.canActivate(source.getControllerId(), game).canActivate()) { game.getState().getSpecialActions().add(specialAction); @@ -76,15 +100,21 @@ public class ImproviseAbility extends SimpleStaticAbility implements AlternateMa } @Override - public String getRule() { - return "Improvise (Your artifacts can help cast this spell. Each artifact you tap after you're done activating mana abilities pays for {1}.)"; + public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) { + ManaOptions options = new ManaOptions(); + Player controller = game.getPlayer(source.getControllerId()); + int canPayCount = untappedCount.calculate(game, source, null); + if (controller != null && canPayCount > 0) { + options.addMana(Mana.GenericMana(Math.min(unpaid.getMana().getGeneric(), canPayCount))); + } + return options; } } class ImproviseSpecialAction extends SpecialAction { - public ImproviseSpecialAction(ManaCost unpaid) { - super(Zone.ALL, true); + public ImproviseSpecialAction(ManaCost unpaid, AlternateManaPaymentAbility manaAbility) { + super(Zone.ALL, manaAbility); setRuleVisible(false); this.addEffect(new ImproviseEffect(unpaid)); } @@ -136,7 +166,9 @@ class ImproviseEffect extends OneShotEffect { if (!game.isSimulation()) { game.informPlayers("Improvise: " + controller.getLogName() + " taps " + perm.getLogName() + " to pay {1}"); } - spell.setDoneActivatingManaAbilities(true); + + // can't use mana abilities after that (improvise cost must be payed after mana abilities only) + spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.AFTER); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java b/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java index 5ac808f7da..3e20c1da54 100644 --- a/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java @@ -1,29 +1,29 @@ /* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. */ package mage.abilities.keyword; @@ -37,21 +37,28 @@ import mage.game.events.GameEvent.EventType; /** * Inspired ability word * - * * @author LevelX2 */ public class InspiredAbility extends TriggeredAbilityImpl { + private final boolean isInspired; + public InspiredAbility(Effect effect) { this(effect, false); } public InspiredAbility(Effect effect, boolean optional) { + this(effect, optional, true); + } + + public InspiredAbility(Effect effect, boolean optional, boolean isInspired) { super(Zone.BATTLEFIELD, effect, optional); + this.isInspired = isInspired; } public InspiredAbility(final InspiredAbility ability) { super(ability); + this.isInspired = ability.isInspired; } @Override @@ -71,6 +78,6 @@ public class InspiredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Inspired — Whenever {this} becomes untapped, " + super.getRule(); + return (isInspired ? "Inspired — " : "") + "Whenever {this} becomes untapped, " + super.getRule(); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java b/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java index 3d944ba133..c1fa3c209c 100644 --- a/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java @@ -50,8 +50,10 @@ import java.util.concurrent.ConcurrentHashMap; public class KickerAbility extends StaticAbility implements OptionalAdditionalSourceCosts { protected static final String KICKER_KEYWORD = "Kicker"; - protected static final String KICKER_REMINDER_MANA = "You may pay an additional {cost} as you cast this spell."; - protected static final String KICKER_REMINDER_COST = "You may {cost} in addition to any other costs as you cast this spell."; + protected static final String KICKER_REMINDER_MANA = "You may pay an additional " + + "{cost} as you cast this spell."; + protected static final String KICKER_REMINDER_COST = "You may {cost} in addition " + + "to any other costs as you cast this spell."; protected Map activations = new ConcurrentHashMap<>(); // zoneChangeCounter, activations @@ -93,13 +95,15 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo } public final OptionalAdditionalCost addKickerCost(String manaString) { - OptionalAdditionalCost kickerCost = new OptionalAdditionalCostImpl(keywordText, reminderText, new ManaCostsImpl(manaString)); + OptionalAdditionalCost kickerCost = new OptionalAdditionalCostImpl( + keywordText, reminderText, new ManaCostsImpl(manaString)); kickerCosts.add(kickerCost); return kickerCost; } public final OptionalAdditionalCost addKickerCost(Cost cost) { - OptionalAdditionalCost kickerCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderText, cost); + OptionalAdditionalCost kickerCost = new OptionalAdditionalCostImpl( + keywordText, "-", reminderText, cost); kickerCosts.add(kickerCost); return kickerCost; } @@ -109,9 +113,10 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo cost.reset(); } String key = getActivationKey(source, "", game); - for (Iterator iterator = activations.keySet().iterator(); iterator.hasNext(); ) { + for (Iterator iterator = activations.keySet().iterator(); iterator.hasNext();) { String activationKey = iterator.next(); - if (activationKey.startsWith(key) && activations.get(activationKey) > 0) { + if (activationKey.startsWith(key) + && activations.get(activationKey) > 0) { activations.put(key, 0); } } @@ -126,7 +131,8 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo String key = getActivationKey(source, costText, game); if (kickerCosts.size() > 1) { for (String activationKey : activations.keySet()) { - if (activationKey.startsWith(key) && activations.get(activationKey) > 0) { + if (activationKey.startsWith(key) + && activations.get(activationKey) > 0) { return true; } } @@ -150,7 +156,8 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo amount += activations.get(key); } activations.put(key, amount); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.KICKED, source.getSourceId(), source.getSourceId(), source.getControllerId())); + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.KICKED, + source.getSourceId(), source.getSourceId(), source.getControllerId())); } private String getActivationKey(Ability source, String costText, Game game) { @@ -170,7 +177,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo @Override public void addOptionalAdditionalCosts(Ability ability, Game game) { if (ability instanceof SpellAbility) { - Player player = game.getPlayer(controllerId); + Player player = game.getPlayer(ability.getControllerId()); if (player != null) { this.resetKicker(game, ability); for (OptionalAdditionalCost kickerCost : kickerCosts) { @@ -183,14 +190,17 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo } // TODO: add AI support to find max number of possible activations (from available mana) // canPay checks only single mana available, not total mana usage - if (kickerCost.canPay(ability, sourceId, controllerId, game) - && player.chooseUse(/*Outcome.Benefit*/Outcome.AIDontUseIt, "Pay " + times + kickerCost.getText(false) + " ?", ability, game)) { + if (kickerCost.canPay(ability, sourceId, ability.getControllerId(), game) + && player.chooseUse(/*Outcome.Benefit*/Outcome.AIDontUseIt, + "Pay " + times + kickerCost.getText(false) + " ?", ability, game)) { this.activateKicker(kickerCost, ability, game); if (kickerCost instanceof Costs) { - for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext(); ) { + for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext();) { Object kickerCostObject = itKickerCost.next(); - if ((kickerCostObject instanceof Costs) || (kickerCostObject instanceof CostsImpl)) { - for (@SuppressWarnings("unchecked") Iterator itDetails = ((Costs) kickerCostObject).iterator(); itDetails.hasNext(); ) { + if ((kickerCostObject instanceof Costs) + || (kickerCostObject instanceof CostsImpl)) { + for (@SuppressWarnings("unchecked") Iterator itDetails + = ((Costs) kickerCostObject).iterator(); itDetails.hasNext();) { addKickerCostsToAbility(itDetails.next(), ability, game); } } else { diff --git a/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java b/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java index 719ad2266a..91b58c68e9 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java @@ -1,6 +1,5 @@ package mage.abilities.keyword; -import java.util.UUID; import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; @@ -13,35 +12,33 @@ import mage.abilities.costs.mana.ManaCosts; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SpellAbilityCastMode; -import mage.constants.SpellAbilityType; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.stack.Spell; import mage.players.Player; +import java.util.UUID; + /** * 702.33. Madness - * + *

* 702.33a. Madness is a keyword that represents two abilities. - * + *

* The first is a static ability that functions while the card with madness is * in a player's hand. The second is a triggered ability that functions when the * first ability is applied. - * + *

* "Madness [cost]" means "If a player would discard this card, that player * discards it, but may exile it instead of putting it into their graveyard" and * "When this card is exiled this way, its owner may cast it by paying [cost] * rather than paying its mana cost. If that player doesn't, they put this * card into their graveyard. - * + *

* 702.33b. Casting a spell using its madness ability follows the rules for * paying alternative costs in rules 601.2b and 601.2e-g. - * + *

* SOI Changes: If you discard a card with madness, you exile it instead of * putting it into your graveyard. Note that the mandatory discard into exile is * a small change from previous rules. Before, you could discard a card with @@ -52,7 +49,7 @@ import mage.players.Player; */ public class MadnessAbility extends StaticAbility { - private String rule; + private final String rule; @SuppressWarnings("unchecked") public MadnessAbility(Card card, ManaCosts madnessCost) { @@ -205,26 +202,24 @@ class MadnessCastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player owner = null; Card card = game.getCard(source.getSourceId()); - if (card != null) { - owner = game.getPlayer(card.getOwnerId()); + if (card == null) { + return false; } - if (owner != null && card != null - && owner.chooseUse(outcome, "Cast " + card.getLogName() + " by madness?", source, game)) { - - // replace with the new cost - SpellAbility castByMadness = card.getSpellAbility().copy(); - ManaCosts costRef = castByMadness.getManaCostsToPay(); - castByMadness.setSpellAbilityType(SpellAbilityType.BASE_ALTERNATE); - castByMadness.setSpellAbilityCastMode(SpellAbilityCastMode.MADNESS); - costRef.clear(); - costRef.add(madnessCost); - boolean result = owner.cast(castByMadness, game, false, new MageObjectReference(source.getSourceObject(game), game)); - return result; + Player owner = game.getPlayer(card.getOwnerId()); + if (owner == null) { + return false; } - return false; + + // replace with the new cost + SpellAbility castByMadness = card.getSpellAbility().copy(); + ManaCosts costRef = castByMadness.getManaCostsToPay(); + castByMadness.setSpellAbilityType(SpellAbilityType.BASE_ALTERNATE); + castByMadness.setSpellAbilityCastMode(SpellAbilityCastMode.MADNESS); + costRef.clear(); + costRef.add(madnessCost); + return owner.cast(castByMadness, game, false, new MageObjectReference(source.getSourceObject(game), game)); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/MeditateAbility.java b/Mage/src/main/java/mage/abilities/keyword/MeditateAbility.java index af486663ba..f40165312f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MeditateAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MeditateAbility.java @@ -1,80 +1,80 @@ - -package mage.abilities.keyword; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.ActivatedAbilityImpl; -import mage.abilities.costs.Cost; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.constants.TimingRule; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * - * @author Styxo - */ -public class MeditateAbility extends ActivatedAbilityImpl { - - public MeditateAbility(Cost cost) { - super(Zone.BATTLEFIELD, new ReturnToHandEffect(), cost); - this.timing = TimingRule.SORCERY; - } - - public MeditateAbility(final MeditateAbility ability) { - super(ability); - } - - @Override - public MeditateAbility copy() { - return new MeditateAbility(this); - } - - @Override - public String getRule() { - StringBuilder sb = new StringBuilder("Meditate ").append(manaCosts.getText()); - sb.append(" (Return this creature to its owner's hand. Meditate only as a sorcery.)"); - return sb.toString(); - } - -} - -class ReturnToHandEffect extends OneShotEffect { - - public ReturnToHandEffect() { - super(Outcome.ReturnToHand); - } - - public ReturnToHandEffect(final ReturnToHandEffect effect) { - super(effect); - } - - @Override - public ReturnToHandEffect copy() { - return new ReturnToHandEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - MageObject mageObject = source.getSourceObjectIfItStillExists(game); - if (mageObject != null) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - boolean ret = controller.moveCards(permanent, Zone.HAND, source, game); - if (ret) { - game.fireEvent(new GameEvent(EventType.MEDITATED, source.getSourceId(), source.getSourceId(), controller.getId())); - } - return ret; - } - } - } - return false; - } -} + +package mage.abilities.keyword; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbilityImpl; +import mage.abilities.costs.Cost; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.TimingRule; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author Styxo + */ +public class MeditateAbility extends ActivatedAbilityImpl { + + public MeditateAbility(Cost cost) { + super(Zone.BATTLEFIELD, new ReturnToHandEffect(), cost); + this.timing = TimingRule.SORCERY; + } + + public MeditateAbility(final MeditateAbility ability) { + super(ability); + } + + @Override + public MeditateAbility copy() { + return new MeditateAbility(this); + } + + @Override + public String getRule() { + StringBuilder sb = new StringBuilder("Meditate ").append(manaCosts.getText()); + sb.append(" (Return this creature to its owner's hand. Meditate only as a sorcery.)"); + return sb.toString(); + } + +} + +class ReturnToHandEffect extends OneShotEffect { + + public ReturnToHandEffect() { + super(Outcome.ReturnToHand); + } + + public ReturnToHandEffect(final ReturnToHandEffect effect) { + super(effect); + } + + @Override + public ReturnToHandEffect copy() { + return new ReturnToHandEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + MageObject mageObject = source.getSourceObjectIfItStillExists(game); + if (mageObject != null) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + boolean ret = controller.moveCards(permanent, Zone.HAND, source, game); + if (ret) { + game.fireEvent(new GameEvent(EventType.MEDITATED, source.getSourceId(), source.getSourceId(), controller.getId())); + } + return ret; + } + } + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/MeleeAbility.java b/Mage/src/main/java/mage/abilities/keyword/MeleeAbility.java index 2e4f406775..995e27004a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MeleeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MeleeAbility.java @@ -1,104 +1,104 @@ - -package mage.abilities.keyword; - -import java.util.*; - -import mage.abilities.Ability; -import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.constants.Duration; -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.watchers.Watcher; - -/** - * - * @author emerald000 - */ -public class MeleeAbility extends AttacksTriggeredAbility { - - public MeleeAbility() { - super(new BoostSourceEffect(new MeleeDynamicValue(), new MeleeDynamicValue(), Duration.EndOfTurn), false); - this.addWatcher(new MeleeWatcher()); - } - - public MeleeAbility(final MeleeAbility ability) { - super(ability); - } - - @Override - public MeleeAbility copy() { - return new MeleeAbility(this); - } - - @Override - public String getRule() { - return "Melee (Whenever this creature attacks, it gets +1/+1 until end of turn for each opponent you attacked with a creature this combat.)"; - } -} - -class MeleeWatcher extends Watcher { - - private Map> playersAttacked = new HashMap<>(0); - - public MeleeWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == EventType.BEGIN_COMBAT_STEP_PRE) { - this.playersAttacked.clear(); - } - else if (event.getType() == EventType.ATTACKER_DECLARED) { - Set attackedPlayers = this.playersAttacked.getOrDefault(event.getPlayerId(), new HashSet<>(1)); - attackedPlayers.add(event.getTargetId()); - this.playersAttacked.put(event.getPlayerId(), attackedPlayers); - } - } - - public int getNumberOfAttackedPlayers(UUID attackerId) { - if (this.playersAttacked.get(attackerId) != null) { - return this.playersAttacked.get(attackerId).size(); - } - return 0; - } -} - -class MeleeDynamicValue implements DynamicValue { - - private boolean valueChecked = false; - private int lockedInValue; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - MeleeWatcher watcher = game.getState().getWatcher(MeleeWatcher.class); - if (watcher != null) { - if (!valueChecked) { - this.lockedInValue = watcher.getNumberOfAttackedPlayers(sourceAbility.getControllerId()); - valueChecked = true; - } - return this.lockedInValue; - } - return 0; - } - - @Override - public MeleeDynamicValue copy() { - return new MeleeDynamicValue(); - } - - @Override - public String getMessage() { - return "number of opponents you attacked this combat"; - } - - @Override - public String toString() { - return "X"; - } -} + +package mage.abilities.keyword; + +import java.util.*; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.constants.Duration; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.watchers.Watcher; + +/** + * + * @author emerald000 + */ +public class MeleeAbility extends AttacksTriggeredAbility { + + public MeleeAbility() { + super(new BoostSourceEffect(new MeleeDynamicValue(), new MeleeDynamicValue(), Duration.EndOfTurn), false); + this.addWatcher(new MeleeWatcher()); + } + + public MeleeAbility(final MeleeAbility ability) { + super(ability); + } + + @Override + public MeleeAbility copy() { + return new MeleeAbility(this); + } + + @Override + public String getRule() { + return "melee (Whenever this creature attacks, it gets +1/+1 until end of turn for each opponent you attacked this combat.)"; + } +} + +class MeleeWatcher extends Watcher { + + private Map> playersAttacked = new HashMap<>(0); + + public MeleeWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == EventType.BEGIN_COMBAT_STEP_PRE) { + this.playersAttacked.clear(); + } + else if (event.getType() == EventType.ATTACKER_DECLARED) { + Set attackedPlayers = this.playersAttacked.getOrDefault(event.getPlayerId(), new HashSet<>(1)); + attackedPlayers.add(event.getTargetId()); + this.playersAttacked.put(event.getPlayerId(), attackedPlayers); + } + } + + public int getNumberOfAttackedPlayers(UUID attackerId) { + if (this.playersAttacked.get(attackerId) != null) { + return this.playersAttacked.get(attackerId).size(); + } + return 0; + } +} + +class MeleeDynamicValue implements DynamicValue { + + private boolean valueChecked = false; + private int lockedInValue; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + MeleeWatcher watcher = game.getState().getWatcher(MeleeWatcher.class); + if (watcher != null) { + if (!valueChecked) { + this.lockedInValue = watcher.getNumberOfAttackedPlayers(sourceAbility.getControllerId()); + valueChecked = true; + } + return this.lockedInValue; + } + return 0; + } + + @Override + public MeleeDynamicValue copy() { + return new MeleeDynamicValue(); + } + + @Override + public String getMessage() { + return "number of opponents you attacked this combat"; + } + + @Override + public String toString() { + return "X"; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/MiracleAbility.java b/Mage/src/main/java/mage/abilities/keyword/MiracleAbility.java index 8c5c8b1dec..99b6ce2b42 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MiracleAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MiracleAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.keyword; import mage.MageObjectReference; @@ -102,9 +101,7 @@ public class MiracleAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getSourceId().equals(getSourceId())) { // Refer to the card at the zone it is now (hand) - FixedTarget fixedTarget = new FixedTarget(event.getSourceId()); - fixedTarget.init(game, this); - getEffects().get(0).setTargetPointer(fixedTarget); + getEffects().setTargetPointer(new FixedTarget(game.getCard(event.getSourceId()), game)); return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java b/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java index 70c22a4484..f0155b945a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.StaticAbility; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.EntersBattlefieldEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -38,7 +38,7 @@ import mage.util.CardUtil; * * @author Loki, LevelX2 */ -public class ModularAbility extends DiesTriggeredAbility { +public class ModularAbility extends DiesSourceTriggeredAbility { private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("artifact creature"); diff --git a/Mage/src/main/java/mage/abilities/keyword/MonstrosityAbility.java b/Mage/src/main/java/mage/abilities/keyword/MonstrosityAbility.java index d194f74e97..56c9ad4598 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MonstrosityAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MonstrosityAbility.java @@ -7,6 +7,7 @@ import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.common.MonstrousHint; import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; @@ -54,6 +55,8 @@ public class MonstrosityAbility extends ActivatedAbilityImpl { public MonstrosityAbility(String manaString, int monstrosityValue) { super(Zone.BATTLEFIELD, new BecomeMonstrousSourceEffect(monstrosityValue),new ManaCostsImpl(manaString)); this.monstrosityValue = monstrosityValue; + + this.addHint(MonstrousHint.instance); } public MonstrosityAbility(final MonstrosityAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java b/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java index e6482cc92e..22bd6c4f03 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.keyword; import java.util.Iterator; @@ -43,7 +42,7 @@ import mage.players.Player; * mana cost. Any effects or prohibitions that would apply to casting a card * with these characteristics (and not the face-up card’s characteristics) are * applied to casting this card. These values are the copiable values of that - * object’s characteristics. (See rule 613, "Interaction of Continuous Effects," + * object's characteristics. (See rule 613, "Interaction of Continuous Effects," * and rule 706, "Copying Objects.") Put it onto the stack (as a face-down spell * with the same characteristics), and pay {3} rather than pay its mana cost. * This follows the rules for paying alternative costs. You can use morph to @@ -52,13 +51,13 @@ import mage.players.Player; * spell had. The morph effect applies to the face-down object wherever it is, * and it ends when the permanent is turned face up. # * - * 702.36c You can’t cast a card face down if it doesn’t have morph. + * 702.36c You can't cast a card face down if it doesn't have morph. * * 702.36d If you have priority, you may turn a face-down permanent you control - * face up. This is a special action; it doesn’t use the stack (see rule 115). + * face up. This is a special action; it doesn't use the stack (see rule 115). * To do this, show all players what the permanent’s morph cost would be if it * were face up, pay that cost, then turn the permanent face up. (If the - * permanent wouldn’t have a morph cost if it were face up, it can’t be turned + * permanent wouldn't have a morph cost if it were face up, it can’t be turned * face up this way.) The morph effect on it ends, and it regains its normal * characteristics. Any abilities relating to the permanent entering the * battlefield don’t trigger when it’s turned face up and don’t have any effect, @@ -73,10 +72,14 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost protected static final String ABILITY_KEYWORD = "Morph"; protected static final String ABILITY_KEYWORD_MEGA = "Megamorph"; - protected static final String REMINDER_TEXT = "(You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.)"; - protected static final String REMINDER_TEXT_MEGA = "(You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its megamorph cost and put a +1/+1 counter on it.)"; + protected static final String REMINDER_TEXT = "(You may cast this card face down as a " + + "2/2 creature for {3}. Turn it face up any time for its morph cost.)"; + protected static final String REMINDER_TEXT_MEGA = "(You may cast this card face down " + + "as a 2/2 creature for {3}. Turn it face up any time for its megamorph " + + "cost and put a +1/+1 counter on it.)"; protected String ruleText; - protected AlternativeCost2Impl alternateCosts = new AlternativeCost2Impl(ABILITY_KEYWORD, REMINDER_TEXT, new GenericManaCost(3)); + protected AlternativeCost2Impl alternateCosts = new AlternativeCost2Impl( + ABILITY_KEYWORD, REMINDER_TEXT, new GenericManaCost(3)); protected Costs morphCosts; // needed to check activation status, if card changes zone after casting it private int zoneChangeCounter = 0; @@ -126,7 +129,8 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost ruleText = sb.toString(); - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesFaceDownCreatureEffect(morphCosts, (megamorph ? FaceDownType.MEGAMORPHED : FaceDownType.MORPHED))); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesFaceDownCreatureEffect( + morphCosts, (megamorph ? FaceDownType.MEGAMORPHED : FaceDownType.MORPHED))); ability.setWorksFaceDown(true); ability.setRuleVisible(false); addSubAbility(ability); @@ -165,7 +169,8 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost @Override public boolean isActivated(Ability ability, Game game) { Card card = game.getCard(sourceId); - if (card != null && card.getZoneChangeCounter(game) <= zoneChangeCounter + 1) { + if (card != null + && card.getZoneChangeCounter(game) <= zoneChangeCounter + 1) { return alternateCosts.isActivated(game); } return false; @@ -179,14 +184,17 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost @Override public boolean askToActivateAlternativeCosts(Ability ability, Game game) { if (ability.getAbilityType() == AbilityType.SPELL) { - Player player = game.getPlayer(controllerId); + Player player = game.getPlayer(ability.getControllerId()); Spell spell = game.getStack().getSpell(ability.getId()); - if (player != null && spell != null) { + if (player != null + && spell != null) { this.resetMorph(); spell.setFaceDown(true, game); // so only the back is visible - if (alternateCosts.canPay(ability, sourceId, controllerId, game)) { - if (player.chooseUse(Outcome.Benefit, "Cast this card as a 2/2 face-down creature for " + getCosts().getText() + " ?", ability, game)) { - game.getState().setValue("MorphAbility" + ability.getSourceId(), "activated"); // Gift of Doom + if (alternateCosts.canPay(ability, sourceId, ability.getControllerId(), game)) { + if (player.chooseUse(Outcome.Benefit, "Cast this card as a 2/2 " + + "face-down creature for " + getCosts().getText() + " ?", ability, game)) { + game.getState().setValue("MorphAbility" + + ability.getSourceId(), "activated"); // Gift of Doom activateMorph(game); // change mana costs ability.getManaCostsToPay().clear(); @@ -214,11 +222,12 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost } } if (ability.getAbilityType() == AbilityType.PLAY_LAND) { - Player player = game.getPlayer(controllerId); + Player player = game.getPlayer(ability.getControllerId()); if (player != null) { this.resetMorph(); - if (alternateCosts.canPay(ability, sourceId, controllerId, game)) { - if (player.chooseUse(Outcome.Benefit, "Cast this card as a 2/2 face-down creature for " + getCosts().getText() + " ?", ability, game)) { + if (alternateCosts.canPay(ability, sourceId, ability.getControllerId(), game)) { + if (player.chooseUse(Outcome.Benefit, "Cast this card as a 2/2 " + + "face-down creature for " + getCosts().getText() + " ?", ability, game)) { activateMorph(game); // change mana costs ability.getManaCostsToPay().clear(); diff --git a/Mage/src/main/java/mage/abilities/keyword/MutateAbility.java b/Mage/src/main/java/mage/abilities/keyword/MutateAbility.java new file mode 100644 index 0000000000..83872f3ebe --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/MutateAbility.java @@ -0,0 +1,39 @@ +package mage.abilities.keyword; + +import mage.abilities.SpellAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.cards.Card; +import mage.constants.SpellAbilityType; +import mage.constants.TimingRule; + +public class MutateAbility extends SpellAbility { + + public MutateAbility(Card card, String manaString) { + super(new ManaCostsImpl(manaString), card.getName() + " using mutate"); + this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE; + this.timing = TimingRule.SORCERY; + // TODO: Implement this + } + + private MutateAbility(final MutateAbility ability) { + super(ability); + } + + @Override + public MutateAbility copy() { + return new MutateAbility(this); + } + + @Override + public String getRule(boolean all) { + return getRule(); + } + + @Override + public String getRule() { + return "Mutate " + getManaCostsToPay().getText() + " (If you cast this spell for its mutate cost, " + + "put it over or under target non-Human creature you own. " + + "They mutate into the creature on top plus all abilities from under it.)"; + } + +} diff --git a/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java b/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java index 7c92b2f447..3ea3a2b4ca 100644 --- a/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java @@ -43,7 +43,8 @@ import mage.target.common.TargetControlledPermanent; public class NinjutsuAbility extends ActivatedAbilityImpl { private final boolean commander; - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("unblocked attacker you control"); + private static final FilterControlledCreaturePermanent filter = + new FilterControlledCreaturePermanent("unblocked attacker you control"); static { filter.add(UnblockedPredicate.instance); @@ -149,7 +150,8 @@ class ReturnAttackerToHandTargetCost extends CostImpl { for (UUID targetId : targets.get(0).getTargets()) { Permanent permanent = game.getPermanent(targetId); Player controller = game.getPlayer(controllerId); - if (permanent == null || controller == null) { + if (permanent == null + || controller == null) { return false; } defendingPlayerId = game.getCombat().getDefenderId(permanent.getId()); diff --git a/Mage/src/main/java/mage/abilities/keyword/OfferingAbility.java b/Mage/src/main/java/mage/abilities/keyword/OfferingAbility.java index 9dbbdb3c03..da1c503eb6 100644 --- a/Mage/src/main/java/mage/abilities/keyword/OfferingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/OfferingAbility.java @@ -1,7 +1,6 @@ package mage.abilities.keyword; import java.util.UUID; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; @@ -15,7 +14,9 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import mage.util.GameLog; /** * 702.46. Offering # 702.46a Offering is a static ability of a card that @@ -128,7 +129,7 @@ class OfferingAsThoughEffect extends AsThoughEffectImpl { return false; } Player player = game.getPlayer(source.getControllerId()); - if (player != null && !game.inCheckPlayableState() + if (player != null && player.chooseUse(Outcome.Benefit, "Offer a " + filter.getMessage() + " to cast " + spellToCast.getName() + '?', source, game)) { Target target = new TargetControlledCreaturePermanent(1, 1, filter, true); player.chooseTarget(Outcome.Sacrifice, target, source, game); @@ -139,10 +140,14 @@ class OfferingAsThoughEffect extends AsThoughEffectImpl { Permanent offer = game.getPermanent(target.getFirstTarget()); if (offer != null) { UUID activationId = UUID.randomUUID(); - OfferingCostReductionEffect effect = new OfferingCostReductionEffect(spellToCast.getSpellAbility().getId(), new MageObjectReference(offer, game), activationId); + OfferingCostReductionEffect effect = new OfferingCostReductionEffect(activationId); + effect.setTargetPointer(new FixedTarget(offer, game)); game.addEffect(effect, source); game.getState().setValue("offering_ok_" + card.getId(), true); game.getState().setValue("offering_Id_" + card.getId(), activationId); + game.informPlayers(player.getLogName() + " announces to offer " + + offer.getLogName() + " to cast " + + GameLog.getColoredObjectName(spellToCast));// No id name to prevent to offer hand card knowledge after cancel casting return true; } } else { @@ -156,29 +161,22 @@ class OfferingAsThoughEffect extends AsThoughEffectImpl { class OfferingCostReductionEffect extends CostModificationEffectImpl { - private final UUID spellAbilityId; private final UUID activationId; - private final MageObjectReference offeredPermanent; - // private final ManaCosts manaCostsToReduce; - OfferingCostReductionEffect(UUID spellAbilityId, MageObjectReference offeredPermanent, UUID activationId) { + OfferingCostReductionEffect(UUID activationId) { super(Duration.OneUse, Outcome.Benefit, CostModificationType.REDUCE_COST); - this.spellAbilityId = spellAbilityId; - this.offeredPermanent = offeredPermanent; this.activationId = activationId; staticText = "mana costs reduction from offering"; } OfferingCostReductionEffect(OfferingCostReductionEffect effect) { super(effect); - this.spellAbilityId = effect.spellAbilityId; - this.offeredPermanent = effect.offeredPermanent; this.activationId = effect.activationId; } @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - Permanent toOffer = offeredPermanent.getPermanent(game); + Permanent toOffer = game.getPermanent(getTargetPointer().getFirst(game, source)); if (toOffer != null) { toOffer.sacrifice(source.getSourceId(), game); CardUtil.reduceCost((SpellAbility) abilityToModify, toOffer.getSpellAbility().getManaCosts()); @@ -194,15 +192,16 @@ class OfferingCostReductionEffect extends CostModificationEffectImpl { if (game.inCheckPlayableState()) { // Cost modifaction does not work correctly for checking available spells return false; } - if (abilityToModify.getId().equals(spellAbilityId) && abilityToModify instanceof SpellAbility) { + if (abilityToModify.getSourceId().equals(source.getSourceId()) + && abilityToModify instanceof SpellAbility) { Card card = game.getCard(source.getSourceId()); if (card != null) { Object object = game.getState().getValue("offering_Id_" + card.getId()); - if (object != null && object.equals(this.activationId) && offeredPermanent.getPermanent(game) != null) { + if (object != null && object.equals(this.activationId) && getTargetPointer().getFirst(game, source) != null) { return true; } } - // no or other id, this effect is no longer valid + // to target to offer, no correct activation ID this effect is no longer valid this.discard(); } return false; diff --git a/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java b/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java index 43aa4bea32..adf31aceaf 100644 --- a/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java @@ -2,7 +2,7 @@ package mage.abilities.keyword; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.constants.Outcome; @@ -13,7 +13,7 @@ import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -public class PersistAbility extends DiesTriggeredAbility { +public class PersistAbility extends DiesSourceTriggeredAbility { public PersistAbility() { super(new PersistEffect()); diff --git a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java index 35da5caded..b6af39c002 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java @@ -1,27 +1,24 @@ package mage.abilities.keyword; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.ObjectColor; import mage.abilities.StaticAbility; import mage.cards.Card; import mage.constants.Zone; -import mage.filter.Filter; -import mage.filter.FilterCard; -import mage.filter.FilterObject; -import mage.filter.FilterPermanent; -import mage.filter.FilterSpell; +import mage.filter.*; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.game.stack.StackObject; +import mage.players.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public class ProtectionAbility extends StaticAbility { @@ -87,6 +84,7 @@ public class ProtectionAbility extends StaticAbility { } return true; } + if (filter instanceof FilterSpell) { if (source instanceof Spell) { return !filter.match(source, game); @@ -99,9 +97,21 @@ public class ProtectionAbility extends StaticAbility { return true; } } + if (filter instanceof FilterObject) { return !filter.match(source, game); } + + if (filter instanceof FilterPlayer) { + Player player = null; + if (source instanceof Permanent) { + player = game.getPlayer(((Permanent) source).getControllerId()); + } else if (source instanceof Card) { + player = game.getPlayer(((Card) source).getOwnerId()); + } + return !((FilterPlayer) filter).match(player, getSourceId(), this.getControllerId(), game); + } + return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/ProwlAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProwlAbility.java index 3e3ff1ca3c..5c73a369f2 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProwlAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProwlAbility.java @@ -1,30 +1,26 @@ - package mage.abilities.keyword; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; -import mage.abilities.costs.AlternativeCost2; -import mage.abilities.costs.AlternativeCost2Impl; -import mage.abilities.costs.AlternativeSourceCosts; -import mage.abilities.costs.Cost; -import mage.abilities.costs.Costs; -import mage.abilities.costs.CostsImpl; +import mage.abilities.condition.common.ProwlCondition; +import mage.abilities.costs.*; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.hint.common.ProwlHint; import mage.cards.Card; import mage.constants.Outcome; -import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.watchers.common.ProwlWatcher; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + /** * 702.74. Prowl # - * + *

* 702.74a Prowl is a static ability that functions on the stack. "Prowl [cost]" * means "You may pay [cost] rather than pay this spell's mana cost if a player * was dealt combat damage this turn by a source that, at the time it dealt that @@ -41,13 +37,13 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost private String reminderText; public ProwlAbility(Card card, String manaString) { - super(Zone.STACK, null); - setRuleAtTheTop(true); - name = PROWL_KEYWORD; - setReminderText(card); + super(Zone.ALL, null); + this.setRuleAtTheTop(true); + this.name = PROWL_KEYWORD; + this.setReminderText(card); this.addProwlCost(manaString); - addWatcher(new ProwlWatcher()); - + this.addWatcher(new ProwlWatcher()); + this.addHint(ProwlHint.instance); } public ProwlAbility(final ProwlAbility ability) { @@ -62,7 +58,8 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost } public final AlternativeCost2 addProwlCost(String manaString) { - AlternativeCost2 prowlCost = new AlternativeCost2Impl(PROWL_KEYWORD, reminderText, new ManaCostsImpl(manaString)); + AlternativeCost2 prowlCost = new AlternativeCost2Impl(PROWL_KEYWORD, + reminderText, new ManaCostsImpl(manaString)); prowlCosts.add(prowlCost); return prowlCost; } @@ -85,30 +82,23 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost @Override public boolean isAvailable(Ability source, Game game) { - return true; + return ProwlCondition.instance.apply(game, source); } @Override public boolean askToActivateAlternativeCosts(Ability ability, Game game) { if (ability instanceof SpellAbility) { - Player player = game.getPlayer(controllerId); - ProwlWatcher prowlWatcher = game.getState().getWatcher(ProwlWatcher.class); - Card card = game.getCard(ability.getSourceId()); - if (player == null || prowlWatcher == null || card == null) { - throw new IllegalArgumentException("Params can't be null"); + Player player = game.getPlayer(ability.getControllerId()); + if (player == null) { + return false; } - boolean canProwl = false; - for (SubType subtype : card.getSubtype(game)) { - if (prowlWatcher.hasSubtypeMadeCombatDamage(ability.getControllerId(), subtype)) { - canProwl = true; - break; - } - } - if (canProwl) { + if (ProwlCondition.instance.apply(game, ability)) { this.resetProwl(); for (AlternativeCost2 prowlCost : prowlCosts) { - if (prowlCost.canPay(ability, sourceId, controllerId, game) - && player.chooseUse(Outcome.Benefit, "Cast for " + PROWL_KEYWORD + " cost " + prowlCost.getText(true) + " ?", ability, game)) { + if (prowlCost.canPay(ability, sourceId, ability.getControllerId(), game) + && player.chooseUse(Outcome.Benefit, "Cast for " + + PROWL_KEYWORD + " cost " + prowlCost.getText(true) + + " ?", ability, game)) { prowlCost.activate(); ability.getManaCostsToPay().clear(); ability.getCosts().clear(); @@ -162,8 +152,9 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost } private void setReminderText(Card card) { - reminderText = - "(You may cast this for its prowl cost if you dealt combat damage to a player this turn with a creature that shared a creature type with {this}"; + reminderText + = "(You may cast this for its prowl cost if you dealt combat damage to a " + + "player this turn with a creature that shared a creature type with {this}"; } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/RampageAbility.java b/Mage/src/main/java/mage/abilities/keyword/RampageAbility.java index 00b7bf47be..a7c95e2c1f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/RampageAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/RampageAbility.java @@ -1,7 +1,7 @@ package mage.abilities.keyword; import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -13,7 +13,7 @@ import mage.game.combat.CombatGroup; * * @author LoneFox */ -public class RampageAbility extends BecomesBlockedTriggeredAbility { +public class RampageAbility extends BecomesBlockedSourceTriggeredAbility { private final String rule; diff --git a/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java b/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java index 9bde3078e5..03b4c2042c 100644 --- a/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java @@ -1,148 +1,148 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.keyword; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; -import mage.abilities.condition.common.SourceHasCounterCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; -import mage.cards.Card; -import mage.constants.AsThoughEffectType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; - -/** - * - * @author Styxo - */ -public class RepairAbility extends DiesTriggeredAbility { - - private String ruleText; - - public RepairAbility(int count) { - super(new AddCountersSourceEffect(CounterType.REPAIR.createInstance(), StaticValue.get(count), false, true)); - addSubAbility(new RepairBeginningOfUpkeepInterveningIfTriggeredAbility()); - addSubAbility(new RepairCastFromGraveyardTriggeredAbility()); - - ruleText = "Repair " + count + " (When this creature dies, put " + count - + " repair counters on it. At the beginning of your upkeep, remove a repair counter. " - + "Whenever the last is removed, you may cast it from graveyard until end of turn.)"; - - } - - public RepairAbility(final RepairAbility ability) { - super(ability); - this.ruleText = ability.ruleText; - } - - @Override - public String getRule() { - return ruleText; - } - - @Override - public RepairAbility copy() { - return new RepairAbility(this); - } -} - -class RepairCastFromGraveyardEffect extends AsThoughEffectImpl { - - public RepairCastFromGraveyardEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "You may cast it from graveyard until end of turn"; - } - - public RepairCastFromGraveyardEffect(final RepairCastFromGraveyardEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public RepairCastFromGraveyardEffect copy() { - return new RepairCastFromGraveyardEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return source.isControlledBy(affectedControllerId); - } -} - -class RepairCastFromGraveyardTriggeredAbility extends TriggeredAbilityImpl { - - public RepairCastFromGraveyardTriggeredAbility() { - super(Zone.GRAVEYARD, new RepairCastFromGraveyardEffect()); - setRuleVisible(false); - } - - public RepairCastFromGraveyardTriggeredAbility(RepairCastFromGraveyardTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.COUNTER_REMOVED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(getSourceId())) { - Card card = game.getCard(getSourceId()); - if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD - && card.getCounters(game).getCount(CounterType.REPAIR) == 0) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever the last repair counter is removed, you may cast {this} from your graveyard until end of turn"; - } - - @Override - public RepairCastFromGraveyardTriggeredAbility copy() { - return new RepairCastFromGraveyardTriggeredAbility(this); - } -} - -class RepairBeginningOfUpkeepInterveningIfTriggeredAbility extends ConditionalInterveningIfTriggeredAbility { - - public RepairBeginningOfUpkeepInterveningIfTriggeredAbility() { - super(new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD, new RemoveCounterSourceEffect(CounterType.REPAIR.createInstance()), TargetController.YOU, false), - new SourceHasCounterCondition(CounterType.REPAIR), - "At the beginning of your upkeep, remove a repair counter from {this}"); - this.setRuleVisible(false); - - } - - public RepairBeginningOfUpkeepInterveningIfTriggeredAbility(final RepairBeginningOfUpkeepInterveningIfTriggeredAbility effect) { - super(effect); - } - - @Override - public RepairBeginningOfUpkeepInterveningIfTriggeredAbility copy() { - return new RepairBeginningOfUpkeepInterveningIfTriggeredAbility(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.keyword; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.cards.Card; +import mage.constants.AsThoughEffectType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author Styxo + */ +public class RepairAbility extends DiesSourceTriggeredAbility { + + private String ruleText; + + public RepairAbility(int count) { + super(new AddCountersSourceEffect(CounterType.REPAIR.createInstance(), StaticValue.get(count), false, true)); + addSubAbility(new RepairBeginningOfUpkeepInterveningIfTriggeredAbility()); + addSubAbility(new RepairCastFromGraveyardTriggeredAbility()); + + ruleText = "Repair " + count + " (When this creature dies, put " + count + + " repair counters on it. At the beginning of your upkeep, remove a repair counter. " + + "Whenever the last is removed, you may cast it from graveyard until end of turn.)"; + + } + + public RepairAbility(final RepairAbility ability) { + super(ability); + this.ruleText = ability.ruleText; + } + + @Override + public String getRule() { + return ruleText; + } + + @Override + public RepairAbility copy() { + return new RepairAbility(this); + } +} + +class RepairCastFromGraveyardEffect extends AsThoughEffectImpl { + + public RepairCastFromGraveyardEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); + staticText = "You may cast it from graveyard until end of turn"; + } + + public RepairCastFromGraveyardEffect(final RepairCastFromGraveyardEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public RepairCastFromGraveyardEffect copy() { + return new RepairCastFromGraveyardEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + return source.isControlledBy(affectedControllerId); + } +} + +class RepairCastFromGraveyardTriggeredAbility extends TriggeredAbilityImpl { + + public RepairCastFromGraveyardTriggeredAbility() { + super(Zone.GRAVEYARD, new RepairCastFromGraveyardEffect()); + setRuleVisible(false); + } + + public RepairCastFromGraveyardTriggeredAbility(RepairCastFromGraveyardTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTER_REMOVED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getTargetId().equals(getSourceId())) { + Card card = game.getCard(getSourceId()); + if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD + && card.getCounters(game).getCount(CounterType.REPAIR) == 0) { + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever the last repair counter is removed, you may cast {this} from your graveyard until end of turn"; + } + + @Override + public RepairCastFromGraveyardTriggeredAbility copy() { + return new RepairCastFromGraveyardTriggeredAbility(this); + } +} + +class RepairBeginningOfUpkeepInterveningIfTriggeredAbility extends ConditionalInterveningIfTriggeredAbility { + + public RepairBeginningOfUpkeepInterveningIfTriggeredAbility() { + super(new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD, new RemoveCounterSourceEffect(CounterType.REPAIR.createInstance()), TargetController.YOU, false), + new SourceHasCounterCondition(CounterType.REPAIR), + "At the beginning of your upkeep, remove a repair counter from {this}"); + this.setRuleVisible(false); + + } + + public RepairBeginningOfUpkeepInterveningIfTriggeredAbility(final RepairBeginningOfUpkeepInterveningIfTriggeredAbility effect) { + super(effect); + } + + @Override + public RepairBeginningOfUpkeepInterveningIfTriggeredAbility copy() { + return new RepairBeginningOfUpkeepInterveningIfTriggeredAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java b/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java index e4dd52dc79..22d6bfce04 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java @@ -25,12 +25,15 @@ import java.util.Iterator; public class ReplicateAbility extends StaticAbility implements OptionalAdditionalSourceCosts { private static final String keywordText = "Replicate"; - private static final String reminderTextMana = "When you cast this spell, copy it for each time you paid its replicate cost. You may choose new targets for the copies."; + private static final String reminderTextMana = "When you cast this spell, " + + "copy it for each time you paid its replicate cost." + + " You may choose new targets for the copies."; protected OptionalAdditionalCost additionalCost; public ReplicateAbility(Card card, String manaString) { super(Zone.STACK, null); - this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderTextMana, new ManaCostsImpl(manaString)); + this.additionalCost = new OptionalAdditionalCostImpl(keywordText, + reminderTextMana, new ManaCostsImpl(manaString)); this.additionalCost.setRepeatable(true); setRuleAtTheTop(true); addSubAbility(new ReplicateTriggeredAbility()); @@ -77,12 +80,12 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona @Override public void addOptionalAdditionalCosts(Ability ability, Game game) { if (ability instanceof SpellAbility) { - Player player = game.getPlayer(controllerId); + Player player = game.getPlayer(ability.getControllerId()); if (player != null) { this.resetReplicate(); - boolean again = true; - while (player.canRespond() && again) { + while (player.canRespond() + && again) { String times = ""; if (additionalCost.isRepeatable()) { int numActivations = additionalCost.getActivateCount(); @@ -91,10 +94,12 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona // TODO: add AI support to find max number of possible activations (from available mana) // canPay checks only single mana available, not total mana usage - if (additionalCost.canPay(ability, sourceId, controllerId, game) - && player.chooseUse(/*Outcome.Benefit*/Outcome.AIDontUseIt, new StringBuilder("Pay ").append(times).append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) { + if (additionalCost.canPay(ability, sourceId, ability.getControllerId(), game) + && player.chooseUse(/*Outcome.Benefit*/Outcome.AIDontUseIt, + new StringBuilder("Pay ").append(times).append( + additionalCost.getText(false)).append(" ?").toString(), ability, game)) { additionalCost.activate(); - for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext(); ) { + for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) { Cost cost = (Cost) it.next(); if (cost instanceof ManaCostsImpl) { ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); @@ -185,7 +190,8 @@ class ReplicateTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Replicate (When you cast this spell, copy it for each time you paid its replicate cost. You may choose new targets for the copies.)"; + return "Replicate (When you cast this spell, copy it for each time you paid " + + "its replicate cost. You may choose new targets for the copies.)"; } } @@ -205,7 +211,8 @@ class ReplicateCopyEffect extends OneShotEffect { if (controller != null) { Spell spell = (Spell) this.getValue("ReplicateSpell"); int replicateCount = (Integer) this.getValue("ReplicateCount"); - if (spell != null && replicateCount > 0) { + if (spell != null + && replicateCount > 0) { // reset replicate now so the copies don't report x times Replicate Card card = game.getCard(spell.getSourceId()); if (card != null) { @@ -218,12 +225,8 @@ class ReplicateCopyEffect extends OneShotEffect { } } // create the copies - for (int i = 0; i < replicateCount; i++) { - StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true); - if (newStackObject instanceof Spell && !game.isSimulation()) { - game.informPlayers(controller.getLogName() + ((Spell) newStackObject).getActivatedMessage(game)); - } - } + StackObject newStackObject = spell.createCopyOnStack(game, source, + source.getControllerId(), true, replicateCount); return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java b/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java index 0ccefbb914..fd5ff1e9df 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java @@ -3,7 +3,7 @@ package mage.abilities.keyword; import mage.constants.ComparisonType; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.ReturnToHandTargetEffect; @@ -27,7 +27,7 @@ import java.util.UUID; * * @author Loki, LevelX2 */ -public class SoulshiftAbility extends DiesTriggeredAbility { +public class SoulshiftAbility extends DiesSourceTriggeredAbility { private final DynamicValue amount; @@ -57,7 +57,7 @@ public class SoulshiftAbility extends DiesTriggeredAbility { } @Override - public DiesTriggeredAbility copy() { + public DiesSourceTriggeredAbility copy() { return new SoulshiftAbility(this); } diff --git a/Mage/src/main/java/mage/abilities/keyword/SpaceflightAbility.java b/Mage/src/main/java/mage/abilities/keyword/SpaceflightAbility.java index ed9223756e..19ae36997f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SpaceflightAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SpaceflightAbility.java @@ -1,78 +1,78 @@ -package mage.abilities.keyword; - -import mage.abilities.Ability; -import mage.abilities.EvasionAbility; -import mage.abilities.MageSingleton; -import mage.abilities.effects.RestrictionEffect; -import mage.constants.Duration; -import mage.game.Game; -import mage.game.permanent.Permanent; - -import java.io.ObjectStreamException; - -/** - * @author Styxo - */ -public class SpaceflightAbility extends EvasionAbility implements MageSingleton { - - private static final SpaceflightAbility instance = new SpaceflightAbility(); - - private Object readResolve() throws ObjectStreamException { - return instance; - } - - public static SpaceflightAbility getInstance() { - return instance; - } - - private SpaceflightAbility() { - this.addEffect(new SpaceFlightEffect()); - } - - @Override - public String getRule() { - return "Spaceflight (This creature can only block or be blocked by creatures with spaceflight)"; - } - - @Override - public SpaceflightAbility copy() { - return instance; - } - -} - -class SpaceFlightEffect extends RestrictionEffect implements MageSingleton { - - public SpaceFlightEffect() { - super(Duration.EndOfGame); - } - - public SpaceFlightEffect(final SpaceFlightEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return permanent.getAbilities().containsKey(SpaceflightAbility.getInstance().getId()); - } - - @Override - public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { - if (attacker == null) { - return true; - } - return attacker.getAbilities().containsKey(SpaceflightAbility.getInstance().getId()); - } - - @Override - public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { - return blocker.getAbilities().containsKey(SpaceflightAbility.getInstance().getId()) - || blocker.getAbilities().containsKey(CanBlockSpaceflightAbility.getInstance().getId()); - } - - @Override - public SpaceFlightEffect copy() { - return new SpaceFlightEffect(this); - } - -} +package mage.abilities.keyword; + +import mage.abilities.Ability; +import mage.abilities.EvasionAbility; +import mage.abilities.MageSingleton; +import mage.abilities.effects.RestrictionEffect; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.io.ObjectStreamException; + +/** + * @author Styxo + */ +public class SpaceflightAbility extends EvasionAbility implements MageSingleton { + + private static final SpaceflightAbility instance = new SpaceflightAbility(); + + private Object readResolve() throws ObjectStreamException { + return instance; + } + + public static SpaceflightAbility getInstance() { + return instance; + } + + private SpaceflightAbility() { + this.addEffect(new SpaceFlightEffect()); + } + + @Override + public String getRule() { + return "Spaceflight (This creature can only block or be blocked by creatures with spaceflight)"; + } + + @Override + public SpaceflightAbility copy() { + return instance; + } + +} + +class SpaceFlightEffect extends RestrictionEffect implements MageSingleton { + + public SpaceFlightEffect() { + super(Duration.EndOfGame); + } + + public SpaceFlightEffect(final SpaceFlightEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.getAbilities().containsKey(SpaceflightAbility.getInstance().getId()); + } + + @Override + public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { + if (attacker == null) { + return true; + } + return attacker.getAbilities().containsKey(SpaceflightAbility.getInstance().getId()); + } + + @Override + public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { + return blocker.getAbilities().containsKey(SpaceflightAbility.getInstance().getId()) + || blocker.getAbilities().containsKey(CanBlockSpaceflightAbility.getInstance().getId()); + } + + @Override + public SpaceFlightEffect copy() { + return new SpaceFlightEffect(this); + } + +} diff --git a/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java b/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java index 0bf383e545..3dbcbc2a67 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java @@ -20,7 +20,7 @@ public class SpectacleAbility extends SpellAbility { public static final String SPECTACLE_ACTIVATION_VALUE_KEY = "spectacleActivation"; - private String rule; + private final String rule; public SpectacleAbility(Card card, ManaCost spectacleCosts) { super(card.getSpellAbility()); @@ -48,7 +48,7 @@ public class SpectacleAbility extends SpellAbility { public ActivationStatus canActivate(UUID playerId, Game game) { if (OpponentsLostLifeCount.instance.calculate(game, playerId) > 0 && super.canActivate(playerId, game).canActivate()) { - return ActivationStatus.getTrue(); + return ActivationStatus.getTrue(this, game); } return ActivationStatus.getFalse(); } diff --git a/Mage/src/main/java/mage/abilities/keyword/StormAbility.java b/Mage/src/main/java/mage/abilities/keyword/StormAbility.java index 001c1add9b..d7ea81e60c 100644 --- a/Mage/src/main/java/mage/abilities/keyword/StormAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/StormAbility.java @@ -16,7 +16,6 @@ import mage.watchers.common.CastSpellLastTurnWatcher; import org.apache.log4j.Logger; /** - * * @author Plopman */ public class StormAbility extends TriggeredAbilityImpl { @@ -83,13 +82,11 @@ class StormEffect extends OneShotEffect { if (!game.isSimulation()) { game.informPlayers("Storm: " + spell.getLogName() + " will be copied " + stormCount + " time" + (stormCount > 1 ? "s" : "")); } - for (int i = 0; i < stormCount; i++) { - spell.createCopyOnStack(game, source, source.getControllerId(), true); - } + spell.createCopyOnStack(game, source, source.getControllerId(), true, stormCount); } } } else { - Logger.getLogger(StormEffect.class).fatal("CastSpellLastTurnWatcher not found. game = " +game.getGameType().toString()); + Logger.getLogger(StormEffect.class).fatal("CastSpellLastTurnWatcher not found. game = " + game.getGameType().toString()); } return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java b/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java index 4b4d591f88..63fec0c718 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java @@ -19,8 +19,12 @@ import mage.target.common.TargetCreaturePermanent; */ public class SupportAbility extends EntersBattlefieldTriggeredAbility { - public SupportAbility(Card card, int amount) { - super(new SupportEffect(card, amount, true)); + /* + * For enchantments, the text should not include the word "other". + * The otherPermanent choice removes the word "other" from rule text creation. + */ + public SupportAbility(Card card, int amount, boolean otherPermanent) { + super(new SupportEffect(card, amount, otherPermanent)); if (!card.isInstant() && !card.isSorcery()) { FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures"); if (card.isCreature()) { @@ -31,6 +35,10 @@ public class SupportAbility extends EntersBattlefieldTriggeredAbility { } } + + public SupportAbility(Card card, int amount){ this( card, amount, true); } + + public SupportAbility(final SupportAbility ability) { super(ability); } diff --git a/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java b/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java index 207bf737be..0833adf2df 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java @@ -54,7 +54,7 @@ public class SurgeAbility extends SpellAbility { if (!player.hasOpponent(playerToCheckId, game)) { if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(playerToCheckId) > 0 && super.canActivate(playerId, game).canActivate()) { - return ActivationStatus.getTrue(); + return ActivationStatus.getTrue(this, game); } } } diff --git a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java index 379d81027b..df6ca1b3ff 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java @@ -1,8 +1,5 @@ package mage.abilities.keyword; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; @@ -27,10 +24,13 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** - * * 502.59. Suspend - * + *

* 502.59a Suspend is a keyword that represents three abilities. The first is a * static ability that functions while the card with suspend is in a player's * hand. The second and third are triggered abilities that function in the @@ -42,13 +42,13 @@ import mage.target.targetpointer.FixedTarget; * card, if it's removed from the game, play it without paying its mana cost if * able. If you can't, it remains removed from the game. If you play it this way * and it's a creature, it gains haste until you lose control of it." - * + *

* 502.59b A card is "suspended" if it's in the removed-from-the-game zone, has * suspend, and has a time counter on it. - * + *

* 502.59c Playing a spell as an effect of its suspend ability follows the rules * for paying alternative costs in rules 409.1b and 409.1f-h. - * + *

* The phrase "if you could play this card from your hand" checks only for * timing restrictions and permissions. This includes both what's inherent in * the card's type (for example, if the card with suspend is a creature, it must @@ -57,38 +57,38 @@ import mage.target.targetpointer.FixedTarget; * actually follow all steps in playing the card is irrelevant. If the card is * impossible to play due to a lack of legal targets or an unpayable mana cost, * for example, it may still be removed from the game with suspend. - * + *

* Removing a card from the game with its suspend ability is not playing that * card. This action doesn't use the stack and can't be responded to. - * + *

* If a spell with suspend has targets, the targets are chosen when the spell is * played, not when it's removed from the game. - * + *

* If the first triggered ability of suspend is countered, no time counter is * removed. The ability will trigger again during its owner's next upkeep. - * + *

* When the last time counter is removed from a suspended card, the second * triggered ability of suspend will trigger. It doesn't matter why the time * counter was removed or whose effect removed it. (The _Time Spiral_ reminder * text is misleading on this point.) - * + *

* If the second triggered ability of suspend is countered, the card can't be * played. It remains in the removed-from-the-game zone without any time * counters on it for the rest of the game, and it's no longer considered * suspended. - * + *

* If the second triggered ability of suspend resolves, the card's owner must * play the spell if possible, even if that player doesn't want to. Normal * timing considerations for the spell are ignored (for example, if the * suspended card is a creature and this ability resolves during your upkeep, * you're able to play the card), but other play restrictions are not ignored. - * + *

* If the second triggered ability of suspend resolves and the suspended card * can't be played due to a lack of legal targets or a play restriction, for * example, it remains in the removed-from-the-game zone without any time * counters on it for the rest of the game, and it's no longer considered * suspended. - * + *

* As the second triggered ability of suspend resolves, if playing the suspended * card involves an additional cost, the card's owner must pay that cost if * able. If they can't, the card remains removed from the game. If the @@ -99,14 +99,12 @@ import mage.target.targetpointer.FixedTarget; * cost, then they have a choice: The player may play the spell, produce mana, * and pay the cost. Or the player may choose to play no mana abilities, thus * making the card impossible to play because the additional mana can't be paid. - * + *

* A creature played via suspend comes into play with haste. It still has haste * after the first turn it's in play as long as the same player controls it. As * soon as another player takes control of it, it loses haste. * - * * @author LevelX2 - * */ public class SuspendAbility extends SpecialAction { @@ -138,13 +136,14 @@ public class SuspendAbility extends SpecialAction { } StringBuilder sb = new StringBuilder("Suspend "); if (cost != null) { - sb.append(suspend == Integer.MAX_VALUE ? "X" : suspend).append("—").append(cost.getText()).append(suspend + sb.append(suspend == Integer.MAX_VALUE ? "X" : suspend).append("—") + .append(cost.getText()).append(suspend == Integer.MAX_VALUE ? ". X can't be 0" : ""); if (!shortRule) { sb.append(" (Rather than cast this card from your hand, pay ") .append(cost.getText()) .append(" and exile it with ") - .append((suspend == 1 ? "a time counter" : (suspend == Integer.MAX_VALUE + .append((suspend == 1 ? "a time counter" : (suspend == Integer.MAX_VALUE ? "X time counters" : suspend + " time counters"))) .append(" on it.") .append(" At the beginning of your upkeep, remove a time counter. " @@ -176,8 +175,8 @@ public class SuspendAbility extends SpecialAction { ability.setControllerId(card.getOwnerId()); game.getState().addOtherAbility(card, ability); - SuspendBeginningOfUpkeepInterveningIfTriggeredAbility ability1 = - new SuspendBeginningOfUpkeepInterveningIfTriggeredAbility(); + SuspendBeginningOfUpkeepInterveningIfTriggeredAbility ability1 + = new SuspendBeginningOfUpkeepInterveningIfTriggeredAbility(); ability1.setSourceId(card.getId()); ability1.setControllerId(card.getOwnerId()); game.getState().addOtherAbility(card, ability1); @@ -213,8 +212,8 @@ public class SuspendAbility extends SpecialAction { } MageObject object = game.getObject(sourceId); return new ActivationStatus(object.isInstant() - || object.hasAbility(FlashAbility.getInstance().getId(), game) - || null != game.getContinuousEffects().asThough(sourceId, + || object.hasAbility(FlashAbility.getInstance(), game) + || null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) || game.canPlaySorcery(playerId), null); } @@ -241,7 +240,7 @@ class SuspendExileEffect extends OneShotEffect { public SuspendExileEffect(int suspend) { super(Outcome.PutCardInPlay); - this.staticText = new StringBuilder("Suspend ").append(suspend + this.staticText = new StringBuilder("Suspend ").append(suspend == Integer.MAX_VALUE ? "X" : suspend).toString(); this.suspend = suspend; } @@ -262,14 +261,15 @@ class SuspendExileEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (card != null && controller != null) { UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); - if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source.getSourceId(), game, Zone.HAND, true)) { if (suspend == Integer.MAX_VALUE) { suspend = source.getManaCostsToPay().getX(); } card.addCounters(CounterType.TIME.createInstance(suspend), source, game); if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + " suspends (" + suspend + ") " + card.getLogName()); + game.informPlayers(controller.getLogName() + + " suspends (" + suspend + ") " + card.getLogName()); } return true; } @@ -298,18 +298,17 @@ class SuspendPlayCardAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getTargetId().equals(getSourceId())) { Card card = game.getCard(getSourceId()); - if (card != null + return card != null && game.getState().getZone(card.getId()) == Zone.EXILED - && card.getCounters(game).getCount(CounterType.TIME) == 0) { - return true; - } + && card.getCounters(game).getCount(CounterType.TIME) == 0; } return false; } @Override public String getRule() { - return "When the last time counter is removed from this card ({this}), if it's removed from the game, " + super.getRule(); + return "When the last time counter is removed from this card ({this}), " + + "if it's removed from the game, " + super.getRule(); } @Override @@ -322,7 +321,8 @@ class SuspendPlayCardEffect extends OneShotEffect { public SuspendPlayCardEffect() { super(Outcome.PlayForFree); - this.staticText = "play it without paying its mana cost if able. If you can't, it remains removed from the game"; + this.staticText = "play it without paying its mana cost if able. " + + "If you can't, it remains removed from the game"; } public SuspendPlayCardEffect(final SuspendPlayCardEffect effect) { @@ -340,8 +340,9 @@ class SuspendPlayCardEffect extends OneShotEffect { Card card = game.getCard(source.getSourceId()); if (player != null && card != null) { // remove temporary suspend ability (used e.g. for Epochrasite) + // TODO: isGainedTemporary is not set or use in other places, so it can be deleted?! List abilitiesToRemove = new ArrayList<>(); - for (Ability ability : card.getAbilities()) { + for (Ability ability : card.getAbilities(game)) { if (ability instanceof SuspendAbility) { if (((SuspendAbility) ability).isGainedTemporary()) { abilitiesToRemove.add(ability); @@ -350,12 +351,19 @@ class SuspendPlayCardEffect extends OneShotEffect { } if (!abilitiesToRemove.isEmpty()) { for (Ability ability : card.getAbilities()) { - if (ability instanceof SuspendBeginningOfUpkeepInterveningIfTriggeredAbility + if (ability instanceof SuspendBeginningOfUpkeepInterveningIfTriggeredAbility || ability instanceof SuspendPlayCardAbility) { abilitiesToRemove.add(ability); } } // remove the abilities from the card + // TODO: will not work with Adventure Cards and another auto-generated abilities list + // TODO: is it work after blink or return to hand? + /* + bug example: + Epochrasite bug: It comes out of suspend, is cast and enters the battlefield. THEN if it's returned to + its owner's hand from battlefield, the bounced Epochrasite can't be cast for the rest of the game. + */ card.getAbilities().removeAll(abilitiesToRemove); } // cast the card for free @@ -409,7 +417,7 @@ class GainHasteEffect extends ContinuousEffectImpl { } return true; } - if (game.getState().getZoneChangeCounter(((FixedTarget) getTargetPointer()).getTarget()) + if (game.getState().getZoneChangeCounter(((FixedTarget) getTargetPointer()).getTarget()) >= ((FixedTarget) getTargetPointer()).getZoneChangeCounter()) { this.discard(); } @@ -421,7 +429,7 @@ class GainHasteEffect extends ContinuousEffectImpl { class SuspendBeginningOfUpkeepInterveningIfTriggeredAbility extends ConditionalInterveningIfTriggeredAbility { public SuspendBeginningOfUpkeepInterveningIfTriggeredAbility() { - super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), + super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), TargetController.YOU, false), SuspendedCondition.instance, "At the beginning of your upkeep, if this card ({this}) is suspended, remove a time counter from it."); diff --git a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java index fb90104d2d..6e45eccd8a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java @@ -38,7 +38,7 @@ public class TransformAbility extends SimpleStaticAbility { return ""; } - public static void transform(Permanent permanent, Card sourceCard, Game game) { + public static void transform(Permanent permanent, Card sourceCard, Game game, Ability source) { if (sourceCard == null) { return; @@ -63,7 +63,9 @@ public class TransformAbility extends SimpleStaticAbility { permanent.setExpansionSetCode(sourceCard.getExpansionSetCode()); permanent.getAbilities().clear(); for (Ability ability : sourceCard.getAbilities()) { - permanent.addAbility(ability, game); + // source == null -- call from init card (e.g. own abilities) + // source != null -- from apply effect + permanent.addAbility(ability, source == null ? permanent.getId() : source.getSourceId(), game); } permanent.getPower().modifyBaseValue(sourceCard.getPower().getValue()); permanent.getToughness().modifyBaseValue(sourceCard.getToughness().getValue()); @@ -105,7 +107,7 @@ class TransformEffect extends ContinuousEffectImpl { return false; } - TransformAbility.transform(permanent, card, game); + TransformAbility.transform(permanent, card, game, source); return true; diff --git a/Mage/src/main/java/mage/abilities/keyword/UndauntedAbility.java b/Mage/src/main/java/mage/abilities/keyword/UndauntedAbility.java index 52061608d3..72f76d60f0 100644 --- a/Mage/src/main/java/mage/abilities/keyword/UndauntedAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/UndauntedAbility.java @@ -1,12 +1,8 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.keyword; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.SpellCostReductionSourceForOpponentsEffect; +import mage.abilities.dynamicvalue.common.OpponentsCount; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; import mage.constants.Zone; /** @@ -15,7 +11,7 @@ import mage.constants.Zone; public class UndauntedAbility extends SimpleStaticAbility { public UndauntedAbility() { - super(Zone.ALL, new SpellCostReductionSourceForOpponentsEffect("undaunted (This spell costs {1} less to cast for each opponent.)")); + super(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, OpponentsCount.instance)); setRuleAtTheTop(true); } @@ -28,4 +24,8 @@ public class UndauntedAbility extends SimpleStaticAbility { return new UndauntedAbility(this); } + @Override + public String getRule() { + return "undaunted (This spell costs {1} less to cast for each opponent.)"; + } } \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/keyword/UndyingAbility.java b/Mage/src/main/java/mage/abilities/keyword/UndyingAbility.java index 66df34d1fb..c5c31546ec 100644 --- a/Mage/src/main/java/mage/abilities/keyword/UndyingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/UndyingAbility.java @@ -1,7 +1,7 @@ package mage.abilities.keyword; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.constants.Outcome; @@ -15,7 +15,7 @@ import mage.game.permanent.Permanent; /** * @author Loki */ -public class UndyingAbility extends DiesTriggeredAbility { +public class UndyingAbility extends DiesSourceTriggeredAbility { public UndyingAbility() { super(new UndyingEffect()); @@ -27,7 +27,7 @@ public class UndyingAbility extends DiesTriggeredAbility { } @Override - public DiesTriggeredAbility copy() { + public DiesSourceTriggeredAbility copy() { return new UndyingAbility(this); } diff --git a/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java b/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java index 5ebda8934b..46b9a085c7 100644 --- a/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java @@ -5,15 +5,12 @@ import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.Cost; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ManaEffect; -import mage.constants.AbilityType; -import mage.constants.AsThoughEffectType; -import mage.constants.TimingRule; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; /** * @author BetaSteward_at_googlemail.com @@ -54,10 +51,24 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl && null == game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_AS_INSTANT, this, controllerId, game)) { return ActivationStatus.getFalse(); } - // check if player is in the process of playing spell costs and they are no longer allowed to use activated mana abilities (e.g. because they started to use improvise) + + // check if player is in the process of playing spell costs and they are no longer allowed to use + // activated mana abilities (e.g. because they started to use improvise or convoke) + if (!game.getStack().isEmpty()) { + StackObject stackObject = game.getStack().getFirst(); + if (stackObject instanceof Spell) { + switch (((Spell) stackObject).getCurrentActivatingManaAbilitiesStep()) { + case BEFORE: + case NORMAL: + break; + case AFTER: + return ActivationStatus.getFalse(); + } + } + } + //20091005 - 605.3a return new ActivationStatus(costs.canPay(this, sourceId, controllerId, game), null); - } /** @@ -71,7 +82,7 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl */ @Override public List getNetMana(Game game) { - if (netMana.isEmpty()) { + if (netMana.isEmpty() || (game != null && game.inCheckPlayableState())) { List dynamicNetMana = new ArrayList<>(); for (Effect effect : getEffects()) { if (effect instanceof ManaEffect) { @@ -90,6 +101,17 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl return netManaCopy; } + @Override + public Set getProducableManaTypes(Game game) { + Set manaTypes = new HashSet<>(); + for (Effect effect : getEffects()) { + if (effect instanceof ManaEffect) { + manaTypes.addAll(((ManaEffect) effect).getProducableManaTypes(game, this)); + } + } + return manaTypes; + } + /** * Used to check if the ability itself defines mana types it can produce. * @@ -104,6 +126,9 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl * Is it allowed to undo the mana creation. It's e.g. not allowed if some * game revealing information is related (like reveal the top card of the * library) + *

+ * TODO: it helps with single mana activate for mana pool, but will not work while activates on paying for casting + * (e.g. user can cheats to see next draw card) * * @return */ diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java index 3df21cd459..b57d040745 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java @@ -17,7 +17,10 @@ import mage.game.permanent.Permanent; import mage.players.Player; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import mage.constants.ManaType; /** * @author LevelX2 @@ -55,6 +58,16 @@ public class AnyColorLandsProduceManaAbility extends ActivatedManaAbilityImpl { return true; } + public static Set getManaTypesFromPermanent(Permanent permanent, Game game) { + Set allTypes = new HashSet<>(); + if (permanent != null) { + Abilities manaAbilities = permanent.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD); + for (ActivatedManaAbilityImpl ability : manaAbilities) { + allTypes.addAll(ability.getProducableManaTypes(game)); + } + } + return allTypes; + } } class AnyColorLandsProduceManaEffect extends ManaEffect { @@ -87,27 +100,30 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { @Override public List getNetMana(Game game, Ability source) { List netManas = new ArrayList<>(); - Mana types = getManaTypes(game, source); - if (types.getBlack() > 0) { - netManas.add(new Mana(ColoredManaSymbol.B)); - } - if (types.getRed() > 0) { - netManas.add(new Mana(ColoredManaSymbol.R)); - } - if (types.getBlue() > 0) { - netManas.add(new Mana(ColoredManaSymbol.U)); - } - if (types.getGreen() > 0) { - netManas.add(new Mana(ColoredManaSymbol.G)); - } - if (types.getWhite() > 0) { - netManas.add(new Mana(ColoredManaSymbol.W)); - } - if (!onlyColors && types.getColorless() > 0) { - netManas.add(Mana.ColorlessMana(1)); - } - if (types.getAny() > 0) { - netManas.add(Mana.AnyMana(1)); + if (game != null) { + Set manaTypes = getManaTypes(game, source); + if ((manaTypes.size() == 5 && !manaTypes.contains(ManaType.COLORLESS)) || manaTypes.size() == 6) { // GENERIC should never be returned from getManaTypes + netManas.add(Mana.AnyMana(1)); + } else { + if (manaTypes.contains(ManaType.BLACK)) { + netManas.add(Mana.BlackMana(1)); + } + if (manaTypes.contains(ManaType.RED)) { + netManas.add(Mana.RedMana(1)); + } + if (manaTypes.contains(ManaType.BLUE)) { + netManas.add(Mana.BlueMana(1)); + } + if (manaTypes.contains(ManaType.GREEN)) { + netManas.add(Mana.GreenMana(1)); + } + if (manaTypes.contains(ManaType.WHITE)) { + netManas.add(Mana.WhiteMana(1)); + } + } + if (!onlyColors && manaTypes.contains(ManaType.COLORLESS)) { + netManas.add(Mana.ColorlessMana(1)); + } } return netManas; } @@ -118,39 +134,28 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { if (game == null) { return mana; } - Mana types = getManaTypes(game, source); + Set types = getManaTypes(game, source); Choice choice = new ChoiceColor(true); choice.getChoices().clear(); - choice.setMessage("Pick a mana color"); - if (types.getBlack() > 0) { + choice.setMessage("Pick a mana " + (onlyColors ? "color" : "type")); + if (types.contains(ManaType.BLACK)) { choice.getChoices().add("Black"); } - if (types.getRed() > 0) { + if (types.contains(ManaType.RED)) { choice.getChoices().add("Red"); } - if (types.getBlue() > 0) { + if (types.contains(ManaType.BLUE)) { choice.getChoices().add("Blue"); } - if (types.getGreen() > 0) { + if (types.contains(ManaType.GREEN)) { choice.getChoices().add("Green"); } - if (types.getWhite() > 0) { + if (types.contains(ManaType.WHITE)) { choice.getChoices().add("White"); } - if (!onlyColors && types.getColorless() > 0) { + if (types.contains(ManaType.COLORLESS)) { choice.getChoices().add("Colorless"); } - if (types.getAny() > 0) { - choice.getChoices().add("Black"); - choice.getChoices().add("Red"); - choice.getChoices().add("Blue"); - choice.getChoices().add("Green"); - choice.getChoices().add("White"); - if (!onlyColors) { - choice.getChoices().add("Colorless"); - } - - } if (!choice.getChoices().isEmpty()) { Player player = game.getPlayer(source.getControllerId()); if (choice.getChoices().size() == 1) { @@ -186,24 +191,19 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { return mana; } - private Mana getManaTypes(Game game, Ability source) { - Mana types = new Mana(); + private Set getManaTypes(Game game, Ability source) { + Set types = new HashSet<>(); if (game == null || game.getPhase() == null) { return types; } - if (inManaTypeCalculation) { + if (inManaTypeCalculation) { // Stop endless loops return types; } inManaTypeCalculation = true; List lands = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); for (Permanent land : lands) { - Abilities mana = land.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD); - for (ActivatedManaAbilityImpl ability : mana) { - if (!ability.equals(source) && ability.definesMana(game)) { - for (Mana netMana : ability.getNetMana(game)) { - types.add(netMana); - } - } + if (!land.getId().equals(source.getSourceId())) { + types.addAll(AnyColorLandsProduceManaAbility.getManaTypesFromPermanent(land, game)); } } inManaTypeCalculation = false; @@ -214,4 +214,5 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { public AnyColorLandsProduceManaEffect copy() { return new AnyColorLandsProduceManaEffect(this); } + } diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java index e22d6b79a4..c5921d5372 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java @@ -1,10 +1,16 @@ package mage.abilities.mana; +import java.util.ArrayList; +import java.util.List; import mage.Mana; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.ManaEffect; import mage.abilities.effects.mana.AddManaOfAnyColorEffect; import mage.constants.Zone; +import mage.game.Game; public class AnyColorManaAbility extends ActivatedManaAbilityImpl { @@ -21,10 +27,39 @@ public class AnyColorManaAbility extends ActivatedManaAbilityImpl { this.netMana.add(new Mana(0, 0, 0, 0, 0, 0, 1, 0)); } + /** + * + * @param cost + * @param netAmount dynamic value used during available mana calculation to + * set the max possible amount the source can produce + * @param setFlag + */ + public AnyColorManaAbility(Cost cost, DynamicValue netAmount, boolean setFlag) { + super(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(1, netAmount, setFlag), cost); + this.netMana.add(new Mana(0, 0, 0, 0, 0, 0, 1, 0)); + } + public AnyColorManaAbility(final AnyColorManaAbility ability) { super(ability); } + @Override + public List getNetMana(Game game) { + if (game != null && game.inCheckPlayableState()) { + List dynamicNetMana = new ArrayList<>(); + for (Effect effect : getEffects()) { + if (effect instanceof ManaEffect) { + List effectNetMana = ((ManaEffect) effect).getNetMana(game, this); + if (effectNetMana != null) { + dynamicNetMana.addAll(effectNetMana); + } + } + } + return dynamicNetMana; + } + return super.getNetMana(game); + } + @Override public AnyColorManaAbility copy() { return new AnyColorManaAbility(this); diff --git a/Mage/src/main/java/mage/abilities/mana/BasicManaAbility.java b/Mage/src/main/java/mage/abilities/mana/BasicManaAbility.java index 7392ba4fad..4f6b2eb2e8 100644 --- a/Mage/src/main/java/mage/abilities/mana/BasicManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/BasicManaAbility.java @@ -1,13 +1,10 @@ - - package mage.abilities.mana; -import mage.constants.Zone; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.ManaEffect; +import mage.constants.Zone; /** - * * @author BetaSteward_at_googlemail.com */ public abstract class BasicManaAbility extends ActivatedManaAbilityImpl { @@ -19,5 +16,4 @@ public abstract class BasicManaAbility extends ActivatedManaAbilityImpl { public BasicManaAbility(BasicManaAbility ability) { super(ability); } - } diff --git a/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java index c8d9cf3ac4..e9065044d9 100644 --- a/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.mana; import java.util.List; @@ -13,8 +12,8 @@ import mage.constants.Zone; import mage.game.Game; /** - * For cards like: - * {tap}: Add three mana of any one color. Spend this mana only to cast creature spells. + * For cards like: {tap}: Add three mana of any one color. Spend this mana only + * to cast creature spells. * * @author noxx */ @@ -31,11 +30,11 @@ public class ConditionalAnyColorManaAbility extends ActivatedManaAbilityImpl { } public ConditionalAnyColorManaAbility(Cost cost, int amount, ConditionalManaBuilder manaBuilder, boolean oneChoice) { - this(cost, StaticValue.get(amount), manaBuilder, oneChoice); + this(cost, StaticValue.get(amount), StaticValue.get(amount), manaBuilder, oneChoice); } - public ConditionalAnyColorManaAbility(Cost cost, DynamicValue amount, ConditionalManaBuilder manaBuilder, boolean oneChoice) { - super(Zone.BATTLEFIELD, new AddConditionalManaOfAnyColorEffect(amount, manaBuilder, oneChoice), cost); + public ConditionalAnyColorManaAbility(Cost cost, DynamicValue amount, DynamicValue netAmount, ConditionalManaBuilder manaBuilder, boolean oneChoice) { + super(Zone.BATTLEFIELD, new AddConditionalManaOfAnyColorEffect(amount, netAmount, manaBuilder, oneChoice), cost); this.amount = amount; } @@ -47,7 +46,10 @@ public class ConditionalAnyColorManaAbility extends ActivatedManaAbilityImpl { @Override public List getNetMana(Game game) { this.netMana.clear(); - this.netMana.add(new Mana(0,0,0,0,0,0, amount.calculate(game, this, null), 0)); + int count = amount.calculate(game, this, null); + if (count > 0) { + this.netMana.add(Mana.AnyMana(count)); + } return super.getNetMana(game); } @@ -56,7 +58,6 @@ public class ConditionalAnyColorManaAbility extends ActivatedManaAbilityImpl { return true; } - @Override public ConditionalAnyColorManaAbility copy() { return new ConditionalAnyColorManaAbility(this); diff --git a/Mage/src/main/java/mage/abilities/mana/DynamicManaAbility.java b/Mage/src/main/java/mage/abilities/mana/DynamicManaAbility.java index 087a1d23c6..e880c07101 100644 --- a/Mage/src/main/java/mage/abilities/mana/DynamicManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/DynamicManaAbility.java @@ -79,17 +79,6 @@ public class DynamicManaAbility extends ActivatedManaAbilityImpl { return new DynamicManaAbility(this); } - @Override - public List getNetMana(Game game) { - List newNetMana = new ArrayList<>(); - if (game != null) { - // TODO: effects from replacement effects like Mana Reflection are not considered yet - // TODO: effects that need a X payment (e.g. Mage-Ring Network) return always 0 - newNetMana.addAll(manaEffect.getNetMana(game, this)); - } - return newNetMana; - } - @Override public boolean definesMana(Game game) { return true; diff --git a/Mage/src/main/java/mage/abilities/mana/ManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ManaAbility.java index d4d0de12ff..2db974fa80 100644 --- a/Mage/src/main/java/mage/abilities/mana/ManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ManaAbility.java @@ -6,7 +6,9 @@ package mage.abilities.mana; import java.util.List; +import java.util.Set; import mage.Mana; +import mage.constants.ManaType; import mage.game.Game; /** @@ -24,6 +26,18 @@ public interface ManaAbility { */ List getNetMana(Game game); + /** + * The type of mana a permanent "could produce" is the type of mana that any + * ability of that permanent can generate, taking into account any + * applicable replacement effects. If the type of mana can’t be defined, + * there’s no type of mana that that permanent could produce. The "type" of + * mana is its color, or lack thereof (for colorless mana). + * + * @param game + * @return + */ + Set getProducableManaTypes(Game game); + /** * Used to check if the ability itself defines mana types it can produce. * diff --git a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java index bfd32e4747..fc8a7152cf 100644 --- a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java +++ b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java @@ -1,26 +1,27 @@ package mage.abilities.mana; +import mage.Mana; +import mage.abilities.Ability; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ManaEvent; +import mage.players.Player; +import org.apache.log4j.Logger; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import mage.Mana; -import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.TapSourceCost; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ManaEvent; -import org.apache.log4j.Logger; /** - * * @author BetaSteward_at_googlemail.com - * + *

* this class is used to build a list of all possible mana combinations it can * be used to find all the ways to pay a mana cost or all the different mana * combinations available to a player - * + *

+ * TODO: Conditional Mana is not supported yet. The mana adding removes the + * condition of conditional mana */ public class ManaOptions extends ArrayList { @@ -44,23 +45,11 @@ public class ManaOptions extends ArrayList { //if there is only one mana option available add it to all the existing options List netManas = abilities.get(0).getNetMana(game); if (netManas.size() == 1) { - if (!hasTapCost(abilities.get(0)) || checkTappedForManaReplacement(abilities.get(0), game, netManas.get(0))) { - addMana(netManas.get(0)); - } - } else { - List copy = copy(); - this.clear(); - boolean hasTapCost = hasTapCost(abilities.get(0)); - for (Mana netMana : netManas) { - for (Mana mana : copy) { - if (!hasTapCost /* || checkTappedForManaReplacement(abilities.get(0), game, netMana) */) { // Seems to produce endless iterations so deactivated for now: https://github.com/magefree/mage/issues/5023 - Mana newMana = new Mana(); - newMana.add(mana); - newMana.add(netMana); - this.add(newMana); - } - } - } + checkManaReplacementAndTriggeredMana(abilities.get(0), game, netManas.get(0)); + addMana(netManas.get(0)); + addTriggeredMana(game, abilities.get(0)); + } else if (netManas.size() > 1) { + addManaVariation(netManas, abilities.get(0), game); } } else { // mana source has more than 1 ability @@ -68,14 +57,14 @@ public class ManaOptions extends ArrayList { List copy = copy(); this.clear(); for (ActivatedManaAbilityImpl ability : abilities) { - boolean hasTapCost = hasTapCost(ability); for (Mana netMana : ability.getNetMana(game)) { - if (!hasTapCost || checkTappedForManaReplacement(ability, game, netMana)) { + checkManaReplacementAndTriggeredMana(ability, game, netMana); + for (Mana triggeredManaVariation : getTriggeredManaVariations(game, ability, netMana)) { SkipAddMana: for (Mana mana : copy) { Mana newMana = new Mana(); newMana.add(mana); - newMana.add(netMana); + newMana.add(triggeredManaVariation); for (Mana existingMana : this) { if (existingMana.equalManaValue(newMana)) { continue SkipAddMana; @@ -90,99 +79,158 @@ public class ManaOptions extends ArrayList { this.add(newMana); } } + } } } } + + forceManaDeduplication(); } - private boolean checkTappedForManaReplacement(Ability ability, Game game, Mana mana) { - ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, ability.getSourceId(), ability.getSourceId(), ability.getControllerId(), mana); - if (!game.replaceEvent(event)) { - return true; - } - return false; - } - - private boolean hasTapCost(Ability ability) { - for (Cost cost : ability.getCosts()) { - if (cost instanceof TapSourceCost) { - return true; + private void addManaVariation(List netManas, ActivatedManaAbilityImpl ability, Game game) { + List copy = copy(); + this.clear(); + for (Mana netMana : netManas) { + for (Mana mana : copy) { + if (!ability.hasTapCost() || checkManaReplacementAndTriggeredMana(ability, game, netMana)) { + Mana newMana = new Mana(); + newMana.add(mana); + newMana.add(netMana); + this.add(newMana); + } } } - return false; + + forceManaDeduplication(); } - public void addManaWithCost(List abilities, Game game) { + private static List> getSimulatedTriggeredManaFromPlayer(Game game, Ability ability) { + Player player = game.getPlayer(ability.getControllerId()); + List> newList = new ArrayList<>(); + if (player != null) { + newList.addAll(player.getAvailableTriggeredMana()); + player.getAvailableTriggeredMana().clear(); + } + return newList; + } + + /** + * Generates triggered mana and checks replacement of Tapped_For_Mana event. + * Also generates triggered mana for MANA_ADDED event. + * + * @param ability + * @param game + * @param mana + * @return false if mana production was completely replaced + */ + private boolean checkManaReplacementAndTriggeredMana(Ability ability, Game game, Mana mana) { + if (ability.hasTapCost()) { + ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, ability.getSourceId(), ability.getSourceId(), ability.getControllerId(), mana); + if (game.replaceEvent(event)) { + return false; + } + game.fireEvent(event); + } + ManaEvent manaEvent = new ManaEvent(GameEvent.EventType.MANA_ADDED, ability.getSourceId(), ability.getSourceId(), ability.getControllerId(), mana); + manaEvent.setData(mana.toString()); + game.fireEvent(manaEvent); + return true; + } + + /** + * This adds the mana the abilities can produce to the possible mana + * variabtion. + * + * @param abilities + * @param game + * @return false if the costs could not be paid + */ + public boolean addManaWithCost(List abilities, Game game) { + boolean wasUsable = false; int replaces = 0; if (isEmpty()) { - this.add(new Mana()); + this.add(new Mana()); // needed if this is the first available mana, otherwise looping over existing options woold not loop } if (!abilities.isEmpty()) { if (abilities.size() == 1) { - //if there is only one mana option available add it to all the existing options - ActivatedManaAbilityImpl ability = abilities.get(0); List netManas = abilities.get(0).getNetMana(game); - // no mana costs - if (ability.getManaCosts().isEmpty()) { - if (netManas.size() == 1) { - addMana(netManas.get(0)); - } else { + if (netManas.size() > 0) { // ability can produce mana + ActivatedManaAbilityImpl ability = abilities.get(0); + // The ability has no mana costs + if (ability.getManaCosts().isEmpty()) { // No mana costs, so no mana to subtract from available + if (netManas.size() == 1) { + checkManaReplacementAndTriggeredMana(ability, game, netManas.get(0)); + addMana(netManas.get(0)); + addTriggeredMana(game, ability); + } else { + List copy = copy(); + this.clear(); + for (Mana netMana : netManas) { + checkManaReplacementAndTriggeredMana(ability, game, netMana); + for (Mana triggeredManaVariation : getTriggeredManaVariations(game, ability, netMana)) { + for (Mana mana : copy) { + Mana newMana = new Mana(); + newMana.add(mana); + newMana.add(triggeredManaVariation); + this.add(newMana); + } + } + } + } + } else {// The ability has mana costs List copy = copy(); this.clear(); for (Mana netMana : netManas) { - for (Mana mana : copy) { - Mana newMana = new Mana(); - newMana.add(mana); - newMana.add(netMana); - this.add(newMana); + checkManaReplacementAndTriggeredMana(ability, game, netMana); + for (Mana triggeredManaVariation : getTriggeredManaVariations(game, ability, netMana)) { + for (Mana prevMana : copy) { + Mana startingMana = prevMana.copy(); + Mana manaCosts = ability.getManaCosts().getMana(); + if (startingMana.includesMana(manaCosts)) { // can pay the mana costs to use the ability + if (!subtractCostAddMana(manaCosts, triggeredManaVariation, ability.getCosts().isEmpty(), startingMana)) { + // the starting mana includes mana parts that the increased mana does not include, so add starting mana also as an option + add(prevMana); + } + wasUsable = true; + } else { + // mana costs can't be paid so keep starting mana + add(prevMana); + } + } } } } - } else // the ability has mana costs - if (netManas.size() == 1) { - subtractCostAddMana(ability.getManaCosts().getMana(), netManas.get(0), ability.getCosts().isEmpty()); - } else { - List copy = copy(); - this.clear(); - for (Mana netMana : netManas) { - for (Mana mana : copy) { - Mana newMana = new Mana(); - newMana.add(mana); - newMana.add(netMana); - subtractCostAddMana(ability.getManaCosts().getMana(), netMana, ability.getCosts().isEmpty()); - } - } } } else { //perform a union of all existing options and the new options List copy = copy(); this.clear(); for (ActivatedManaAbilityImpl ability : abilities) { - boolean hasTapCost = hasTapCost(ability); List netManas = ability.getNetMana(game); - if (ability.getManaCosts().isEmpty()) { for (Mana netMana : netManas) { - if (!hasTapCost || checkTappedForManaReplacement(ability, game, netMana)) { + checkManaReplacementAndTriggeredMana(ability, game, netMana); + for (Mana triggeredManaVariation : getTriggeredManaVariations(game, ability, netMana)) { for (Mana mana : copy) { Mana newMana = new Mana(); newMana.add(mana); - newMana.add(netMana); + newMana.add(triggeredManaVariation); this.add(newMana); } } } } else { for (Mana netMana : netManas) { - if (!hasTapCost || checkTappedForManaReplacement(ability, game, netMana)) { + checkManaReplacementAndTriggeredMana(ability, game, netMana); + for (Mana triggeredManaVariation : getTriggeredManaVariations(game, ability, netMana)) { for (Mana previousMana : copy) { CombineWithExisting: for (Mana manaOption : ability.getManaCosts().getManaOptions()) { Mana newMana = new Mana(previousMana); if (previousMana.includesMana(manaOption)) { // costs can be paid newMana.subtractCost(manaOption); - newMana.add(netMana); + newMana.add(triggeredManaVariation); // if the new mana is in all colors more than another already existing than replace for (Mana existingMana : this) { Mana moreValuable = Mana.getMoreValuableMana(newMana, existingMana); @@ -204,10 +252,62 @@ public class ManaOptions extends ArrayList { } } } + if (this.size() > 30 || replaces > 30) { logger.trace("ManaOptionsCosts " + this.size() + " Ign:" + replaces + " => " + this.toString()); logger.trace("Abilities: " + abilities.toString()); } + forceManaDeduplication(); + + return wasUsable; + } + + public static List getTriggeredManaVariations(Game game, Ability ability, Mana baseMana) { + List baseManaPlusTriggeredMana = new ArrayList<>(); + baseManaPlusTriggeredMana.add(baseMana); + List> availableTriggeredManaList = ManaOptions.getSimulatedTriggeredManaFromPlayer(game, ability); + for (List availableTriggeredMana : availableTriggeredManaList) { + if (availableTriggeredMana.size() == 1) { + for (Mana prevMana : baseManaPlusTriggeredMana) { + prevMana.add(availableTriggeredMana.get(0)); + } + } else if (availableTriggeredMana.size() > 1) { + List copy = new ArrayList<>(baseManaPlusTriggeredMana); + baseManaPlusTriggeredMana.clear(); + for (Mana triggeredMana : availableTriggeredMana) { + for (Mana prevMana : copy) { + Mana newMana = new Mana(); + newMana.add(prevMana); + newMana.add(triggeredMana); + baseManaPlusTriggeredMana.add(newMana); + } + } + } + } + return baseManaPlusTriggeredMana; + } + + private void addTriggeredMana(Game game, Ability ability) { + List> netManaList = getSimulatedTriggeredManaFromPlayer(game, ability); + for (List triggeredNetMana : netManaList) { + if (triggeredNetMana.size() == 1) { + addMana(triggeredNetMana.get(0)); + } else if (triggeredNetMana.size() > 1) { + // Add variations + List copy = copy(); + this.clear(); + for (Mana triggeredMana : triggeredNetMana) { + for (Mana mana : copy) { + Mana newMana = new Mana(); + newMana.add(mana); + newMana.add(triggeredMana); + this.add(newMana); + } + } + } + } + + forceManaDeduplication(); } public void addMana(Mana addMana) { @@ -217,6 +317,8 @@ public class ManaOptions extends ArrayList { for (Mana mana : this) { mana.add(addMana); } + + forceManaDeduplication(); } public void addMana(ManaOptions options) { @@ -241,67 +343,102 @@ public class ManaOptions extends ArrayList { } } } + + forceManaDeduplication(); + } + + private void forceManaDeduplication() { + // memory overflow protection - force de-duplication on too much mana sources + // bug example: https://github.com/magefree/mage/issues/6938 + // use it after new mana adding + if (this.size() > 1000) { + this.removeDuplicated(); + } } public ManaOptions copy() { return new ManaOptions(this); } - public void subtractCostAddMana(Mana cost, Mana addMana, boolean onlyManaCosts) { - if (isEmpty()) { - this.add(new Mana()); - } + /** + * Performs the simulation of a mana ability with costs + * + * @param cost cost to use the ability + * @param manaToAdd one mana variation that can be added by using + * this ability + * @param onlyManaCosts flag to know if the costs are mana costs only + * @param currentMana the mana available before the usage of the + * ability + * @param oldManaWasReplaced returns the info if the new complete mana does + * replace the current mana completely + */ + private boolean subtractCostAddMana(Mana cost, Mana manaToAdd, boolean onlyManaCosts, Mana currentMana) { + boolean oldManaWasReplaced = false; // true if the newly created mana includes all mana possibilities of the old boolean repeatable = false; - if (addMana.getAny() == 1 && addMana.count() == 1 && onlyManaCosts) { + if ((manaToAdd.countColored() > 0 || manaToAdd.getAny() > 0) && manaToAdd.count() > 0 && onlyManaCosts) { // deactivated because it does cause loops TODO: Find reason repeatable = true; // only replace to any with mana costs only will be repeated if able } - List copy = copy(); - this.clear(); - for (Mana mana : copy) { - Mana oldMan = mana.copy(); - if (mana.includesMana(cost)) { // it can be paid - // generic mana costs can be paid with different colored mana, can lead to different color combinations - if (cost.getGeneric() > 0 && cost.getGeneric() > (mana.getGeneric() + mana.getColorless())) { - Mana coloredCost = cost.copy(); - coloredCost.setGeneric(0); - mana.subtract(coloredCost); - boolean oldManaWasReplaced = false; - for (Mana payCombination : getPossiblePayCombinations(cost.getGeneric(), mana)) { - Mana newMana = mana.copy(); - newMana.subtract(payCombination); - newMana.add(addMana); - Mana moreValuable = Mana.getMoreValuableMana(oldMan, newMana); - if (!oldMan.equals(moreValuable)) { - this.add(newMana); - if (moreValuable != null) { - oldManaWasReplaced = true; // the new mana includes all possibilities of the old one - } - } + Mana prevMana = currentMana.copy(); + // generic mana costs can be paid with different colored mana, can lead to different color combinations + if (cost.getGeneric() > 0 && cost.getGeneric() > (currentMana.getGeneric() + currentMana.getColorless())) { + for (Mana payCombination : ManaOptions.getPossiblePayCombinations(cost.getGeneric(), currentMana)) { + Mana currentManaCopy = currentMana.copy(); + while (currentManaCopy.includesMana(payCombination)) { // loop for multiple usage if possible + boolean newCombinations = false; - } - if (!oldManaWasReplaced) { - this.add(oldMan); - } - } else { - while (mana.includesMana(cost)) { - mana.subtractCost(cost); - mana.add(addMana); - if (!repeatable) { - break; + Mana newMana = currentManaCopy.copy(); + newMana.subtract(payCombination); + newMana.add(manaToAdd); + // Mana moreValuable = Mana.getMoreValuableMana(currentMana, newMana); + if (!isExistingManaCombination(newMana)) { + this.add(newMana); // add the new combination + newCombinations = true; // repeat the while as long there are new combinations and usage is repeatable + currentManaCopy = newMana.copy(); + Mana moreValuable = Mana.getMoreValuableMana(currentMana, newMana); + if (!oldManaWasReplaced && newMana.equals(moreValuable)) { + oldManaWasReplaced = true; // the new mana includes all possibilities of the old one, so no need to add it after return } } - // Don't use mana that only reduce the available mana - if (oldMan.contains(mana) && oldMan.count() > mana.count()) { - mana.setToMana(oldMan); + if (!newCombinations || !repeatable) { + break; } - this.add(mana); + } + + } + } else { + while (currentMana.includesMana(cost)) { // loop for multiple usage if possible + currentMana.subtractCost(cost); + currentMana.add(manaToAdd); + if (!repeatable) { + break; // Stop adding multiple usages of the ability } } + // Don't use mana that only reduce the available mana + if (prevMana.contains(currentMana) && prevMana.count() > currentMana.count()) { + currentMana.setToMana(prevMana); + } + Mana moreValuable = Mana.getMoreValuableMana(prevMana, currentMana); + if (!prevMana.equals(moreValuable)) { + this.add(currentMana); + if (moreValuable != null) { + oldManaWasReplaced = true; // the new mana includes all possibilities of the old one + } + } + } + + forceManaDeduplication(); + + return oldManaWasReplaced; } - private List getPossiblePayCombinations(int number, Mana manaAvailable) { + /** + * @param number of generic mana + * @param manaAvailable + * @return + */ + public static List getPossiblePayCombinations(int number, Mana manaAvailable) { List payCombinations = new ArrayList<>(); List payCombinationsStrings = new ArrayList<>(); if (manaAvailable.countColored() > 0) { @@ -320,28 +457,28 @@ public class ManaOptions extends ArrayList { manaToPayFrom.subtract(existingMana); if (manaToPayFrom.getBlack() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.BlackMana(1).toString())) { manaToPayFrom.subtract(Mana.BlackMana(1)); - addManaCombination(Mana.BlackMana(1), existingMana, payCombinations, payCombinationsStrings); + ManaOptions.addManaCombination(Mana.BlackMana(1), existingMana, payCombinations, payCombinationsStrings); } if (manaToPayFrom.getBlue() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.BlueMana(1).toString())) { manaToPayFrom.subtract(Mana.BlueMana(1)); - addManaCombination(Mana.BlueMana(1), existingMana, payCombinations, payCombinationsStrings); + ManaOptions.addManaCombination(Mana.BlueMana(1), existingMana, payCombinations, payCombinationsStrings); } if (manaToPayFrom.getGreen() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.GreenMana(1).toString())) { manaToPayFrom.subtract(Mana.GreenMana(1)); - addManaCombination(Mana.GreenMana(1), existingMana, payCombinations, payCombinationsStrings); + ManaOptions.addManaCombination(Mana.GreenMana(1), existingMana, payCombinations, payCombinationsStrings); } if (manaToPayFrom.getRed() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.RedMana(1).toString())) { manaToPayFrom.subtract(Mana.RedMana(1)); - addManaCombination(Mana.RedMana(1), existingMana, payCombinations, payCombinationsStrings); + ManaOptions.addManaCombination(Mana.RedMana(1), existingMana, payCombinations, payCombinationsStrings); } if (manaToPayFrom.getWhite() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.WhiteMana(1).toString())) { manaToPayFrom.subtract(Mana.WhiteMana(1)); - addManaCombination(Mana.WhiteMana(1), existingMana, payCombinations, payCombinationsStrings); + ManaOptions.addManaCombination(Mana.WhiteMana(1), existingMana, payCombinations, payCombinationsStrings); } // Pay with any only needed if colored payment was not possible if (payCombinations.isEmpty() && manaToPayFrom.getAny() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.AnyMana(1).toString())) { manaToPayFrom.subtract(Mana.AnyMana(1)); - addManaCombination(Mana.AnyMana(1), existingMana, payCombinations, payCombinationsStrings); + ManaOptions.addManaCombination(Mana.AnyMana(1), existingMana, payCombinations, payCombinationsStrings); } } } @@ -351,7 +488,17 @@ public class ManaOptions extends ArrayList { return payCombinations; } - private void addManaCombination(Mana mana, Mana existingMana, List payCombinations, List payCombinationsStrings) { + private boolean isExistingManaCombination(Mana newMana) { + for (Mana mana : this) { + Mana moreValuable = Mana.getMoreValuableMana(mana, newMana); + if (mana.equals(moreValuable)) { + return true; + } + } + return false; + } + + public static void addManaCombination(Mana mana, Mana existingMana, List payCombinations, List payCombinationsStrings) { Mana newMana = existingMana.copy(); newMana.add(mana); payCombinations.add(newMana); @@ -382,4 +529,20 @@ public class ManaOptions extends ArrayList { } } } + + /** + * Checks if the given mana (cost) is already included in one available mana + * option + * + * @param mana + * @return + */ + public boolean enough(Mana mana) { + for (Mana avail : this) { + if (mana.enough(avail)) { + return true; + } + } + return false; + } } diff --git a/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java b/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java index 0877a667c2..501ab6f5a3 100644 --- a/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import mage.Mana; import mage.abilities.costs.Cost; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.common.ManaEffect; import mage.abilities.effects.mana.BasicManaEffect; import mage.constants.Zone; @@ -35,7 +36,20 @@ public class SimpleManaAbility extends ActivatedManaAbilityImpl { } public SimpleManaAbility(Zone zone, Mana mana, Cost cost) { - super(zone, new BasicManaEffect(mana), cost); + this(zone, mana, cost, null); + this.netMana.add(mana.copy()); + this.predictable = true; + } + + /** + * + * @param zone + * @param mana + * @param cost cost for one usage + * @param netAmount DynamicValu to calculate the max available mana if effect is repeatable + */ + public SimpleManaAbility(Zone zone, Mana mana, Cost cost, DynamicValue netAmount) { + super(zone, new BasicManaEffect(mana, netAmount), cost); this.netMana.add(mana.copy()); this.predictable = true; } @@ -55,7 +69,7 @@ public class SimpleManaAbility extends ActivatedManaAbilityImpl { if (predictable) { return super.getNetMana(game); } - return new ArrayList(netMana); + return new ArrayList<>(netMana); } } diff --git a/Mage/src/main/java/mage/abilities/mana/TriggeredManaAbility.java b/Mage/src/main/java/mage/abilities/mana/TriggeredManaAbility.java index 9e19220464..7d3d424854 100644 --- a/Mage/src/main/java/mage/abilities/mana/TriggeredManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/TriggeredManaAbility.java @@ -9,7 +9,10 @@ import mage.constants.Zone; import mage.game.Game; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import mage.constants.ManaType; /** * see 20110715 - 605.1b @@ -46,17 +49,28 @@ public abstract class TriggeredManaAbility extends TriggeredAbilityImpl implemen @Override public List getNetMana(Game game) { if (game != null) { - List newNetMana = new ArrayList<>(); + List netMana = new ArrayList<>(); for (Effect effect : getEffects()) { if (effect instanceof ManaEffect) { - newNetMana.addAll(((ManaEffect) effect).getNetMana(game, this)); + netMana.addAll(((ManaEffect) effect).getNetMana(game, this)); } } - return newNetMana; + return netMana; } return new ArrayList<>(netMana); } - + + @Override + public Set getProducableManaTypes(Game game) { + Set manaTypes = new HashSet<>(); + for (Effect effect : getEffects()) { + if (effect instanceof ManaEffect) { + manaTypes.addAll(((ManaEffect) effect).getProducableManaTypes(game, this)); + } + } + return manaTypes; + } + /** * Used to check if the ability itself defines mana types it can produce. * diff --git a/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalAddManaOfTwoDifferentColorsAbility.java b/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalAddManaOfTwoDifferentColorsAbility.java index e700aeb612..09930bde7d 100644 --- a/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalAddManaOfTwoDifferentColorsAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/conditional/ConditionalAddManaOfTwoDifferentColorsAbility.java @@ -1,37 +1,37 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.mana.conditional; - -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.mana.AddConditionalManaOfTwoDifferentColorsEffect; -import mage.abilities.mana.ActivatedManaAbilityImpl; -import mage.abilities.mana.builder.ConditionalManaBuilder; -import mage.constants.Zone; - -/** - * - * @author jeffwadsworth - */ -public class ConditionalAddManaOfTwoDifferentColorsAbility extends ActivatedManaAbilityImpl { - - public ConditionalAddManaOfTwoDifferentColorsAbility(ConditionalManaBuilder manaBuilder) { - this(new TapSourceCost(), manaBuilder); - } - - public ConditionalAddManaOfTwoDifferentColorsAbility(Cost cost, ConditionalManaBuilder manaBuilder) { - super(Zone.BATTLEFIELD, new AddConditionalManaOfTwoDifferentColorsEffect(manaBuilder), cost); - } - - public ConditionalAddManaOfTwoDifferentColorsAbility(final ConditionalAddManaOfTwoDifferentColorsAbility ability) { - super(ability); - } - - @Override - public ConditionalAddManaOfTwoDifferentColorsAbility copy() { - return new ConditionalAddManaOfTwoDifferentColorsAbility(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.mana.conditional; + +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.mana.AddConditionalManaOfTwoDifferentColorsEffect; +import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.constants.Zone; + +/** + * + * @author jeffwadsworth + */ +public class ConditionalAddManaOfTwoDifferentColorsAbility extends ActivatedManaAbilityImpl { + + public ConditionalAddManaOfTwoDifferentColorsAbility(ConditionalManaBuilder manaBuilder) { + this(new TapSourceCost(), manaBuilder); + } + + public ConditionalAddManaOfTwoDifferentColorsAbility(Cost cost, ConditionalManaBuilder manaBuilder) { + super(Zone.BATTLEFIELD, new AddConditionalManaOfTwoDifferentColorsEffect(manaBuilder), cost); + } + + public ConditionalAddManaOfTwoDifferentColorsAbility(final ConditionalAddManaOfTwoDifferentColorsAbility ability) { + super(ability); + } + + @Override + public ConditionalAddManaOfTwoDifferentColorsAbility copy() { + return new ConditionalAddManaOfTwoDifferentColorsAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java b/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java index 0a4a17cb4c..000631583a 100644 --- a/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java @@ -2,6 +2,7 @@ package mage.abilities.meta; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.UUID; import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; @@ -86,15 +87,15 @@ public class OrTriggeredAbility extends TriggeredAbilityImpl { } StringBuilder sb = new StringBuilder(); if (triggeredAbilities[0].getRule().length() > 0) { - sb.append(triggeredAbilities[0].getRule().substring(0, 1).toUpperCase()) - .append(triggeredAbilities[0].getRule().substring(1).toLowerCase()); + sb.append(triggeredAbilities[0].getRule().substring(0, 1).toUpperCase(Locale.ENGLISH)) + .append(triggeredAbilities[0].getRule().substring(1).toLowerCase(Locale.ENGLISH)); } for (int i = 1; i < (triggeredAbilities.length - 1); i++) { - sb.append(triggeredAbilities[i].getRule().toLowerCase()); + sb.append(triggeredAbilities[i].getRule().toLowerCase(Locale.ENGLISH)); } - sb.append(" or ").append(triggeredAbilities[triggeredAbilities.length - 1].getRule().toLowerCase()); + sb.append(" or ").append(triggeredAbilities[triggeredAbilities.length - 1].getRule().toLowerCase(Locale.ENGLISH)); return sb.toString() + super.getRule(); } diff --git a/Mage/src/main/java/mage/actions/MageDrawAction.java b/Mage/src/main/java/mage/actions/MageDrawAction.java index dba094ec98..f40d409b40 100644 --- a/Mage/src/main/java/mage/actions/MageDrawAction.java +++ b/Mage/src/main/java/mage/actions/MageDrawAction.java @@ -1,8 +1,5 @@ package mage.actions; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.actions.impl.MageAction; import mage.actions.score.ArtificialScoringSystem; import mage.cards.Card; @@ -12,6 +9,10 @@ import mage.game.events.GameEvent; import mage.players.Player; import mage.util.CardUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** * Action for drawing cards. * @@ -37,11 +38,12 @@ public class MageDrawAction extends MageAction { /** * Draw and set action score. * - * @param game Game context. + * @param sourceId + * @param game Game context. * @return */ @Override - public int doAction(Game game) { + public int doAction(UUID sourceId, Game game) { int numDrawn = 0; int score = 0; GameEvent event = GameEvent.getEvent(GameEvent.EventType.DRAW_CARDS, player.getId(), null, player.getId(), null, amount); @@ -49,7 +51,7 @@ public class MageDrawAction extends MageAction { if (amount < 2 || !game.replaceEvent(event)) { amount = event.getAmount(); for (int i = 0; i < amount; i++) { - int value = drawCard(game); + int value = drawCard(sourceId, game); if (value == NEGATIVE_VALUE) { continue; } @@ -59,12 +61,6 @@ public class MageDrawAction extends MageAction { if (!player.isTopCardRevealed() && numDrawn > 0) { game.fireInformEvent(player.getLogName() + " draws " + CardUtil.numberToText(numDrawn, "a") + " card" + (numDrawn > 1 ? "s" : "")); } - if (player.isEmptyDraw()) { - event = GameEvent.getEvent(GameEvent.EventType.EMPTY_DRAW, player.getId(), player.getId()); - if (!game.replaceEvent(event)) { - game.doAction(new MageLoseGameAction(player, MageLoseGameAction.DRAW_REASON)); - } - } setScore(player, score); game.setStateCheckRequired(); @@ -76,11 +72,12 @@ public class MageDrawAction extends MageAction { * Draw a card if possible (there is no replacement effect that prevent us * from drawing). Fire event about card drawn. * + * @param sourceId * @param game * @return */ - protected int drawCard(Game game) { - GameEvent event = GameEvent.getEvent(GameEvent.EventType.DRAW_CARD, player.getId(), player.getId()); + protected int drawCard(UUID sourceId, Game game) { + GameEvent event = GameEvent.getEvent(GameEvent.EventType.DRAW_CARD, player.getId(), sourceId, player.getId()); event.addAppliedEffects(appliedEffects); if (!game.replaceEvent(event)) { Card card = player.getLibrary().removeFromTop(game); diff --git a/Mage/src/main/java/mage/actions/MageLoseGameAction.java b/Mage/src/main/java/mage/actions/MageLoseGameAction.java index f824f1f126..55468ae9a3 100644 --- a/Mage/src/main/java/mage/actions/MageLoseGameAction.java +++ b/Mage/src/main/java/mage/actions/MageLoseGameAction.java @@ -5,6 +5,8 @@ import mage.actions.score.ArtificialScoringSystem; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** * Lose game action. * @@ -26,7 +28,7 @@ public class MageLoseGameAction extends MageAction { } @Override - public int doAction(final Game game) { + public int doAction(UUID sourceId, final Game game) { oldLosingPlayer = game.getLosingPlayer(); if (oldLosingPlayer == null && player.canLose(game)) { setScore(player, ArtificialScoringSystem.inst.getLoseGameScore(game)); diff --git a/Mage/src/main/java/mage/actions/impl/MageAction.java b/Mage/src/main/java/mage/actions/impl/MageAction.java index 7ad9aa7972..9a384769eb 100644 --- a/Mage/src/main/java/mage/actions/impl/MageAction.java +++ b/Mage/src/main/java/mage/actions/impl/MageAction.java @@ -3,6 +3,8 @@ package mage.actions.impl; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** * Base class for mage actions. * @@ -52,10 +54,12 @@ public abstract class MageAction { /** * Execute action. * + * + * @param sourceId * @param game Game context. * @return */ - public abstract int doAction(final Game game); + public abstract int doAction(UUID sourceId, final Game game); /** * Undo action. diff --git a/Mage/src/main/java/mage/cards/AdventureCard.java b/Mage/src/main/java/mage/cards/AdventureCard.java index 1cb62d410a..1fc792311e 100644 --- a/Mage/src/main/java/mage/cards/AdventureCard.java +++ b/Mage/src/main/java/mage/cards/AdventureCard.java @@ -1,7 +1,5 @@ package mage.cards; -import java.util.List; -import java.util.UUID; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; @@ -9,6 +7,10 @@ import mage.abilities.SpellAbility; import mage.constants.CardType; import mage.constants.Zone; import mage.game.Game; +import mage.game.events.ZoneChangeEvent; + +import java.util.List; +import java.util.UUID; /** * @author TheElk801 @@ -64,6 +66,22 @@ public abstract class AdventureCard extends CardImpl { return false; } + @Override + public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) { + // zone contains only one main card + return super.removeFromZone(game, fromZone, sourceId); + } + + @Override + public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) { + if (isCopy()) { // same as meld cards + super.updateZoneChangeCounter(game, event); + return; + } + super.updateZoneChangeCounter(game, event); + getSpellCard().updateZoneChangeCounter(game, event); + } + @Override public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) { switch (ability.getSpellAbilityType()) { diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index 81a6877abb..364415d96d 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -26,6 +26,12 @@ public interface Card extends MageObject { void setOwnerId(UUID ownerId); + /** + * For cards: return all basic and dynamic abilities + * For permanents: return all basic and dynamic abilities + * @param game + * @return + */ Abilities getAbilities(Game game); void setSpellAbility(SpellAbility ability); diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index d5daa56d42..24f34599fe 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -1,12 +1,6 @@ package mage.cards; import com.google.common.collect.ImmutableList; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.MageObjectImpl; import mage.Mana; @@ -14,6 +8,7 @@ import mage.ObjectColor; import mage.abilities.*; import mage.abilities.hint.Hint; import mage.abilities.hint.HintUtils; +import mage.abilities.keyword.FlashbackAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.repository.PluginClassloaderRegistery; import mage.constants.*; @@ -32,6 +27,10 @@ import mage.util.SubTypeList; import mage.watchers.Watcher; import org.apache.log4j.Logger; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + public abstract class CardImpl extends MageObjectImpl implements Card { private static final long serialVersionUID = 1L; @@ -134,11 +133,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { secondSideCardClazz = card.secondSideCardClazz; nightCard = card.nightCard; } - if (card.spellAbility != null) { - spellAbility = card.getSpellAbility().copy(); - } else { - spellAbility = null; - } + spellAbility = null; // will be set on first getSpellAbility call if card has one. flipCard = card.flipCard; flipCardName = card.flipCardName; @@ -285,7 +280,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { /** * Gets all current abilities - includes additional abilities added by other - * cards or effects + * cards or effects. Warning, you can't modify that list. * * @param game * @return A list of {@link Ability} - this collection is not modifiable @@ -295,15 +290,38 @@ public abstract class CardImpl extends MageObjectImpl implements Card { if (game == null) { return abilities; // deck editor with empty game } + CardState cardState = game.getState().getCardState(this.getId()); - if (!cardState.hasLostAllAbilities() && (cardState.getAbilities() == null || cardState.getAbilities().isEmpty())) { + if (cardState == null) { return abilities; } + + // collects all abilities Abilities all = new AbilitiesImpl<>(); + + // basic if (!cardState.hasLostAllAbilities()) { all.addAll(abilities); } + + // dynamic all.addAll(cardState.getAbilities()); + + // workaround to add dynamic flashback ability from main card to all parts (example: Snapcaster Mage gives flashback to split card) + if (!this.getId().equals(this.getMainCard().getId())) { + CardState mainCardState = game.getState().getCardState(this.getMainCard().getId()); + if (mainCardState != null + && !mainCardState.hasLostAllAbilities() + && mainCardState.getAbilities().containsClass(FlashbackAbility.class)) { + FlashbackAbility flash = new FlashbackAbility(this.getManaCost(), this.isInstant() ? TimingRule.INSTANT : TimingRule.SORCERY); + flash.setSourceId(this.getId()); + flash.setControllerId(this.getOwnerId()); + flash.setSpellAbilityType(this.getSpellAbility().getSpellAbilityType()); + flash.setAbilityName(this.getName()); + all.add(flash); + } + } + return all; } @@ -314,6 +332,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card { cardState.getAbilities().clear(); } + @Override + public boolean hasAbility(Ability ability, Game game) { + // getAbilities(game) searches all abilities from base and dynamic lists (other) + return this.getAbilities(game).contains(ability); + } + /** * Public in order to support adding abilities to SplitCardHalf's * @@ -326,6 +350,24 @@ public abstract class CardImpl extends MageObjectImpl implements Card { for (Ability subAbility : ability.getSubAbilities()) { abilities.add(subAbility); } + + // verify check: draw effect can't be rollback after mana usage (example: Chromatic Sphere) + // (player can cheat with cancel button to see next card) + // verify test will catch that errors + if (ability instanceof ActivatedManaAbilityImpl) { + ActivatedManaAbilityImpl manaAbility = (ActivatedManaAbilityImpl) ability; + String rule = manaAbility.getRule().toLowerCase(Locale.ENGLISH); + if (manaAbility.getEffects().stream().anyMatch(e -> e.getOutcome().equals(Outcome.DrawCard)) + || rule.contains("reveal ") + || rule.contains("draw ")) { + if (manaAbility.isUndoPossible()) { + throw new IllegalArgumentException("Ability contains draw/reveal effect, but isUndoPossible is true. Ability: " + + ability.getClass().getSimpleName() + "; " + ability.getRule()); + } + } + } + + } protected void addAbilities(List abilities) { @@ -365,6 +407,20 @@ public abstract class CardImpl extends MageObjectImpl implements Card { return spellAbility; } + /** + * Dynamic cost modification for card (process only own abilities). Example: + * if it need stack related info (like real targets) then must check two + * states (game.inCheckPlayableState): + *

+ * 1. In playable state it must check all possible use cases (e.g. allow to + * reduce on any available target and modes) + *

+ * 2. In real cast state it must check current use case (e.g. real selected + * targets and modes) + * + * @param ability + * @param game + */ @Override public void adjustCosts(Ability ability, Game game) { ability.adjustCosts(game); @@ -493,7 +549,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { case EXILED: if (game.getExile().getCard(getId(), game) != null) { removed = game.getExile().removeCard(this, game); - } break; case STACK: @@ -503,15 +558,18 @@ public abstract class CardImpl extends MageObjectImpl implements Card { } else { stackObject = game.getStack().getSpell(this.getId(), false); } + if (stackObject == null && (this instanceof SplitCard)) { // handle if half of Split cast is on the stack stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId(), false); if (stackObject == null) { stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId(), false); } } + if (stackObject == null && (this instanceof AdventureCard)) { stackObject = game.getStack().getSpell(((AdventureCard) this).getSpellCard().getId(), false); } + if (stackObject == null) { stackObject = game.getStack().getSpell(getId(), false); } @@ -559,6 +617,8 @@ public abstract class CardImpl extends MageObjectImpl implements Card { } } else { logger.warn("Couldn't find card in fromZone, card=" + getIdName() + ", fromZone=" + fromZone); + // possible reason: you to remove card from wrong zone or card already removed, + // e.g. you added copy card to wrong graveyard (see owner) or removed card from graveyard before moveToZone call } return removed; } diff --git a/Mage/src/main/java/mage/cards/Cards.java b/Mage/src/main/java/mage/cards/Cards.java index 4ffe0c228f..a6c6a21efc 100644 --- a/Mage/src/main/java/mage/cards/Cards.java +++ b/Mage/src/main/java/mage/cards/Cards.java @@ -15,7 +15,7 @@ public interface Cards extends Set, Serializable { Card get(UUID cardId, Game game); - void remove(Card card); + boolean remove(Card card); void setOwner(UUID ownerId, Game game); diff --git a/Mage/src/main/java/mage/cards/CardsImpl.java b/Mage/src/main/java/mage/cards/CardsImpl.java index 250bb3f3b8..ed69e8ea2f 100644 --- a/Mage/src/main/java/mage/cards/CardsImpl.java +++ b/Mage/src/main/java/mage/cards/CardsImpl.java @@ -64,11 +64,11 @@ public class CardsImpl extends LinkedHashSet implements Cards, Serializabl } @Override - public void remove(Card card) { + public boolean remove(Card card) { if (card == null) { - return; + return false; } - this.remove(card.getId()); + return this.remove(card.getId()); } @Override diff --git a/Mage/src/main/java/mage/cards/MeldCard.java b/Mage/src/main/java/mage/cards/MeldCard.java index af29691a26..ece76ef521 100644 --- a/Mage/src/main/java/mage/cards/MeldCard.java +++ b/Mage/src/main/java/mage/cards/MeldCard.java @@ -1,6 +1,7 @@ - package mage.cards; +import java.util.List; +import java.util.UUID; import mage.abilities.Ability; import mage.constants.CardType; import mage.constants.Zone; @@ -9,11 +10,7 @@ import mage.game.Game; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import java.util.List; -import java.util.UUID; - /** - * * @author emerald000 */ public abstract class MeldCard extends CardImpl { @@ -22,7 +19,6 @@ public abstract class MeldCard extends CardImpl { protected Card bottomHalfCard; protected int topLastZoneChangeCounter; protected int bottomLastZoneChangeCounter; - protected boolean isMelded; protected Cards halves; public MeldCard(UUID ownerId, CardSetInfo setInfo, CardType[] cardTypes, String costs) { @@ -37,15 +33,14 @@ public abstract class MeldCard extends CardImpl { this.topLastZoneChangeCounter = card.topLastZoneChangeCounter; this.bottomLastZoneChangeCounter = card.bottomLastZoneChangeCounter; this.halves = new CardsImpl(card.halves); - this.isMelded = card.isMelded; } - public void setMelded(boolean isMelded) { - this.isMelded = isMelded; + public void setMelded(boolean isMelded, Game game) { + game.getState().getCardState(getId()).setMelded(isMelded); } - public boolean isMelded() { - return isMelded; + public boolean isMelded(Game game) { + return game.getState().getCardState(getId()).isMelded(); } public Card getTopHalfCard() { @@ -109,7 +104,7 @@ public abstract class MeldCard extends CardImpl { @Override public boolean addCounters(Counter counter, Ability source, Game game, List appliedEffects) { - if (this.isMelded()) { + if (this.isMelded(game)) { return super.addCounters(counter, source, game, appliedEffects); } else { // can this really happen? @@ -142,12 +137,30 @@ public abstract class MeldCard extends CardImpl { return value; } + @Override + public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, List appliedEffects) { + // TODO: missing override method for meld cards? See removeFromZone, updateZoneChangeCounter, etc + return super.moveToZone(toZone, sourceId, game, flag, appliedEffects); + } + + @Override + public void setZone(Zone zone, Game game) { + // TODO: missing override method for meld cards? See removeFromZone, updateZoneChangeCounter, etc + super.setZone(zone, game); + } + + @Override + public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List appliedEffects) { + // TODO: missing override method for meld cards? See removeFromZone, updateZoneChangeCounter, etc + return super.moveToExile(exileId, name, sourceId, game, appliedEffects); + } + @Override public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) { if (isCopy()) { return super.removeFromZone(game, fromZone, sourceId); } - if (isMelded() && fromZone == Zone.BATTLEFIELD) { + if (isMelded(game) && fromZone == Zone.BATTLEFIELD) { Permanent permanent = game.getPermanent(objectId); return permanent != null && permanent.removeFromZone(game, fromZone, sourceId); } @@ -166,11 +179,11 @@ public abstract class MeldCard extends CardImpl { @Override public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) { - if (isCopy() || !isMelded()) { + if (isCopy() || !isMelded(game)) { super.updateZoneChangeCounter(game, event); return; } - game.getState().updateZoneChangeCounter(objectId); + super.updateZoneChangeCounter(game, event); if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game) && halves.contains(topHalfCard.getId())) { topHalfCard.updateZoneChangeCounter(game, event); diff --git a/Mage/src/main/java/mage/cards/SplitCard.java b/Mage/src/main/java/mage/cards/SplitCard.java index 21c58c6c13..936339bf64 100644 --- a/Mage/src/main/java/mage/cards/SplitCard.java +++ b/Mage/src/main/java/mage/cards/SplitCard.java @@ -1,8 +1,5 @@ package mage.cards; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; @@ -12,6 +9,11 @@ import mage.constants.CardType; import mage.constants.SpellAbilityType; import mage.constants.Zone; import mage.game.Game; +import mage.game.events.ZoneChangeEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; /** * @author LevelX2 @@ -73,6 +75,13 @@ public abstract class SplitCard extends CardImpl { return false; } + @Override + public void setZone(Zone zone, Game game) { + super.setZone(zone, game); + game.setZone(getLeftHalfCard().getId(), zone); + game.setZone(getRightHalfCard().getId(), zone); + } + @Override public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List appliedEffects) { if (super.moveToExile(exileId, name, sourceId, game, appliedEffects)) { @@ -84,6 +93,23 @@ public abstract class SplitCard extends CardImpl { return false; } + @Override + public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) { + // zone contains only one main card + return super.removeFromZone(game, fromZone, sourceId); + } + + @Override + public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) { + if (isCopy()) { // same as meld cards + super.updateZoneChangeCounter(game, event); + return; + } + super.updateZoneChangeCounter(game, event); + getLeftHalfCard().updateZoneChangeCounter(game, event); + getRightHalfCard().updateZoneChangeCounter(game, event); + } + @Override public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) { switch (ability.getSpellAbilityType()) { @@ -98,22 +124,17 @@ public abstract class SplitCard extends CardImpl { } } - @Override - public void setZone(Zone zone, Game game) { - super.setZone(zone, game); - game.setZone(getLeftHalfCard().getId(), zone); - game.setZone(getRightHalfCard().getId(), zone); - } - @Override public Abilities getAbilities() { Abilities allAbilites = new AbilitiesImpl<>(); for (Ability ability : super.getAbilities()) { + // ignore split abilities TODO: why it here, for GUI's cleanup in card texts? Maybe it can be removed if (ability instanceof SpellAbility - && ((SpellAbility) ability).getSpellAbilityType() != SpellAbilityType.SPLIT - && ((SpellAbility) ability).getSpellAbilityType() != SpellAbilityType.SPLIT_AFTERMATH) { - allAbilites.add(ability); + && (((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT + || ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT_AFTERMATH)) { + continue; } + allAbilites.add(ability); } allAbilites.addAll(leftHalfCard.getAbilities()); allAbilites.addAll(rightHalfCard.getAbilities()); @@ -135,11 +156,13 @@ public abstract class SplitCard extends CardImpl { public Abilities getAbilities(Game game) { Abilities allAbilites = new AbilitiesImpl<>(); for (Ability ability : super.getAbilities(game)) { + // ignore split abilities TODO: why it here, for GUI's cleanup in card texts? Maybe it can be removed if (ability instanceof SpellAbility - && ((SpellAbility) ability).getSpellAbilityType() != SpellAbilityType.SPLIT - && ((SpellAbility) ability).getSpellAbilityType() != SpellAbilityType.SPLIT_AFTERMATH) { - allAbilites.add(ability); + && (((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT + || ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT_AFTERMATH)) { + continue; } + allAbilites.add(ability); } allAbilites.addAll(leftHalfCard.getAbilities(game)); allAbilites.addAll(rightHalfCard.getAbilities(game)); @@ -163,7 +186,5 @@ public abstract class SplitCard extends CardImpl { leftHalfCard.setOwnerId(ownerId); rightHalfCard.getAbilities().setControllerId(ownerId); rightHalfCard.setOwnerId(ownerId); - } - } diff --git a/Mage/src/main/java/mage/cards/decks/Constructed.java b/Mage/src/main/java/mage/cards/decks/Constructed.java index 8718c63540..9c6c74bbd0 100644 --- a/Mage/src/main/java/mage/cards/decks/Constructed.java +++ b/Mage/src/main/java/mage/cards/decks/Constructed.java @@ -27,6 +27,7 @@ public class Constructed extends DeckValidator { protected List restricted = new ArrayList<>(); protected List setCodes = new ArrayList<>(); protected List rarities = new ArrayList<>(); + protected Set singleCards = new HashSet<>(); public Constructed() { super("Constructed"); @@ -36,6 +37,10 @@ public class Constructed extends DeckValidator { super(name); } + protected Constructed(String name, String shortName) { + super(name, shortName); + } + public List getSetCodes() { return setCodes; } @@ -53,14 +58,15 @@ public class Constructed extends DeckValidator { @Override public boolean validate(Deck deck) { boolean valid = true; + errorsList.clear(); //20091005 - 100.2a if (deck.getCards().size() < getDeckMinSize()) { - invalid.put("Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards"); valid = false; } //20130713 - 100.4a if (deck.getSideboard().size() > 15) { - invalid.put("Sideboard", "Must contain no more than 15 cards : has " + deck.getSideboard().size() + " cards"); + addError(DeckValidatorErrorType.DECK_SIZE, "Sideboard", "Must contain no more than 15 cards : has " + deck.getSideboard().size() + " cards"); valid = false; } @@ -71,7 +77,7 @@ public class Constructed extends DeckValidator { for (String bannedCard : banned) { if (counts.containsKey(bannedCard)) { - invalid.put(bannedCard, "Banned"); + addError(DeckValidatorErrorType.BANNED, "Banned", bannedCard); valid = false; } } @@ -80,7 +86,7 @@ public class Constructed extends DeckValidator { if (counts.containsKey(restrictedCard)) { int count = counts.get(restrictedCard); if (count > 1) { - invalid.put(restrictedCard, "Restricted: " + count); + addError(DeckValidatorErrorType.OTHER, restrictedCard, "Restricted amount: " + count); valid = false; } } @@ -136,8 +142,8 @@ public class Constructed extends DeckValidator { break; } } - if (!legal && !invalid.containsKey(card.getName())) { - invalid.put(card.getName(), "Invalid rarity: " + card.getRarity()); + if (!legal && !errorsListContainsGroup(card.getName())) { + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid rarity: " + card.getRarity()); } return legal; } @@ -153,7 +159,7 @@ public class Constructed extends DeckValidator { } /** - * Checks if the given card is legal in any of the given sets + * Checks if the given card is legal in any of the given sets or as single card * * @param card - the card to check * @return Whether the card was printed in any of this format's sets. @@ -168,8 +174,14 @@ public class Constructed extends DeckValidator { break; } } - if (!legal && !invalid.containsKey(card.getName())) { - invalid.put(card.getName(), "Invalid set: " + card.getExpansionSetCode()); + + // check if single card allows + if (singleCards.contains(card.getName())) { + legal = true; + } + + if (!legal && !errorsListContainsGroup(card.getName())) { + addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Invalid set: " + card.getExpansionSetCode()); } return legal; } @@ -180,14 +192,14 @@ public class Constructed extends DeckValidator { if (entry.getValue() > maxCopies && !basicLandNames.contains(entry.getKey()) && !anyNumberCardsAllowed.contains(entry.getKey())) { - invalid.put(entry.getKey(), "Too many: " + entry.getValue()); + addError(DeckValidatorErrorType.OTHER, entry.getKey(), "Too many: " + entry.getValue()); valid = false; } if (entry.getValue() > 7 && entry.getKey().equals("Seven Dwarves")) { - invalid.put(entry.getKey(), "Too many: " + entry.getValue()); + addError(DeckValidatorErrorType.OTHER, entry.getKey(), "Too many: " + entry.getValue()); valid = false; } } return valid; } -} +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/cards/decks/Deck.java b/Mage/src/main/java/mage/cards/decks/Deck.java index 8ffb88bcc0..7c80c0de46 100644 --- a/Mage/src/main/java/mage/cards/decks/Deck.java +++ b/Mage/src/main/java/mage/cards/decks/Deck.java @@ -55,6 +55,15 @@ public class Deck implements Serializable { return currentDeck; } + /** + * Warning, AI can't play Mock cards, so call it with extra params in real games or tests + * + * @param deckCardLists cards to load + * @param ignoreErrors - do not raise exception error on wrong deck + * @param mockCards - use it for GUI only code, real game cards must be real + * @return + * @throws GameException + */ public static Deck load(DeckCardLists deckCardLists, boolean ignoreErrors, boolean mockCards) throws GameException { Deck deck = new Deck(); deck.setName(deckCardLists.getName()); diff --git a/Mage/src/main/java/mage/cards/decks/DeckFormatsTest.java b/Mage/src/main/java/mage/cards/decks/DeckFormatsTest.java index 2b0e71e502..6dfe1d612f 100644 --- a/Mage/src/main/java/mage/cards/decks/DeckFormatsTest.java +++ b/Mage/src/main/java/mage/cards/decks/DeckFormatsTest.java @@ -4,6 +4,7 @@ import org.junit.Assert; import org.junit.Test; import java.util.HashMap; +import java.util.Locale; import java.util.Map; /** @@ -23,8 +24,8 @@ public class DeckFormatsTest { } // 2. must work with files String fileName = "C:\\xmage\\deck" + "." + df.getExporter().getDefaultFileExt(); - Assert.assertTrue("Must support lower ext: " + df.getExporter().getDescription(), DeckFormats.getFormatForExtension(fileName.toLowerCase()).isPresent()); - Assert.assertTrue("Must support upper ext: " + df.getExporter().getDescription(), DeckFormats.getFormatForExtension(fileName.toUpperCase()).isPresent()); + Assert.assertTrue("Must support lower ext: " + df.getExporter().getDescription(), DeckFormats.getFormatForExtension(fileName.toLowerCase(Locale.ENGLISH)).isPresent()); + Assert.assertTrue("Must support upper ext: " + df.getExporter().getDescription(), DeckFormats.getFormatForExtension(fileName.toUpperCase(Locale.ENGLISH)).isPresent()); } // 3. wrong ext diff --git a/Mage/src/main/java/mage/cards/decks/DeckValidator.java b/Mage/src/main/java/mage/cards/decks/DeckValidator.java index 506fd6310b..b36c03c553 100644 --- a/Mage/src/main/java/mage/cards/decks/DeckValidator.java +++ b/Mage/src/main/java/mage/cards/decks/DeckValidator.java @@ -3,9 +3,8 @@ package mage.cards.decks; import mage.cards.Card; import java.io.Serializable; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; /** * @author BetaSteward_at_googlemail.com @@ -13,11 +12,15 @@ import java.util.Map; public abstract class DeckValidator implements Serializable { protected String name; - - protected Map invalid = new HashMap<>(); + protected String shortName; + protected List errorsList = new ArrayList<>(); public DeckValidator(String name) { - this.name = name; + setName(name); + } + + public DeckValidator(String name, String shortName) { + setName(name, shortName); } public abstract boolean validate(Deck deck); @@ -26,8 +29,80 @@ public abstract class DeckValidator implements Serializable { return name; } - public Map getInvalid() { - return invalid; + public String getShortName() { + return shortName; + } + + protected void setName(String name) { + this.name = name; + this.shortName = name.contains("-") ? name.substring(name.indexOf("-") + 1).trim() : name; + } + + protected void setName(String name, String shortName) { + this.name = name; + this.shortName = shortName; + } + + protected void setShortName(String shortName) { + this.shortName = shortName; + } + + public List getErrorsList() { + return this.errorsList; + } + + /** + * Get errors list sorted by error type and texts + * + * @return + */ + public List getErrorsListSorted() { + List list = new ArrayList<>(this.getErrorsList()); + + Collections.sort(list, new Comparator() { + @Override + public int compare(DeckValidatorError e1, DeckValidatorError e2) { + int res = 0; + + // sort by error type + Integer order1 = e1.getErrorType().getSortOrder(); + Integer order2 = e2.getErrorType().getSortOrder(); + res = order2.compareTo(order1); + + // sort by group + if (res != 0) { + res = e2.getGroup().compareTo(e1.getGroup()); + } + + // sort by message + if (res != 0) { + res = e2.getMessage().compareTo(e1.getMessage()); + } + + return res; + } + }); + + return list; + } + + public String getErrorsListInfo() { + // for tests + return this.errorsList.stream() + .map(e -> e.getGroup() + "=" + e.getMessage()) + .collect(Collectors.joining(", ")); + } + + public void addError(DeckValidatorErrorType errorType, String group, String message) { + this.errorsList.add(new DeckValidatorError(errorType, group, message)); + } + + public boolean errorsListContainsGroup(String group) { + return this.errorsList.stream().anyMatch(e -> e.getGroup().equals(group)); + } + + public boolean isPartlyValid() { + return errorsList.size() == 0 || !errorsList.stream().anyMatch(e -> !e.getErrorType().isPartlyLegal()); } protected void countCards(Map counts, Collection cards) { diff --git a/Mage/src/main/java/mage/cards/decks/DeckValidatorError.java b/Mage/src/main/java/mage/cards/decks/DeckValidatorError.java new file mode 100644 index 0000000000..07650a0d5d --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/DeckValidatorError.java @@ -0,0 +1,29 @@ +package mage.cards.decks; + +/** + * @author JayDi85 + */ +public class DeckValidatorError { + + private final DeckValidatorErrorType errorType; + private final String group; + private final String message; + + public DeckValidatorError(DeckValidatorErrorType errorType, String group, String message) { + this.errorType = errorType; + this.group = group; + this.message = message; + } + + public DeckValidatorErrorType getErrorType() { + return this.errorType; + } + + public String getGroup() { + return this.group; + } + + public String getMessage() { + return this.message; + } +} diff --git a/Mage/src/main/java/mage/cards/decks/DeckValidatorErrorType.java b/Mage/src/main/java/mage/cards/decks/DeckValidatorErrorType.java new file mode 100644 index 0000000000..af43662ca6 --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/DeckValidatorErrorType.java @@ -0,0 +1,29 @@ +package mage.cards.decks; + +/** + * @author JayDi85 + */ +public enum DeckValidatorErrorType { + + PRIMARY(false, 10), // first errors to show (e.g. missing commander) + DECK_SIZE(true, 20), // wrong deck size (deck must be legal while building) + BANNED(false, 30), + WRONG_SET(false, 40), + OTHER(false, 50); + + private final boolean partlyLegal; // for deck legality panel: is it partly legal (e.g. show deck legal even without full deck size) + private final int sortOrder; // errors list sort order from small to big + + DeckValidatorErrorType(boolean partlyLegal, int sortOrder) { + this.partlyLegal = partlyLegal; + this.sortOrder = sortOrder; + } + + public boolean isPartlyLegal() { + return this.partlyLegal; + } + + public int getSortOrder() { + return this.sortOrder; + } +} diff --git a/Mage/src/main/java/mage/cards/decks/PennyDreadfulLegalityUtil.java b/Mage/src/main/java/mage/cards/decks/PennyDreadfulLegalityUtil.java new file mode 100644 index 0000000000..248c6ac49c --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/PennyDreadfulLegalityUtil.java @@ -0,0 +1,23 @@ +package mage.cards.decks; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class PennyDreadfulLegalityUtil { + public static Map getLegalCardList() { + Map pdAllowed = new HashMap<>(); + + Properties properties = new Properties(); + try { + properties.load(PennyDreadfulLegalityUtil.class.getResourceAsStream("/pennydreadful.properties")); + } catch (Exception e) { + e.printStackTrace(); + } + for (final Map.Entry entry : properties.entrySet()) { + pdAllowed.put((String) entry.getKey(), 1); + } + + return pdAllowed; + } +} diff --git a/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java index dd153c5abe..cabf599b23 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java @@ -34,6 +34,8 @@ public abstract class DeckImporter { return new CodDeckImporter(); } else if (file.toLowerCase(Locale.ENGLISH).endsWith("o8d")) { return new O8dDeckImporter(); + } else if (file.toLowerCase(Locale.ENGLISH).endsWith("json")) { + return new MtgjsonDeckImporter(); } else if (file.toLowerCase(Locale.ENGLISH).endsWith("draft")) { return new DraftLogImporter(); } else if (file.toLowerCase(Locale.ENGLISH).endsWith("mtga")) { diff --git a/Mage/src/main/java/mage/cards/decks/importer/JsonDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/JsonDeckImporter.java new file mode 100644 index 0000000000..c754fd04e0 --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/importer/JsonDeckImporter.java @@ -0,0 +1,70 @@ +package mage.cards.decks.importer; + +import java.io.File; +import java.io.FileReader; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +import mage.cards.decks.DeckCardLists; + +/** + * @author github: timhae + */ +public abstract class JsonDeckImporter extends DeckImporter { + + protected StringBuilder sbMessage = new StringBuilder(); + + /** + * + * @param file file to import + * @param errorMessages you can setup output messages to showup to user + * @return decks list + */ + public DeckCardLists importDeck(String file, StringBuilder errorMessages) { + File f = new File(file); + DeckCardLists deckList = new DeckCardLists(); + if (!f.exists()) { + logger.warn("Deckfile " + file + " not found."); + return deckList; + } + + sbMessage.setLength(0); + try { + try (FileReader reader = new FileReader(f)) { + try { // Json parsing + JSONParser parser = new JSONParser(); + JSONObject rootObj = (JSONObject) parser.parse(reader); + deckList.setName((String) rootObj.get("name")); + readJson(rootObj, deckList); + + if (sbMessage.length() > 0) { + if (errorMessages != null) { + // normal output for user + errorMessages.append(sbMessage); + } else { + // fatal error + logger.fatal(sbMessage); + } + } + } catch (ParseException ex) { + logger.fatal(null, ex); + } + } catch (Exception ex) { + logger.fatal(null, ex); + } + } catch (Exception ex) { + logger.fatal(null, ex); + } + return deckList; + } + + @Override + public DeckCardLists importDeck(String file) { + return importDeck(file, null); + } + + protected abstract void readJson(JSONObject line, DeckCardLists decklist); +} diff --git a/Mage/src/main/java/mage/cards/decks/importer/MtgjsonDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/MtgjsonDeckImporter.java new file mode 100644 index 0000000000..2908e3bc30 --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/importer/MtgjsonDeckImporter.java @@ -0,0 +1,51 @@ +package mage.cards.decks.importer; + +import java.util.Optional; +import java.util.List; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import mage.cards.decks.DeckCardInfo; +import mage.cards.decks.DeckCardLists; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; + + +/** + * + * @author github: timhae + */ +public class MtgjsonDeckImporter extends JsonDeckImporter { + + @Override + protected void readJson(JSONObject rootObj, DeckCardLists deckList) { + // set + String set = (String) rootObj.get("code"); + // mainboard + JSONArray mainBoard = (JSONArray) rootObj.get("mainBoard"); + List mainDeckList = deckList.getCards(); + addBoardToList(mainBoard, mainDeckList, set); + // sideboard + JSONArray sideBoard = (JSONArray) rootObj.get("sideBoard"); + List sideDeckList = deckList.getSideboard(); + addBoardToList(sideBoard, sideDeckList, set); + } + + private void addBoardToList(JSONArray board, List list, String set) { + board.forEach(arrayCard -> { + JSONObject card = (JSONObject) arrayCard; + String name = (String) card.get("name"); + int num = ((Number) card.get("count")).intValue(); + Optional cardLookup = getCardLookup().lookupCardInfo(name, set); + if (!cardLookup.isPresent()) { + sbMessage.append("Could not find card: '").append(name).append("'\n"); + } else { + CardInfo cardInfo = cardLookup.get(); + for (int i = 0; i < num; i++) { + list.add(new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode())); + } + } + }); + } +} diff --git a/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java index 21aef12f49..01e1d81862 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java @@ -1,52 +1,70 @@ package mage.cards.decks.importer; -import static javax.xml.xpath.XPathConstants.NODESET; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; - import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static javax.xml.xpath.XPathConstants.NODESET; + public abstract class XmlDeckImporter extends DeckImporter { - private XPathFactory xpathFactory = XPathFactory.newInstance(); - private DocumentBuilder builder = getDocumentBuilder(); + private final XPathFactory xpathFactory = XPathFactory.newInstance(); + private final DocumentBuilder builder = getDocumentBuilder(); - protected List getNodes(Document doc, String xpathExpression) throws XPathExpressionException { - NodeList nodes = (NodeList) xpathFactory.newXPath().evaluate(xpathExpression, doc, NODESET); - List list = new ArrayList<>(); - for (int i = 0; i < nodes.getLength(); i++) { - list.add(nodes.item(i)); + protected List getNodes(Document doc, String xpathExpression) throws XPathExpressionException { + NodeList nodes = (NodeList) xpathFactory.newXPath().evaluate(xpathExpression, doc, NODESET); + List list = new ArrayList<>(); + for (int i = 0; i < nodes.getLength(); i++) { + list.add(nodes.item(i)); + } + return list; } - return list; - } - private DocumentBuilder getDocumentBuilder() { - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - return factory.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - throw new RuntimeException(); - } - } + private DocumentBuilder getDocumentBuilder() { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - protected Document getXmlDocument(String filename) throws IOException { - try { - return builder.parse(new File(filename)); - } catch (SAXException e) { - throw new IOException(e); + // security fix from https://stackoverflow.com/a/59736162/1276632 to disable external data download + // REDHAT + // https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf + factory.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + // OWASP + // https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + // Disable external DTDs as well + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" + factory.setXIncludeAware(false); + factory.setExpandEntityReferences(false); + + return factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new RuntimeException(); + } + } + + protected Document getXmlDocument(String filename) throws IOException { + try { + return builder.parse(new File(filename)); + } catch (SAXException e) { + throw new IOException(e); + } } - } } diff --git a/Mage/src/main/java/mage/cards/repository/CardCriteria.java b/Mage/src/main/java/mage/cards/repository/CardCriteria.java index 85882512b2..7497e76aec 100644 --- a/Mage/src/main/java/mage/cards/repository/CardCriteria.java +++ b/Mage/src/main/java/mage/cards/repository/CardCriteria.java @@ -1,18 +1,17 @@ - package mage.cards.repository; import com.j256.ormlite.stmt.QueryBuilder; import com.j256.ormlite.stmt.SelectArg; import com.j256.ormlite.stmt.Where; +import mage.constants.CardType; +import mage.constants.Rarity; + import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import mage.constants.CardType; -import mage.constants.Rarity; /** - * * @author North */ public class CardCriteria { @@ -177,6 +176,8 @@ public class CardCriteria { } public void buildQuery(QueryBuilder qb) throws SQLException { + optimize(); + Where where = qb.where(); where.eq("nightCard", false); where.eq("splitCardHalf", false); @@ -249,37 +250,35 @@ public class CardCriteria { clausesCount++; } - if (!black || !blue || !green || !red || !white || !colorless) { - int colorClauses = 0; - if (black) { - where.eq("black", true); - colorClauses++; - } - if (blue) { - where.eq("blue", true); - colorClauses++; - } - if (green) { - where.eq("green", true); - colorClauses++; - } - if (red) { - where.eq("red", true); - colorClauses++; - } - if (white) { - where.eq("white", true); - colorClauses++; - } - if (colorless) { - where.eq("black", false).eq("blue", false).eq("green", false).eq("red", false).eq("white", false); - where.and(5); - colorClauses++; - } - if (colorClauses > 0) { - where.or(colorClauses); - clausesCount++; - } + int colorClauses = 0; + if (black) { + where.eq("black", true); + colorClauses++; + } + if (blue) { + where.eq("blue", true); + colorClauses++; + } + if (green) { + where.eq("green", true); + colorClauses++; + } + if (red) { + where.eq("red", true); + colorClauses++; + } + if (white) { + where.eq("white", true); + colorClauses++; + } + if (colorless) { + where.eq("black", false).eq("blue", false).eq("green", false).eq("red", false).eq("white", false); + where.and(5); + colorClauses++; + } + if (colorClauses > 0) { + where.or(colorClauses); + clausesCount++; } if (minCardNumber != Integer.MIN_VALUE) { @@ -310,6 +309,38 @@ public class CardCriteria { } } + private CardCriteria optimize() { + // remove rarity + if (rarities.size() > 0) { + List unusedRarities = new ArrayList<>(Arrays.asList(Rarity.values())); + unusedRarities.removeAll(rarities); + if (unusedRarities.isEmpty()) { + rarities.clear(); + } + } + + // remove color + if (black && blue && green && red && white && colorless) { + black = false; + blue = false; + green = false; + red = false; + white = false; + colorless = false; + } + + // remove card type + if (types.size() > 0) { + List unusedCardTypes = new ArrayList<>(Arrays.asList(CardType.values())); + unusedCardTypes.removeAll(types); + if (unusedCardTypes.stream().noneMatch(CardType::isIncludeInSearch)) { + types.clear(); + } + } + + return this; + } + public String getName() { return name; } diff --git a/Mage/src/main/java/mage/cards/repository/CardInfo.java b/Mage/src/main/java/mage/cards/repository/CardInfo.java index 28921fc116..7b9b62305f 100644 --- a/Mage/src/main/java/mage/cards/repository/CardInfo.java +++ b/Mage/src/main/java/mage/cards/repository/CardInfo.java @@ -3,6 +3,8 @@ package mage.cards.repository; import com.j256.ormlite.field.DataType; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; +import java.util.*; +import java.util.stream.Collectors; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -15,9 +17,6 @@ import mage.util.CardUtil; import mage.util.SubTypeList; import org.apache.log4j.Logger; -import java.util.*; -import java.util.stream.Collectors; - /** * @author North */ @@ -289,8 +288,8 @@ public class CardInfo { return res; } - public final Set getTypes() { - Set list = EnumSet.noneOf(CardType.class); + public final ArrayList getTypes() { + ArrayList list = new ArrayList<>(); for (String type : this.types.split(SEPARATOR)) { try { list.add(CardType.valueOf(type)); @@ -300,7 +299,7 @@ public class CardInfo { return list; } - public final void setTypes(Set types) { + public final void setTypes(ArrayList types) { StringBuilder sb = new StringBuilder(); for (CardType item : types) { sb.append(item.name()).append(SEPARATOR); diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java index 690da5f9fa..96fe7d248b 100644 --- a/Mage/src/main/java/mage/cards/repository/CardRepository.java +++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java @@ -12,6 +12,7 @@ import com.j256.ormlite.support.DatabaseConnection; import com.j256.ormlite.table.TableUtils; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetType; import mage.constants.SuperType; import mage.game.events.Listener; import mage.util.RandomUtil; @@ -35,7 +36,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 52; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 229; + private static final long CARD_CONTENT_VERSION = 231; private Dao cardDao; private Set classNames; private RepositoryEventSource eventSource = new RepositoryEventSource(); @@ -486,6 +487,37 @@ public enum CardRepository { return Collections.emptyList(); } + public CardInfo findOldestNonPromoVersionCard(String name) { + List allVersions = this.findCards(name); + if (!allVersions.isEmpty()) { + allVersions.sort(new OldestNonPromoComparator()); + return allVersions.get(0); + } else { + return null; + } + } + + static class OldestNonPromoComparator implements Comparator { + @Override + public int compare(CardInfo a, CardInfo b) { + ExpansionInfo aSet = ExpansionRepository.instance.getSetByCode(a.getSetCode()); + ExpansionInfo bSet = ExpansionRepository.instance.getSetByCode(b.getSetCode()); + if (aSet.getType() == SetType.PROMOTIONAL && bSet.getType() != SetType.PROMOTIONAL) { + return 1; + } + if (bSet.getType() == SetType.PROMOTIONAL && aSet.getType() != SetType.PROMOTIONAL) { + return -1; + } + if (aSet.getReleaseDate().after(bSet.getReleaseDate())) { + return 1; + } + if (aSet.getReleaseDate().before(bSet.getReleaseDate())) { + return -1; + } + return Integer.compare(a.getCardNumberAsInt(), b.getCardNumberAsInt()); + } + } + public long getContentVersionFromDB() { try { ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL); diff --git a/Mage/src/main/java/mage/choices/ChoiceCardType.java b/Mage/src/main/java/mage/choices/ChoiceCardType.java new file mode 100644 index 0000000000..40c752c58e --- /dev/null +++ b/Mage/src/main/java/mage/choices/ChoiceCardType.java @@ -0,0 +1,32 @@ +package mage.choices; + +import mage.constants.CardType; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author emerald000 + */ +public class ChoiceCardType extends ChoiceImpl { + + public ChoiceCardType() { + this(true, Arrays.stream(CardType.values()).collect(Collectors.toList())); + } + + public ChoiceCardType(boolean required, List cardTypes) { + super(required); + this.choices.addAll(cardTypes.stream().map(CardType::toString).collect(Collectors.toList())); + this.message = "Choose a card type"; + } + + private ChoiceCardType(final ChoiceCardType choice) { + super(choice); + } + + @Override + public ChoiceCardType copy() { + return new ChoiceCardType(this); + } +} diff --git a/Mage/src/main/java/mage/choices/ChoiceColor.java b/Mage/src/main/java/mage/choices/ChoiceColor.java index aacbee2f13..e93a2b844a 100644 --- a/Mage/src/main/java/mage/choices/ChoiceColor.java +++ b/Mage/src/main/java/mage/choices/ChoiceColor.java @@ -16,11 +16,11 @@ public class ChoiceColor extends ChoiceImpl { public static List getBaseColors() { List arr = new ArrayList<>(); - arr.add("Green"); + arr.add("White"); arr.add("Blue"); arr.add("Black"); arr.add("Red"); - arr.add("White"); + arr.add("Green"); return arr; } diff --git a/Mage/src/main/java/mage/choices/ManaChoice.java b/Mage/src/main/java/mage/choices/ManaChoice.java index 45ada26689..f5922dfa87 100644 --- a/Mage/src/main/java/mage/choices/ManaChoice.java +++ b/Mage/src/main/java/mage/choices/ManaChoice.java @@ -1,54 +1,54 @@ -package mage.choices; - -import mage.Mana; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; - -public class ManaChoice { - public static Mana chooseAnyColor(Player player, Game game, int amount) { - if (player == null) { - return null; - } - Mana mana = new Mana(); - for (int i = 0; i < amount; i++) { - ChoiceColor choiceColor = new ChoiceColor(); - if (amount > 1) { - choiceColor.setMessage("Choose color " + (i + 1)); - } - if (!player.choose(Outcome.Benefit, choiceColor, game)) { - return null; - } - choiceColor.increaseMana(mana); - } - return mana; - } - - public static Mana chooseTwoDifferentColors(Player player, Game game) { - Mana mana = new Mana(); - - if (game == null || player == null) { - return mana; - } - - ChoiceColor color1 = new ChoiceColor(true, "Choose color 1"); - if (!player.choose(Outcome.PutManaInPool, color1, game) || color1.getColor() == null) { - return mana; - } - - ChoiceColor color2 = new ChoiceColor(true, "Choose color 2"); - color2.removeColorFromChoices(color1.getChoice()); - if (!player.choose(Outcome.PutManaInPool, color2, game) || color2.getColor() == null) { - return mana; - } - - if (color1.getColor().equals(color2.getColor())) { - game.informPlayers("Player " + player.getName() + " is cheating with mana choices."); - return mana; - } - - mana.add(color1.getMana(1)); - mana.add(color2.getMana(1)); - return mana; - } -} +package mage.choices; + +import mage.Mana; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +public class ManaChoice { + public static Mana chooseAnyColor(Player player, Game game, int amount) { + if (player == null) { + return null; + } + Mana mana = new Mana(); + for (int i = 0; i < amount; i++) { + ChoiceColor choiceColor = new ChoiceColor(); + if (amount > 1) { + choiceColor.setMessage("Choose color " + (i + 1)); + } + if (!player.choose(Outcome.Benefit, choiceColor, game)) { + return null; + } + choiceColor.increaseMana(mana); + } + return mana; + } + + public static Mana chooseTwoDifferentColors(Player player, Game game) { + Mana mana = new Mana(); + + if (game == null || player == null) { + return mana; + } + + ChoiceColor color1 = new ChoiceColor(true, "Choose color 1"); + if (!player.choose(Outcome.PutManaInPool, color1, game) || color1.getColor() == null) { + return mana; + } + + ChoiceColor color2 = new ChoiceColor(true, "Choose color 2"); + color2.removeColorFromChoices(color1.getChoice()); + if (!player.choose(Outcome.PutManaInPool, color2, game) || color2.getColor() == null) { + return mana; + } + + if (color1.getColor().equals(color2.getColor())) { + game.informPlayers("Player " + player.getName() + " is cheating with mana choices."); + return mana; + } + + mana.add(color1.getMana(1)); + mana.add(color2.getMana(1)); + return mana; + } +} diff --git a/Mage/src/main/java/mage/constants/CardType.java b/Mage/src/main/java/mage/constants/CardType.java index 821db4a1ba..15dc72b6b7 100644 --- a/Mage/src/main/java/mage/constants/CardType.java +++ b/Mage/src/main/java/mage/constants/CardType.java @@ -4,30 +4,36 @@ import mage.MageObject; import mage.filter.predicate.Predicate; import mage.game.Game; +import java.util.ArrayList; import java.util.Arrays; -import java.util.EnumSet; /** * @author North */ public enum CardType { - ARTIFACT("Artifact", true), - CONSPIRACY("Conspiracy", false), - CREATURE("Creature", true), - ENCHANTMENT("Enchantment", true), - INSTANT("Instant", false), - LAND("Land", true), - PLANESWALKER("Planeswalker", true), - SORCERY("Sorcery", false), - TRIBAL("Tribal", false); + ARTIFACT("Artifact", true, true), + CONSPIRACY("Conspiracy", false, false), + CREATURE("Creature", true, true), + ENCHANTMENT("Enchantment", true, true), + INSTANT("Instant", false, true), + LAND("Land", true, true), + PHENOMENON("Phenomenon", false, false), + PLANE("Plane", false, false), + PLANESWALKER("Planeswalker", true, true), + SCHEME("Scheme", false, false), + SORCERY("Sorcery", false, true), + TRIBAL("Tribal", false, false), + VANGUARD("Vanguard", false, false); private final String text; private final boolean permanentType; + private final boolean includeInSearch; // types that can be searched/filtered by Deck Editor private final CardTypePredicate predicate; - CardType(String text, boolean permanentType) { + CardType(String text, boolean permanentType, boolean includeInSearch) { this.text = text; this.permanentType = permanentType; + this.includeInSearch = includeInSearch; this.predicate = new CardTypePredicate(this); } @@ -50,6 +56,10 @@ public enum CardType { return permanentType; } + public boolean isIncludeInSearch() { + return includeInSearch; + } + /** * Returns all of the card types from two lists of card types. Duplicates * are eliminated. @@ -59,7 +69,7 @@ public enum CardType { * @return */ public static CardType[] mergeTypes(CardType[] a, CardType[] b) { - EnumSet cardTypes = EnumSet.noneOf(CardType.class); + ArrayList cardTypes = new ArrayList<>(); cardTypes.addAll(Arrays.asList(a)); cardTypes.addAll(Arrays.asList(b)); return cardTypes.toArray(new CardType[0]); diff --git a/Mage/src/main/java/mage/constants/Duration.java b/Mage/src/main/java/mage/constants/Duration.java index 0ec5060ed0..1b857000ae 100644 --- a/Mage/src/main/java/mage/constants/Duration.java +++ b/Mage/src/main/java/mage/constants/Duration.java @@ -4,25 +4,27 @@ package mage.constants; * @author North */ public enum Duration { - OneUse("", true), - EndOfGame("for the rest of the game", false), - WhileOnBattlefield("", false), - WhileOnStack("", false), - WhileInGraveyard("", false), - EndOfTurn("until end of turn", true), - UntilYourNextTurn("until your next turn", true), - UntilEndOfYourNextTurn("until the end of your next turn", true), - UntilSourceLeavesBattlefield("until {source} leaves the battlefield", true), // supported for continuous layered effects - EndOfCombat("until end of combat", true), - EndOfStep("until end of phase step", true), - Custom("", true); + OneUse("", true, true), + EndOfGame("for the rest of the game", false, false), + WhileOnBattlefield("", false, false), + WhileOnStack("", false, true), + WhileInGraveyard("", false, false), + EndOfTurn("until end of turn", true, true), + UntilYourNextTurn("until your next turn", true, true), + UntilEndOfYourNextTurn("until the end of your next turn", true, true), + UntilSourceLeavesBattlefield("until {source} leaves the battlefield", true, false), // supported for continuous layered effects + EndOfCombat("until end of combat", true, true), + EndOfStep("until end of phase step", true, true), + Custom("", true, true); private final String text; - private final boolean onlyValidIfNoZoneChange; // defines if an effect lasts only if the source has not chnaged zone since init of the effect + private final boolean onlyValidIfNoZoneChange; // defines if an effect lasts only if the source has not changed zone since init of the effect + private final boolean fixedController; // has the controller of the effect to change, if the controller of the source changes - Duration(String text, boolean onlyValidIfNoZoneChange) { + Duration(String text, boolean onlyValidIfNoZoneChange, boolean fixedController) { this.text = text; this.onlyValidIfNoZoneChange = onlyValidIfNoZoneChange; + this.fixedController = fixedController; } @Override @@ -34,4 +36,7 @@ public enum Duration { return onlyValidIfNoZoneChange; } + public boolean isFixedController() { + return fixedController; + } } diff --git a/Mage/src/main/java/mage/constants/EnterEventType.java b/Mage/src/main/java/mage/constants/EnterEventType.java index 09a5b75a92..9fc0b834e7 100644 --- a/Mage/src/main/java/mage/constants/EnterEventType.java +++ b/Mage/src/main/java/mage/constants/EnterEventType.java @@ -1,12 +1,12 @@ -package mage.constants; - -/** - * - * @author LevelX2 - */ -public enum EnterEventType { - SELF, - CONTROL, - COPY, - OTHER -} +package mage.constants; + +/** + * + * @author LevelX2 + */ +public enum EnterEventType { + SELF, + CONTROL, + COPY, + OTHER +} diff --git a/Mage/src/main/java/mage/constants/Planes.java b/Mage/src/main/java/mage/constants/Planes.java index 3fd92cc5b9..17d791603f 100644 --- a/Mage/src/main/java/mage/constants/Planes.java +++ b/Mage/src/main/java/mage/constants/Planes.java @@ -1,41 +1,69 @@ - package mage.constants; /** - * * @author spjspj */ public enum Planes { - PLANE_ACADEMY_AT_TOLARIA_WEST("AcademyAtTolariaWestPlane"), - PLANE_AGYREM("AgyremPlane"), - PLANE_AKOUM("AkoumPlane"), - PLANE_ASTRAL_ARENA("AstralArenaPlane"), - PLANE_BANT("BantPlane"), - PLANE_EDGE_OF_MALACOL("EdgeOfMalacolPlane"), - PLANE_FEEDING_GROUNDS("FeedingGroundsPlane"), - PLANE_FIELDS_OF_SUMMER("FieldsOfSummerPlane"), - PLANE_HEDRON_FIELDS_OF_AGADEEM("HedronFieldsOfAgadeemPlane"), - PLANE_LETHE_LAKE("LetheLakePlane"), - PLANE_NAYA("NayaPlane"), - PLANE_PANOPTICON("PanopticonPlane"), - PLANE_TAZEEM("TazeemPlane"), - PLANE_THE_DARK_BARONY("TheDarkBaronyPlane"), - PLANE_THE_EON_FOG("TheEonFogPlane"), - PLANE_THE_GREAT_FOREST("TheGreatForestPlane"), - PLANE_THE_ZEPHYR_MAZE_FOG("TheZephyrMazePlane"), - PLANE_TRUGA_JUNGLE("TrugaJunglePlane"), - PLANE_TRAIL_OF_THE_MAGE_RINGS("TrailOfTheMageRingsPlane"), - PLANE_TURRI_ISLAND("TurriIslandPlane"), - PLANE_UNDERCITY_REACHES("UndercityReachesPlane"); - - private final String text; + PLANE_ACADEMY_AT_TOLARIA_WEST("AcademyAtTolariaWestPlane", "Plane - Academy at Tolaria West"), + PLANE_AGYREM("AgyremPlane", "Plane - Agyrem"), + PLANE_AKOUM("AkoumPlane", "Plane - Akoum"), + PLANE_ASTRAL_ARENA("AstralArenaPlane", "Plane - Astral Arena"), + PLANE_BANT("BantPlane", "Plane - Bant"), + PLANE_EDGE_OF_MALACOL("EdgeOfMalacolPlane", "Plane - Edge of Malacol"), + PLANE_FEEDING_GROUNDS("FeedingGroundsPlane", "Plane - Feeding Grounds"), + PLANE_FIELDS_OF_SUMMER("FieldsOfSummerPlane", "Plane - Fields of Summer"), + PLANE_HEDRON_FIELDS_OF_AGADEEM("HedronFieldsOfAgadeemPlane", "Plane - Hedron Fields of Agadeem"), + PLANE_LETHE_LAKE("LetheLakePlane", "Plane - Lethe Lake"), + PLANE_NAYA("NayaPlane", "Plane - Naya"), + PLANE_PANOPTICON("PanopticonPlane", "Plane - Panopticon"), + PLANE_TAZEEM("TazeemPlane", "Plane - Tazeem"), + PLANE_THE_DARK_BARONY("TheDarkBaronyPlane", "Plane - The Dark Barony"), + PLANE_THE_EON_FOG("TheEonFogPlane", "Plane - The Eon Fog"), + PLANE_THE_GREAT_FOREST("TheGreatForestPlane", "Plane - The Great Forest"), + PLANE_THE_ZEPHYR_MAZE_FOG("TheZephyrMazePlane", "Plane - The Zephyr Maze"), + PLANE_TRUGA_JUNGLE("TrugaJunglePlane", "Plane - Truga Jungle"), + PLANE_TRAIL_OF_THE_MAGE_RINGS("TrailOfTheMageRingsPlane", "Plane - Trail of the Mage-Rings"), + PLANE_TURRI_ISLAND("TurriIslandPlane", "Plane - Turri Island"), + PLANE_UNDERCITY_REACHES("UndercityReachesPlane", "Plane - Undercity Reaches"); - Planes(String text) { - this.text = text; + private final String className; + private final String fullName; + + Planes(String className, String fullName) { + this.className = className; + this.fullName = fullName; } @Override public String toString() { - return text; + return className; + } + + public String getClassName() { + return className; + } + + public String getFullName() { + return fullName; + } + + public static Planes fromFullName(String fullName) { + for (Planes p : Planes.values()) { + if (p.fullName.equals(fullName)) { + return p; + } + } + + return null; + } + + public static Planes fromClassName(String className) { + for (Planes p : Planes.values()) { + if (p.className.equals(className)) { + return p; + } + } + + return null; } } diff --git a/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java b/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java index cc3484d9a1..9fa3da2862 100644 --- a/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java +++ b/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java @@ -1,6 +1,9 @@ - package mage.constants; +import mage.abilities.keyword.BestowAbility; +import mage.cards.Card; +import mage.game.Game; + /** * * @author LevelX2 @@ -8,7 +11,8 @@ package mage.constants; public enum SpellAbilityCastMode { NORMAL("Normal"), MADNESS("Madness"), - FLASHBACK("Flashback"); + FLASHBACK("Flashback"), + BESTOW("Bestow"); private final String text; @@ -20,4 +24,12 @@ public enum SpellAbilityCastMode { public String toString() { return text; } + + public Card getTypeModifiedCardObjectCopy(Card card, Game game) { + Card cardCopy = card.copy(); + if (this.equals(BESTOW)) { + BestowAbility.becomeAura(cardCopy); + } + return cardCopy; + } } diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index ddd7ffc040..8fe70263a2 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -119,6 +119,7 @@ public enum SubType { DEVIL("Devil", SubTypeSet.CreatureType), DINOSAUR("Dinosaur", SubTypeSet.CreatureType), // With Ixalan now being spoiled, need this to be selectable DJINN("Djinn", SubTypeSet.CreatureType), + DOG("Dog", SubTypeSet.CreatureType), DRAGON("Dragon", SubTypeSet.CreatureType), DRAKE("Drake", SubTypeSet.CreatureType), DREADNOUGHT("Dreadnought", SubTypeSet.CreatureType), @@ -175,7 +176,6 @@ public enum SubType { HOMUNCULUS("Homunculus", SubTypeSet.CreatureType), HORROR("Horror", SubTypeSet.CreatureType), HORSE("Horse", SubTypeSet.CreatureType), - HOUND("Hound", SubTypeSet.CreatureType), HUMAN("Human", SubTypeSet.CreatureType), HUNTER("Hunter", SubTypeSet.CreatureType), HUTT("Hutt", SubTypeSet.CreatureType, true), // Star Wars @@ -258,6 +258,7 @@ public enum SubType { ORC("Orc", SubTypeSet.CreatureType), ORGG("Orgg", SubTypeSet.CreatureType), ORTOLAN("Ortolan", SubTypeSet.CreatureType, true), + OTTER("Otter", SubTypeSet.CreatureType), OUPHE("Ouphe", SubTypeSet.CreatureType), OX("Ox", SubTypeSet.CreatureType), OYSTER("Oyster", SubTypeSet.CreatureType), @@ -308,6 +309,7 @@ public enum SubType { SHADE("Shade", SubTypeSet.CreatureType), SHAMAN("Shaman", SubTypeSet.CreatureType), SHAPESHIFTER("Shapeshifter", SubTypeSet.CreatureType), + SHARK("Shark", SubTypeSet.CreatureType), SHEEP("Sheep", SubTypeSet.CreatureType), SIREN("Siren", SubTypeSet.CreatureType), SITH("Sith", SubTypeSet.CreatureType), @@ -389,6 +391,7 @@ public enum SubType { ARLINN("Arlinn", SubTypeSet.PlaneswalkerType), ASHIOK("Ashiok", SubTypeSet.PlaneswalkerType), AURRA("Aurra", SubTypeSet.PlaneswalkerType, true), // Star Wars + BASRI("Basri", SubTypeSet.PlaneswalkerType), BOLAS("Bolas", SubTypeSet.PlaneswalkerType), CALIX("Calix", SubTypeSet.PlaneswalkerType), CHANDRA("Chandra", SubTypeSet.PlaneswalkerType), @@ -411,6 +414,7 @@ public enum SubType { KIORA("Kiora", SubTypeSet.PlaneswalkerType), KOTH("Koth", SubTypeSet.PlaneswalkerType), LILIANA("Liliana", SubTypeSet.PlaneswalkerType), + LUKKA("Lukka", SubTypeSet.PlaneswalkerType), NAHIRI("Nahiri", SubTypeSet.PlaneswalkerType), NARSET("Narset", SubTypeSet.PlaneswalkerType), NISSA("Nissa", SubTypeSet.PlaneswalkerType), @@ -492,6 +496,19 @@ public enum SubType { return predicate; } + + public String getIndefiniteArticle() { + if (isVowel(description.charAt(0))) { + return "an"; + } else { + return "a"; + } + } + + private boolean isVowel(char c) { + return "AEIOUaeiou".indexOf(c) != -1; + } + public static SubType fromString(String value) { for (SubType st : SubType.values()) { if (st.toString().equals(value)) { diff --git a/Mage/src/main/java/mage/counters/AbilityCounter.java b/Mage/src/main/java/mage/counters/AbilityCounter.java new file mode 100644 index 0000000000..d63463226b --- /dev/null +++ b/Mage/src/main/java/mage/counters/AbilityCounter.java @@ -0,0 +1,34 @@ +package mage.counters; + +import mage.abilities.Ability; + +/** + * @author TheElk801 + */ +public class AbilityCounter extends Counter { + + private final Ability ability; + + AbilityCounter(Ability ability, int count) { + super(makeName(ability.getRule()), count); + this.ability = ability; + } + + private AbilityCounter(final AbilityCounter counter) { + super(counter); + this.ability = counter.ability; + } + + public Ability getAbility() { + return ability; + } + + @Override + public AbilityCounter copy() { + return new AbilityCounter(this); + } + + private static String makeName(String name) { + return name.replaceAll(" .*<\\/i>", ""); + } +} diff --git a/Mage/src/main/java/mage/counters/BoostCounter.java b/Mage/src/main/java/mage/counters/BoostCounter.java index 3c793ca810..a2b40d7f99 100644 --- a/Mage/src/main/java/mage/counters/BoostCounter.java +++ b/Mage/src/main/java/mage/counters/BoostCounter.java @@ -1,9 +1,8 @@ - - package mage.counters; +import mage.util.CardUtil; + /** - * * @author BetaSteward_at_googlemail.com */ public class BoostCounter extends Counter { @@ -16,7 +15,7 @@ public class BoostCounter extends Counter { } public BoostCounter(int power, int toughness, int count) { - super(String.format("%1$+d/%2$+d", power, toughness), count); + super(CardUtil.getBoostCountAsStr(power, toughness), count); this.power = power; this.toughness = toughness; } diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index 26e85ce9a4..a0fa7e1513 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -1,5 +1,7 @@ package mage.counters; +import mage.abilities.keyword.*; + /** * Enum for counters, names and instances. * @@ -28,12 +30,14 @@ public enum CounterType { CUBE("cube"), CURRENCY("currency"), DEATH("death"), + DEATHTOUCH("deathtouch"), DELAY("delay"), DEPLETION("depletion"), DESPAIR("despair"), DEVOTION("devotion"), DIVINITY("divinity"), DOOM("doom"), + DOUBLE_STRIKE("double strike"), DREAM("dream"), ECHO("echo"), EGG("egg"), @@ -45,8 +49,12 @@ public enum CounterType { FADE("fade"), FATE("fate"), FEATHER("feather"), + FETCH("fetch"), FILIBUSTER("filibuster"), + FIRST_STRIKE("first strike"), FLOOD("flood"), + FLYING("flying"), + FORESHADOW("foreshadow"), FUNK("funk"), FURY("fury"), FUNGUS("fungus"), @@ -58,12 +66,15 @@ public enum CounterType { GROWTH("growth"), HATCHLING("hatchling"), HEALING("healing"), + HEXPROOF("hexproof"), HIT("hit"), HOOFPRINT("hoofprint"), HOUR("hour"), HOURGLASS("hourglass"), HUNGER("hunger"), ICE("ice"), + INCARNATION("incarnation"), + INDESTRUCTIBLE("indestructible"), INFECTION("infection"), INTERVENTION("intervention"), ISOLATION("isolation"), @@ -72,12 +83,14 @@ public enum CounterType { KI("ki"), LANDMARK("landmark"), LEVEL("level"), + LIFELINK("lifelink"), LORE("lore"), LUCK("luck"), LOYALTY("loyalty"), MANIFESTATION("manifestation"), MANNEQUIN("mannequin"), MATRIX("matrix"), + MENACE("menace"), M1M1(new BoostCounter(-1, -1).name), M2M1(new BoostCounter(-2, -1).name), M2M2(new BoostCounter(-2, -2).name), @@ -108,6 +121,7 @@ public enum CounterType { PRESSURE("pressure"), PREY("prey"), PUPA("pupa"), + REACH("reach"), REPAIR("repair"), RUST("rust"), QUEST("quest"), @@ -120,6 +134,7 @@ public enum CounterType { SLIME("slime"), SLUMBER("slumber"), SOOT("soot"), + SOUL("soul"), SPITE("spite"), SPORE("spore"), STORAGE("storage"), @@ -131,11 +146,13 @@ public enum CounterType { TIME("time"), TOWER("tower"), TRAINING("training"), + TRAMPLE("trample"), TRAP("trap"), TREASURE("treasure"), UNITY("unity"), VELOCITY("velocity"), VERSE("verse"), + VIGILANCE("vigilance"), VITALITY("vitality"), VORTEX("vortex"), WAGE("wage"), @@ -192,6 +209,28 @@ public enum CounterType { return new BoostCounter(-2, -1, amount); case M2M2: return new BoostCounter(-2, -2, amount); + case DEATHTOUCH: + return new AbilityCounter(DeathtouchAbility.getInstance(), amount); + case DOUBLE_STRIKE: + return new AbilityCounter(DoubleStrikeAbility.getInstance(), amount); + case FIRST_STRIKE: + return new AbilityCounter(FirstStrikeAbility.getInstance(), amount); + case FLYING: + return new AbilityCounter(FlyingAbility.getInstance(), amount); + case HEXPROOF: + return new AbilityCounter(HexproofAbility.getInstance(), amount); + case INDESTRUCTIBLE: + return new AbilityCounter(IndestructibleAbility.getInstance(), amount); + case LIFELINK: + return new AbilityCounter(LifelinkAbility.getInstance(), amount); + case MENACE: + return new AbilityCounter(new MenaceAbility(), amount); + case REACH: + return new AbilityCounter(ReachAbility.getInstance(), amount); + case TRAMPLE: + return new AbilityCounter(TrampleAbility.getInstance(), amount); + case VIGILANCE: + return new AbilityCounter(VigilanceAbility.getInstance(), amount); default: return new Counter(name, amount); } diff --git a/Mage/src/main/java/mage/counters/Counters.java b/Mage/src/main/java/mage/counters/Counters.java index 303ba78aa5..633a226826 100644 --- a/Mage/src/main/java/mage/counters/Counters.java +++ b/Mage/src/main/java/mage/counters/Counters.java @@ -96,9 +96,18 @@ public class Counters extends HashMap implements Serializable { } public List getBoostCounters() { - return values().stream(). - filter(counter -> counter instanceof BoostCounter). - map(counter -> (BoostCounter) counter). - collect(Collectors.toList()); + return values() + .stream() + .filter(BoostCounter.class::isInstance) + .map(BoostCounter.class::cast) + .collect(Collectors.toList()); + } + + public List getAbilityCounters() { + return values() + .stream() + .filter(AbilityCounter.class::isInstance) + .map(AbilityCounter.class::cast) + .collect(Collectors.toList()); } } diff --git a/Mage/src/main/java/mage/designations/Designation.java b/Mage/src/main/java/mage/designations/Designation.java index 3554e68c50..e8e633c56f 100644 --- a/Mage/src/main/java/mage/designations/Designation.java +++ b/Mage/src/main/java/mage/designations/Designation.java @@ -1,5 +1,9 @@ package mage.designations; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.ObjectColor; @@ -19,11 +23,6 @@ import mage.game.events.ZoneChangeEvent; import mage.util.GameLog; import mage.util.SubTypeList; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.UUID; - /** * @author LevelX2 */ @@ -151,8 +150,8 @@ public abstract class Designation implements MageObject { } @Override - public EnumSet getCardType() { - return emptySet; + public ArrayList getCardType() { + return new ArrayList<>(); } @Override @@ -171,8 +170,8 @@ public abstract class Designation implements MageObject { } @Override - public boolean hasAbility(UUID abilityId, Game game) { - return abilites.containsKey(abilityId); + public boolean hasAbility(Ability ability, Game game) { + return this.getAbilities().contains(ability); } @Override diff --git a/Mage/src/main/java/mage/filter/FilterCard.java b/Mage/src/main/java/mage/filter/FilterCard.java index b9c3e18fa6..f5560b493f 100644 --- a/Mage/src/main/java/mage/filter/FilterCard.java +++ b/Mage/src/main/java/mage/filter/FilterCard.java @@ -1,14 +1,15 @@ package mage.filter; +import mage.cards.Card; +import mage.constants.TargetController; +import mage.filter.predicate.*; +import mage.game.Game; + import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; -import mage.cards.Card; -import mage.constants.TargetController; -import mage.filter.predicate.*; -import mage.game.Game; /** * @author BetaSteward_at_googlemail.com @@ -95,4 +96,9 @@ public class FilterCard extends FilterObject { throw new IllegalArgumentException("Card filter doesn't support controller predicate"); } } + + public FilterCard withMessage(String message) { + this.setMessage(message); + return this; + } } diff --git a/Mage/src/main/java/mage/filter/FilterImpl.java b/Mage/src/main/java/mage/filter/FilterImpl.java index b30a61f4f4..704acf389e 100644 --- a/Mage/src/main/java/mage/filter/FilterImpl.java +++ b/Mage/src/main/java/mage/filter/FilterImpl.java @@ -1,16 +1,16 @@ package mage.filter; -import java.util.ArrayList; -import java.util.List; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; import mage.game.Game; +import java.util.ArrayList; +import java.util.List; + /** - * + * @param * @author BetaSteward_at_googlemail.com * @author North - * @param */ public abstract class FilterImpl implements Filter { @@ -78,5 +78,4 @@ public abstract class FilterImpl implements Filter { public List> getPredicates() { return predicates; } - } diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index 7e71f5e9cf..1727c44168 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.filter; import mage.constants.CardType; @@ -94,6 +89,12 @@ public final class StaticFilters { FILTER_CARD_CREATURE_A.setLockedFilter(true); } + public static final FilterCreatureCard FILTER_CARD_CREATURE_YOUR_HAND = new FilterCreatureCard("a creature card from your hand"); + + static { + FILTER_CARD_CREATURE_YOUR_HAND.setLockedFilter(true); + } + public static final FilterCreatureCard FILTER_CARD_CREATURE_YOUR_GRAVEYARD = new FilterCreatureCard("creature card from your graveyard"); static { @@ -148,6 +149,12 @@ public final class StaticFilters { FILTER_CARD_BASIC_LAND.setLockedFilter(true); } + public static final FilterBasicLandCard FILTER_CARD_BASIC_LANDS = new FilterBasicLandCard("basic land cards"); + + static { + FILTER_CARD_BASIC_LANDS.setLockedFilter(true); + } + public static final FilterBasicLandCard FILTER_CARD_BASIC_LAND_A = new FilterBasicLandCard("a basic land card"); static { @@ -184,6 +191,12 @@ public final class StaticFilters { FILTER_CARD_INSTANT_AND_SORCERY.setLockedFilter(true); } + public static final FilterPermanentCard FILTER_CARD_PERMANENT = new FilterPermanentCard("permanent card"); + + static { + FILTER_CARD_PERMANENT.setLockedFilter(true); + } + public static final FilterPermanent FILTER_PERMANENT = new FilterPermanent(); static { @@ -333,6 +346,13 @@ public final class StaticFilters { FILTER_OPPONENTS_PERMANENT_ARTIFACT_OR_CREATURE.setLockedFilter(true); } + public static final FilterCreaturePermanent FILTER_CREATURE_YOU_DONT_CONTROL = new FilterCreaturePermanent("creature you don't control"); + + static { + FILTER_CREATURE_YOU_DONT_CONTROL.add(TargetController.NOT_YOU.getControllerPredicate()); + FILTER_CREATURE_YOU_DONT_CONTROL.setLockedFilter(true); + } + public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_CREATURE = new FilterControlledCreaturePermanent(); static { @@ -595,13 +615,7 @@ public final class StaticFilters { FILTER_PERMANENT_AURA.setLockedFilter(true); } - public static final FilterPermanent FILTER_PERMANENT_EQUIPMENT = new FilterPermanent(); - - static { - FILTER_PERMANENT_EQUIPMENT.add(CardType.ARTIFACT.getPredicate()); - FILTER_PERMANENT_EQUIPMENT.add(SubType.EQUIPMENT.getPredicate()); - FILTER_PERMANENT_EQUIPMENT.setLockedFilter(true); - } + public static final FilterPermanent FILTER_PERMANENT_EQUIPMENT = new FilterEquipmentPermanent(); public static final FilterPermanent FILTER_PERMANENT_FORTIFICATION = new FilterPermanent(); diff --git a/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java b/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java index 34d729d289..40748c5e52 100644 --- a/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java +++ b/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java @@ -12,7 +12,7 @@ import mage.filter.FilterPermanent; public class FilterEquipmentPermanent extends FilterPermanent { public FilterEquipmentPermanent() { - this("equipment"); + this("Equipment"); } public FilterEquipmentPermanent(String name) { diff --git a/Mage/src/main/java/mage/filter/common/FilterInstantSpell.java b/Mage/src/main/java/mage/filter/common/FilterInstantSpell.java new file mode 100644 index 0000000000..c5c8ef6a88 --- /dev/null +++ b/Mage/src/main/java/mage/filter/common/FilterInstantSpell.java @@ -0,0 +1,27 @@ +package mage.filter.common; + +import mage.constants.CardType; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; + +public class FilterInstantSpell extends FilterSpell { + + public FilterInstantSpell() { + this("instant spell"); + } + + public FilterInstantSpell(String name) { + super(name); + this.add(CardType.INSTANT.getPredicate()); + } + + public FilterInstantSpell(final FilterInstantSpell filter) { + super(filter); + } + + @Override + public FilterInstantSpell copy() { + return new FilterInstantSpell(this); + } + +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/filter/common/FilterOpponentsCreaturePermanent.java b/Mage/src/main/java/mage/filter/common/FilterOpponentsCreaturePermanent.java index ef20a14cd9..c50379ba46 100644 --- a/Mage/src/main/java/mage/filter/common/FilterOpponentsCreaturePermanent.java +++ b/Mage/src/main/java/mage/filter/common/FilterOpponentsCreaturePermanent.java @@ -1,40 +1,40 @@ -package mage.filter.common; - -import mage.constants.SubType; -import mage.constants.TargetController; - -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -/** - * - * @author Styxo - */ -public class FilterOpponentsCreaturePermanent extends FilterCreaturePermanent { - - public FilterOpponentsCreaturePermanent() { - this("creature an opponent controls"); - } - - public FilterOpponentsCreaturePermanent(String name) { - super(name); - this.add(TargetController.OPPONENT.getControllerPredicate()); - - } - - public FilterOpponentsCreaturePermanent(SubType subtype, String name) { - super(subtype, name); - this.add(TargetController.OPPONENT.getControllerPredicate()); - } - - public FilterOpponentsCreaturePermanent(final FilterOpponentsCreaturePermanent filter) { - super(filter); - } - - @Override - public FilterOpponentsCreaturePermanent copy() { - return new FilterOpponentsCreaturePermanent(this); - } -} +package mage.filter.common; + +import mage.constants.SubType; +import mage.constants.TargetController; + +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +/** + * + * @author Styxo + */ +public class FilterOpponentsCreaturePermanent extends FilterCreaturePermanent { + + public FilterOpponentsCreaturePermanent() { + this("creature an opponent controls"); + } + + public FilterOpponentsCreaturePermanent(String name) { + super(name); + this.add(TargetController.OPPONENT.getControllerPredicate()); + + } + + public FilterOpponentsCreaturePermanent(SubType subtype, String name) { + super(subtype, name); + this.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public FilterOpponentsCreaturePermanent(final FilterOpponentsCreaturePermanent filter) { + super(filter); + } + + @Override + public FilterOpponentsCreaturePermanent copy() { + return new FilterOpponentsCreaturePermanent(this); + } +} diff --git a/Mage/src/main/java/mage/filter/common/FilterTeamCreaturePermanent.java b/Mage/src/main/java/mage/filter/common/FilterTeamCreaturePermanent.java index e7f42f0c58..06bec479d0 100644 --- a/Mage/src/main/java/mage/filter/common/FilterTeamCreaturePermanent.java +++ b/Mage/src/main/java/mage/filter/common/FilterTeamCreaturePermanent.java @@ -1,40 +1,40 @@ -package mage.filter.common; - -import mage.constants.SubType; -import mage.constants.TargetController; - -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -/** - * - * @author TheElk801 - */ -public class FilterTeamCreaturePermanent extends FilterCreaturePermanent { - - public FilterTeamCreaturePermanent() { - this("creature your team controls"); - } - - public FilterTeamCreaturePermanent(String name) { - super(name); - this.add(TargetController.TEAM.getControllerPredicate()); - - } - - public FilterTeamCreaturePermanent(SubType subtype, String name) { - super(subtype, name); - this.add(TargetController.TEAM.getControllerPredicate()); - } - - public FilterTeamCreaturePermanent(final FilterTeamCreaturePermanent filter) { - super(filter); - } - - @Override - public FilterTeamCreaturePermanent copy() { - return new FilterTeamCreaturePermanent(this); - } -} +package mage.filter.common; + +import mage.constants.SubType; +import mage.constants.TargetController; + +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +/** + * + * @author TheElk801 + */ +public class FilterTeamCreaturePermanent extends FilterCreaturePermanent { + + public FilterTeamCreaturePermanent() { + this("creature your team controls"); + } + + public FilterTeamCreaturePermanent(String name) { + super(name); + this.add(TargetController.TEAM.getControllerPredicate()); + + } + + public FilterTeamCreaturePermanent(SubType subtype, String name) { + super(subtype, name); + this.add(TargetController.TEAM.getControllerPredicate()); + } + + public FilterTeamCreaturePermanent(final FilterTeamCreaturePermanent filter) { + super(filter); + } + + @Override + public FilterTeamCreaturePermanent copy() { + return new FilterTeamCreaturePermanent(this); + } +} diff --git a/Mage/src/main/java/mage/filter/common/FilterTeamPermanent.java b/Mage/src/main/java/mage/filter/common/FilterTeamPermanent.java index f7a770e848..6fb625d397 100644 --- a/Mage/src/main/java/mage/filter/common/FilterTeamPermanent.java +++ b/Mage/src/main/java/mage/filter/common/FilterTeamPermanent.java @@ -1,41 +1,41 @@ -package mage.filter.common; - -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.FilterPermanent; - -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -/** - * - * @author TheElk801 - */ -public class FilterTeamPermanent extends FilterPermanent { - - public FilterTeamPermanent() { - this("permanent your team controls"); - } - - public FilterTeamPermanent(String name) { - super(name); - this.add(TargetController.TEAM.getControllerPredicate()); - - } - - public FilterTeamPermanent(SubType subtype, String name) { - super(subtype, name); - this.add(TargetController.TEAM.getControllerPredicate()); - } - - public FilterTeamPermanent(final FilterTeamPermanent filter) { - super(filter); - } - - @Override - public FilterTeamPermanent copy() { - return new FilterTeamPermanent(this); - } -} +package mage.filter.common; + +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; + +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +/** + * + * @author TheElk801 + */ +public class FilterTeamPermanent extends FilterPermanent { + + public FilterTeamPermanent() { + this("permanent your team controls"); + } + + public FilterTeamPermanent(String name) { + super(name); + this.add(TargetController.TEAM.getControllerPredicate()); + + } + + public FilterTeamPermanent(SubType subtype, String name) { + super(subtype, name); + this.add(TargetController.TEAM.getControllerPredicate()); + } + + public FilterTeamPermanent(final FilterTeamPermanent filter) { + super(filter); + } + + @Override + public FilterTeamPermanent copy() { + return new FilterTeamPermanent(this); + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/AbilityPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/AbilityPredicate.java index 08a9cd27e9..23baecae34 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/AbilityPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/AbilityPredicate.java @@ -1,4 +1,3 @@ - package mage.filter.predicate.mageobject; import mage.MageObject; @@ -9,26 +8,25 @@ import mage.filter.predicate.Predicate; import mage.game.Game; /** - * * @author North */ public class AbilityPredicate implements Predicate { - private final Class abilityClass; + private final Class abilityClass; - public AbilityPredicate(Class abilityClass) { + public AbilityPredicate(Class abilityClass) { this.abilityClass = abilityClass; } @Override public boolean apply(MageObject input, Game game) { Abilities abilities; - if (input instanceof Card){ - abilities = ((Card)input).getAbilities(game); + if (input instanceof Card) { + abilities = ((Card) input).getAbilities(game); } else { abilities = input.getAbilities(); } - return abilities.stream().anyMatch(ability -> ability.getClass().equals(abilityClass)); + return abilities.stream().anyMatch(abilityClass::isInstance); } diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/ConvertedManaCostParityPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/ConvertedManaCostParityPredicate.java new file mode 100644 index 0000000000..273f01e087 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/ConvertedManaCostParityPredicate.java @@ -0,0 +1,27 @@ +package mage.filter.predicate.mageobject; + +import mage.MageObject; +import mage.filter.predicate.Predicate; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public enum ConvertedManaCostParityPredicate implements Predicate { + EVEN(0), + ODD(1); + private final int parity; + + ConvertedManaCostParityPredicate(int parity) { + this.parity = parity; + } + + @Override + public boolean apply(MageObject input, Game game) { + return input.getConvertedManaCost() % 2 == parity; + } + + @Override + public String toString() { + return "ConvertedManaCostParity" + super.toString(); + }} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java index b80f7766a7..fbc763c73a 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java @@ -31,15 +31,20 @@ public class NamePredicate implements Predicate { // A split card has the chosen name if one of its two names matches the chosen name. if (input instanceof SplitCard) { return CardUtil.haveSameNames(name, ((SplitCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || - CardUtil.haveSameNames(name, ((SplitCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames); + CardUtil.haveSameNames(name, ((SplitCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || + CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames); } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { SplitCard card = (SplitCard) ((Spell) input).getCard(); return CardUtil.haveSameNames(name, card.getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || - CardUtil.haveSameNames(name, card.getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames); + CardUtil.haveSameNames(name, card.getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || + CardUtil.haveSameNames(name, card.getName(), this.ignoreMtgRuleForEmptyNames); + } else if (input instanceof Spell && ((Spell) input).isFaceDown(game)) { + // face down spells don't have names, so it's not equal, see https://github.com/magefree/mage/issues/6569 + return false; } else { if (name.contains(" // ")) { String leftName = name.substring(0, name.indexOf(" // ")); - String rightName = name.substring(name.indexOf(" // ") + 4, name.length()); + String rightName = name.substring(name.indexOf(" // ") + 4); return CardUtil.haveSameNames(leftName, input.getName(), this.ignoreMtgRuleForEmptyNames) || CardUtil.haveSameNames(rightName, input.getName(), this.ignoreMtgRuleForEmptyNames); } else { @@ -50,6 +55,6 @@ public class NamePredicate implements Predicate { @Override public String toString() { - return "Name(" + name + ')'; + return "Name (" + name + ')'; } } diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java index e5c060ea0d..97cfe4d37f 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java @@ -1,8 +1,6 @@ - package mage.filter.predicate.mageobject; import java.util.UUID; -import mage.MageObject; import mage.abilities.Mode; import mage.filter.predicate.Predicate; import mage.game.Controllable; @@ -37,18 +35,6 @@ public class NumberOfTargetsPredicate implements Predicate { return true; } } -// Spell spell = game.getStack().getSpell(input.getId()); -// if (spell != null) { -// int numberOfTargets = 0; -// for (Mode mode : spell.getSpellAbility().getModes().getSelectedModes()) { -// for (Target target : mode.getTargets()) { -// numberOfTargets += target.getTargets().size(); -// } -// } -// if (numberOfTargets == targets) { -// return true; -// } -// } return false; } diff --git a/Mage/src/main/java/mage/filter/predicate/other/AuraCardCanAttachToLKIPermanentId.java b/Mage/src/main/java/mage/filter/predicate/other/AuraCardCanAttachToLKIPermanentId.java index 6d4b211bcf..9f4f7040d0 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/AuraCardCanAttachToLKIPermanentId.java +++ b/Mage/src/main/java/mage/filter/predicate/other/AuraCardCanAttachToLKIPermanentId.java @@ -1,42 +1,42 @@ - - -package mage.filter.predicate.other; - -import java.util.UUID; -import mage.cards.Card; -import mage.filter.Filter; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.Target; - -/** - * - * @author jeffwadsworth - */ -public class AuraCardCanAttachToLKIPermanentId implements Predicate { - - private final UUID toBeCheckedLKIPermanentId; - - public AuraCardCanAttachToLKIPermanentId(UUID toBeCheckedLKIPermanentId) { - this.toBeCheckedLKIPermanentId = toBeCheckedLKIPermanentId; - } - - @Override - public boolean apply(Card input, Game game) { - final Permanent permanent = game.getPermanentOrLKIBattlefield(toBeCheckedLKIPermanentId); - Filter filter; - for (Target target : input.getSpellAbility().getTargets()) { - filter = target.getFilter(); - if (filter.match(permanent, game)) { - return true; - } - } - return false; - } - - @Override - public String toString() { - return "AuraCardCanAttachToLKIPermanentId(" + toBeCheckedLKIPermanentId + ')'; - } + + +package mage.filter.predicate.other; + +import java.util.UUID; +import mage.cards.Card; +import mage.filter.Filter; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.Target; + +/** + * + * @author jeffwadsworth + */ +public class AuraCardCanAttachToLKIPermanentId implements Predicate { + + private final UUID toBeCheckedLKIPermanentId; + + public AuraCardCanAttachToLKIPermanentId(UUID toBeCheckedLKIPermanentId) { + this.toBeCheckedLKIPermanentId = toBeCheckedLKIPermanentId; + } + + @Override + public boolean apply(Card input, Game game) { + final Permanent permanent = game.getPermanentOrLKIBattlefield(toBeCheckedLKIPermanentId); + Filter filter; + for (Target target : input.getSpellAbility().getTargets()) { + filter = target.getFilter(); + if (filter.match(permanent, game)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "AuraCardCanAttachToLKIPermanentId(" + toBeCheckedLKIPermanentId + ')'; + } } \ No newline at end of file diff --git a/Mage/src/main/java/mage/filter/predicate/other/FaceDownCastablePredicate.java b/Mage/src/main/java/mage/filter/predicate/other/FaceDownCastablePredicate.java new file mode 100644 index 0000000000..e7d3d55f5b --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/other/FaceDownCastablePredicate.java @@ -0,0 +1,24 @@ +package mage.filter.predicate.other; + +import mage.abilities.keyword.MorphAbility; +import mage.cards.Card; +import mage.filter.predicate.Predicate; +import mage.game.Game; + +/** + * @author JayDi85 + */ +public enum FaceDownCastablePredicate implements Predicate { + instance; + + @Override + public boolean apply(Card input, Game game) { + // is card able to cast as face down + return input.getAbilities(game).containsClass(MorphAbility.class); + } + + @Override + public String toString() { + return "Face-down"; + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/other/FaceDownPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/FaceDownPredicate.java index c6079cf9d4..9edc49598c 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/FaceDownPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/FaceDownPredicate.java @@ -1,9 +1,10 @@ - package mage.filter.predicate.other; +import mage.abilities.keyword.MorphAbility; import mage.cards.Card; import mage.filter.predicate.Predicate; import mage.game.Game; +import mage.game.permanent.Permanent; /** * @author North @@ -13,7 +14,15 @@ public enum FaceDownPredicate implements Predicate { @Override public boolean apply(Card input, Game game) { - return input.isFaceDown(game); + if (game.inCheckPlayableState()) { + // Check for cost reduction of possible face down spell to cast + if (input != null && !(input instanceof Permanent)) { + return input.getAbilities().containsClass(MorphAbility.class); + } + return false; + } else { + return input.isFaceDown(game); + } } @Override diff --git a/Mage/src/main/java/mage/filter/predicate/other/TargetsOnlyOnePlayerPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/TargetsOnlyOnePlayerPredicate.java index 08433ae089..7d5a6820d2 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/TargetsOnlyOnePlayerPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/TargetsOnlyOnePlayerPredicate.java @@ -1,48 +1,48 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.filter.predicate.other; - -import java.util.UUID; -import mage.MageObject; -import mage.abilities.Mode; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.game.Game; -import mage.game.stack.StackObject; -import mage.players.Player; -import mage.target.Target; - -/** - * - * @author jeffwadsworth - */ -public class TargetsOnlyOnePlayerPredicate implements ObjectSourcePlayerPredicate> { - - public TargetsOnlyOnePlayerPredicate() { - } - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - StackObject object = game.getStack().getStackObject(input.getObject().getId()); - if (object != null) { - for (UUID modeId : object.getStackAbility().getModes().getSelectedModes()) { - Mode mode = object.getStackAbility().getModes().get(modeId); - for (Target target : mode.getTargets()) { - if (target.getTargets().size() == 1) { // only one player targeted - Player player = game.getPlayer(target.getFirstTarget()); - return player != null; - } - } - } - } - return false; - } - - @Override - public String toString() { - return "that targets only one player"; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.filter.predicate.other; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Mode; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.Target; + +/** + * + * @author jeffwadsworth + */ +public class TargetsOnlyOnePlayerPredicate implements ObjectSourcePlayerPredicate> { + + public TargetsOnlyOnePlayerPredicate() { + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + StackObject object = game.getStack().getStackObject(input.getObject().getId()); + if (object != null) { + for (UUID modeId : object.getStackAbility().getModes().getSelectedModes()) { + Mode mode = object.getStackAbility().getModes().get(modeId); + for (Target target : mode.getTargets()) { + if (target.getTargets().size() == 1) { // only one player targeted + Player player = game.getPlayer(target.getFirstTarget()); + return player != null; + } + } + } + } + return false; + } + + @Override + public String toString() { + return "that targets only one player"; + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/other/TargetsPlayerPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/TargetsPlayerPredicate.java index af8f556bc4..45abc29cf3 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/TargetsPlayerPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/TargetsPlayerPredicate.java @@ -1,48 +1,48 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.filter.predicate.other; - -import java.util.UUID; -import mage.MageObject; -import mage.abilities.Mode; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.game.Game; -import mage.game.stack.StackObject; -import mage.players.Player; -import mage.target.Target; - -/** - * - * @author jeffwadsworth - */ -public class TargetsPlayerPredicate implements ObjectSourcePlayerPredicate> { - - public TargetsPlayerPredicate() { - } - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - StackObject object = game.getStack().getStackObject(input.getObject().getId()); - if (object != null) { - for (UUID modeId : object.getStackAbility().getModes().getSelectedModes()) { - Mode mode = object.getStackAbility().getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetId : target.getTargets()) { - Player player = game.getPlayer(targetId); - return player != null; - } - } - } - } - return false; - } - - @Override - public String toString() { - return "that targets a player"; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.filter.predicate.other; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Mode; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.Target; + +/** + * + * @author jeffwadsworth + */ +public class TargetsPlayerPredicate implements ObjectSourcePlayerPredicate> { + + public TargetsPlayerPredicate() { + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + StackObject object = game.getStack().getStackObject(input.getObject().getId()); + if (object != null) { + for (UUID modeId : object.getStackAbility().getModes().getSelectedModes()) { + Mode mode = object.getStackAbility().getModes().get(modeId); + for (Target target : mode.getTargets()) { + for (UUID targetId : target.getTargets()) { + Player player = game.getPlayer(targetId); + return player != null; + } + } + } + } + return false; + } + + @Override + public String toString() { + return "that targets a player"; + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/AnotherEnchantedPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/AnotherEnchantedPredicate.java index 956e595f77..04d3fa4e6f 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/AnotherEnchantedPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/AnotherEnchantedPredicate.java @@ -1,30 +1,30 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.filter.predicate.permanent; - -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * Filters out the id of the enchanted object, if the source is an enchantment - * - * @author LevelX2 - */ -public class AnotherEnchantedPredicate implements ObjectSourcePlayerPredicate> { - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - Permanent enchantment = game.getPermanentOrLKIBattlefield(input.getSourceId()); - return enchantment != null && !input.getObject().getId().equals(enchantment.getAttachedTo()); - } - - @Override - public String toString() { - return "Another enchanted"; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.filter.predicate.permanent; + +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * Filters out the id of the enchanted object, if the source is an enchantment + * + * @author LevelX2 + */ +public class AnotherEnchantedPredicate implements ObjectSourcePlayerPredicate> { + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Permanent enchantment = game.getPermanentOrLKIBattlefield(input.getSourceId()); + return enchantment != null && !input.getObject().getId().equals(enchantment.getAttachedTo()); + } + + @Override + public String toString() { + return "Another enchanted"; + } +} diff --git a/Mage/src/main/java/mage/game/CardState.java b/Mage/src/main/java/mage/game/CardState.java index 7ce8d79f5c..ad89663885 100644 --- a/Mage/src/main/java/mage/game/CardState.java +++ b/Mage/src/main/java/mage/game/CardState.java @@ -19,6 +19,7 @@ public class CardState implements Serializable { protected Counters counters; protected Abilities abilities; protected boolean lostAllAbilities; + protected boolean melded; private static final Map emptyInfo = new HashMap<>(); private static final Abilities emptyAbilities = new AbilitiesImpl<>(); @@ -41,6 +42,7 @@ public class CardState implements Serializable { } } this.lostAllAbilities = state.lostAllAbilities; + this.melded = state.melded; } public CardState copy() { @@ -117,4 +119,12 @@ public class CardState implements Serializable { this.lostAllAbilities = lostAllAbilities; } + public boolean isMelded() { + return melded; + } + + public void setMelded(boolean melded) { + this.melded = melded; + } + } diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java index 3a3cdd1b81..8a3a983ea4 100644 --- a/Mage/src/main/java/mage/game/Game.java +++ b/Mage/src/main/java/mage/game/Game.java @@ -1,11 +1,15 @@ package mage.game; +import java.io.Serializable; +import java.util.*; +import java.util.stream.Collectors; import mage.MageItem; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.TriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffects; import mage.abilities.effects.PreventionEffectData; @@ -41,10 +45,6 @@ import mage.players.Players; import mage.util.MessageToClient; import mage.util.functions.ApplyToPermanent; -import java.io.Serializable; -import java.util.*; -import java.util.stream.Collectors; - public interface Game extends MageItem, Serializable { MatchType getGameType(); @@ -300,8 +300,8 @@ public interface Game extends MageItem, Serializable { /** * Creates and fires an damage prevention event * - * @param damageEvent damage event that will be replaced (instanceof check - * will be done) + * @param damageEvent damage event that will be replaced (instanceof + * check will be done) * @param source ability that's the source of the prevention effect * @param game * @param amountToPrevent max preventable amount @@ -312,9 +312,10 @@ public interface Game extends MageItem, Serializable { /** * Creates and fires an damage prevention event * - * @param event damage event that will be replaced (instanceof check will be - * done) - * @param source ability that's the source of the prevention effect + * @param event damage event that will be replaced (instanceof + * check will be done) + * @param source ability that's the source of the prevention + * effect * @param game * @param preventAllDamage true if there is no limit to the damage that can * be prevented @@ -372,7 +373,16 @@ public interface Game extends MageItem, Serializable { void addCommander(Commander commander); - void addPermanent(Permanent permanent); + /** + * Adds a permanent to the battlefield + * + * @param permanent + * @param createOrder upcounting number from state about the create order of + * all permanents. Can equal for multiple permanents, if + * they go to battlefield at the same time. If the value + * is set to 0, a next number will be set automatically. + */ + void addPermanent(Permanent permanent, int createOrder); // priority method void sendPlayerAction(PlayerAction playerAction, UUID playerId, Object data); @@ -398,6 +408,8 @@ public interface Game extends MageItem, Serializable { UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility, Ability source); + UUID fireReflexiveTriggeredAbility(ReflexiveTriggeredAbility reflexiveAbility, Ability source); + void applyEffects(); boolean checkStateAndTriggered(); @@ -408,7 +420,7 @@ public interface Game extends MageItem, Serializable { boolean endTurn(Ability source); - int doAction(MageAction action); + int doAction(MageAction action, UUID sourceId); //game transaction methods void saveState(boolean bookmark); @@ -485,4 +497,8 @@ public interface Game extends MageItem, Serializable { default Set getCommandersIds(Player player) { return getCommandersIds(player, CommanderCardType.ANY); } + + void setGameStopped(boolean gameStopped); + + boolean isGameStopped(); } diff --git a/Mage/src/main/java/mage/game/GameCanadianHighlanderImpl.java b/Mage/src/main/java/mage/game/GameCanadianHighlanderImpl.java index ab7408114a..f306f4cf20 100644 --- a/Mage/src/main/java/mage/game/GameCanadianHighlanderImpl.java +++ b/Mage/src/main/java/mage/game/GameCanadianHighlanderImpl.java @@ -12,7 +12,7 @@ import java.util.UUID; public abstract class GameCanadianHighlanderImpl extends GameImpl { public GameCanadianHighlanderImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 100); } public GameCanadianHighlanderImpl(final GameCanadianHighlanderImpl game) { diff --git a/Mage/src/main/java/mage/game/GameCommanderImpl.java b/Mage/src/main/java/mage/game/GameCommanderImpl.java index 6731f4f39f..ade44a7727 100644 --- a/Mage/src/main/java/mage/game/GameCommanderImpl.java +++ b/Mage/src/main/java/mage/game/GameCommanderImpl.java @@ -1,12 +1,11 @@ package mage.game; -import java.util.Map; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.CommanderReplacementEffect; import mage.abilities.effects.common.cost.CommanderCostModification; +import mage.abilities.keyword.CompanionAbility; import mage.cards.Card; import mage.constants.MultiplayerAttackOption; import mage.constants.PhaseStep; @@ -18,6 +17,9 @@ import mage.players.Player; import mage.watchers.common.CommanderInfoWatcher; import mage.watchers.common.CommanderPlaysCountWatcher; +import java.util.Map; +import java.util.UUID; + public abstract class GameCommanderImpl extends GameImpl { // private final Map mulliganedCards = new HashMap<>(); @@ -29,8 +31,8 @@ public abstract class GameCommanderImpl extends GameImpl { protected boolean startingPlayerSkipsDraw = true; - public GameCommanderImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + public GameCommanderImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife, int startingSize) { + super(attackOption, range, mulligan, startLife, startingSize); } public GameCommanderImpl(final GameCommanderImpl game) { @@ -53,10 +55,14 @@ public abstract class GameCommanderImpl extends GameImpl { Player player = getPlayer(playerId); if (player != null) { // add new commanders - for (UUID id : player.getSideboard()) { - Card commander = this.getCard(id); - if (commander != null) { - addCommander(commander, player); + for (UUID cardId : player.getSideboard()) { + Card card = this.getCard(cardId); + if (card != null) { + // Check for companions. If it is the only card in the sideboard, it is the commander, not a companion. + if (player.getSideboard().size() > 1 && card.getAbilities(this).stream().anyMatch(ability -> ability instanceof CompanionAbility)) { + continue; + } + addCommander(card, player); } } diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index d9cd7acba9..a7d8590b98 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -6,12 +6,14 @@ import mage.abilities.*; import mage.abilities.common.AttachableToRestrictedAbility; import mage.abilities.common.CantHaveMoreThanAmountCountersSourceAbility; import mage.abilities.common.SagaAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffects; import mage.abilities.effects.Effect; import mage.abilities.effects.PreventionEffectData; import mage.abilities.effects.common.CopyEffect; import mage.abilities.keyword.BestowAbility; +import mage.abilities.keyword.CompanionAbility; import mage.abilities.keyword.MorphAbility; import mage.abilities.keyword.TransformAbility; import mage.abilities.mana.DelayedTriggeredManaAbility; @@ -30,6 +32,7 @@ import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.combat.Combat; @@ -57,6 +60,7 @@ import mage.target.Target; import mage.target.TargetCard; import mage.target.TargetPermanent; import mage.target.TargetPlayer; +import mage.util.CardUtil; import mage.util.GameLog; import mage.util.MessageToClient; import mage.util.RandomUtil; @@ -104,11 +108,13 @@ public abstract class GameImpl implements Game, Serializable { // game states to allow player rollback protected transient Map gameStatesRollBack = new HashMap<>(); protected boolean executingRollback; + protected int turnToGoToForRollback; protected Date startTime; protected Date endTime; protected UUID startingPlayerId; protected UUID winnerId; + protected boolean gameStopped = false; protected RangeOfInfluence range; protected Mulligan mulligan; @@ -129,6 +135,7 @@ public abstract class GameImpl implements Game, Serializable { private int priorityTime; private final int startLife; + private final int startingSize; protected PlayerList playerList; // auto-generated from state, don't copy // infinite loop check (no copy of this attributes neccessary) @@ -143,7 +150,7 @@ public abstract class GameImpl implements Game, Serializable { // temporary store for income concede commands, don't copy private final LinkedList concedingPlayers = new LinkedList<>(); - public GameImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { + public GameImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife, int startingSize) { this.id = UUID.randomUUID(); this.range = range; this.mulligan = mulligan; @@ -151,6 +158,7 @@ public abstract class GameImpl implements Game, Serializable { this.state = new GameState(); this.startLife = startLife; this.executingRollback = false; + this.startingSize = startingSize; initGameDefaultWatchers(); } @@ -180,6 +188,8 @@ public abstract class GameImpl implements Game, Serializable { this.saveGame = game.saveGame; this.startLife = game.startLife; this.enterWithCounters.putAll(game.enterWithCounters); + this.startingSize = game.startingSize; + this.gameStopped = game.gameStopped; } @Override @@ -689,6 +699,12 @@ public abstract class GameImpl implements Game, Serializable { return savedStates.size(); } + /** + * Warning, for inner usage only, use player.restoreState as much as possible instead + * + * @param bookmark + * @param context additional information for error message + */ @Override public void restoreState(int bookmark, String context) { if (!simulation && !this.hasEnded()) { // if player left or game is over no undo is possible - this could lead to wrong winner @@ -761,27 +777,6 @@ public abstract class GameImpl implements Game, Serializable { } } - @Override - public void resume() { - playerList = state.getPlayerList(state.getActivePlayerId()); - Player player = getPlayer(playerList.get()); - boolean wasPaused = state.isPaused(); - state.resume(); - if (!checkIfGameIsOver()) { - fireInformEvent("Turn " + state.getTurnNum()); - if (checkStopOnTurnOption()) { - return; - } - state.getTurn().resumePlay(this, wasPaused); - if (!isPaused() && !checkIfGameIsOver()) { - endOfTurn(); - player = playerList.getNext(this, true); - state.setTurnNum(state.getTurnNum() + 1); - } - } - play(player.getId()); - } - protected void play(UUID nextPlayerId) { if (!isPaused() && !checkIfGameIsOver()) { playerList = state.getPlayerList(nextPlayerId); @@ -870,13 +865,8 @@ public abstract class GameImpl implements Game, Serializable { boolean skipTurn = false; do { if (executingRollback) { - executingRollback = false; + rollbackTurnsExecution(turnToGoToForRollback); player = getPlayer(state.getActivePlayerId()); - for (Player playerObject : getPlayers().values()) { - if (playerObject.isInGame()) { - playerObject.abortReset(); - } - } } else { state.setActivePlayerId(player.getId()); saveRollBackGameState(); @@ -898,6 +888,27 @@ public abstract class GameImpl implements Game, Serializable { return true; } + @Override + public void resume() { + playerList = state.getPlayerList(state.getActivePlayerId()); + Player player = getPlayer(playerList.get()); + boolean wasPaused = state.isPaused(); + state.resume(); + if (!checkIfGameIsOver()) { + fireInformEvent("Turn " + state.getTurnNum()); + if (checkStopOnTurnOption()) { + return; + } + state.getTurn().resumePlay(this, wasPaused); + if (!isPaused() && !checkIfGameIsOver()) { + endOfTurn(); + player = playerList.getNext(this, true); + state.setTurnNum(state.getTurnNum() + 1); + } + } + play(player.getId()); + } + private boolean checkStopOnTurnOption() { if (gameOptions.stopOnTurn != null && gameOptions.stopAtStep == PhaseStep.UNTAP) { if (gameOptions.stopOnTurn.equals(state.getTurnNum())) { @@ -929,6 +940,39 @@ public abstract class GameImpl implements Game, Serializable { return; } + // Handle companions + Map playerCompanionMap = new HashMap<>(); + for (Player player : state.getPlayers().values()) { + // Make a list of legal companions present in the sideboard + Set potentialCompanions = new HashSet<>(); + for (Card card : player.getSideboard().getUniqueCards(this)) { + for (Ability ability : card.getAbilities(this)) { + if (ability instanceof CompanionAbility) { + CompanionAbility companionAbility = (CompanionAbility) ability; + if (companionAbility.isLegal(new HashSet<>(player.getLibrary().getCards(this)), startingSize)) { + potentialCompanions.add(card); + break; + } + } + } + } + // Choose a companion from the list of legal companions + for (Card card : potentialCompanions) { + if (player.chooseUse(Outcome.Benefit, "Use " + card.getLogName() + " as your companion?", null, this)) { + playerCompanionMap.put(player, card); + break; + } + } + } + + // Announce companions and set the companion effect + playerCompanionMap.forEach((player, companion) -> { + if (companion != null) { + this.informPlayers(player.getLogName() + " has chosen " + companion.getLogName() + " as their companion."); + this.getState().getCompanion().update(player.getName() + "'s companion", new CardsImpl(companion)); + } + }); + //20091005 - 103.1 if (!gameOptions.skipInitShuffling) { //don't shuffle in test mode for card injection on top of player's libraries for (Player player : state.getPlayers().values()) { @@ -943,7 +987,7 @@ public abstract class GameImpl implements Game, Serializable { targetPlayer.setTargetName("starting player"); if (choosingPlayerId != null) { choosingPlayer = this.getPlayer(choosingPlayerId); - if (choosingPlayer != null && !choosingPlayer.isInGame()) { + if (choosingPlayer != null && !choosingPlayer.canRespond()) { choosingPlayer = null; } } @@ -968,7 +1012,7 @@ public abstract class GameImpl implements Game, Serializable { if (startingPlayerId == null) { // choose any available player as starting player for (Player player : state.getPlayers().values()) { - if (player.isInGame()) { + if (player.canRespond()) { startingPlayerId = player.getId(); break; } @@ -992,7 +1036,7 @@ public abstract class GameImpl implements Game, Serializable { player.initLife(this.getLife()); } if (!gameOptions.testMode) { - player.drawCards(startingHandSize, this); + player.drawCards(startingHandSize, null, this); } } @@ -1041,7 +1085,7 @@ public abstract class GameImpl implements Game, Serializable { // 20180408 - 901.5 if (gameOptions.planeChase) { - Plane plane = Plane.getRandomPlane(); + Plane plane = Plane.createRandomPlane(); plane.setControllerId(startingPlayerId); addPlane(plane, null, startingPlayerId); state.setPlaneChase(this, gameOptions.planeChase); @@ -1122,7 +1166,7 @@ public abstract class GameImpl implements Game, Serializable { while (!hasEnded()) { playerId = players[RandomUtil.nextInt(players.length)]; Player player = getPlayer(playerId); - if (player != null && player.isInGame()) { + if (player != null && player.canRespond()) { fireInformEvent(state.getPlayer(playerId).getLogName() + " won the toss"); return player.getId(); } @@ -1209,7 +1253,7 @@ public abstract class GameImpl implements Game, Serializable { if (player != null) { int bookmark = player.getStoredBookmark(); if (bookmark != -1) { - restoreState(bookmark, "undo"); + player.restoreState(bookmark, "undo", this); player.setStoredBookmark(-1); fireUpdatePlayersEvent(); } @@ -1384,7 +1428,7 @@ public abstract class GameImpl implements Game, Serializable { if (spell != null) { if (spell.getCommandedBy() != null) { UUID commandedBy = spell.getCommandedBy(); - UUID spellControllerId = null; + UUID spellControllerId; if (commandedBy.equals(spell.getControllerId())) { spellControllerId = spell.getSpellAbility().getFirstTarget(); // i.e. resolved spell is Word of Command } else { @@ -1528,8 +1572,8 @@ public abstract class GameImpl implements Game, Serializable { /** * @param plane * @param sourceObject - * @param toPlayerId controller and owner of the plane (may only be one per - * game..) + * @param toPlayerId controller and owner of the plane (may only be one + * per game..) * @return boolean - whether the plane was added successfully or not */ @Override @@ -1570,9 +1614,12 @@ public abstract class GameImpl implements Game, Serializable { } @Override - public void addPermanent(Permanent permanent) { + public void addPermanent(Permanent permanent, int createOrder) { + if (createOrder == 0) { + createOrder = getState().getNextPermanentOrderNumber(); + } + permanent.setCreateOrder(createOrder); getBattlefield().addPermanent(permanent); - permanent.setCreateOrder(getState().getNextPermanentOrderNumber()); } @Override @@ -1603,12 +1650,13 @@ public abstract class GameImpl implements Game, Serializable { newBluePrint.reset(this); //getState().addCard(permanent); - if (copyFromPermanent.isMorphed() || copyFromPermanent.isManifested()) { + if (copyFromPermanent.isMorphed() || copyFromPermanent.isManifested() + || copyFromPermanent.isFaceDown(this)) { MorphAbility.setPermanentToFaceDownCreature(newBluePrint); } newBluePrint.assignNewId(); if (copyFromPermanent.isTransformed()) { - TransformAbility.transform(newBluePrint, newBluePrint.getSecondCardFace(), this); + TransformAbility.transform(newBluePrint, newBluePrint.getSecondCardFace(), this, source); } } if (applier != null) { @@ -1710,6 +1758,13 @@ public abstract class GameImpl implements Game, Serializable { return newAbility.getId(); } + @Override + public UUID fireReflexiveTriggeredAbility(ReflexiveTriggeredAbility reflexiveAbility, Ability source) { + UUID uuid = this.addDelayedTriggeredAbility(reflexiveAbility, source); + this.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId())); + return uuid; + } + /** * 116.5. Each time a player would get priority, the game first performs all * applicable state-based actions as a single event (see rule 704, @@ -1752,7 +1807,7 @@ public abstract class GameImpl implements Game, Serializable { state.getTriggers().checkStateTriggers(this); for (UUID playerId : state.getPlayerList(state.getActivePlayerId())) { Player player = getPlayer(playerId); - while (player.isInGame()) { // player can die or win caused by triggered abilities or leave the game + while (player.canRespond()) { // player can die or win caused by triggered abilities or leave the game List abilities = state.getTriggered(player.getId()); if (abilities.isEmpty()) { break; @@ -1802,12 +1857,51 @@ public abstract class GameImpl implements Game, Serializable { for (Player player : state.getPlayers().values()) { if (!player.hasLost() && ((player.getLife() <= 0 && player.canLoseByZeroOrLessLife()) - || player.isEmptyDraw() + || player.getLibrary().isEmptyDraw() || player.getCounters().getCount(CounterType.POISON) >= 10)) { player.lost(this); } } + // If a commander is in a graveyard or in exile and that card was put into that zone + // since the last time state-based actions were checked, its owner may put it into the command zone. + for (Player player : state.getPlayers().values()) { + Set commanderIds = getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER); + if (commanderIds.isEmpty()) { + continue; + } + Set commanders = new HashSet<>(); + Cards toMove = new CardsImpl(); + player.getGraveyard() + .stream() + .filter(commanderIds::contains) + .map(this::getCard) + .filter(Objects::nonNull) + .forEach(commanders::add); + commanderIds + .stream() + .map(uuid -> getExile().getCard(uuid, this)) + .filter(Objects::nonNull) + .forEach(commanders::add); + commanders.removeIf(card -> state.checkCommanderShouldStay(card, this)); + for (Card card : commanders) { + Zone currentZone = this.getState().getZone(card.getId()); + String currentZoneInfo = (currentZone == null ? "(error)" : "(" + currentZone.name() + ")"); + if (player.chooseUse(Outcome.Benefit, "Move " + card.getIdName() + + " to the command zone or leave it in current zone " + currentZoneInfo + "?", "You can only make this choice once per object", + "Move to command", "Leave in current zone " + currentZoneInfo, null, this)) { + toMove.add(card); + } else { + state.setCommanderShouldStay(card, this); + } + } + if (toMove.isEmpty()) { + continue; + } + player.moveCards(toMove, Zone.COMMAND, null, this); + somethingHappened = true; + } + // 704.5e If a copy of a spell is in a zone other than the stack, it ceases to exist. If a copy of a card is in any zone other than the stack or the battlefield, it ceases to exist. // (Isochron Scepter) 12/1/2004: If you don't want to cast the copy, you can choose not to; the copy ceases to exist the next time state-based actions are checked. Iterator copiedCards = this.getState().getCopiedCards().iterator(); @@ -1854,6 +1948,7 @@ public abstract class GameImpl implements Game, Serializable { List legendary = new ArrayList<>(); List worldEnchantment = new ArrayList<>(); + List usePowerInsteadOfToughnessForDamageLethalityFilters = getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters(); for (Permanent perm : getBattlefield().getAllActivePermanents()) { if (perm.isCreature()) { //20091005 - 704.5f @@ -1863,10 +1958,21 @@ public abstract class GameImpl implements Game, Serializable { continue; } } //20091005 - 704.5g/704.5h - else if (perm.getToughness().getValue() <= perm.getDamage() || perm.isDeathtouched()) { - if (perm.destroy(null, this, false)) { - somethingHappened = true; - continue; + else { + /* + * for handling Zilortha, Strength Incarnate: + * 2020-04-17: Any time the game is checking whether damage is lethal or if a creature should be destroyed for having lethal damage marked on it, use the power of your creatures rather than their toughness to check the damage against. This includes being assigned trample damage, damage from Flame Spill, and so on. + */ + boolean usePowerInsteadOfToughnessForDamageLethality = usePowerInsteadOfToughnessForDamageLethalityFilters.stream() + .anyMatch(filter -> filter.match(perm, this)); + int lethalDamageThreshold = usePowerInsteadOfToughnessForDamageLethality + ? // Zilortha, Strength Incarnate, 2020-04-17: A creature with 0 power isn’t destroyed unless it has at least 1 damage marked on it. + Math.max(perm.getPower().getValue(), 1) : perm.getToughness().getValue(); + if (lethalDamageThreshold <= perm.getDamage() || perm.isDeathtouched()) { + if (perm.destroy(null, this, false)) { + somethingHappened = true; + continue; + } } } if (perm.getPairedCard() != null) { @@ -1961,8 +2067,6 @@ public abstract class GameImpl implements Game, Serializable { if (card != null && card.isCreature()) { UUID wasAttachedTo = perm.getAttachedTo(); perm.attachTo(null, this); - //moved to mage.game.permanent.PermanentImpl::detachAllAttachments - //BestowAbility.becomeCreature(perm, this); fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, wasAttachedTo, perm.getId(), perm.getControllerId())); } else if (movePermanentToGraveyardWithInfo(perm)) { somethingHappened = true; @@ -2162,30 +2266,43 @@ public abstract class GameImpl implements Game, Serializable { } } } - //704.5m - World Enchantments + //704.5k - World Enchantments if (worldEnchantment.size() > 1) { int newestCard = -1; + Set controllerIdOfNewest = new HashSet<>(); Permanent newestPermanent = null; for (Permanent permanent : worldEnchantment) { if (newestCard == -1) { newestCard = permanent.getCreateOrder(); newestPermanent = permanent; + controllerIdOfNewest.clear(); + controllerIdOfNewest.add(permanent.getControllerId()); } else if (newestCard < permanent.getCreateOrder()) { newestCard = permanent.getCreateOrder(); newestPermanent = permanent; + controllerIdOfNewest.clear(); + controllerIdOfNewest.add(permanent.getControllerId()); } else if (newestCard == permanent.getCreateOrder()) { + // In the event of a tie for the shortest amount of time, all are put into their owners’ graveyards. This is called the “world rule.” newestPermanent = null; + controllerIdOfNewest.add(permanent.getControllerId()); } } - for (Permanent permanent : worldEnchantment) { - if (!Objects.equals(newestPermanent, permanent)) { - movePermanentToGraveyardWithInfo(permanent); - somethingHappened = true; + for (UUID controllerId : controllerIdOfNewest) { + PlayerList newestPermanentControllerRange = state.getPlayersInRange(controllerId, this); + + // 801.12 The "world rule" applies to a permanent only if other world permanents are within its controller's range of influence. + for (Permanent permanent : worldEnchantment) { + if (newestPermanentControllerRange.contains(permanent.getControllerId()) + && !Objects.equals(newestPermanent, permanent)) { + movePermanentToGraveyardWithInfo(permanent); + somethingHappened = true; + } } } } - //TODO: implement the rest + //TODO: implement the rest return somethingHappened; } @@ -2518,6 +2635,10 @@ public abstract class GameImpl implements Game, Serializable { for (ContinuousEffect effect : getContinuousEffects().getLayeredEffects(this)) { if (effect.hasLayer(Layer.ControlChangingEffects_2)) { for (Ability ability : getContinuousEffects().getLayeredEffectAbilities(effect)) { + if (effect.getTargetPointer().getTargets(this, ability).contains(perm.getId())) { + effect.discard(); + continue Effects; + } for (Target target : ability.getTargets()) { for (UUID targetId : target.getTargets()) { if (targetId.equals(perm.getId())) { @@ -2527,10 +2648,14 @@ public abstract class GameImpl implements Game, Serializable { } } } + } } } } + for (Card card : toOutside) { + rememberLKI(card.getId(), Zone.BATTLEFIELD, card); + } // needed to send event that permanent leaves the battlefield to allow non stack effects to execute player.moveCards(toOutside, Zone.OUTSIDE, null, this); // triggered abilities that don't use the stack have to be executed @@ -2585,7 +2710,7 @@ public abstract class GameImpl implements Game, Serializable { for (Player aplayer : state.getPlayers().values()) { if (!aplayer.hasLeft() && !addedAgain) { addedAgain = true; - Plane plane = Plane.getRandomPlane(); + Plane plane = Plane.createRandomPlane(); plane.setControllerId(aplayer.getId()); addPlane(plane, null, aplayer.getId()); } @@ -2683,7 +2808,7 @@ public abstract class GameImpl implements Game, Serializable { } if (amountToPrevent != Integer.MAX_VALUE) { // set remaining amount - result.setRemainingAmount(amountToPrevent -= result.getPreventedDamage()); + result.setRemainingAmount(amountToPrevent - result.getPreventedDamage()); } MageObject damageSource = game.getObject(damageEvent.getSourceId()); MageObject preventionSource = game.getObject(source.getSourceId()); @@ -2946,28 +3071,10 @@ public abstract class GameImpl implements Game, Serializable { throw new IllegalArgumentException("Command zone supports in commander test games"); } - // warning, permanents go to battlefield without resolve, continuus effects must be init for (PermanentCard permanentCard : battlefield) { - permanentCard.setZone(Zone.BATTLEFIELD, this); - permanentCard.setOwnerId(ownerId); - PermanentCard newPermanent = new PermanentCard(permanentCard.getCard(), ownerId, this); - getPermanentsEntering().put(newPermanent.getId(), newPermanent); - newPermanent.entersBattlefield(newPermanent.getId(), this, Zone.OUTSIDE, false); - getBattlefield().addPermanent(newPermanent); - getPermanentsEntering().remove(newPermanent.getId()); - newPermanent.removeSummoningSickness(); - if (permanentCard.isTapped()) { - newPermanent.setTapped(true); - } - - // init effects on static abilities (init continuous effects, warning, game state contains copy) - for (ContinuousEffect effect : this.getState().getContinuousEffects().getLayeredEffects(this)) { - Optional ability = this.getState().getContinuousEffects().getLayeredEffectAbilities(effect).stream().findFirst(); - if (ability.isPresent() && newPermanent.getId().equals(ability.get().getSourceId())) { - effect.init(ability.get(), this, activePlayerId); // game is not setup yet, game.activePlayer is null -- need direct id - } - } + CardUtil.putCardOntoBattlefieldWithEffects(this, permanentCard, player); } + applyEffects(); } } @@ -2987,9 +3094,9 @@ public abstract class GameImpl implements Game, Serializable { } @Override - public int doAction(MageAction action) { + public int doAction(MageAction action, UUID sourceId) { //actions.add(action); - int value = action.doAction(this); + int value = action.doAction(sourceId, this); // score += action.getScore(scorePlayer); return value; } @@ -3151,32 +3258,46 @@ public abstract class GameImpl implements Game, Serializable { return turnToGoTo > 0 && gameStatesRollBack.containsKey(turnToGoTo); } + private void rollbackTurnsExecution(int turnToGoToForRollback) { + GameState restore = gameStatesRollBack.get(turnToGoToForRollback); + if (restore != null) { + informPlayers(GameLog.getPlayerRequestColoredText("Player request: Rolling back to start of turn " + restore.getTurnNum())); + state.restoreForRollBack(restore); + playerList.setCurrent(state.getPlayerByOrderId()); + // Reset temporary created bookmarks because no longer valid after rollback + savedStates.clear(); + gameStates.clear(); + // because restore uses the objects without copy each copy the state again + gameStatesRollBack.put(getTurnNum(), state.copy()); + + for (Player playerObject : getPlayers().values()) { + if (playerObject.isInGame()) { + playerObject.abortReset(); + } + } + } + executingRollback = false; + } + @Override public synchronized void rollbackTurns(int turnsToRollback) { - if (gameOptions.rollbackTurnsAllowed) { + if (gameOptions.rollbackTurnsAllowed && !executingRollback) { int turnToGoTo = getTurnNum() - turnsToRollback; if (turnToGoTo < 1 || !gameStatesRollBack.containsKey(turnToGoTo)) { informPlayers(GameLog.getPlayerRequestColoredText("Player request: It's not possible to rollback " + turnsToRollback + " turn(s)")); } else { - GameState restore = gameStatesRollBack.get(turnToGoTo); - if (restore != null) { - informPlayers(GameLog.getPlayerRequestColoredText("Player request: Rolling back to start of turn " + restore.getTurnNum())); - state.restoreForRollBack(restore); - playerList.setCurrent(state.getPlayerByOrderId()); - // Reset temporary created bookmarks because no longer valid after rollback - savedStates.clear(); - gameStates.clear(); - // because restore uses the objects without copy each copy the state again - gameStatesRollBack.put(getTurnNum(), state.copy()); - executingRollback = true; - for (Player playerObject : getPlayers().values()) { - if (playerObject.isHuman() && playerObject.isInGame()) { - playerObject.resetStoredBookmark(this); - playerObject.abort(); - playerObject.resetPlayerPassedActions(); - } + executingRollback = true; + turnToGoToForRollback = turnToGoTo; + for (Player playerObject : getPlayers().values()) { + if (playerObject.isHuman() && playerObject.canRespond()) { + playerObject.resetStoredBookmark(this); + playerObject.abort(); + playerObject.resetPlayerPassedActions(); } - fireUpdatePlayersEvent(); + } + fireUpdatePlayersEvent(); + if (gameOptions.testMode && gameStopped) { // in test mode execute rollback directly + rollbackTurnsExecution(turnToGoToForRollback); } } } @@ -3266,4 +3387,14 @@ public abstract class GameImpl implements Game, Serializable { return player.getCommandersIds(); } + @Override + public void setGameStopped(boolean gameStopped) { + this.gameStopped = gameStopped; + } + + @Override + public boolean isGameStopped() { + return gameStopped; + } + } diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 5f61dc0dc1..7ab0ae2076 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -1,6 +1,11 @@ package mage.game; +import java.io.Serializable; +import java.util.*; +import static java.util.Collections.emptyList; +import java.util.stream.Collectors; import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.*; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffects; @@ -10,6 +15,7 @@ import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.Zone; import mage.designations.Designation; +import mage.filter.common.FilterCreaturePermanent; import mage.game.combat.Combat; import mage.game.combat.CombatGroup; import mage.game.command.Command; @@ -34,10 +40,6 @@ import mage.util.ThreadLocalStringBuilder; import mage.watchers.Watcher; import mage.watchers.Watchers; -import java.io.Serializable; -import java.util.*; -import java.util.stream.Collectors; - /** * @author BetaSteward_at_googlemail.com *

@@ -59,6 +61,7 @@ public class GameState implements Serializable, Copyable { // revealed cards >, will be reset if all players pass priority private final Revealed revealed; private final Map lookedAt = new HashMap<>(); + private final Revealed companion; private DelayedTriggeredAbilities delayed; private SpecialActions specialActions; @@ -95,6 +98,8 @@ public class GameState implements Serializable, Copyable { private Map zoneChangeCounter = new HashMap<>(); private Map copiedCards = new HashMap<>(); private int permanentOrderNumber; + private final Map usePowerInsteadOfToughnessForDamageLethalityFilters = new HashMap<>(); + private Set commandersToStay = new HashSet<>(); // commanders that do not go back to command zone private int applyEffectsCounter; // Upcounting number of each applyEffects execution @@ -106,6 +111,7 @@ public class GameState implements Serializable, Copyable { command = new Command(); exile = new Exile(); revealed = new Revealed(); + companion = new Revealed(); battlefield = new Battlefield(); effects = new ContinuousEffects(); triggers = new TriggeredAbilities(); @@ -123,6 +129,7 @@ public class GameState implements Serializable, Copyable { this.choosingPlayerId = state.choosingPlayerId; this.revealed = state.revealed.copy(); this.lookedAt.putAll(state.lookedAt); + this.companion = state.companion.copy(); this.gameOver = state.gameOver; this.paused = state.paused; @@ -172,6 +179,9 @@ public class GameState implements Serializable, Copyable { this.copiedCards.putAll(state.copiedCards); this.permanentOrderNumber = state.permanentOrderNumber; this.applyEffectsCounter = state.applyEffectsCounter; + state.usePowerInsteadOfToughnessForDamageLethalityFilters.forEach((uuid, filter) + -> this.usePowerInsteadOfToughnessForDamageLethalityFilters.put(uuid, filter.copy())); + this.commandersToStay.addAll(state.commandersToStay); } public void restoreForRollBack(GameState state) { @@ -217,6 +227,9 @@ public class GameState implements Serializable, Copyable { this.copiedCards = state.copiedCards; this.permanentOrderNumber = state.permanentOrderNumber; this.applyEffectsCounter = state.applyEffectsCounter; + state.usePowerInsteadOfToughnessForDamageLethalityFilters.forEach((uuid, filter) + -> this.usePowerInsteadOfToughnessForDamageLethalityFilters.put(uuid, filter.copy())); + this.commandersToStay = state.commandersToStay; } @Override @@ -473,6 +486,10 @@ public class GameState implements Serializable, Copyable { return lookedAt.get(playerId); } + public Revealed getCompanion() { + return companion; + } + public void clearRevealed() { revealed.clear(); } @@ -481,6 +498,10 @@ public class GameState implements Serializable, Copyable { lookedAt.clear(); } + public void clearCompanion() { + companion.clear(); + } + public Turn getTurn() { return turn; } @@ -586,7 +607,6 @@ public class GameState implements Serializable, Copyable { delayed.removeEndOfTurnAbilities(game); exile.cleanupEndOfTurnZones(game); game.applyEffects(); - effects.incYourTurnNumPlayed(game); } public void addEffect(ContinuousEffect effect, Ability source) { @@ -604,7 +624,6 @@ public class GameState implements Serializable, Copyable { // public void addMessage(String message) { // this.messages.add(message); // } - /** * Returns a list of all players of the game ignoring range or if a player * has lost or left the game. @@ -778,7 +797,7 @@ public class GameState implements Serializable, Copyable { for (Map.Entry> entry : eventsByKey.entrySet()) { Set movedCards = new LinkedHashSet<>(); Set movedTokens = new LinkedHashSet<>(); - for (Iterator it = entry.getValue().iterator(); it.hasNext(); ) { + for (Iterator it = entry.getValue().iterator(); it.hasNext();) { GameEvent event = it.next(); ZoneChangeEvent castEvent = (ZoneChangeEvent) event; UUID targetId = castEvent.getTargetId(); @@ -850,7 +869,7 @@ public class GameState implements Serializable, Copyable { } /** - * Abilities that are applied to other objects or applie for a certain time + * Abilities that are applied to other objects or applied for a certain time * span * * @param ability @@ -871,6 +890,7 @@ public class GameState implements Serializable, Copyable { // TODO: add sources for triggers - the same way as in addEffect: sources this.triggers.add((TriggeredAbility) ability, sourceId, attachedTo); } + List watcherList = new ArrayList<>(ability.getWatchers()); // Workaround to prevent ConcurrentModificationException, not clear to me why this is happening now for (Watcher watcher : watcherList) { // TODO: Check that watcher for commanderAbility (where attachedTo = null) also work correctly @@ -878,6 +898,7 @@ public class GameState implements Serializable, Copyable { watcher.setSourceId(attachedTo == null ? ability.getSourceId() : attachedTo.getId()); watchers.add(watcher); } + for (Ability sub : ability.getSubAbilities()) { addAbility(sub, sourceId, attachedTo); } @@ -930,6 +951,13 @@ public class GameState implements Serializable, Copyable { public void addDelayedTriggeredAbility(DelayedTriggeredAbility ability) { this.delayed.add(ability); + + List watcherList = new ArrayList<>(ability.getWatchers()); // Workaround to prevent ConcurrentModificationException, not clear to me why this is happening now + for (Watcher watcher : watcherList) { + watcher.setControllerId(ability.getControllerId()); + watcher.setSourceId(ability.getSourceId()); + this.watchers.add(watcher); + } } public void removeDelayedTriggeredAbility(UUID abilityId) { @@ -1067,6 +1095,7 @@ public class GameState implements Serializable, Copyable { isPlaneChase = false; revealed.clear(); lookedAt.clear(); + companion.clear(); turnNum = 0; stepNum = 0; extraTurn = false; @@ -1081,6 +1110,7 @@ public class GameState implements Serializable, Copyable { zones.clear(); simultaneousEvents.clear(); copiedCards.clear(); + usePowerInsteadOfToughnessForDamageLethalityFilters.clear(); permanentOrderNumber = 0; } @@ -1197,4 +1227,23 @@ public class GameState implements Serializable, Copyable { return applyEffectsCounter; } + public void addPowerInsteadOfToughnessForDamageLethalityFilter(UUID source, FilterCreaturePermanent filter) { + usePowerInsteadOfToughnessForDamageLethalityFilters.put(source, filter); + } + + public List getActivePowerInsteadOfToughnessForDamageLethalityFilters() { + return usePowerInsteadOfToughnessForDamageLethalityFilters.isEmpty() ? emptyList() : getBattlefield().getAllActivePermanents().stream() + .map(Card::getId) + .filter(usePowerInsteadOfToughnessForDamageLethalityFilters::containsKey) + .map(usePowerInsteadOfToughnessForDamageLethalityFilters::get) + .collect(Collectors.toList()); + } + + boolean checkCommanderShouldStay(Card card, Game game) { + return commandersToStay.stream().anyMatch(mor -> mor.refersTo(card, game)); + } + + void setCommanderShouldStay(Card card, Game game) { + commandersToStay.add(new MageObjectReference(card, game)); + } } diff --git a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java index 0a64d1b381..97bccb6b1a 100644 --- a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java +++ b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java @@ -32,7 +32,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl { protected boolean startingPlayerSkipsDraw = true; public GameTinyLeadersImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { - super(attackOption, range, mulligan, startLife); + super(attackOption, range, mulligan, startLife, 50); } public GameTinyLeadersImpl(final GameTinyLeadersImpl game) { diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java index d7bac4124b..1964e08b47 100644 --- a/Mage/src/main/java/mage/game/ZonesHandler.java +++ b/Mage/src/main/java/mage/game/ZonesHandler.java @@ -27,7 +27,7 @@ public final class ZonesHandler { public static boolean cast(ZoneChangeInfo info, Game game) { if (maybeRemoveFromSourceZone(info, game)) { - placeInDestinationZone(info, game); + placeInDestinationZone(info, game, 0); // create a group zone change event if a card is moved to stack for casting (it's always only one card, but some effects check for group events (one or more xxx)) Set cards = new HashSet<>(); Set tokens = new HashSet<>(); @@ -57,7 +57,7 @@ public final class ZonesHandler { ZoneChangeInfo info = itr.next(); MeldCard card = game.getMeldCard(info.event.getTargetId()); // Copies should be handled as normal cards. - if (card != null && !card.isMelded() && !card.isCopy()) { + if (card != null && !card.isMelded(game) && !card.isCopy()) { ZoneChangeInfo.Unmelded unmelded = new ZoneChangeInfo.Unmelded(info, game); if (unmelded.subInfo.isEmpty()) { itr.remove(); @@ -67,8 +67,13 @@ public final class ZonesHandler { } } zoneChangeInfos.removeIf(zoneChangeInfo -> !maybeRemoveFromSourceZone(zoneChangeInfo, game)); + int createOrder = 0; for (ZoneChangeInfo zoneChangeInfo : zoneChangeInfos) { - placeInDestinationZone(zoneChangeInfo, game); + if (createOrder == 0 && Zone.BATTLEFIELD.equals(zoneChangeInfo.event.getToZone())) { + // All permanents go to battlefield at the same time (=create order) + createOrder = game.getState().getNextPermanentOrderNumber(); + } + placeInDestinationZone(zoneChangeInfo, game, createOrder); if (game.getPhase() != null) { // moving cards to zones before game started does not need events game.addSimultaneousEvent(zoneChangeInfo.event); } @@ -76,14 +81,14 @@ public final class ZonesHandler { return zoneChangeInfos; } - private static void placeInDestinationZone(ZoneChangeInfo info, Game game) { + private static void placeInDestinationZone(ZoneChangeInfo info, Game game, int createOrder) { // Handle unmelded cards if (info instanceof ZoneChangeInfo.Unmelded) { ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info; Zone toZone = null; for (ZoneChangeInfo subInfo : unmelded.subInfo) { toZone = subInfo.event.getToZone(); - placeInDestinationZone(subInfo, game); + placeInDestinationZone(subInfo, game, createOrder); } // We arbitrarily prefer the bottom half card. This should never be relevant. if (toZone != null) { @@ -161,7 +166,7 @@ public final class ZonesHandler { break; case BATTLEFIELD: Permanent permanent = event.getTarget(); - game.addPermanent(permanent); + game.addPermanent(permanent, createOrder); game.getPermanentsEntering().remove(permanent.getId()); break; default: @@ -171,7 +176,7 @@ public final class ZonesHandler { game.setZone(event.getTargetId(), event.getToZone()); if (targetCard instanceof MeldCard && cards != null) { if (event.getToZone() != Zone.BATTLEFIELD) { - ((MeldCard) targetCard).setMelded(false); + ((MeldCard) targetCard).setMelded(false, game); } for (Card card : cards.getCards(game)) { game.setZone(card.getId(), event.getToZone()); @@ -229,6 +234,7 @@ public final class ZonesHandler { if (!game.replaceEvent(event)) { Zone fromZone = event.getFromZone(); if (event.getToZone() == Zone.BATTLEFIELD) { + // prepare card and permanent // If needed take attributes from the spell (e.g. color of spell was changed) card = takeAttributesFromSpell(card, event, game); // controlling player can be replaced so use event player now @@ -241,15 +247,18 @@ public final class ZonesHandler { } else { permanent = new PermanentCard(card, event.getPlayerId(), game); } + + // put onto battlefield with possible counters game.getPermanentsEntering().put(permanent.getId(), permanent); card.checkForCountersToAdd(permanent, game); - permanent.setTapped( - info instanceof ZoneChangeInfo.Battlefield && ((ZoneChangeInfo.Battlefield) info).tapped); - permanent.setFaceDown(info.faceDown, game); + permanent.setTapped(info instanceof ZoneChangeInfo.Battlefield + && ((ZoneChangeInfo.Battlefield) info).tapped); + permanent.setFaceDown(info.faceDown, game); if (info.faceDown) { card.setFaceDown(false, game); } + // make sure the controller of all continuous effects of this card are switched to the current controller game.setScopeRelevant(true); game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId()); @@ -290,7 +299,7 @@ public final class ZonesHandler { } TargetCard target = new TargetCard(Zone.ALL, new FilterCard(message)); target.setRequired(true); - while (player.isInGame() && cards.size() > 1) { + while (player.canRespond() && cards.size() > 1) { player.choose(Outcome.Neutral, cards, target, game); UUID targetObjectId = target.getFirstTarget(); order.add(cards.get(targetObjectId, game)); diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index c502dae73b..a3c25098d5 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -1,5 +1,7 @@ package mage.game.combat; +import java.io.Serializable; +import java.util.*; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.RequirementEffect; @@ -33,9 +35,6 @@ import mage.util.Copyable; import mage.util.trace.TraceUtil; import org.apache.log4j.Logger; -import java.io.Serializable; -import java.util.*; - /** * @author BetaSteward_at_googlemail.com */ @@ -306,7 +305,7 @@ public class Combat implements Serializable, Copyable { || getAttackers().size() <= 1) { return; } - boolean canBand = attacker.getAbilities().containsKey(BandingAbility.getInstance().getId()); + boolean canBand = attacker.hasAbility(BandingAbility.getInstance(), game); List bandsWithOther = new ArrayList<>(); for (Ability ability : attacker.getAbilities()) { if (ability.getClass().equals(BandsWithOtherAbility.class)) { @@ -346,8 +345,8 @@ public class Combat implements Serializable, Copyable { if (game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, attackingPlayerId, attackingPlayerId)) || (!canBand && !canBandWithOther) || !player.chooseUse(Outcome.Benefit, - "Do you wish to " + (isBanded ? "band " + attacker.getLogName() - + " with another " : "form a band with " + attacker.getLogName() + " and an ") + "Do you wish to " + (isBanded ? "band " + attacker.getLogName() + + " with another " : "form a band with " + attacker.getLogName() + " and an ") + "attacking creature?", null, game)) { break; } @@ -390,7 +389,7 @@ public class Combat implements Serializable, Copyable { permanent.addBandedCard(creatureId); attacker.addBandedCard(targetId); if (canBand) { - if (!permanent.getAbilities().containsKey(BandingAbility.getInstance().getId())) { + if (!permanent.hasAbility(BandingAbility.getInstance(), game)) { filter.add(new AbilityPredicate(BandingAbility.class)); canBandWithOther = false; } @@ -412,7 +411,8 @@ public class Combat implements Serializable, Copyable { if (!isBanded) { return; } - StringBuilder sb = new StringBuilder(player.getLogName()).append(" formed a band with ").append((attacker.getBandedCards().size() + 1) + " creatures: "); + StringBuilder sb = new StringBuilder(player.getLogName()).append(" formed a band with ") + .append(attacker.getBandedCards().size()).append(1).append(" creatures: "); sb.append(attacker.getLogName()); for (UUID id : attacker.getBandedCards()) { sb.append(", "); @@ -433,7 +433,8 @@ public class Combat implements Serializable, Copyable { // check if a creature has to attack for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(creature, false, game).entrySet()) { RequirementEffect effect = entry.getKey(); - if (effect.mustAttack(game)) { + if (effect.mustAttack(game) + && checkAttackRestrictions(player, game)) { // needed for Goad Effect mustAttack = true; for (Ability ability : entry.getValue()) { UUID defenderId = effect.mustAttackDefender(ability, game); @@ -564,7 +565,7 @@ public class Combat implements Serializable, Copyable { * Handle the blocker selection process * * @param blockController player that controls how to block, if null the - * defender is the controller + * defender is the controller * @param game */ public void selectBlockers(Player blockController, Game game) { @@ -743,15 +744,15 @@ public class Combat implements Serializable, Copyable { } /** - * 509.1c The defending player checks each creature they control to - * see whether it's affected by any requirements (effects that say a - * creature must block, or that it must block if some condition is met). If - * the number of requirements that are being obeyed is fewer than the - * maximum possible number of requirements that could be obeyed without - * disobeying any restrictions, the declaration of blockers is illegal. If a - * creature can't block unless a player pays a cost, that player is not - * required to pay that cost, even if blocking with that creature would - * increase the number of requirements being obeyed. + * 509.1c The defending player checks each creature they control to see + * whether it's affected by any requirements (effects that say a creature + * must block, or that it must block if some condition is met). If the + * number of requirements that are being obeyed is fewer than the maximum + * possible number of requirements that could be obeyed without disobeying + * any restrictions, the declaration of blockers is illegal. If a creature + * can't block unless a player pays a cost, that player is not required to + * pay that cost, even if blocking with that creature would increase the + * number of requirements being obeyed. *

*

* Example: A player controls one creature that "blocks if able" and another @@ -1289,7 +1290,8 @@ public class Combat implements Serializable, Copyable { if (attacker != null) { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, creatureId, playerId))) { if (addAttackerToCombat(creatureId, defenderId, game)) { - if (!attacker.getAbilities().containsKey(VigilanceAbility.getInstance().getId()) && !attacker.getAbilities().containsKey(JohanVigilanceAbility.getInstance().getId())) { + if (!attacker.hasAbility(VigilanceAbility.getInstance(), game) + && !attacker.hasAbility(JohanVigilanceAbility.getInstance(), game)) { if (!attacker.isTapped()) { attacker.setTapped(true); attackersTappedByAttack.add(attacker.getId()); @@ -1377,7 +1379,7 @@ public class Combat implements Serializable, Copyable { * @param playerId * @param game * @param solveBanding check whether also add creatures banded with - * attackerId + * attackerId */ public void addBlockingGroup(UUID blockerId, UUID attackerId, UUID playerId, Game game, boolean solveBanding) { Permanent blocker = game.getPermanent(blockerId); diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index be2f139af6..616eebe772 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -1,32 +1,25 @@ - package mage.game.combat; -import java.io.Serializable; -import java.util.*; -import java.util.stream.Stream; - import mage.abilities.Ability; import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility; import mage.abilities.common.ControllerDivideCombatDamageAbility; import mage.abilities.common.DamageAsThoughNotBlockedAbility; -import mage.abilities.keyword.BandingAbility; -import mage.abilities.keyword.BandsWithOtherAbility; -import mage.abilities.keyword.CantBlockAloneAbility; -import mage.abilities.keyword.DeathtouchAbility; -import mage.abilities.keyword.DoubleStrikeAbility; -import mage.abilities.keyword.FirstStrikeAbility; -import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.*; import mage.constants.AsThoughEffectType; import mage.constants.Outcome; import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.Copyable; +import java.io.Serializable; +import java.util.*; +import java.util.stream.Stream; + /** - * * @author BetaSteward_at_googlemail.com */ public class CombatGroup implements Serializable, Copyable { @@ -42,9 +35,9 @@ public class CombatGroup implements Serializable, Copyable { protected boolean defenderIsPlaneswalker; /** - * @param defenderId the player that controls the defending permanents + * @param defenderId the player that controls the defending permanents * @param defenderIsPlaneswalker is the defending permanent a planeswalker - * @param defendingPlayerId regular controller of the defending permanents + * @param defendingPlayerId regular controller of the defending permanents */ public CombatGroup(UUID defenderId, boolean defenderIsPlaneswalker, UUID defendingPlayerId) { this.defenderId = defenderId; @@ -173,7 +166,7 @@ public class CombatGroup implements Serializable, Copyable { Player player = game.getPlayer(defenderAssignsCombatDamage(game) ? defendingPlayerId : attacker.getControllerId()); if ((attacker.getAbilities().containsKey(DamageAsThoughNotBlockedAbility.getInstance().getId()) && player.chooseUse(Outcome.Damage, "Do you wish to assign damage for " - + attacker.getLogName() + " as though it weren't blocked?", null, game)) || + + attacker.getLogName() + " as though it weren't blocked?", null, game)) || game.getContinuousEffects().asThough(attacker.getId(), AsThoughEffectType.DAMAGE_NOT_BLOCKED , null, attacker.getControllerId(), game) != null) { // for handling creatures like Thorn Elemental @@ -226,7 +219,7 @@ public class CombatGroup implements Serializable, Copyable { * Determines if permanent can damage in current (First Strike or not) * combat damage step * - * @param perm Permanent to check + * @param perm Permanent to check * @param first First strike or common combat damage step * @return */ @@ -271,12 +264,7 @@ public class CombatGroup implements Serializable, Copyable { if (blocked && canDamage(attacker, first)) { int damage = getDamageValueFromPermanent(attacker, game); if (hasTrample(attacker)) { - int lethalDamage; - if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { - lethalDamage = 1; - } else { - lethalDamage = Math.max(blocker.getToughness().getValue() - blocker.getDamage(), 0); - } + int lethalDamage = getLethalDamage(blocker, attacker, game); if (lethalDamage >= damage) { blocker.markDamage(damage, attacker.getId(), game, true, true); } else { @@ -325,12 +313,7 @@ public class CombatGroup implements Serializable, Copyable { for (UUID blockerId : new ArrayList<>(blockerOrder)) { // prevent ConcurrentModificationException Permanent blocker = game.getPermanent(blockerId); if (blocker != null) { - int lethalDamage; - if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { - lethalDamage = 1; - } else { - lethalDamage = Math.max(blocker.getToughness().getValue() - blocker.getDamage(), 0); - } + int lethalDamage = getLethalDamage(blocker, attacker, game); if (lethalDamage >= damage) { if (!oldRuleDamage) { assigned.put(blockerId, damage); @@ -418,7 +401,7 @@ public class CombatGroup implements Serializable, Copyable { } if (damage > 0) { Player defendingPlayer = game.getPlayer(defendingPlayerId); - if (defendingPlayer.isInGame()) { + if (defendingPlayer != null) { defendingPlayer.damage(damage, attacker.getId(), game, true, true); } } @@ -475,9 +458,9 @@ public class CombatGroup implements Serializable, Copyable { * Damages attacking creatures by a creature that blocked several ones * Damages only attackers as blocker was damage in * {@link #singleBlockerDamage}. - * + *

* Handles abilities like "{this} an block any number of creatures.". - * + *

* Blocker damage for blockers blocking single creatures is handled in the * single/multi blocker methods, so this shouldn't be used anymore. * @@ -501,7 +484,7 @@ public class CombatGroup implements Serializable, Copyable { * Damages attacking creatures by a creature that blocked several ones * Damages only attackers as blocker was damage in either * {@link #singleBlockerDamage} or {@link #multiBlockerDamage}. - * + *

* Handles abilities like "{this} an block any number of creatures.". * * @param first @@ -521,12 +504,7 @@ public class CombatGroup implements Serializable, Copyable { for (UUID attackerId : attackerOrder) { Permanent attacker = game.getPermanent(attackerId); if (attacker != null) { - int lethalDamage; - if (blocker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { - lethalDamage = 1; - } else { - lethalDamage = Math.max(attacker.getToughness().getValue() - attacker.getDamage(), 0); - } + int lethalDamage = getLethalDamage(attacker, blocker, game); if (lethalDamage >= damage) { if (!oldRuleDamage) { assigned.put(attackerId, damage); @@ -566,7 +544,7 @@ public class CombatGroup implements Serializable, Copyable { } } else { Player defender = game.getPlayer(defenderId); - if (defender.isInGame()) { + if (defender != null) { defender.damage(amount, attacker.getId(), game, true, true); } } @@ -586,9 +564,8 @@ public class CombatGroup implements Serializable, Copyable { } /** - * * @param blockerId - * @param playerId controller of the blocking creature + * @param playerId controller of the blocking creature * @param game */ public void addBlocker(UUID blockerId, UUID playerId, Game game) { @@ -605,7 +582,7 @@ public class CombatGroup implements Serializable, Copyable { * event. * * @param blockerId - * @param playerId controller of the blocking creature + * @param playerId controller of the blocking creature * @param game */ public void addBlockerToGroup(UUID blockerId, UUID playerId, Game game) { @@ -659,7 +636,7 @@ public class CombatGroup implements Serializable, Copyable { attackerPerms.add(game.getPermanent(attackerId)); } UUID attackerId = player.chooseAttackerOrder(attackerPerms, game); - if (!player.isInGame()) { + if (attackerId == null) { break; } attackerOrder.add(attackerId); @@ -805,7 +782,7 @@ public class CombatGroup implements Serializable, Copyable { /** * There are effects, that set an attacker to be blocked. Therefore this * setter can be used. - * + *

* This method lacks a band check, use setBlocked(blocked, game) instead. * * @param blocked @@ -936,4 +913,29 @@ public class CombatGroup implements Serializable, Copyable { } return false; } + + private static int getLethalDamage(Permanent blocker, Permanent attacker, Game game) { + int lethalDamage; + if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { + lethalDamage = 1; + } else { + lethalDamage = getLethalDamage(blocker, game); + } + return lethalDamage; + } + + public static int getLethalDamage(Permanent damagedPermanent, Game game) { + List usePowerInsteadOfToughnessForDamageLethalityFilters = game.getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters(); + /* + * for handling Zilortha, Strength Incarnate: + * 2020-04-17 + * Any time the game is checking whether damage is lethal or if a creature should be destroyed for having lethal damage marked on it, use the power of your creatures rather than their toughness to check the damage against. This includes being assigned trample damage, damage from Flame Spill, and so on. + */ + boolean usePowerInsteadOfToughnessForDamageLethality = usePowerInsteadOfToughnessForDamageLethalityFilters.stream() + .anyMatch(filter -> filter.match(damagedPermanent, game)); + int lethalDamageThreshold = usePowerInsteadOfToughnessForDamageLethality ? + // Zilortha, Strength Incarnate, 2020-04-17: A creature with 0 power isn’t destroyed unless it has at least 1 damage marked on it. + Math.max(damagedPermanent.getPower().getValue(), 1) : damagedPermanent.getToughness().getValue(); + return Math.max(lethalDamageThreshold - damagedPermanent.getDamage(), 0); + } } diff --git a/Mage/src/main/java/mage/game/command/Commander.java b/Mage/src/main/java/mage/game/command/Commander.java index e8a34c3a7d..8186c14ca5 100644 --- a/Mage/src/main/java/mage/game/command/Commander.java +++ b/Mage/src/main/java/mage/game/command/Commander.java @@ -1,5 +1,9 @@ package mage.game.command; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.ObjectColor; @@ -20,10 +24,6 @@ import mage.game.events.ZoneChangeEvent; import mage.util.GameLog; import mage.util.SubTypeList; -import java.util.List; -import java.util.Set; -import java.util.UUID; - public class Commander implements CommandObject { private final Card sourceObject; @@ -133,7 +133,7 @@ public class Commander implements CommandObject { } @Override - public Set getCardType() { + public ArrayList getCardType() { return sourceObject.getCardType(); } @@ -158,12 +158,12 @@ public class Commander implements CommandObject { } @Override - public boolean hasAbility(UUID abilityId, Game game) { - if (this.getAbilities().containsKey(abilityId)) { + public boolean hasAbility(Ability ability, Game game) { + if (this.getAbilities().contains(ability)) { return true; } Abilities otherAbilities = game.getState().getAllOtherAbilities(getId()); - return otherAbilities != null && otherAbilities.containsKey(abilityId); + return otherAbilities != null && otherAbilities.contains(ability); } @Override diff --git a/Mage/src/main/java/mage/game/command/Emblem.java b/Mage/src/main/java/mage/game/command/Emblem.java index 7a4a8896aa..b3396da0b4 100644 --- a/Mage/src/main/java/mage/game/command/Emblem.java +++ b/Mage/src/main/java/mage/game/command/Emblem.java @@ -1,5 +1,9 @@ package mage.game.command; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.ObjectColor; @@ -22,16 +26,12 @@ import mage.game.events.ZoneChangeEvent; import mage.util.GameLog; import mage.util.SubTypeList; -import java.util.EnumSet; -import java.util.List; -import java.util.UUID; - /** * @author nantuko */ public class Emblem implements CommandObject { - private static EnumSet emptySet = EnumSet.noneOf(CardType.class); + private static ArrayList emptySet = new ArrayList<>(); private static ObjectColor emptyColor = new ObjectColor(); private static ManaCosts emptyCost = new ManaCostsImpl(); @@ -148,7 +148,7 @@ public class Emblem implements CommandObject { } @Override - public EnumSet getCardType() { + public ArrayList getCardType() { return emptySet; } @@ -173,8 +173,8 @@ public class Emblem implements CommandObject { } @Override - public boolean hasAbility(UUID abilityId, Game game) { - return abilites.containsKey(abilityId); + public boolean hasAbility(Ability ability, Game game) { + return getAbilities().contains(ability); } @Override diff --git a/Mage/src/main/java/mage/game/command/Plane.java b/Mage/src/main/java/mage/game/command/Plane.java index d06d5b355b..764e1fa1a8 100644 --- a/Mage/src/main/java/mage/game/command/Plane.java +++ b/Mage/src/main/java/mage/game/command/Plane.java @@ -1,5 +1,10 @@ package mage.game.command; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.ObjectColor; @@ -24,21 +29,16 @@ import mage.util.GameLog; import mage.util.RandomUtil; import mage.util.SubTypeList; -import java.lang.reflect.Constructor; -import java.util.EnumSet; -import java.util.List; -import java.util.UUID; - /** * @author spjspj */ public class Plane implements CommandObject { - private static EnumSet emptySet = EnumSet.noneOf(CardType.class); + private static ArrayList emptySet = new ArrayList<>(); private static ObjectColor emptyColor = new ObjectColor(); private static ManaCosts emptyCost = new ManaCostsImpl(); - private String name = ""; + private Planes planeType = null; private UUID id; private UUID controllerId; private MageObject sourceObject; @@ -55,7 +55,7 @@ public class Plane implements CommandObject { public Plane(final Plane plane) { this.id = plane.id; - this.name = plane.name; + this.planeType = plane.planeType; this.frameStyle = plane.frameStyle; this.controllerId = plane.controllerId; this.sourceObject = plane.sourceObject; @@ -78,9 +78,6 @@ public class Plane implements CommandObject { public void setSourceObject(MageObject sourceObject) { this.sourceObject = sourceObject; if (sourceObject instanceof Card) { - if (name.isEmpty()) { - name = sourceObject.getSubtype(null).toString(); - } if (expansionSetCodeForImage.isEmpty()) { expansionSetCodeForImage = ((Card) sourceObject).getExpansionSetCode(); } @@ -128,7 +125,7 @@ public class Plane implements CommandObject { @Override public String getName() { - return name; + return planeType != null ? planeType.getFullName() : ""; } @Override @@ -143,16 +140,24 @@ public class Plane implements CommandObject { @Override public String getImageName() { - return this.name; + return planeType != null ? planeType.getFullName() : ""; } @Override public void setName(String name) { - this.name = name; + throw new UnsupportedOperationException("Planes don't use setName, use setPlaneType instead"); + } + + public void setPlaneType(Planes planeType) { + this.planeType = planeType; + } + + public Planes getPlaneType() { + return this.planeType; } @Override - public EnumSet getCardType() { + public ArrayList getCardType() { return emptySet; } @@ -177,8 +182,8 @@ public class Plane implements CommandObject { } @Override - public boolean hasAbility(UUID abilityId, Game game) { - return abilites.containsKey(abilityId); + public boolean hasAbility(Ability ability, Game game) { + return getAbilities().contains(ability); } @Override @@ -288,19 +293,30 @@ public class Plane implements CommandObject { public void removePTCDA() { } - public static Plane getRandomPlane() { - int pick = RandomUtil.nextInt(Planes.values().length); - String planeName = Planes.values()[pick].toString(); - planeName = "mage.game.command.planes." + planeName; - try { - Class c = Class.forName(planeName); - Constructor cons = c.getConstructor(); - Object plane = cons.newInstance(); - if (plane instanceof Plane) { - return (Plane) plane; + public static Plane createPlane(Planes planeType) { + if (planeType != null) { + String planeFullClass = "mage.game.command.planes." + planeType.getClassName(); + try { + Class c = Class.forName(planeFullClass); + Constructor cons = c.getConstructor(); + Object plane = cons.newInstance(); + if (plane instanceof Plane) { + return (Plane) plane; + } + } catch (Exception ex) { } - } catch (Exception ex) { } return null; } + + public static Plane createPlaneByFullName(String fullName) { + Planes planeType = Planes.fromFullName(fullName); + return createPlane(planeType); + } + + public static Plane createRandomPlane() { + int pick = RandomUtil.nextInt(Planes.values().length); + Planes planeType = Planes.values()[pick]; + return createPlane(planeType); + } } diff --git a/Mage/src/main/java/mage/game/command/emblems/BasriKetEmblem.java b/Mage/src/main/java/mage/game/command/emblems/BasriKetEmblem.java new file mode 100644 index 0000000000..e8fb405fc7 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/BasriKetEmblem.java @@ -0,0 +1,31 @@ +package mage.game.command.emblems; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.command.Emblem; +import mage.game.permanent.token.SoldierToken; + +public final class BasriKetEmblem extends Emblem { + /** + * Emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control." + */ + + public BasriKetEmblem() { + setName("Emblem Basri"); + Ability ability = new BeginningOfCombatTriggeredAbility( + Zone.COMMAND, + new CreateTokenEffect(new SoldierToken()), + TargetController.YOU, false, false); + ability.addEffect( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED) + .setText(", then put a +1/+1 counter on each creature you control") + ); + this.getAbilities().add(ability); + } +} diff --git a/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java b/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java index 85a51c0e9a..d9104aa345 100644 --- a/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java @@ -93,7 +93,7 @@ class DarettiScrapSavantEffect extends OneShotEffect { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return that card to the battlefield at the beginning of the next end step"); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.COMMAND, effect, TargetController.ANY), source); return true; diff --git a/Mage/src/main/java/mage/game/command/emblems/DomriRadeEmblem.java b/Mage/src/main/java/mage/game/command/emblems/DomriRadeEmblem.java index d71bb577c1..700f2ebbdf 100644 --- a/Mage/src/main/java/mage/game/command/emblems/DomriRadeEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/DomriRadeEmblem.java @@ -1,7 +1,7 @@ package mage.game.command.emblems; -import mage.abilities.Ability; +import mage.abilities.CompoundAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.DoubleStrikeAbility; @@ -22,16 +22,15 @@ public final class DomriRadeEmblem extends Emblem { // "Creatures you control have double strike, trample, hexproof and haste." public DomriRadeEmblem() { - this.setName("Emblem Domri"); + this.setName("Emblem Domri Rade"); FilterPermanent filter = new FilterControlledCreaturePermanent("Creatures"); - GainAbilityControlledEffect effect = new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfGame, filter); - Ability ability = new SimpleStaticAbility(Zone.COMMAND, effect); - effect = new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfGame, filter); - ability.addEffect(effect); - effect = new GainAbilityControlledEffect(HexproofAbility.getInstance(), Duration.EndOfGame, filter); - ability.addEffect(effect); - effect = new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.EndOfGame, filter); - ability.addEffect(effect); - this.getAbilities().add(ability); + + CompoundAbility compoundAbilities = new CompoundAbility( + DoubleStrikeAbility.getInstance(), + TrampleAbility.getInstance(), + HexproofAbility.getInstance(), + HasteAbility.getInstance() + ); + this.getAbilities().add(new SimpleStaticAbility(Zone.COMMAND, new GainAbilityControlledEffect(compoundAbilities, Duration.EndOfGame, filter))); } } diff --git a/Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java b/Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java new file mode 100644 index 0000000000..50c394c315 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java @@ -0,0 +1,21 @@ +package mage.game.command.emblems; + +import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.command.Emblem; +import mage.target.common.TargetCardInLibrary; + +public class GarrukUnleashedEmblem extends Emblem { + + // At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library. + public GarrukUnleashedEmblem() { + this.setName("Emblem Garruk"); + Effect effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_CREATURE), false, true, Outcome.PutCreatureInPlay) + .setText("search your library for a creature card, put it onto the battlefield, then shuffle your library"); + this.getAbilities().add(new BeginningOfYourEndStepTriggeredAbility(Zone.COMMAND, effect, true)); + } +} diff --git a/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java b/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java index 6891d99b3e..31b9302ca6 100644 --- a/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java @@ -1,4 +1,3 @@ - package mage.game.command.emblems; import mage.abilities.Ability; @@ -11,7 +10,6 @@ import mage.game.command.Emblem; import mage.target.common.TargetOpponent; /** - * * @author spjspj */ public final class JaceTelepathUnboundEmblem extends Emblem { @@ -20,7 +18,7 @@ public final class JaceTelepathUnboundEmblem extends Emblem { public JaceTelepathUnboundEmblem() { this.setName("Emblem Jace"); Effect effect = new PutTopCardOfLibraryIntoGraveTargetEffect(5); - effect.setText("target opponent puts the top five cards of their library into their graveyard"); + effect.setText("target opponent mills five cards"); Ability ability = new SpellCastControllerTriggeredAbility(Zone.COMMAND, effect, new FilterSpell("a spell"), false, false); ability.addTarget(new TargetOpponent()); getAbilities().add(ability); diff --git a/Mage/src/main/java/mage/game/command/emblems/LilianaDefiantNecromancerEmblem.java b/Mage/src/main/java/mage/game/command/emblems/LilianaDefiantNecromancerEmblem.java index de57aa1f3c..abdf853a60 100644 --- a/Mage/src/main/java/mage/game/command/emblems/LilianaDefiantNecromancerEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/LilianaDefiantNecromancerEmblem.java @@ -53,7 +53,7 @@ class LilianaDefiantNecromancerEmblemEffect extends OneShotEffect { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return that card to the battlefield at the beginning of the next end step"); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.COMMAND, effect, TargetController.ANY), source); return true; diff --git a/Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java b/Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java new file mode 100644 index 0000000000..dead907e5b --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java @@ -0,0 +1,26 @@ +package mage.game.command.emblems; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.common.ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.command.Emblem; +import mage.target.common.TargetCardInGraveyard; + +public final class LilianaWakerOfTheDeadEmblem extends Emblem { + /** + * Emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste." + */ + + public LilianaWakerOfTheDeadEmblem() { + setName("Emblem Liliana"); + Ability ability = new BeginningOfCombatTriggeredAbility( + Zone.COMMAND, + new ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect(), + TargetController.YOU, false, false); + ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE)); + this.getAbilities().add(ability); + } +} diff --git a/Mage/src/main/java/mage/game/command/emblems/NarsetOfTheAncientWayEmblem.java b/Mage/src/main/java/mage/game/command/emblems/NarsetOfTheAncientWayEmblem.java new file mode 100644 index 0000000000..0ea263a631 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/NarsetOfTheAncientWayEmblem.java @@ -0,0 +1,26 @@ +package mage.game.command.emblems; + +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.command.Emblem; +import mage.target.common.TargetAnyTarget; + +/** + * @author TheElk801 + */ +public final class NarsetOfTheAncientWayEmblem extends Emblem { + + // −6: You get an emblem with "Whenever you cast a noncreature spell, this emblem deals 2 damage to any target." + public NarsetOfTheAncientWayEmblem() { + this.setName("Emblem Narset"); + Ability ability = new SpellCastControllerTriggeredAbility( + Zone.COMMAND, new DamageTargetEffect(2, "this emblem"), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false, false + ); + ability.addTarget(new TargetAnyTarget()); + this.getAbilities().add(ability); + } +} diff --git a/Mage/src/main/java/mage/game/command/emblems/VivienReidEmblem.java b/Mage/src/main/java/mage/game/command/emblems/VivienReidEmblem.java index f63da5fa04..bf9dcf0887 100644 --- a/Mage/src/main/java/mage/game/command/emblems/VivienReidEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/VivienReidEmblem.java @@ -14,7 +14,7 @@ import mage.game.command.Emblem; /** * - * @author spjspj + * @author TheElk801 */ public final class VivienReidEmblem extends Emblem { // -8: You get an emblem with "Creatures you control get +2/+2 and have vigilance, trample, and indestructible. @@ -34,18 +34,17 @@ public final class VivienReidEmblem extends Emblem { VigilanceAbility.getInstance(), Duration.EndOfGame, StaticFilters.FILTER_PERMANENT_CREATURES - ).setText("and have vigilance,")); + ).setText("and have vigilance")); ability.addEffect(new GainAbilityControlledEffect( TrampleAbility.getInstance(), Duration.EndOfGame, StaticFilters.FILTER_PERMANENT_CREATURES - ).setText("trample,")); + ).setText(", trample")); ability.addEffect(new GainAbilityControlledEffect( IndestructibleAbility.getInstance(), Duration.EndOfGame, StaticFilters.FILTER_PERMANENT_CREATURES - ).setText("and indestructible")); + ).setText(", and indestructible")); this.getAbilities().add(ability); - } } diff --git a/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java b/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java index e8b54b8e29..c5fce051a7 100644 --- a/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; @@ -15,8 +12,10 @@ import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.abilities.effects.common.discard.DiscardHandControllerEffect; import mage.constants.Outcome; +import mage.constants.Planes; import mage.constants.TargetController; import mage.constants.Zone; import mage.game.Game; @@ -26,14 +25,16 @@ import mage.target.Target; import mage.util.CardUtil; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class AcademyAtTolariaWestPlane extends Plane { public AcademyAtTolariaWestPlane() { - this.setName("Plane - Academy at Tolaria West"); + this.setPlaneType(Planes.PLANE_ACADEMY_AT_TOLARIA_WEST); this.setExpansionSetCodeForImage("PCA"); // At the beginning of your end step, if you have 0 cards in hand, draw seven cards @@ -88,12 +89,12 @@ class DrawCardsActivePlayerEffect extends OneShotEffect { if (cPlane == null) { return false; } - if (!cPlane.getName().equalsIgnoreCase("Plane - Academy at Tolaria West")) { + if (!cPlane.getPlaneType().equals(Planes.PLANE_ACADEMY_AT_TOLARIA_WEST)) { return false; } Player player = game.getPlayer(game.getActivePlayerId()); if (player != null) { - player.drawCards(amount.calculate(game, source, this), game); + player.drawCards(amount.calculate(game, source, this), source.getSourceId(), game); return true; } return false; diff --git a/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java b/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java index e879dd6054..09812ac170 100644 --- a/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java @@ -14,12 +14,10 @@ import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.cards.Card; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; @@ -38,8 +36,8 @@ import java.util.UUID; */ public class AgyremPlane extends Plane { - private static final FilterControlledCreaturePermanent filterWhite = new FilterControlledCreaturePermanent("a white creature"); - private static final FilterControlledCreaturePermanent filterNonWhite = new FilterControlledCreaturePermanent("a nonwhite creature"); + private static final FilterCreaturePermanent filterWhite = new FilterCreaturePermanent("a white creature"); + private static final FilterCreaturePermanent filterNonWhite = new FilterCreaturePermanent("a nonwhite creature"); static { filterWhite.add(new ColorPredicate(ObjectColor.WHITE)); @@ -47,12 +45,13 @@ public class AgyremPlane extends Plane { } public AgyremPlane() { - this.setName("Plane - Agyrem"); + this.setPlaneType(Planes.PLANE_AGYREM); this.setExpansionSetCodeForImage("PCA"); // Whenever a white creature dies, return it to the battlefield under its owner's control at the beginning of the next end step DiesCreatureTriggeredAbility ability = new DiesCreatureTriggeredAbility(Zone.COMMAND, new AgyremEffect(), false, filterWhite, true); this.getAbilities().add(ability); + // Whenever a nonwhite creature dies, return it to its owner's hand at the beginning of the next end step. DiesCreatureTriggeredAbility ability2 = new DiesCreatureTriggeredAbility(Zone.COMMAND, new AgyremEffect2(), false, filterNonWhite, true); this.getAbilities().add(ability2); @@ -60,9 +59,9 @@ public class AgyremPlane extends Plane { Effect chaosEffect = new AgyremRestrictionEffect(); Target chaosTarget = null; - List chaosEffects = new ArrayList(); + List chaosEffects = new ArrayList<>(); chaosEffects.add(chaosEffect); - List chaosTargets = new ArrayList(); + List chaosTargets = new ArrayList<>(); chaosTargets.add(chaosTarget); ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); @@ -93,8 +92,8 @@ class AgyremEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { - Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return that card to the battlefield under its owner's control at the beginning of the next end step"); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect, TargetController.ANY), source); return true; @@ -124,7 +123,7 @@ class AgyremEffect2 extends OneShotEffect { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { Effect effect = new ReturnFromGraveyardToHandTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return it to its owner's hand at the beginning of the next end step"); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect, TargetController.ANY), source); return true; @@ -156,7 +155,7 @@ class AgyremRestrictionEffect extends RestrictionEffect { } Plane cPlane = game.getState().getCurrentPlane(); - if (cPlane != null && cPlane.getName().equalsIgnoreCase("Plane - Agyrem")) { + if (cPlane != null && cPlane.getPlaneType().equals(Planes.PLANE_AGYREM)) { return !defenderId.equals(source.getControllerId()); } return true; diff --git a/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java b/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java index 3582cc9482..57607eb484 100644 --- a/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MainPhaseStackEmptyCondition; @@ -11,10 +8,8 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; @@ -24,8 +19,10 @@ import mage.target.Target; import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class AkoumPlane extends Plane { @@ -39,7 +36,7 @@ public class AkoumPlane extends Plane { } public AkoumPlane() { - this.setName("Plane - Akoum"); + this.setPlaneType(Planes.PLANE_AKOUM); this.setExpansionSetCodeForImage("PCA"); // Players may cast enchantment spells as if they had flash diff --git a/Mage/src/main/java/mage/game/command/planes/AstralArenaPlane.java b/Mage/src/main/java/mage/game/command/planes/AstralArenaPlane.java index 15260c05f5..2a60058195 100644 --- a/Mage/src/main/java/mage/game/command/planes/AstralArenaPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/AstralArenaPlane.java @@ -9,7 +9,9 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.constants.Duration; +import mage.constants.Planes; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; @@ -30,7 +32,7 @@ import java.util.UUID; public class AstralArenaPlane extends Plane { public AstralArenaPlane() { - this.setName("Plane - Astral Arena"); + this.setPlaneType(Planes.PLANE_ASTRAL_ARENA); this.setExpansionSetCodeForImage("PCA"); // No more than one creature can attack each turn. No more than one creature can block each turn. @@ -80,7 +82,7 @@ class AstralArenaAttackRestrictionEffect extends RestrictionEffect { if (cPlane == null) { return false; } - return cPlane.getName().equalsIgnoreCase("Plane - Astral Arena"); + return cPlane.getPlaneType().equals(Planes.PLANE_ASTRAL_ARENA); } @Override @@ -111,7 +113,7 @@ class AstralArenaBlockRestrictionEffect extends RestrictionEffect { if (cPlane == null) { return false; } - return cPlane.getName().equalsIgnoreCase("Plane - Astral Arena"); + return cPlane.getPlaneType().equals(Planes.PLANE_ASTRAL_ARENA); } @Override diff --git a/Mage/src/main/java/mage/game/command/planes/BantPlane.java b/Mage/src/main/java/mage/game/command/planes/BantPlane.java index 5ea0fc27e6..a717305033 100644 --- a/Mage/src/main/java/mage/game/command/planes/BantPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/BantPlane.java @@ -1,11 +1,6 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.IsStillOnPlaneCondition; @@ -17,13 +12,12 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.ExaltedAbility; import mage.abilities.keyword.IndestructibleAbility; -import mage.constants.CostModificationType; import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.Planes; import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; @@ -31,16 +25,15 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; import mage.game.command.Plane; -import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class BantPlane extends Plane { @@ -56,15 +49,15 @@ public class BantPlane extends Plane { private static final String exaltedRule = "All creatures have exalted"; public BantPlane() { - this.setName("Plane - Bant"); + this.setPlaneType(Planes.PLANE_BANT); this.setExpansionSetCodeForImage("PCA"); // All creatures have exalted SimpleStaticAbility ability = new SimpleStaticAbility(Zone.COMMAND, new ConditionalContinuousEffect( - new GainAbilityAllEffect(new ExaltedAbility(), Duration.Custom, StaticFilters.FILTER_PERMANENT_CREATURE), - new IsStillOnPlaneCondition(this.getName()), - exaltedRule)); + new GainAbilityAllEffect(new ExaltedAbility(), Duration.Custom, StaticFilters.FILTER_PERMANENT_CREATURE), + new IsStillOnPlaneCondition(this.getName()), + exaltedRule)); this.getAbilities().add(ability); @@ -88,42 +81,3 @@ public class BantPlane extends Plane { this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); } } - -class PlanarDieRollCostIncreasingEffect extends CostModificationEffectImpl { - - private final UUID originalId; - - PlanarDieRollCostIncreasingEffect(UUID originalId) { - super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.INCREASE_COST); - this.originalId = originalId; - } - - PlanarDieRollCostIncreasingEffect(final PlanarDieRollCostIncreasingEffect effect) { - super(effect); - this.originalId = effect.originalId; - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - Player activePlayer = game.getPlayer(game.getActivePlayerId()); - if (activePlayer != null) { - PlanarRollWatcher watcher = game.getState().getWatcher(PlanarRollWatcher.class); - int rolledCounter = 0; - if (watcher != null) { - rolledCounter = watcher.getNumberTimesPlanarDieRolled(activePlayer.getId()); - } - CardUtil.increaseCost(abilityToModify, rolledCounter); - } - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify.getOriginalId().equals(originalId); - } - - @Override - public PlanarDieRollCostIncreasingEffect copy() { - return new PlanarDieRollCostIncreasingEffect(this); - } -} diff --git a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java index 4a29f9fd16..60ce3e1e88 100644 --- a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java @@ -1,27 +1,17 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MainPhaseStackEmptyCondition; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RollPlanarDieEffect; -import mage.abilities.effects.common.SkipUntapStepEffect; import mage.abilities.effects.common.UntapAllControllerEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.PhaseStep; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; @@ -34,14 +24,18 @@ import mage.target.Target; import mage.target.targetpointer.FixedTarget; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + /** - * * @author spjspj */ public class EdgeOfMalacolPlane extends Plane { public EdgeOfMalacolPlane() { - this.setName("Plane - Edge Of Malacol"); + this.setPlaneType(Planes.PLANE_EDGE_OF_MALACOL); this.setExpansionSetCodeForImage("PCA"); // If a creature you control would untap during your untap step, put two +1/+1 counters on it instead. @@ -101,7 +95,7 @@ class EdgeOfMalacolEffect extends ContinuousRuleModifyingEffectImpl { if (cPlane == null) { return false; } - if (!cPlane.getName().equalsIgnoreCase("Plane - Edge of Malacol")) { + if (!cPlane.getPlaneType().equals(Planes.PLANE_EDGE_OF_MALACOL)) { return false; } Permanent permanent = game.getPermanent(event.getTargetId()); diff --git a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java index 04fb098a1f..553e7ce747 100644 --- a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java @@ -1,4 +1,3 @@ - package mage.game.command.planes; import java.util.ArrayList; @@ -15,13 +14,10 @@ import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.Card; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.common.FilterCreaturePermanent; @@ -36,7 +32,6 @@ import mage.util.CardUtil; import mage.watchers.common.PlanarRollWatcher; /** - * * @author spjspj */ public class FeedingGroundsPlane extends Plane { @@ -45,7 +40,7 @@ public class FeedingGroundsPlane extends Plane { private static final String rule = "put X +1/+1 counters on target creature, where X is that creature's converted mana cost"; public FeedingGroundsPlane() { - this.setName("Plane - Feeding Grounds"); + this.setPlaneType(Planes.PLANE_FEEDING_GROUNDS); this.setExpansionSetCodeForImage("PCA"); // Red spells cost {1} less to cast. Green spells cost {1} less to cast @@ -56,9 +51,9 @@ public class FeedingGroundsPlane extends Plane { Effect chaosEffect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(), TargetConvertedManaCost.instance); Target chaosTarget = new TargetCreaturePermanent(1, 1, filter, false); - List chaosEffects = new ArrayList(); + List chaosEffects = new ArrayList<>(); chaosEffects.add(chaosEffect); - List chaosTargets = new ArrayList(); + List chaosTargets = new ArrayList<>(); chaosTargets.add(chaosTarget); ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); @@ -79,7 +74,7 @@ class FeedingGroundsEffect extends CostModificationEffectImpl { new ColorPredicate(ObjectColor.GREEN))); } - private static final String rule = "Red spells cost {1} less to cast. Green spells cost {1} less to cast."; + private static final String rule = "Red spells cost {1} less to cast. Green spells cost {1} less to cast"; private int amount = 1; public FeedingGroundsEffect() { @@ -133,7 +128,7 @@ class FeedingGroundsEffect extends CostModificationEffectImpl { if (cPlane == null) { return false; } - if (!cPlane.getName().equalsIgnoreCase("Plane - Feeding Grounds")) { + if (!cPlane.getPlaneType().equals(Planes.PLANE_FEEDING_GROUNDS)) { return false; } diff --git a/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java b/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java index f8444d6fc8..ae561f6dca 100644 --- a/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -14,10 +11,8 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.GainLifeTargetEffect; import mage.abilities.effects.common.RollPlanarDieEffect; -import mage.constants.Outcome; -import mage.constants.SetTargetPointer; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; +import mage.constants.*; import mage.filter.FilterSpell; import mage.game.Game; import mage.game.command.Plane; @@ -26,8 +21,10 @@ import mage.target.Target; import mage.target.targetpointer.FixedTarget; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class FieldsOfSummerPlane extends Plane { @@ -35,7 +32,7 @@ public class FieldsOfSummerPlane extends Plane { private static final FilterSpell filter = new FilterSpell("a spell"); public FieldsOfSummerPlane() { - this.setName("Plane - Fields of Summer"); + this.setPlaneType(Planes.PLANE_FIELDS_OF_SUMMER); this.setExpansionSetCodeForImage("PCA"); // Whenever a player casts a spell, that player may gain 2 life @@ -78,7 +75,7 @@ class FieldsOfSummerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Plane cPlane = game.getState().getCurrentPlane(); - if (cPlane == null || !cPlane.getName().equalsIgnoreCase("Plane - Fields of Summer")) { + if (cPlane == null || !cPlane.getPlaneType().equals(Planes.PLANE_FIELDS_OF_SUMMER)) { return false; } Player owner = game.getPlayer(this.getTargetPointer().getFirst(game, source)); diff --git a/Mage/src/main/java/mage/game/command/planes/HedronFieldsOfAgadeemPlane.java b/Mage/src/main/java/mage/game/command/planes/HedronFieldsOfAgadeemPlane.java index 06cb2d410e..4e498c5bfe 100644 --- a/Mage/src/main/java/mage/game/command/planes/HedronFieldsOfAgadeemPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/HedronFieldsOfAgadeemPlane.java @@ -9,10 +9,8 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.RollPlanarDieEffect; -import mage.constants.ComparisonType; -import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; +import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; import mage.game.Game; @@ -31,7 +29,7 @@ import java.util.List; public class HedronFieldsOfAgadeemPlane extends Plane { public HedronFieldsOfAgadeemPlane() { - this.setName("Plane - Hedron Fields of Agadeem"); + this.setPlaneType(Planes.PLANE_HEDRON_FIELDS_OF_AGADEEM); this.setExpansionSetCodeForImage("PCA"); // Creatures with power 7 or greater can't attack or block @@ -94,7 +92,7 @@ class HedronFieldsOfAgadeemRestrictionEffect extends RestrictionEffect { if (cPlane == null) { return false; } - if (!cPlane.getName().equalsIgnoreCase("Plane - Hedron Fields of Agadeem")) { + if (!cPlane.getPlaneType().equals(Planes.PLANE_HEDRON_FIELDS_OF_AGADEEM)) { return false; } return filter.match(permanent, source.getSourceId(), source.getControllerId(), game); diff --git a/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java b/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java index ca7299dc14..9bd2c8e0b9 100644 --- a/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java +++ b/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -13,6 +10,8 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveTargetEffect; import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; +import mage.constants.Planes; import mage.constants.TargetController; import mage.constants.Zone; import mage.game.command.Plane; @@ -20,18 +19,20 @@ import mage.target.Target; import mage.target.TargetPlayer; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class LetheLakePlane extends Plane { public LetheLakePlane() { - this.setName("Plane - Lethe Lake"); + this.setPlaneType(Planes.PLANE_LETHE_LAKE); this.setExpansionSetCodeForImage("PCA"); // At the beginning of your upkeep, put the top ten cards of your libary into your graveyard - Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.COMMAND, new PutLibraryIntoGraveTargetEffect(10).setText("that player puts the top 10 cards of their library into their graveyard"), TargetController.ANY, false, true); + Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.COMMAND, new PutLibraryIntoGraveTargetEffect(10).setText("that player mills 10 cards"), TargetController.ANY, false, true); this.getAbilities().add(ability); // Active player can roll the planar die: Whenever you roll {CHAOS}, target player puts the top ten cards of their library into their graveyard diff --git a/Mage/src/main/java/mage/game/command/planes/NayaPlane.java b/Mage/src/main/java/mage/game/command/planes/NayaPlane.java index 335eba469a..764a479d4c 100644 --- a/Mage/src/main/java/mage/game/command/planes/NayaPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/NayaPlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; @@ -15,7 +12,9 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.PlayAdditionalLandsAllEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.constants.Duration; +import mage.constants.Planes; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.StaticFilters; @@ -27,8 +26,10 @@ import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class NayaPlane extends Plane { @@ -40,7 +41,7 @@ public class NayaPlane extends Plane { } public NayaPlane() { - this.setName("Plane - Naya"); + this.setPlaneType(Planes.PLANE_NAYA); this.setExpansionSetCodeForImage("PCA"); // You may play any number of lands on each of your turns diff --git a/Mage/src/main/java/mage/game/command/planes/PanopticonPlane.java b/Mage/src/main/java/mage/game/command/planes/PanopticonPlane.java index 3f2123ecfb..54691deffa 100644 --- a/Mage/src/main/java/mage/game/command/planes/PanopticonPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/PanopticonPlane.java @@ -1,7 +1,5 @@ package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.ActivateIfConditionActivatedAbility; @@ -13,6 +11,8 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.DrawCardTargetEffect; import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; +import mage.constants.Planes; import mage.constants.TargetController; import mage.constants.Zone; import mage.game.Game; @@ -24,8 +24,10 @@ import mage.target.Target; import mage.target.targetpointer.FixedTarget; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class PanopticonPlane extends Plane { @@ -33,7 +35,7 @@ public class PanopticonPlane extends Plane { private static final String rule = "At the beginning of your draw step, draw an additional card"; public PanopticonPlane() { - this.setName("Plane - Panopticon"); + this.setPlaneType(Planes.PLANE_PANOPTICON); this.setExpansionSetCodeForImage("PCA"); // When you planeswalk to Panopticon, draw a card @@ -80,7 +82,7 @@ class PanopticonTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Plane cPlane = game.getState().getCurrentPlane(); - if (cPlane == null || !cPlane.getName().equalsIgnoreCase("Plane - Panopticon")) { + if (cPlane == null || !cPlane.getPlaneType().equals(Planes.PLANE_PANOPTICON)) { return false; } diff --git a/Mage/src/main/java/mage/game/command/planes/TazeemPlane.java b/Mage/src/main/java/mage/game/command/planes/TazeemPlane.java index e1accd02cb..1671cd0664 100644 --- a/Mage/src/main/java/mage/game/command/planes/TazeemPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/TazeemPlane.java @@ -11,7 +11,9 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.constants.Duration; +import mage.constants.Planes; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterControlledLandPermanent; @@ -33,7 +35,7 @@ public class TazeemPlane extends Plane { private static final String rule = "Creatures can't block"; public TazeemPlane() { - this.setName("Plane - Tazeem"); + this.setPlaneType(Planes.PLANE_TAZEEM); this.setExpansionSetCodeForImage("PCA"); // Creatures can't block @@ -75,7 +77,7 @@ class TazeemCantBlockAllEffect extends RestrictionEffect { public boolean applies(Permanent permanent, Ability source, Game game) { Plane cPlane = game.getState().getCurrentPlane(); - if (cPlane == null || !cPlane.getName().equalsIgnoreCase("Plane - Tazeem")) { + if (cPlane == null || !cPlane.getPlaneType().equals(Planes.PLANE_TAZEEM)) { return false; } return filter.match(permanent, source.getSourceId(), source.getControllerId(), game); diff --git a/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java b/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java index 1ca83d8f7b..ab5682e9aa 100644 --- a/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; @@ -13,7 +10,9 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.Effect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; +import mage.constants.Planes; import mage.constants.SetTargetPointer; import mage.constants.TargetController; import mage.constants.Zone; @@ -24,8 +23,10 @@ import mage.game.command.Plane; import mage.target.Target; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class TheDarkBaronyPlane extends Plane { @@ -37,7 +38,7 @@ public class TheDarkBaronyPlane extends Plane { } public TheDarkBaronyPlane() { - this.setName("Plane - The Dark Barony"); + this.setPlaneType(Planes.PLANE_THE_DARK_BARONY); this.setExpansionSetCodeForImage("PCA"); // Whenever a nonblack card is put into a player's graveyard from anywhere, that player loses 1 life diff --git a/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java b/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java index 75592637a0..d153355cbf 100644 --- a/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -12,10 +9,8 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.UntapAllControllerEffect; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; +import mage.constants.*; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.command.Plane; @@ -23,14 +18,16 @@ import mage.game.events.GameEvent; import mage.target.Target; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class TheEonFogPlane extends Plane { public TheEonFogPlane() { - this.setName("Plane - The Eon Fog"); + this.setPlaneType(Planes.PLANE_THE_EON_FOG); this.setExpansionSetCodeForImage("PCA"); // All players miss their untap step @@ -87,7 +84,7 @@ class TheEonFogSkipUntapStepEffect extends ContinuousRuleModifyingEffectImpl { if (cPlane == null) { return false; } - if (!cPlane.getName().equalsIgnoreCase("Plane - The Eon Fog")) { + if (!cPlane.getPlaneType().equals(Planes.PLANE_THE_EON_FOG)) { return false; } return event.getType() == GameEvent.EventType.UNTAP_STEP; diff --git a/Mage/src/main/java/mage/game/command/planes/TheGreatForestPlane.java b/Mage/src/main/java/mage/game/command/planes/TheGreatForestPlane.java index 329a91b5e8..3d363d8707 100644 --- a/Mage/src/main/java/mage/game/command/planes/TheGreatForestPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/TheGreatForestPlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -13,22 +10,19 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.abilities.keyword.TrampleAbility; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.command.Plane; import mage.target.Target; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class TheGreatForestPlane extends Plane { @@ -36,7 +30,7 @@ public class TheGreatForestPlane extends Plane { private static final String rule = "Each creature assigns combat damage equal to its toughness rather than its power"; public TheGreatForestPlane() { - this.setName("Plane - The Great Forest"); + this.setPlaneType(Planes.PLANE_THE_GREAT_FOREST); this.setExpansionSetCodeForImage("PCA"); // Each creature assigns combat damage equal to its toughness rather than its power @@ -82,11 +76,11 @@ class TheGreatForestCombatDamageRuleEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Plane cPlane = game.getState().getCurrentPlane(); + Plane cPlane = game.getState().getCurrentPlane(); if (cPlane == null) { return false; } - if (!cPlane.getName().equalsIgnoreCase("Plane - The Great Forest")) { + if (!cPlane.getPlaneType().equals(Planes.PLANE_THE_GREAT_FOREST)) { return false; } diff --git a/Mage/src/main/java/mage/game/command/planes/TheZephyrMazePlane.java b/Mage/src/main/java/mage/game/command/planes/TheZephyrMazePlane.java index 74fd5f5edc..b46cc06d12 100644 --- a/Mage/src/main/java/mage/game/command/planes/TheZephyrMazePlane.java +++ b/Mage/src/main/java/mage/game/command/planes/TheZephyrMazePlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -14,8 +11,10 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.abilities.keyword.FlyingAbility; import mage.constants.Duration; +import mage.constants.Planes; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; @@ -26,8 +25,10 @@ import mage.target.Target; import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class TheZephyrMazePlane extends Plane { @@ -44,7 +45,7 @@ public class TheZephyrMazePlane extends Plane { private static final String walkingRule = "Creatures without flying get -2/-0"; public TheZephyrMazePlane() { - this.setName("Plane - The Zephyr Maze"); + this.setPlaneType(Planes.PLANE_THE_ZEPHYR_MAZE_FOG); this.setExpansionSetCodeForImage("PCA"); // Creatures with flying get +2/+0 @@ -61,7 +62,7 @@ public class TheZephyrMazePlane extends Plane { this.getAbilities().add(ability2); // Active player can roll the planar die: Whenever you roll {CHAOS}, target creature gains flying until end of turn - Effect chaosEffect = new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); + Effect chaosEffect = new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); Target chaosTarget = new TargetCreaturePermanent(0, 1); List chaosEffects = new ArrayList(); diff --git a/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java b/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java index fe00e2898d..be02a597c8 100644 --- a/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java @@ -1,10 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -13,16 +8,11 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.abilities.keyword.ReboundAbility; import mage.cards.Card; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.Predicates; @@ -35,8 +25,12 @@ import mage.target.Target; import mage.target.common.TargetCardInLibrary; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + /** - * * @author spjspj */ public class TrailOfTheMageRingsPlane extends Plane { @@ -48,7 +42,7 @@ public class TrailOfTheMageRingsPlane extends Plane { } public TrailOfTheMageRingsPlane() { - this.setName("Plane - Trail of the Mage-Rings"); + this.setPlaneType(Planes.PLANE_TRAIL_OF_THE_MAGE_RINGS); this.setExpansionSetCodeForImage("PCA"); // Instant and sorcery spells have rebound @@ -100,7 +94,7 @@ class TrailOfTheMageRingsReboundEffect extends ContinuousEffectImpl { if (cPlane == null) { return false; } - if (!cPlane.getName().equalsIgnoreCase("Plane - Trail of the Mage-Rings")) { + if (!cPlane.getPlaneType().equals(Planes.PLANE_TRAIL_OF_THE_MAGE_RINGS)) { return false; } @@ -110,7 +104,7 @@ class TrailOfTheMageRingsReboundEffect extends ContinuousEffectImpl { for (Card card : player.getHand().getCards(filter, game)) { addReboundAbility(card, source, game); } - for (Iterator iterator = game.getStack().iterator(); iterator.hasNext();) { + for (Iterator iterator = game.getStack().iterator(); iterator.hasNext(); ) { StackObject stackObject = iterator.next(); if (stackObject instanceof Spell && stackObject.isControlledBy(source.getControllerId())) { Spell spell = (Spell) stackObject; @@ -127,7 +121,7 @@ class TrailOfTheMageRingsReboundEffect extends ContinuousEffectImpl { private void addReboundAbility(Card card, Ability source, Game game) { if (filter.match(card, game)) { - boolean found = card.getAbilities().stream().anyMatch(ability -> ability instanceof ReboundAbility); + boolean found = card.getAbilities(game).containsClass(ReboundAbility.class); if (!found) { Ability ability = new ReboundAbility(); game.getState().addOtherAbility(card, ability); diff --git a/Mage/src/main/java/mage/game/command/planes/TrugaJunglePlane.java b/Mage/src/main/java/mage/game/command/planes/TrugaJunglePlane.java index 24ca5e3f43..f04613c19c 100644 --- a/Mage/src/main/java/mage/game/command/planes/TrugaJunglePlane.java +++ b/Mage/src/main/java/mage/game/command/planes/TrugaJunglePlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.IsStillOnPlaneCondition; @@ -13,8 +10,10 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.RevealLibraryPutIntoHandEffect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.constants.Duration; +import mage.constants.Planes; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.StaticFilters; @@ -23,8 +22,10 @@ import mage.game.command.Plane; import mage.target.Target; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class TrugaJunglePlane extends Plane { @@ -32,14 +33,14 @@ public class TrugaJunglePlane extends Plane { private static final String rule = "All lands have '{t}: Add one mana of any color"; public TrugaJunglePlane() { - this.setName("Plane - Truga Jungle"); + this.setPlaneType(Planes.PLANE_TRUGA_JUNGLE); this.setExpansionSetCodeForImage("PCA"); SimpleStaticAbility ability = new SimpleStaticAbility(Zone.COMMAND, new ConditionalContinuousEffect( - new GainAbilityAllEffect(new AnyColorManaAbility(), Duration.Custom, StaticFilters.FILTER_LANDS), - new IsStillOnPlaneCondition(this.getName()), - rule)); + new GainAbilityAllEffect(new AnyColorManaAbility(), Duration.Custom, StaticFilters.FILTER_LANDS), + new IsStillOnPlaneCondition(this.getName()), + rule)); this.getAbilities().add(ability); // Active player can roll the planar die: Whenever you roll {CHAOS}, reveal the top three cards of your libary. Put all land cards revealed this way into your hand the rest on the bottom of your library in any order. diff --git a/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java b/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java index 16b5665f2d..99b658cde3 100644 --- a/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.ActivateIfConditionActivatedAbility; @@ -13,30 +10,27 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.RevealLibraryPutIntoHandEffect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; import mage.cards.Card; -import mage.constants.CardType; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.command.Plane; -import mage.game.stack.Spell; import mage.target.Target; import mage.util.CardUtil; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class TurriIslandPlane extends Plane { public TurriIslandPlane() { - this.setName("Plane - Turri Island"); + this.setPlaneType(Planes.PLANE_TURRI_ISLAND); this.setExpansionSetCodeForImage("PCA"); // Creature spells cost {2} less to cast. @@ -47,9 +41,9 @@ public class TurriIslandPlane extends Plane { Effect chaosEffect = new RevealLibraryPutIntoHandEffect(3, new FilterCreatureCard("creature cards"), Zone.GRAVEYARD); Target chaosTarget = null; - List chaosEffects = new ArrayList(); + List chaosEffects = new ArrayList<>(); chaosEffects.add(chaosEffect); - List chaosTargets = new ArrayList(); + List chaosTargets = new ArrayList<>(); chaosTargets.add(chaosTarget); ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); @@ -76,11 +70,11 @@ class TurriIslandEffect extends CostModificationEffectImpl { this.amount = 2; this.staticText = rule; } - + protected TurriIslandEffect(TurriIslandEffect effect) { super(effect); - this.amount = effect.amount; - } + this.amount = effect.amount; + } @Override public void init(Ability source, Game game) { @@ -112,17 +106,12 @@ class TurriIslandEffect extends CostModificationEffectImpl { if (cPlane == null) { return false; } - if (!cPlane.getName().equalsIgnoreCase("Plane - Turri Island")) { + if (!cPlane.getPlaneType().equals(Planes.PLANE_TURRI_ISLAND)) { return false; } - - Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); - if (spell != null) { - return filter.match(spell, game) && selectedByRuntimeData(spell, source, game); - } else { - // used at least for flashback ability because Flashback ability doesn't use stack - Card sourceCard = game.getCard(abilityToModify.getSourceId()); - return sourceCard != null && filter.match(sourceCard, game) && selectedByRuntimeData(sourceCard, source, game); + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + if (spellCard != null) { + return filter.match(spellCard, game) && selectedByRuntimeData(spellCard, source, game); } } return false; diff --git a/Mage/src/main/java/mage/game/command/planes/UndercityReachesPlane.java b/Mage/src/main/java/mage/game/command/planes/UndercityReachesPlane.java index 1bb70f17f6..8c0eb0de09 100644 --- a/Mage/src/main/java/mage/game/command/planes/UndercityReachesPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/UndercityReachesPlane.java @@ -1,8 +1,5 @@ - package mage.game.command.planes; -import java.util.ArrayList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.ActivateIfConditionActivatedAbility; @@ -15,10 +12,8 @@ import mage.abilities.effects.common.DrawCardTargetEffect; import mage.abilities.effects.common.RollPlanarDieEffect; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect.HandSizeModification; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect; +import mage.constants.*; import mage.filter.FilterCard; import mage.game.Game; import mage.game.command.Plane; @@ -30,8 +25,10 @@ import mage.target.Target; import mage.target.targetpointer.FixedTarget; import mage.watchers.common.PlanarRollWatcher; +import java.util.ArrayList; +import java.util.List; + /** - * * @author spjspj */ public class UndercityReachesPlane extends Plane { @@ -43,7 +40,7 @@ public class UndercityReachesPlane extends Plane { } public UndercityReachesPlane() { - this.setName("Plane - Undercity Reaches"); + this.setPlaneType(Planes.PLANE_UNDERCITY_REACHES); this.setExpansionSetCodeForImage("PCA"); // Whenever a creature deals combat damage to a player, its controller may a draw a card @@ -94,7 +91,7 @@ class UndercityReachesTriggeredAbility extends TriggeredAbilityImpl { if (cPlane == null) { return false; } - if (!cPlane.getName().equalsIgnoreCase("Plane - Undercity Reaches")) { + if (!cPlane.getPlaneType().equals(Planes.PLANE_UNDERCITY_REACHES)) { return false; } diff --git a/Mage/src/main/java/mage/game/events/CostEvent.java b/Mage/src/main/java/mage/game/events/CostEvent.java index 75dee4fad2..10b76ce73b 100644 --- a/Mage/src/main/java/mage/game/events/CostEvent.java +++ b/Mage/src/main/java/mage/game/events/CostEvent.java @@ -1,28 +1,28 @@ - -package mage.game.events; - -import java.util.UUID; -import mage.abilities.costs.Cost; - -/** - * - * @author LevelX2 - */ -public class CostEvent extends GameEvent { - - protected Cost cost; - - public CostEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, Cost cost) { - super(type, targetId, sourceId, playerId); - this.cost = cost; - } - - public Cost getCost() { - return cost; - } - - public void setCost(Cost cost) { - this.cost = cost; - } - -} + +package mage.game.events; + +import java.util.UUID; +import mage.abilities.costs.Cost; + +/** + * + * @author LevelX2 + */ +public class CostEvent extends GameEvent { + + protected Cost cost; + + public CostEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, Cost cost) { + super(type, targetId, sourceId, playerId); + this.cost = cost; + } + + public Cost getCost() { + return cost; + } + + public void setCost(Cost cost) { + this.cost = cost; + } + +} diff --git a/Mage/src/main/java/mage/game/events/CreatedTokenEvent.java b/Mage/src/main/java/mage/game/events/CreatedTokenEvent.java new file mode 100644 index 0000000000..b300038a58 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/CreatedTokenEvent.java @@ -0,0 +1,12 @@ +package mage.game.events; + +import mage.game.permanent.PermanentToken; + +import java.util.UUID; + +public class CreatedTokenEvent extends GameEvent { + + public CreatedTokenEvent(UUID sourceId, PermanentToken tokenPerm) { + super(EventType.CREATED_TOKEN, tokenPerm.getId(), sourceId, tokenPerm.getControllerId()); + } +} diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index fb854bcdf1..24a483f82a 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -68,7 +68,6 @@ public class GameEvent implements Serializable { */ ZONE_CHANGE, ZONE_CHANGE_GROUP, - EMPTY_DRAW, DRAW_CARDS, // applies to an instruction to draw more than one card before any replacement effects apply to individual cards drawn DRAW_CARD, DREW_CARD, EXPLORED, @@ -85,9 +84,11 @@ public class GameEvent implements Serializable { CONVOKED, DISCARD_CARD, DISCARDED_CARD, - CYCLE_CARD, CYCLED_CARD, + DISCARDED_CARDS, + CYCLE_CARD, CYCLED_CARD, CYCLE_DRAW, CLASH, CLASHED, DAMAGE_PLAYER, + MILL_CARDS, /* DAMAGED_PLAYER targetId the id of the damaged player sourceId sourceId of the ability which caused the damage @@ -132,7 +133,7 @@ public class GameEvent implements Serializable { targetId id of the spell that's cast playerId player that casts the spell or ability amount X multiplier to change X value, default 1 - */ + */ CAST_SPELL, /* SPELL_CAST x-Costs are already defined @@ -148,8 +149,20 @@ public class GameEvent implements Serializable { */ SPELL_CAST, ACTIVATE_ABILITY, ACTIVATED_ABILITY, + /* ACTIVATE_ABILITY, ACTIVATED_ABILITY, + targetId id of the ability to activate / use + sourceId sourceId of the object with that ability + playerId player that tries to use this ability + */ + TAKE_SPECIAL_ACTION, TAKEN_SPECIAL_ACTION, // not used in implementation yet + /* TAKE_SPECIAL_ACTION, TAKEN_SPECIAL_ACTION, + targetId id of the ability to activate / use + sourceId sourceId of the object with that ability + playerId player that tries to use this ability + */ TRIGGERED_ABILITY, - COPIED_STACKOBJECT, + RESOLVING_ABILITY, + COPY_STACKOBJECT, COPIED_STACKOBJECT, /* ADD_MANA targetId id of the ability that added the mana sourceId sourceId of the ability that added the mana @@ -222,6 +235,7 @@ public class GameEvent implements Serializable { */ DECLARE_BLOCKER, BLOCKER_DECLARED, CREATURE_BLOCKED, + BATCH_BLOCK_NONCOMBAT, UNBLOCKED_ATTACKER, SEARCH_LIBRARY, LIBRARY_SEARCHED, SHUFFLE_LIBRARY, LIBRARY_SHUFFLED, @@ -240,7 +254,13 @@ public class GameEvent implements Serializable { ENTERS_THE_BATTLEFIELD_CONTROL, // 616.1b ENTERS_THE_BATTLEFIELD_COPY, // 616.1c ENTERS_THE_BATTLEFIELD, // 616.1d - TAP, TAPPED, TAPPED_FOR_MANA, + TAP, TAPPED, + TAPPED_FOR_MANA, + /* TAPPED_FOR_MANA + During calculation of the available mana for a player the "TappedForMana" event is fired to simulate triggered mana production. + By checking the inCheckPlayableState these events are handled to give back only the available mana of instead really producing mana. + IMPORTANT: Triggered non mana abilities have to ignore the event if game.inCheckPlayableState is true. + */ UNTAP, UNTAPPED, FLIP, FLIPPED, UNFLIP, UNFLIPPED, @@ -295,6 +315,7 @@ public class GameEvent implements Serializable { DESTROYED_PERMANENT, SACRIFICE_PERMANENT, SACRIFICED_PERMANENT, FIGHTED_PERMANENT, + BATCH_FIGHT, EXPLOITED_CREATURE, EVOLVED_CREATURE, EMBALMED_CREATURE, @@ -315,7 +336,7 @@ public class GameEvent implements Serializable { */ LOST_CONTROL, GAIN_CONTROL, GAINED_CONTROL, - CREATE_TOKEN, + CREATE_TOKEN, CREATED_TOKEN, /* REGENERATE targetId id of the creature to regenerate sourceId sourceId of the effect doing the regeneration @@ -397,12 +418,12 @@ public class GameEvent implements Serializable { } private GameEvent(EventType type, UUID customEventType, - UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) { + UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) { this(type, customEventType, targetId, sourceId, playerId, amount, flag, null); } private GameEvent(EventType type, UUID customEventType, - UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag, MageObjectReference reference) { + UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag, MageObjectReference reference) { this.type = type; this.customEventType = customEventType; this.targetId = targetId; diff --git a/Mage/src/main/java/mage/game/events/NumberOfTriggersEvent.java b/Mage/src/main/java/mage/game/events/NumberOfTriggersEvent.java index 79d0f2e675..5caa84f4a0 100644 --- a/Mage/src/main/java/mage/game/events/NumberOfTriggersEvent.java +++ b/Mage/src/main/java/mage/game/events/NumberOfTriggersEvent.java @@ -1,24 +1,24 @@ - - -package mage.game.events; - -import java.util.UUID; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class NumberOfTriggersEvent extends GameEvent { - - private final GameEvent sourceEvent; - - public NumberOfTriggersEvent(UUID controllerOfAbilityId, UUID sourceOfTrigger, GameEvent sourceEvent) { - super(EventType.NUMBER_OF_TRIGGERS, null, sourceOfTrigger, controllerOfAbilityId); - this.sourceEvent = sourceEvent; - this.amount = 1; // Number of times to trigger. Panharmonicon can change this. - } - - public GameEvent getSourceEvent() { - return sourceEvent; - } -} + + +package mage.game.events; + +import java.util.UUID; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class NumberOfTriggersEvent extends GameEvent { + + private final GameEvent sourceEvent; + + public NumberOfTriggersEvent(UUID controllerOfAbilityId, UUID sourceOfTrigger, GameEvent sourceEvent) { + super(EventType.NUMBER_OF_TRIGGERS, null, sourceOfTrigger, controllerOfAbilityId); + this.sourceEvent = sourceEvent; + this.amount = 1; // Number of times to trigger. Panharmonicon can change this. + } + + public GameEvent getSourceEvent() { + return sourceEvent; + } +} diff --git a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java new file mode 100644 index 0000000000..9cd6d15f4e --- /dev/null +++ b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java @@ -0,0 +1,129 @@ +package mage.game.jumpstart; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import com.google.common.base.Charsets; +import com.google.common.io.CharSource; +import com.google.common.io.Resources; + +import mage.cards.Card; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardInfo; +import mage.cards.decks.DeckCardLists; +import mage.game.GameException; + +public class JumpstartPoolGenerator { + + /** + * "Jumpstart is a new way to play Magic that mashes together themes from throughout the history of the game and + * lets you skip the deckbuilding part. Grab two boosters, shuffle them into a 40-card deck, and start playing. + * The set contains almost 500 reprints but also introduces 37 new cards that were designed to help fill out some + * of the themes. These are not going to be Standard-, Pioneer-, or Modern-legal cards, but are legal in Eternal + * formats (Legacy, Vintage, Pauper and Commander). There are no foil cards in the set. 120 cards are from Core + * Set 2021[7], and more than 400 reprints are from sets before Core Set 2021. Cards from M21 have the M21 expansion + * symbol, while new cards and other reprints will have the Jumpstart symbol. Reprints have a Post-M15 card frame. + * The new cards and cards with new art are numbered #1/78 to #78/78 (40 basic lands, TBA common, TBA uncommon, + * TBA rare, and TBA mythic rare). The other reprints with the Jumpstart expansion symbol are numbered #79 to 495." + * + * "Jumpstart is sold in 20-card boosters, and booster boxes contain 24 boosters.[9] All 20 cards in a booster fit a + * theme, and most themes are mono-color and have multiple variations of cards included in the pack, totaling 121 + * different possible pack contents. Special "Mythic Rare" packs don't have variations at all, but just one possible + * card list and can feature multiple colors. What theme each booster contains is randomized. Most packs are + * singletons, but there are some instances of having two copies of a card in the pack. All boosters contain at + * least one rare, and one in three boosters includes an extra rare. Of the 20 cards in a pack, seven or eight + * are lands. One basic land (or in the case of Rainbow, Terramorphic Expanse) features art that matches the pack's + * theme. The planeswalker themed packs use the respective Showcase lands from M21, but the other packs use brand-new + * themed land art created for the set. The booster pack is wrapped like a regular pack, but the set of cards is + * packed in an additional plastic wrap, with a face card (the "Pack Summary card") that indicates the theme and + * the color of the half-deck." + * + * Source: https://mtg.gamepedia.com/Jumpstart + * + * Announcement: https://magic.wizards.com/en/articles/archive/news/introducing-jumpstart-new-way-play-magic-2020-02-20 + * Card Pool: https://magic.wizards.com/en/articles/archive/card-image-gallery/jumpstart + * Deck Lists: https://magic.wizards.com/en/articles/archive/feature/jumpstart-decklists-2020-06-18 + */ + + private static final String RESOURCE_NAME = "jumpstart/jumpstart.txt"; + private static final List JUMPSTART_PACKS; + + static { + try { + CharSource source = Resources.asCharSource(Resources.getResource(RESOURCE_NAME), Charsets.UTF_8); + List packs = new ArrayList<>(); + JumpstartPack pack = new JumpstartPack(); + packs.add(pack); + for (String line : source.readLines()) { + if (line.isEmpty()) { + if (!pack.isEmpty()) { + pack = new JumpstartPack(); + packs.add(pack); + } + } else if (line.startsWith("#")) { + // skip comment + } else { + String[] ls = line.split(" ", 4); + int quantity = Integer.parseInt(ls[0]); + for (int i = 0; i < quantity; i++) { + pack.add(new DeckCardInfo(ls[3], ls[2], ls[1])); + } + } + } + JUMPSTART_PACKS = Collections.unmodifiableList(packs); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /* Notes + * + * 1) the pools generated by this method are for how the prerelease will be played (see https://mtg.gamepedia.com/Jumpstart#Events) + * In order to support the 4 pack version, xmage would need to support editing multiple decks and some Duo format + * similar to https://mtg.gamepedia.com/Duo_Standard + * + * 2) this treats all packs to have similar chance to open (packs will actually be opened at a particular rarity). This + * could be implemented if you know the various ratios between packs by knowing the rarities from this source: + * https://mtg.gamepedia.com/Jumpstart#Themes_and_mechanics + * + * 3) this does not attempt to add an additional rare 1/3 of the time as described here: + * https://mtg.gamepedia.com/Jumpstart#Marketing + */ + public static Set generatePool() { + try { + DeckCardLists list = new DeckCardLists(); + SecureRandom random = new SecureRandom(); + for (int i = 0; i < 2; i++) { + int index = random.nextInt(JUMPSTART_PACKS.size()); + list.getCards().addAll(JUMPSTART_PACKS.get(index).getCards()); + } + return Deck.load(list, false, false).getCards(); + } catch (GameException e) { + throw new RuntimeException(e); + } + } + + public static class JumpstartPack { + + private final List cards = new ArrayList<>(); + + public void add(DeckCardInfo card) { + cards.add(card); + } + + public boolean isEmpty() { + return cards.isEmpty(); + } + + public List getCards() { + return Collections.unmodifiableList(cards); + } + + } + +} diff --git a/Mage/src/main/java/mage/game/match/MatchImpl.java b/Mage/src/main/java/mage/game/match/MatchImpl.java index 668ef67670..535b768ccd 100644 --- a/Mage/src/main/java/mage/game/match/MatchImpl.java +++ b/Mage/src/main/java/mage/game/match/MatchImpl.java @@ -420,7 +420,11 @@ public abstract class MatchImpl implements Match { } sb.append(" Mulligan type: ").append(options.getMulliganType().toString()).append("
"); sb.append(" Free mulligans: ").append(options.getFreeMulligans()).append("
"); - sb.append("
").append("Match is ").append(this.getOptions().isRated() ? "" : "not ").append("rated
"); + if (options.isPlaneChase()) { + sb.append(" Planechase mode activated").append("
"); + } + sb.append("
"); + sb.append("Match is ").append(this.getOptions().isRated() ? "" : "not ").append("rated
"); sb.append("You have to win ").append(this.getWinsNeeded()).append(this.getWinsNeeded() == 1 ? " game" : " games").append(" to win the complete match
"); sb.append("
Game has started

"); return sb.toString(); diff --git a/Mage/src/main/java/mage/game/mulligan/CanadianHighlanderMulligan.java b/Mage/src/main/java/mage/game/mulligan/CanadianHighlanderMulligan.java index 320f39897c..7ded68c343 100644 --- a/Mage/src/main/java/mage/game/mulligan/CanadianHighlanderMulligan.java +++ b/Mage/src/main/java/mage/game/mulligan/CanadianHighlanderMulligan.java @@ -118,7 +118,7 @@ public class CanadianHighlanderMulligan extends VancouverMulligan { .append(" mulligans to ") .append(Integer.toString(numToMulliganTo)) .append(numToMulliganTo == 1 ? " card" : " cards").toString()); - player.drawCards(numToMulliganTo, game); + player.drawCards(numToMulliganTo, null, game); } } diff --git a/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java b/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java index aec6f4bc28..7bf13dccab 100644 --- a/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java +++ b/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java @@ -1,13 +1,12 @@ package mage.game.mulligan; -import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; +import mage.target.Target; +import mage.target.common.TargetCardInHand; import java.util.HashMap; import java.util.Map; @@ -15,8 +14,8 @@ import java.util.UUID; public class LondonMulligan extends Mulligan { - protected Map startingHandSizes = new HashMap<>(); - protected Map openingHandSizes = new HashMap<>(); + private final Map startingHandSizes = new HashMap<>(); + private final Map openingHandSizes = new HashMap<>(); public LondonMulligan(int freeMulligans) { super(freeMulligans); @@ -93,27 +92,20 @@ public class LondonMulligan extends Mulligan { openingHandSizes.put(playerId, openingHandSizes.get(playerId) - deduction); int newHandSize = openingHandSizes.get(player.getId()); if (deduction == 0) { - game.fireInformEvent(new StringBuilder(player.getLogName()) - .append(" mulligans for free.") - .toString()); + game.fireInformEvent(player.getLogName() + + " mulligans for free."); } else { - game.fireInformEvent(new StringBuilder(player.getLogName()) - .append(" mulligans") - .append(" down to ") - .append(newHandSize) - .append(newHandSize == 1 ? " card" : " cards").toString()); + game.fireInformEvent(player.getLogName() + + " mulligans down to " + + newHandSize + + (newHandSize == 1 ? " card" : " cards")); } - player.drawCards(numCards, game); + player.drawCards(numCards, null, game); - if (player.getHand().size() > newHandSize) { - int cardsToDiscard = player.getHand().size() - newHandSize; - Cards cards = new CardsImpl(); - cards.addAll(player.getHand()); - TargetCard target = new TargetCard(cardsToDiscard, cardsToDiscard, Zone.HAND, - new FilterCard("card" + (cardsToDiscard > 1 ? "s" : "") + " to PUT on the BOTTOM of your library (Discard for Mulligan)")); - player.chooseTarget(Outcome.Neutral, cards, target, null, game); + while (player.canRespond() && player.getHand().size() > newHandSize) { + Target target = new TargetCardInHand(new FilterCard("card (" + (player.getHand().size() - newHandSize) + " more) to put on the bottom of your library")); + player.chooseTarget(Outcome.Discard, target, null, game); player.putCardsOnBottomOfLibrary(new CardsImpl(target.getTargets()), game, null, true); - cards.removeAll(target.getTargets()); } } @@ -128,5 +120,4 @@ public class LondonMulligan extends Mulligan { mulligan.startingHandSizes.putAll(startingHandSizes); return mulligan; } - } diff --git a/Mage/src/main/java/mage/game/mulligan/Mulligan.java b/Mage/src/main/java/mage/game/mulligan/Mulligan.java index 43abf6c284..52bab013f7 100644 --- a/Mage/src/main/java/mage/game/mulligan/Mulligan.java +++ b/Mage/src/main/java/mage/game/mulligan/Mulligan.java @@ -4,9 +4,10 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; +import java.io.Serializable; import java.util.*; -public abstract class Mulligan { +public abstract class Mulligan implements Serializable { protected final int freeMulligans; protected final Map usedFreeMulligans = new HashMap<>(); diff --git a/Mage/src/main/java/mage/game/mulligan/ParisMulligan.java b/Mage/src/main/java/mage/game/mulligan/ParisMulligan.java index 89ed63c087..33b2ae0b76 100644 --- a/Mage/src/main/java/mage/game/mulligan/ParisMulligan.java +++ b/Mage/src/main/java/mage/game/mulligan/ParisMulligan.java @@ -53,7 +53,7 @@ public class ParisMulligan extends Mulligan { .append(deduction == 0 ? " for free and draws " : " down to ") .append((numCards - deduction)) .append(numCards - deduction == 1 ? " card" : " cards").toString()); - player.drawCards(numCards - deduction, game); + player.drawCards(numCards - deduction, null, game); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/Battlefield.java b/Mage/src/main/java/mage/game/permanent/Battlefield.java index 6aacd3e45f..d2e11c2c48 100644 --- a/Mage/src/main/java/mage/game/permanent/Battlefield.java +++ b/Mage/src/main/java/mage/game/permanent/Battlefield.java @@ -327,16 +327,16 @@ public class Battlefield implements Serializable { } } - public List getPhasedIn(UUID controllerId) { + public List getPhasedIn(Game game, UUID controllerId) { return field.values() .stream() - .filter(perm -> perm.getAbilities().containsKey(PhasingAbility.getInstance().getId()) + .filter(perm -> perm.hasAbility(PhasingAbility.getInstance(), game) && perm.isPhasedIn() && perm.isControlledBy(controllerId)) .collect(Collectors.toList()); } - public List getPhasedOut(UUID controllerId) { + public List getPhasedOut(Game game, UUID controllerId) { return field.values() .stream() .filter(perm -> !perm.isPhasedIn() && perm.isControlledBy(controllerId)) diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index da45f0341b..80706b1877 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -16,6 +16,8 @@ import java.util.UUID; public interface Permanent extends Card, Controllable { + void setOriginalControllerId(UUID controllerId); + void setControllerId(UUID controllerId); boolean isTapped(); @@ -103,7 +105,8 @@ public interface Permanent extends Card, Controllable { /** * @param source * @param game - * @param silentMode - use it to ignore warning message for users (e.g. for checking only) + * @param silentMode - use it to ignore warning message for users (e.g. for + * checking only) * @return */ boolean cantBeAttachedBy(MageObject source, Game game, boolean silentMode); @@ -146,23 +149,24 @@ public interface Permanent extends Card, Controllable { boolean sacrifice(UUID sourceId, Game game); - boolean regenerate(UUID sourceId, Game game); + boolean regenerate(Ability source, Game game); boolean fight(Permanent fightTarget, Ability source, Game game); + boolean fight(Permanent fightTarget, Ability source, Game game, boolean batchTrigger); + boolean entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent); String getValue(GameState state); - @Deprecated - void addAbility(Ability ability, Game game); - void addAbility(Ability ability, UUID sourceId, Game game); - void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId); - void removeAllAbilities(UUID sourceId, Game game); + void removeAbility(Ability abilityToRemove, UUID sourceId, Game game); + + void removeAbilities(List abilitiesToRemove, UUID sourceId, Game game); + void addLoyaltyUsed(); boolean canLoyaltyBeUsed(Game game); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentCard.java b/Mage/src/main/java/mage/game/permanent/PermanentCard.java index bb387139e6..a78bcebe14 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentCard.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentCard.java @@ -1,5 +1,8 @@ package mage.game.permanent; +import java.util.UUID; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; import mage.MageObject; import mage.abilities.Abilities; import mage.abilities.Ability; @@ -11,11 +14,10 @@ import mage.cards.LevelerCard; import mage.game.Game; import mage.game.events.ZoneChangeEvent; -import java.util.UUID; - /** * @author BetaSteward_at_googlemail.com */ +@SupportedSourceVersion(SourceVersion.RELEASE_8) public class PermanentCard extends PermanentImpl { protected int maxLevelCounters; @@ -53,7 +55,7 @@ public class PermanentCard extends PermanentImpl { if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId()) != null) { game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId(), null); setTransformed(true); - TransformAbility.transform(this, getSecondCardFace(), game); + TransformAbility.transform(this, getSecondCardFace(), game, null); } } } @@ -86,6 +88,7 @@ public class PermanentCard extends PermanentImpl { } } else { this.abilities = card.getAbilities().copy(); + this.spellAbility = null; // will be set on first getSpellAbility call if card has one. } // adventure cards must show adventure spell info on battlefield too /* diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 16073d3c83..829e75df52 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -10,6 +10,7 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.RegenerateSourceEffect; import mage.abilities.hint.Hint; import mage.abilities.hint.HintUtils; import mage.abilities.keyword.*; @@ -29,7 +30,6 @@ import mage.game.command.CommandObject; import mage.game.events.*; import mage.game.events.GameEvent.EventType; import mage.game.permanent.token.SquirrelToken; -import mage.game.permanent.token.Token; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; @@ -185,6 +185,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { abilities.setControllerId(controllerId); } + @Override + public void setOriginalControllerId(UUID originalControllerId) { + this.originalControllerId = originalControllerId; + } + /** * Called before each applyEffects or if after a permanent was copied for * the copied object @@ -352,62 +357,64 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public Abilities getAbilities() { - return abilities; + return super.getAbilities(); } @Override public Abilities getAbilities(Game game) { - return abilities; - } - - /** - * @param ability - * @param game - */ - @Override - public void addAbility(Ability ability, Game game) { - if (!abilities.containsKey(ability.getId())) { - Ability copyAbility = ability.copy(); - copyAbility.setControllerId(controllerId); - copyAbility.setSourceId(objectId); - if (game != null) { - game.getState().addAbility(copyAbility, this); - } - abilities.add(copyAbility); - } + return super.getAbilities(game); } @Override public void addAbility(Ability ability, UUID sourceId, Game game) { - addAbility(ability, sourceId, game, true); - } - - @Override - public void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId) { + // singleton abilities -- only one instance + // other abilities -- any amount of instances if (!abilities.containsKey(ability.getId())) { Ability copyAbility = ability.copy(); - if (createNewId) { - copyAbility.newId(); // needed so that source can get an ability multiple times (e.g. Raging Ravine) - } + copyAbility.newId(); // needed so that source can get an ability multiple times (e.g. Raging Ravine) copyAbility.setControllerId(controllerId); copyAbility.setSourceId(objectId); - game.getState().addAbility(copyAbility, sourceId, this); - abilities.add(copyAbility); - } else if (!createNewId) { - // triggered abilities must be added to the state().triggerdAbilities + // triggered abilities must be added to the state().triggers // still as long as the prev. permanent is known to the LKI (e.g. Showstopper) so gained dies triggered ability will trigger - if (!game.getBattlefield().containsPermanent(this.getId())) { - Ability copyAbility = ability.copy(); - copyAbility.setControllerId(controllerId); - copyAbility.setSourceId(objectId); + if (game != null) { + // game is null in cards viewer window (MageBook) game.getState().addAbility(copyAbility, sourceId, this); } + abilities.add(copyAbility); } } @Override public void removeAllAbilities(UUID sourceId, Game game) { - getAbilities().clear(); + // TODO: what about triggered abilities? See addAbility above -- triggers adds to GameState + abilities.clear(); + } + + @Override + public void removeAbility(Ability abilityToRemove, UUID sourceId, Game game) { + if (abilityToRemove == null) { + return; + } + + // 112.10b Effects that remove an ability remove all instances of it. + List toRemove = new ArrayList<>(); + abilities.forEach(a -> { + if (a.isSameInstance(abilityToRemove)) { + toRemove.add(a); + } + }); + + // TODO: what about triggered abilities? See addAbility above -- triggers adds to GameState + toRemove.forEach(r -> abilities.remove(r)); + } + + @Override + public void removeAbilities(List abilitiesToRemove, UUID sourceId, Game game) { + if (abilitiesToRemove == null) { + return; + } + + abilitiesToRemove.forEach(a -> removeAbility(a, sourceId, game)); } @Override @@ -729,7 +736,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { game.fireEvent(new GameEvent(EventType.GAINED_CONTROL, objectId, objectId, controllerId)); return true; - } else if (isCopy()) {// Because the previous copied abilities can be from another controller chnage controller in any case for abilities + } else if (isCopy()) {// Because the previous copied abilities can be from another controller - change controller in any case for abilities this.getAbilities(game).setControllerId(controllerId); game.getContinuousEffects().setController(objectId, controllerId); } @@ -1062,43 +1069,13 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return false; } } - if (abilities.containsKey(HexproofAbility.getInstance().getId())) { - if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) { - return false; - } - } - - if (abilities.containsKey(HexproofFromWhiteAbility.getInstance().getId())) { - if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isWhite()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromBlueAbility.getInstance().getId())) { - if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isBlue()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromBlackAbility.getInstance().getId())) { - if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isBlack()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromMonocoloredAbility.getInstance().getId())) { - if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && !source.getColor(game).isColorless() && !source.getColor(game).isMulticolored()) { - return false; - } + if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) + && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null + && abilities.stream() + .filter(HexproofBaseAbility.class::isInstance) + .map(HexproofBaseAbility.class::cast) + .anyMatch(ability -> ability.checkObject(source, game))) { + return false; } if (hasProtectionFrom(source, game)) { @@ -1144,14 +1121,18 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public boolean destroy(UUID sourceId, Game game, boolean noRegen) { + // Only permanets on the battlefield can be destroyed + if (!game.getState().getZone(getId()).equals(Zone.BATTLEFIELD)) { + return false; + } //20091005 - 701.6 if (abilities.containsKey(IndestructibleAbility.getInstance().getId())) { return false; } if (!game.replaceEvent(GameEvent.getEvent(EventType.DESTROY_PERMANENT, objectId, sourceId, controllerId, noRegen ? 1 : 0))) { - // this means destroy was successful, if object movement to graveyard will be replaced (e.g. commander to command zone) its still - // is handled as successful destroying (but not as sucessful "dies this way" for destroying). + // this means destroy was successful, if object movement to graveyard will be replaced (e.g. commander to command zone) it's still + // handled as successful destroying (but not as sucessful "dies this way" for destroying). if (moveToZone(Zone.GRAVEYARD, sourceId, game, false)) { if (!game.isSimulation()) { String logName; @@ -1192,13 +1173,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } @Override - public boolean regenerate(UUID sourceId, Game game) { + public boolean regenerate(Ability source, Game game) { //20110930 - 701.12 - if (!game.replaceEvent(GameEvent.getEvent(EventType.REGENERATE, objectId, sourceId, controllerId))) { + if (!game.replaceEvent(GameEvent.getEvent(EventType.REGENERATE, objectId, source.getSourceId(), controllerId))) { this.tap(game); this.removeFromCombat(game); this.removeAllDamage(game); - game.fireEvent(GameEvent.getEvent(EventType.REGENERATED, objectId, sourceId, controllerId)); + + // remove one regen shield + RegenerateSourceEffect.decRegenerationShieldsAmount(game, this.getId()); + + game.fireEvent(GameEvent.getEvent(EventType.REGENERATED, objectId, source.getSourceId(), controllerId)); return true; } return false; @@ -1580,10 +1565,24 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public boolean fight(Permanent fightTarget, Ability source, Game game) { + return this.fight(fightTarget, source, game, true); + } + + @Override + public boolean fight(Permanent fightTarget, Ability source, Game game, boolean batchTrigger) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FIGHTED_PERMANENT, fightTarget.getId(), getId(), source.getControllerId())); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FIGHTED_PERMANENT, getId(), fightTarget.getId(), source.getControllerId())); - damage(fightTarget.getPower().getValue(), fightTarget.getId(), game, false, true); + damage(fightTarget.getPower().getValue(), fightTarget.getId(), game); fightTarget.damage(getPower().getValue(), getId(), game); + if (!batchTrigger) { + return true; + } + Set morSet = new HashSet<>(); + morSet.add(new MageObjectReference(this, game)); + morSet.add(new MageObjectReference(fightTarget, game)); + String data = UUID.randomUUID().toString(); + game.getState().setValue("batchFight_" + data, morSet); + game.fireEvent(GameEvent.getEvent(EventType.BATCH_FIGHT, getId(), getId(), source.getControllerId(), data, 0)); return true; } @@ -1644,9 +1643,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List appliedEffects) { Zone fromZone = game.getState().getZone(objectId); ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, ownerId, fromZone, Zone.EXILED, appliedEffects); - ZoneChangeInfo.Exile info = new ZoneChangeInfo.Exile(event, exileId, name); + ZoneChangeInfo.Exile zcInfo = new ZoneChangeInfo.Exile(event, exileId, name); - boolean successfullyMoved = ZonesHandler.moveCard(info, game); + boolean successfullyMoved = ZonesHandler.moveCard(zcInfo, game); //20180810 - 701.3d detachAllAttachments(game); return successfullyMoved; diff --git a/Mage/src/main/java/mage/game/permanent/PermanentToken.java b/Mage/src/main/java/mage/game/permanent/PermanentToken.java index ba809c2056..b97a168e0f 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentToken.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentToken.java @@ -58,8 +58,9 @@ public class PermanentToken extends PermanentImpl { this.abilities.addAll(token.getAbilities()); } else { // first time -> create ContinuousEffects only once + // so sourceId must be null (keep triggered abilities forever?) for (Ability ability : token.getAbilities()) { - this.addAbility(ability, game); + this.addAbility(ability, null, game); } } this.abilities.setControllerId(this.controllerId); diff --git a/Mage/src/main/java/mage/game/permanent/token/ATATToken.java b/Mage/src/main/java/mage/game/permanent/token/ATATToken.java index f2d7c93246..e58cb29e90 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ATATToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ATATToken.java @@ -4,7 +4,7 @@ package mage.game.permanent.token; import mage.constants.CardType; import mage.constants.SubType; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; /** @@ -19,7 +19,7 @@ public final class ATATToken extends TokenImpl { cardType.add(CardType.CREATURE); cardType.add(CardType.ARTIFACT); color.setWhite(true); - addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new TrooperToken(), 2))); + addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new TrooperToken(), 2))); subtype.add(SubType.ATAT); } diff --git a/Mage/src/main/java/mage/game/permanent/token/AkoumStonewakerElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/AkoumStonewakerElementalToken.java index d992ba44f4..b4137682d7 100644 --- a/Mage/src/main/java/mage/game/permanent/token/AkoumStonewakerElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/AkoumStonewakerElementalToken.java @@ -23,6 +23,11 @@ public final class AkoumStonewakerElementalToken extends TokenImpl { this.addAbility(TrampleAbility.getInstance()); this.addAbility(HasteAbility.getInstance()); availableImageSetCodes.addAll(Arrays.asList("BFZ", "MH1")); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("BFZ")) { setTokenType(2); diff --git a/Mage/src/main/java/mage/game/permanent/token/AkroanSoldierToken.java b/Mage/src/main/java/mage/game/permanent/token/AkroanSoldierToken.java index 6715ee73f1..49b481684e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/AkroanSoldierToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/AkroanSoldierToken.java @@ -1,13 +1,13 @@ - - package mage.game.permanent.token; -import mage.constants.CardType; -import mage.constants.SubType; + import mage.MageInt; import mage.abilities.keyword.HasteAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; /** - * * @author spjspj */ public final class AkroanSoldierToken extends TokenImpl { @@ -21,6 +21,8 @@ public final class AkroanSoldierToken extends TokenImpl { power = new MageInt(1); toughness = new MageInt(1); this.addAbility(HasteAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("THS"); } public AkroanSoldierToken(final AkroanSoldierToken token) { @@ -30,4 +32,13 @@ public final class AkroanSoldierToken extends TokenImpl { public AkroanSoldierToken copy() { return new AkroanSoldierToken(this); } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("THS")) { + this.setTokenType(3); + } + } } diff --git a/Mage/src/main/java/mage/game/permanent/token/AngelToken.java b/Mage/src/main/java/mage/game/permanent/token/AngelToken.java index 960758bc3d..0a1f871834 100644 --- a/Mage/src/main/java/mage/game/permanent/token/AngelToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/AngelToken.java @@ -1,36 +1,29 @@ package mage.game.permanent.token; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import mage.MageInt; import mage.abilities.keyword.FlyingAbility; import mage.constants.CardType; import mage.constants.SubType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public final class AngelToken extends TokenImpl { static final private List tokenImageSets = new ArrayList<>(); - static { - tokenImageSets.addAll(Arrays.asList("AVR", "C14", "CFX", "GTC", "ISD", "M14", "ORI", "SOI", "ZEN", "C15", "MM3")); - } - public AngelToken() { - this((String)null); - } - - public AngelToken(String setCode) { super("Angel", "4/4 white Angel creature token with flying"); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); - cardType.add(CardType.CREATURE); color.setWhite(true); subtype.add(SubType.ANGEL); power = new MageInt(4); toughness = new MageInt(4); addAbility(FlyingAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("APC", "AVR", "C14", "C15", "C18", "CON", "DDQ", "GTC", + "ISD", "M14", "MM3", "NEM", "OGW", "ORI", "PC2", "SCG", "SOI", "ZEN", "C20", "M21"); } public AngelToken(final AngelToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/AssassinToken2.java b/Mage/src/main/java/mage/game/permanent/token/AssassinToken2.java index 5f7b407718..2ddde6e5b4 100644 --- a/Mage/src/main/java/mage/game/permanent/token/AssassinToken2.java +++ b/Mage/src/main/java/mage/game/permanent/token/AssassinToken2.java @@ -1,16 +1,10 @@ package mage.game.permanent.token; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.common.DestroyPlaneswalkerWhenDamagedTriggeredAbility; import mage.abilities.keyword.DeathtouchAbility; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.target.targetpointer.FixedTarget; /** * @author TheElk801 @@ -25,7 +19,7 @@ public final class AssassinToken2 extends TokenImpl { power = new MageInt(1); toughness = new MageInt(1); addAbility(DeathtouchAbility.getInstance()); - addAbility(new AssassinToken2TriggeredAbility()); + addAbility(new DestroyPlaneswalkerWhenDamagedTriggeredAbility()); setOriginalExpansionSetCode("WAR"); } @@ -39,40 +33,3 @@ public final class AssassinToken2 extends TokenImpl { } } -class AssassinToken2TriggeredAbility extends TriggeredAbilityImpl { - - AssassinToken2TriggeredAbility() { - super(Zone.BATTLEFIELD, null); - } - - private AssassinToken2TriggeredAbility(final AssassinToken2TriggeredAbility effect) { - super(effect); - } - - @Override - public AssassinToken2TriggeredAbility copy() { - return new AssassinToken2TriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(getSourceId())) { - Effect effect = new DestroyTargetEffect(); - effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); - this.getEffects().clear(); - this.addEffect(effect); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever this creature deals damage to a planeswalker, destroy that planeswalker."; - } -} \ No newline at end of file diff --git a/Mage/src/main/java/mage/game/permanent/token/BeastToken.java b/Mage/src/main/java/mage/game/permanent/token/BeastToken.java index 78e19582c0..be61632bf1 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BeastToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/BeastToken.java @@ -3,50 +3,47 @@ package mage.game.permanent.token; import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; +import mage.util.RandomUtil; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** * @author BetaSteward_at_googlemail.com */ public final class BeastToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("C14", "LRW", "M15", "M14", "DDL", "M13", "M12", "GVL", "NPH", "M11", "M10", "EVE", "MM3", "CMA", "E01", "C19")); - } - public BeastToken() { - this(null, 0); - } - - public BeastToken(String setCode) { - this(setCode, 0); - } - - public BeastToken(String setCode, int tokenType) { super("Beast", "3/3 green Beast creature token"); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.BEAST); power = new MageInt(3); toughness = new MageInt(3); + availableImageSetCodes = Arrays.asList("5DN", "C14", "C16", "C19", "CMA", "CMD", "CN2", + "DD3C", "DD3GVL", "DDD", "DDL", "DST", "E01", "EVE", "LRW", "M10", "M11", "M12", + "M13", "M14", "M15", "MM3", "NPH", "PC2", "USG", "M19", "IKO", "M21"); } @Override public void setExpansionSetCodeForImage(String code) { super.setExpansionSetCodeForImage(code); - if (getOriginalExpansionSetCode().equals("M15")) { + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("M15")) { this.setTokenType(2); } - if (getOriginalExpansionSetCode().equals("GVL") || getOriginalExpansionSetCode().equals("C14") || getOriginalExpansionSetCode().equals("DDD") || getOriginalExpansionSetCode().equals("MM3")) { - this.setTokenType(1); + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("CMD")) { + this.setTokenType(2); + } + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("GVL")) { + this.setTokenType(2); + } + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("DD3C")) { + setTokenType(RandomUtil.nextInt(2) + 1); + } + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("MM3")) { + setTokenType(RandomUtil.nextInt(2) + 1); } } @@ -56,6 +53,6 @@ public final class BeastToken extends TokenImpl { @Override public BeastToken copy() { - return new BeastToken(this); //To change body of generated methods, choose Tools | Templates. + return new BeastToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java b/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java index c25bd3e8b4..8425d40c95 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java +++ b/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java @@ -16,7 +16,7 @@ public final class BeastToken2 extends TokenImpl { static final private List tokenImageSets = new ArrayList<>(); static { - tokenImageSets.addAll(Arrays.asList("ZEN", "C14", "DDD", "C15", "GVL", "MM3", "CMA", "E01", "C19")); + tokenImageSets.addAll(Arrays.asList("ZEN", "C14", "DDD", "C15", "GVL", "MM3", "CMA", "E01", "C19", "C20")); } public BeastToken2() { diff --git a/Mage/src/main/java/mage/game/permanent/token/BirdToken.java b/Mage/src/main/java/mage/game/permanent/token/BirdToken.java index c73f04867b..8dd75ae842 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BirdToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/BirdToken.java @@ -1,15 +1,14 @@ - - package mage.game.permanent.token; -import java.util.Arrays; import mage.MageInt; import mage.abilities.keyword.FlyingAbility; import mage.constants.CardType; import mage.constants.SubType; +import mage.util.RandomUtil; + +import java.util.Arrays; /** - * * @author LoneFox */ public final class BirdToken extends TokenImpl { @@ -22,7 +21,8 @@ public final class BirdToken extends TokenImpl { power = new MageInt(1); toughness = new MageInt(1); addAbility(FlyingAbility.getInstance()); - availableImageSetCodes.addAll(Arrays.asList("BNG", "RTR", "ZEN", "C16", "MM3", "DGM")); + + availableImageSetCodes.addAll(Arrays.asList("BNG", "CSP", "DGM", "JUD", "MM3", "RTR", "VMA", "ZEN", "MH1", "C20", "M21")); } public BirdToken(final BirdToken token) { @@ -30,18 +30,16 @@ public final class BirdToken extends TokenImpl { } @Override - public BirdToken copy() { + public BirdToken copy() { return new BirdToken(this); } @Override public void setExpansionSetCodeForImage(String code) { super.setExpansionSetCodeForImage(code); - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("BNG")) { - this.setTokenType(1); - } + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C16")) { - this.setTokenType(1); + setTokenType(RandomUtil.nextInt(2) + 1); } } } diff --git a/Mage/src/main/java/mage/game/permanent/token/CallTheSkyBreakerElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/CallTheSkyBreakerElementalToken.java index 1e43effc15..3b6ef19b38 100644 --- a/Mage/src/main/java/mage/game/permanent/token/CallTheSkyBreakerElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/CallTheSkyBreakerElementalToken.java @@ -1,4 +1,3 @@ - package mage.game.permanent.token; import mage.MageInt; @@ -7,7 +6,6 @@ import mage.constants.CardType; import mage.constants.SubType; /** - * * @author spjspj */ public final class CallTheSkyBreakerElementalToken extends TokenImpl { @@ -18,14 +16,20 @@ public final class CallTheSkyBreakerElementalToken extends TokenImpl { color.setBlue(true); color.setRed(true); subtype.add(SubType.ELEMENTAL); - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("EMA")) { - setTokenType(2); - } power = new MageInt(5); toughness = new MageInt(5); this.addAbility(FlyingAbility.getInstance()); } + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("EMA")) { + setTokenType(2); + } + } + public CallTheSkyBreakerElementalToken(final CallTheSkyBreakerElementalToken token) { super(token); } diff --git a/Mage/src/main/java/mage/game/permanent/token/CatBirdToken.java b/Mage/src/main/java/mage/game/permanent/token/CatBirdToken.java new file mode 100644 index 0000000000..c4b276bcda --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/CatBirdToken.java @@ -0,0 +1,32 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class CatBirdToken extends TokenImpl { + + public CatBirdToken() { + super("Cat Bird", "1/1 white Cat Bird creature token with flying"); + cardType.add(CardType.CREATURE); + color.setWhite(true); + subtype.add(SubType.CAT); + subtype.add(SubType.BIRD); + power = new MageInt(1); + toughness = new MageInt(1); + addAbility(FlyingAbility.getInstance()); + } + + private CatBirdToken(final CatBirdToken token) { + super(token); + } + + @Override + public CatBirdToken copy() { + return new CatBirdToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/CatToken.java b/Mage/src/main/java/mage/game/permanent/token/CatToken.java index bfc82b3969..7848d6cff8 100644 --- a/Mage/src/main/java/mage/game/permanent/token/CatToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/CatToken.java @@ -1,43 +1,25 @@ - - package mage.game.permanent.token; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; +import java.util.Arrays; + /** - * * @author LoneFox */ public final class CatToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("SOM", "M13", "M14", "C14", "C15", "C17")); - } - public CatToken() { - this(null, 0); - } - - public CatToken(String setCode) { - this(setCode, 0); - } - - public CatToken(String setCode, int tokenType) { super("Cat", "2/2 white Cat creature token"); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); cardType.add(CardType.CREATURE); color.setWhite(true); subtype.add(SubType.CAT); power = new MageInt(2); toughness = new MageInt(2); + + availableImageSetCodes = Arrays.asList("MBP", "C14", "C15", "C17", "C18", "M13", "M14", "MBS", "SOM"); } public CatToken(final CatToken token) { @@ -47,5 +29,4 @@ public final class CatToken extends TokenImpl { public CatToken copy() { return new CatToken(this); } - } diff --git a/Mage/src/main/java/mage/game/permanent/token/CatToken2.java b/Mage/src/main/java/mage/game/permanent/token/CatToken2.java index f751d049e0..3e9ce3a75d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/CatToken2.java +++ b/Mage/src/main/java/mage/game/permanent/token/CatToken2.java @@ -1,5 +1,3 @@ - - package mage.game.permanent.token; import mage.MageInt; @@ -7,26 +5,25 @@ import mage.abilities.keyword.LifelinkAbility; import mage.constants.CardType; import mage.constants.SubType; +import java.util.Arrays; + /** - * * @author fireshoes */ public final class CatToken2 extends TokenImpl { public CatToken2() { - this((String)null); - } - - public CatToken2(String setCode) { super("Cat", "1/1 white Cat creature token with lifelink"); - setOriginalExpansionSetCode("AKH"); cardType.add(CardType.CREATURE); color.setWhite(true); subtype.add(SubType.CAT); power = new MageInt(1); toughness = new MageInt(1); addAbility(LifelinkAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("AKH", "M19", "IKO"); } + public CatToken2(final CatToken2 token) { super(token); } @@ -34,5 +31,5 @@ public final class CatToken2 extends TokenImpl { public CatToken2 copy() { return new CatToken2(this); } - + } diff --git a/Mage/src/main/java/mage/game/permanent/token/RetrofitterFoundryToken.java b/Mage/src/main/java/mage/game/permanent/token/Construct4Token.java similarity index 53% rename from Mage/src/main/java/mage/game/permanent/token/RetrofitterFoundryToken.java rename to Mage/src/main/java/mage/game/permanent/token/Construct4Token.java index c413d20bca..13936b0261 100644 --- a/Mage/src/main/java/mage/game/permanent/token/RetrofitterFoundryToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/Construct4Token.java @@ -1,34 +1,32 @@ package mage.game.permanent.token; +import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -import mage.MageInt; + +import java.util.Arrays; /** - * * @author TheElk801 */ -public final class RetrofitterFoundryToken extends TokenImpl { +public final class Construct4Token extends TokenImpl { - public RetrofitterFoundryToken() { - this("C18"); - } - - public RetrofitterFoundryToken(String setCode) { + public Construct4Token() { super("Construct", "4/4 colorless Construct artifact creature token"); - this.setOriginalExpansionSetCode(setCode); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); subtype.add(SubType.CONSTRUCT); power = new MageInt(4); toughness = new MageInt(4); + + availableImageSetCodes.addAll(Arrays.asList("C18", "M21")); } - public RetrofitterFoundryToken(final RetrofitterFoundryToken token) { + public Construct4Token(final Construct4Token token) { super(token); } - public RetrofitterFoundryToken copy() { - return new RetrofitterFoundryToken(this); + public Construct4Token copy() { + return new Construct4Token(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/DemonToken.java b/Mage/src/main/java/mage/game/permanent/token/DemonToken.java index 9f494ab5cb..e39d299c50 100644 --- a/Mage/src/main/java/mage/game/permanent/token/DemonToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/DemonToken.java @@ -1,15 +1,13 @@ - - package mage.game.permanent.token; -import java.util.Arrays; import mage.MageInt; import mage.abilities.keyword.FlyingAbility; import mage.constants.CardType; import mage.constants.SubType; +import java.util.Arrays; + /** - * * @author LoneFox */ public final class DemonToken extends TokenImpl { @@ -22,7 +20,8 @@ public final class DemonToken extends TokenImpl { power = new MageInt(5); toughness = new MageInt(5); addAbility(FlyingAbility.getInstance()); - availableImageSetCodes.addAll(Arrays.asList("ISD", "AVR", "C14", "ORI")); + + availableImageSetCodes.addAll(Arrays.asList("AVR", "C14", "DD3A", "ISD", "ORI", "M20", "M21")); } public DemonToken(final DemonToken token) { @@ -30,15 +29,7 @@ public final class DemonToken extends TokenImpl { } @Override - public DemonToken copy() { + public DemonToken copy() { return new DemonToken(this); } - - @Override - public void setExpansionSetCodeForImage(String code) { - super.setExpansionSetCodeForImage(code); - if (getOriginalExpansionSetCode().equals("C14")) { - this.setTokenType(2); - } - } } diff --git a/Mage/src/main/java/mage/game/permanent/token/DevilToken.java b/Mage/src/main/java/mage/game/permanent/token/DevilToken.java index bb7da534fe..9e30bee955 100644 --- a/Mage/src/main/java/mage/game/permanent/token/DevilToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/DevilToken.java @@ -5,7 +5,7 @@ import java.util.Collections; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.constants.CardType; @@ -29,7 +29,7 @@ public final class DevilToken extends TokenImpl { toughness = new MageInt(1); Effect effect = new DamageTargetEffect(1); effect.setText("it deals 1 damage to any target"); - Ability ability = new DiesTriggeredAbility(effect); + Ability ability = new DiesSourceTriggeredAbility(effect); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java b/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java new file mode 100644 index 0000000000..17f93178ef --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java @@ -0,0 +1,35 @@ +package mage.game.permanent.token; + + import mage.MageInt; + import mage.abilities.keyword.TrampleAbility; + import mage.constants.CardType; + import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class DinosaurBeastToken extends TokenImpl { + + public DinosaurBeastToken() { + this(0); + } + public DinosaurBeastToken(int xValue) { + super("Dinosaur Beast", "X/X green Dinosaur Beast creature token with trample"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add(SubType.DINOSAUR); + subtype.add(SubType.BEAST); + power = new MageInt(xValue); + toughness = new MageInt(xValue); + addAbility(TrampleAbility.getInstance()); + } + + private DinosaurBeastToken(final DinosaurBeastToken token) { + super(token); + } + + @Override + public DinosaurBeastToken copy() { + return new DinosaurBeastToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/DinosaurCatToken.java b/Mage/src/main/java/mage/game/permanent/token/DinosaurCatToken.java new file mode 100644 index 0000000000..c5a2db7fd2 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/DinosaurCatToken.java @@ -0,0 +1,27 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +public final class DinosaurCatToken extends TokenImpl { + public DinosaurCatToken() { + super("Dinosaur Cat", "2/2 red and white Dinosaur Cat creature token"); + cardType.add(CardType.CREATURE); + color.setRed(true); + color.setWhite(true); + subtype.add(SubType.DINOSAUR); + subtype.add(SubType.CAT); + power = new MageInt(2); + toughness = new MageInt(2); + } + + public DinosaurCatToken(final DinosaurCatToken token) { + super(token); + } + + @Override + public DinosaurCatToken copy() { + return new DinosaurCatToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/DinosaurHasteToken.java b/Mage/src/main/java/mage/game/permanent/token/DinosaurHasteToken.java new file mode 100644 index 0000000000..49a560d930 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/DinosaurHasteToken.java @@ -0,0 +1,30 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.HasteAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class DinosaurHasteToken extends TokenImpl { + + public DinosaurHasteToken() { + super("Dinosaur", "1/1 red Dinosaur creature token with haste"); + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add(SubType.DINOSAUR); + power = new MageInt(1); + toughness = new MageInt(1); + addAbility(HasteAbility.getInstance()); + } + + private DinosaurHasteToken(final DinosaurHasteToken token) { + super(token); + } + + public DinosaurHasteToken copy() { + return new DinosaurHasteToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/ElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/ElementalToken.java deleted file mode 100644 index 5369b5bff6..0000000000 --- a/Mage/src/main/java/mage/game/permanent/token/ElementalToken.java +++ /dev/null @@ -1,63 +0,0 @@ - - -package mage.game.permanent.token; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import mage.MageInt; -import mage.abilities.keyword.HasteAbility; -import mage.constants.CardType; -import mage.constants.SubType; - -/** - * - * @author magenoxx - */ -public final class ElementalToken extends TokenImpl { - - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("OGW", "CON", "DIS")); - } - - public ElementalToken() { - this ("OGW", 0); - } - - public ElementalToken(String setCode, int tokenType) { - super("Elemental", "3/1 red Elemental creature token"); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); - cardType.add(CardType.CREATURE); - color.setRed(true); - subtype.add(SubType.ELEMENTAL); - power = new MageInt(3); - toughness = new MageInt(1); - - this.setOriginalExpansionSetCode("CON"); - } - - public ElementalToken(String setCode, int tokenType, boolean hasHaste) { - super("Elemental", "3/1 red Elemental creature token"); - setTokenType(tokenType); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); - cardType.add(CardType.CREATURE); - color.setRed(true); - subtype.add(SubType.ELEMENTAL); - power = new MageInt(3); - toughness = new MageInt(1); - - if (hasHaste) this.addAbility(HasteAbility.getInstance()); - } - - public ElementalToken(final ElementalToken token) { - super(token); - } - - public ElementalToken copy() { - return new ElementalToken(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java b/Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java new file mode 100644 index 0000000000..b01be1d548 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java @@ -0,0 +1,49 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.HasteAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author magenoxx + */ +public final class ElementalTokenWithHaste extends TokenImpl { + + public ElementalTokenWithHaste() { + super("Elemental", "3/1 red Elemental creature token with haste"); + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add(SubType.ELEMENTAL); + power = new MageInt(3); + toughness = new MageInt(1); + this.addAbility(HasteAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("C20", "MBP", "OGW", "SOK", "MRD"); + } + + public ElementalTokenWithHaste(final ElementalTokenWithHaste token) { + super(token); + } + + public ElementalTokenWithHaste copy() { + return new ElementalTokenWithHaste(this); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("OGW")) { + setTokenType(2); + } + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C20")) { + setTokenType(1); + } + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("SOK")) { + setTokenType(1); + } + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/FeatherToken.java b/Mage/src/main/java/mage/game/permanent/token/FeatherToken.java new file mode 100644 index 0000000000..4e056c3d6f --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/FeatherToken.java @@ -0,0 +1,39 @@ +package mage.game.permanent.token; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; + +public final class FeatherToken extends TokenImpl { + + private static final FilterCard filter = new FilterCard("Phoenix card"); + + static { + filter.add(SubType.PHOENIX.getPredicate()); + } + + public FeatherToken() { + super("Feather", "red artifact token named Feather with \"{1}, Sacrifice Feather: Return target Phoenix card from your graveyard to the battlefield tapped.\""); + + this.cardType.add(CardType.ARTIFACT); + this.color.setRed(true); + Ability ability = new SimpleActivatedAbility( + new ReturnFromGraveyardToBattlefieldTargetEffect(true), new GenericManaCost(1) + ); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private FeatherToken(final FeatherToken token) { + super(token); + } + + public FeatherToken copy() { + return new FeatherToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java b/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java index fa5a9797d5..5629aad50c 100644 --- a/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java @@ -5,7 +5,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; @@ -26,7 +26,7 @@ public final class FesteringGoblinToken extends TokenImpl { power = new MageInt(1); toughness = new MageInt(1); - Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false); + Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java b/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java index fd5b1f0c08..2fb450d53e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java @@ -1,7 +1,7 @@ package mage.game.permanent.token; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.constants.CardType; import mage.constants.SubType; @@ -26,7 +26,7 @@ public final class GarrukCursedHuntsmanToken extends TokenImpl { power = new MageInt(2); toughness = new MageInt(2); - this.addAbility(new DiesTriggeredAbility(new AddCountersAllEffect(CounterType.LOYALTY.createInstance(), filter))); + this.addAbility(new DiesSourceTriggeredAbility(new AddCountersAllEffect(CounterType.LOYALTY.createInstance(), filter))); } public GarrukCursedHuntsmanToken(final GarrukCursedHuntsmanToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/WortTheRaidmotherToken.java b/Mage/src/main/java/mage/game/permanent/token/GoblinWarriorToken.java similarity index 61% rename from Mage/src/main/java/mage/game/permanent/token/WortTheRaidmotherToken.java rename to Mage/src/main/java/mage/game/permanent/token/GoblinWarriorToken.java index b59b644580..ec79599e77 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WortTheRaidmotherToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GoblinWarriorToken.java @@ -1,17 +1,17 @@ - - package mage.game.permanent.token; + +import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -import mage.MageInt; + +import java.util.Arrays; /** - * * @author spjspj */ -public final class WortTheRaidmotherToken extends TokenImpl { +public final class GoblinWarriorToken extends TokenImpl { - public WortTheRaidmotherToken() { + public GoblinWarriorToken() { super("Goblin Warrior", "1/1 red and green Goblin Warrior creature token"); cardType.add(CardType.CREATURE); color.setRed(true); @@ -20,13 +20,15 @@ public final class WortTheRaidmotherToken extends TokenImpl { subtype.add(SubType.WARRIOR); power = new MageInt(1); toughness = new MageInt(1); + + availableImageSetCodes.addAll(Arrays.asList("C20")); } - public WortTheRaidmotherToken(final WortTheRaidmotherToken token) { + public GoblinWarriorToken(final GoblinWarriorToken token) { super(token); } - public WortTheRaidmotherToken copy() { - return new WortTheRaidmotherToken(this); + public GoblinWarriorToken copy() { + return new GoblinWarriorToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/GoblinWizardToken.java b/Mage/src/main/java/mage/game/permanent/token/GoblinWizardToken.java new file mode 100644 index 0000000000..1ab3106fb8 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/GoblinWizardToken.java @@ -0,0 +1,35 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.ProwessAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class GoblinWizardToken extends TokenImpl { + + public GoblinWizardToken() { + super("Goblin Wizard", "1/1 red Goblin Wizard creature token with prowess"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.GOBLIN); + subtype.add(SubType.WIZARD); + color.setRed(true); + power = new MageInt(1); + toughness = new MageInt(1); + this.addAbility(new ProwessAbility()); + + availableImageSetCodes.addAll(Arrays.asList("M21")); + } + + private GoblinWizardToken(final GoblinWizardToken token) { + super(token); + } + + public GoblinWizardToken copy() { + return new GoblinWizardToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java new file mode 100644 index 0000000000..25d37498d9 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java @@ -0,0 +1,35 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.VigilanceAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author JayDi85 + */ +public final class GreenAndWhiteElementalToken extends TokenImpl { + + public GreenAndWhiteElementalToken() { + super("Elemental", "8/8 green and white Elemental creature token with vigilance"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + color.setWhite(true); + this.subtype.add(SubType.ELEMENTAL); + power = new MageInt(8); + toughness = new MageInt(8); + this.addAbility(VigilanceAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("GK1", "PTC", "RTR"); + } + + public GreenAndWhiteElementalToken(final GreenAndWhiteElementalToken token) { + super(token); + } + + public GreenAndWhiteElementalToken copy() { + return new GreenAndWhiteElementalToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/GreenCat2Token.java b/Mage/src/main/java/mage/game/permanent/token/GreenCat2Token.java new file mode 100644 index 0000000000..7d7d6ab7f0 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/GreenCat2Token.java @@ -0,0 +1,41 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class GreenCat2Token extends TokenImpl { + + public GreenCat2Token() { + super("Cat", "2/2 green Cat creature token"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add(SubType.CAT); + power = new MageInt(2); + toughness = new MageInt(2); + + availableImageSetCodes.addAll(Arrays.asList("M21")); + } + + private GreenCat2Token(final GreenCat2Token token) { + super(token); + } + + public GreenCat2Token copy() { + return new GreenCat2Token(this); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("M21")) { + this.setTokenType(2); + } + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/WaitingInTheWeedsCatToken.java b/Mage/src/main/java/mage/game/permanent/token/GreenCatToken.java similarity index 58% rename from Mage/src/main/java/mage/game/permanent/token/WaitingInTheWeedsCatToken.java rename to Mage/src/main/java/mage/game/permanent/token/GreenCatToken.java index d7d78e4205..d90855b0e4 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WaitingInTheWeedsCatToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GreenCatToken.java @@ -1,30 +1,32 @@ - package mage.game.permanent.token; import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; +import java.util.Arrays; + /** - * * @author spjspj */ -public final class WaitingInTheWeedsCatToken extends TokenImpl { +public final class GreenCatToken extends TokenImpl { - public WaitingInTheWeedsCatToken() { + public GreenCatToken() { super("Cat", "1/1 green Cat creature token"); cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.CAT); power = new MageInt(1); toughness = new MageInt(1); + + availableImageSetCodes.addAll(Arrays.asList("6ED", "M21")); } - - public WaitingInTheWeedsCatToken(final WaitingInTheWeedsCatToken token) { + + public GreenCatToken(final GreenCatToken token) { super(token); } - public WaitingInTheWeedsCatToken copy() { - return new WaitingInTheWeedsCatToken(this); + public GreenCatToken copy() { + return new GreenCatToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/HoundToken.java b/Mage/src/main/java/mage/game/permanent/token/GreenDogToken.java similarity index 52% rename from Mage/src/main/java/mage/game/permanent/token/HoundToken.java rename to Mage/src/main/java/mage/game/permanent/token/GreenDogToken.java index a20c160fc1..dddd731733 100644 --- a/Mage/src/main/java/mage/game/permanent/token/HoundToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GreenDogToken.java @@ -1,31 +1,29 @@ - - package mage.game.permanent.token; + +import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -import mage.MageInt; /** - * * @author spjspj */ -public final class HoundToken extends TokenImpl { +public final class GreenDogToken extends TokenImpl { - public HoundToken() { - super("Hound", "1/1 green Hound creature token"); + public GreenDogToken() { + super("Dog", "1/1 green Dog creature token"); cardType.add(CardType.CREATURE); - subtype.add(SubType.HOUND); + subtype.add(SubType.DOG); color.setGreen(true); power = new MageInt(1); toughness = new MageInt(1); } - public HoundToken(final HoundToken token) { + private GreenDogToken(final GreenDogToken token) { super(token); } - public HoundToken copy() { - return new HoundToken(this); + public GreenDogToken copy() { + return new GreenDogToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/GriffinToken.java b/Mage/src/main/java/mage/game/permanent/token/GriffinToken.java index 5d59a2248e..4914ca065b 100644 --- a/Mage/src/main/java/mage/game/permanent/token/GriffinToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GriffinToken.java @@ -1,13 +1,13 @@ - - package mage.game.permanent.token; -import mage.constants.CardType; -import mage.constants.SubType; + import mage.MageInt; import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; /** - * * @author spjspj */ public final class GriffinToken extends TokenImpl { @@ -16,11 +16,12 @@ public final class GriffinToken extends TokenImpl { super("Griffin", "2/2 white Griffin creature token with flying"); cardType.add(CardType.CREATURE); color.setWhite(true); - subtype.add(SubType.GRIFFIN); power = new MageInt(2); toughness = new MageInt(2); this.addAbility(FlyingAbility.getInstance()); + + availableImageSetCodes.addAll(Arrays.asList("DDG", "DDH", "DDL", "TSP", "M21")); } public GriffinToken(final GriffinToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/HornetQueenInsectToken.java b/Mage/src/main/java/mage/game/permanent/token/HornetQueenInsectToken.java index 91fea8b5ef..cc93e12e1d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/HornetQueenInsectToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/HornetQueenInsectToken.java @@ -1,21 +1,20 @@ - - package mage.game.permanent.token; -import mage.constants.CardType; -import mage.constants.SubType; + import mage.MageInt; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; /** - * * @author spjspj */ public final class HornetQueenInsectToken extends TokenImpl { public HornetQueenInsectToken() { super("Insect", "1/1 green Insect creature token with flying and deathtouch"); - setOriginalExpansionSetCode("M15"); cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.INSECT); @@ -23,6 +22,8 @@ public final class HornetQueenInsectToken extends TokenImpl { toughness = new MageInt(1); addAbility(FlyingAbility.getInstance()); addAbility(DeathtouchAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("M15", "C20"); } public HornetQueenInsectToken(final HornetQueenInsectToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java b/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java index 1d99d2328c..d331b634d5 100644 --- a/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java @@ -3,6 +3,7 @@ package mage.game.permanent.token; import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; +import mage.util.RandomUtil; public final class HumanSoldierToken extends TokenImpl { @@ -16,6 +17,15 @@ public final class HumanSoldierToken extends TokenImpl { toughness = new MageInt(1); } + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("IKO")) { + setTokenType(RandomUtil.nextInt(3) + 1); // 1...3 + } + } + public HumanSoldierToken(final HumanSoldierToken token) { super(token); } diff --git a/Mage/src/main/java/mage/game/permanent/token/HumanToken.java b/Mage/src/main/java/mage/game/permanent/token/HumanToken.java index 79f039152e..f0df12ed62 100644 --- a/Mage/src/main/java/mage/game/permanent/token/HumanToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/HumanToken.java @@ -18,7 +18,7 @@ public final class HumanToken extends TokenImpl { subtype.add(SubType.HUMAN); power = new MageInt(1); toughness = new MageInt(1); - availableImageSetCodes.addAll(Arrays.asList("DKA", "AVR", "FNMP", "RNA", "ELD", "C19")); + availableImageSetCodes.addAll(Arrays.asList("DKA", "AVR", "FNMP", "RNA", "ELD", "C19", "C20")); } public HumanToken(final HumanToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/HuntedDragonKnightToken.java b/Mage/src/main/java/mage/game/permanent/token/HuntedDragonKnightToken.java index f0751efe80..856a763e91 100644 --- a/Mage/src/main/java/mage/game/permanent/token/HuntedDragonKnightToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/HuntedDragonKnightToken.java @@ -1,34 +1,27 @@ package mage.game.permanent.token; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import mage.MageInt; import mage.abilities.keyword.FirstStrikeAbility; import mage.constants.CardType; import mage.constants.SubType; +import java.util.Arrays; + /** - * * @author spjspj */ public final class HuntedDragonKnightToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("ORI", "RTR", "C15")); - } - public HuntedDragonKnightToken() { super("Knight", "2/2 white Knight creature tokens with first strike"); cardType.add(CardType.CREATURE); color.setWhite(true); - subtype.add(SubType.KNIGHT); power = new MageInt(2); toughness = new MageInt(2); this.addAbility(FirstStrikeAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("ORI", "RTR", "C15"); } public HuntedDragonKnightToken(final HuntedDragonKnightToken token) { @@ -38,4 +31,13 @@ public final class HuntedDragonKnightToken extends TokenImpl { public HuntedDragonKnightToken copy() { return new HuntedDragonKnightToken(this); } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C15")) { + setTokenType(2); + } + } } diff --git a/Mage/src/main/java/mage/game/permanent/token/HydraBroodmasterToken.java b/Mage/src/main/java/mage/game/permanent/token/HydraBroodmasterToken.java index 5dace1e254..82b0acf3bb 100644 --- a/Mage/src/main/java/mage/game/permanent/token/HydraBroodmasterToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/HydraBroodmasterToken.java @@ -1,29 +1,29 @@ - - package mage.game.permanent.token; +import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -import mage.MageInt; + +import java.util.Arrays; /** - * * @author spjspj */ public final class HydraBroodmasterToken extends TokenImpl { public HydraBroodmasterToken() { - this(1,1); + this(1, 1); } public HydraBroodmasterToken(int power, int toughness) { super("Hydra", "green Hydra creature token"); - this.setOriginalExpansionSetCode("JOU"); cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.HYDRA); this.power = new MageInt(power); this.toughness = new MageInt(toughness); + + availableImageSetCodes = Arrays.asList("JOU"); } public HydraBroodmasterToken(final HydraBroodmasterToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java b/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java index 842cc6d7da..95b330293d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java @@ -2,14 +2,12 @@ package mage.game.permanent.token; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; import java.util.ArrayList; import java.util.Arrays; @@ -20,12 +18,6 @@ import java.util.List; */ public final class KarnConstructToken extends TokenImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifacts you control"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } - static final private List tokenImageSets = new ArrayList<>(); static { @@ -41,10 +33,8 @@ public final class KarnConstructToken extends TokenImpl { power = new MageInt(0); toughness = new MageInt(0); - DynamicValue value = new PermanentsOnBattlefieldCount(filter); - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - new BoostSourceEffect(value, value, Duration.WhileOnBattlefield) + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new BoostSourceEffect(ArtifactYouControlCount.instance, ArtifactYouControlCount.instance, Duration.WhileOnBattlefield) .setText("This creature gets +1/+1 for each artifact you control") )); } diff --git a/Mage/src/main/java/mage/game/permanent/token/KnightToken.java b/Mage/src/main/java/mage/game/permanent/token/KnightToken.java index 35f63cd23b..780caeb355 100644 --- a/Mage/src/main/java/mage/game/permanent/token/KnightToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/KnightToken.java @@ -6,29 +6,16 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.util.RandomUtil; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** * @author LevelX2 */ public final class KnightToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("ORI", "RTR", "C15", "CMA", "DOM", "ELD")); - } - public KnightToken() { super("Knight", "2/2 white Knight creature token with vigilance"); - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C15")) { - setTokenType(2); - } - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("DOM")) { - this.setTokenType(RandomUtil.nextInt(2) + 1); - } + cardType.add(CardType.CREATURE); color.setWhite(true); subtype.add(SubType.KNIGHT); @@ -36,7 +23,7 @@ public final class KnightToken extends TokenImpl { toughness = new MageInt(2); this.addAbility(VigilanceAbility.getInstance()); - availableImageSetCodes = tokenImageSets; + availableImageSetCodes = Arrays.asList("C13", "C15", "CMA", "DGM", "ORI", "RTR", "M19", "ELD", "M21"); } public KnightToken(final KnightToken token) { @@ -46,4 +33,17 @@ public final class KnightToken extends TokenImpl { public KnightToken copy() { return new KnightToken(this); } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C15")) { + setTokenType(2); + } + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("DOM")) { + this.setTokenType(RandomUtil.nextInt(2) + 1); + } + } } diff --git a/Mage/src/main/java/mage/game/permanent/token/KrakenToken.java b/Mage/src/main/java/mage/game/permanent/token/KrakenToken.java new file mode 100644 index 0000000000..2dc5194b46 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/KrakenToken.java @@ -0,0 +1,31 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.ObjectColor; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ + +public final class KrakenToken extends TokenImpl { + + public KrakenToken() { + super("Kraken", "8/8 blue Kraken creature token"); + this.cardType.add(CardType.CREATURE); + this.subtype.add(SubType.KRAKEN); + this.color = ObjectColor.BLUE; + this.power = new MageInt(8); + this.toughness = new MageInt(8); + } + + private KrakenToken(final KrakenToken token) { + super(token); + } + + public KrakenToken copy() { + return new KrakenToken(this); + } + +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/game/permanent/token/MinotaurToken.java b/Mage/src/main/java/mage/game/permanent/token/MinotaurToken.java new file mode 100644 index 0000000000..2e9d4e7d90 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/MinotaurToken.java @@ -0,0 +1,29 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.ObjectColor; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class MinotaurToken extends TokenImpl { + + public MinotaurToken() { + super("Minotaur", "2/3 red Minotaur creature token"); + cardType.add(CardType.CREATURE); + color.setColor(ObjectColor.RED); + subtype.add(SubType.MINOTAUR); + power = new MageInt(2); + toughness = new MageInt(3); + } + + private MinotaurToken(final MinotaurToken token) { + super(token); + } + + public MinotaurToken copy() { + return new MinotaurToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/MowuToken.java b/Mage/src/main/java/mage/game/permanent/token/MowuToken.java index 7312106610..5d38df4ce0 100644 --- a/Mage/src/main/java/mage/game/permanent/token/MowuToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/MowuToken.java @@ -1,4 +1,3 @@ - package mage.game.permanent.token; import mage.MageInt; @@ -13,11 +12,11 @@ import mage.constants.SuperType; public final class MowuToken extends TokenImpl { public MowuToken() { - super("Mowu", "legendary 3/3 green Hound creature token named Mowu"); + super("Mowu", "legendary 3/3 green Dog creature token named Mowu"); cardType.add(CardType.CREATURE); color.setGreen(true); this.addSuperType(SuperType.LEGENDARY); - subtype.add(SubType.HOUND); + subtype.add(SubType.DOG); power = new MageInt(3); toughness = new MageInt(3); } diff --git a/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java b/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java index e33ce89329..b2b640913c 100644 --- a/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java @@ -1,7 +1,7 @@ package mage.game.permanent.token; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DefenderAbility; import mage.constants.CardType; @@ -30,7 +30,7 @@ public final class NestingDragonToken extends TokenImpl { power = new MageInt(0); toughness = new MageInt(2); addAbility(DefenderAbility.getInstance()); - this.addAbility(new DiesTriggeredAbility( + this.addAbility(new DiesSourceTriggeredAbility( new CreateTokenEffect(new DragonEggDragonToken()), false )); } diff --git a/Mage/src/main/java/mage/game/permanent/token/Ooze2Token.java b/Mage/src/main/java/mage/game/permanent/token/Ooze2Token.java index 42736bec1e..513573d97f 100644 --- a/Mage/src/main/java/mage/game/permanent/token/Ooze2Token.java +++ b/Mage/src/main/java/mage/game/permanent/token/Ooze2Token.java @@ -4,7 +4,7 @@ package mage.game.permanent.token; import mage.constants.CardType; import mage.constants.SubType; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; /** @@ -20,7 +20,7 @@ public final class Ooze2Token extends TokenImpl { color.setGreen(true); power = new MageInt(2); toughness = new MageInt(2); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new OozeToken(1, 1), 2), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new OozeToken(1, 1), 2), false)); } public Ooze2Token(final Ooze2Token token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java b/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java index 2934b37f99..2517b79aa0 100644 --- a/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java @@ -11,6 +11,9 @@ import mage.constants.SubType; */ public final class PurphorossInterventionToken extends TokenImpl { + public PurphorossInterventionToken() { + this(0); + } public PurphorossInterventionToken(int power) { super("Elemental", "X/1 red Elemental creature token with trample and haste"); this.cardType.add(CardType.CREATURE); diff --git a/Mage/src/main/java/mage/game/permanent/token/PursuedWhaleToken.java b/Mage/src/main/java/mage/game/permanent/token/PursuedWhaleToken.java new file mode 100644 index 0000000000..a083103150 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/PursuedWhaleToken.java @@ -0,0 +1,51 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.combat.AttacksIfAbleAllEffect; +import mage.abilities.effects.common.combat.CantBlockSourceEffect; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class PursuedWhaleToken extends TokenImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public PursuedWhaleToken() { + super("Pirate", "1/1 red Pirate creature token with \"This creature can't block\" and \"Creatures you control attack each combat if able.\""); + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add(SubType.PIRATE); + power = new MageInt(1); + toughness = new MageInt(1); + + this.addAbility(new SimpleStaticAbility(new CantBlockSourceEffect(Duration.WhileOnBattlefield) + .setText("this creature can't block"))); + this.addAbility(new SimpleStaticAbility(new AttacksIfAbleAllEffect( + filter, Duration.WhileOnBattlefield, true + ))); + + availableImageSetCodes = Arrays.asList("M21"); + } + + private PursuedWhaleToken(final PursuedWhaleToken token) { + super(token); + } + + @Override + public PursuedWhaleToken copy() { + return new PursuedWhaleToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/YoungPyromancerElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/RedElementalToken.java similarity index 60% rename from Mage/src/main/java/mage/game/permanent/token/YoungPyromancerElementalToken.java rename to Mage/src/main/java/mage/game/permanent/token/RedElementalToken.java index e9d7a704c0..9b8fc4ce8c 100644 --- a/Mage/src/main/java/mage/game/permanent/token/YoungPyromancerElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/RedElementalToken.java @@ -5,37 +5,46 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.util.RandomUtil; +import java.util.Arrays; + /** * @author spjspj */ -public final class YoungPyromancerElementalToken extends TokenImpl { +public final class RedElementalToken extends TokenImpl { - public YoungPyromancerElementalToken() { + public RedElementalToken() { super("Elemental", "1/1 red Elemental creature token"); - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("M14")) { - setTokenType(RandomUtil.nextInt(2) + 1); - } - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("EMA")) { - setTokenType(1); - } - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("SHM")) { - setTokenType(2); - } - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("MH1")) { - setTokenType(1); - } cardType.add(CardType.CREATURE); color.setRed(true); subtype.add(SubType.ELEMENTAL); power = new MageInt(1); toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("C13", "EMA", "M14", "SHM", "MH1", "M20"); } - public YoungPyromancerElementalToken(final YoungPyromancerElementalToken token) { + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C13")) { + setTokenType(2); + } + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("M14")) { + setTokenType(RandomUtil.nextInt(2) + 1); + } + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("SHM")) { + setTokenType(2); + } + } + + public RedElementalToken(final RedElementalToken token) { super(token); } - public YoungPyromancerElementalToken copy() { - return new YoungPyromancerElementalToken(this); + public RedElementalToken copy() { + return new RedElementalToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/ElementalAppealElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/RedElementalWithTrampleAndHaste.java similarity index 50% rename from Mage/src/main/java/mage/game/permanent/token/ElementalAppealElementalToken.java rename to Mage/src/main/java/mage/game/permanent/token/RedElementalWithTrampleAndHaste.java index 0a54bf078f..30c9a7b197 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ElementalAppealElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/RedElementalWithTrampleAndHaste.java @@ -1,4 +1,3 @@ - package mage.game.permanent.token; import mage.MageInt; @@ -6,14 +5,14 @@ import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.TrampleAbility; import mage.constants.CardType; import mage.constants.SubType; +import mage.util.RandomUtil; /** - * * @author spjspj */ -public final class ElementalAppealElementalToken extends TokenImpl { +public final class RedElementalWithTrampleAndHaste extends TokenImpl { - public ElementalAppealElementalToken() { + public RedElementalWithTrampleAndHaste() { super("Elemental", "7/1 red Elemental creature token with trample and haste"); cardType.add(CardType.CREATURE); color.setRed(true); @@ -24,11 +23,20 @@ public final class ElementalAppealElementalToken extends TokenImpl { addAbility(HasteAbility.getInstance()); } - public ElementalAppealElementalToken(final ElementalAppealElementalToken token) { + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("ZEN")) { + setTokenType(RandomUtil.nextInt(2) + 1); + } + } + + public RedElementalWithTrampleAndHaste(final RedElementalWithTrampleAndHaste token) { super(token); } - public ElementalAppealElementalToken copy() { - return new ElementalAppealElementalToken(this); + public RedElementalWithTrampleAndHaste copy() { + return new RedElementalWithTrampleAndHaste(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/ReefWormFishToken.java b/Mage/src/main/java/mage/game/permanent/token/ReefWormFishToken.java index 1ac90fc567..70c9cad349 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ReefWormFishToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ReefWormFishToken.java @@ -4,7 +4,7 @@ package mage.game.permanent.token; import mage.constants.CardType; import mage.constants.SubType; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; /** @@ -22,7 +22,7 @@ public final class ReefWormFishToken extends TokenImpl { power = new MageInt(3); toughness = new MageInt(3); - addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ReefWormWhaleToken()))); + addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ReefWormWhaleToken()))); } public ReefWormFishToken(final ReefWormFishToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/ReefWormWhaleToken.java b/Mage/src/main/java/mage/game/permanent/token/ReefWormWhaleToken.java index 634a8a8b59..02b9f7d19f 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ReefWormWhaleToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ReefWormWhaleToken.java @@ -4,7 +4,7 @@ package mage.game.permanent.token; import mage.constants.CardType; import mage.constants.SubType; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; /** @@ -22,7 +22,7 @@ public final class ReefWormWhaleToken extends TokenImpl { power = new MageInt(6); toughness = new MageInt(6); - addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ReefWormKrakenToken()))); + addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ReefWormKrakenToken()))); } public ReefWormWhaleToken(final ReefWormWhaleToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/ResearchDevelopmentToken.java b/Mage/src/main/java/mage/game/permanent/token/ResearchDevelopmentToken.java new file mode 100644 index 0000000000..29938baeb3 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/ResearchDevelopmentToken.java @@ -0,0 +1,39 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author JayDi85 + */ +public final class ResearchDevelopmentToken extends TokenImpl { + + static final private List tokenImageSets = new ArrayList<>(); + + static { + tokenImageSets.addAll(Arrays.asList("DIS")); + } + + public ResearchDevelopmentToken() { + super("Elemental", "3/1 red Elemental creature token"); + availableImageSetCodes = tokenImageSets; + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add(SubType.ELEMENTAL); + power = new MageInt(3); + toughness = new MageInt(1); + } + + public ResearchDevelopmentToken(final ResearchDevelopmentToken token) { + super(token); + } + + public ResearchDevelopmentToken copy() { + return new ResearchDevelopmentToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java b/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java index 828a2545d8..dd71a45a20 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java @@ -29,37 +29,26 @@ public final class SaprolingToken extends TokenImpl { "MM2", "MM3", "MMA", + "NEM", "RTR", "C15", "MM3", + "INV", "C16", // 2 different token images... "CMA", "VMA", // 2 different token, one with DIFFERENT stats, "Saproling Burst" create different token, see https://scryfall.com/card/tvma/12 "E02", "RIX", "DOM", // 3 different token images - "C19" + "C19", + "C20", + "M21" )); } public SaprolingToken() { - this(null, 0); - } - - public SaprolingToken(String setCode) { - this(setCode, 0); - } - - public SaprolingToken(String setCode, int tokenType) { super("Saproling", "1/1 green Saproling creature token"); availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C16")) { - this.setTokenType(RandomUtil.nextInt(2) + 1); - } - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("DOM")) { - this.setTokenType(RandomUtil.nextInt(3) + 1); - } cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.SAPROLING); @@ -74,4 +63,23 @@ public final class SaprolingToken extends TokenImpl { public SaprolingToken copy() { return new SaprolingToken(this); } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("NEM")) { + this.setTokenType(2); + } + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("VMA")) { + this.setTokenType(2); + } + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C16")) { + this.setTokenType(RandomUtil.nextInt(2) + 1); + } + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("DOM")) { + this.setTokenType(RandomUtil.nextInt(3) + 1); + } + } } \ No newline at end of file diff --git a/Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java b/Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java index ddf2bbefa6..1be69e9ca0 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java @@ -4,6 +4,8 @@ import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; +import java.util.Arrays; + /** * @author spjspj */ @@ -15,14 +17,13 @@ public final class SeedGuardianToken extends TokenImpl { public SeedGuardianToken(int xValue) { super("Elemental", "X/X green Elemental creature token"); - setTokenType(1); - setOriginalExpansionSetCode("OGW"); cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.ELEMENTAL); power = new MageInt(xValue); toughness = new MageInt(xValue); + availableImageSetCodes = Arrays.asList("C13", "CHK", "OGW"); } public SeedGuardianToken(final SeedGuardianToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/SharkToken.java b/Mage/src/main/java/mage/game/permanent/token/SharkToken.java new file mode 100644 index 0000000000..0cbe1538b7 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/SharkToken.java @@ -0,0 +1,34 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class SharkToken extends TokenImpl { + + public SharkToken() { + this(0); + } + public SharkToken(int xValue) { + super("Shark", "X/X blue Shark creature token with flying"); + cardType.add(CardType.CREATURE); + color.setBlue(true); + subtype.add(SubType.SHARK); + power = new MageInt(xValue); + toughness = new MageInt(xValue); + addAbility(FlyingAbility.getInstance()); + } + + private SharkToken(final SharkToken token) { + super(token); + } + + @Override + public SharkToken copy() { + return new SharkToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java b/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java index 6275b24333..f622eb1b1c 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java @@ -5,43 +5,23 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.util.RandomUtil; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** * @author BetaSteward_at_googlemail.com */ public final class SoldierToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("10E", "M15", "C14", "ORI", "ALA", "DDF", "THS", "M12", "M13", "MM2", "MMA", "RTR", - "SOM", "DDO", "M10", "ORI", "EMN", "EMA", "CN2", "C16", "MM3", "E01", "DOM", "MH1", "M20")); - } - public SoldierToken() { super("Soldier", "1/1 white Soldier creature token"); - availableImageSetCodes = tokenImageSets; - cardType.add(CardType.CREATURE); color.setWhite(true); subtype.add(SubType.SOLDIER); power = new MageInt(1); toughness = new MageInt(1); - } - - @Override - public void setExpansionSetCodeForImage(String code) { - super.setExpansionSetCodeForImage(code); - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("THS")) { - this.setTokenType(RandomUtil.nextInt(2) + 1); - } - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("CN2") || getOriginalExpansionSetCode().equals("MM3")) { - setTokenType(1); - } + availableImageSetCodes = Arrays.asList("10E", "M15", "C14", "ORI", "ALA", "DDF", "THS", "M12", "M13", "MM2", "MMA", "RTR", + "SOM", "DDO", "M10", "ORI", "EMN", "EMA", "CN2", "C16", "MM3", "E01", "DOM", "MH1", "M20", "C20", "M21"); } public SoldierToken(final SoldierToken token) { @@ -50,6 +30,24 @@ public final class SoldierToken extends TokenImpl { @Override public SoldierToken copy() { - return new SoldierToken(this); //To change body of generated methods, choose Tools | Templates. + return new SoldierToken(this); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("CN2")) { + this.setTokenType(RandomUtil.nextInt(2) + 1); + } + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("CN2")) { + this.setTokenType(RandomUtil.nextInt(2) + 1); + } + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("MM3")) { + this.setTokenType(RandomUtil.nextInt(2) + 1); + } + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("THS")) { + this.setTokenType(RandomUtil.nextInt(2) + 1); + } } } diff --git a/Mage/src/main/java/mage/game/permanent/token/SoldierTokenWithHaste.java b/Mage/src/main/java/mage/game/permanent/token/SoldierTokenWithHaste.java index 59fe7b1231..14c3cc0599 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SoldierTokenWithHaste.java +++ b/Mage/src/main/java/mage/game/permanent/token/SoldierTokenWithHaste.java @@ -1,27 +1,17 @@ - - package mage.game.permanent.token; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import mage.constants.CardType; -import mage.constants.SubType; import mage.MageInt; import mage.abilities.keyword.HasteAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; /** - * * @author LoneFox */ public final class SoldierTokenWithHaste extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("GTC", "MM3")); - } - public SoldierTokenWithHaste() { super("Soldier", "1/1 red and white Soldier creature token with haste"); cardType.add(CardType.CREATURE); @@ -31,12 +21,18 @@ public final class SoldierTokenWithHaste extends TokenImpl { power = new MageInt(1); toughness = new MageInt(1); addAbility(HasteAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("GTC", "MM3"); } @Override public void setExpansionSetCodeForImage(String code) { super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("GTC")) { + setTokenType(2); + } if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("MM3")) { setTokenType(2); } diff --git a/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java b/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java index bfcf62c011..0d1ab1807b 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java @@ -18,24 +18,12 @@ public final class SpiritWhiteToken extends TokenImpl { static { tokenImageSets.addAll(Arrays.asList("AVR", "C14", "CNS", "DDC", "DDK", "FRF", "ISD", "KTK", "M15", "MM2", "SHM", - "SOI", "EMA", "C16", "MM3", "CMA", "E01", "ANA", "RNA", "M20")); + "SOI", "EMA", "C16", "MM3", "CMA", "E01", "ANA", "GPT", "RAV", "EMN", "RNA", "M20", "C20")); } public SpiritWhiteToken() { - this(null, 0); - } - - public SpiritWhiteToken(String setCode) { - this(setCode, 0); - } - - public SpiritWhiteToken(String setCode, int tokenType) { super("Spirit", "1/1 white Spirit creature token with flying"); availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); - if (tokenType > 0) { - setTokenType(tokenType); - } cardType.add(CardType.CREATURE); subtype.add(SubType.SPIRIT); color.setWhite(true); diff --git a/Mage/src/main/java/mage/game/permanent/token/StarfishToken.java b/Mage/src/main/java/mage/game/permanent/token/StarfishToken.java index 3e2b867fb3..7d355ceff3 100644 --- a/Mage/src/main/java/mage/game/permanent/token/StarfishToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/StarfishToken.java @@ -1,34 +1,34 @@ -package mage.game.permanent.token; - -import mage.MageInt; -import mage.constants.CardType; -import mage.constants.SubType; - -/** - * - * @author spike - */ -public final class StarfishToken extends TokenImpl { - - public StarfishToken() { - this(null, 0); - } - - public StarfishToken(String setCode, int tokenType) { - super("Starfish", "0/1 blue Starfish creature token"); - cardType.add(CardType.CREATURE); - subtype.add(SubType.STARFISH); - color.setBlue(true); - power = new MageInt(0); - toughness = new MageInt(1); - } - - public StarfishToken(final StarfishToken token) { - super(token); - } - - @Override - public StarfishToken copy() { - return new StarfishToken(this); - } +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * + * @author spike + */ +public final class StarfishToken extends TokenImpl { + + public StarfishToken() { + this(null, 0); + } + + public StarfishToken(String setCode, int tokenType) { + super("Starfish", "0/1 blue Starfish creature token"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.STARFISH); + color.setBlue(true); + power = new MageInt(0); + toughness = new MageInt(1); + } + + public StarfishToken(final StarfishToken token) { + super(token); + } + + @Override + public StarfishToken copy() { + return new StarfishToken(this); + } } \ No newline at end of file diff --git a/Mage/src/main/java/mage/game/permanent/token/TheLocustGodInsectToken.java b/Mage/src/main/java/mage/game/permanent/token/TheLocustGodInsectToken.java index 439d6ba897..c22ec75b43 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TheLocustGodInsectToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/TheLocustGodInsectToken.java @@ -1,21 +1,20 @@ - - package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; import mage.constants.CardType; import mage.constants.SubType; -import mage.MageInt; -import mage.abilities.keyword.HasteAbility; -import mage.abilities.keyword.FlyingAbility; + +import java.util.Arrays; /** - * * @author spjspj */ public final class TheLocustGodInsectToken extends TokenImpl { public TheLocustGodInsectToken() { super("Insect", "1/1 blue and red Insect creature token with flying and haste"); - setOriginalExpansionSetCode("HOU"); cardType.add(CardType.CREATURE); color.setBlue(true); color.setRed(true); @@ -24,6 +23,8 @@ public final class TheLocustGodInsectToken extends TokenImpl { toughness = new MageInt(1); addAbility(FlyingAbility.getInstance()); addAbility(HasteAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("HOU", "C20"); } public TheLocustGodInsectToken(final TheLocustGodInsectToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java index d2dcf64ce5..57ec537789 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java @@ -1,4 +1,3 @@ - package mage.game.permanent.token; import mage.MageInt; @@ -6,26 +5,26 @@ import mage.constants.CardType; import mage.constants.SubType; /** - * * @author LevelX2 */ public final class TilonallisSummonerElementalToken extends TokenImpl { public TilonallisSummonerElementalToken() { super("Elemental", "1/1 red Elemental creature tokens"); - setTokenType(2); cardType.add(CardType.CREATURE); subtype.add(SubType.ELEMENTAL); - color.setRed(true); power = new MageInt(1); toughness = new MageInt(1); + + setTokenType(2); } public TilonallisSummonerElementalToken(final TilonallisSummonerElementalToken token) { super(token); } + @Override public TilonallisSummonerElementalToken copy() { return new TilonallisSummonerElementalToken(this); } diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java index ce2dcc0d04..0ba43809a5 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java +++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java @@ -1,25 +1,22 @@ package mage.game.permanent.token; -import mage.MageObject; -import mage.MageObjectImpl; -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.cards.Card; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.CreateTokenEvent; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentToken; -import mage.players.Player; -import mage.util.RandomUtil; - import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.UUID; +import mage.MageObject; +import mage.MageObjectImpl; +import mage.abilities.Ability; +import mage.cards.Card; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.CreateTokenEvent; +import mage.game.events.CreatedTokenEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import mage.players.Player; +import mage.util.RandomUtil; public abstract class TokenImpl extends MageObjectImpl implements Token { @@ -52,6 +49,9 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { } } + public TokenImpl() { + } + public TokenImpl(String name, String description) { this.name = name; this.description = description; @@ -162,6 +162,9 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { if (controller == null) { return false; } + if (amount == 0) { + return false; + } lastAddedTokenIds.clear(); CreateTokenEvent event = new CreateTokenEvent(sourceId, controllerId, amount, this); @@ -198,8 +201,9 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { } } game.setScopeRelevant(false); + int createOrder = game.getState().getNextPermanentOrderNumber(); for (Permanent permanent : permanentsEntered) { - game.addPermanent(permanent); + game.addPermanent(permanent, createOrder); permanent.setZone(Zone.BATTLEFIELD, game); game.getPermanentsEntering().remove(permanent.getId()); @@ -208,6 +212,9 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { ((TokenImpl) token).lastAddedTokenId = permanent.getId(); } game.addSimultaneousEvent(new ZoneChangeEvent(permanent, permanent.getControllerId(), Zone.OUTSIDE, Zone.BATTLEFIELD)); + if (permanent instanceof PermanentToken) { + game.addSimultaneousEvent(new CreatedTokenEvent(event.getSourceId(), (PermanentToken) permanent)); + } if (attacking && game.getCombat() != null && game.getActivePlayerId().equals(permanent.getControllerId())) { game.getCombat().addAttackingCreature(permanent.getId(), game, attackedPlayer); } @@ -234,6 +241,10 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { return tokenType; } + /** + * Set token index to search in card-pictures-tok.txt (if set have multiple + * tokens with same name) Default is 1 + */ @Override public void setTokenType(int tokenType) { this.tokenType = tokenType; diff --git a/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java b/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java index dd23d59eda..21e61a6f39 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java @@ -8,6 +8,7 @@ import mage.abilities.mana.SimpleManaAbility; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; +import mage.util.RandomUtil; import java.util.ArrayList; import java.util.Arrays; @@ -20,28 +21,15 @@ public final class TreasureToken extends TokenImpl { static final private List tokenImageSets = new ArrayList<>(); - static { - tokenImageSets.addAll(Arrays.asList("XLN", "RNA", "M20", "C19")); - } - public TreasureToken() { - this(null, 0); - } - - public TreasureToken(String setCode) { - this(setCode, 0); - } - - public TreasureToken(String setCode, int tokenType) { super("Treasure", "Treasure token"); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); cardType.add(CardType.ARTIFACT); subtype.add(SubType.TREASURE); - Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(), new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); + + availableImageSetCodes = Arrays.asList("XLN", "RNA", "M20", "C19", "C20", "M21"); } public TreasureToken(final TreasureToken token) { @@ -51,4 +39,13 @@ public final class TreasureToken extends TokenImpl { public TreasureToken copy() { return new TreasureToken(this); } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("XLN")) { + this.setTokenType(RandomUtil.nextInt(4) + 1); + } + } } diff --git a/Mage/src/main/java/mage/game/permanent/token/UnicornToken.java b/Mage/src/main/java/mage/game/permanent/token/UnicornToken.java new file mode 100644 index 0000000000..4d118d3df8 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/UnicornToken.java @@ -0,0 +1,27 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +public final class UnicornToken extends TokenImpl { + + public UnicornToken() { + super("Unicorn", "2/2 white Unicorn creature token"); + setExpansionSetCodeForImage("JMP"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.UNICORN); + color.setWhite(true); + power = new MageInt(2); + toughness = new MageInt(2); + } + + private UnicornToken(final UnicornToken token) { + super(token); + } + + @Override + public UnicornToken copy() { + return new UnicornToken(this); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java b/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java index b07977696f..0a31fce579 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java @@ -1,12 +1,12 @@ - - package mage.game.permanent.token; + +import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -import mage.MageInt; + +import java.util.Arrays; /** - * * @author spjspj */ public final class WalkerOfTheGroveToken extends TokenImpl { @@ -18,6 +18,17 @@ public final class WalkerOfTheGroveToken extends TokenImpl { this.color.setGreen(true); power = new MageInt(4); toughness = new MageInt(4); + + availableImageSetCodes = Arrays.asList("C13", "LRW", "MMA", "MOR"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode().equals("C13")) { + this.setTokenType(2); + } } public WalkerOfTheGroveToken(final WalkerOfTheGroveToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsFirstToken.java b/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsFirstToken.java index 40f6f42503..13fc1c12e9 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsFirstToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsFirstToken.java @@ -1,4 +1,3 @@ - package mage.game.permanent.token; import mage.MageInt; @@ -7,20 +6,20 @@ import mage.constants.CardType; import mage.constants.SubType; /** - * * @author spjspj */ public final class WandOfTheElementsFirstToken extends TokenImpl { public WandOfTheElementsFirstToken() { super("Elemental", "2/2 blue Elemental creature token with flying"); - setTokenType(1); cardType.add(CardType.CREATURE); this.subtype.add(SubType.ELEMENTAL); this.color.setBlue(true); power = new MageInt(2); toughness = new MageInt(2); this.addAbility(FlyingAbility.getInstance()); + + setTokenType(1); } public WandOfTheElementsFirstToken(final WandOfTheElementsFirstToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsSecondToken.java b/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsSecondToken.java index 12e38624bd..43baf70390 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsSecondToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsSecondToken.java @@ -1,4 +1,3 @@ - package mage.game.permanent.token; import mage.MageInt; @@ -6,19 +5,19 @@ import mage.constants.CardType; import mage.constants.SubType; /** - * * @author spjspj */ public final class WandOfTheElementsSecondToken extends TokenImpl { public WandOfTheElementsSecondToken() { super("Elemental", "3/3 red Elemental creature token"); - setTokenType(2); cardType.add(CardType.CREATURE); this.subtype.add(SubType.ELEMENTAL); this.color.setRed(true); power = new MageInt(3); toughness = new MageInt(3); + + setTokenType(2); } public WandOfTheElementsSecondToken(final WandOfTheElementsSecondToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java b/Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java new file mode 100644 index 0000000000..aeb5d0a367 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java @@ -0,0 +1,37 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class WeirdToken2 extends TokenImpl { + + public WeirdToken2() { + this(0); + } + + public WeirdToken2(int xValue) { + super("Weird", "X/X blue and red Weird creature token"); + cardType.add(CardType.CREATURE); + color.setBlue(true); + color.setRed(true); + subtype.add(SubType.WEIRD); + power = new MageInt(xValue); + toughness = new MageInt(xValue); + + availableImageSetCodes = Arrays.asList("M21"); + } + + private WeirdToken2(final WeirdToken2 token) { + super(token); + } + + public WeirdToken2 copy() { + return new WeirdToken2(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/WhiteDogToken.java b/Mage/src/main/java/mage/game/permanent/token/WhiteDogToken.java new file mode 100644 index 0000000000..2e182866dd --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/WhiteDogToken.java @@ -0,0 +1,34 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author htrajan + */ +public final class WhiteDogToken extends TokenImpl { + + public WhiteDogToken() { + super("Dog", "1/1 white Dog creature token"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.DOG); + + color.setWhite(true); + power = new MageInt(1); + toughness = new MageInt(1); + + availableImageSetCodes.addAll(Arrays.asList("M21")); + } + + private WhiteDogToken(final WhiteDogToken token) { + super(token); + } + + public WhiteDogToken copy() { + return new WhiteDogToken(this); + } + +} diff --git a/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java index 52edc3276e..59ea1d700e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java @@ -1,13 +1,13 @@ - - package mage.game.permanent.token; -import mage.constants.CardType; -import mage.constants.SubType; + import mage.MageInt; import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; /** - * * @author spjspj */ public final class WhiteElementalToken extends TokenImpl { @@ -19,8 +19,22 @@ public final class WhiteElementalToken extends TokenImpl { subtype.add(SubType.ELEMENTAL); power = new MageInt(4); toughness = new MageInt(4); - setTokenType(2); this.addAbility(FlyingAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("LRW", "C16", "C20", "RTR"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C20")) { + setTokenType(2); + } + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("LRW")) { + setTokenType(2); + } } public WhiteElementalToken(final WhiteElementalToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/WolfsQuarryToken.java b/Mage/src/main/java/mage/game/permanent/token/WolfsQuarryToken.java index 60f4b9f96d..889e46a3d7 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WolfsQuarryToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/WolfsQuarryToken.java @@ -1,7 +1,7 @@ package mage.game.permanent.token; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.constants.CardType; import mage.constants.SubType; @@ -19,7 +19,7 @@ public final class WolfsQuarryToken extends TokenImpl { power = new MageInt(1); toughness = new MageInt(1); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new FoodToken()))); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new FoodToken()))); } private WolfsQuarryToken(final WolfsQuarryToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/Wurm2Token.java b/Mage/src/main/java/mage/game/permanent/token/Wurm2Token.java deleted file mode 100644 index 7b313746d8..0000000000 --- a/Mage/src/main/java/mage/game/permanent/token/Wurm2Token.java +++ /dev/null @@ -1,39 +0,0 @@ - -package mage.game.permanent.token; - -import mage.constants.CardType; -import mage.constants.SubType; -import mage.MageInt; -import mage.abilities.keyword.LifelinkAbility; - -/** - * - * @author spjspj - */ -public final class Wurm2Token extends TokenImpl { - - public Wurm2Token() { - this("MBS"); - } - - public Wurm2Token(String setCode) { - super("Wurm", "3/3 colorless Wurm artifact creature token with lifelink"); - setOriginalExpansionSetCode(setCode); - cardType.add(CardType.ARTIFACT); - cardType.add(CardType.CREATURE); - subtype.add(SubType.WURM); - power = new MageInt(3); - toughness = new MageInt(3); - this.addAbility(LifelinkAbility.getInstance()); - - setTokenType(2); // for image - } - - public Wurm2Token(final Wurm2Token token) { - super(token); - } - - public Wurm2Token copy() { - return new Wurm2Token(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/Wurm55Token.java b/Mage/src/main/java/mage/game/permanent/token/Wurm55Token.java new file mode 100644 index 0000000000..b327c02263 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/Wurm55Token.java @@ -0,0 +1,41 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author fireshoes + */ +public final class Wurm55Token extends TokenImpl { + + public Wurm55Token() { + super("Wurm", "5/5 green Wurm creature token"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add(SubType.WURM); + power = new MageInt(5); + toughness = new MageInt(5); + + availableImageSetCodes = Arrays.asList("AKH", "DGM"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode().equals("DGM")) { + this.setTokenType(2); + } + } + + public Wurm55Token(final Wurm55Token token) { + super(token); + } + + public Wurm55Token copy() { + return new Wurm55Token(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/WurmToken.java b/Mage/src/main/java/mage/game/permanent/token/WurmToken.java index f6af923e0e..e05fe87b6e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WurmToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/WurmToken.java @@ -1,10 +1,11 @@ - package mage.game.permanent.token; import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; +import java.util.Arrays; + /** * @author magenoxx_at_gmail.com */ @@ -17,6 +18,17 @@ public final class WurmToken extends TokenImpl { subtype.add(SubType.WURM); power = new MageInt(6); toughness = new MageInt(6); + + availableImageSetCodes = Arrays.asList("C19", "EMA", "GPT", "JUD", "M12", "M13", "MM3", "ODY", "VMA"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode().equals("VMA")) { + this.setTokenType(2); + } } public WurmToken(final WurmToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/WurmToken3.java b/Mage/src/main/java/mage/game/permanent/token/WurmToken3.java deleted file mode 100644 index 31425bb2bd..0000000000 --- a/Mage/src/main/java/mage/game/permanent/token/WurmToken3.java +++ /dev/null @@ -1,40 +0,0 @@ - - -package mage.game.permanent.token; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.MageInt; - -/** - * - * @author fireshoes - */ -public final class WurmToken3 extends TokenImpl { - - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("AKH")); - } - - public WurmToken3() { - super("Wurm", "5/5 green Wurm creature token"); - cardType.add(CardType.CREATURE); - color.setGreen(true); - subtype.add(SubType.WURM); - power = new MageInt(5); - toughness = new MageInt(5); - } - - public WurmToken3(final WurmToken3 token) { - super(token); - } - - public WurmToken3 copy() { - return new WurmToken3(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/Wurm1Token.java b/Mage/src/main/java/mage/game/permanent/token/WurmWithDeathtouchToken.java similarity index 54% rename from Mage/src/main/java/mage/game/permanent/token/Wurm1Token.java rename to Mage/src/main/java/mage/game/permanent/token/WurmWithDeathtouchToken.java index 85d0116826..4f55a1bcc6 100644 --- a/Mage/src/main/java/mage/game/permanent/token/Wurm1Token.java +++ b/Mage/src/main/java/mage/game/permanent/token/WurmWithDeathtouchToken.java @@ -1,22 +1,19 @@ package mage.game.permanent.token; -import mage.constants.CardType; -import mage.constants.SubType; import mage.MageInt; import mage.abilities.keyword.DeathtouchAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; /** - * * @author spjspj */ -public final class Wurm1Token extends TokenImpl { +public final class WurmWithDeathtouchToken extends TokenImpl { - public Wurm1Token() { - this("MBS"); - } - public Wurm1Token(String setCode) { + public WurmWithDeathtouchToken() { super("Wurm", "3/3 colorless Wurm artifact creature token with deathtouch"); - setOriginalExpansionSetCode(setCode); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); subtype.add(SubType.WURM); @@ -24,14 +21,19 @@ public final class Wurm1Token extends TokenImpl { toughness = new MageInt(3); this.addAbility(DeathtouchAbility.getInstance()); - setTokenType(1); // for image + availableImageSetCodes = Arrays.asList("C14", "SOM"); } - public Wurm1Token(final Wurm1Token token) { + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + } + + public WurmWithDeathtouchToken(final WurmWithDeathtouchToken token) { super(token); } - public Wurm1Token copy() { - return new Wurm1Token(this); + public WurmWithDeathtouchToken copy() { + return new WurmWithDeathtouchToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/WurmWithLifelinkToken.java b/Mage/src/main/java/mage/game/permanent/token/WurmWithLifelinkToken.java new file mode 100644 index 0000000000..8ecc9cad32 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/WurmWithLifelinkToken.java @@ -0,0 +1,47 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.LifelinkAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author spjspj + */ +public final class WurmWithLifelinkToken extends TokenImpl { + + public WurmWithLifelinkToken() { + super("Wurm", "3/3 colorless Wurm artifact creature token with lifelink"); + cardType.add(CardType.ARTIFACT); + cardType.add(CardType.CREATURE); + subtype.add(SubType.WURM); + power = new MageInt(3); + toughness = new MageInt(3); + this.addAbility(LifelinkAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("C14", "SOM"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode().equals("C14")) { + this.setTokenType(2); + } + + if (getOriginalExpansionSetCode().equals("SOM")) { + this.setTokenType(2); + } + } + + public WurmWithLifelinkToken(final WurmWithLifelinkToken token) { + super(token); + } + + public WurmWithLifelinkToken copy() { + return new WurmWithLifelinkToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/WurmToken2.java b/Mage/src/main/java/mage/game/permanent/token/WurmWithTrampleToken.java similarity index 58% rename from Mage/src/main/java/mage/game/permanent/token/WurmToken2.java rename to Mage/src/main/java/mage/game/permanent/token/WurmWithTrampleToken.java index 598e85a5a4..dad1649b03 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WurmToken2.java +++ b/Mage/src/main/java/mage/game/permanent/token/WurmWithTrampleToken.java @@ -1,28 +1,18 @@ - - package mage.game.permanent.token; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import mage.constants.CardType; -import mage.constants.SubType; import mage.MageInt; import mage.abilities.keyword.TrampleAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; /** - * * @author LoneFox */ -public final class WurmToken2 extends TokenImpl { +public final class WurmWithTrampleToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("RTR", "MM3")); - } - - public WurmToken2() { + public WurmWithTrampleToken() { super("Wurm", "5/5 green Wurm creature token with trample"); cardType.add(CardType.CREATURE); color.setGreen(true); @@ -30,13 +20,15 @@ public final class WurmToken2 extends TokenImpl { power = new MageInt(5); toughness = new MageInt(5); addAbility(TrampleAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("DGM", "RTR"); } - public WurmToken2(final WurmToken2 token) { + public WurmWithTrampleToken(final WurmWithTrampleToken token) { super(token); } - public WurmToken2 copy() { - return new WurmToken2(this); + public WurmWithTrampleToken copy() { + return new WurmWithTrampleToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/ZaxaraTheExemplaryHydraToken.java b/Mage/src/main/java/mage/game/permanent/token/ZaxaraTheExemplaryHydraToken.java new file mode 100644 index 0000000000..ce9eaee6dd --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/ZaxaraTheExemplaryHydraToken.java @@ -0,0 +1,33 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author AsterAether + */ +public class ZaxaraTheExemplaryHydraToken extends TokenImpl { + + public ZaxaraTheExemplaryHydraToken() { + super("Hydra", "0/0 green Hydra creature token"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add(SubType.HYDRA); + power = new MageInt(0); + toughness = new MageInt(0); + + availableImageSetCodes = Arrays.asList("C20"); + } + + private ZaxaraTheExemplaryHydraToken(final ZaxaraTheExemplaryHydraToken token) { + super(token); + } + + @Override + public ZaxaraTheExemplaryHydraToken copy() { + return new ZaxaraTheExemplaryHydraToken(this); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/game/permanent/token/ZektarShrineElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/ZektarShrineElementalToken.java deleted file mode 100644 index 1d836890a8..0000000000 --- a/Mage/src/main/java/mage/game/permanent/token/ZektarShrineElementalToken.java +++ /dev/null @@ -1,34 +0,0 @@ - -package mage.game.permanent.token; - -import mage.MageInt; -import mage.abilities.keyword.HasteAbility; -import mage.abilities.keyword.TrampleAbility; -import mage.constants.CardType; -import mage.constants.SubType; - -/** - * - * @author spjspj - */ -public final class ZektarShrineElementalToken extends TokenImpl { - - public ZektarShrineElementalToken() { - super("Elemental", "7/1 red Elemental creature token with trample and haste"); - cardType.add(CardType.CREATURE); - color.setRed(true); - subtype.add(SubType.ELEMENTAL); - power = new MageInt(7); - toughness = new MageInt(1); - addAbility(TrampleAbility.getInstance()); - addAbility(HasteAbility.getInstance()); - } - - public ZektarShrineElementalToken(final ZektarShrineElementalToken token) { - super(token); - } - - public ZektarShrineElementalToken copy() { - return new ZektarShrineElementalToken(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java b/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java index 0518703268..5c2d49a668 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java @@ -5,30 +5,26 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.util.RandomUtil; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** * @author BetaSteward_at_googlemail.com */ public final class ZombieToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("10E", "M10", "M11", "M12", "M13", "M14", "M15", "MBS", "ALA", "ISD", "C14", "C15", "C16", "C17", "CNS", - "MMA", "BNG", "KTK", "DTK", "ORI", "OGW", "SOI", "EMN", "EMA", "MM3", "AKH", "CMA", "E01", "RNA", "WAR", "MH1", "M20", "C19", "THB")); - } - public ZombieToken() { super("Zombie", "2/2 black Zombie creature token"); - availableImageSetCodes = tokenImageSets; cardType.add(CardType.CREATURE); color.setBlack(true); subtype.add(SubType.ZOMBIE); power = new MageInt(2); toughness = new MageInt(2); + + availableImageSetCodes = Arrays.asList("10E", "M10", "M11", "M12", "M13", "M14", "M15", + "MBS", "ALA", "ISD", "C14", "C15", "C16", "C17", + "CNS", "MMA", "BNG", "KTK", "DTK", "ORI", "OGW", + "SOI", "EMN", "EMA", "MM3", "AKH", "CMA", "E01", + "RNA", "WAR", "MH1", "M20", "C19", "THB", "M21"); } @Override @@ -55,6 +51,6 @@ public final class ZombieToken extends TokenImpl { @Override public ZombieToken copy() { - return new ZombieToken(this); //To change body of generated methods, choose Tools | Templates. + return new ZombieToken(this); } } diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 3c29624c2e..83aacef2db 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -11,9 +11,9 @@ import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ActivationManaAbilityStep; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.keyword.BestowAbility; import mage.abilities.keyword.MorphAbility; import mage.abilities.text.TextPart; import mage.cards.*; @@ -62,7 +62,7 @@ public class Spell extends StackObjImpl implements Card { private boolean resolving = false; private UUID commandedBy = null; // for Word of Command - private boolean doneActivatingManaAbilities; // if this is true, the player is no longer allowed to pay the spell costs with activating of mana abilies + private ActivationManaAbilityStep currentActivatingManaAbilitiesStep = ActivationManaAbilityStep.BEFORE; public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone) { this.card = card; @@ -118,32 +118,36 @@ public class Spell extends StackObjImpl implements Card { this.resolving = spell.resolving; this.commandedBy = spell.commandedBy; - this.doneActivatingManaAbilities = spell.doneActivatingManaAbilities; + this.currentActivatingManaAbilitiesStep = spell.currentActivatingManaAbilitiesStep; this.targetChanged = spell.targetChanged; } public boolean activate(Game game, boolean noMana) { - setDoneActivatingManaAbilities(false); // Used for e.g. improvise - if (!spellAbilities.get(0).activate(game, noMana)) { + setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.BEFORE); // mana payment step started, can use any mana abilities, see AlternateManaPaymentAbility + + if (!ability.activate(game, noMana)) { return false; } - if (spellAbilities.size() > 1) { - // if there are more abilities (fused split spell) or first ability added new abilities (splice), activate the additional abilities - boolean ignoreAbility = true; + + // spell can contains multiple abilities to activate (fused split, splice) + for (SpellAbility spellAbility : spellAbilities) { + if (ability.equals(spellAbility)) { + // activated first + continue; + } + boolean payNoMana = noMana; - for (SpellAbility spellAbility : spellAbilities) { - if (ignoreAbility) { - ignoreAbility = false; - } else { - // costs for spliced abilities were added to main spellAbility, so pay no mana for spliced abilities - payNoMana |= spellAbility.getSpellAbilityType() == SpellAbilityType.SPLICE; - if (!spellAbility.activate(game, payNoMana)) { - return false; - } - } + // costs for spliced abilities were added to main spellAbility, so pay no mana for spliced abilities + payNoMana |= spellAbility.getSpellAbilityType() == SpellAbilityType.SPLICE; + // costs for fused ability pay on first spell activate, so all parts must be without mana + // see https://github.com/magefree/mage/issues/6603 + payNoMana |= ability.getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED; + + if (!spellAbility.activate(game, payNoMana)) { + return false; } } - setDoneActivatingManaAbilities(true); // can be activated again maybe during the resolution of the spell (e.g. Metallic Rebuke) + setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.NORMAL); return true; } @@ -210,15 +214,14 @@ public class Spell extends StackObjImpl implements Card { // resolve if legal parts if (notTargeted || legalParts) { for (SpellAbility spellAbility : this.spellAbilities) { - if (spellAbilityHasLegalParts(spellAbility, game)) { + // legality of targets is checked only as the spell begins to resolve, not in between modes (spliced spells handeled correctly?) + if (spellAbilityCheckTargetsAndDeactivateModes(spellAbility, game)) { for (UUID modeId : spellAbility.getModes().getSelectedModes()) { spellAbility.getModes().setActiveMode(modeId); - if (spellAbility.getTargets().stillLegal(spellAbility, game)) { - if (spellAbility.getSpellAbilityType() != SpellAbilityType.SPLICE) { - updateOptionalCosts(index); - } - result |= spellAbility.resolve(game); + if (spellAbility.getSpellAbilityType() != SpellAbilityType.SPLICE) { + updateOptionalCosts(index); } + result |= spellAbility.resolve(game); } index++; } @@ -239,7 +242,7 @@ public class Spell extends StackObjImpl implements Card { } else if (this.isEnchantment() && this.hasSubtype(SubType.AURA, game)) { if (ability.getTargets().stillLegal(ability, game)) { updateOptionalCosts(0); - boolean bestow = ability instanceof BestowAbility; + boolean bestow = SpellAbilityCastMode.BESTOW.equals(ability.getSpellAbilityCastMode()); if (bestow) { // Must be removed first time, after that will be removed by continous effect // Otherwise effects like evolve trigger from creature comes into play event @@ -269,7 +272,7 @@ public class Spell extends StackObjImpl implements Card { return false; } // Aura has no legal target and its a bestow enchantment -> Add it to battlefield as creature - if (this.getSpellAbility() instanceof BestowAbility) { + if (SpellAbilityCastMode.BESTOW.equals(this.getSpellAbility().getSpellAbilityCastMode())) { updateOptionalCosts(0); if (controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null)) { Permanent permanent = game.getPermanent(card.getId()); @@ -309,6 +312,33 @@ public class Spell extends StackObjImpl implements Card { } } + /** + * Legality of the targets of all modes are only checked as the spell begins + * to resolve A mode without any legal target (if it has targets at all) + * won't resolve. So modes with targets without legal targets are + * unselected. + * + * @param spellAbility + * @param game + * @return + */ + private boolean spellAbilityCheckTargetsAndDeactivateModes(SpellAbility spellAbility, Game game) { + boolean legalModes = false; + for (Iterator iterator = spellAbility.getModes().getSelectedModes().iterator(); iterator.hasNext();) { + UUID nextSelectedModeId = iterator.next(); + Mode mode = spellAbility.getModes().get(nextSelectedModeId); + if (!mode.getTargets().isEmpty()) { + if (!mode.getTargets().stillLegal(spellAbility, game)) { + spellAbility.getModes().removeSelectedMode(mode.getId()); + iterator.remove(); + continue; + } + } + legalModes = true; + } + return legalModes; + } + private boolean spellAbilityHasLegalParts(SpellAbility spellAbility, Game game) { if (spellAbility.getModes().getSelectedModes().size() > 1) { boolean targetedMode = false; @@ -397,12 +427,12 @@ public class Spell extends StackObjImpl implements Card { } } - public boolean isDoneActivatingManaAbilities() { - return doneActivatingManaAbilities; + public ActivationManaAbilityStep getCurrentActivatingManaAbilitiesStep() { + return this.currentActivatingManaAbilitiesStep; } - public void setDoneActivatingManaAbilities(boolean doneActivatingManaAbilities) { - this.doneActivatingManaAbilities = doneActivatingManaAbilities; + public void setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep currentActivatingManaAbilitiesStep) { + this.currentActivatingManaAbilitiesStep = currentActivatingManaAbilitiesStep; } @Override @@ -462,14 +492,14 @@ public class Spell extends StackObjImpl implements Card { } @Override - public Set getCardType() { + public ArrayList getCardType() { if (faceDown) { - EnumSet cardTypes = EnumSet.noneOf(CardType.class); + ArrayList cardTypes = new ArrayList<>(); cardTypes.add(CardType.CREATURE); return cardTypes; } - if (this.getSpellAbility() instanceof BestowAbility) { - EnumSet cardTypes = EnumSet.noneOf(CardType.class); + if (SpellAbilityCastMode.BESTOW.equals(this.getSpellAbility().getSpellAbilityCastMode())) { + ArrayList cardTypes = new ArrayList<>(); cardTypes.addAll(card.getCardType()); cardTypes.remove(CardType.CREATURE); return cardTypes; @@ -479,7 +509,7 @@ public class Spell extends StackObjImpl implements Card { @Override public SubTypeList getSubtype(Game game) { - if (this.getSpellAbility() instanceof BestowAbility) { + if (SpellAbilityCastMode.BESTOW.equals(this.getSpellAbility().getSpellAbilityCastMode())) { SubTypeList subtypes = card.getSubtype(game); if (!subtypes.contains(SubType.AURA)) { // do it only once subtypes.add(SubType.AURA); @@ -491,7 +521,7 @@ public class Spell extends StackObjImpl implements Card { @Override public boolean hasSubtype(SubType subtype, Game game) { - if (this.getSpellAbility() instanceof BestowAbility) { // workaround for Bestow (don't like it) + if (SpellAbilityCastMode.BESTOW.equals(this.getSpellAbility().getSpellAbilityCastMode())) { // workaround for Bestow (don't like it) SubTypeList subtypes = card.getSubtype(game); if (!subtypes.contains(SubType.AURA)) { // do it only once subtypes.add(SubType.AURA); @@ -523,8 +553,8 @@ public class Spell extends StackObjImpl implements Card { } @Override - public boolean hasAbility(UUID abilityId, Game game) { - return card.hasAbility(abilityId, game); + public boolean hasAbility(Ability ability, Game game) { + return card.hasAbility(ability, game); } @Override @@ -597,7 +627,9 @@ public class Spell extends StackObjImpl implements Card { spellAbilities.add(spellAbility); } + @Override public void addAbility(Ability ability) { + throw new UnsupportedOperationException("Not supported."); } @Override @@ -700,7 +732,7 @@ public class Spell extends StackObjImpl implements Card { } public Spell copySpell(UUID newController) { - Spell copy = new Spell(this.card, this.ability.copySpell(), this.controllerId, this.fromZone); + Spell spellCopy = new Spell(this.card, this.ability.copySpell(), this.controllerId, this.fromZone); boolean firstDone = false; for (SpellAbility spellAbility : this.getSpellAbilities()) { if (!firstDone) { @@ -709,11 +741,11 @@ public class Spell extends StackObjImpl implements Card { } SpellAbility newAbility = spellAbility.copy(); // e.g. spliced spell newAbility.newId(); - copy.addSpellAbility(newAbility); + spellCopy.addSpellAbility(newAbility); } - copy.setCopy(true, this); - copy.setControllerId(newController); - return copy; + spellCopy.setCopy(true, this); + spellCopy.setControllerId(newController); + return spellCopy; } @Override @@ -938,12 +970,12 @@ public class Spell extends StackObjImpl implements Card { } if (isTransformable()) { Card secondCard = getSecondCardFace(); - ObjectColor color = secondCard.getColor(null); - mana.setBlack(mana.isBlack() || color.isBlack()); - mana.setGreen(mana.isGreen() || color.isGreen()); - mana.setRed(mana.isRed() || color.isRed()); - mana.setBlue(mana.isBlue() || color.isBlue()); - mana.setWhite(mana.isWhite() || color.isWhite()); + ObjectColor objectColor = secondCard.getColor(null); + mana.setBlack(mana.isBlack() || objectColor.isBlack()); + mana.setGreen(mana.isGreen() || objectColor.isGreen()); + mana.setRed(mana.isRed() || objectColor.isRed()); + mana.setBlue(mana.isBlue() || objectColor.isBlue()); + mana.setWhite(mana.isWhite() || objectColor.isWhite()); for (String rule : secondCard.getRules()) { rule = rule.replaceAll("(?i)", ""); // Ignoring reminder text in italic if (!mana.isBlack() && rule.matches(regexBlack)) { @@ -992,14 +1024,26 @@ public class Spell extends StackObjImpl implements Card { @Override public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) { - Spell copy = this.copySpell(newControllerId); - game.getState().setZone(copy.getId(), Zone.STACK); // required for targeting ex: Nivmagus Elemental - game.getStack().push(copy); - if (chooseNewTargets) { - copy.chooseNewTargets(game, newControllerId); + return createCopyOnStack(game, source, newControllerId, chooseNewTargets, 1); + } + + @Override + public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount) { + Spell spellCopy = null; + GameEvent gameEvent = GameEvent.getEvent(EventType.COPY_STACKOBJECT, this.getId(), source.getSourceId(), newControllerId, amount); + if (game.replaceEvent(gameEvent)) { + return null; } - game.fireEvent(new GameEvent(EventType.COPIED_STACKOBJECT, copy.getId(), this.getId(), newControllerId)); - return copy; + for (int i = 0; i < gameEvent.getAmount(); i++) { + spellCopy = this.copySpell(newControllerId); + game.getState().setZone(spellCopy.getId(), Zone.STACK); // required for targeting ex: Nivmagus Elemental + game.getStack().push(spellCopy); + if (chooseNewTargets) { + spellCopy.chooseNewTargets(game, newControllerId); + } + game.fireEvent(new GameEvent(EventType.COPIED_STACKOBJECT, spellCopy.getId(), this.getId(), newControllerId)); + } + return spellCopy; } @Override diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index bd37c6436f..1060db5318 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -40,7 +40,7 @@ import java.util.UUID; */ public class StackAbility extends StackObjImpl implements Ability { - private static EnumSet emptyCardType = EnumSet.noneOf(CardType.class); + private static ArrayList emptyCardType = new ArrayList<>(); private static List emptyString = new ArrayList<>(); private static ObjectColor emptyColor = new ObjectColor(); private static ManaCosts emptyCost = new ManaCostsImpl<>(); @@ -153,7 +153,7 @@ public class StackAbility extends StackObjImpl implements Ability { } @Override - public EnumSet getCardType() { + public ArrayList getCardType() { return emptyCardType; } @@ -178,7 +178,7 @@ public class StackAbility extends StackObjImpl implements Ability { } @Override - public boolean hasAbility(UUID abilityId, Game game) { + public boolean hasAbility(Ability ability, Game game) { return false; } @@ -308,6 +308,11 @@ public class StackAbility extends StackObjImpl implements Ability { return ability.getTargets(); } + @Override + public Targets getAllSelectedTargets() { + return ability.getAllSelectedTargets(); + } + @Override public void addTarget(Target target) { } @@ -467,7 +472,7 @@ public class StackAbility extends StackObjImpl implements Ability { } @Override - public void setAbilityWord(AbilityWord abilityWord) { + public Ability setAbilityWord(AbilityWord abilityWord) { throw new UnsupportedOperationException("Not supported."); } @@ -573,19 +578,30 @@ public class StackAbility extends StackObjImpl implements Ability { @Override public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) { - Ability newAbility = this.copy(); - newAbility.newId(); - StackAbility newStackAbility = new StackAbility(newAbility, newControllerId); - game.getStack().push(newStackAbility); - if (chooseNewTargets && !newAbility.getTargets().isEmpty()) { - Player controller = game.getPlayer(newControllerId); - Outcome outcome = newAbility.getEffects().getOutcome(newAbility); - if (controller.chooseUse(outcome, "Choose new targets?", source, game)) { - newAbility.getTargets().clearChosen(); - newAbility.getTargets().chooseTargets(outcome, newControllerId, newAbility, false, game, false); - } + return createCopyOnStack(game, source, newControllerId, chooseNewTargets, 1); + } + + public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount) { + StackAbility newStackAbility = null; + GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.COPY_STACKOBJECT, this.getId(), source.getSourceId(), newControllerId, amount); + if (game.replaceEvent(gameEvent)) { + return null; + } + for (int i = 0; i < gameEvent.getAmount(); i++) { + Ability newAbility = this.copy(); + newAbility.newId(); + newStackAbility = new StackAbility(newAbility, newControllerId); + game.getStack().push(newStackAbility); + if (chooseNewTargets && !newAbility.getTargets().isEmpty()) { + Player controller = game.getPlayer(newControllerId); + Outcome outcome = newAbility.getEffects().getOutcome(newAbility); + if (controller.chooseUse(outcome, "Choose new targets?", source, game)) { + newAbility.getTargets().clearChosen(); + newAbility.getTargets().chooseTargets(outcome, newControllerId, newAbility, false, game, false); + } + } + game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, newStackAbility.getId(), this.getId(), newControllerId)); } - game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, newStackAbility.getId(), this.getId(), newControllerId)); return newStackAbility; } @@ -661,4 +677,17 @@ public class StackAbility extends StackObjImpl implements Ability { public Outcome getCustomOutcome() { return this.ability.getCustomOutcome(); } + + @Override + public boolean isSameInstance(Ability ability) { + // same instance (by mtg rules) = same object, ID or class+text (you can't check class only cause it can be different by params/text) + if (ability == null) { + return false; + } + + return (this == ability) + || (this.getId().equals(ability.getId())) + || (this.getOriginalId().equals(ability.getOriginalId())) + || (this.getClass() == ability.getClass() && this.getRule().equals(ability.getRule())); + } } diff --git a/Mage/src/main/java/mage/game/stack/StackObjImpl.java b/Mage/src/main/java/mage/game/stack/StackObjImpl.java index 8d7130458a..2bfcbbf5c2 100644 --- a/Mage/src/main/java/mage/game/stack/StackObjImpl.java +++ b/Mage/src/main/java/mage/game/stack/StackObjImpl.java @@ -272,7 +272,7 @@ public abstract class StackObjImpl implements StackObject { name = targetPlayer.getLogName(); } } else { - name = object.getName(); + name = object.getIdName(); } return name; } diff --git a/Mage/src/main/java/mage/game/stack/StackObject.java b/Mage/src/main/java/mage/game/stack/StackObject.java index 9a51ed2d4d..4536ee44f1 100644 --- a/Mage/src/main/java/mage/game/stack/StackObject.java +++ b/Mage/src/main/java/mage/game/stack/StackObject.java @@ -1,7 +1,5 @@ - package mage.game.stack; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.constants.Zone; @@ -10,6 +8,8 @@ import mage.filter.FilterPermanent; import mage.game.Controllable; import mage.game.Game; +import java.util.UUID; + public interface StackObject extends MageObject, Controllable { boolean resolve(Game game); @@ -22,13 +22,15 @@ public interface StackObject extends MageObject, Controllable { Ability getStackAbility(); -// int getConvertedManaCost(); + // int getConvertedManaCost(); boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget); StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets); - + + StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount); + boolean isTargetChanged(); - + void setTargetChanged(boolean targetChanged); @Override diff --git a/Mage/src/main/java/mage/game/tournament/LimitedOptions.java b/Mage/src/main/java/mage/game/tournament/LimitedOptions.java index 0151beed80..9628b5c33a 100644 --- a/Mage/src/main/java/mage/game/tournament/LimitedOptions.java +++ b/Mage/src/main/java/mage/game/tournament/LimitedOptions.java @@ -19,7 +19,8 @@ public class LimitedOptions implements Serializable { protected int numberBoosters; protected boolean isRandom; protected boolean isRichMan; - protected Deck cubeFromDeck = null; + protected Deck cubeFromDeck; + protected boolean isJumpstart; public List getSetCodes() { return sets; @@ -80,4 +81,13 @@ public class LimitedOptions implements Serializable { public void setIsRichMan(boolean isRichMan) { this.isRichMan = isRichMan; } + + public void setIsJumpstart(boolean isJumpstart) { + this.isJumpstart = isJumpstart; + } + + public boolean getIsJumpstart() { + return this.isJumpstart; + } + } diff --git a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java index 016ca73dd6..001cf9dc6e 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java @@ -1,15 +1,32 @@ package mage.game.tournament; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.log4j.Logger; + import mage.cards.ExpansionSet; import mage.cards.decks.Deck; import mage.constants.TournamentPlayerState; import mage.game.draft.Draft; import mage.game.draft.DraftCube; -import mage.game.events.*; +import mage.game.events.Listener; +import mage.game.events.PlayerQueryEvent; +import mage.game.events.PlayerQueryEventSource; +import mage.game.events.TableEvent; import mage.game.events.TableEvent.EventType; +import mage.game.events.TableEventSource; +import mage.game.jumpstart.JumpstartPoolGenerator; import mage.game.match.Match; import mage.game.match.MatchPlayer; import mage.game.result.ResultProtos.MatchPlayerProto; @@ -20,7 +37,6 @@ import mage.game.result.ResultProtos.TourneyRoundProto; import mage.players.Player; import mage.players.PlayerType; import mage.util.RandomUtil; -import org.apache.log4j.Logger; /** * @@ -394,6 +410,8 @@ public abstract class TournamentImpl implements Tournament { for (int i = 0; i < options.getLimitedOptions().getNumberBoosters(); i++) { player.getDeck().getSideboard().addAll(cube.createBooster()); } + } else if (options.getLimitedOptions().getIsJumpstart()) { + player.getDeck().getCards().addAll(JumpstartPoolGenerator.generatePool()); } else { for (ExpansionSet set : sets) { player.getDeck().getSideboard().addAll(set.createBooster()); diff --git a/Mage/src/main/java/mage/game/tournament/TournamentType.java b/Mage/src/main/java/mage/game/tournament/TournamentType.java index 5b227ed3d1..f11bd7e38e 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentType.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentType.java @@ -18,7 +18,8 @@ public class TournamentType implements Serializable { protected boolean limited; // or construced protected boolean elimination; // or Swiss protected boolean isRandom; - protected boolean isRichMan = false; // or Rich Man Draft + protected boolean isRichMan; // or Rich Man Draft + protected boolean isJumpstart; protected TournamentType() { } @@ -68,4 +69,8 @@ public class TournamentType implements Serializable { return this.isRichMan; } + public boolean isJumpstart() { + return this.isJumpstart; + } + } diff --git a/Mage/src/main/java/mage/game/turn/DrawStep.java b/Mage/src/main/java/mage/game/turn/DrawStep.java index 1e721d416b..b1320c740a 100644 --- a/Mage/src/main/java/mage/game/turn/DrawStep.java +++ b/Mage/src/main/java/mage/game/turn/DrawStep.java @@ -1,15 +1,13 @@ - - package mage.game.turn; -import java.util.UUID; import mage.constants.PhaseStep; import mage.game.Game; import mage.game.events.GameEvent.EventType; import mage.players.Player; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public class DrawStep extends Step { @@ -29,7 +27,7 @@ public class DrawStep extends Step { public void beginStep(Game game, UUID activePlayerId) { Player activePlayer = game.getPlayer(activePlayerId); //20091005 - 504.1/703.4c - activePlayer.drawCards(1, game); + activePlayer.drawCards(1, null, game); // game.saveState(); game.applyEffects(); super.beginStep(game, activePlayerId); diff --git a/Mage/src/main/java/mage/players/Library.java b/Mage/src/main/java/mage/players/Library.java index 82323073e8..5ff04b98e9 100644 --- a/Mage/src/main/java/mage/players/Library.java +++ b/Mage/src/main/java/mage/players/Library.java @@ -239,6 +239,8 @@ public class Library implements Serializable { /** * Tests only -- find card position in library + * @param cardId + * @return */ public int getCardPosition(UUID cardId) { UUID[] list = library.toArray(new UUID[0]); diff --git a/Mage/src/main/java/mage/players/ManaPool.java b/Mage/src/main/java/mage/players/ManaPool.java index dc9e6dfda9..c0178f702f 100644 --- a/Mage/src/main/java/mage/players/ManaPool.java +++ b/Mage/src/main/java/mage/players/ManaPool.java @@ -1,5 +1,7 @@ package mage.players; +import java.io.Serializable; +import java.util.*; import mage.ConditionalMana; import mage.Mana; import mage.abilities.Ability; @@ -15,9 +17,6 @@ import mage.game.events.GameEvent.EventType; import mage.game.events.ManaEvent; import mage.game.stack.Spell; -import java.io.Serializable; -import java.util.*; - /** * @author BetaSteward_at_googlemail.com */ @@ -83,20 +82,20 @@ public class ManaPool implements Serializable { * @param ability * @param filter * @param game - * @param costToPay complete costs to pay (needed to check conditional mana) + * @param costToPay complete costs to pay (needed to check conditional + * mana) * @param usedManaToPay the information about what mana was paid * @return */ public boolean pay(ManaType manaType, Ability ability, Filter filter, Game game, Cost costToPay, Mana usedManaToPay) { - if (!isAutoPayment() - && manaType != unlockedManaType) { + if (!isAutoPayment() && manaType != unlockedManaType) { // if manual payment and the needed mana type was not unlocked, nothing will be paid return false; } ManaType possibleAsThoughPoolManaType = null; if (isAutoPayment() && isAutoPaymentRestricted() - && !wasManaAddedBeyondStock() + && !wasManaAddedBeyondStock() // was not more mana added than at the start of casting something && manaType != unlockedManaType) { // if automatic restricted payment and there is already mana in the pool // and the needed mana type was not unlocked, nothing will be paid @@ -111,9 +110,10 @@ public class ManaPool implements Serializable { return false; // if it's not possible return } } - - if (getConditional(manaType, ability, filter, game, costToPay) > 0) { - removeConditional(manaType, ability, game, costToPay, usedManaToPay); + // first try to pay from conditional mana (the returned manaType can be changed if AsThoughEffects are active) + ManaType conditionalManaType = getConditional(manaType, ability, filter, game, costToPay, possibleAsThoughPoolManaType); + if (conditionalManaType != null) { + removeConditional(conditionalManaType, ability, game, costToPay, usedManaToPay); lockManaType(); // pay only one mana if mana payment is set to manually return true; } @@ -144,7 +144,7 @@ public class ManaPool implements Serializable { GameEvent event = new GameEvent(GameEvent.EventType.MANA_PAID, ability.getId(), mana.getSourceId(), ability.getControllerId(), 0, mana.getFlag()); event.setData(mana.getOriginalId().toString()); game.fireEvent(event); - usedManaToPay.increase(mana.getFirstAvailable()); + usedManaToPay.increase(usableManaType); mana.remove(usableManaType); if (mana.count() == 0) { // so no items with count 0 stay in list manaItems.remove(mana); @@ -160,21 +160,33 @@ public class ManaPool implements Serializable { return getMana().get(manaType); } - private int getConditional(ManaType manaType, Ability ability, Filter filter, Game game, Cost costToPay) { + private ManaType getConditional(ManaType manaType, Ability ability, Filter filter, Game game, Cost costToPay, ManaType possibleAsThoughPoolManaType) { if (ability == null || getConditionalMana().isEmpty()) { - return 0; + return null; } for (ManaPoolItem mana : manaItems) { - if (mana.isConditional() - && mana.getConditionalMana().get(manaType) > 0 - && mana.getConditionalMana().apply(ability, game, mana.getSourceId(), costToPay)) { - if (filter == null - || filter.match(mana.getSourceObject(), game)) { - return mana.getConditionalMana().get(manaType); + if (mana.isConditional()) { + ManaType manaTypeToUse = null; + if (mana.getConditionalMana().get(manaType) > 0) { + manaTypeToUse = manaType; + } else { + if (possibleAsThoughPoolManaType == null) { + possibleAsThoughPoolManaType = game.getContinuousEffects().asThoughMana(manaType, mana, ability.getSourceId(), ability, ability.getControllerId(), game); + } + if (possibleAsThoughPoolManaType != null && mana.getConditionalMana().get(possibleAsThoughPoolManaType) > 0) { + manaTypeToUse = possibleAsThoughPoolManaType; + } + } + if (manaTypeToUse != null && mana.getConditionalMana().apply(ability, game, mana.getSourceId(), costToPay)) { + if (filter == null + || filter.match(mana.getSourceObject(), game)) { + return manaTypeToUse; + } } } } - return 0; + + return null; } public int getConditionalCount(Ability ability, Game game, FilterMana filter, Cost costToPay) { @@ -301,7 +313,7 @@ public class ManaPool implements Serializable { if (mana instanceof ConditionalMana) { ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceObject(game), ((ConditionalMana) mana).getManaProducerOriginalId() != null - ? ((ConditionalMana) mana).getManaProducerOriginalId() : source.getOriginalId()); + ? ((ConditionalMana) mana).getManaProducerOriginalId() : source.getOriginalId()); if (emptyOnTurnsEnd) { item.setDuration(Duration.EndOfTurn); } diff --git a/Mage/src/main/java/mage/players/ManaPoolItem.java b/Mage/src/main/java/mage/players/ManaPoolItem.java index 34db510c20..b694bbe852 100644 --- a/Mage/src/main/java/mage/players/ManaPoolItem.java +++ b/Mage/src/main/java/mage/players/ManaPoolItem.java @@ -1,4 +1,3 @@ - package mage.players; import java.io.Serializable; @@ -205,6 +204,21 @@ public class ManaPoolItem implements Serializable { } else if (colorless > 0) { return ManaType.COLORLESS; } + if (conditionalMana != null) { + if (conditionalMana.getBlack() > 0) { + return ManaType.BLACK; + } else if (conditionalMana.getBlue() > 0) { + return ManaType.BLUE; + } else if (conditionalMana.getGreen() > 0) { + return ManaType.GREEN; + } else if (conditionalMana.getRed() > 0) { + return ManaType.RED; + } else if (conditionalMana.getWhite() > 0) { + return ManaType.WHITE; + } else if (conditionalMana.getColorless() > 0) { + return ManaType.COLORLESS; + } + } return null; } diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index c4a7a065ca..1da17ef95f 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -1,8 +1,11 @@ package mage.players; +import java.io.Serializable; +import java.util.*; import mage.MageItem; import mage.MageObject; import mage.MageObjectReference; +import mage.Mana; import mage.abilities.*; import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.Cost; @@ -38,9 +41,6 @@ import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; import mage.util.Copyable; -import java.io.Serializable; -import java.util.*; - /** * @author BetaSteward_at_googlemail.com */ @@ -103,7 +103,13 @@ public interface Player extends MageItem, Copyable { void setCanPayLifeCost(boolean canPayLifeCost); - boolean canPayLifeCost(); + /** + * Can the player pay life for spells or activated abilities + * + * @param Ability + * @return + */ + boolean canPayLifeCost(Ability Ability); void setCanPaySacrificeCostFilter(FilterPermanent filter); @@ -152,8 +158,6 @@ public interface Player extends MageItem, Copyable { boolean isPassed(); - boolean isEmptyDraw(); - void pass(Game game); void resetPassed(); @@ -311,14 +315,12 @@ public interface Player extends MageItem, Copyable { void shuffleLibrary(Ability source, Game game); - int drawCards(int num, Game game); + int drawCards(int num, UUID sourceId, Game game); - int drawCards(int num, Game game, List appliedEffects); + int drawCards(int num, UUID sourceId, Game game, List appliedEffects); boolean cast(SpellAbility ability, Game game, boolean noMana, MageObjectReference reference); - SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana); - SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana); boolean putInHand(Card card, Game game); @@ -369,8 +371,9 @@ public interface Player extends MageItem, Copyable { * @param game * @param noMana if it's a spell i can be cast without paying mana * @param ignoreTiming if it's cast during the resolution of another spell - * no sorcery or play land timing restriction are checked. For a land it has - * to be the turn of the player playing that card. + * no sorcery or play land timing restriction are + * checked. For a land it has to be the turn of the + * player playing that card. * @param reference mage object that allows to play the card * @return */ @@ -380,8 +383,9 @@ public interface Player extends MageItem, Copyable { * @param card the land card to play * @param game * @param ignoreTiming false - it won't be checked if the stack is empty and - * you are able to play a Sorcery. It's still checked, if you are able to - * play a land concerning the number of lands you already played. + * you are able to play a Sorcery. It's still checked, + * if you are able to play a land concerning the number + * of lands you already played. * @return */ boolean playLand(Card card, Game game, boolean ignoreTiming); @@ -412,6 +416,8 @@ public interface Player extends MageItem, Copyable { Cards discard(int amount, boolean random, Ability source, Game game); + Cards discard(Cards cards, Ability source, Game game); + void discardToMax(Game game); boolean discard(Card card, Ability source, Game game); @@ -445,13 +451,20 @@ public interface Player extends MageItem, Copyable { void resetStoredBookmark(Game game); + default void restoreState(int bookmark, String text, Game game) { + game.restoreState(bookmark, text); + if (getStoredBookmark() >= bookmark) { + resetStoredBookmark(game); + } + } + void revealCards(Ability source, Cards cards, Game game); - void revealCards(String name, Cards cards, Game game); + void revealCards(String titelSuffix, Cards cards, Game game); - void revealCards(Ability source, String name, Cards cards, Game game); + void revealCards(Ability source, String titelSuffix, Cards cards, Game game); - void revealCards(String name, Cards cards, Game game, boolean postToLog); + void revealCards(String titelSuffix, Cards cards, Game game, boolean postToLog); /** * Adds the cards to the reveal window and adds the source object's id name @@ -532,8 +545,12 @@ public interface Player extends MageItem, Copyable { * * @param cards - list of cards that have to be moved * @param game - game - * @param anyOrder - true if player can determine the order of the cards - * else random order + * @param anyOrder - true = if player can determine the order of the cards + * else false = random order 401.4. If an effect puts two or + * more cards in a specific position in a library at the + * same time, the owner of those cards may arrange them in + * any order. That library’s owner doesn’t reveal the order + * in which the cards go into the library. * @param source - source ability * @return */ @@ -563,6 +580,12 @@ public interface Player extends MageItem, Copyable { */ boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder); + boolean putCardsOnTopOfLibrary(Card card, Game game, Ability source, boolean anyOrder); + + boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source); + + boolean shuffleCardsToLibrary(Card card, Game game, Ability source); + // set the value for X mana spells and abilities default int announceXMana(int min, int max, String message, Game game, Ability ability) { return announceXMana(min, max, 1, message, game, ability); @@ -629,13 +652,17 @@ public interface Player extends MageItem, Copyable { ManaOptions getManaAvailable(Game game); - List getPlayable(Game game, boolean hidden); + void addAvailableTriggeredMana(List netManaAvailable); + + List> getAvailableTriggeredMana(); + + List getPlayable(Game game, boolean hidden); List getPlayableOptions(Ability ability, Game game); Map getPlayableObjects(Game game, Zone zone); - LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game); + LinkedHashMap getPlayableActivatedAbilities(MageObject object, Zone zone, Game game); boolean addCounters(Counter counter, Game game); @@ -727,7 +754,7 @@ public interface Player extends MageItem, Copyable { /** * Universal method to move cards from one zone to another. Do not mix - * objects from different from zones to move. + * objects from different zones to move. * * @param cards * @param toZone @@ -735,9 +762,10 @@ public interface Player extends MageItem, Copyable { * @param game * @param tapped the cards are tapped on the battlefield * @param faceDown the cards are face down in the to zone - * @param byOwner the card is moved (or put onto battlefield) by the owner - * of the card and if target zone is battlefield controls the permanent - * (instead of the controller of the source) + * @param byOwner the card is moved (or put onto battlefield) by the + * owner of the card and if target zone is battlefield + * controls the permanent (instead of the controller + * of the source) * @param appliedEffects * @return */ @@ -833,6 +861,8 @@ public interface Player extends MageItem, Copyable { */ boolean moveCardToCommandWithInfo(Card card, UUID sourceId, Game game, Zone fromZone); + Cards millCards(int toMill, Ability source, Game game); + /** * Checks if the playerToCheckId is from an opponent in range * @@ -909,10 +939,16 @@ public interface Player extends MageItem, Copyable { List getDesignations(); + /** + * Set the mana colors the user can pay with 2 life instead + * @param colors + */ void addPhyrexianToColors(FilterMana colors); - void removePhyrexianFromColors(FilterMana colors); - + /** + * Mana colors the player can pay instead with 2 life + * @return + */ FilterMana getPhyrexianColors(); } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 861e4eaef1..339c7e0a49 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -12,6 +12,7 @@ import mage.abilities.common.PlayLandAsCommanderAbility; import mage.abilities.common.WhileSearchingPlayFromLibraryAbility; import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility; import mage.abilities.costs.*; +import mage.abilities.costs.mana.AlternateManaPaymentAbility; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; @@ -68,6 +69,7 @@ import org.apache.log4j.Logger; import java.io.Serializable; import java.util.*; import java.util.Map.Entry; +import java.util.stream.Collectors; public abstract class PlayerImpl implements Player, Serializable { @@ -130,7 +132,7 @@ public abstract class PlayerImpl implements Player, Serializable { protected boolean idleTimeout; protected RangeOfInfluence range; - protected Set inRange = new HashSet<>(); + protected Set inRange = new HashSet<>(); // players list in current range of influence (updates each turn) protected boolean isTestMode = false; protected boolean canGainLife = true; @@ -173,8 +175,12 @@ public abstract class PlayerImpl implements Player, Serializable { protected List designations = new ArrayList<>(); + // mana colors the player can handle like Phyrexian mana protected FilterMana phyrexianColors; + // Used during available mana calculation to give back possible available net mana from triggered mana abilities (No need to copy) + protected final List> availableTriggeredManaList = new ArrayList<>(); + /** * During some steps we can't play anything */ @@ -192,7 +198,7 @@ public abstract class PlayerImpl implements Player, Serializable { manaPool = new ManaPool(playerId); library = new Library(playerId); sideboard = new CardsImpl(); - phyrexianColors = new FilterMana(); + phyrexianColors = null; } protected PlayerImpl(UUID id) { @@ -246,7 +252,6 @@ public abstract class PlayerImpl implements Player, Serializable { this.storedBookmark = player.storedBookmark; this.topCardRevealed = player.topCardRevealed; - this.playersUnderYourControl.clear(); this.playersUnderYourControl.addAll(player.playersUnderYourControl); this.usersAllowedToSeeHandCards.addAll(player.usersAllowedToSeeHandCards); @@ -254,7 +259,6 @@ public abstract class PlayerImpl implements Player, Serializable { this.isGameUnderControl = player.isGameUnderControl; this.turnController = player.turnController; - this.turnControllers.clear(); this.turnControllers.addAll(player.turnControllers); this.passed = player.passed; @@ -277,8 +281,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.castSourceIdCosts.putAll(player.castSourceIdCosts); this.payManaMode = player.payManaMode; - this.phyrexianColors = player.phyrexianColors.copy(); - + this.phyrexianColors = player.getPhyrexianColors() != null ? player.phyrexianColors.copy() : null; this.designations.addAll(player.designations); } @@ -316,6 +319,10 @@ public abstract class PlayerImpl implements Player, Serializable { this.maxHandSize = player.getMaxHandSize(); this.maxAttackedBy = player.getMaxAttackedBy(); this.manaPool = player.getManaPool().copy(); + // Restore user specific settings in case changed since state save + this.manaPool.setAutoPayment(this.getUserData().isManaPoolAutomatic()); + this.manaPool.setAutoPaymentRestricted(this.getUserData().isManaPoolAutomaticRestricted()); + this.turns = player.getTurns(); this.range = player.getRange(); @@ -326,7 +333,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.inRange.clear(); this.inRange.addAll(player.getInRange()); - this.canPayLifeCost = player.canPayLifeCost(); + this.canPayLifeCost = player.canPayLifeCost(null); this.sacrificeCostFilter = player.getSacrificeCostFilter() != null ? player.getSacrificeCostFilter().copy() : null; this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife(); @@ -345,11 +352,14 @@ public abstract class PlayerImpl implements Player, Serializable { this.clearCastSourceIdManaCosts(); this.castSourceIdWithAlternateMana.addAll(player.getCastSourceIdWithAlternateMana()); - this.castSourceIdManaCosts.putAll(player.getCastSourceIdManaCosts()); - this.castSourceIdCosts.putAll(player.getCastSourceIdCosts()); - - this.phyrexianColors = player.getPhyrexianColors().copy(); + for (Entry> entry : player.getCastSourceIdManaCosts().entrySet()) { + this.castSourceIdManaCosts.put(entry.getKey(), entry.getValue().copy()); + } + for (Entry> entry : player.getCastSourceIdCosts().entrySet()) { + this.castSourceIdCosts.put(entry.getKey(), entry.getValue().copy()); + } + this.phyrexianColors = player.getPhyrexianColors() != null ? player.getPhyrexianColors().copy() : null; this.designations.clear(); this.designations.addAll(player.getDesignations()); @@ -425,7 +435,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.clearCastSourceIdManaCosts(); this.getManaPool().init(); // needed to remove mana that not empties on step change from previous game if left - this.phyrexianColors = new FilterMana(); + this.phyrexianColors = null; this.designations.clear(); } @@ -450,7 +460,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.alternativeSourceCosts.clear(); this.clearCastSourceIdManaCosts(); this.getManaPool().clearEmptyManaPoolRules(); - this.phyrexianColors = new FilterMana(); + this.phyrexianColors = null; } @Override @@ -461,7 +471,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void beginTurn(Game game) { this.landsPlayed = 0; - findRange(game); + updateRangeOfInfluence(game); } @Override @@ -469,44 +479,34 @@ public abstract class PlayerImpl implements Player, Serializable { return range; } - protected void findRange(Game game) { - //20100423 - 801.2c + protected void updateRangeOfInfluence(Game game) { + // 20100423 - 801.2c + // 801.2c The particular players within each player’s range of influence are determined as each turn begins. inRange.clear(); - if (range == RangeOfInfluence.ALL) { - for (Player player : game.getPlayers().values()) { - if (!player.hasLeft()) { - inRange.add(player.getId()); - } - } - } else if ((range.getRange() * 2) + 1 >= game.getPlayers().size()) { - for (Player player : game.getPlayers().values()) { - if (!player.hasLeft()) { - inRange.add(player.getId()); - } - } - } else { - inRange.add(playerId); - PlayerList players = game.getState().getPlayerList(playerId); - for (int i = 0; i < range.getRange(); i++) { - Player player = players.getNext(game, false); - if (player != null) { - while (player.hasLeft()) { - player = players.getNext(game, false); - } - inRange.add(player.getId()); - } - } - players = game.getState().getPlayerList(playerId); - for (int i = 0; i < range.getRange(); i++) { - Player player = players.getPrevious(game); - if (player != null) { - while (player.hasLeft()) { - player = players.getPrevious(game); - } - inRange.add(player.getId()); - } + inRange.add(this.playerId); + inRange.addAll(getAllNearPlayers(game, true)); + inRange.addAll(getAllNearPlayers(game, false)); + } + + private Set getAllNearPlayers(Game game, boolean needPrevious) { + // find all near players (search from current player position) + Set foundedList = new HashSet<>(); + PlayerList players = game.getState().getPlayerList(this.playerId); + int needAmount = this.getRange().getRange(); // distance to search (0 - ALL range) + int foundedAmount = 0; + while (needAmount == 0 || foundedAmount < needAmount) { + Player foundedPlayer = needPrevious ? players.getPrevious(game) : players.getNext(game, false); + + // PlayerList is inifine, so stops on repeats + if (foundedPlayer == null || foundedPlayer.getId().equals(this.playerId) || foundedList.contains(foundedPlayer.getId())) { + break; } + // skip leaved player (no needs cause next/previous code already checks it) + + foundedList.add(foundedPlayer.getId()); + foundedAmount++; } + return foundedList; } @Override @@ -610,50 +610,14 @@ public abstract class PlayerImpl implements Player, Serializable { if (abilities.containsKey(ShroudAbility.getInstance().getId())) { return false; } - - if (abilities.containsKey(HexproofAbility.getInstance().getId())) { - if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), - AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) { - return false; - } - } - - if (abilities.containsKey(HexproofFromWhiteAbility.getInstance().getId())) { - if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), - AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isWhite()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromBlueAbility.getInstance().getId())) { - if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), - AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isBlue()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromBlackAbility.getInstance().getId())) { - if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), - AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isBlack()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromMonocoloredAbility.getInstance().getId())) { - if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), - AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && !source.getColor(game).isColorless() - && !source.getColor(game).isMulticolored()) { - return false; - } + if (sourceControllerId != null + && this.hasOpponent(sourceControllerId, game) + && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null + && abilities.stream() + .filter(HexproofBaseAbility.class::isInstance) + .map(HexproofBaseAbility.class::cast) + .anyMatch(ability -> ability.checkObject(source, game))) { + return false; } return !hasProtectionFrom(source, game); @@ -673,16 +637,16 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public int drawCards(int num, Game game) { + public int drawCards(int num, UUID sourceId, Game game) { if (num > 0) { - return game.doAction(new MageDrawAction(this, num, null)); + return game.doAction(new MageDrawAction(this, num, null), sourceId); } return 0; } @Override - public int drawCards(int num, Game game, List appliedEffects) { - return game.doAction(new MageDrawAction(this, num, appliedEffects)); + public int drawCards(int num, UUID sourceId, Game game, List appliedEffects) { + return game.doAction(new MageDrawAction(this, num, appliedEffects), sourceId); } @Override @@ -729,6 +693,8 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } library.remove(card.getId(), game); + // must return true all the time (some cards can be removed directly from library, see getLibrary().removeFromTop) + // TODO: replace removeFromTop logic to normal with moveToZone return true; } @@ -743,6 +709,39 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public Cards discard(int amount, boolean random, Ability source, Game game) { + Cards discardedCards = doDiscard(amount, random, source, game); + if (!discardedCards.isEmpty()) { + UUID sourceId = source == null ? null : source.getSourceId(); + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.DISCARDED_CARDS, sourceId, + sourceId, playerId, discardedCards.size() + )); + } + return discardedCards; + } + + @Override + public Cards discard(Cards cards, Ability source, Game game) { + Cards discardedCards = new CardsImpl(); + if (cards == null) { + return discardedCards; + } + for (Card card : cards.getCards(game)) { + if (doDiscard(card, source, game, false)) { + discardedCards.add(card); + } + } + if (!discardedCards.isEmpty()) { + UUID sourceId = source == null ? null : source.getSourceId(); + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.DISCARDED_CARDS, sourceId, + sourceId, playerId, discardedCards.size() + )); + } + return discardedCards; + } + + private Cards doDiscard(int amount, boolean random, Ability source, Game game) { Cards discardedCards = new CardsImpl(); if (amount <= 0) { return discardedCards; @@ -752,7 +751,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (this.getHand().size() == 1 || this.getHand().size() == amount) { List cardsToDiscard = new ArrayList<>(this.getHand()); for (UUID id : cardsToDiscard) { - if (discard(this.getHand().get(id, game), source, game)) { + if (doDiscard(this.getHand().get(id, game), source, game, false)) { discardedCards.add(id); } } @@ -762,7 +761,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (random) { for (int i = 0; i < amount; i++) { Card card = this.getHand().getRandom(game); - if (discard(card, source, game)) { + if (doDiscard(card, source, game, false)) { discardedCards.add(card); } } @@ -773,7 +772,7 @@ public abstract class PlayerImpl implements Player, Serializable { + " card" + (possibleAmount > 1 ? "s" : "")), playerId); choose(Outcome.Discard, target, source == null ? null : source.getSourceId(), game); for (UUID cardId : target.getTargets()) { - if (discard(this.getHand().get(cardId, game), source, game)) { + if (doDiscard(this.getHand().get(cardId, game), source, game, false)) { discardedCards.add(cardId); } } @@ -783,6 +782,10 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean discard(Card card, Ability source, Game game) { + return doDiscard(card, source, game, true); + } + + private boolean doDiscard(Card card, Ability source, Game game, boolean fireEvent) { //20100716 - 701.7 /* 701.7. Discard # 701.7a To discard a card, move it from its owner’s hand to that player’s graveyard. @@ -797,29 +800,36 @@ public abstract class PlayerImpl implements Player, Serializable { about the discarded card, that cost payment is illegal; the game returns to the moment before the cost was paid (see rule 717, "Handling Illegal Actions"). */ - if (card != null) { - GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD, - card.getId(), source == null - ? null : source.getSourceId(), playerId); - gameEvent.setFlag(source != null); // event from effect or from cost (source == null) - if (!game.replaceEvent(gameEvent, source)) { - // write info to game log first so game log infos from triggered or replacement effects follow in the game log - if (!game.isSimulation()) { - game.informPlayers(getLogName() + " discards " + card.getLogName()); - } - /* If a card is discarded while Rest in Peace is on the battlefield, abilities that function - * when a card is discarded (such as madness) still work, even though that card never reaches - * a graveyard. In addition, spells or abilities that check the characteristics of a discarded - * card (such as Chandra Ablaze's first ability) can find that card in exile. */ - card.moveToZone(Zone.GRAVEYARD, source == null ? null : source.getSourceId(), game, false); - // So discard is also successful if card is moved to another zone by replacement effect! - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD, - card.getId(), source == null - ? null : source.getSourceId(), playerId)); - return true; - } + if (card == null) { + return false; } - return false; + GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD, + card.getId(), source == null + ? null : source.getSourceId(), playerId); + gameEvent.setFlag(source != null); // event from effect or from cost (source == null) + if (game.replaceEvent(gameEvent, source)) { + return false; + } + // write info to game log first so game log infos from triggered or replacement effects follow in the game log + if (!game.isSimulation()) { + game.informPlayers(getLogName() + " discards " + card.getLogName()); + } + /* If a card is discarded while Rest in Peace is on the battlefield, abilities that function + * when a card is discarded (such as madness) still work, even though that card never reaches + * a graveyard. In addition, spells or abilities that check the characteristics of a discarded + * card (such as Chandra Ablaze's first ability) can find that card in exile. */ + card.moveToZone(Zone.GRAVEYARD, source == null ? null : source.getSourceId(), game, false); + // So discard is also successful if card is moved to another zone by replacement effect! + UUID sourceId = source == null ? null : source.getSourceId(); + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD, + card.getId(), sourceId, playerId)); + if (fireEvent) { + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.DISCARDED_CARDS, sourceId, + sourceId, playerId, 1 + )); + } + return true; } @Override @@ -913,8 +923,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean removeFromGraveyard(Card card, Game game) { - this.graveyard.remove(card); - return true; + return this.graveyard.remove(card); } @Override @@ -958,6 +967,27 @@ public abstract class PlayerImpl implements Player, Serializable { return true; } + @Override + public boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source) { + if (cards.isEmpty()) { + return true; + } + game.informPlayers(getLogName() + " shuffels " + CardUtil.numberToText(cards.size(), "a") + + " card" + (cards.size() == 1 ? "" : "s") + + " into their library."); + boolean status = moveCards(cards, Zone.LIBRARY, source, game); + shuffleLibrary(source, game); + return status; + } + + @Override + public boolean shuffleCardsToLibrary(Card card, Game game, Ability source) { + if (card == null) { + return true; + } + return shuffleCardsToLibrary(new CardsImpl(card), game, source); + } + @Override public boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop) { if (card.isOwnedBy(getId())) { @@ -997,7 +1027,6 @@ public abstract class PlayerImpl implements Player, Serializable { public boolean putCardsOnTopOfLibrary(Cards cardsToLibrary, Game game, Ability source, boolean anyOrder) { if (cardsToLibrary != null && !cardsToLibrary.isEmpty()) { Cards cards = new CardsImpl(cardsToLibrary); // prevent possible ConcurrentModificationException - UUID sourceId = (source == null ? null : source.getSourceId()); if (!anyOrder) { // random order List ids = new ArrayList<>(cards); @@ -1031,8 +1060,25 @@ public abstract class PlayerImpl implements Player, Serializable { return true; } + @Override + public boolean putCardsOnTopOfLibrary(Card cardToLibrary, Game game, Ability source, boolean anyOrder) { + if (cardToLibrary != null) { + return putCardsOnTopOfLibrary(new CardsImpl(cardToLibrary), game, source, anyOrder); + } + return true; + } + private boolean moveObjectToLibrary(UUID objectId, UUID sourceId, Game game, boolean toTop, boolean withName) { MageObject mageObject = game.getObject(objectId); + if (mageObject instanceof Spell && mageObject.isCopy()) { + // Spell copies are not moved as cards, so here the no copy spell has to be selected to move + // (but because copy and original have the same objectId the wrong sepell can be selected from stack). + // So let's check if the original spell is on the stack and has to be selected. // TODO: Better handling so each spell could be selected by a unique id + Spell spellNoCopy = game.getStack().getSpell(sourceId, false); + if (spellNoCopy != null) { + mageObject = spellNoCopy; + } + } if (mageObject != null) { Zone fromZone = game.getState().getZone(objectId); if ((mageObject instanceof Permanent)) { @@ -1048,9 +1094,10 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts manaCosts, Costs costs) { + // cost must be copied for data consistence between game simulations castSourceIdWithAlternateMana.add(sourceId); - castSourceIdManaCosts.put(sourceId, manaCosts); - castSourceIdCosts.put(sourceId, costs); + castSourceIdManaCosts.put(sourceId, manaCosts != null ? manaCosts.copy() : null); + castSourceIdCosts.put(sourceId, costs != null ? costs.copy() : null); } @Override @@ -1102,6 +1149,13 @@ public abstract class PlayerImpl implements Player, Serializable { return result; } + /** + * @param originalAbility + * @param game + * @param noMana cast it without paying mana costs + * @param permittingObject which object permitted the cast + * @return + */ @Override public boolean cast(SpellAbility originalAbility, Game game, boolean noMana, MageObjectReference permittingObject) { if (game == null || originalAbility == null) { @@ -1112,13 +1166,7 @@ public abstract class PlayerImpl implements Player, Serializable { SpellAbility ability = originalAbility.copy(); ability.setControllerId(getId()); ability.setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(ability.getSourceId())); - if (ability.getSpellAbilityType() != SpellAbilityType.BASE) { - ability = chooseSpellAbilityForCast(ability, game, noMana); - if (ability == null) { - // No ability could be cast (selected), probably because of no valid targets (happens often if a card can be cast by an effect). - return false; - } - } + //20091005 - 601.2a if (ability.getSourceId() == null) { logger.error("Ability without sourceId turn " + game.getTurnNum() + ". Ability: " + ability.getRule()); @@ -1129,6 +1177,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getId(), ability.getSourceId(), playerId, permittingObject), ability)) { int bookmark = game.bookmarkState(); + setStoredBookmark(bookmark); // move global bookmark to current state (if you activated mana before then you can't rollback it) Zone fromZone = game.getState().getZone(card.getMainCard().getId()); card.cast(game, fromZone, ability, playerId); Spell spell = game.getStack().getSpell(ability.getId()); @@ -1181,11 +1230,6 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - @Override - public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { - return ability; - } - @Override public boolean playLand(Card card, Game game, boolean ignoreTiming) { // Check for alternate casting possibilities: e.g. land with Morph @@ -1193,18 +1237,20 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } ActivatedAbility playLandAbility = null; - boolean found = false; + boolean foundAlternative = false; for (Ability ability : card.getAbilities()) { // if cast for noMana no Alternative costs are allowed if ((ability instanceof AlternativeSourceCosts) || (ability instanceof OptionalAdditionalSourceCosts)) { - found = true; + foundAlternative = true; } if (ability instanceof PlayLandAbility) { playLandAbility = (ActivatedAbility) ability; } } - if (found) { + + // try alternative cast (face down) + if (foundAlternative) { SpellAbility spellAbility = new SpellAbility(null, "", game.getState().getZone(card.getId()), SpellAbilityType.FACE_DOWN_CREATURE); spellAbility.setControllerId(this.getId()); @@ -1213,9 +1259,11 @@ public abstract class PlayerImpl implements Player, Serializable { return true; } } + if (playLandAbility == null) { return false; } + //20091005 - 114.2a ActivationStatus activationStatus = playLandAbility.canActivate(this.playerId, game); if (ignoreTiming) { @@ -1286,6 +1334,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATE_ABILITY, ability.getId(), ability.getSourceId(), playerId))) { int bookmark = game.bookmarkState(); + setStoredBookmark(bookmark); // move global bookmark to current state (if you activated mana before then you can't rollback it) ability.newId(); ability.setControllerId(playerId); game.getStack().push(new StackAbility(ability, playerId)); @@ -1317,11 +1366,11 @@ public abstract class PlayerImpl implements Player, Serializable { protected boolean specialAction(SpecialAction action, Game game) { //20091005 - 114 if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATE_ABILITY, - action.getSourceId(), action.getId(), playerId))) { + action.getId(), action.getSourceId(), getId()))) { int bookmark = game.bookmarkState(); if (action.activate(game, false)) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATED_ABILITY, - action.getSourceId(), action.getId(), playerId)); + action.getId(), action.getSourceId(), getId())); if (!game.isSimulation()) { game.informPlayers(getLogName() + action.getGameLogMessage(game)); } @@ -1336,15 +1385,11 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - protected void restoreState(int bookmark, String text, Game game) { - game.restoreState(bookmark, text); - if (storedBookmark >= bookmark) { - resetStoredBookmark(game); - } - } - @Override public boolean activateAbility(ActivatedAbility ability, Game game) { + if (ability == null) { + return false; + } boolean result; if (ability instanceof PassAbility) { pass(game); @@ -1447,7 +1492,7 @@ public abstract class PlayerImpl implements Player, Serializable { return true; } } - restoreState(bookmark, triggeredAbility.getRule(), game); // why restore is needed here? (to remove the triggered ability from the stack) + restoreState(bookmark, triggeredAbility.getRule(), game); // why restore is needed here? (to remove the triggered ability from the stack because of no possible targets) return false; } @@ -1499,163 +1544,50 @@ public abstract class PlayerImpl implements Player, Serializable { return useable; } - // Get the usable activated abilities for a *single card object*, that is, either a card or half of a split card. - // Also called on the whole split card but only passing the fuse ability and other whole-split-card shared abilities - // as candidates. - private void getUseableActivatedAbilitiesHalfImpl(MageObject object, Zone zone, Game game, Abilities candidateAbilites, - LinkedHashMap output) { - boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game); - ManaOptions availableMana = null; - // ManaOptions availableMana = getManaAvailable(game); // can only be activated if mana calculation works flawless otherwise player can't play spells they could play if calculation would work correctly - // availableMana.addMana(manaPool.getMana()); - for (Ability ability : candidateAbilites) { - if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) { - if (ability.getZone().match(zone)) { - if (ability instanceof ActivatedAbility) { - if (ability instanceof ActivatedManaAbilityImpl) { - if (((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) { - output.put(ability.getId(), (ActivatedAbility) ability); - } - } else if (canPlay(((ActivatedAbility) ability), availableMana, object, game)) { - output.put(ability.getId(), (ActivatedAbility) ability); - } - } else if (ability instanceof AlternativeSourceCosts) { - if (object.isLand()) { - for (Ability ability2 : object.getAbilities().copy()) { - if (ability2 instanceof PlayLandAbility) { - output.put(ability2.getId(), (ActivatedAbility) ability2); - } - } - } - } - } - } - } - if (zone != Zone.HAND) { - if (Zone.GRAVEYARD == zone && canPlayCardsFromGraveyard()) { - for (ActivatedAbility ability : candidateAbilites.getPlayableAbilities(Zone.HAND)) { - if (canUse - || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) { - if (ability.getZone().equals(zone) || ability.getZone().equals(Zone.HAND)) { - if (ability.canActivate(playerId, game).canActivate()) { - output.put(ability.getId(), ability); - } - } - } - } - } - if (zone != Zone.BATTLEFIELD) { - for (Ability ability : candidateAbilites) { - if (ability.getZone().equals(zone) || ability.getZone().equals(Zone.HAND)) { - if (game.getContinuousEffects().asThough(object.getId(), - AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, - null, - this.getId(), - game) - != null - // if anyone sees an issue with this code, please report it. Worked in my testing. ! - || game.getContinuousEffects().asThough(object.getId(), - AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, - ability, - this.getId(), - game) - != null) { - if (canUse - || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) { - ability.setControllerId(this.getId()); - if (ability instanceof ActivatedAbility - && ability.getZone().match(Zone.HAND) - && ((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) { - output.put(ability.getId(), (ActivatedAbility) ability); - } - } - } - } - } - } - } - } - @Override - public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { - // TODO: replace with getPlayableFromNonHandCardAll (uses for all tests) - boolean previousState = game.inCheckPlayableState(); - game.setCheckPlayableState(true); + public LinkedHashMap getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) { LinkedHashMap useable = new LinkedHashMap<>(); - if (object instanceof StackAbility) { // It may not be possible to activate abilities of stack abilities + // It may not be possible to activate abilities of stack abilities + if (object instanceof StackAbility || object == null) { return useable; } - if (object instanceof SplitCard) { // TODO: use of getAbilities(game) - SplitCard splitCard = (SplitCard) object; - getUseableActivatedAbilitiesHalfImpl(splitCard.getLeftHalfCard(), - zone, game, splitCard.getLeftHalfCard().getAbilities(game), useable); - getUseableActivatedAbilitiesHalfImpl(splitCard.getRightHalfCard(), - zone, game, splitCard.getRightHalfCard().getAbilities(game), useable); - getUseableActivatedAbilitiesHalfImpl(splitCard, - zone, game, splitCard.getSharedAbilities(game), useable); - } else if (object instanceof Card) { - getUseableActivatedAbilitiesHalfImpl(object, - zone, game, ((Card) object).getAbilities(game), useable); - } else if (object != null) { - getUseableActivatedAbilitiesHalfImpl(object, - zone, game, object.getAbilities(), useable); - getOtherUseableActivatedAbilities(object, zone, game, useable); - } + boolean previousState = game.inCheckPlayableState(); + game.setCheckPlayableState(true); + try { + // collect and filter playable activated abilities + // GUI: user clicks on card, but it must activate ability from any card's parts (main, left, right) + UUID needId1, needId2, needId3; + if (object instanceof SplitCard) { + needId1 = object.getId(); + needId2 = ((SplitCard) object).getLeftHalfCard().getId(); + needId3 = ((SplitCard) object).getRightHalfCard().getId(); + } else if (object instanceof AdventureCard) { + needId1 = object.getId(); + needId2 = ((AdventureCard) object).getMainCard().getId(); + needId3 = ((AdventureCard) object).getSpellCard().getId(); + } else if (object instanceof AdventureCardSpell) { + needId1 = object.getId(); + needId2 = ((AdventureCardSpell) object).getParentCard().getId(); + needId3 = object.getId(); + } else { + needId1 = object.getId(); + needId2 = object.getId(); + needId3 = object.getId(); + } - game.setCheckPlayableState(previousState); - return useable; - } - - // Adds special abilities that are given to non permanents by continuous effects - private void getOtherUseableActivatedAbilities(MageObject object, Zone zone, Game game, Map useable) { - Abilities otherAbilities = game.getState().getActivatedOtherAbilities(object.getId(), zone); - if (otherAbilities != null) { - boolean canUse = !(object instanceof Permanent) - || ((Permanent) object).canUseActivatedAbilities(game); - for (ActivatedAbility ability : otherAbilities) { - if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) { - Card card = game.getCard(ability.getSourceId()); - if (card != null) { - if (card.isSplitCard() && ability instanceof FlashbackAbility) { - FlashbackAbility flashbackAbility; - // Left Half - if (card.isInstant()) { - flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(), - TimingRule.INSTANT); - } else { - flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(), - TimingRule.SORCERY); - } - flashbackAbility.setSourceId(card.getId()); - flashbackAbility.setControllerId(card.getOwnerId()); - flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_LEFT); - flashbackAbility.setAbilityName(((SplitCard) card).getLeftHalfCard().getName()); - if (flashbackAbility.canActivate(playerId, game).canActivate()) { - useable.put(flashbackAbility.getId(), flashbackAbility); - } - // Right Half - if (card.isInstant()) { - flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(), - TimingRule.INSTANT); - } else { - flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(), - TimingRule.SORCERY); - } - flashbackAbility.setSourceId(card.getId()); - flashbackAbility.setControllerId(card.getOwnerId()); - flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_RIGHT); - flashbackAbility.setAbilityName(((SplitCard) card).getRightHalfCard().getName()); - if (flashbackAbility.canActivate(playerId, game).canActivate()) { - useable.put(flashbackAbility.getId(), flashbackAbility); - } - - } else { - useable.put(ability.getId(), ability); - } - } + // workaround to find all abilities first and filter it for one object + List allPlayable = getPlayable(game, true, zone, false); + for (ActivatedAbility ability : allPlayable) { + if (Objects.equals(ability.getSourceId(), needId1) + || Objects.equals(ability.getSourceId(), needId2) + || Objects.equals(ability.getSourceId(), needId3)) { + useable.putIfAbsent(ability.getId(), ability); } } + } finally { + game.setCheckPlayableState(previousState); } + return useable; } protected LinkedHashMap getUseableManaAbilities(MageObject object, Zone zone, Game game) { @@ -1762,8 +1694,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void phasing(Game game) { //20091005 - 502.1 - List phasedOut = game.getBattlefield().getPhasedOut(playerId); - for (Permanent permanent : game.getBattlefield().getPhasedIn(playerId)) { + List phasedOut = game.getBattlefield().getPhasedOut(game, playerId); + for (Permanent permanent : game.getBattlefield().getPhasedIn(game, playerId)) { // 502.15i When a permanent phases out, any local enchantments or Equipment // attached to that permanent phase out at the same time. This alternate way of // phasing out is known as phasing out "indirectly." An enchantment or Equipment @@ -2120,6 +2052,10 @@ public abstract class PlayerImpl implements Player, Serializable { @SuppressWarnings({"null", "ConstantConditions"}) private int doDamage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List< UUID> appliedEffects) { + if (!this.isInGame()) { + return 0; + } + if (damage > 0) { if (canDamage(game.getObject(sourceId), game)) { GameEvent event = new DamagePlayerEvent(playerId, @@ -2341,11 +2277,6 @@ public abstract class PlayerImpl implements Player, Serializable { resetStoredBookmark(game); } - @Override - public boolean isEmptyDraw() { - return library.isEmptyDraw(); - } - @Override public void resetPassed() { this.passed = this.loses || this.hasLeft(); @@ -2529,12 +2460,14 @@ public abstract class PlayerImpl implements Player, Serializable { opponent.lostForced(game); } } - // if no more opponents alive, you win and the game ends + // if no more opponents alive (independant from range), you win and the game ends int opponentsAlive = 0; - for (UUID opponentId : game.getOpponents(playerId)) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null && !opponent.hasLost()) { - opponentsAlive++; + for (UUID playerIdToCheck : game.getPlayerList()) { + if (game.isOpponent(this, playerIdToCheck)) { // Check without range + Player opponent = game.getPlayer(playerIdToCheck); + if (opponent != null && !opponent.hasLost()) { + opponentsAlive++; + } } } if (opponentsAlive == 0 && !hasWon()) { @@ -2544,7 +2477,7 @@ public abstract class PlayerImpl implements Player, Serializable { game.end(); } } else { - logger.debug("player won -> but already lost before: " + this.getName()); + logger.debug("player won -> but already lost before or other players still alive: " + this.getName()); } } } @@ -2814,10 +2747,8 @@ public abstract class PlayerImpl implements Player, Serializable { (event.isWinnable() ? "(You called " + event.getChosenName() + ")" : null), "Heads", "Tails", source, game )); - } else if (canChooseHeads) { - event.setResult(true); } else { - event.setResult(false); + event.setResult(canChooseHeads); } game.informPlayers(getLogName() + " chose to keep " + CardUtil.booleanToFlipName(event.getResult())); } @@ -2935,9 +2866,25 @@ public abstract class PlayerImpl implements Player, Serializable { return game.getBattlefield().getAllActivePermanents(blockFilter, playerId, game); } + /** + * Returns the mana options the player currently has. That means which + * combinations of mana are available to cast spells or activate abilities + * etc. + * + * @param game + * @return + */ @Override public ManaOptions getManaAvailable(Game game) { - ManaOptions available = new ManaOptions(); + boolean oldState = game.inCheckPlayableState(); + game.setCheckPlayableState(true); + + ManaOptions availableMana = new ManaOptions(); + availableMana.addMana(manaPool.getMana()); + // conditional mana + for (ConditionalMana conditionalMana : manaPool.getConditionalMana()) { + availableMana.addMana(conditionalMana); + } List> sourceWithoutManaCosts = new ArrayList<>(); List> sourceWithCosts = new ArrayList<>(); @@ -2947,11 +2894,24 @@ public abstract class PlayerImpl implements Player, Serializable { boolean withCost = false; Abilities manaAbilities = permanent.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game); - for (ActivatedManaAbilityImpl ability : manaAbilities) { + for (Iterator it = manaAbilities.iterator(); it.hasNext(); ) { + ActivatedManaAbilityImpl ability = it.next(); if (canUse == null) { canUse = permanent.canUseActivatedAbilities(game); } if (canUse && ability.canActivate(playerId, game).canActivate()) { + // abilities without Tap costs have to be handled as separate sources, because they can be used also + if (!ability.hasTapCost()) { + it.remove(); + Abilities noTapAbilities = new AbilitiesImpl<>(ability); + if (ability.getManaCosts().isEmpty()) { + sourceWithoutManaCosts.add(noTapAbilities); + } else { + sourceWithCosts.add(noTapAbilities); + } + continue; + } + canAdd = true; if (!ability.getManaCosts().isEmpty()) { withCost = true; @@ -2969,17 +2929,54 @@ public abstract class PlayerImpl implements Player, Serializable { } for (Abilities manaAbilities : sourceWithoutManaCosts) { - available.addMana(manaAbilities, game); + availableMana.addMana(manaAbilities, game); } - for (Abilities manaAbilities : sourceWithCosts) { - available.removeDuplicated(); - available.addManaWithCost(manaAbilities, game); + + boolean anAbilityWasUsed = true; + while (anAbilityWasUsed && !sourceWithCosts.isEmpty()) { + anAbilityWasUsed = false; + for (Iterator> iterator = sourceWithCosts.iterator(); iterator.hasNext(); ) { + Abilities manaAbilities = iterator.next(); + boolean used = availableMana.addManaWithCost(manaAbilities, game); + if (used) { + iterator.remove(); + availableMana.removeDuplicated(); + anAbilityWasUsed = true; + } + } } // remove duplicated variants (see ManaOptionsTest for info - when that rises) - available.removeDuplicated(); + availableMana.removeDuplicated(); - return available; + game.setCheckPlayableState(oldState); + return availableMana; + } + + /** + * Used during calculation of available mana to gather the amount of + * producable triggered mana caused by using mana sources. So the set value + * is only used during the calculation of the mana produced by one source + * and cleared thereafter + * + * @param netManaAvailable the net mana produced by the triggered mana + * abaility + */ + @Override + public void addAvailableTriggeredMana(List netManaAvailable) { + this.availableTriggeredManaList.add(netManaAvailable); + } + + /** + * Used during calculation of available mana to get the amount of producable + * triggered mana caused by using mana sources. The list is cleared as soon + * the value is retrieved during available mana calculation. + * + * @return + */ + @Override + public List> getAvailableTriggeredMana() { + return availableTriggeredManaList; } // returns only mana producers that don't require mana payment @@ -3043,18 +3040,20 @@ public abstract class PlayerImpl implements Player, Serializable { /** * @param ability - * @param available if null, it won't be checked if enough mana is available + * @param availableMana if null, it won't be checked if enough mana is + * available * @param sourceObject * @param game * @return */ - protected boolean canPlay(ActivatedAbility ability, ManaOptions available, MageObject sourceObject, Game game) { + protected boolean canPlay(ActivatedAbility ability, ManaOptions availableMana, MageObject sourceObject, Game game) { if (!(ability instanceof ActivatedManaAbilityImpl)) { ActivatedAbility copy = ability.copy(); // Copy is needed because cost reduction effects modify e.g. the mana to activate/cast the ability if (!copy.canActivate(playerId, game).canActivate()) { return false; } - if (available != null) { + if (availableMana != null) { + sourceObject.adjustCosts(copy, game); game.getContinuousEffects().costModification(copy, game); } boolean canBeCastRegularly = true; @@ -3065,31 +3064,8 @@ public abstract class PlayerImpl implements Player, Serializable { canBeCastRegularly = false; } if (canBeCastRegularly) { - ManaOptions abilityOptions = copy.getMinimumCostToActivate(playerId, game); - if (abilityOptions.isEmpty()) { + if (canPayMinimumManaCost(copy, availableMana, game)) { return true; - } else { - if (available == null) { - return true; - } - MageObjectReference permittingObject = game.getContinuousEffects().asThough(copy.getSourceId(), - AsThoughEffectType.SPEND_OTHER_MANA, copy, copy.getControllerId(), game); - for (Mana mana : abilityOptions) { - for (Mana avail : available) { - // TODO: SPEND_OTHER_MANA effects with getAsThoughManaType can change mana type to pay, - // but that code processing it as any color, need to test and fix another use cases - // (example: Sunglasses of Urza - may spend white mana as though it were red mana) - - // - // add tests for non any color like Sunglasses of Urza - if (permittingObject != null && mana.count() <= avail.count()) { - return true; - } - if (mana.enough(avail)) { // here we need to check if spend mana as though allow to pay the mana cost - return true; - } - } - } } } @@ -3121,13 +3097,92 @@ public abstract class PlayerImpl implements Player, Serializable { } // ALTERNATIVE COST from source card (any AlternativeSourceCosts) - return canPlayCardByAlternateCost(game.getCard(ability.getSourceId()), available, copy, game); + return canPlayCardByAlternateCost(game.getCard(ability.getSourceId()), availableMana, copy, game); } return false; } - protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions available, Ability ability, Game game) { + protected boolean canPayMinimumManaCost(ActivatedAbility ability, ManaOptions availableMana, Game game) { + ManaOptions abilityOptions = ability.getMinimumCostToActivate(playerId, game); + if (abilityOptions.isEmpty()) { + return true; + } else { + if (availableMana == null) { + return true; + } + // Check for pay option with like phyrexian mana + if (getPhyrexianColors() != null) { + addPhyrexianLikePayOptions(abilityOptions, availableMana, game); + } + + MageObjectReference permittingObject = game.getContinuousEffects().asThough(ability.getSourceId(), + AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game); + for (Mana mana : abilityOptions) { + for (Mana avail : availableMana) { + // TODO: SPEND_OTHER_MANA effects with getAsThoughManaType can change mana type to pay, + // but that code processing it as any color, need to test and fix another use cases + // (example: Sunglasses of Urza - may spend white mana as though it were red mana) + + // + // add tests for non any color like Sunglasses of Urza + if (permittingObject != null && mana.count() <= avail.count()) { + return true; + } + if (mana.enough(avail)) { // here we need to check if spend mana as though allow to pay the mana cost + return true; + } + } + } + } + return false; + } + + private void addPhyrexianLikePayOptions(ManaOptions abilityOptions, ManaOptions availableMana, Game game) { + int maxLifeMana = getLife() / 2; + if (maxLifeMana > 0) { + Set phyrexianOptions = new HashSet<>(); + for (Mana mana : abilityOptions) { + int availableLifeMana = maxLifeMana; + if (getPhyrexianColors().isBlack()) { + createReducedManaPayOption(availableLifeMana, mana, phyrexianOptions, ManaType.BLACK); + } + if (getPhyrexianColors().isBlue()) { + createReducedManaPayOption(availableLifeMana, mana, phyrexianOptions, ManaType.BLUE); + } + if (getPhyrexianColors().isRed()) { + createReducedManaPayOption(availableLifeMana, mana, phyrexianOptions, ManaType.RED); + } + if (getPhyrexianColors().isGreen()) { + createReducedManaPayOption(availableLifeMana, mana, phyrexianOptions, ManaType.GREEN); + } + if (getPhyrexianColors().isWhite()) { + createReducedManaPayOption(availableLifeMana, mana, phyrexianOptions, ManaType.WHITE); + } + } + abilityOptions.addAll(phyrexianOptions); + } + } + + private int createReducedManaPayOption(int availableLifeMana, Mana oldPayOption, Set phyrexianOptions, ManaType manaType) { + if (oldPayOption.get(manaType) > 0) { + Mana manaCopy = oldPayOption.copy(); + int restVal; + if (availableLifeMana > oldPayOption.get(manaType)) { + restVal = 0; + availableLifeMana -= oldPayOption.get(manaType); + } else { + restVal = oldPayOption.get(manaType) - availableLifeMana; + availableLifeMana = 0; + } + manaCopy.set(manaType, restVal); + phyrexianOptions.add(manaCopy); + } + return availableLifeMana; + } + + protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions availableMana, Ability ability, Game game) { if (sourceObject != null && !(sourceObject instanceof Permanent)) { + Ability copyAbility; // for alternative cost and reduce tries for (Ability alternateSourceCostsAbility : sourceObject.getAbilities()) { // if cast for noMana no Alternative costs are allowed if (alternateSourceCostsAbility instanceof AlternativeSourceCosts) { @@ -3143,14 +3198,25 @@ public abstract class PlayerImpl implements Player, Serializable { if (manaCosts.isEmpty()) { return true; } else { - if (available == null) { + if (availableMana == null) { return true; } - for (Mana mana : manaCosts.getOptions()) { - for (Mana avail : available) { - if (mana.enough(avail)) { - return true; - } + + // alternative cost reduce + copyAbility = ability.copy(); + copyAbility.getManaCostsToPay().clear(); + copyAbility.getManaCostsToPay().addAll(manaCosts.copy()); + sourceObject.adjustCosts(copyAbility, game); + game.getContinuousEffects().costModification(copyAbility, game); + + // reduced all cost + if (copyAbility.getManaCostsToPay().isEmpty()) { + return true; + } + + for (Mana mana : copyAbility.getManaCostsToPay().getOptions()) { + if (availableMana.enough(mana)) { + return true; } } } @@ -3174,11 +3240,25 @@ public abstract class PlayerImpl implements Player, Serializable { if (manaCosts.isEmpty()) { return true; } else { - for (Mana mana : manaCosts.getOptions()) { - for (Mana avail : available) { - if (mana.enough(avail)) { - return true; - } + if (availableMana == null) { + return true; + } + + // alternative cost reduce + copyAbility = ability.copy(); + copyAbility.getManaCostsToPay().clear(); + copyAbility.getManaCostsToPay().addAll(manaCosts.copy()); + sourceObject.adjustCosts(copyAbility, game); + game.getContinuousEffects().costModification(copyAbility, game); + + // reduced all cost + if (copyAbility.getManaCostsToPay().isEmpty()) { + return true; + } + + for (Mana mana : copyAbility.getManaCostsToPay().getOptions()) { + if (availableMana.enough(mana)) { + return true; } } } @@ -3190,108 +3270,108 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability ability, Game game) { - if (!(sourceObject instanceof Permanent)) { - Ability sourceAbility = sourceObject.getAbilities().stream() - .filter(landAbility -> landAbility.getAbilityType() == AbilityType.PLAY_LAND) - .findFirst().orElse(null); + protected ActivatedAbility findActivatedAbilityFromPlayable(MageObject object, ManaOptions availableMana, Ability ability, Game game) { - if (sourceAbility != null && ((AlternativeSourceCosts) ability).isAvailable(sourceAbility, game)) { - if (ability.getCosts().canPay(ability, sourceObject.getId(), this.getId(), game)) { - ManaCostsImpl manaCosts = new ManaCostsImpl(); - for (Cost cost : ability.getCosts()) { - if (cost instanceof ManaCost) { - manaCosts.add((ManaCost) cost); - } - } - - if (manaCosts.isEmpty()) { - return true; - } else { - for (Mana mana : manaCosts.getOptions()) { - for (Mana avail : available) { - if (mana.enough(avail)) { - return true; - } - } - } - } - } + // special mana to pay spell cost + ManaOptions manaFull = availableMana.copy(); + if (ability instanceof SpellAbility) { + for (AlternateManaPaymentAbility altAbility : CardUtil.getAbilities(object, game).stream() + .filter(a -> a instanceof AlternateManaPaymentAbility) + .map(a -> (AlternateManaPaymentAbility) a) + .collect(Collectors.toList())) { + ManaOptions manaSpecial = altAbility.getManaOptions(ability, game, ability.getManaCostsToPay()); + manaFull.addMana(manaSpecial); } } - return false; - } - private void getPlayableFromGraveyardCard(Game game, Card card, Abilities candidateAbilities, ManaOptions availableMana, List output) { - MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(), - AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), this.getId(), game); - for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) { - boolean possible = false; - if (ability.getZone().match(Zone.GRAVEYARD)) { - possible = true; - } else if (ability.getZone().match(Zone.HAND) - && (ability instanceof SpellAbility - || ability instanceof PlayLandAbility)) { - if (permittingObject != null || canPlayCardsFromGraveyard()) { - possible = true; - } + // replace alternative abilities by real play abilities (e.g. morph/facedown static ability by play land) + if (ability instanceof ActivatedManaAbilityImpl) { + // mana ability + if (((ActivatedManaAbilityImpl) ability).canActivate(this.getId(), game).canActivate()) { + return (ActivatedManaAbilityImpl) ability; } - if (possible && canPlay(ability, availableMana, card, game)) { - output.add(ability); + } else if (ability instanceof AlternativeSourceCosts) { + // alternative cost must be replaced by real play ability + return findActivatedAbilityFromAlternativeSourceCost(object, manaFull, ability, game); + } else if (ability instanceof ActivatedAbility) { + // all other activated ability + if (canPlay((ActivatedAbility) ability, manaFull, object, game)) { + return (ActivatedAbility) ability; } } + + // non playable abilities like static + return null; } - private void getPlayableFromNonHandCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List output) { - if (fromZone == null) { + protected ActivatedAbility findActivatedAbilityFromAlternativeSourceCost(MageObject object, ManaOptions availableMana, Ability ability, Game game) { + // return play ability that can activate AlternativeSourceCosts + if (ability instanceof AlternativeSourceCosts && object != null && !(object instanceof Permanent)) { + ActivatedAbility playAbility = null; + if (object.isLand()) { + playAbility = (PlayLandAbility) CardUtil.getAbilities(object, game).stream().filter(a -> a instanceof PlayLandAbility).findFirst().orElse(null); + } else if (object instanceof Card) { + playAbility = ((Card) object).getSpellAbility(); + } + if (playAbility == null) { + return null; + } + + // 707.4.Objects that are cast face down are turned face down before they are put onto the stack + // E.g. no lands per turn limit, no cast restrictions, cost reduce, etc + // Even mana cost can't be checked here without lookahead + // So make it available all the time + boolean canUse; + if (ability instanceof MorphAbility && object instanceof Card && game.canPlaySorcery(getId())) { + canUse = canPlayCardByAlternateCost((Card) object, availableMana, playAbility, game); + } else { + canUse = canPlay(playAbility, availableMana, object, game); // canPlay already checks alternative source costs and all conditions + } + + if (canUse) { + return playAbility; + } + } + return null; + } + + private void getPlayableFromObjectAll(Game game, Zone fromZone, MageObject object, ManaOptions availableMana, List output) { + if (fromZone == null || object == null) { return; } // BASIC abilities - if (card instanceof SplitCard) { - SplitCard splitCard = (SplitCard) card; - getPlayableFromNonHandCardSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(), availableMana, output); - getPlayableFromNonHandCardSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(), availableMana, output); - getPlayableFromNonHandCardSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output); - } else if (card instanceof AdventureCard) { + if (object instanceof SplitCard) { + SplitCard splitCard = (SplitCard) object; + getPlayableFromObjectSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(game), availableMana, output); + getPlayableFromObjectSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(game), availableMana, output); + getPlayableFromObjectSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output); + } else if (object instanceof AdventureCard) { // adventure must use different card characteristics for different spells (main or adventure) - AdventureCard adventureCard = (AdventureCard) card; - getPlayableFromNonHandCardSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(), availableMana, output); - getPlayableFromNonHandCardSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output); + AdventureCard adventureCard = (AdventureCard) object; + getPlayableFromObjectSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(game), availableMana, output); + getPlayableFromObjectSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output); + } else if (object instanceof Card) { + getPlayableFromObjectSingle(game, fromZone, object, ((Card) object).getAbilities(game), availableMana, output); } else { - getPlayableFromNonHandCardSingle(game, fromZone, card, card.getAbilities(), availableMana, output); + // other things like StackObject or CommandObject + getPlayableFromObjectSingle(game, fromZone, object, object.getAbilities(), availableMana, output); } - // DYNAMIC ADDED abilities - if (fromZone != Zone.ALL) { // TODO: test revealed cards with dynamic added abilities - // Other activated abilities (added dynamic by effects) - LinkedHashMap useable; - if (card instanceof AdventureCard) { - // adventure cards (contains two different cards: main and adventure spell) - useable = new LinkedHashMap<>(); - getOtherUseableActivatedAbilities(((AdventureCard) card).getSpellCard(), fromZone, game, useable); - output.addAll(useable.values()); - - useable = new LinkedHashMap<>(); - getOtherUseableActivatedAbilities(card, fromZone, game, useable); - output.addAll(useable.values()); - } else { - // all other cards (TODO: check split cards with dynamic added abilities) - useable = new LinkedHashMap<>(); - getOtherUseableActivatedAbilities(card, fromZone, game, useable); - output.addAll(useable.values()); - } - } + // DYNAMIC ADDED abilities are adds in getAbilities(game) } - private void getPlayableFromNonHandCardSingle(Game game, Zone fromZone, Card card, Abilities candidateAbilities, ManaOptions availableMana, List output) { + private void getPlayableFromObjectSingle(Game game, Zone fromZone, MageObject object, Abilities candidateAbilities, ManaOptions availableMana, List output) { // check "can play" condition as affected controller (BUT play from not own hand zone must be checked as original controller) - for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) { + // must check all abilities, not activated only + for (Ability ability : candidateAbilities) { + if (!(ability instanceof ActivatedAbility)) { + continue; + } boolean isPlaySpell = (ability instanceof SpellAbility); boolean isPlayLand = (ability instanceof PlayLandAbility); // as original controller - // play land restrictions if (isPlayLand && game.getContinuousEffects().preventedByRuleModification( GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(), @@ -3314,7 +3394,7 @@ public abstract class PlayerImpl implements Player, Serializable { MageObjectReference permittingObject; if (isPlaySpell || isPlayLand) { // play hand from non hand zone - permittingObject = game.getContinuousEffects().asThough(card.getId(), + permittingObject = game.getContinuousEffects().asThough(object.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, ability, this.getId(), game); } else { // other abilities from direct zones @@ -3323,52 +3403,77 @@ public abstract class PlayerImpl implements Player, Serializable { boolean canActivateAsHandZone = permittingObject != null || (fromZone == Zone.GRAVEYARD && canPlayCardsFromGraveyard()); + boolean possibleToPlay = false; - // as affected controller + // spell/hand abilities (play from all zones) + // need permitingObject or canPlayCardsFromGraveyard + if (canActivateAsHandZone + && ability.getZone().match(Zone.HAND) + && (isPlaySpell || isPlayLand)) { + possibleToPlay = true; + } - UUID savedControllerId = ability.getControllerId(); - ability.setControllerId(this.getId()); - try { - boolean possibleToPlay = false; + // zone's abilities (play from specific zone) + // no need in permitingObject + if (fromZone != Zone.ALL && ability.getZone().match(fromZone)) { + possibleToPlay = true; + } - // spell/hand abilities (play from all zones) - // need permitingObject or canPlayCardsFromGraveyard - if (canActivateAsHandZone - && ability.getZone().match(Zone.HAND) - && (isPlaySpell || isPlayLand)) { - possibleToPlay = true; + if (!possibleToPlay) { + continue; + } + + // direct mode (with original controller) + ActivatedAbility playAbility = findActivatedAbilityFromPlayable(object, availableMana, ability, game); + if (playAbility != null && !output.contains(playAbility)) { + output.add(playAbility); + continue; + } + + // from non hand mode (with affected controller) + if (canActivateAsHandZone && ability.getControllerId() != this.getId()) { + UUID savedControllerId = ability.getControllerId(); + ability.setControllerId(this.getId()); + try { + playAbility = findActivatedAbilityFromPlayable(object, availableMana, ability, game); + if (playAbility != null && !output.contains(playAbility)) { + output.add(playAbility); + } + } finally { + ability.setControllerId(savedControllerId); } - - // zone's abilities (play from specific zone) - // no need in permitingObject - if (fromZone != Zone.ALL && ability.getZone().match(fromZone)) { - possibleToPlay = true; - } - - if (possibleToPlay && canPlay(ability, availableMana, card, game)) { - output.add(ability); - } - } finally { - ability.setControllerId(savedControllerId); } } } @Override - public List getPlayable(Game game, boolean hidden) { + public List getPlayable(Game game, boolean hidden) { return getPlayable(game, hidden, Zone.ALL, true); } - public List getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) { - List playable = new ArrayList<>(); - game.setCheckPlayableState(true); - if (!shouldSkipGettingPlayable(game)) { - ManaOptions availableMana = getManaAvailable(game); - availableMana.addMana(manaPool.getMana()); - for (ConditionalMana conditionalMana : manaPool.getConditionalMana()) { - availableMana.addMana(conditionalMana); - } + /** + * Returns a list of all available spells and abilities the player can + * currently cast/activate with his available ressources + * + * @param game + * @param hidden also from hidden objects (e.g. turned face + * down cards ?) + * @param fromZone of objects from which zone (ALL = from all + * zones) + * @param hideDuplicatedAbilities if equal abilities exist return only the + * first instance + * @return + */ + public List getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) { + List playable = new ArrayList<>(); + if (shouldSkipGettingPlayable(game)) { + return playable; + } + boolean previousState = game.inCheckPlayableState(); + game.setCheckPlayableState(true); + try { + ManaOptions availableMana = getManaAvailable(game); // get available mana options (mana pool and conditional mana added (but conditional still lose condition)) boolean fromAll = fromZone.equals(Zone.ALL); if (hidden && (fromAll || fromZone == Zone.HAND)) { for (Card card : hand.getCards(game)) { @@ -3396,26 +3501,9 @@ public abstract class PlayerImpl implements Player, Serializable { continue; } - // if have alternative cost - if (ability instanceof ActivatedAbility) { - // normal ability - if (canPlay((ActivatedAbility) ability, availableMana, card, game)) { - playable.add(ability); - } - } else if (ability instanceof AlternativeSourceCosts) { - if (card.isLand()) { - if (canLandPlayAlternateSourceCostsAbility(card, availableMana, ability, game)) { // e.g. Land with Morph - playable.add(ability); - } - } else if (card.isCreature()) { // e.g. makes a card available for play by Morph if the card may not be cast normally - if (!playable.contains(card.getSpellAbility())) { - if (((AlternativeSourceCosts) ability).isAvailable(card.getSpellAbility(), game)) { - playable.add(card.getSpellAbility()); - } - } - } - } else { - // unknown type + ActivatedAbility playAbility = findActivatedAbilityFromPlayable(card, availableMana, ability, game); + if (playAbility != null && !playable.contains(playAbility)) { + playable.add(playAbility); } } } @@ -3424,14 +3512,14 @@ public abstract class PlayerImpl implements Player, Serializable { if (fromAll || fromZone == Zone.GRAVEYARD) { for (Card card : graveyard.getCards(game)) { - getPlayableFromNonHandCardAll(game, Zone.GRAVEYARD, card, availableMana, playable); + getPlayableFromObjectAll(game, Zone.GRAVEYARD, card, availableMana, playable); } } if (fromAll || fromZone == Zone.EXILED) { for (ExileZone exile : game.getExile().getExileZones()) { for (Card card : exile.getCards(game)) { - getPlayableFromNonHandCardAll(game, Zone.EXILED, card, availableMana, playable); + getPlayableFromObjectAll(game, Zone.EXILED, card, availableMana, playable); } } } @@ -3441,7 +3529,16 @@ public abstract class PlayerImpl implements Player, Serializable { for (Cards revealedCards : game.getState().getRevealed().values()) { for (Card card : revealedCards.getCards(game)) { // revealed cards can be from any zones - getPlayableFromNonHandCardAll(game, game.getState().getZone(card.getId()), card, availableMana, playable); + getPlayableFromObjectAll(game, game.getState().getZone(card.getId()), card, availableMana, playable); + } + } + } + + // check to play companion cards + if (fromAll || fromZone == Zone.OUTSIDE) { + for (Cards companionCards : game.getState().getCompanion().values()) { + for (Card card : companionCards.getCards(game)) { + getPlayableFromObjectAll(game, Zone.OUTSIDE, card, availableMana, playable); } } } @@ -3450,28 +3547,30 @@ public abstract class PlayerImpl implements Player, Serializable { if (fromAll || fromZone == Zone.LIBRARY) { for (UUID playerInRangeId : game.getState().getPlayersInRange(getId(), game)) { Player player = game.getPlayer(playerInRangeId); - if (player != null) { - if (/*player.isTopCardRevealed() &&*/player.getLibrary().hasCards()) { - Card card = player.getLibrary().getFromTop(game); - if (card != null) { - getPlayableFromNonHandCardAll(game, Zone.LIBRARY, card, availableMana, playable); - } + if (player != null && player.getLibrary().hasCards()) { + Card card = player.getLibrary().getFromTop(game); + if (card != null) { + getPlayableFromObjectAll(game, Zone.LIBRARY, card, availableMana, playable); } } } } // eliminate duplicate activated abilities (uses for AI plays) - Map activatedUnique = new HashMap<>(); - List activatedAll = new ArrayList<>(); + Map activatedUnique = new HashMap<>(); + List activatedAll = new ArrayList<>(); // activated abilities from battlefield objects if (fromAll || fromZone == Zone.BATTLEFIELD) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) { - LinkedHashMap useableAbilities = getUseableActivatedAbilities(permanent, Zone.BATTLEFIELD, game); - for (ActivatedAbility ability : useableAbilities.values()) { - activatedUnique.putIfAbsent(ability.toString(), ability); - activatedAll.add(ability); + for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { + boolean canUseActivated = permanent.canUseActivatedAbilities(game); + List currentPlayable = new ArrayList<>(); + getPlayableFromObjectAll(game, Zone.BATTLEFIELD, permanent, availableMana, currentPlayable); + for (ActivatedAbility ability : currentPlayable) { + if (ability instanceof SpecialAction || canUseActivated) { + activatedUnique.putIfAbsent(ability.toString(), ability); + activatedAll.add(ability); + } } } } @@ -3479,12 +3578,11 @@ public abstract class PlayerImpl implements Player, Serializable { // activated abilities from stack objects if (fromAll || fromZone == Zone.STACK) { for (StackObject stackObject : game.getState().getStack()) { - for (ActivatedAbility ability : stackObject.getAbilities().getActivatedAbilities(Zone.STACK)) { - if (ability != null - && canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) { - activatedUnique.put(ability.toString(), ability); - activatedAll.add(ability); - } + List currentPlayable = new ArrayList<>(); + getPlayableFromObjectAll(game, Zone.STACK, stackObject, availableMana, currentPlayable); + for (ActivatedAbility ability : currentPlayable) { + activatedUnique.put(ability.toString(), ability); + activatedAll.add(ability); } } } @@ -3492,12 +3590,11 @@ public abstract class PlayerImpl implements Player, Serializable { // activated abilities from objects in the command zone (emblems or commanders) if (fromAll || fromZone == Zone.COMMAND) { for (CommandObject commandObject : game.getState().getCommand()) { - for (ActivatedAbility ability : commandObject.getAbilities().getActivatedAbilities(Zone.COMMAND)) { - if (ability.isControlledBy(getId()) - && canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) { - activatedUnique.put(ability.toString(), ability); - activatedAll.add(ability); - } + List currentPlayable = new ArrayList<>(); + getPlayableFromObjectAll(game, Zone.COMMAND, commandObject, availableMana, currentPlayable); + for (ActivatedAbility ability : currentPlayable) { + activatedUnique.put(ability.toString(), ability); + activatedAll.add(ability); } } } @@ -3507,8 +3604,10 @@ public abstract class PlayerImpl implements Player, Serializable { } else { playable.addAll(activatedAll); } + } finally { + game.setCheckPlayableState(previousState); } - game.setCheckPlayableState(false); + return playable; } @@ -3524,7 +3623,7 @@ public abstract class PlayerImpl implements Player, Serializable { */ @Override public Map getPlayableObjects(Game game, Zone zone) { - List playableAbilities = getPlayable(game, true, zone, false); // do not hide duplicated abilities/cards + List playableAbilities = getPlayable(game, true, zone, false); // do not hide duplicated abilities/cards Map playableObjects = new HashMap<>(); for (Ability ability : playableAbilities) { if (ability.getSourceId() != null) { @@ -3575,7 +3674,6 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public List getPlayableOptions(Ability ability, Game game) { List options = new ArrayList<>(); - if (ability.isModal()) { addModeOptions(options, ability, game); } else if (!ability.getTargets().getUnchosen().isEmpty()) { @@ -3596,8 +3694,8 @@ public abstract class PlayerImpl implements Player, Serializable { // TODO: Support modal spells with more than one selectable mode for (Mode mode : option.getModes().values()) { Ability newOption = option.copy(); - newOption.getModes().getSelectedModes().clear(); - newOption.getModes().getSelectedModes().add(mode.getId()); + newOption.getModes().clearSelectedModes(); + newOption.getModes().addSelectedMode(mode.getId()); newOption.getModes().setActiveMode(mode); if (!newOption.getTargets().getUnchosen().isEmpty()) { if (!newOption.getManaCosts().getVariableCosts().isEmpty()) { @@ -3712,8 +3810,13 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean canPayLifeCost() { - return isLifeTotalCanChange() && canPayLifeCost; + public boolean canPayLifeCost(Ability ability) { + if (!canPayLifeCost + && (AbilityType.ACTIVATED.equals(ability.getAbilityType()) + || AbilityType.SPELL.equals(ability.getAbilityType()))) { + return false; + } + return isLifeTotalCanChange(); } @Override @@ -3982,7 +4085,7 @@ public abstract class PlayerImpl implements Player, Serializable { break; case COMMAND: for (Card card : cards) { - if (moveCardToCommandWithInfo(card, source.getSourceId(), game, fromZone)) { + if (moveCardToCommandWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone)) { successfulMovedCards.add(card); } } @@ -3991,7 +4094,7 @@ public abstract class PlayerImpl implements Player, Serializable { for (Card card : cards) { if (card instanceof Permanent) { game.getBattlefield().removePermanent(card.getId()); - ZoneChangeEvent event = new ZoneChangeEvent(card.getId(), + ZoneChangeEvent event = new ZoneChangeEvent((Permanent) card, (source == null ? null : source.getSourceId()), byOwner ? card.getOwnerId() : getId(), Zone.BATTLEFIELD, Zone.OUTSIDE, appliedEffects); game.fireEvent(event); @@ -4251,10 +4354,12 @@ public abstract class PlayerImpl implements Player, Serializable { game.getStack().remove(spell, game); } } - game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName() - + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' ' - + (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH) - + ' ' : "") + "to the exile zone"); + if (Zone.EXILED.equals(game.getState().getZone(card.getId()))) { // only if target zone was not replaced + game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName() + + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' ' + + (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH) + + ' ' : "") + "to the exile zone"); + } } result = true; @@ -4262,6 +4367,17 @@ public abstract class PlayerImpl implements Player, Serializable { return result; } + @Override + public Cards millCards(int toMill, Ability source, Game game) { + GameEvent event = GameEvent.getEvent(EventType.MILL_CARDS, getId(), source.getSourceId(), getId(), toMill); + if (game.replaceEvent(event)) { + return new CardsImpl(); + } + Cards cards = new CardsImpl(this.getLibrary().getTopCards(game, event.getAmount())); + this.moveCards(cards, Zone.GRAVEYARD, source, game); + return cards; + } + @Override public boolean hasOpponent(UUID playerToCheckId, Game game) { return !this.getId().equals(playerToCheckId) @@ -4375,7 +4491,8 @@ public abstract class PlayerImpl implements Player, Serializable { cards.addAll(getLibrary().getTopCards(game, value)); if (!cards.isEmpty()) { TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, - new FilterCard("cards to PUT on the BOTTOM of your library (Scry)")); + new FilterCard("card" + (cards.size() == 1 ? "" : "s") + + " to PUT on the BOTTOM of your library (Scry)")); chooseTarget(Outcome.Benefit, cards, target, source, game); putCardsOnBottomOfLibrary(new CardsImpl(target.getTargets()), game, source, true); cards.removeAll(target.getTargets()); @@ -4470,39 +4587,24 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void addPhyrexianToColors(FilterMana colors) { - if (colors.isWhite()) { - this.phyrexianColors.setWhite(true); - } - if (colors.isBlue()) { - this.phyrexianColors.setBlue(true); - } - if (colors.isBlack()) { - this.phyrexianColors.setBlack(true); - } - if (colors.isRed()) { - this.phyrexianColors.setRed(true); - } - if (colors.isGreen()) { - this.phyrexianColors.setGreen(true); - } - } - - @Override - public void removePhyrexianFromColors(FilterMana colors) { - if (colors.isWhite()) { - this.phyrexianColors.setWhite(false); - } - if (colors.isBlue()) { - this.phyrexianColors.setBlue(false); - } - if (colors.isBlack()) { - this.phyrexianColors.setBlack(false); - } - if (colors.isRed()) { - this.phyrexianColors.setRed(false); - } - if (colors.isGreen()) { - this.phyrexianColors.setGreen(false); + if (phyrexianColors == null) { + phyrexianColors = colors.copy(); + } else { + if (colors.isWhite()) { + this.phyrexianColors.setWhite(true); + } + if (colors.isBlue()) { + this.phyrexianColors.setBlue(true); + } + if (colors.isBlack()) { + this.phyrexianColors.setBlack(true); + } + if (colors.isRed()) { + this.phyrexianColors.setRed(true); + } + if (colors.isGreen()) { + this.phyrexianColors.setGreen(true); + } } } diff --git a/Mage/src/main/java/mage/players/PlayerList.java b/Mage/src/main/java/mage/players/PlayerList.java index f25b5a9a16..c01d4cfc3d 100644 --- a/Mage/src/main/java/mage/players/PlayerList.java +++ b/Mage/src/main/java/mage/players/PlayerList.java @@ -53,6 +53,7 @@ public class PlayerList extends CircularList { player.setReachedNextTurnAfterLeaving(true); } } + if (player.getId().equals(start)) { return null; } @@ -63,11 +64,15 @@ public class PlayerList extends CircularList { public Player getPrevious(Game game) { Player player; UUID start = this.get(); + if (start == null) { + return null; + } while (true) { player = game.getPlayer(super.getPrevious()); if (player.isInGame()) { break; } + if (player.getId().equals(start)) { return null; } diff --git a/Mage/src/main/java/mage/players/StubPlayer.java b/Mage/src/main/java/mage/players/StubPlayer.java index 6891338002..2f926a12e4 100644 --- a/Mage/src/main/java/mage/players/StubPlayer.java +++ b/Mage/src/main/java/mage/players/StubPlayer.java @@ -13,6 +13,7 @@ import mage.cards.decks.Deck; import mage.choices.Choice; import mage.constants.Outcome; import mage.constants.RangeOfInfluence; +import mage.filter.FilterMana; import mage.game.Game; import mage.game.combat.CombatGroup; import mage.game.draft.Draft; @@ -25,16 +26,16 @@ import mage.target.TargetCard; import mage.target.TargetPlayer; import java.io.Serializable; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import static com.google.common.collect.Iterables.getOnlyElement; -import static java.util.stream.Collectors.toList; -import mage.filter.FilterMana; public class StubPlayer extends PlayerImpl implements Player { + @Override public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { if (target instanceof TargetPlayer) { for (Player player : game.getPlayers().values()) { @@ -47,6 +48,7 @@ public class StubPlayer extends PlayerImpl implements Player { return false; } + @Override public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) { cards.getCards(game).stream().map(MageItem::getId).forEach(cardId -> target.add(cardId, game)); return true; @@ -54,15 +56,10 @@ public class StubPlayer extends PlayerImpl implements Player { @Override public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { - if (target.getFilter().getMessage() != null && target.getFilter().getMessage().endsWith("(Discard for Mulligan)")) { - chooseDiscardBottom(game, target.getMinNumberOfTargets(), cards.getCards(game) - .stream().map(MageItem::getId).collect(toList())).forEach(cardId -> target.add(cardId, game)); - } else { - UUID cardId = getOnlyElement(cards.getCards(game)).getId(); - if (chooseScry(game, cardId)) { - target.add(cardId, game); - return true; - } + UUID cardId = getOnlyElement(cards.getCards(game)).getId(); + if (chooseScry(game, cardId)) { + target.add(cardId, game); + return true; } return false; } @@ -111,6 +108,10 @@ public class StubPlayer extends PlayerImpl implements Player { @Override public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { + if (target.getFilter().getMessage() != null && target.getFilter().getMessage().endsWith(" more) to put on the bottom of your library")) { + chooseDiscardBottom(game, target.getMinNumberOfTargets(), new ArrayList<>(target.possibleTargets(null, null, game))) + .forEach(cardId -> target.add(cardId, game)); + } return false; } @@ -224,11 +225,6 @@ public class StubPlayer extends PlayerImpl implements Player { } - @Override - public void removePhyrexianFromColors(FilterMana colors) { - - } - @Override public FilterMana getPhyrexianColors() { return (new FilterMana()); diff --git a/Mage/src/main/java/mage/target/Target.java b/Mage/src/main/java/mage/target/Target.java index ec2a5aa573..87c6e89c07 100644 --- a/Mage/src/main/java/mage/target/Target.java +++ b/Mage/src/main/java/mage/target/Target.java @@ -52,6 +52,8 @@ public interface Target extends Serializable { boolean canTarget(UUID id, Ability source, Game game); + boolean stillLegalTarget(UUID id, Ability source, Game game); + boolean canTarget(UUID playerId, UUID id, Ability source, Game game); boolean isLegal(Ability source, Game game); @@ -137,5 +139,7 @@ public interface Target extends Serializable { Target withChooseHint(String chooseHint); + String getChooseHint(); + void setEventReporting(boolean shouldReport); } diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java index 20972f39d7..c800d552b5 100644 --- a/Mage/src/main/java/mage/target/TargetImpl.java +++ b/Mage/src/main/java/mage/target/TargetImpl.java @@ -105,14 +105,17 @@ public abstract class TargetImpl implements Target { StringBuilder sb = new StringBuilder(); sb.append("Select ").append(targetName); if (getMaxNumberOfTargets() > 0 && getMaxNumberOfTargets() != Integer.MAX_VALUE) { - sb.append(" (").append(targets.size()).append('/').append(getMaxNumberOfTargets()).append(')'); + sb.append(" (selected ").append(targets.size()).append(" of ").append(getMaxNumberOfTargets()).append(')'); } else { - sb.append(" (").append(targets.size()).append(')'); + sb.append(" (selected ").append(targets.size()).append(')'); } sb.append(suffix); return sb.toString(); } - if (targetName.startsWith("another") || targetName.startsWith("a ") || targetName.startsWith("an ")) { + if (targetName.startsWith("another") + || targetName.startsWith("a ") + || targetName.startsWith("an ") + || targetName.startsWith("any ")) { return "Select " + targetName + suffix; } else if (targetName.startsWith("a") || targetName.startsWith("e") || targetName.startsWith("i") || targetName.startsWith("o") || targetName.startsWith("u")) { return "Select an " + targetName + suffix; @@ -334,7 +337,7 @@ public abstract class TargetImpl implements Target { illegalTargets.add(targetId); continue; } - if (!canTarget(targetId, source, game)) { + if (!stillLegalTarget(targetId, source, game)) { illegalTargets.add(targetId); } } @@ -470,6 +473,11 @@ public abstract class TargetImpl implements Target { return null; } + @Override + public boolean stillLegalTarget(UUID id, Ability source, Game game) { + return canTarget(id, source, game); + } + @Override public void setNotTarget(boolean notTarget) { this.notTarget = notTarget; @@ -552,6 +560,11 @@ public abstract class TargetImpl implements Target { return this; } + @Override + public String getChooseHint() { + return chooseHint; + } + @Override public void setEventReporting(boolean shouldReport) { this.shouldReportEvents = shouldReport; diff --git a/Mage/src/main/java/mage/target/TargetObject.java b/Mage/src/main/java/mage/target/TargetObject.java index 575ad44873..e5efde8b8b 100644 --- a/Mage/src/main/java/mage/target/TargetObject.java +++ b/Mage/src/main/java/mage/target/TargetObject.java @@ -1,13 +1,13 @@ package mage.target; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.constants.Zone; import mage.game.Game; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public abstract class TargetObject extends TargetImpl { @@ -44,7 +44,7 @@ public abstract class TargetObject extends TargetImpl { sb.append(object.getLogName()).append(' '); } } - return sb.toString(); + return sb.toString().trim(); } @Override diff --git a/Mage/src/main/java/mage/target/TargetPlayer.java b/Mage/src/main/java/mage/target/TargetPlayer.java index 7b2d0bdf77..2b7e126551 100644 --- a/Mage/src/main/java/mage/target/TargetPlayer.java +++ b/Mage/src/main/java/mage/target/TargetPlayer.java @@ -1,16 +1,16 @@ package mage.target; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.filter.FilterPlayer; import mage.game.Game; import mage.players.Player; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public class TargetPlayer extends TargetImpl { @@ -51,7 +51,7 @@ public class TargetPlayer extends TargetImpl { * Checks if there are enough {@link Player} that can be chosen. Should only * be used for Ability targets since this checks for protection, shroud etc. * - * @param sourceId - the target event source + * @param sourceId - the target event source * @param sourceControllerId - controller of the target event source * @param game * @return - true if enough valid {@link Player} exist @@ -170,7 +170,7 @@ public class TargetPlayer extends TargetImpl { sb.append("[target missing]"); } } - return sb.toString(); + return sb.toString().trim(); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java b/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java index 5e1ca2be78..d256885d4f 100644 --- a/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java +++ b/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java @@ -1,11 +1,6 @@ - package mage.target.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.ActivatedAbility; import mage.constants.AbilityType; import mage.constants.Zone; import mage.filter.Filter; @@ -16,8 +11,11 @@ import mage.game.stack.StackAbility; import mage.game.stack.StackObject; import mage.target.TargetObject; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author emerald000 */ public class TargetActivatedAbility extends TargetObject { @@ -108,7 +106,6 @@ public class TargetActivatedAbility extends TargetObject { sb.append(object.getRule()).append(' '); } } - sb.append(')'); - return sb.toString(); + return sb.toString().trim() + ")"; } } diff --git a/Mage/src/main/java/mage/target/common/TargetAnyTarget.java b/Mage/src/main/java/mage/target/common/TargetAnyTarget.java index 8204246dc7..f2fa5459b3 100644 --- a/Mage/src/main/java/mage/target/common/TargetAnyTarget.java +++ b/Mage/src/main/java/mage/target/common/TargetAnyTarget.java @@ -1,9 +1,5 @@ package mage.target.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - import mage.MageObject; import mage.abilities.Ability; import mage.constants.Zone; @@ -14,6 +10,10 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetImpl; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** * @author JRHerlehy Created on 4/8/18. */ @@ -96,7 +96,7 @@ public class TargetAnyTarget extends TargetImpl { * be chosen. Should only be used for Ability targets since this checks for * protection, shroud etc. * - * @param sourceId - the target event source + * @param sourceId - the target event source * @param sourceControllerId - controller of the target event source * @param game * @return - true if enough valid {@link Permanent} or {@link Player} exist @@ -252,7 +252,7 @@ public class TargetAnyTarget extends TargetImpl { } } } - return sb.toString(); + return sb.toString().trim(); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java b/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java index 86721777d3..88e0f66386 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java @@ -1,8 +1,5 @@ - - package mage.target.common; -import java.util.UUID; import mage.abilities.Ability; import mage.cards.Card; import mage.constants.Zone; @@ -10,17 +7,17 @@ import mage.filter.FilterCard; import mage.game.Game; import mage.target.TargetCard; +import java.util.UUID; /** - * * @author LevelX2 */ - public class TargetCardInASingleGraveyard extends TargetCard { public TargetCardInASingleGraveyard(int minNumTargets, int maxNumTargets, FilterCard filter) { - super(minNumTargets, maxNumTargets, Zone.GRAVEYARD, filter); + // workaround to add extra message to final ability text + super(minNumTargets, maxNumTargets, Zone.GRAVEYARD, filter.copy().withMessage(filter.getMessage() + " from a single graveyard")); } public TargetCardInASingleGraveyard(final TargetCardInASingleGraveyard target) { @@ -41,9 +38,9 @@ public class TargetCardInASingleGraveyard extends TargetCard { return super.canTarget(id, source, game); } - @Override public TargetCardInASingleGraveyard copy() { return new TargetCardInASingleGraveyard(this); } + } diff --git a/Mage/src/main/java/mage/target/common/TargetCardInGraveyardOrBattlefield.java b/Mage/src/main/java/mage/target/common/TargetCardInGraveyardOrBattlefield.java index c19aebe893..0a4824b4eb 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInGraveyardOrBattlefield.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInGraveyardOrBattlefield.java @@ -1,113 +1,113 @@ -package mage.target.common; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.TargetCard; - -import java.util.Set; -import java.util.UUID; - -/** - * @author LevelX2 - */ -public class TargetCardInGraveyardOrBattlefield extends TargetCard { - - protected final FilterPermanent filterBattlefield; - - public TargetCardInGraveyardOrBattlefield(int minNumTargets, int maxNumTargets, FilterCard filterGraveyard, FilterPermanent filterBattlefield) { - super(minNumTargets, maxNumTargets, Zone.GRAVEYARD, filterGraveyard); // zone for card in graveyard, don't change - this.filterBattlefield = filterBattlefield; - this.targetName = filter.getMessage() - + " in a graveyard " - + (maxNumTargets > 1 ? " and/or " : " or ") - + this.filterBattlefield.getMessage() - + " on the battlefield"; - } - - public TargetCardInGraveyardOrBattlefield(final TargetCardInGraveyardOrBattlefield target) { - super(target); - this.filterBattlefield = target.filterBattlefield; - } - - @Override - public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { - if (!super.canChoose(sourceId, sourceControllerId, game)) { - MageObject targetSource = game.getObject(sourceId); - for (Permanent permanent : game.getBattlefield().getActivePermanents(filterBattlefield, sourceControllerId, game)) { - if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) - && filterBattlefield.match(permanent, sourceId, sourceControllerId, game)) { - return true; - } - } - return false; - } - return true; - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - if (!super.canTarget(id, source, game)) { // in graveyard first - Permanent permanent = game.getPermanent(id); - if (permanent != null) { - return filterBattlefield.match(permanent, game); - } - } - return true; - } - - @Override - public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { - if (!super.canTarget(playerId, id, source, game)) { // in graveyard first - Permanent permanent = game.getPermanent(id); - if (permanent != null) { - return filterBattlefield.match(permanent, source != null ? source.getSourceId() : null, playerId, game); - } - } - return true; - } - - @Override - public boolean canTarget(UUID id, Game game) { - if (!super.canTarget(id, game)) { // in graveyard first - Permanent permanent = game.getPermanent(id); - if (permanent != null) { - return filterBattlefield.match(permanent, game); - } - } - return true; - } - - @Override - public Set possibleTargets(UUID sourceControllerId, Game game) { - Set possibleTargets = super.possibleTargets(sourceControllerId, game); // in graveyard first - for (Permanent permanent : game.getBattlefield().getActivePermanents(filterBattlefield, sourceControllerId, game)) { - if (filterBattlefield.match(permanent, null, sourceControllerId, game)) { - possibleTargets.add(permanent.getId()); - } - } - return possibleTargets; - } - - @Override - public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - Set possibleTargets = super.possibleTargets(sourceId, sourceControllerId, game); // in graveyard first - MageObject targetSource = game.getObject(sourceId); - for (Permanent permanent : game.getBattlefield().getActivePermanents(filterBattlefield, sourceControllerId, sourceId, game)) { - if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filterBattlefield.match(permanent, sourceId, sourceControllerId, game)) { - possibleTargets.add(permanent.getId()); - } - } - return possibleTargets; - } - - @Override - public TargetCardInGraveyardOrBattlefield copy() { - return new TargetCardInGraveyardOrBattlefield(this); - } - -} +package mage.target.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetCard; + +import java.util.Set; +import java.util.UUID; + +/** + * @author LevelX2 + */ +public class TargetCardInGraveyardOrBattlefield extends TargetCard { + + protected final FilterPermanent filterBattlefield; + + public TargetCardInGraveyardOrBattlefield(int minNumTargets, int maxNumTargets, FilterCard filterGraveyard, FilterPermanent filterBattlefield) { + super(minNumTargets, maxNumTargets, Zone.GRAVEYARD, filterGraveyard); // zone for card in graveyard, don't change + this.filterBattlefield = filterBattlefield; + this.targetName = filter.getMessage() + + " in a graveyard " + + (maxNumTargets > 1 ? " and/or " : " or ") + + this.filterBattlefield.getMessage() + + " on the battlefield"; + } + + public TargetCardInGraveyardOrBattlefield(final TargetCardInGraveyardOrBattlefield target) { + super(target); + this.filterBattlefield = target.filterBattlefield; + } + + @Override + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { + if (!super.canChoose(sourceId, sourceControllerId, game)) { + MageObject targetSource = game.getObject(sourceId); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filterBattlefield, sourceControllerId, game)) { + if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) + && filterBattlefield.match(permanent, sourceId, sourceControllerId, game)) { + return true; + } + } + return false; + } + return true; + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + if (!super.canTarget(id, source, game)) { // in graveyard first + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + return filterBattlefield.match(permanent, game); + } + } + return true; + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + if (!super.canTarget(playerId, id, source, game)) { // in graveyard first + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + return filterBattlefield.match(permanent, source != null ? source.getSourceId() : null, playerId, game); + } + } + return true; + } + + @Override + public boolean canTarget(UUID id, Game game) { + if (!super.canTarget(id, game)) { // in graveyard first + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + return filterBattlefield.match(permanent, game); + } + } + return true; + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Game game) { + Set possibleTargets = super.possibleTargets(sourceControllerId, game); // in graveyard first + for (Permanent permanent : game.getBattlefield().getActivePermanents(filterBattlefield, sourceControllerId, game)) { + if (filterBattlefield.match(permanent, null, sourceControllerId, game)) { + possibleTargets.add(permanent.getId()); + } + } + return possibleTargets; + } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Set possibleTargets = super.possibleTargets(sourceId, sourceControllerId, game); // in graveyard first + MageObject targetSource = game.getObject(sourceId); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filterBattlefield, sourceControllerId, sourceId, game)) { + if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filterBattlefield.match(permanent, sourceId, sourceControllerId, game)) { + possibleTargets.add(permanent.getId()); + } + } + return possibleTargets; + } + + @Override + public TargetCardInGraveyardOrBattlefield copy() { + return new TargetCardInGraveyardOrBattlefield(this); + } + +} diff --git a/Mage/src/main/java/mage/target/common/TargetCardInHand.java b/Mage/src/main/java/mage/target/common/TargetCardInHand.java index dd3a6be708..747678ceb5 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInHand.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInHand.java @@ -12,6 +12,7 @@ import mage.target.TargetCard; import java.util.HashSet; import java.util.Set; import java.util.UUID; +import mage.filter.StaticFilters; /** * @author BetaSteward_at_googlemail.com @@ -19,7 +20,7 @@ import java.util.UUID; public class TargetCardInHand extends TargetCard { public TargetCardInHand() { - this(1, 1, new FilterCard()); + this(1, 1, StaticFilters.FILTER_CARD_A); } public TargetCardInHand(FilterCard filter) { @@ -41,8 +42,10 @@ public class TargetCardInHand extends TargetCard { @Override public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { - Card card = game.getPlayer(playerId).getHand().get(id, game); - return card != null && filter.match(card, source != null ? source.getSourceId() : null, playerId, game); + // Has to be a card in the hand of a player in range. We don't know here, from which player's hand so we have to check all possible players + // And because a card in hand is never targeted we can omitt specific targeting related checks + return game.getState().getZone(id) == Zone.HAND + && game.getState().getPlayersInRange(getTargetController() == null ? playerId : getTargetController(), game).contains(game.getOwnerId(id)); } @Override @@ -90,4 +93,10 @@ public class TargetCardInHand extends TargetCard { public String getTargetedName(Game game) { return filter.getMessage(); } + + @Override + public TargetCardInHand withChooseHint(String chooseHint) { + super.withChooseHint(chooseHint); + return this; + } } diff --git a/Mage/src/main/java/mage/target/common/TargetCardInOpponentsGraveyard.java b/Mage/src/main/java/mage/target/common/TargetCardInOpponentsGraveyard.java index 4ebbfa167f..84d34020f8 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInOpponentsGraveyard.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInOpponentsGraveyard.java @@ -1,8 +1,11 @@ package mage.target.common; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.cards.Card; +import mage.cards.Cards; import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; @@ -33,6 +36,23 @@ public class TargetCardInOpponentsGraveyard extends TargetCard { this.allFromOneOpponent = target.allFromOneOpponent; } + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + Card card = game.getCard(id); + if (card != null && zone.match(game.getState().getZone(id))) { + if (game.getPlayer(source.getControllerId()).hasOpponent(card.getOwnerId(), game)) { + if (allFromOneOpponent && !targets.isEmpty()) { + Card firstCard = game.getCard(targets.keySet().iterator().next()); + if (firstCard != null && !card.isOwnedBy(firstCard.getOwnerId())) { + return false; + } + } + return filter.match(card, source.getId(), playerId, game); + } + } + return false; + } + @Override public boolean canTarget(UUID id, Ability source, Game game) { Card card = game.getCard(id); @@ -69,7 +89,14 @@ public class TargetCardInOpponentsGraveyard extends TargetCard { if (getNumberOfTargets() == 0) { // if 0 target is valid, the canChoose is always true return true; } + Player sourceController = game.getPlayer(sourceControllerId); for (UUID playerId: game.getState().getPlayersInRange(sourceControllerId, game)) { + if (!sourceController.hasOpponent(playerId, game)) { + continue; + } + if (this.allFromOneOpponent) { + possibleTargets = 0; + } if (!playerId.equals(sourceControllerId)) { Player player = game.getPlayer(playerId); if (player != null) { @@ -86,6 +113,33 @@ public class TargetCardInOpponentsGraveyard extends TargetCard { } return false; } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Set possibleTargets = new HashSet<>(); + Player sourceController = game.getPlayer(sourceControllerId); + for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { + if (!sourceController.hasOpponent(playerId, game)) { + continue; + } + Player player = game.getPlayer(playerId); + if (player != null) { + Set targetsInThisGraveyeard = new HashSet<>(); + for (Card card : player.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) { + if (sourceId == null || isNotTarget() || !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, sourceControllerId))) { + targetsInThisGraveyeard.add(card.getId()); + } + } + // if there is not enough possible targets, the can't be any + if (this.allFromOneOpponent && targetsInThisGraveyeard.size() < this.minNumberOfTargets) { + continue; + } + possibleTargets.addAll(targetsInThisGraveyeard); + } + } + return possibleTargets; + } + @Override public TargetCardInOpponentsGraveyard copy() { return new TargetCardInOpponentsGraveyard(this); diff --git a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java index 3485646d4d..39e044d449 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java @@ -9,7 +9,7 @@ import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; public class TargetCreatureOrPlaneswalkerAmount extends TargetPermanentAmount { private static final FilterCreatureOrPlaneswalkerPermanent defaultFilter - = new FilterCreatureOrPlaneswalkerPermanent(); + = new FilterCreatureOrPlaneswalkerPermanent("target creatures or planeswalkers"); public TargetCreatureOrPlaneswalkerAmount(int amount) { super(amount, defaultFilter); diff --git a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java index 2c74ef554a..a8ef0efe33 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java +++ b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java @@ -1,9 +1,8 @@ - package mage.target.common; -import mage.constants.Zone; import mage.MageObject; import mage.abilities.Ability; +import mage.constants.Zone; import mage.filter.Filter; import mage.filter.common.FilterCreatureOrPlayer; import mage.game.Game; @@ -16,7 +15,6 @@ import java.util.Set; import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public class TargetCreatureOrPlayer extends TargetImpl { @@ -98,7 +96,7 @@ public class TargetCreatureOrPlayer extends TargetImpl { * be chosen. Should only be used for Ability targets since this checks for * protection, shroud etc. * - * @param sourceId - the target event source + * @param sourceId - the target event source * @param sourceControllerId - controller of the target event source * @param game * @return - true if enough valid {@link Permanent} or {@link Player} exist @@ -211,7 +209,7 @@ public class TargetCreatureOrPlayer extends TargetImpl { } } } - return sb.toString(); + return sb.toString().trim(); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentSameController.java b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentSameController.java index 642440373a..0bc13bb396 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentSameController.java +++ b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentSameController.java @@ -1,50 +1,50 @@ - -package mage.target.common; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author LevelX2 - */ -public class TargetCreaturePermanentSameController extends TargetCreaturePermanent { - - public TargetCreaturePermanentSameController(int minNumTargets, int maxNumTargets, FilterCreaturePermanent filter, boolean notTarget) { - super(minNumTargets, maxNumTargets, filter, notTarget); - } - - public TargetCreaturePermanentSameController(final TargetCreaturePermanentSameController target) { - super(target); - } - - @Override - public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { - if (super.canTarget(controllerId, id, source, game)) { - Permanent firstTargetPermanent = game.getPermanent(id); - if (firstTargetPermanent != null) { - for (Object object : getTargets()) { - UUID targetId = (UUID) object; - Permanent targetPermanent = game.getPermanent(targetId); - if (targetPermanent != null) { - if (!firstTargetPermanent.getId().equals(targetPermanent.getId())) { - if (!firstTargetPermanent.isControlledBy(targetPermanent.getOwnerId())) { - return false; - } - } - } - } - return true; - } - } - return false; - } - - @Override - public TargetCreaturePermanentSameController copy() { - return new TargetCreaturePermanentSameController(this); - } -} + +package mage.target.common; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class TargetCreaturePermanentSameController extends TargetCreaturePermanent { + + public TargetCreaturePermanentSameController(int minNumTargets, int maxNumTargets, FilterCreaturePermanent filter, boolean notTarget) { + super(minNumTargets, maxNumTargets, filter, notTarget); + } + + public TargetCreaturePermanentSameController(final TargetCreaturePermanentSameController target) { + super(target); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + if (super.canTarget(controllerId, id, source, game)) { + Permanent firstTargetPermanent = game.getPermanent(id); + if (firstTargetPermanent != null) { + for (Object object : getTargets()) { + UUID targetId = (UUID) object; + Permanent targetPermanent = game.getPermanent(targetId); + if (targetPermanent != null) { + if (!firstTargetPermanent.getId().equals(targetPermanent.getId())) { + if (!firstTargetPermanent.isControlledBy(targetPermanent.getOwnerId())) { + return false; + } + } + } + } + return true; + } + } + return false; + } + + @Override + public TargetCreaturePermanentSameController copy() { + return new TargetCreaturePermanentSameController(this); + } +} diff --git a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java index 4bbc69cabc..7807cb601c 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java +++ b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java @@ -1,50 +1,50 @@ - -package mage.target.common; - -import mage.abilities.Ability; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; - -import java.util.UUID; - -/** - * - * @author LevelX2 - */ -public class TargetCreaturePermanentWithDifferentTypes extends TargetCreaturePermanent { - - public TargetCreaturePermanentWithDifferentTypes(int minNumTargets, int maxNumTargets, FilterCreaturePermanent filter, boolean notTarget) { - super(minNumTargets, maxNumTargets, filter, notTarget); - } - - public TargetCreaturePermanentWithDifferentTypes(final TargetCreaturePermanentWithDifferentTypes target) { - super(target); - } - - @Override - public TargetCreaturePermanentWithDifferentTypes copy() { - return new TargetCreaturePermanentWithDifferentTypes(this); - } - - @Override - public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { - if (super.canTarget(controllerId, id, source, game)) { - Permanent creature = game.getPermanent(id); - if (creature != null) { - for (Object object : getTargets()) { - UUID targetId = (UUID) object; - Permanent selectedCreature = game.getPermanent(targetId); - if (selectedCreature != null - && !creature.getId().equals(selectedCreature.getId())) { - if (creature.shareSubtypes(selectedCreature, game)) { - return false; - } - } - } - return true; - } - } - return false; - } -} + +package mage.target.common; + +import mage.abilities.Ability; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class TargetCreaturePermanentWithDifferentTypes extends TargetCreaturePermanent { + + public TargetCreaturePermanentWithDifferentTypes(int minNumTargets, int maxNumTargets, FilterCreaturePermanent filter, boolean notTarget) { + super(minNumTargets, maxNumTargets, filter, notTarget); + } + + public TargetCreaturePermanentWithDifferentTypes(final TargetCreaturePermanentWithDifferentTypes target) { + super(target); + } + + @Override + public TargetCreaturePermanentWithDifferentTypes copy() { + return new TargetCreaturePermanentWithDifferentTypes(this); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + if (super.canTarget(controllerId, id, source, game)) { + Permanent creature = game.getPermanent(id); + if (creature != null) { + for (Object object : getTargets()) { + UUID targetId = (UUID) object; + Permanent selectedCreature = game.getPermanent(targetId); + if (selectedCreature != null + && !creature.getId().equals(selectedCreature.getId())) { + if (creature.shareSubtypes(selectedCreature, game)) { + return false; + } + } + } + return true; + } + } + return false; + } +} diff --git a/Mage/src/main/java/mage/target/common/TargetDefender.java b/Mage/src/main/java/mage/target/common/TargetDefender.java index 863c419b28..d83839e806 100644 --- a/Mage/src/main/java/mage/target/common/TargetDefender.java +++ b/Mage/src/main/java/mage/target/common/TargetDefender.java @@ -1,9 +1,5 @@ - package mage.target.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.constants.Zone; @@ -15,8 +11,11 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetImpl; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public class TargetDefender extends TargetImpl { @@ -68,7 +67,7 @@ public class TargetDefender extends TargetImpl { } } for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) { - if ((notTarget + if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(permanent, game)) { count++; @@ -85,7 +84,7 @@ public class TargetDefender extends TargetImpl { int count = 0; for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null + if (player != null && filter.match(player, game)) { count++; if (count >= this.minNumberOfTargets) { @@ -111,14 +110,14 @@ public class TargetDefender extends TargetImpl { for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); if (player != null - && (notTarget + && (notTarget || player.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(player, game)) { possibleTargets.add(playerId); } } for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) { - if ((notTarget + if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(permanent, game)) { possibleTargets.add(permanent.getId()); @@ -132,7 +131,7 @@ public class TargetDefender extends TargetImpl { Set possibleTargets = new HashSet<>(); for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null + if (player != null && filter.match(player, game)) { possibleTargets.add(playerId); } @@ -157,7 +156,7 @@ public class TargetDefender extends TargetImpl { sb.append(player.getLogName()).append(' '); } } - return sb.toString(); + return sb.toString().trim(); } @Override @@ -167,7 +166,7 @@ public class TargetDefender extends TargetImpl { return filter.match(player, game); } Permanent permanent = game.getPermanent(id); - return permanent != null + return permanent != null && filter.match(permanent, game); } @@ -176,7 +175,7 @@ public class TargetDefender extends TargetImpl { Player player = game.getPlayer(id); MageObject targetSource = game.getObject(attackerId); if (player != null) { - return (notTarget + return (notTarget || player.canBeTargetedBy(targetSource, (source == null ? null : source.getControllerId()), game)) && filter.match(player, game); } @@ -187,7 +186,7 @@ public class TargetDefender extends TargetImpl { if (source != null) { controllerId = source.getControllerId(); } - return (notTarget + return (notTarget || permanent.canBeTargetedBy(targetSource, controllerId, game)) && filter.match(permanent, game); } diff --git a/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java b/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java index c13d1b9697..2e555a149d 100644 --- a/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java @@ -1,18 +1,16 @@ - - package mage.target.common; +import mage.filter.StaticFilters; import mage.filter.common.FilterEnchantmentPermanent; import mage.target.TargetPermanent; /** - * * @author LevelX2 */ public class TargetEnchantmentPermanent extends TargetPermanent { - + public TargetEnchantmentPermanent() { - this(1, 1, new FilterEnchantmentPermanent(), false); + this(1); } public TargetEnchantmentPermanent(FilterEnchantmentPermanent filter) { @@ -20,7 +18,11 @@ public class TargetEnchantmentPermanent extends TargetPermanent { } public TargetEnchantmentPermanent(int numTargets) { - this(numTargets, numTargets, new FilterEnchantmentPermanent(), false); + this(numTargets, numTargets); + } + + public TargetEnchantmentPermanent(int minNumTargets, int maxNumTargets) { + this(minNumTargets, maxNumTargets, StaticFilters.FILTER_ENCHANTMENT_PERMANENT, false); } public TargetEnchantmentPermanent(int minNumTargets, int maxNumTargets, FilterEnchantmentPermanent filter, boolean notTarget) { diff --git a/Mage/src/main/java/mage/target/common/TargetOpponentsCreaturePermanent.java b/Mage/src/main/java/mage/target/common/TargetOpponentsCreaturePermanent.java index 9a17a690a3..16dafb4358 100644 --- a/Mage/src/main/java/mage/target/common/TargetOpponentsCreaturePermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetOpponentsCreaturePermanent.java @@ -1,45 +1,45 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.target.common; - -import mage.filter.common.FilterOpponentsCreaturePermanent; - -/** - * - * @author Styxo - */ -public class TargetOpponentsCreaturePermanent extends TargetCreaturePermanent { - - public TargetOpponentsCreaturePermanent() { - this(1, 1, new FilterOpponentsCreaturePermanent(), false); - } - - public TargetOpponentsCreaturePermanent(int numTargets) { - this(numTargets, numTargets, new FilterOpponentsCreaturePermanent(), false); - } - - public TargetOpponentsCreaturePermanent(int minNumTargets, int maxNumTargets) { - this(minNumTargets, maxNumTargets, new FilterOpponentsCreaturePermanent(), false); - } - - public TargetOpponentsCreaturePermanent(FilterOpponentsCreaturePermanent filter) { - super(1, 1, filter, false); - } - - public TargetOpponentsCreaturePermanent(int minNumTargets, int maxNumTargets, FilterOpponentsCreaturePermanent filter, boolean notTarget) { - super(minNumTargets, maxNumTargets, filter, notTarget); - this.targetName = filter.getMessage(); - } - - public TargetOpponentsCreaturePermanent(final TargetOpponentsCreaturePermanent target) { - super(target); - } - - @Override - public TargetOpponentsCreaturePermanent copy() { - return new TargetOpponentsCreaturePermanent(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.target.common; + +import mage.filter.common.FilterOpponentsCreaturePermanent; + +/** + * + * @author Styxo + */ +public class TargetOpponentsCreaturePermanent extends TargetCreaturePermanent { + + public TargetOpponentsCreaturePermanent() { + this(1, 1, new FilterOpponentsCreaturePermanent(), false); + } + + public TargetOpponentsCreaturePermanent(int numTargets) { + this(numTargets, numTargets, new FilterOpponentsCreaturePermanent(), false); + } + + public TargetOpponentsCreaturePermanent(int minNumTargets, int maxNumTargets) { + this(minNumTargets, maxNumTargets, new FilterOpponentsCreaturePermanent(), false); + } + + public TargetOpponentsCreaturePermanent(FilterOpponentsCreaturePermanent filter) { + super(1, 1, filter, false); + } + + public TargetOpponentsCreaturePermanent(int minNumTargets, int maxNumTargets, FilterOpponentsCreaturePermanent filter, boolean notTarget) { + super(minNumTargets, maxNumTargets, filter, notTarget); + this.targetName = filter.getMessage(); + } + + public TargetOpponentsCreaturePermanent(final TargetOpponentsCreaturePermanent target) { + super(target); + } + + @Override + public TargetOpponentsCreaturePermanent copy() { + return new TargetOpponentsCreaturePermanent(this); + } +} diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java b/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java index ecfd696f5c..2556df419d 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java @@ -78,13 +78,13 @@ public abstract class TargetPermanentAmount extends TargetAmount { @Override public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { - return game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game).size() + return game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game).size() >= this.minNumberOfTargets; } @Override public boolean canChoose(UUID sourceControllerId, Game game) { - return game.getBattlefield().getActivePermanents(filter, sourceControllerId, game).size() + return game.getBattlefield().getActivePermanents(filter, sourceControllerId, game).size() >= this.minNumberOfTargets; } @@ -127,10 +127,10 @@ public abstract class TargetPermanentAmount extends TargetAmount { getTargets().forEach((targetId) -> { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { - sb.append(permanent.getLogName()).append('(').append(getTargetAmount(targetId)).append(") "); + sb.append(permanent.getLogName()).append(" (").append(getTargetAmount(targetId)).append(") "); } }); - return sb.toString(); + return sb.toString().trim(); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java index cee4973c4f..6a99354f10 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java @@ -220,7 +220,7 @@ public class TargetPermanentOrPlayer extends TargetImpl { sb.append(player.getLogName()).append(' '); } } - return sb.toString(); + return sb.toString().trim(); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java index adc75fae2c..3971c1aa5c 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java @@ -211,12 +211,12 @@ public abstract class TargetPermanentOrPlayerAmount extends TargetAmount { for (UUID targetId : getTargets()) { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { - sb.append(permanent.getLogName()).append('(').append(getTargetAmount(targetId)).append(") "); + sb.append(permanent.getLogName()).append(" (").append(getTargetAmount(targetId)).append(") "); } else { Player player = game.getPlayer(targetId); - sb.append(player.getLogName()).append('(').append(getTargetAmount(targetId)).append(") "); + sb.append(player.getLogName()).append(" (").append(getTargetAmount(targetId)).append(") "); } } - return sb.toString(); + return sb.toString().trim(); } } diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java index 1575dcc4ba..50e37b7300 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java @@ -29,9 +29,6 @@ */ package mage.target.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.cards.Card; @@ -42,18 +39,21 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetImpl; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author emerald000 */ public class TargetPermanentOrSuspendedCard extends TargetImpl { - + protected final FilterPermanentOrSuspendedCard filter; - + public TargetPermanentOrSuspendedCard() { this(new FilterPermanentOrSuspendedCard(), false); } - + public TargetPermanentOrSuspendedCard(FilterPermanentOrSuspendedCard filter, boolean notTarget) { super(notTarget); this.filter = filter; @@ -62,17 +62,17 @@ public class TargetPermanentOrSuspendedCard extends TargetImpl { this.minNumberOfTargets = 1; this.maxNumberOfTargets = 1; } - + public TargetPermanentOrSuspendedCard(final TargetPermanentOrSuspendedCard target) { super(target); this.filter = target.filter.copy(); } - + @Override public Filter getFilter() { return this.filter; } - + @Override public TargetPermanentOrSuspendedCard copy() { return new TargetPermanentOrSuspendedCard(this); @@ -166,6 +166,6 @@ public class TargetPermanentOrSuspendedCard extends TargetImpl { } } } - return sb.toString(); + return sb.toString().trim(); } } diff --git a/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java b/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java index f2a846ba37..c8b7749875 100644 --- a/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java @@ -29,9 +29,6 @@ */ package mage.target.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.constants.Zone; @@ -45,8 +42,11 @@ import mage.game.stack.StackObject; import mage.target.TargetImpl; import mage.util.GameLog; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX */ public class TargetSpellOrPermanent extends TargetImpl { @@ -135,7 +135,7 @@ public class TargetSpellOrPermanent extends TargetImpl { * {@link mage.game.stack.Spell} that can be chosen. Should only be used for * Ability targets since this checks for protection, shroud etc. * - * @param sourceId - the target event source + * @param sourceId - the target event source * @param sourceControllerId - controller of the target event source * @param game * @return - true if enough valid {@link mage.game.permanent.Permanent} or @@ -257,7 +257,7 @@ public class TargetSpellOrPermanent extends TargetImpl { } } } - return sb.toString(); + return sb.toString().trim(); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetTappedPermanentAsYouCast.java b/Mage/src/main/java/mage/target/common/TargetTappedPermanentAsYouCast.java new file mode 100644 index 0000000000..50e7540323 --- /dev/null +++ b/Mage/src/main/java/mage/target/common/TargetTappedPermanentAsYouCast.java @@ -0,0 +1,62 @@ +package mage.target.common; + +import mage.abilities.Ability; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +public class TargetTappedPermanentAsYouCast extends TargetPermanent { + + public TargetTappedPermanentAsYouCast() {} + + public TargetTappedPermanentAsYouCast(FilterPermanent filter) { + this.filter = filter; + this.targetName = filter.getMessage(); + } + + private TargetTappedPermanentAsYouCast(TargetTappedPermanentAsYouCast target) { + super(target); + } + + @Override + public TargetTappedPermanentAsYouCast copy() { + return new TargetTappedPermanentAsYouCast(this); + } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + return game.getBattlefield().getAllActivePermanents(getFilter(), game).stream() + .filter(Permanent::isTapped) + .map(Permanent::getId) + .collect(Collectors.toSet()); + } + + @Override + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { + return game.getBattlefield().getAllActivePermanents(getFilter(), game).stream() + .anyMatch(Permanent::isTapped); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + if (super.canTarget(controllerId, id, source, game)) { + Permanent permanent = game.getPermanent(id); + return permanent != null && permanent.isTapped(); + } + return false; + } + + // See ruling: https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/253345-dream-leash + @Override + public boolean stillLegalTarget(UUID id, Ability source, Game game) { + Permanent permanent = game.getPermanent(id); + return permanent != null + && getFilter().match(permanent, game) + && super.canTarget(id, game); // check everything but leave out the tapped requirement + } +} diff --git a/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java index b35a440dfa..d3c535e7fb 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java @@ -5,8 +5,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.cards.Card; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -94,4 +96,25 @@ public class FirstTargetPointer implements TargetPointer { } + @Override + public Permanent getFirstTargetPermanentOrLKI(Game game, Ability source) { + UUID targetId = source.getFirstTarget(); + Permanent permanent; + if (zoneChangeCounter.containsKey(targetId)) { + permanent = game.getPermanent(targetId); + if (permanent != null && permanent.getZoneChangeCounter(game) == zoneChangeCounter.get(targetId)) { + return permanent; + } + MageObject mageObject = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD, zoneChangeCounter.get(targetId)); + if (mageObject instanceof Permanent) { + return (Permanent) mageObject; + } + } else { + permanent = game.getPermanent(targetId); + if (permanent == null) { + permanent = (Permanent) game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); + } + } + return permanent; + } } diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java index 137303c8c5..b6c7871b92 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java @@ -1,5 +1,10 @@ package mage.target.targetpointer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; import mage.cards.Card; @@ -7,11 +12,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - public class FixedTarget implements TargetPointer { private final UUID targetId; @@ -27,6 +27,13 @@ public class FixedTarget implements TargetPointer { this(mor.getSourceId(), mor.getZoneChangeCounter()); } + /** + * Target counter is immediatly initialised with current zoneChangeCounter + * value from the GameState Sets fixed the currect zone chnage counter + * + * @param card used to get the objectId + * @param game + */ public FixedTarget(Card card, Game game) { this.targetId = card.getId(); this.zoneChangeCounter = card.getZoneChangeCounter(game); @@ -123,18 +130,23 @@ public class FixedTarget implements TargetPointer { return zoneChangeCounter; } - public Permanent getTargetedPermanentOrLKIBattlefield(Game game) { - Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); - if (permanent != null && permanent.getZoneChangeCounter(game) != zoneChangeCounter) { - permanent = (Permanent) game.getLastKnownInformation(targetId, Zone.BATTLEFIELD, zoneChangeCounter); - } - return permanent; - } - @Override public FixedTarget getFixedTarget(Game game, Ability source) { init(game, source); return this; } + @Override + public Permanent getFirstTargetPermanentOrLKI(Game game, Ability source) { + Permanent permanent = game.getPermanent(targetId); + if (permanent != null && permanent.getZoneChangeCounter(game) == zoneChangeCounter) { + return permanent; + } + MageObject mageObject = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD, zoneChangeCounter); + if (mageObject instanceof Permanent) { + return (Permanent) mageObject; + } + return null; + } + } diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java index 620c796d4d..ccef9d4d40 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java @@ -5,17 +5,18 @@ */ package mage.target.targetpointer; -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.cards.Card; -import mage.cards.Cards; -import mage.game.Game; -import mage.game.permanent.Permanent; - import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.cards.Card; +import mage.cards.Cards; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; /** * @author LevelX2 @@ -116,4 +117,34 @@ public class FixedTargets implements TargetPointer { } return null; } + + @Override + public Permanent getFirstTargetPermanentOrLKI(Game game, Ability source) { + UUID targetId = null; + int zoneChangeCounter = Integer.MIN_VALUE; + if (!targets.isEmpty()) { + MageObjectReference mor = targets.get(0); + targetId = mor.getSourceId(); + zoneChangeCounter = mor.getZoneChangeCounter(); + } else if (!targetsNotInitialized.isEmpty()) { + targetId = targetsNotInitialized.get(0); + } + if (targetId != null) { + Permanent permanent = game.getPermanent(targetId); + if (permanent != null + && (zoneChangeCounter == Integer.MIN_VALUE || permanent.getZoneChangeCounter(game) == zoneChangeCounter)) { + return permanent; + } + MageObject mageObject; + if (zoneChangeCounter == Integer.MIN_VALUE) { + mageObject = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); + } else { + mageObject = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD, zoneChangeCounter); + } + if (mageObject instanceof Permanent) { + return (Permanent) mageObject; + } + } + return null; + } } diff --git a/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java index a1dcc24f1c..bbb54aa65e 100644 --- a/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java @@ -1,9 +1,12 @@ package mage.target.targetpointer; import java.util.*; +import mage.MageObject; import mage.abilities.Ability; import mage.cards.Card; +import mage.constants.Zone; import mage.game.Game; +import mage.game.permanent.Permanent; public class SecondTargetPointer implements TargetPointer { @@ -81,4 +84,30 @@ public class SecondTargetPointer implements TargetPointer { } return null; } + + @Override + public Permanent getFirstTargetPermanentOrLKI(Game game, Ability source) { + if (source.getTargets().size() > 1) { + Permanent permanent; + UUID targetId = source.getTargets().get(1).getFirstTarget(); + if (zoneChangeCounter.containsKey(targetId)) { + permanent = game.getPermanent(targetId); + if (permanent != null && permanent.getZoneChangeCounter(game) == zoneChangeCounter.get(targetId)) { + return permanent; + } + MageObject mageObject = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD, zoneChangeCounter.get(targetId)); + if (mageObject instanceof Permanent) { + return (Permanent) mageObject; + } + + } else { + permanent = game.getPermanent(targetId); + if (permanent == null) { + permanent = (Permanent) game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); + } + } + return permanent; + } + return null; + } } diff --git a/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java index 6dd021c9ac..c55e6a290b 100644 --- a/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.game.Game; +import mage.game.permanent.Permanent; public interface TargetPointer extends Serializable { @@ -17,4 +18,20 @@ public interface TargetPointer extends Serializable { TargetPointer copy(); FixedTarget getFixedTarget(Game game, Ability source); + + /** + * Retrieves the permanent according the first targetId and + * zoneChangeCounter if set.
+ * Retrieves also the LKI if the permanent is no longer onto the + * battlefield.
+ * This should not be used for true targeted objects, because they are not + * retrieved using LKI (608.2b).
+ * This is only used if the the target pointer is used to transfer + * information about a related permanent (often from triggered abilities). + * + * @param game + * @param source + * @return permanent + */ + Permanent getFirstTargetPermanentOrLKI(Game game, Ability source); } diff --git a/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java index 77d1293adb..31639c3dbb 100644 --- a/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java @@ -1,98 +1,127 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.target.targetpointer; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import mage.abilities.Ability; -import mage.cards.Card; -import mage.game.Game; - -/** - * - * @author Ludwig.Hirth - */ -public class ThirdTargetPointer implements TargetPointer { - - private Map zoneChangeCounter = new HashMap<>(); - - public static ThirdTargetPointer getInstance() { - return new ThirdTargetPointer(); - } - - public ThirdTargetPointer() { - } - - public ThirdTargetPointer(ThirdTargetPointer targetPointer) { - this.zoneChangeCounter = new HashMap<>(); - for (Map.Entry entry : targetPointer.zoneChangeCounter.entrySet()) { - this.zoneChangeCounter.put(entry.getKey(), entry.getValue()); - } - } - - @Override - public void init(Game game, Ability source) { - if (source.getTargets().size() > 2) { - for (UUID target : source.getTargets().get(2).getTargets()) { - Card card = game.getCard(target); - if (card != null) { - this.zoneChangeCounter.put(target, card.getZoneChangeCounter(game)); - } - } - } - } - - @Override - public List getTargets(Game game, Ability source) { - List target = new ArrayList<>(); - if (source.getTargets().size() > 2) { - for (UUID targetId : source.getTargets().get(2).getTargets()) { - Card card = game.getCard(targetId); - if (card != null && zoneChangeCounter.containsKey(targetId) - && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { - continue; - } - target.add(targetId); - } - } - return target; - } - - @Override - public UUID getFirst(Game game, Ability source) { - if (source.getTargets().size() > 2) { - UUID targetId = source.getTargets().get(2).getFirstTarget(); - if (zoneChangeCounter.containsKey(targetId)) { - Card card = game.getCard(targetId); - if (card != null && zoneChangeCounter.containsKey(targetId) - && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { - return null; - } - } - return targetId; - } - return null; - } - - @Override - public TargetPointer copy() { - return new ThirdTargetPointer(this); - } - - @Override - public FixedTarget getFixedTarget(Game game, Ability source) { - this.init(game, source); - UUID firstId = getFirst(game, source); - if (firstId != null) { - return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); - } - return null; - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.target.targetpointer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.cards.Card; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class ThirdTargetPointer implements TargetPointer { + + private Map zoneChangeCounter = new HashMap<>(); + + public static ThirdTargetPointer getInstance() { + return new ThirdTargetPointer(); + } + + public ThirdTargetPointer() { + } + + public ThirdTargetPointer(ThirdTargetPointer targetPointer) { + this.zoneChangeCounter = new HashMap<>(); + for (Map.Entry entry : targetPointer.zoneChangeCounter.entrySet()) { + this.zoneChangeCounter.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void init(Game game, Ability source) { + if (source.getTargets().size() > 2) { + for (UUID target : source.getTargets().get(2).getTargets()) { + Card card = game.getCard(target); + if (card != null) { + this.zoneChangeCounter.put(target, card.getZoneChangeCounter(game)); + } + } + } + } + + @Override + public List getTargets(Game game, Ability source) { + List target = new ArrayList<>(); + if (source.getTargets().size() > 2) { + for (UUID targetId : source.getTargets().get(2).getTargets()) { + Card card = game.getCard(targetId); + if (card != null && zoneChangeCounter.containsKey(targetId) + && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + continue; + } + target.add(targetId); + } + } + return target; + } + + @Override + public UUID getFirst(Game game, Ability source) { + if (source.getTargets().size() > 2) { + UUID targetId = source.getTargets().get(2).getFirstTarget(); + if (zoneChangeCounter.containsKey(targetId)) { + Card card = game.getCard(targetId); + if (card != null && zoneChangeCounter.containsKey(targetId) + && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + return null; + } + } + return targetId; + } + return null; + } + + @Override + public TargetPointer copy() { + return new ThirdTargetPointer(this); + } + + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + this.init(game, source); + UUID firstId = getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + return null; + + } + + @Override + public Permanent getFirstTargetPermanentOrLKI(Game game, Ability source) { + if (source.getTargets().size() > 2) { + Permanent permanent; + UUID targetId = source.getTargets().get(2).getFirstTarget(); + if (zoneChangeCounter.containsKey(targetId)) { + permanent = game.getPermanent(targetId); + if (permanent != null && permanent.getZoneChangeCounter(game) == zoneChangeCounter.get(targetId)) { + return permanent; + } + MageObject mageObject = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD, zoneChangeCounter.get(targetId)); + if (mageObject instanceof Permanent) { + return (Permanent) mageObject; + } + + } else { + permanent = game.getPermanent(targetId); + if (permanent == null) { + permanent = (Permanent) game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); + } + } + return permanent; + } + return null; + } +} diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 64bc15c843..47680901cf 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -2,30 +2,35 @@ package mage.util; import mage.MageObject; import mage.Mana; +import mage.abilities.Abilities; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.*; +import mage.abilities.effects.ContinuousEffect; import mage.cards.Card; -import mage.constants.ColoredManaSymbol; -import mage.constants.EmptyNames; -import mage.constants.ManaType; +import mage.cards.MeldCard; +import mage.constants.*; import mage.filter.Filter; +import mage.filter.predicate.mageobject.NamePredicate; import mage.game.CardState; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentCard; +import mage.game.permanent.PermanentMeld; import mage.game.permanent.token.Token; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.Target; import mage.util.functions.CopyTokenFunction; -import org.junit.Assert; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; +import java.util.*; +import java.util.stream.Collectors; /** * @author nantuko @@ -658,6 +663,14 @@ public final class CardUtil { return object1 != null && object2 != null && haveSameNames(object1.getName(), object2.getName()); } + public static boolean haveSameNames(MageObject object, String needName, Game game) { + return containsName(object, needName, game); + } + + public static boolean containsName(MageObject object, String name, Game game) { + return new NamePredicate(name).apply(object, game); + } + public static boolean haveEmptyName(String name) { return name == null || name.isEmpty() || name.equals(EmptyNames.FACE_DOWN_CREATURE.toString()) || name.equals(EmptyNames.FACE_DOWN_TOKEN.toString()); } @@ -747,4 +760,119 @@ public final class CardUtil { throw new IllegalArgumentException("Wrong mana type " + manaType); } } + + public static String getBoostCountAsStr(int power, int toughness) { + // sign fix for zero values + // -1/+0 must be -1/-0 + // +0/-1 must be -0/-1 + String signedP = String.format("%1$+d", power); + String signedT = String.format("%1$+d", toughness); + if (signedP.equals("+0") && signedT.startsWith("-")) signedP = "-0"; + if (signedT.equals("+0") && signedP.startsWith("-")) signedT = "-0"; + + return signedP + "/" + signedT; + } + + public static boolean isSpliceAbility(Ability ability, Game game) { + if (ability instanceof SpellAbility) { + return ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLICE; + } + return false; + } + + public static boolean isFusedPartAbility(Ability ability, Game game) { + // TODO: is works fine with copies of spells on stack? + if (ability instanceof SpellAbility) { + Spell mainSpell = game.getSpell(ability.getId()); + if (mainSpell == null) { + return true; + } else { + SpellAbility mainSpellAbility = mainSpell.getSpellAbility(); + return mainSpellAbility.getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED + && !ability.equals(mainSpellAbility); + } + } + return false; + } + + public static Abilities getAbilities(MageObject object, Game game) { + if (object instanceof Card) { + return ((Card) object).getAbilities(game); + } else { + return object.getAbilities(); + } + } + + public static String getTextWithFirstCharUpperCase(String text) { + if (text != null && text.length() >= 1) { + return Character.toUpperCase(text.charAt(0)) + text.substring(1); + } else { + return text; + } + } + + public static Set getAllSelectedTargets(Ability ability, Game game) { + return ability.getModes().getSelectedModes() + .stream() + .map(ability.getModes()::get) + .map(Mode::getTargets) + .flatMap(Collection::stream) + .map(Target::getTargets) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } + + public static Set getAllPossibleTargets(Ability ability, Game game) { + return ability.getModes().values() + .stream() + .map(Mode::getTargets) + .flatMap(Collection::stream) + .map(t -> t.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } + + /** + * Put card to battlefield without resolve + * + * @param game + * @param card + * @param player + */ + public static void putCardOntoBattlefieldWithEffects(Game game, Card card, Player player) { + // same logic as ZonesHandler->maybeRemoveFromSourceZone + + // prepare card and permanent + card.setZone(Zone.BATTLEFIELD, game); + card.setOwnerId(player.getId()); + PermanentCard permanent; + if (card instanceof MeldCard) { + permanent = new PermanentMeld(card, player.getId(), game); + } else { + permanent = new PermanentCard(card, player.getId(), game); + } + + // put onto battlefield with possible counters + game.getPermanentsEntering().put(permanent.getId(), permanent); + card.checkForCountersToAdd(permanent, game); + permanent.entersBattlefield(permanent.getId(), game, Zone.OUTSIDE, false); + game.addPermanent(permanent, game.getState().getNextPermanentOrderNumber()); + game.getPermanentsEntering().remove(permanent.getId()); + + // workaround for special tapped status from test framework's command (addCard) + if (card instanceof PermanentCard && ((PermanentCard) card).isTapped()) { + permanent.setTapped(true); + } + + // remove sickness + permanent.removeSummoningSickness(); + + // init effects on static abilities (init continuous effects; warning, game state contains copy) + for (ContinuousEffect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) { + Optional ability = game.getState().getContinuousEffects().getLayeredEffectAbilities(effect).stream().findFirst(); + if (ability.isPresent() && permanent.getId().equals(ability.get().getSourceId())) { + effect.init(ability.get(), game, player.getId()); + } + } + } } diff --git a/Mage/src/main/java/mage/util/ManaUtil.java b/Mage/src/main/java/mage/util/ManaUtil.java index d153fe9315..1d606b33cd 100644 --- a/Mage/src/main/java/mage/util/ManaUtil.java +++ b/Mage/src/main/java/mage/util/ManaUtil.java @@ -5,6 +5,7 @@ import mage.Mana; import mage.ManaSymbol; import mage.abilities.Ability; import mage.abilities.costs.Cost; +import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.*; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.ManacostVariableValue; @@ -373,7 +374,12 @@ public final class ManaUtil { if (countColorfull == 0) { // seems there is no colorful mana we can use // try to pay {1} if (mana.getGeneric() > 0) { - // use any (lets choose first) + // choose first without addional costs if all have addional costs take the first + for (ActivatedManaAbilityImpl manaAbility : useableAbilities.values()) { + if (manaAbility.getCosts().size() == 1 && manaAbility.getCosts().get(0).getClass().equals(TapSourceCost.class)) { + return replace(useableAbilities, manaAbility); + } + } return replace(useableAbilities, useableAbilities.values().iterator().next()); } @@ -398,7 +404,7 @@ public final class ManaUtil { } /** - * This activates the special button inthe feedback panel of the client if + * This activates the special button in the feedback panel of the client if * there exists special ways to pay the mana (e.g. Delve, Convoke) * * @param source ability the mana costs have to be paid for @@ -499,7 +505,8 @@ public final class ManaUtil { } /** - * all ability/effect code with "= new GenericManaCost" must be replaced by createManaCost call + * all ability/effect code with "= new GenericManaCost" must be replaced by + * createManaCost call */ public static ManaCost createManaCost(int genericManaCount, boolean payAsX) { if (payAsX) { @@ -542,7 +549,7 @@ public final class ManaUtil { } if (!payed) { - game.restoreState(bookmark, restoreContextName); + player.restoreState(bookmark, restoreContextName, game); game.fireUpdatePlayersEvent(); } else { game.removeBookmark(bookmark); diff --git a/Mage/src/main/java/mage/util/TargetAddress.java b/Mage/src/main/java/mage/util/TargetAddress.java index 703d0eaf3e..5b4518fe40 100644 --- a/Mage/src/main/java/mage/util/TargetAddress.java +++ b/Mage/src/main/java/mage/util/TargetAddress.java @@ -1,9 +1,5 @@ - package mage.util; -import java.util.Iterator; -import java.util.Objects; -import java.util.UUID; import mage.abilities.Mode; import mage.abilities.Modes; import mage.abilities.SpellAbility; @@ -11,6 +7,10 @@ import mage.cards.Card; import mage.game.stack.Spell; import mage.target.Target; +import java.util.Iterator; +import java.util.Objects; +import java.util.UUID; + /** * @author duncant */ @@ -44,7 +44,7 @@ public class TargetAddress { protected final Iterator spellAbilityIterator; protected Integer lastSpellAbilityIndex = null; - protected Iterator modeIterator = null; + protected Iterator modeIterator = null; // must be read only protected Modes modes = null; protected UUID lastMode = null; protected Iterator targetIterator = null; diff --git a/Mage/src/main/java/mage/util/functions/AbilityApplier.java b/Mage/src/main/java/mage/util/functions/AbilityApplier.java index abbc57d354..215ba18200 100644 --- a/Mage/src/main/java/mage/util/functions/AbilityApplier.java +++ b/Mage/src/main/java/mage/util/functions/AbilityApplier.java @@ -21,7 +21,7 @@ public class AbilityApplier extends ApplyToPermanent { @Override public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) { - permanent.addAbility(ability, game); + permanent.addAbility(ability, source.getSourceId(), game); return true; } diff --git a/Mage/src/main/java/mage/watchers/Watcher.java b/Mage/src/main/java/mage/watchers/Watcher.java index 97ebc27bc0..2b75cba4d8 100644 --- a/Mage/src/main/java/mage/watchers/Watcher.java +++ b/Mage/src/main/java/mage/watchers/Watcher.java @@ -1,15 +1,12 @@ - package mage.watchers; -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import org.apache.log4j.Logger; -import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; - import java.io.Serializable; import java.lang.reflect.*; import java.util.*; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import org.apache.log4j.Logger; /** * watches for certain game events to occur and flags condition @@ -25,7 +22,6 @@ public abstract class Watcher implements Serializable { protected boolean condition; protected final WatcherScope scope; - public Watcher(WatcherScope scope) { this.scope = scope; } @@ -115,21 +111,38 @@ public abstract class Watcher implements Serializable { if (field.getType() == Set.class) { ((Set) field.get(watcher)).clear(); ((Set) field.get(watcher)).addAll((Set) field.get(this)); - } else if (field.getType() == Map.class) { - Map target = ((Map) field.get(watcher)); - target.clear(); - Map source = (Map) field.get(this); - + } else if (field.getType() == Map.class || field.getType() == HashMap.class) { ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType(); Type valueType = parameterizedType.getActualTypeArguments()[1]; - if (valueType instanceof ParameterizedTypeImpl && ((ParameterizedTypeImpl) valueType).getRawType().getSimpleName().contains("Set")) { - source.entrySet().forEach(kv -> { - Object key = ((Map.Entry) kv).getKey(); - Set value = (Set) ((Map.Entry) kv).getValue(); - target.put(key, new HashSet<>(value)); - }); - } - else { + if (valueType.getTypeName().contains("Set")) { + Map> source = (Map>) field.get(this); + Map> target = (Map>) field.get(watcher); + target.clear(); + for (Map.Entry> e : source.entrySet()) { + Set set = new HashSet<>(); + set.addAll(e.getValue()); + target.put(e.getKey(), set); + } + } else if (valueType.getTypeName().contains("List")) { + Map> source = (Map>) field.get(this); + Map> target = (Map>) field.get(watcher); + target.clear(); + for (Map.Entry> e : source.entrySet()) { + List list = new ArrayList<>(); + list.addAll(e.getValue()); + target.put(e.getKey(), list); + } + } else if (valueType.getTypeName().contains("Map")) { + Map> source = (Map>) field.get(this); + Map> target = (Map>) field.get(watcher); + target.clear(); + for (Map.Entry> e : source.entrySet()) { + Map map = new HashMap<>(); + map.putAll(e.getValue()); + target.put(e.getKey(), map); + } + + } else { ((Map) field.get(watcher)).putAll((Map) field.get(this)); } } else if (field.getType() == List.class) { diff --git a/Mage/src/main/java/mage/watchers/WatcherUtils.java b/Mage/src/main/java/mage/watchers/WatcherUtils.java index 75ee40a8d7..b90425706c 100644 --- a/Mage/src/main/java/mage/watchers/WatcherUtils.java +++ b/Mage/src/main/java/mage/watchers/WatcherUtils.java @@ -1,20 +1,20 @@ - -package mage.watchers; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.game.Game; -import org.apache.log4j.Logger; - -/** - * - * @author LevelX2 - */ -public final class WatcherUtils { - - public static void logMissingWatcher(Game game, Ability source, Class watcherClass, Class usingClass) { - MageObject sourceObject = source.getSourceObject(game); - Logger.getLogger(usingClass).error("Needed watcher is not started " + watcherClass.getSimpleName() - + " - " + (sourceObject == null ? " no source object" : sourceObject.getName())); - } -} + +package mage.watchers; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.game.Game; +import org.apache.log4j.Logger; + +/** + * + * @author LevelX2 + */ +public final class WatcherUtils { + + public static void logMissingWatcher(Game game, Ability source, Class watcherClass, Class usingClass) { + MageObject sourceObject = source.getSourceObject(game); + Logger.getLogger(usingClass).error("Needed watcher is not started " + watcherClass.getSimpleName() + + " - " + (sourceObject == null ? " no source object" : sourceObject.getName())); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java b/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java new file mode 100644 index 0000000000..953e4d64df --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java @@ -0,0 +1,39 @@ +package mage.watchers.common; + +import mage.abilities.Ability; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author TheElk801 + */ +public class AbilityResolvedWatcher extends Watcher { + + private final Map resolutionMap = new HashMap<>(); + + public AbilityResolvedWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.RESOLVING_ABILITY) { + resolutionMap.merge(event.getTargetId().toString() + game.getState().getZoneChangeCounter(event.getSourceId()), 1, Integer::sum); + } + } + + @Override + public void reset() { + super.reset(); + resolutionMap.clear(); + } + + public int getResolutionCount(Game game, Ability source) { + return resolutionMap.getOrDefault(source.getOriginalId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()), 0); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/CardsCycledOrDiscardedThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/CardsCycledOrDiscardedThisTurnWatcher.java index 0cb116c282..979026278d 100644 --- a/Mage/src/main/java/mage/watchers/common/CardsCycledOrDiscardedThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CardsCycledOrDiscardedThisTurnWatcher.java @@ -1,72 +1,72 @@ - -package mage.watchers.common; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.UUID; -import mage.MageObjectReference; -import mage.cards.Card; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.watchers.Watcher; - -/** - * Stores cards that were cycled or discarded by any player this turn. - * - * @author jeffwadsworth - */ -public class CardsCycledOrDiscardedThisTurnWatcher extends Watcher { - - private final Map cycledOrDiscardedCardsThisTurn = new HashMap<>(); - private final Map> numberOfCycledOrDiscardedCardsThisTurn = new HashMap<>(); - - public CardsCycledOrDiscardedThisTurnWatcher() { - super(WatcherScope.GAME); - } - - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DISCARDED_CARD - || event.getType() == GameEvent.EventType.CYCLED_CARD - && event.getPlayerId() != null) { - Card card = game.getCard(event.getTargetId()); - if (card != null) { - Cards c = getCardsCycledOrDiscardedThisTurn(event.getPlayerId()); - c.add(card); - cycledOrDiscardedCardsThisTurn.put(event.getPlayerId(), c); - Set cycledOrDiscardedCards = numberOfCycledOrDiscardedCardsThisTurn.get(event.getPlayerId()); - if (cycledOrDiscardedCards == null) { - cycledOrDiscardedCards = new HashSet<>(); - numberOfCycledOrDiscardedCardsThisTurn.put(event.getPlayerId(), cycledOrDiscardedCards); - } - cycledOrDiscardedCards.add(new MageObjectReference(card, game)); - } - } - } - - public Cards getCardsCycledOrDiscardedThisTurn(UUID playerId) { - return cycledOrDiscardedCardsThisTurn.getOrDefault(playerId, new CardsImpl()); - } - - public int getNumberOfCardsCycledOrDiscardedThisTurn(UUID playerId) { - if (numberOfCycledOrDiscardedCardsThisTurn.containsKey(playerId)) { - return numberOfCycledOrDiscardedCardsThisTurn.get(playerId).size(); - } - return 0; - } - - @Override - public void reset() { - super.reset(); - cycledOrDiscardedCardsThisTurn.clear(); - numberOfCycledOrDiscardedCardsThisTurn.clear(); - } - -} + +package mage.watchers.common; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; +import mage.MageObjectReference; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +/** + * Stores cards that were cycled or discarded by any player this turn. + * + * @author jeffwadsworth + */ +public class CardsCycledOrDiscardedThisTurnWatcher extends Watcher { + + private final Map cycledOrDiscardedCardsThisTurn = new HashMap<>(); + private final Map> numberOfCycledOrDiscardedCardsThisTurn = new HashMap<>(); + + public CardsCycledOrDiscardedThisTurnWatcher() { + super(WatcherScope.GAME); + } + + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DISCARDED_CARD + || event.getType() == GameEvent.EventType.CYCLED_CARD + && event.getPlayerId() != null) { + Card card = game.getCard(event.getTargetId()); + if (card != null) { + Cards c = getCardsCycledOrDiscardedThisTurn(event.getPlayerId()); + c.add(card); + cycledOrDiscardedCardsThisTurn.put(event.getPlayerId(), c); + Set cycledOrDiscardedCards = numberOfCycledOrDiscardedCardsThisTurn.get(event.getPlayerId()); + if (cycledOrDiscardedCards == null) { + cycledOrDiscardedCards = new HashSet<>(); + numberOfCycledOrDiscardedCardsThisTurn.put(event.getPlayerId(), cycledOrDiscardedCards); + } + cycledOrDiscardedCards.add(new MageObjectReference(card, game)); + } + } + } + + public Cards getCardsCycledOrDiscardedThisTurn(UUID playerId) { + return cycledOrDiscardedCardsThisTurn.getOrDefault(playerId, new CardsImpl()); + } + + public int getNumberOfCardsCycledOrDiscardedThisTurn(UUID playerId) { + if (numberOfCycledOrDiscardedCardsThisTurn.containsKey(playerId)) { + return numberOfCycledOrDiscardedCardsThisTurn.get(playerId).size(); + } + return 0; + } + + @Override + public void reset() { + super.reset(); + cycledOrDiscardedCardsThisTurn.clear(); + numberOfCycledOrDiscardedCardsThisTurn.clear(); + } + +} diff --git a/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java b/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java index e8a093451a..d96483b227 100644 --- a/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java @@ -5,9 +5,9 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; -import mage.players.Player; import mage.watchers.Watcher; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -21,6 +21,7 @@ import java.util.UUID; public class CommanderPlaysCountWatcher extends Watcher { private final Map playsCount = new HashMap<>(); + private final Map playerCount = new HashMap<>(); public CommanderPlaysCountWatcher() { super(WatcherScope.GAME); @@ -28,27 +29,31 @@ public class CommanderPlaysCountWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() != EventType.LAND_PLAYED && event.getType() != EventType.SPELL_CAST) { + if (event.getType() != EventType.LAND_PLAYED + && event.getType() != EventType.SPELL_CAST) { return; } - - UUID possibleCommanderId = event.getSourceId(); - boolean isCommanderObject = false; - for (Player player : game.getPlayers().values()) { - if (game.getCommandersIds(player).contains(possibleCommanderId)) { - isCommanderObject = true; - break; - } - } - - if (isCommanderObject && event.getZone() == Zone.COMMAND) { - int count = playsCount.getOrDefault(possibleCommanderId, 0); - count++; - playsCount.put(possibleCommanderId, count); + boolean isCommanderObject = game + .getPlayerList() + .stream() + .map(game::getPlayer) + .map(game::getCommandersIds) + .flatMap(Collection::stream) + .anyMatch(event.getSourceId()::equals); + if (!isCommanderObject || event.getZone() != Zone.COMMAND) { + return; } + playsCount.putIfAbsent(event.getSourceId(), 0); + playsCount.computeIfPresent(event.getSourceId(), (u, i) -> i + 1); + playerCount.putIfAbsent(event.getPlayerId(), 0); + playerCount.compute(event.getPlayerId(), (u, i) -> i + 1); } public int getPlaysCount(UUID commanderId) { return this.playsCount.getOrDefault(commanderId, 0); } + + public int getPlayerCount(UUID playerId) { + return this.playerCount.getOrDefault(playerId, 0); + } } diff --git a/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java b/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java index df816d4d6d..6359e97c34 100644 --- a/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java @@ -1,47 +1,47 @@ -package mage.watchers.common; - -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.watchers.Watcher; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; - -/** - * @author jeffwadsworth - *

- * Return the last player that was attacked by specified creature this turn - */ -public class CreatureAttackedWhichPlayerWatcher extends Watcher { - - private final Map getPlayerAttackedThisTurnByCreature = new HashMap<>(); - - public CreatureAttackedWhichPlayerWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { - UUID creatureId = event.getSourceId(); - UUID playerId = event.getTargetId(); - if (playerId != null - && creatureId != null) { - getPlayerAttackedThisTurnByCreature.putIfAbsent(creatureId, playerId); - } - } - } - - public UUID getPlayerAttackedThisTurnByCreature(UUID creatureId) { - return getPlayerAttackedThisTurnByCreature.getOrDefault(creatureId, null); - } - - @Override - public void reset() { - super.reset(); - getPlayerAttackedThisTurnByCreature.clear(); - } -} +package mage.watchers.common; + +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; + +/** + * @author jeffwadsworth + *

+ * Return the last player that was attacked by specified creature this turn + */ +public class CreatureAttackedWhichPlayerWatcher extends Watcher { + + private final Map getPlayerAttackedThisTurnByCreature = new HashMap<>(); + + public CreatureAttackedWhichPlayerWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { + UUID creatureId = event.getSourceId(); + UUID playerId = event.getTargetId(); + if (playerId != null + && creatureId != null) { + getPlayerAttackedThisTurnByCreature.putIfAbsent(creatureId, playerId); + } + } + } + + public UUID getPlayerAttackedThisTurnByCreature(UUID creatureId) { + return getPlayerAttackedThisTurnByCreature.getOrDefault(creatureId, null); + } + + @Override + public void reset() { + super.reset(); + getPlayerAttackedThisTurnByCreature.clear(); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/LifeLossOtherFromCombatWatcher.java b/Mage/src/main/java/mage/watchers/common/LifeLossOtherFromCombatWatcher.java index 71790be235..a68cf3eaa8 100644 --- a/Mage/src/main/java/mage/watchers/common/LifeLossOtherFromCombatWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/LifeLossOtherFromCombatWatcher.java @@ -1,50 +1,50 @@ - -package mage.watchers.common; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; -import mage.watchers.Watcher; - -/* - * - * @author Styxo - */ -public class LifeLossOtherFromCombatWatcher extends Watcher { - - private final Set players = new HashSet<>(); - - public LifeLossOtherFromCombatWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_LIFE && !event.getFlag()) { - UUID playerId = event.getPlayerId(); - if (playerId != null) { - players.add(playerId); - } - } - } - - public boolean opponentLostLifeOtherFromCombat(UUID playerId, Game game) { - Player player = game.getPlayer(playerId); - if (player != null) { - if (players.stream().anyMatch(damagedPlayerId -> player.hasOpponent(damagedPlayerId, game))) { - return true; - } - } - return false; - } - - @Override - public void reset() { - super.reset(); - players.clear(); - } -} + +package mage.watchers.common; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.watchers.Watcher; + +/* + * + * @author Styxo + */ +public class LifeLossOtherFromCombatWatcher extends Watcher { + + private final Set players = new HashSet<>(); + + public LifeLossOtherFromCombatWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.LOST_LIFE && !event.getFlag()) { + UUID playerId = event.getPlayerId(); + if (playerId != null) { + players.add(playerId); + } + } + } + + public boolean opponentLostLifeOtherFromCombat(UUID playerId, Game game) { + Player player = game.getPlayer(playerId); + if (player != null) { + if (players.stream().anyMatch(damagedPlayerId -> player.hasOpponent(damagedPlayerId, game))) { + return true; + } + } + return false; + } + + @Override + public void reset() { + super.reset(); + players.clear(); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/LostControlWatcher.java b/Mage/src/main/java/mage/watchers/common/LostControlWatcher.java new file mode 100644 index 0000000000..86f8cf26dd --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/LostControlWatcher.java @@ -0,0 +1,39 @@ +package mage.watchers.common; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +/** + * + * @author LevelX2 + */ +public class LostControlWatcher extends Watcher { + + private final Map lastLostControl = new HashMap<>(); + + public LostControlWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.LOST_CONTROL) { + lastLostControl.put(event.getTargetId(), System.currentTimeMillis()); + } + } + + @Override + public void reset() { + super.reset(); + lastLostControl.clear(); + } + + public long getOrderOfLastLostControl(UUID sourceId) { + return lastLostControl.getOrDefault(sourceId, new Long(0)); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java index e71a844b28..1df8fa84d1 100644 --- a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java @@ -1,53 +1,53 @@ - - -package mage.watchers.common; - -import java.util.HashMap; -import java.util.Map; -import mage.MageObjectReference; -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.watchers.Watcher; - -/** - * - * @author LevelX2 - */ -public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher { - - private final Map permanentsTargeted = new HashMap<>(); - - public NumberOfTimesPermanentTargetedATurnWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.TARGETED) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null) { - MageObjectReference mor = new MageObjectReference(permanent, game); - int amount = 0; - if (permanentsTargeted.containsKey(mor)) { - amount = permanentsTargeted.get(mor); - } - permanentsTargeted.put(mor, ++amount); - } - } - } - - public boolean notMoreThanOnceTargetedThisTurn(Permanent creature, Game game) { - if (permanentsTargeted.containsKey(new MageObjectReference(creature, game))) { - return permanentsTargeted.get(new MageObjectReference(creature, game)) < 2; - } - return true; - } - - @Override - public void reset() { - super.reset(); - permanentsTargeted.clear(); - } -} + + +package mage.watchers.common; + +import java.util.HashMap; +import java.util.Map; +import mage.MageObjectReference; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +/** + * + * @author LevelX2 + */ +public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher { + + private final Map permanentsTargeted = new HashMap<>(); + + public NumberOfTimesPermanentTargetedATurnWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.TARGETED) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null) { + MageObjectReference mor = new MageObjectReference(permanent, game); + int amount = 0; + if (permanentsTargeted.containsKey(mor)) { + amount = permanentsTargeted.get(mor); + } + permanentsTargeted.put(mor, ++amount); + } + } + } + + public boolean notMoreThanOnceTargetedThisTurn(Permanent creature, Game game) { + if (permanentsTargeted.containsKey(new MageObjectReference(creature, game))) { + return permanentsTargeted.get(new MageObjectReference(creature, game)) < 2; + } + return true; + } + + @Override + public void reset() { + super.reset(); + permanentsTargeted.clear(); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/PlayLandWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayLandWatcher.java index d0af1cecc2..8da3d39d6d 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayLandWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayLandWatcher.java @@ -1,52 +1,52 @@ -package mage.watchers.common; - -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.watchers.Watcher; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -/** - * @author jeffwadsworth - */ -public class PlayLandWatcher extends Watcher { - - private final Set playerPlayedLand = new HashSet<>(); // player that played land - private final Set landPlayed = new HashSet<>(); // land played - - public PlayLandWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LAND_PLAYED) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent != null - && permanent.isLand() - && !playerPlayedLand.contains(event.getPlayerId())) { - playerPlayedLand.add(event.getPlayerId()); - landPlayed.add(event.getTargetId()); - } - } - } - - @Override - public void reset() { - super.reset(); - playerPlayedLand.clear(); - landPlayed.clear(); - } - - public boolean landPlayed(UUID playerId) { - return playerPlayedLand.contains(playerId); - } - - public boolean wasLandPlayed(UUID landId) { - return landPlayed.contains(landId); - } -} +package mage.watchers.common; + +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author jeffwadsworth + */ +public class PlayLandWatcher extends Watcher { + + private final Set playerPlayedLand = new HashSet<>(); // player that played land + private final Set landPlayed = new HashSet<>(); // land played + + public PlayLandWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.LAND_PLAYED) { + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent != null + && permanent.isLand() + && !playerPlayedLand.contains(event.getPlayerId())) { + playerPlayedLand.add(event.getPlayerId()); + landPlayed.add(event.getTargetId()); + } + } + } + + @Override + public void reset() { + super.reset(); + playerPlayedLand.clear(); + landPlayed.clear(); + } + + public boolean landPlayed(UUID playerId) { + return playerPlayedLand.contains(playerId); + } + + public boolean wasLandPlayed(UUID landId) { + return landPlayed.contains(landId); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/RevoltWatcher.java b/Mage/src/main/java/mage/watchers/common/RevoltWatcher.java index b2fb2e4ab7..db1dd3483a 100644 --- a/Mage/src/main/java/mage/watchers/common/RevoltWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/RevoltWatcher.java @@ -1,49 +1,49 @@ -package mage.watchers.common; - -import mage.constants.WatcherScope; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.watchers.Watcher; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -/** - * @author BetaSteward_at_googlemail.com - */ -public class RevoltWatcher extends Watcher { - - private final Set revoltActivePlayerIds = new HashSet<>(0); - - public RevoltWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == EventType.ZONE_CHANGE && event instanceof ZoneChangeEvent) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - Permanent permanent = zEvent.getTarget(); - if (permanent != null) { - revoltActivePlayerIds.add(permanent.getControllerId()); - } - } - } - } - - public boolean revoltActive(UUID playerId) { - return revoltActivePlayerIds.contains(playerId); - } - - @Override - public void reset() { - super.reset(); - revoltActivePlayerIds.clear(); - } -} +package mage.watchers.common; + +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author BetaSteward_at_googlemail.com + */ +public class RevoltWatcher extends Watcher { + + private final Set revoltActivePlayerIds = new HashSet<>(0); + + public RevoltWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == EventType.ZONE_CHANGE && event instanceof ZoneChangeEvent) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getFromZone() == Zone.BATTLEFIELD) { + Permanent permanent = zEvent.getTarget(); + if (permanent != null) { + revoltActivePlayerIds.add(permanent.getControllerId()); + } + } + } + } + + public boolean revoltActive(UUID playerId) { + return revoltActivePlayerIds.contains(playerId); + } + + @Override + public void reset() { + super.reset(); + revoltActivePlayerIds.clear(); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java index 883ab6f9ef..46a1a7fcc0 100644 --- a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java @@ -5,8 +5,6 @@ */ package mage.watchers.common; -import java.util.*; - import mage.MageObject; import mage.constants.WatcherScope; import mage.constants.Zone; @@ -17,6 +15,12 @@ import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.watchers.Watcher; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + /** * * @author LevelX2 @@ -24,6 +28,7 @@ import mage.watchers.Watcher; public class SpellsCastWatcher extends Watcher { private final Map> spellsCast = new HashMap<>(); + private final Map> spellsCastFromGraveyard = new HashMap<>(); private int nonCreatureSpells; public SpellsCastWatcher() { @@ -42,13 +47,21 @@ public class SpellsCastWatcher extends Watcher { } if (spell != null) { List spells; + List graveyardSpells; if (!spellsCast.containsKey(spell.getControllerId())) { spells = new ArrayList<>(); spellsCast.put(spell.getControllerId(), spells); + graveyardSpells = new ArrayList<>(); + spellsCastFromGraveyard.put(spell.getControllerId(), graveyardSpells); + } else { spells = spellsCast.get(spell.getControllerId()); + graveyardSpells = spellsCastFromGraveyard.get(spell.getControllerId()); } spells.add(spell.copy()); // copy needed because attributes like color could be changed later + if (event.getZone() == Zone.GRAVEYARD) { + graveyardSpells.add(spell.copy()); + } if (StaticFilters.FILTER_SPELL_NON_CREATURE.match(spell, game)) { nonCreatureSpells++; } @@ -61,12 +74,17 @@ public class SpellsCastWatcher extends Watcher { super.reset(); nonCreatureSpells = 0; spellsCast.clear(); + spellsCastFromGraveyard.clear(); } public List getSpellsCastThisTurn(UUID playerId) { return spellsCast.get(playerId); } + public List getSpellsCastFromGraveyardThisTurn(UUID playerId) { + return spellsCastFromGraveyard.get(playerId); + } + public int getNumberOfNonCreatureSpells() { return nonCreatureSpells; } diff --git a/Mage/src/main/resources/jumpstart/jumpstart.txt b/Mage/src/main/resources/jumpstart/jumpstart.txt new file mode 100644 index 0000000000..73df97d972 --- /dev/null +++ b/Mage/src/main/resources/jumpstart/jumpstart.txt @@ -0,0 +1,1214 @@ +# Archaeology (1) +1 JMP 471 Juggernaut +1 JMP 474 Meteor Golem +1 JMP 458 Ancestral Statue +1 JMP 150 Erratic Visionary +1 JMP 183 Thirst for Knowledge +1 JMP 462 Chromatic Sphere +1 JMP 468 Hedron Archive +1 JMP 491 Buried Ruin +5 JMP 50 Island +1 JMP 14 Scholar of the Lost Trove +1 JMP 456 Aether Spellbomb +2 JMP 9 Archaeomender +1 JMP 32 Lightning-Core Excavator +1 JMP 36 Thriving Isle +1 JMP 49 Island + +# Archaeology (2) +1 JMP 471 Juggernaut +1 JMP 474 Meteor Golem +1 JMP 458 Ancestral Statue +1 JMP 150 Erratic Visionary +1 JMP 183 Thirst for Knowledge +1 JMP 464 Dreamstone Hedron +1 JMP 488 Terrarion +1 JMP 491 Buried Ruin +5 JMP 50 Island +1 JMP 14 Scholar of the Lost Trove +1 JMP 456 Aether Spellbomb +2 JMP 9 Archaeomender +1 JMP 32 Lightning-Core Excavator +1 JMP 36 Thriving Isle +1 JMP 49 Island + +# Archaeology (3) +1 JMP 176 Sharding Sphinx +1 JMP 187 Vedalken Archmage +1 JMP 471 Juggernaut +1 JMP 474 Meteor Golem +1 JMP 458 Ancestral Statue +1 M21 235 Prismite +1 JMP 183 Thirst for Knowledge +1 JMP 468 Hedron Archive +1 JMP 491 Buried Ruin +5 JMP 50 Island +1 JMP 456 Aether Spellbomb +2 JMP 9 Archaeomender +1 JMP 32 Lightning-Core Excavator +1 JMP 36 Thriving Isle +1 JMP 49 Island + +# Archaeology (4) +1 JMP 482 Scarecrone +1 JMP 471 Juggernaut +1 JMP 474 Meteor Golem +1 JMP 484 Scuttlemutt +1 JMP 470 Jousting Dummy +1 JMP 485 Signpost Scarecrow +1 JMP 183 Thirst for Knowledge +1 M21 236 Short Sword +1 JMP 491 Buried Ruin +5 JMP 50 Island +1 JMP 456 Aether Spellbomb +2 JMP 9 Archaeomender +1 JMP 32 Lightning-Core Excavator +1 JMP 36 Thriving Isle +1 JMP 49 Island + +# Basri +1 M21 12 Concordia Pegasus +1 JMP 114 Knight of the Tusk +1 M21 24 Legion's Judgment +1 M21 19 Feat of Resistance +1 M21 17 Faith's Fetters +7 JMP 45 Plains +1 JMP 35 Thriving Heath +1 M21 280 Basri Ket +1 M21 9 Basri's Lieutenant +1 M21 41 Tempered Veteran +1 M21 37 Siege Striker +1 M21 10 Basri's Solidarity +1 M21 39 Staunch Shieldmate +1 M21 287 Basri's Acolyte + +# Cats (1) +1 JMP 407 Keeper of Fables +1 JMP 403 Initiate's Companion +1 JMP 419 Pouncing Cheetah +1 JMP 397 Feral Prowler +1 JMP 392 Enlarge +1 JMP 412 Nature's Way +1 JMP 386 Crushing Canopy +1 JMP 396 Feral Invocation +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 74 Forest +1 M21 374 Feline Sovereign +1 M21 175 Canopy Stalker +1 M21 202 Sabertooth Mauler +1 M21 196 Pridemalkin + +# Cats (2) +1 JMP 407 Keeper of Fables +1 JMP 403 Initiate's Companion +1 JMP 418 Penumbra Bobcat +1 JMP 397 Feral Prowler +1 JMP 392 Enlarge +1 JMP 412 Nature's Way +1 JMP 386 Crushing Canopy +1 JMP 410 Lurking Predators +1 JMP 396 Feral Invocation +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 74 Forest +1 M21 374 Feline Sovereign +1 M21 175 Canopy Stalker +1 M21 196 Pridemalkin + +# Chandra +1 JMP 356 Pyroclastic Elemental +1 JMP 372 Young Pyromancer +1 JMP 315 Fanatical Firebrand +1 JMP 317 Flames of the Firebrand +1 JMP 355 Pillar of Flame +1 JMP 336 Hungry Flames +1 M21 165 Thrill of Possibility +7 JMP 64 Mountain +1 JMP 33 Thriving Bluff +1 M21 135 Chandra, Heart of Fire +1 M21 136 Chandra's Incinerator +1 M21 138 Chandra's Pyreling +2 M21 303 Chandra's Magmutt + +# Dinosaurs (1) +1 JMP 399 Ghalta, Primal Hunger +1 JMP 388 Drover of the Mighty +1 JMP 416 Orazca Frillback +1 M21 209 Thrashing Brontodon +1 M21 176 Colossal Dreadmaw +1 JMP 427 Savage Stomp +1 JMP 384 Commune with Dinosaurs +1 M21 177 Cultivate +1 JMP 386 Crushing Canopy +1 M21 205 Setessan Training +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 73 Forest +1 M21 194 Ornery Dilophosaur +1 M21 178 Drowsing Tyrannodon + +# Dinosaurs (2) +1 JMP 429 Selvala, Heart of the Wilds +1 JMP 388 Drover of the Mighty +1 JMP 437 Thundering Spineback +1 JMP 416 Orazca Frillback +1 M21 176 Colossal Dreadmaw +1 JMP 427 Savage Stomp +1 JMP 384 Commune with Dinosaurs +1 JMP 386 Crushing Canopy +1 JMP 414 New Horizons +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 73 Forest +1 M21 308 Garruk's Uprising +1 M21 194 Ornery Dilophosaur +1 M21 178 Drowsing Tyrannodon + +# Dinosaurs (3) +1 JMP 399 Ghalta, Primal Hunger +1 JMP 388 Drover of the Mighty +1 JMP 437 Thundering Spineback +1 JMP 416 Orazca Frillback +1 M21 176 Colossal Dreadmaw +1 JMP 427 Savage Stomp +1 JMP 384 Commune with Dinosaurs +1 M21 177 Cultivate +1 JMP 386 Crushing Canopy +1 M21 210 Titanic Growth +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 73 Forest +1 M21 194 Ornery Dilophosaur +1 M21 178 Drowsing Tyrannodon + +# Dinosaurs (4) +1 JMP 388 Drover of the Mighty +1 JMP 416 Orazca Frillback +1 M21 209 Thrashing Brontodon +1 M21 176 Colossal Dreadmaw +1 JMP 427 Savage Stomp +1 JMP 384 Commune with Dinosaurs +1 JMP 414 New Horizons +6 JMP 74 Forest +1 JMP 423 Rampaging Brontodon +1 JMP 34 Thriving Grove +1 JMP 73 Forest +1 M21 377 Primal Might +1 M21 308 Garruk's Uprising +1 M21 194 Ornery Dilophosaur +1 M21 178 Drowsing Tyrannodon + +# Discarding 1 +1 JMP 233 Fell Specter +1 JMP 269 Ravenous Chupacabra +1 JMP 227 Entomber Exarch +1 JMP 214 Burglar Rat +1 JMP 266 Phyrexian Rager +1 JMP 279 Slate Street Ruffian +2 JMP 287 Wight of Precinct Six +1 JMP 200 Assassin's Strike +1 M21 115 Mind Rot +1 JMP 222 Death's Approach +7 JMP 57 Swamp +1 JMP 17 Tinybones, Trinket Thief +1 JMP 37 Thriving Moor + +# Discarding 2 +1 JMP 251 Liliana's Reaver +1 JMP 259 Nyxathid +1 JMP 233 Fell Specter +1 JMP 269 Ravenous Chupacabra +1 JMP 227 Entomber Exarch +1 JMP 214 Burglar Rat +1 JMP 266 Phyrexian Rager +1 JMP 279 Slate Street Ruffian +2 JMP 287 Wight of Precinct Six +1 JMP 200 Assassin's Strike +1 JMP 222 Death's Approach +7 JMP 57 Swamp +1 JMP 37 Thriving Moor + +# Dogs (1) +1 JMP 81 Affa Guard Hound +1 JMP 94 Cathar's Companion +1 M21 19 Feat of Resistance +1 JMP 125 Pacifism +6 JMP 45 Plains +1 JMP 4 Release the Dogs +1 JMP 7 Supply Runners +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 45 Plains +1 M21 29 Pack Leader +1 M21 36 Selfless Savior +1 M21 35 Secure the Scene +1 M21 2 Alpine Watchdog +1 M21 30 Rambunctious Mutt + +# Dogs (2) +1 JMP 113 Isamaru, Hound of Konda +1 JMP 81 Affa Guard Hound +1 JMP 94 Cathar's Companion +1 JMP 99 Dauntless Onslaught +1 JMP 125 Pacifism +6 JMP 45 Plains +1 JMP 4 Release the Dogs +1 JMP 7 Supply Runners +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 45 Plains +1 M21 29 Pack Leader +1 M21 35 Secure the Scene +1 M21 2 Alpine Watchdog +1 M21 30 Rambunctious Mutt + +# Elves (1) +1 JMP 385 Craterhoof Behemoth +1 JMP 391 Elvish Archdruid +1 JMP 389 Dwynen's Elite +1 JMP 400 Ghirapur Guide +1 JMP 430 Silhana Wayfinder +1 JMP 444 Wildheart Invoker +1 JMP 386 Crushing Canopy +1 M21 210 Titanic Growth +1 JMP 420 Presence of Gond +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 77 Forest +1 M21 206 Skyway Sniper +1 M21 193 Llanowar Visionary +1 M21 189 Hunter's Edge + +# Elves (2) +1 JMP 389 Dwynen's Elite +1 JMP 430 Silhana Wayfinder +1 JMP 447 Wren's Run Vanquisher +1 JMP 408 Leaf Gilder +1 JMP 444 Wildheart Invoker +1 JMP 386 Crushing Canopy +1 M21 210 Titanic Growth +1 JMP 420 Presence of Gond +6 JMP 74 Forest +1 JMP 28 Allosaurus Shepherd +1 JMP 34 Thriving Grove +1 JMP 77 Forest +1 M21 206 Skyway Sniper +1 M21 193 Llanowar Visionary +1 M21 189 Hunter's Edge + +# Feathered Friends (1) +1 JMP 107 Healer's Hawk +1 M21 12 Concordia Pegasus +1 JMP 80 Aerial Assault +1 JMP 99 Dauntless Onslaught +1 JMP 124 Moment of Heroism +1 JMP 133 Sky Tether +6 JMP 45 Plains +1 JMP 5 Steel-Plume Marshal +1 JMP 35 Thriving Heath +1 JMP 44 Plains +1 M21 5 Aven Gagglemaster +1 M21 18 Falconer Adept +1 M21 44 Warded Battlements +1 M21 20 Gale Swooper +1 M21 11 Celestial Enforcer + +# Feathered Friends (2) +1 M21 12 Concordia Pegasus +1 JMP 107 Healer's Hawk +1 JMP 135 Tandem Tactics +1 JMP 133 Sky Tether +6 JMP 45 Plains +1 JMP 5 Steel-Plume Marshal +1 JMP 35 Thriving Heath +1 JMP 44 Plains +1 M21 3 Angelic Ascension +1 M21 5 Aven Gagglemaster +1 M21 18 Falconer Adept +1 M21 44 Warded Battlements +1 M21 40 Swift Response +1 M21 20 Gale Swooper +1 M21 11 Celestial Enforcer + +# Feathered Friends (3) +1 JMP 85 Angel of the Dire Hour +1 JMP 107 Healer's Hawk +1 M21 12 Concordia Pegasus +1 JMP 99 Dauntless Onslaught +1 JMP 134 Take Heart +1 JMP 133 Sky Tether +6 JMP 45 Plains +1 JMP 35 Thriving Heath +1 JMP 44 Plains +1 M21 5 Aven Gagglemaster +1 M21 18 Falconer Adept +1 M21 44 Warded Battlements +1 M21 40 Swift Response +1 M21 20 Gale Swooper +1 M21 11 Celestial Enforcer + +# Feathered Friends (4) +1 JMP 89 Archon of Justice +1 JMP 90 Archon of Redemption +1 JMP 107 Healer's Hawk +1 M21 12 Concordia Pegasus +1 JMP 80 Aerial Assault +1 JMP 79 Aegis of the Heavens +1 JMP 99 Dauntless Onslaught +1 JMP 133 Sky Tether +6 JMP 45 Plains +1 JMP 35 Thriving Heath +1 JMP 44 Plains +1 M21 18 Falconer Adept +1 M21 44 Warded Battlements +1 M21 20 Gale Swooper +1 M21 11 Celestial Enforcer + +# Garruk +1 JMP 373 Affectionate Indrik +1 JMP 381 Brushstrider +1 JMP 426 Rumbling Baloth +1 JMP 416 Orazca Frillback +1 JMP 402 Hunter's Insight +1 M21 199 Ranger's Guile +7 JMP 74 Forest +1 JMP 34 Thriving Grove +1 M21 305 Garruk, Unleashed +1 M21 185 Garruk's Harbinger +1 M21 308 Garruk's Uprising +1 M21 189 Hunter's Edge +1 M21 306 Garruk's Gorehorn +1 M21 178 Drowsing Tyrannodon + +# Heavily Armored (1) +1 JMP 128 Patron of the Valiant +1 JMP 93 Bulwark Giant +1 JMP 118 Lightwalker +1 M21 26 Makeshift Battalion +1 M21 19 Feat of Resistance +1 JMP 95 Cathars' Crusade +6 JMP 45 Plains +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 43 Plains +1 M21 41 Tempered Veteran +1 M21 37 Siege Striker +1 M21 10 Basri's Solidarity +1 M21 35 Secure the Scene +1 M21 287 Basri's Acolyte + +# Heavily Armored (2) +1 JMP 128 Patron of the Valiant +1 JMP 93 Bulwark Giant +1 JMP 118 Lightwalker +1 M21 26 Makeshift Battalion +1 JMP 106 Gird for Battle +1 JMP 91 Battlefield Promotion +1 JMP 95 Cathars' Crusade +6 JMP 45 Plains +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 43 Plains +1 M21 41 Tempered Veteran +1 M21 37 Siege Striker +1 M21 35 Secure the Scene +1 M21 287 Basri's Acolyte + +# Heavily Armored (3) +1 JMP 123 Mikaeus, the Lunarch +1 JMP 128 Patron of the Valiant +1 JMP 93 Bulwark Giant +1 JMP 118 Lightwalker +1 M21 26 Makeshift Battalion +1 JMP 91 Battlefield Promotion +1 JMP 100 Divine Arrow +6 JMP 45 Plains +1 JMP 7 Supply Runners +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 43 Plains +1 M21 41 Tempered Veteran +1 M21 10 Basri's Solidarity +1 M21 287 Basri's Acolyte + +# Heavily Armored (4) +1 JMP 108 High Sentinels of Arashin +1 JMP 93 Bulwark Giant +1 JMP 118 Lightwalker +1 M21 26 Makeshift Battalion +1 JMP 120 Long Road Home +1 JMP 101 Duelist's Heritage +6 JMP 45 Plains +1 JMP 7 Supply Runners +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 43 Plains +1 M21 41 Tempered Veteran +1 M21 37 Siege Striker +1 M21 40 Swift Response +1 M21 287 Basri's Acolyte + +# Lands (1) +1 JMP 439 Ulvenwald Hydra +1 JMP 379 Awakener Druid +1 JMP 395 Feral Hydra +1 JMP 394 Fa'adiyah Seer +1 JMP 398 Fertilid +1 JMP 435 Sylvan Ranger +1 JMP 444 Wildheart Invoker +1 M21 177 Cultivate +1 JMP 386 Crushing Canopy +1 JMP 390 Elemental Uprising +1 JMP 448 Zendikar's Roil +1 JMP 440 Vastwood Zendikon +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 71 Forest + +# Lands (2) +1 JMP 415 Oracle of Mul Daya +1 JMP 379 Awakener Druid +1 JMP 395 Feral Hydra +1 JMP 446 Woodborn Behemoth +1 JMP 394 Fa'adiyah Seer +1 JMP 398 Fertilid +1 JMP 433 Sporemound +1 JMP 435 Sylvan Ranger +1 JMP 444 Wildheart Invoker +1 M21 177 Cultivate +1 JMP 386 Crushing Canopy +1 JMP 390 Elemental Uprising +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 71 Forest + +# Lightning (1) +1 JMP 371 Weaver of Lightning +1 JMP 344 Lightning Elemental +1 JMP 345 Lightning Shrieker +1 JMP 335 Homing Lightning +1 JMP 341 Lightning Axe +1 JMP 342 Lightning Bolt +1 JMP 343 Lightning Diadem +6 JMP 64 Mountain +1 JMP 21 Lightning Phoenix +1 JMP 23 Living Lightning +2 JMP 22 Lightning Visionary +1 JMP 32 Lightning-Core Excavator +1 JMP 33 Thriving Bluff +1 JMP 68 Mountain + +# Lightning (2) +1 JMP 291 Ball Lightning +1 JMP 371 Weaver of Lightning +1 JMP 344 Lightning Elemental +1 JMP 302 Chain Lightning +1 JMP 341 Lightning Axe +1 JMP 359 Riddle of Lightning +1 JMP 343 Lightning Diadem +6 JMP 64 Mountain +1 JMP 21 Lightning Phoenix +1 JMP 23 Living Lightning +2 JMP 22 Lightning Visionary +1 JMP 32 Lightning-Core Excavator +1 JMP 33 Thriving Bluff +1 JMP 68 Mountain + +# Liliana +1 JMP 250 Liliana's Elite +1 JMP 205 Blighted Bat +1 JMP 238 Ghoulraiser +1 JMP 286 Wailing Ghoul +1 JMP 276 Settle the Score +1 JMP 217 Cemetery Recruitment +7 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 M21 297 Liliana, Waker of the Dead +1 M21 110 Liliana's Standard Bearer +1 M21 101 Goremand +1 M21 109 Liliana's Devotee +1 M21 119 Rise Again +1 M21 300 Liliana's Steward + +# Minions (1) +1 JMP 237 Ghoulcaller's Accomplice +1 JMP 212 Bone Picker +1 JMP 226 Dutiful Attendant +1 JMP 277 Shambling Goblin +1 JMP 244 Innocent Blood +6 JMP 57 Swamp +1 JMP 15 Kels, Fight Fixer +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 54 Swamp +1 M21 129 Witch's Cauldron +1 M21 97 Eliminate +1 M21 101 Goremand +1 M21 93 Crypt Lurker +1 M21 126 Village Rites + +# Minions (2) +1 JMP 228 Eternal Taskmaster +1 JMP 224 Drainpipe Vermin +1 JMP 226 Dutiful Attendant +1 JMP 237 Ghoulcaller's Accomplice +1 JMP 213 Bone Splinters +6 JMP 57 Swamp +1 JMP 15 Kels, Fight Fixer +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 54 Swamp +1 M21 129 Witch's Cauldron +1 M21 97 Eliminate +1 M21 101 Goremand +1 M21 93 Crypt Lurker +1 M21 126 Village Rites + +# Minions (3) +1 JMP 226 Dutiful Attendant +1 JMP 224 Drainpipe Vermin +1 JMP 237 Ghoulcaller's Accomplice +1 M21 98 Fetid Imp +1 JMP 248 Launch Party +1 JMP 493 Phyrexian Tower +5 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 54 Swamp +1 M21 129 Witch's Cauldron +1 M21 97 Eliminate +1 M21 101 Goremand +1 M21 109 Liliana's Devotee +1 M21 93 Crypt Lurker +1 M21 126 Village Rites + +# Minions (4) +1 JMP 236 Ghoulcaller Gisa +1 JMP 226 Dutiful Attendant +1 JMP 237 Ghoulcaller's Accomplice +1 JMP 277 Shambling Goblin +1 M21 98 Fetid Imp +1 JMP 244 Innocent Blood +6 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 54 Swamp +1 M21 129 Witch's Cauldron +1 M21 97 Eliminate +1 M21 101 Goremand +1 M21 109 Liliana's Devotee +1 M21 126 Village Rites + +# Phyrexian +1 JMP 278 Sheoldred, Whispering One +1 JMP 227 Entomber Exarch +1 JMP 265 Phyrexian Gargantua +1 JMP 475 Myr Sire +1 JMP 476 Perilous Myr +1 JMP 263 Phyrexian Broodlings +1 JMP 264 Phyrexian Debaser +1 JMP 266 Phyrexian Rager +1 JMP 223 Douse in Gloom +1 JMP 267 Phyrexian Reclamation +1 JMP 262 Parasitic Implant +6 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 JMP 58 Swamp +1 M21 112 Malefic Scythe + +# Pirates (1) +1 JMP 170 Rishadan Airship +1 JMP 149 Departed Deckhand +1 JMP 178 Spectral Sailor +1 JMP 155 Kitesail Corsair +1 JMP 165 Prosperous Pirates +1 JMP 172 Sailor of Means +1 JMP 142 Chart a Course +1 JMP 189 Voyage's End +1 JMP 477 Pirate's Cutlass +1 JMP 144 Coastal Piracy +1 JMP 192 Waterknot +6 JMP 50 Island +1 JMP 11 Corsair Captain +1 JMP 36 Thriving Isle +1 JMP 52 Island + +# Pirates (2) +1 JMP 149 Departed Deckhand +1 JMP 178 Spectral Sailor +1 JMP 155 Kitesail Corsair +1 JMP 165 Prosperous Pirates +1 JMP 170 Rishadan Airship +1 JMP 172 Sailor of Means +1 JMP 142 Chart a Course +1 JMP 477 Pirate's Cutlass +1 JMP 148 Curious Obsession +1 M21 47 Capture Sphere +6 JMP 50 Island +1 JMP 11 Corsair Captain +1 JMP 36 Thriving Isle +1 JMP 52 Island +1 M21 62 Read the Tides + +# Predatory (1) +1 JMP 431 Somberwald Stag +1 JMP 434 Sylvan Brushstrider +1 JMP 380 Brindle Shoat +1 JMP 387 Dawntreader Elk +1 JMP 406 Irresistible Prey +1 JMP 438 Time to Feed +1 JMP 386 Crushing Canopy +1 JMP 473 Marauder's Axe +6 JMP 74 Forest +1 JMP 30 Neyith of the Dire Hunt +1 JMP 34 Thriving Grove +1 JMP 76 Forest +1 M21 182 Fungal Rebirth +1 M21 212 Trufflesnout +1 M21 202 Sabertooth Mauler + +# Predatory (2) +1 JMP 466 Gingerbrute +1 JMP 373 Affectionate Indrik +1 JMP 380 Brindle Shoat +1 JMP 387 Dawntreader Elk +1 JMP 406 Irresistible Prey +1 JMP 438 Time to Feed +1 JMP 386 Crushing Canopy +1 JMP 473 Marauder's Axe +6 JMP 74 Forest +1 JMP 30 Neyith of the Dire Hunt +1 JMP 34 Thriving Grove +1 JMP 76 Forest +1 M21 182 Fungal Rebirth +1 M21 212 Trufflesnout +1 M21 202 Sabertooth Mauler + +# Predatory (3) +1 JMP 436 Thragtusk +1 JMP 431 Somberwald Stag +1 JMP 380 Brindle Shoat +1 JMP 387 Dawntreader Elk +1 JMP 466 Gingerbrute +1 JMP 406 Irresistible Prey +1 JMP 438 Time to Feed +1 JMP 386 Crushing Canopy +1 JMP 473 Marauder's Axe +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 76 Forest +1 M21 182 Fungal Rebirth +1 M21 212 Trufflesnout +1 M21 202 Sabertooth Mauler + +# Predatory (4) +1 JMP 424 Ravenous Baloth +1 JMP 373 Affectionate Indrik +1 JMP 380 Brindle Shoat +1 JMP 387 Dawntreader Elk +1 JMP 434 Sylvan Brushstrider +1 JMP 406 Irresistible Prey +1 JMP 438 Time to Feed +1 JMP 411 Momentous Fall +1 JMP 386 Crushing Canopy +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 76 Forest +1 M21 182 Fungal Rebirth +1 M21 212 Trufflesnout +1 M21 202 Sabertooth Mauler + +# Rainbow +1 JMP 454 Maelstrom Archangel +1 JMP 461 Chamber Sentry +1 JMP 450 Dinrova Horror +1 JMP 451 Fusion Elemental +1 JMP 455 Raging Regisaur +1 JMP 452 Ironroot Warlord +1 JMP 457 Alloy Myr +1 JMP 486 Skittering Surveyor +1 M21 235 Prismite +1 JMP 449 Auger Spree +1 JMP 478 Prophetic Prism +1 JMP 453 Lawmage's Binding +1 JMP 492 Mirrodin's Core +1 JMP 495 Rupture Spire +1 JMP 74 Forest +1 JMP 50 Island +1 JMP 64 Mountain +1 JMP 45 Plains +1 JMP 57 Swamp +1 JMP 78 Terramorphic Expanse + +# Reanimated (1) +1 JMP 257 Mire Triton +1 JMP 215 Cadaver Imp +1 JMP 221 Crow of Dark Tidings +1 JMP 256 Miasmic Mummy +1 JMP 284 Tithebearer Giant +1 JMP 270 Reanimate +1 JMP 235 Funeral Rites +6 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 JMP 56 Swamp +1 M21 88 Archfiend's Vessel +1 M21 101 Goremand +1 M21 97 Eliminate +1 M21 93 Crypt Lurker +1 M21 119 Rise Again + +# Reanimated (2) +1 JMP 257 Mire Triton +1 JMP 221 Crow of Dark Tidings +1 JMP 256 Miasmic Mummy +1 JMP 270 Reanimate +1 JMP 235 Funeral Rites +1 JMP 252 Macabre Waltz +6 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 JMP 56 Swamp +1 M21 92 Carrion Grub +1 M21 101 Goremand +1 M21 97 Eliminate +1 M21 93 Crypt Lurker +1 M21 119 Rise Again +1 M21 100 Gloom Sower + +# Reanimated (3) +1 JMP 241 Gravewaker +1 JMP 274 Scourge of Nel Toth +1 JMP 257 Mire Triton +1 JMP 221 Crow of Dark Tidings +1 JMP 256 Miasmic Mummy +1 JMP 230 Exhume +1 JMP 235 Funeral Rites +1 JMP 288 Zombie Infestation +6 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 JMP 56 Swamp +1 M21 101 Goremand +1 M21 97 Eliminate +1 M21 93 Crypt Lurker +1 M21 119 Rise Again + +# Reanimated (4) +1 JMP 257 Mire Triton +1 JMP 221 Crow of Dark Tidings +1 JMP 256 Miasmic Mummy +1 JMP 271 Rise of the Dark Realms +1 JMP 235 Funeral Rites +1 JMP 280 Soul Salvage +6 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 JMP 56 Swamp +1 M21 92 Carrion Grub +1 M21 101 Goremand +1 M21 97 Eliminate +1 M21 93 Crypt Lurker +1 M21 119 Rise Again +1 M21 100 Gloom Sower + +# Seismic +1 JMP 331 Grim Lavamancer +1 JMP 304 Cinder Elemental +1 JMP 362 Seismic Elemental +1 JMP 290 Ashmouth Hound +1 JMP 297 Bloodrock Cyclops +1 JMP 351 Molten Ravager +1 JMP 364 Spitting Earth +1 JMP 347 Magmaquake +1 JMP 346 Magma Jet +1 JMP 368 Volcanic Fallout +1 M21 171 Volcanic Geyser +1 JMP 472 Mana Geode +6 JMP 64 Mountain +1 JMP 33 Thriving Bluff +1 JMP 64 Mountain + +# Teferi +1 JMP 164 Prescient Chimera +1 JMP 171 Sage's Row Savant +1 M21 83 Vodalian Arcanist +1 JMP 182 Talrand's Invocation +1 JMP 152 Exclude +1 JMP 156 Leave in the Dust +1 M21 59 Opt +7 JMP 50 Island +1 JMP 36 Thriving Isle +1 M21 290 Teferi, Master of Time +1 M21 76 Teferi's Ageless Insight +1 M21 80 Tolarian Kraken +1 M21 78 Teferi's Tutelage +1 M21 295 Teferi's Protege + +# Tree-Hugging (1) +1 JMP 422 Primordial Sage +1 JMP 442 Wall of Blossoms +1 JMP 375 Ambassador Oak +1 M21 207 Snarespinner +1 JMP 412 Nature's Way +1 JMP 393 Explore +1 JMP 374 Aggressive Urge +1 JMP 386 Crushing Canopy +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 70 Forest +1 M21 174 Burlfist Oak +1 M21 213 Warden of the Woods +1 M21 193 Llanowar Visionary +1 M21 187 Gnarled Sage + +# Tree-Hugging (2) +1 JMP 422 Primordial Sage +1 JMP 442 Wall of Blossoms +1 JMP 375 Ambassador Oak +1 M21 207 Snarespinner +1 JMP 412 Nature's Way +1 JMP 393 Explore +1 JMP 386 Crushing Canopy +1 JMP 445 Wildsize +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 70 Forest +1 M21 174 Burlfist Oak +1 M21 213 Warden of the Woods +1 M21 193 Llanowar Visionary +1 M21 187 Gnarled Sage + +# Tree-Hugging (3) +1 JMP 442 Wall of Blossoms +1 JMP 375 Ambassador Oak +1 M21 207 Snarespinner +1 JMP 412 Nature's Way +1 JMP 393 Explore +1 JMP 374 Aggressive Urge +1 JMP 386 Crushing Canopy +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 70 Forest +1 M21 376 Jolrael, Mwonvuli Recluse +1 M21 174 Burlfist Oak +1 M21 213 Warden of the Woods +1 M21 193 Llanowar Visionary +1 M21 187 Gnarled Sage + +# Tree-Hugging (4) +1 JMP 432 Soul of the Harvest +1 JMP 442 Wall of Blossoms +1 M21 207 Snarespinner +1 JMP 412 Nature's Way +1 JMP 393 Explore +1 JMP 386 Crushing Canopy +1 JMP 445 Wildsize +1 JMP 441 Verdant Embrace +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 70 Forest +1 M21 174 Burlfist Oak +1 M21 213 Warden of the Woods +1 M21 193 Llanowar Visionary +1 M21 187 Gnarled Sage + +# Under the Sea (1) +1 JMP 177 Sigiled Starfish +1 JMP 161 Octoprophet +1 JMP 197 Wishful Merfolk +1 JMP 138 Aegis Turtle +1 JMP 189 Voyage's End +1 JMP 489 Unstable Obelisk +1 JMP 192 Waterknot +7 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 46 Island +1 M21 60 Pursued Whale +1 M21 80 Tolarian Kraken +1 M21 84 Waker of Waves +1 M21 67 Rousing Read + +# Under the Sea (2) +1 JMP 146 Cryptic Serpent +1 JMP 177 Sigiled Starfish +1 JMP 161 Octoprophet +1 JMP 197 Wishful Merfolk +1 JMP 138 Aegis Turtle +1 JMP 193 Whelming Wave +1 JMP 180 Sweep Away +1 JMP 489 Unstable Obelisk +1 JMP 192 Waterknot +7 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 46 Island +1 M21 60 Pursued Whale +1 M21 84 Waker of Waves + +# Unicorns +1 JMP 112 Inspiring Unicorn +1 JMP 122 Mesa Unicorn +1 JMP 131 Ronom Unicorn +1 JMP 136 Valorous Stance +1 JMP 97 Cloudshift +1 JMP 110 Inspired Charge +1 M21 17 Faith's Fetters +6 JMP 45 Plains +1 JMP 3 Emiel the Blessed +1 JMP 1 Blessed Sanctuary +1 JMP 2 Brightmare +1 JMP 35 Thriving Heath +1 JMP 41 Plains +1 M21 42 Valorous Steed +1 M21 14 Daybreak Charger + +# Vampires (1) +1 JMP 206 Blood Artist +1 JMP 209 Bloodbond Vampire +1 JMP 239 Gifted Aetherborn +1 JMP 245 Kalastria Nightwatch +1 JMP 199 Agonizing Syphon +1 JMP 247 Last Gasp +1 JMP 229 Eternal Thirst +6 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 60 Swamp +1 M21 362 Vito, Thorn of the Dusk Rose +1 M21 122 Silversmote Ghoul +1 M21 121 Sanguine Indulgence +1 M21 100 Gloom Sower + +# Vampires (2) +1 JMP 208 Blood Host +1 JMP 209 Bloodbond Vampire +1 JMP 239 Gifted Aetherborn +1 JMP 218 Child of Night +1 JMP 199 Agonizing Syphon +1 JMP 247 Last Gasp +1 JMP 229 Eternal Thirst +6 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 60 Swamp +1 M21 362 Vito, Thorn of the Dusk Rose +1 M21 122 Silversmote Ghoul +1 M21 121 Sanguine Indulgence +1 M21 100 Gloom Sower + +# Vampires (3) +1 JMP 272 Sangromancer +1 JMP 209 Bloodbond Vampire +1 JMP 239 Gifted Aetherborn +1 JMP 275 Sengir Vampire +1 JMP 285 Vampire Neonate +1 JMP 199 Agonizing Syphon +1 JMP 247 Last Gasp +1 JMP 231 Exquisite Blood +6 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 60 Swamp +1 M21 122 Silversmote Ghoul +1 M21 121 Sanguine Indulgence +1 M21 100 Gloom Sower + +# Vampires (4) +1 JMP 225 Drana, Liberator of Malakir +1 JMP 209 Bloodbond Vampire +1 JMP 232 Falkenrath Noble +1 JMP 239 Gifted Aetherborn +1 JMP 218 Child of Night +1 JMP 199 Agonizing Syphon +1 JMP 247 Last Gasp +1 JMP 254 Mark of the Vampire +6 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 60 Swamp +1 M21 122 Silversmote Ghoul +1 M21 121 Sanguine Indulgence +1 M21 100 Gloom Sower + +# Walls +1 JMP 442 Wall of Blossoms +1 JMP 382 Carven Caryatid +1 JMP 465 Gargoyle Sentinel +1 JMP 417 Overgrown Battlement +1 JMP 401 Grave Bramble +1 JMP 443 Wall of Vines +1 JMP 480 Roving Keep +1 M21 195 Portcullis Vine +1 JMP 386 Crushing Canopy +1 JMP 490 Warmonger's Chariot +1 JMP 378 Assault Formation +6 JMP 74 Forest +1 JMP 31 Towering Titan +1 JMP 34 Thriving Grove +1 JMP 75 Forest + +# Well-Read (1) +1 JMP 487 Suspicious Bookcase +1 JMP 162 Oneirophage +1 JMP 150 Erratic Visionary +1 JMP 481 Runed Servitor +1 M21 59 Opt +1 JMP 147 Curiosity +1 JMP 159 Narcolepsy +6 JMP 50 Island +1 JMP 13 Ormos, Archive Keeper +1 JMP 36 Thriving Isle +1 JMP 53 Island +1 M21 80 Tolarian Kraken +1 M21 81 Tome Anima +1 M21 55 Library Larcenist +1 M21 67 Rousing Read + +# Well-Read (2) +1 JMP 162 Oneirophage +1 JMP 150 Erratic Visionary +1 JMP 481 Runed Servitor +1 M21 59 Opt +1 JMP 459 Arcane Encyclopedia +1 JMP 147 Curiosity +1 M21 47 Capture Sphere +6 JMP 50 Island +1 JMP 13 Ormos, Archive Keeper +1 JMP 36 Thriving Isle +1 JMP 53 Island +1 M21 80 Tolarian Kraken +1 M21 81 Tome Anima +1 M21 55 Library Larcenist +1 M21 67 Rousing Read + +# Well-Read (3) +1 JMP 162 Oneirophage +1 JMP 487 Suspicious Bookcase +1 JMP 143 Cloudreader Sphinx +1 JMP 150 Erratic Visionary +1 JMP 481 Runed Servitor +1 M21 59 Opt +1 JMP 169 Rhystic Study +1 JMP 147 Curiosity +1 JMP 159 Narcolepsy +6 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 53 Island +1 M21 80 Tolarian Kraken +1 M21 81 Tome Anima +1 M21 55 Library Larcenist + +# Well-Read (4) +1 JMP 158 Mystic Archaeologist +1 JMP 162 Oneirophage +1 JMP 487 Suspicious Bookcase +1 JMP 143 Cloudreader Sphinx +1 JMP 481 Runed Servitor +1 JMP 167 Read the Runes +1 M21 59 Opt +1 JMP 147 Curiosity +1 M21 47 Capture Sphere +6 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 53 Island +1 M21 80 Tolarian Kraken +1 M21 81 Tome Anima +1 M21 55 Library Larcenist + +# Witchcraft (1) +1 JMP 211 Bogbrew Witch +1 JMP 253 Malakir Familiar +1 JMP 283 Tempting Witch +1 JMP 210 Bloodhunter Bat +1 JMP 216 Cauldron Familiar +2 JMP 234 Festering Newt +1 JMP 201 Bake into a Pie +1 JMP 460 Bubbling Cauldron +6 JMP 57 Swamp +1 JMP 18 Witch of the Moors +1 JMP 37 Thriving Moor +1 JMP 59 Swamp +1 M21 129 Witch's Cauldron +1 M21 99 Finishing Blow + +# Witchcraft (2) +1 JMP 283 Tempting Witch +1 JMP 211 Bogbrew Witch +1 JMP 282 Swarm of Bloodflies +1 JMP 203 Black Cat +1 JMP 210 Bloodhunter Bat +2 JMP 234 Festering Newt +1 JMP 207 Blood Divination +1 JMP 201 Bake into a Pie +1 JMP 247 Last Gasp +1 JMP 460 Bubbling Cauldron +6 JMP 57 Swamp +1 JMP 18 Witch of the Moors +1 JMP 37 Thriving Moor +1 JMP 59 Swamp + +# Wizards (1) +1 JMP 181 Talrand, Sky Summoner +1 JMP 153 Exclusion Mage +1 JMP 171 Sage's Row Savant +1 M21 83 Vodalian Arcanist +1 JMP 182 Talrand's Invocation +1 JMP 196 Winged Words +1 JMP 198 Wizard's Retort +1 JMP 140 Befuddle +1 M21 51 Frost Breath +1 M21 59 Opt +6 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 47 Island +1 M21 71 Shipwreck Dowser +1 M21 62 Read the Tides + +# Wizards (2) +1 JMP 153 Exclusion Mage +1 JMP 145 Crookclaw Transmuter +1 JMP 171 Sage's Row Savant +1 JMP 173 Sea Gate Oracle +1 M21 83 Vodalian Arcanist +1 JMP 182 Talrand's Invocation +1 JMP 196 Winged Words +1 JMP 198 Wizard's Retort +1 M21 59 Opt +1 JMP 494 Riptide Laboratory +5 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 47 Island +1 M21 71 Shipwreck Dowser +1 M21 66 Rookie Mistake +1 M21 62 Read the Tides + +# Wizards (3) +1 JMP 181 Talrand, Sky Summoner +1 JMP 171 Sage's Row Savant +1 M21 83 Vodalian Arcanist +1 JMP 182 Talrand's Invocation +1 JMP 196 Winged Words +1 JMP 198 Wizard's Retort +1 M21 82 Unsubstantiate +1 M21 59 Opt +1 M21 47 Capture Sphere +6 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 47 Island +1 M21 71 Shipwreck Dowser +1 M21 62 Read the Tides +1 M21 295 Teferi's Protege + +# Wizards (4) +1 JMP 171 Sage's Row Savant +1 JMP 179 Storm Sculptor +1 M21 83 Vodalian Arcanist +1 JMP 182 Talrand's Invocation +1 JMP 198 Wizard's Retort +1 JMP 163 Peel from Reality +1 M21 61 Rain of Revelation +1 M21 51 Frost Breath +6 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 47 Island +1 M21 348 Barrin, Tolarian Archmage +1 M21 71 Shipwreck Dowser +1 M21 62 Read the Tides +1 M21 295 Teferi's Protege diff --git a/Mage.Client/src/main/resources/mage/client/deckeditor/pennydreadful.properties b/Mage/src/main/resources/pennydreadful.properties similarity index 100% rename from Mage.Client/src/main/resources/mage/client/deckeditor/pennydreadful.properties rename to Mage/src/main/resources/pennydreadful.properties diff --git a/Mage/src/test/java/mage/WatcherTest.java b/Mage/src/test/java/mage/WatcherTest.java index 8b8c44b160..f6b3869aa1 100644 --- a/Mage/src/test/java/mage/WatcherTest.java +++ b/Mage/src/test/java/mage/WatcherTest.java @@ -1,4 +1,217 @@ package mage; +import static mage.constants.WatcherScope.GAME; +import static org.junit.Assert.*; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; +import org.junit.Test; + public class WatcherTest { + + @Test + public void testShallowCopy() { + // Given + Map mapField = new HashMap<>(); + mapField.put("mapFieldKey1", "mapFieldValue1"); + mapField.put("mapFieldKey2", "mapFieldValue2"); + + TestWatcher testWatcher = new TestWatcher(GAME); + testWatcher.setStringField("stringField"); + testWatcher.setSetField(set("setField1", "setField2")); + testWatcher.setMapField(mapField); + + // When + TestWatcher copy = testWatcher.copy(); + + // And + testWatcher.getSetField().add("setField3"); + mapField.put("mapFieldKey3", "mapFieldValue3"); + + // Then + assertEquals("stringField", copy.getStringField()); + assertEquals(set("setField1", "setField2"), copy.getSetField()); + assertEquals(ImmutableMap.of("mapFieldKey1", "mapFieldValue1", "mapFieldKey2", "mapFieldValue2"), copy.getMapField()); + } + + @Test + public void testDeepCopyMapOfList() { + // Given + Map> listInMapField = new HashMap<>(); + List list1 = new ArrayList<>(); + list1.add("v1"); + list1.add("v1.1"); + List list2 = new ArrayList<>(); + list2.add("v2"); + listInMapField.put("k1", list1); + listInMapField.put("k2", list2); + + TestWatcher testWatcher = new TestWatcher(GAME); + testWatcher.setListInMapField(listInMapField); + + // When + TestWatcher copy = testWatcher.copy(); + + // And + testWatcher.getListInMapField().get("k1").add("v1.2"); + List list3 = new ArrayList<>(); + list3.add("v5"); + testWatcher.getListInMapField().put("k5", list3); + + // Then + Map> copyListInMap = copy.getListInMapField(); + assertEquals(2, copyListInMap.size()); + assertTrue(copyListInMap.containsKey("k1")); + assertEquals(ImmutableList.of("v1", "v1.1"), copyListInMap.get("k1")); + assertTrue(copyListInMap.containsKey("k2")); + assertEquals(ImmutableList.of("v2"), copyListInMap.get("k2")); + } + + @Test + public void testDeepCopyMapOfSet() { + // Given + Map> setInMapField = new HashMap<>(); + Set set1 = new HashSet<>(); + set1.add("v3"); + Set set2 = new HashSet<>(); + set2.add("v4"); + set2.add("v4.1"); + setInMapField.put("k3", set1); + setInMapField.put("k4", set2); + setInMapField.put("k", new HashSet()); + + TestWatcher testWatcher = new TestWatcher(GAME); + testWatcher.setSetInMapField(setInMapField); + + // When + TestWatcher copy = testWatcher.copy(); + + // And + testWatcher.getSetInMapField().get("k3").add("v3.1"); + + // Then + Map> copySetInMap = copy.getSetInMapField(); + assertEquals(3, copySetInMap.size()); + assertTrue(copySetInMap.containsKey("k3")); + assertEquals(ImmutableSet.of("v3"), copySetInMap.get("k3")); + assertTrue(copySetInMap.containsKey("k4")); + assertEquals(ImmutableSet.of("v4", "v4.1"), copySetInMap.get("k4")); + assertTrue(copySetInMap.containsKey("k")); + assertEquals(ImmutableSet.of(), copySetInMap.get("k")); + } + + @Test + public void testDeepCopyMapOfMap() { + // Given + Map> mapInMapField = new HashMap<>(); + Map map1 = new HashMap<>(); + map1.put("k1.1", "v1.1"); + map1.put("k1.2", "v1.2"); + Map map2 = new HashMap<>(); + map2.put("k2.1", "v2.1"); + mapInMapField.put("k1", map1); + mapInMapField.put("k2", map2); + + TestWatcher testWatcher = new TestWatcher(GAME); + testWatcher.setMapInMapField(mapInMapField); + + // When + TestWatcher copy = testWatcher.copy(); + + // And + testWatcher.getMapInMapField().get("k2").put("k2.2", "v2.2"); + testWatcher.getMapInMapField().put("k3", new HashMap<>()); + + // Then + Map> copyMapInMap = copy.getMapInMapField(); + assertEquals(2, copyMapInMap.size()); + assertTrue(copyMapInMap.containsKey("k1")); + assertEquals(ImmutableMap.of("k1.1", "v1.1", "k1.2", "v1.2"), copyMapInMap.get("k1")); + assertTrue(copyMapInMap.containsKey("k2")); + assertEquals(ImmutableMap.of("k2.1", "v2.1"), copyMapInMap.get("k2")); + assertFalse(copyMapInMap.containsKey("k3")); + } + + private Set set(String... values) { + return Stream.of(values).collect(Collectors.toSet()); + } + + public static class TestWatcher extends Watcher { + private String stringField; + private Set setField = new HashSet<>(); + private Map mapField = new HashMap<>(); + private Map> listInMapField = new HashMap<>(); + private Map> setInMapField = new HashMap<>(); + private Map> mapInMapField = new HashMap<>(); + + public TestWatcher(WatcherScope scope) { + super(scope); + } + + @Override + public void watch(GameEvent event, Game game) { + System.out.println("watch"); + } + + public String getStringField() { + return stringField; + } + + public void setStringField(String stringField) { + this.stringField = stringField; + } + + public Set getSetField() { + return setField; + } + + public void setSetField(Set setField) { + this.setField = setField; + } + + public Map getMapField() { + return mapField; + } + + public void setMapField(Map mapField) { + this.mapField = mapField; + } + + public Map> getListInMapField() { + return listInMapField; + } + + public void setListInMapField(Map> listInMapField) { + this.listInMapField = listInMapField; + } + + public Map> getSetInMapField() { + return setInMapField; + } + + public void setSetInMapField(Map> setInMapField) { + this.setInMapField = setInMapField; + } + + public Map> getMapInMapField() { + return mapInMapField; + } + + public void setMapInMapField(Map> mapInMapField) { + this.mapInMapField = mapInMapField; + } + } } diff --git a/Mage/src/test/java/mage/cards/decks/importer/MtgjsonDeckImportTest.java b/Mage/src/test/java/mage/cards/decks/importer/MtgjsonDeckImportTest.java new file mode 100644 index 0000000000..1395f4bf16 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/MtgjsonDeckImportTest.java @@ -0,0 +1,57 @@ +package mage.cards.decks.importer; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import mage.cards.decks.DeckCardLists; + +public class MtgjsonDeckImportTest { + + private static final FakeCardLookup LOOKUP = new FakeCardLookup(); + + @Test + public void testImport() { + StringBuilder errors = new StringBuilder(); + MtgjsonDeckImporter importer = new MtgjsonDeckImporter() { + @Override + public CardLookup getCardLookup() { + return LOOKUP; + } + }; + DeckCardLists deck = importer.importDeck( + "src/test/java/mage/cards/decks/importer/samples/testdeck.json", errors); + assertEquals("Arcane Tempo", deck.getName()); + + TestDeckChecker.checker() + .addMain("Goblin Electromancer", 4) + .addMain("Crackling Drake", 4) + .addMain("Murmuring Mystic", 2) + .addMain("Arclight Phoenix", 1) + .addMain("Niv-Mizzet, Parun", 2) + .addMain("Chart a Course", 4) + .addMain("Lava Coil", 4) + .addMain("Beacon Bolt", 1) + .addMain("Opt", 4) + .addMain("Radical Idea", 4) + .addMain("Shock", 4) + .addMain("Dive Down", 2) + .addMain("Blink of an Eye", 1) + .addMain("The Mirari Conjecture", 1) + .addMain("Sulfur Falls", 3) + .addMain("Izzet Guildgate", 4) + .addMain("Island", 8) + .addMain("Mountain", 7) + .addSide("The Mirari Conjecture", 1) + .addSide("Beacon Bolt", 1) + .addSide("Negate", 3) + .addSide("Entrancing Melody", 3) + .addSide("Fiery Cannonade", 3) + .addSide("Shivan Fire", 2) + .addSide("Disdainful Stroke", 2) + .verify(deck, 60, 15); + + assertEquals("", errors.toString()); + } + +} diff --git a/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.json b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.json new file mode 100644 index 0000000000..7e3b601dea --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.json @@ -0,0 +1,4585 @@ +{ + "code":"GRN", + "mainBoard":[ + { + "artist":"Svetlin Velinov", + "borderColor":"black", + "colorIdentity":[ + "R", + "U" + ], + "colors":[ + "R", + "U" + ], + "convertedManaCost":2.0, + "count":4, + "edhrecRank":637, + "flavorText":"\"Result 752: Rapid mass redistribution.\n\"Result 753: Calamitous reverse synthesis.\n\"Result 754: Acute disarrayment.\"\n—Izzet research notes", + "foreignData":[ + { + "flavorText":"„Resultat 752: Beschleunigte Masseumverteilung.\"\n„Resultat 753: Ungewollte umgekehrte Synthese.\"\n„Resultat 754: Akuter Formverlust.\"\n—Notizen von Izzet-Forschern", + "language":"German", + "multiverseId":453183, + "name":"Goblin-Elektromagier", + "text":"Spontanzauber und Hexereien, die du wirkst, kosten beim Wirken {1} weniger.", + "type":"Kreatur — Goblin, Zauberer" + }, + { + "flavorText":"\"Resultado 752: Rápida redistribución de masa.\n\"Resultado 753: Síntesis inversa calamitosa.\n\"Resultado 754: Desbarajuste agudo\".\n—Notas de investigación ízzet", + "language":"Spanish", + "multiverseId":453442, + "name":"Electromante trasgo", + "text":"Te cuesta {1} menos lanzar hechizos de instantáneo o de conjuro.", + "type":"Criatura — Hechicero trasgo" + }, + { + "flavorText":"« Résultat 752 : redistribution rapide de la masse. »\n« Résultat 753 : synthèse d'inversion désastreuse. »\n« Résultat 754 : désorganisation grave. »\n—Notes de recherche d'Izzet", + "language":"French", + "multiverseId":453701, + "name":"Électromancien gobelin", + "text":"Les sorts d'éphémère et de rituel que vous lancez coûtent {1} de moins à lancer.", + "type":"Créature : gobelin et sorcier" + }, + { + "flavorText":"\"Risultato 752: rapida redistribuzione di massa.\"\n\"Risultato 753: disastrosa sintesi inversa.\"\n\"Risultato 754: caos intenso.\"\n—Note di ricerca degli Izzet", + "language":"Italian", + "multiverseId":453960, + "name":"Elettromante Goblin", + "text":"Le magie istantaneo e stregoneria che lanci costano {1} in meno per essere lanciate.", + "type":"Creatura — Mago Goblin" + }, + { + "flavorText":"「結果752:急速な質量再分布。」\n「結果753:悲惨な逆合成。」\n「結果754:深刻な無秩序。」\n――イゼットの研究記録", + "language":"Japanese", + "multiverseId":454219, + "name":"ゴブリンの電術師", + "text":"あなたがインスタントかソーサリーである呪文を唱えるためのコストは{1}少なくなる。", + "type":"クリーチャー — ゴブリン・ウィザード" + }, + { + "flavorText":"\"결과 752: 빠른 질량 재배치.\n\"결과 753: 비참한 역합성.\n\"결과 754: 갑작스러운 혼란.\"\n—이젯 연구 기록", + "language":"Korean", + "multiverseId":454478, + "name":"고블린 전기술사", + "text":"당신이 발동하는 순간마법 주문과 집중마법 주문은 발동하는 데 {1}이 덜 든다.", + "type":"생물 — 고블린 마법사" + }, + { + "flavorText":"\"Resultado 752: Redistribuição de massa rápida.\nResultado 753: Síntese reversa calamitosa.\nResultado 754: Desarranjo agudo.\"\n— Anotações de pesquisa Izzet", + "language":"Portuguese (Brazil)", + "multiverseId":454737, + "name":"Eletromante Goblin", + "text":"As mágicas instantâneas e os feitiços que você conjura custam {1} a menos para serem conjurados.", + "type":"Criatura — Goblin Mago" + }, + { + "flavorText":"«Результат 752: быстрое перераспределение массы.\nРезультат 753: катастрофический обратный синтез.\nРезультат 754: хаотичное расщепление».\n— Исследовательские записки Иззетов", + "language":"Russian", + "multiverseId":454996, + "name":"Гоблин-Электромант", + "text":"Разыгрываемые вами мгновенные заклинания и заклинания волшебства стоят на {1} меньше.", + "type":"Существо — Гоблин Чародей" + }, + { + "flavorText":"「结果752:物质快速重新分配。\n「结果753:逆向合成造成灾难性后果。\n「结果754:严重紊乱。」\n~伊捷研究纪录", + "language":"Chinese Simplified", + "multiverseId":455255, + "name":"鬼怪电流术士", + "text":"你施放的瞬间与法术咒语减少{1}来施放。", + "type":"生物 ~鬼怪/法术师" + }, + { + "flavorText":"「結果752:物質快速重新分配。\n「結果753:逆向合成造成災難性後果。\n「結果754:嚴重紊亂。」\n~伊捷研究紀錄", + "language":"Chinese Traditional", + "multiverseId":455514, + "name":"鬼怪電流術士", + "text":"你施放的瞬間與巫術咒語減少{1}來施放。", + "type":"生物 ~鬼怪/魔法師" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "isReprint":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{U}{R}", + "mcmId":363853, + "mcmMetaId":206414, + "mtgArenaId":68635, + "mtgoId":69721, + "multiverseId":452924, + "name":"Goblin Electromancer", + "number":"174", + "originalText":"Instant and sorcery spells you cast cost {1} less to cast.", + "originalType":"Creature — Goblin Wizard", + "power":"2", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.02 + }, + "paper":{ + "2020-04-26":0.12 + }, + "paperFoil":{ + "2020-04-26":0.75 + } + }, + "printings":[ + "C15", + "DDJ", + "DDS", + "GRN", + "MM3", + "RTR" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/921fc7dd807f2715", + "tcgplayer":"https://mtgjson.com/links/8d81060a54d8e32a" + }, + "rarity":"common", + "rulings":[ + { + "date":"2017-03-14", + "text":"Two Goblin Electromancers will make instant and sorcery spells you cast cost {2} less to cast, and so on." + }, + { + "date":"2018-10-05", + "text":"To determine the total cost of a spell, start with the mana cost or alternative cost you’re paying, add any cost increases, then apply any cost reductions. The converted mana cost of the spell remains unchanged, no matter what the total cost to cast it was." + }, + { + "date":"2018-10-05", + "text":"Goblin Electromancer’s effect reduces only generic mana in the spell’s total cost." + } + ], + "scryfallId":"0ae987da-e4b5-4b82-843c-6aaa87262fc8", + "scryfallIllustrationId":"725b112d-2637-45c1-aec8-e89981ba5fa3", + "scryfallOracleId":"81f06f84-1580-43c0-89d5-08d34541a519", + "subtypes":[ + "Goblin", + "Wizard" + ], + "supertypes":[ + + ], + "tcgplayerProductId":175583, + "text":"Instant and sorcery spells you cast cost {1} less to cast.", + "toughness":"2", + "type":"Creature — Goblin Wizard", + "types":[ + "Creature" + ], + "uuid":"4132a870-1364-5bd4-a27c-85511dcbd26d", + "watermark":"izzet" + }, + { + "artist":"Victor Adame Minguez", + "borderColor":"black", + "colorIdentity":[ + "R", + "U" + ], + "colors":[ + "R", + "U" + ], + "convertedManaCost":4.0, + "count":4, + "edhrecRank":2832, + "foreignData":[ + { + "language":"German", + "multiverseId":453172, + "name":"Knisternder Sceada", + "text":"Fliegend\nDie Stärke des Knisternden Sceada ist gleich der Gesamtanzahl an Spontanzauber- und Hexerei-Karten, die du besitzt und die sich im Exil oder in deinem Friedhof befinden.\nWenn der Knisternde Sceada ins Spiel kommt, ziehe eine Karte.", + "type":"Kreatur — Sceada" + }, + { + "language":"Spanish", + "multiverseId":453431, + "name":"Draco chisporroteante", + "text":"Vuela.\nLa fuerza del Draco chisporroteante es igual a la cantidad total de cartas de instantáneo y de conjuro en tu cementerio y en el exilio de las cuales eres propietario.\nCuando el Draco chisporroteante entre al campo de batalla, roba una carta.", + "type":"Criatura — Draco" + }, + { + "language":"French", + "multiverseId":453690, + "name":"Drakôn crépitant", + "text":"Vol\nLa force du Drakôn crépitant est égale au nombre total de cartes d'éphémère et de rituel que vous possédez en exil et dans votre cimetière.\nQuand le Drakôn crépitant arrive sur le champ de bataille, piochez une carte.", + "type":"Créature : drakôn" + }, + { + "language":"Italian", + "multiverseId":453949, + "name":"Draghetto Crepitante", + "text":"Volare\nLa forza del Draghetto Crepitante è pari al numero totale di carte istantaneo e stregoneria che possiedi in esilio e nel tuo cimitero.\nQuando il Draghetto Crepitante entra nel campo di battaglia, pesca una carta.", + "type":"Creatura — Draghetto" + }, + { + "language":"Japanese", + "multiverseId":454208, + "name":"弾けるドレイク", + "text":"飛行\n弾けるドレイクのパワーは、追放領域かあなたの墓地にあり、あなたがオーナーであり、インスタントかソーサリーであるカードの枚数に等しい。\n弾けるドレイクが戦場に出たとき、カードを1枚引く。", + "type":"クリーチャー — ドレイク" + }, + { + "language":"Korean", + "multiverseId":454467, + "name":"파직거리는 드레이크", + "text":"비행\n파직거리는 드레이크의 공격력은 추방 영역과 당신의 무덤에 있는 당신이 소유한 순간마법과 집중마법 카드 수의 합과 같다.\n파직거리는 드레이크가 전장에 들어올 때, 카드 한 장을 뽑는다.", + "type":"생물 — 드레이크" + }, + { + "language":"Portuguese (Brazil)", + "multiverseId":454726, + "name":"Dragonete Fagulhante", + "text":"Voar\nO poder de Dragonete Fagulhante é igual ao número de cards de mágica instantânea e feitiço que você possui no exílio e em seu cemitério.\nQuando Dragonete Fagulhante entrar no campo de batalha, compre um card.", + "type":"Criatura — Dragonete" + }, + { + "language":"Russian", + "multiverseId":454985, + "name":"Трескучий Дрейк", + "text":"Полет\nСила Трескучего Дрейка равна суммарному количеству принадлежащих вам карт мгновенных заклинаний и волшебства в изгнании и на вашем кладбище.\nКогда Трескучий Дрейк выходит на поле битвы, возьмите карту.", + "type":"Существо — Дрейк" + }, + { + "language":"Chinese Simplified", + "multiverseId":455244, + "name":"爆响龙兽", + "text":"飞行\n爆响龙兽的力量等同于放逐区中由你拥有之瞬间与法术牌数量和你坟墓场中这两类牌数量的加总。\n当爆响龙兽进战场时,抓一张牌。", + "type":"生物 ~龙兽" + }, + { + "language":"Chinese Traditional", + "multiverseId":455503, + "name":"爆響龍獸", + "text":"飛行\n爆響龍獸的力量等同於放逐區中由你擁有之瞬間與巫術牌數量和你墳墓場中這兩類牌數量的加總。\n當爆響龍獸進戰場時,抽一張牌。", + "type":"生物 ~龍獸" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{U}{U}{R}{R}", + "mcmId":364125, + "mcmMetaId":265353, + "mtgArenaId":68624, + "mtgoId":69699, + "multiverseId":452913, + "name":"Crackling Drake", + "number":"163", + "originalText":"Flying\nCrackling Drake's power is equal to the total number of instant and sorcery cards you own in exile and in your graveyard.\nWhen Crackling Drake enters the battlefield, draw a card.", + "originalType":"Creature — Drake", + "power":"*", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.26 + }, + "paper":{ + "2020-04-26":0.23 + }, + "paperFoil":{ + "2020-04-26":2.03 + } + }, + "printings":[ + "C19", + "C20", + "GRN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/984fb1f1a067cde9", + "tcgplayer":"https://mtgjson.com/links/9412970f54807f86" + }, + "rarity":"uncommon", + "rulings":[ + { + "date":"2018-10-05", + "text":"If any exiled cards you own are face down, they have no characteristics. If they’re normally instants or sorceries, they won’t be counted." + }, + { + "date":"2018-10-05", + "text":"The ability that defines Crackling Drake’s power works in all zones." + } + ], + "scryfallId":"f00fa3a7-e3e2-4b23-a126-a076e75b5dbd", + "scryfallIllustrationId":"bb4d6109-3558-4c56-bd8a-f0d0d5e930ca", + "scryfallOracleId":"c8b4eba6-88ce-42d7-a235-5a4765a4a981", + "subtypes":[ + "Drake" + ], + "supertypes":[ + + ], + "tcgplayerProductId":176831, + "text":"Flying\nCrackling Drake's power is equal to the total number of instant and sorcery cards you own in exile and in your graveyard.\nWhen Crackling Drake enters the battlefield, draw a card.", + "toughness":"4", + "type":"Creature — Drake", + "types":[ + "Creature" + ], + "uuid":"1fa4225b-a768-5c4a-a73e-4bc85073a3f0", + "watermark":"izzet" + }, + { + "artist":"Mark Winters", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":4.0, + "count":2, + "edhrecRank":1089, + "flavorText":"Rumors float through the city like crows, alighting on citizens seemingly at random.", + "foreignData":[ + { + "flavorText":"Gerüchte flattern wie Krähen durch die Stadt und landen scheinbar zufällig auf den Bürgern.", + "language":"German", + "multiverseId":453054, + "name":"Murmelnder Mystiker", + "text":"Immer wenn du einen Spontanzauber oder eine Hexerei wirkst, erzeuge einen 1/1 blauen Vogel-Illusion-Kreaturenspielstein mit Flugfähigkeit.", + "type":"Kreatur — Mensch, Zauberer" + }, + { + "flavorText":"Los rumores revolotean por la ciudad como cuervos, posándose sobre algunos ciudadanos al azar... o eso parece.", + "language":"Spanish", + "multiverseId":453313, + "name":"Místico murmurador", + "text":"Siempre que lances un hechizo de instantáneo o de conjuro, crea una ficha de criatura Ilusión Ave azul 1/1 con la habilidad de volar.", + "type":"Criatura — Hechicero humano" + }, + { + "flavorText":"Les rumeurs circulent en ville telles des nuées de corbeaux, se posant apparemment au hasard sur les citoyens.", + "language":"French", + "multiverseId":453572, + "name":"Mystique bruissant", + "text":"À chaque fois que vous lancez un sort d'éphémère ou de rituel, créez un jeton de créature 1/1 bleue Oiseau et Illusion avec le vol.", + "type":"Créature : humain et sorcier" + }, + { + "flavorText":"Le dicerie fluttuano attraverso la città come corvi, posandosi sui cittadini in modo apparentemente casuale.", + "language":"Italian", + "multiverseId":453831, + "name":"Mistico Mormorante", + "text":"Ogniqualvolta lanci una magia istantaneo o stregoneria, crea una pedina creatura Illusione Uccello 1/1 blu con volare.", + "type":"Creatura — Mago Umano" + }, + { + "flavorText":"噂はカラスのように街中を舞い、見たところ無作為な市民の上に降りた。", + "language":"Japanese", + "multiverseId":454090, + "name":"つぶやく神秘家", + "text":"あなたがインスタントかソーサリーである呪文を唱えるたび、飛行を持つ青の1/1の鳥・イリュージョン・クリーチャー・トークンを1体生成する。", + "type":"クリーチャー — 人間・ウィザード" + }, + { + "flavorText":"소문은 까마귀처럼 도시를 떠다니며, 무작위로 고른 것처럼 시민들 위에 내려앉는다.", + "language":"Korean", + "multiverseId":454349, + "name":"속삭이는 신비주의자", + "text":"당신이 순간마법 또는 집중마법 주문을 발동할 때마다, 비행을 가진 1/1 청색 조류 환영 생물 토큰 한 개를 만든다.", + "type":"생물 — 인간 마법사" + }, + { + "flavorText":"Os rumores flutuam pela cidade como corvos, aludindo a cidadãos com aparente aleatoriedade.", + "language":"Portuguese (Brazil)", + "multiverseId":454608, + "name":"Místico dos Murmúrios", + "text":"Toda vez que você conjurar uma mágica instantânea ou um feitiço, crie uma ficha de criatura azul 1/1 do tipo Ave Ilusão com voar.", + "type":"Criatura — Humano Mago" + }, + { + "flavorText":"Слухи кружат над городом, словно вороны, и садятся на горожан как будто случайно.", + "language":"Russian", + "multiverseId":454867, + "name":"Шепчущий Мистик", + "text":"Каждый раз, когда вы разыгрываете мгновенное заклинание или заклинание волшебства, создайте одну фишку существа 1/1 синяя Птица Иллюзия с Полетом.", + "type":"Существо — Человек Чародей" + }, + { + "flavorText":"谣言如鸦群飞舞城中,看似不经意间就停在市民肩头。", + "language":"Chinese Simplified", + "multiverseId":455126, + "name":"细语秘教徒", + "text":"每当你施放瞬间或法术咒语时,派出一个1/1蓝色,具飞行异能的鸟/虚影衍生生物。", + "type":"生物 ~人类/法术师" + }, + { + "flavorText":"謠言如鴉群飛舞城中,看似不經意間就停在市民肩頭。", + "language":"Chinese Traditional", + "multiverseId":455385, + "name":"細語秘教徒", + "text":"每當你施放瞬間或巫術咒語時,派出一個1/1藍色,具飛行異能的鳥/虛影衍生生物。", + "type":"生物 ~人類/魔法師" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{3}{U}", + "mcmId":363622, + "mcmMetaId":265104, + "mtgArenaId":68506, + "mtgoId":69463, + "multiverseId":452795, + "name":"Murmuring Mystic", + "names":[ + + ], + "number":"45", + "originalText":"Whenever you cast an instant or sorcery spell, create a 1/1 blue Bird Illusion creature token with flying.", + "originalType":"Creature — Human Wizard", + "power":"1", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.26 + }, + "paper":{ + "2020-04-26":0.22 + }, + "paperFoil":{ + "2020-04-26":1.39 + } + }, + "printings":[ + "C20", + "GRN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/1ef3b76f2672d18e", + "tcgplayer":"https://mtgjson.com/links/4ddc115402018627" + }, + "rarity":"uncommon", + "rulings":[ + { + "date":"2018-10-05", + "text":"Murmuring Mystic’s triggered ability resolves before the spell that caused it to trigger. It resolves even if that spell is countered." + } + ], + "scryfallId":"5fc6adff-dcb3-456d-a8c2-0e77b784ff89", + "scryfallIllustrationId":"c4773a04-1364-4dd6-a62b-07da23a61a35", + "scryfallOracleId":"dcd4da46-5438-4454-8b1b-43ca51bda1f9", + "subtypes":[ + "Human", + "Wizard" + ], + "supertypes":[ + + ], + "tcgplayerProductId":175547, + "text":"Whenever you cast an instant or sorcery spell, create a 1/1 blue Bird Illusion creature token with flying.", + "toughness":"5", + "type":"Creature — Human Wizard", + "types":[ + "Creature" + ], + "uuid":"3e4780a3-f606-5424-818b-fe3fd32ee490" + }, + { + "artist":"Slawomir Maniak", + "borderColor":"black", + "colorIdentity":[ + "R" + ], + "colors":[ + "R" + ], + "convertedManaCost":4.0, + "count":1, + "edhrecRank":10188, + "flavorText":"Some storms never blow over.", + "foreignData":[ + { + "flavorText":"Einige Stürme ziehen nie vorüber.", + "language":"German", + "multiverseId":453100, + "name":"Lichtbogen-Phoenix", + "text":"Fliegend, Eile\nZu Beginn des Kampfes in deinem Zug und falls du in diesem Zug drei oder mehr Spontanzauber und/oder Hexereien gewirkt hast, bringe den Lichtbogen-Phoenix aus deinem Friedhof ins Spiel zurück.", + "type":"Kreatur — Phoenix" + }, + { + "flavorText":"Algunas tormentas nunca amainan.", + "language":"Spanish", + "multiverseId":453359, + "name":"Fénix arcobrillante", + "text":"Vuela, prisa.\nAl comienzo del combate en tu turno, si lanzaste tres o más hechizos de instantáneo y/o de conjuro este turno, regresa el Fénix arcobrillante de tu cementerio al campo de batalla.", + "type":"Criatura — Fénix" + }, + { + "flavorText":"Certains orages ne se dissipent jamais.", + "language":"French", + "multiverseId":453618, + "name":"Phénix d'arc", + "text":"Vol, célérité\nAu début du combat pendant votre tour, si vous avez lancé au moins trois sorts d'éphémère et/ou de rituel ce tour-ci, renvoyez le Phénix d'arc sur le champ de bataille depuis votre cimetière.", + "type":"Créature : phénix" + }, + { + "flavorText":"Alcune tempeste non si placano mai.", + "language":"Italian", + "multiverseId":453877, + "name":"Fenice dell'Arcolampo", + "text":"Volare, rapidità\nAll'inizio del combattimento nel tuo turno, se hai lanciato tre o più magie istantaneo e/o stregoneria in questo turno, rimetti sul campo di battaglia la Fenice dell'Arcolampo dal tuo cimitero.", + "type":"Creatura — Fenice" + }, + { + "flavorText":"吹き止まない嵐もある。", + "language":"Japanese", + "multiverseId":454136, + "name":"弧光のフェニックス", + "text":"飛行、速攻\nあなたのターンの戦闘の開始時に、このターンにあなたがインスタントかソーサリーである呪文を3つ以上唱えていた場合、あなたの墓地から弧光のフェニックスを戦場に戻す。", + "type":"クリーチャー — フェニックス" + }, + { + "flavorText":"어떤 폭풍은 절대 사그라들지 않는다.", + "language":"Korean", + "multiverseId":454395, + "name":"아크불빛 불사조", + "text":"비행, 신속\n당신의 턴 전투시작에, 당신이 이번 턴에 순간마법 및/또는 집중마법 주문을 세 개 이상 발동했다면, 당신의 무덤에서 아크불빛 불사조를 전장으로 되돌린다.", + "type":"생물 — 불사조" + }, + { + "flavorText":"Algumas tempestades jamais se acalmam.", + "language":"Portuguese (Brazil)", + "multiverseId":454654, + "name":"Fênix Arco-lume", + "text":"Voar, ímpeto\nNo início do combate no seu turno, se você conjurou três ou mais mágicas instantâneas e/ou feitiços neste turno, devolva Fênix Arco-lume de seu cemitério para o campo de batalha.", + "type":"Criatura — Fênix" + }, + { + "flavorText":"Некоторые бури никогда не утихают.", + "language":"Russian", + "multiverseId":454913, + "name":"Феникс Дуги Света", + "text":"Полет, Ускорение\nВ начале боя во время вашего хода, если вы разыграли не менее трех мгновенных заклинаний и (или) заклинаний волшебства в этом ходу, верните Феникса Дуги Света из вашего кладбища на поле битвы.", + "type":"Существо — Феникс" + }, + { + "flavorText":"某些风暴永不停歇。", + "language":"Chinese Simplified", + "multiverseId":455172, + "name":"弧光凤凰", + "text":"飞行,敏捷\n在你回合的战斗开始时,若你本回合中施放过三个或更多瞬间与法术咒语,则将弧光凤凰从你的坟墓场移回战场。", + "type":"生物 ~凤凰" + }, + { + "flavorText":"某些風暴永不停歇。", + "language":"Chinese Traditional", + "multiverseId":455431, + "name":"弧光鳳凰", + "text":"飛行,敏捷\n在你回合的戰鬥開始時,若你本回合中施放過三個或更多瞬間與巫術咒語,則將弧光鳳凰從你的墳墓場移回戰場。", + "type":"生物 ~鳳凰" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{3}{R}", + "mcmId":364116, + "mcmMetaId":265344, + "mtgArenaId":68552, + "mtgoId":69555, + "multiverseId":452841, + "name":"Arclight Phoenix", + "number":"91", + "originalText":"Flying, haste\nAt the beginning of combat on your turn, if you've cast three or more instant and sorcery spells this turn, return Arclight Phoenix from your graveyard to the battlefield.", + "originalType":"Creature — Phoenix", + "power":"3", + "prices":{ + "mtgo":{ + "2020-04-27":5.42 + }, + "mtgoFoil":{ + "2020-04-27":15.11 + }, + "paper":{ + "2020-04-26":4.85 + }, + "paperFoil":{ + "2020-04-26":17.68 + } + }, + "printings":[ + "GRN", + "PGRN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/a5d0dc50682bd224", + "tcgplayer":"https://mtgjson.com/links/4dcdb2deb277620c" + }, + "rarity":"mythic", + "rulings":[ + { + "date":"2018-10-05", + "text":"Because the beginning of combat step is before attackers are declared, you can attack with Arclight Phoenix during the same combat it returns to the battlefield." + } + ], + "scryfallId":"787de9ce-02c5-4a17-a88b-d38e83dbeb0b", + "scryfallIllustrationId":"0aa75a78-f9fe-438a-b0a3-72c017e1d61d", + "scryfallOracleId":"a80ea391-54d1-4b61-95a3-f8deb4cbe842", + "subtypes":[ + "Phoenix" + ], + "supertypes":[ + + ], + "tcgplayerProductId":176685, + "text":"Flying, haste\nAt the beginning of combat on your turn, if you've cast three or more instant and sorcery spells this turn, return Arclight Phoenix from your graveyard to the battlefield.", + "toughness":"2", + "type":"Creature — Phoenix", + "types":[ + "Creature" + ], + "uuid":"08b27673-d7f7-521a-bec1-8cd6f2b4f88d" + }, + { + "artist":"Svetlin Velinov", + "borderColor":"black", + "colorIdentity":[ + "R", + "U" + ], + "colors":[ + "R", + "U" + ], + "convertedManaCost":6.0, + "count":2, + "edhrecRank":615, + "foreignData":[ + { + "language":"German", + "multiverseId":453201, + "name":"Niv-Mizzet der Parun", + "text":"Dieser Zauberspruch kann nicht neutralisiert werden.\nFliegend\nImmer wenn du eine Karte ziehst, fügt Niv-Mizzet der Parun einem Ziel deiner Wahl 1 Schadenspunkt zu.\nImmer wenn ein Spieler einen Spontanzauber oder eine Hexerei wirkt, ziehst du eine Karte.", + "type":"Legendäre Kreatur — Drache, Zauberer" + }, + { + "language":"Spanish", + "multiverseId":453460, + "name":"Niv-Mízzet, parun", + "text":"Este hechizo no puede ser contrarrestado.\nVuela.\nSiempre que robes una carta, Niv-Mízzet, parun hace 1 punto de daño a cualquier objetivo.\nSiempre que un jugador lance un hechizo de instantáneo o de conjuro, tú robas una carta.", + "type":"Criatura legendaria — Hechicero dragón" + }, + { + "language":"French", + "multiverseId":453719, + "name":"Niv-Mizzet, Parun", + "text":"Ce sort ne peut pas être contrecarré.\nVol\nÀ chaque fois que vous piochez une carte, Niv-Mizzet, Parun inflige 1 blessure à n'importe quelle cible.\nÀ chaque fois qu'un joueur lance un sort d'éphémère ou de rituel, vous piochez une carte.", + "type":"Créature légendaire : dragon et sorcier" + }, + { + "language":"Italian", + "multiverseId":453978, + "name":"Niv-Mizzet, il Parun", + "text":"Questa magia non può essere neutralizzata.\nVolare\nOgniqualvolta peschi una carta, Niv-Mizzet, il Parun infligge 1 danno a un qualsiasi bersaglio.\nOgniqualvolta un giocatore lancia una magia istantaneo o stregoneria, tu peschi una carta.", + "type":"Creatura Leggendaria — Mago Drago" + }, + { + "language":"Japanese", + "multiverseId":454237, + "name":"パルン、ニヴ=ミゼット", + "text":"この呪文は打ち消されない。\n飛行\nあなたがカードを1枚引くたび、クリーチャー1体かプレインズウォーカー1体かプレイヤー1人を対象とする。パルン、ニヴ=ミゼットはそれに1点のダメージを与える。\nプレイヤーがインスタントかソーサリーである呪文を唱えるたび、あなたはカードを1枚引く。", + "type":"伝説のクリーチャー — ドラゴン・ウィザード" + }, + { + "language":"Korean", + "multiverseId":454496, + "name":"페이런, 니브-미젯", + "text":"이 주문은 무효화될 수 없다.\n비행\n당신이 카드를 뽑을 때마다, 원하는 목표를 정한다. 페이런, 니브-미젯은 그 목표에게 피해 1점을 입힌다.\n플레이어가 순간마법 또는 집중마법 주문을 발동할 때마다, 당신은 카드 한 장을 뽑는다.", + "type":"전설적 생물 — 용 마법사" + }, + { + "language":"Portuguese (Brazil)", + "multiverseId":454755, + "name":"Niv-Mizzet, Parun", + "text":"Esta mágica não pode ser anulada.\nVoar\nToda vez que você compra um card, Niv-Mizzet, Parun, causa 1 ponto de dano a qualquer alvo.\nToda vez que um jogador conjura uma mágica instantânea ou um feitiço, você compra um card.", + "type":"Criatura Lendária — Dragão Mago" + }, + { + "language":"Russian", + "multiverseId":455014, + "name":"Нив-Миззет, Отец", + "text":"Это заклинание не может быть отменено.\nПолет\nКаждый раз, когда вы берете карту, Нив-Миззет, Отец наносит 1 повреждение любой цели.\nКаждый раз, когда игрок разыгрывает мгновенное заклинание или заклинание волшебства, вы берете карту.", + "type":"Легендарное Существо — Дракон Чародей" + }, + { + "language":"Chinese Simplified", + "multiverseId":455273, + "name":"元祖尼米捷", + "text":"此咒语不能被反击。\n飞行\n每当你抓一张牌时,元祖尼米捷对任意一个目标造成1点伤害。\n每当任一牌手施放瞬间或法术咒语时,你抓一张牌。", + "type":"传奇生物 ~龙/法术师" + }, + { + "language":"Chinese Traditional", + "multiverseId":455532, + "name":"元祖尼米捷", + "text":"此咒語不能被反擊。\n飛行\n每當你抽一張牌時,元祖尼米捷對任意一個目標造成1點傷害。\n每當任一玩家施放瞬間或巫術咒語時,你抽一張牌。", + "type":"傳奇生物 ~龍/魔法師" + } + ], + "frameEffect":"legendary", + "frameEffects":[ + "legendary" + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "leadershipSkills":{ + "brawl":false, + "commander":true, + "oathbreaker":false + }, + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{U}{U}{U}{R}{R}{R}", + "mcmId":363850, + "mcmMetaId":265254, + "mtgArenaId":68653, + "mtgoId":69757, + "multiverseId":452942, + "name":"Niv-Mizzet, Parun", + "number":"192", + "originalText":"This spell can't be countered.\nFlying\nWhenever you draw a card, Niv-Mizzet, Parun deals 1 damage to any target.\nWhenever a player casts an instant or sorcery spell, you draw a card.", + "originalType":"Legendary Creature — Dragon Wizard", + "power":"5", + "prices":{ + "mtgo":{ + "2020-04-27":0.44 + }, + "mtgoFoil":{ + "2020-04-27":0.63 + }, + "paper":{ + "2020-04-26":1.86 + }, + "paperFoil":{ + "2020-04-26":7.73 + } + }, + "printings":[ + "GRN", + "PGRN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/e931e564837348ab", + "tcgplayer":"https://mtgjson.com/links/57f893e2db7e1878" + }, + "rarity":"rare", + "rulings":[ + { + "date":"2018-10-05", + "text":"If an effect instructs you to draw multiple cards, Niv-Mizzet’s first triggered ability triggers that many times. You choose targets for those abilities after you’ve drawn all of the cards." + }, + { + "date":"2018-10-05", + "text":"If a spell or ability causes you to put cards into your hand without specifically using the word “draw,” Niv-Mizzet’s first triggered ability won’t trigger." + }, + { + "date":"2018-10-05", + "text":"Niv-Mizzet’s second triggered ability resolves before the spell that caused it to trigger. It resolves even if that spell is countered. This causes its first triggered ability to trigger, and that also resolves before the spell." + }, + { + "date":"2018-10-05", + "text":"Players can cast spells and activate abilities after Niv-Mizzet’s second triggered ability resolves but before the spell that caused it to trigger does. Notably, the card you draw may be able to counter that spell." + } + ], + "scryfallId":"6f3d2dc5-7b9d-4af6-9f3b-4de90fbf63c9", + "scryfallIllustrationId":"e0137c61-511d-4a18-91d4-cac4437f8692", + "scryfallOracleId":"33666a98-812f-4892-9f8d-33e0cbecc340", + "subtypes":[ + "Dragon", + "Wizard" + ], + "supertypes":[ + "Legendary" + ], + "tcgplayerProductId":175582, + "text":"This spell can't be countered.\nFlying\nWhenever you draw a card, Niv-Mizzet, Parun deals 1 damage to any target.\nWhenever a player casts an instant or sorcery spell, you draw a card.", + "toughness":"5", + "type":"Legendary Creature — Dragon Wizard", + "types":[ + "Creature" + ], + "uuid":"11c1c146-ba7f-50d9-9122-2dce74640e92", + "watermark":"izzet" + }, + { + "artist":"James Ryman", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":2.0, + "count":4, + "edhrecRank":1999, + "flavorText":"While other pirates prowl for treasure, Captain Parrish plunders secrets.", + "foreignData":[ + { + "flavorText":"Während andere Piraten nach Schätzen suchen, verlangt es Kapitän Parrish nur nach Geheimnissen.", + "language":"German", + "multiverseId":435499, + "name":"Den Kurs festlegen", + "text":"Ziehe zwei Karten. Wirf dann eine Karte ab, es sei denn, du hast in diesem Zug mit mindestens einer Kreatur angegriffen.", + "type":"Hexerei" + }, + { + "flavorText":"Mientras que otros piratas van por ahí buscando tesoros, la capitana Parrish descubre secretos.", + "language":"Spanish", + "multiverseId":437592, + "name":"Trazar la ruta", + "text":"Roba dos cartas. Luego descarta una carta a menos que hayas atacado con una criatura este turno.", + "type":"Conjuro" + }, + { + "flavorText":"Tandis que les autres pirates cherchent des trésors, la capitaine Parrish s'empare de secrets.", + "language":"French", + "multiverseId":435798, + "name":"Tracer un cap", + "text":"Piochez deux cartes. Ensuite, défaussez-vous d'une carte à moins que vous n'ayez attaqué avec une créature ce tour-ci.", + "type":"Rituel" + }, + { + "flavorText":"Mentre altri pirati razziano tesori, la Capitana Parrish saccheggia segreti.", + "language":"Italian", + "multiverseId":436097, + "name":"Tracciare la Rotta", + "text":"Pesca due carte. Poi scarta una carta a meno che tu non abbia attaccato con una creatura in questo turno.", + "type":"Stregoneria" + }, + { + "flavorText":"他の海賊が宝物を探している間に、船長パリッシュは秘密を略奪する。", + "language":"Japanese", + "multiverseId":436396, + "name":"航路の作成", + "text":"カードを2枚引く。その後、このターンにあなたがクリーチャーで攻撃していないかぎりカード1枚を捨てる。", + "type":"ソーサリー" + }, + { + "flavorText":"다른 해적들이 보물을 찾아 돌아다니는 동안, 패리시 선장은 비밀을 약탈한다.", + "language":"Korean", + "multiverseId":436695, + "name":"진로 계획", + "text":"카드 두 장을 뽑는다. 그 후 당신이 이 턴에 생물로 공격하지 않았다면 카드 한 장을 버린다.", + "type":"집중마법" + }, + { + "flavorText":"Enquanto outros piratas espreitam tesouros, a Capitã Parrish saqueia segredos.", + "language":"Portuguese (Brazil)", + "multiverseId":436994, + "name":"Traçar uma Rota", + "text":"Compre dois cards. Em seguida, descarte um card, a menos que tenha atacado com uma criatura neste turno.", + "type":"Feitiço" + }, + { + "flavorText":"Остальные пираты охотятся за сокровищами, а капитан Пэрриш похищает секреты.", + "language":"Russian", + "multiverseId":437293, + "name":"Проложить Курс", + "text":"Возьмите две карты. Затем сбросьте карту, если только вы не атаковали существом в этом ходу.", + "type":"Волшебство" + }, + { + "flavorText":"其他海盗看上的是财宝,芭丽许船长盯上的是秘密。", + "language":"Chinese Simplified", + "multiverseId":437891, + "name":"规划航路", + "text":"抓两张牌。然后除非你于本回合中曾以生物攻击,否则弃一张牌。", + "type":"法术" + }, + { + "flavorText":"其他海盜看上的是財寶,芭麗許船長盯上的是秘密。", + "language":"Chinese Traditional", + "multiverseId":438190, + "name":"規劃航路", + "text":"抽兩張牌。然後除非你於本回合中曾以生物攻擊,否則棄一張牌。", + "type":"巫術" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "commander":"Legal", + "duel":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pioneer":"Legal", + "vintage":"Legal" + }, + "manaCost":"{1}{U}", + "mcmId":301397, + "mcmMetaId":226886, + "mtgArenaId":66057, + "mtgoFoilId":65109, + "mtgoId":65108, + "multiverseId":435200, + "name":"Chart a Course", + "number":"48", + "originalText":"Draw two cards. Then discard a card unless you attacked with a creature this turn.", + "originalType":"Sorcery", + "prices":{ + "mtgo":{ + "2020-04-27":0.04 + }, + "mtgoFoil":{ + "2020-04-27":0.29 + }, + "paper":{ + "2020-04-26":0.54 + }, + "paperFoil":{ + "2020-04-26":2.9 + } + }, + "printings":[ + "MB1", + "XLN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/447f30cfc744ca18", + "tcgplayer":"https://mtgjson.com/links/3dc06685eb05a015" + }, + "rarity":"uncommon", + "rulings":[ + + ], + "scryfallId":"98291778-2ec2-47e2-ac99-5f8cfbb3cf24", + "scryfallIllustrationId":"66a9bdee-5088-4f19-94cf-1e436f5e5f6a", + "scryfallOracleId":"05878e49-93ad-4144-9c50-a0bb86126c2e", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":145262, + "text":"Draw two cards. Then discard a card unless you attacked this turn.", + "type":"Sorcery", + "types":[ + "Sorcery" + ], + "uuid":"97f24294-bfb0-5776-9133-c96b7193ed1f" + }, + { + "artist":"Wesley Burt", + "borderColor":"black", + "colorIdentity":[ + "R" + ], + "colors":[ + "R" + ], + "convertedManaCost":2.0, + "count":4, + "edhrecRank":6245, + "flavorText":"\"Commander, we caught the Dimir spy and took her ashes into custody.\"\n—Kramm, Wojek security officer", + "foreignData":[ + { + "flavorText":"„Kommandeur, wir haben die Dimir-Spionin gefasst und ihre Asche in Gewahrsam genommen.\"\n—Kramm, Wojek-Sicherheitsoffizier", + "language":"German", + "multiverseId":453117, + "name":"Lavaschlinge", + "text":"Die Lavaschlinge fügt einer Kreatur deiner Wahl 4 Schadenspunkte zu. Falls die Kreatur in diesem Zug sterben würde, schicke sie stattdessen ins Exil.", + "type":"Hexerei" + }, + { + "flavorText":"\"Comandante, descubrimos a la espía dimir y pusimos bajo custodia sus cenizas\".\n—Kramm, guarda de seguridad wojek", + "language":"Spanish", + "multiverseId":453376, + "name":"Espiral de lava", + "text":"La Espiral de lava hace 4 puntos de daño a la criatura objetivo. Si esa criatura fuese a morir este turno, en vez de eso, exíliala.", + "type":"Conjuro" + }, + { + "flavorText":"« Commandant, nous avons attrapé l'espionne de Dimir et avons mis ses cendres en état d'arrestation. »\n—Kramm, officier de sécurité wojek", + "language":"French", + "multiverseId":453635, + "name":"Étreinte de lave", + "text":"L'Étreinte de lave inflige 4 blessures à une créature ciblée. Si cette créature devait mourir ce tour-ci, exilez-la à la place.", + "type":"Rituel" + }, + { + "flavorText":"\"Comandante, abbiamo catturato la spia Dimir e messo in stato di fermo le sue ceneri.\"\n—Kramm, agente di sicurezza Wojek", + "language":"Italian", + "multiverseId":453894, + "name":"Spira di Lava", + "text":"La Spira di Lava infligge 4 danni a una creatura bersaglio. Se quella creatura sta per morire in questo turno, invece esiliala.", + "type":"Stregoneria" + }, + { + "flavorText":"「司令官、ディミーアのスパイを捕らえ、その遺灰を監禁しました。」\n――ウォジェクの警備兵、クラム", + "language":"Japanese", + "multiverseId":454153, + "name":"溶岩コイル", + "text":"クリーチャー1体を対象とする。溶岩コイルはそれに4点のダメージを与える。このターン、そのクリーチャーが死亡するなら、代わりにそれを追放する。", + "type":"ソーサリー" + }, + { + "flavorText":"\"사령관님, 디미르 스파이를 발견해 그녀의 잿더미를 구속했습니다.\"\n—워젝 보안 장교, 크람", + "language":"Korean", + "multiverseId":454412, + "name":"용암 고리", + "text":"생물을 목표로 정한다. 용암 고리는 그 생물에게 피해 4점을 입힌다. 그 생물이 이 턴에 죽게 된다면, 대신 추방한다.", + "type":"집중마법" + }, + { + "flavorText":"\"Comandante, capturamos uma espiã Dimir. Estamos com as cinzas dela sob custódia.\"\n— Kramm, oficial de segurança de Wojek", + "language":"Portuguese (Brazil)", + "multiverseId":454671, + "name":"Hélice de Lava", + "text":"Hélice de Lava causa 4 pontos de dano à criatura alvo. Se aquela criatura for morrer neste turno, em vez disso, exile-a.", + "type":"Feitiço" + }, + { + "flavorText":"«Командир, мы поймали димирскую шпионку и взяли ее пепел под стражу».\n— Крамм, специалист по безопасности Войеков", + "language":"Russian", + "multiverseId":454930, + "name":"Спираль Лавы", + "text":"Спираль Лавы наносит 4 повреждения целевому существу. Если то существо должно умереть в этом ходу, изгоните его вместо этого.", + "type":"Волшебство" + }, + { + "flavorText":"「指挥官,我们逮到了底密尔间谍,她的灰烬已经收监。」\n~沃耶克警卫克拉姆", + "language":"Chinese Simplified", + "multiverseId":455189, + "name":"熔岩缠绕", + "text":"熔岩缠绕对目标生物造成4点伤害。如果本回合中该生物将死去,则改为将它放逐。", + "type":"法术" + }, + { + "flavorText":"「指揮官,我們逮到了底密爾間諜,她的灰燼已經收監。」\n~沃耶克警衛克拉姆", + "language":"Chinese Traditional", + "multiverseId":455448, + "name":"熔岩纏繞", + "text":"熔岩纏繞對目標生物造成4點傷害。如果本回合中該生物將死去,則改為將它放逐。", + "type":"巫術" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{1}{R}", + "mcmId":364055, + "mcmMetaId":265316, + "mtgArenaId":68569, + "mtgoId":69589, + "multiverseId":452858, + "name":"Lava Coil", + "number":"108", + "originalText":"Lava Coil deals 4 damage to target creature. If that creature would die this turn, exile it instead.", + "originalType":"Sorcery", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.39 + }, + "paper":{ + "2020-04-26":0.45 + }, + "paperFoil":{ + "2020-04-26":2.55 + } + }, + "printings":[ + "GRN", + "PMEI" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/d40e41cf1b90c854", + "tcgplayer":"https://mtgjson.com/links/a6d7abc427b0dfbb" + }, + "rarity":"uncommon", + "rulings":[ + { + "date":"2018-10-05", + "text":"Lava Coil’s replacement effect will exile the target creature if it would die this turn for any reason, not just due to lethal damage. It applies to the target creature even if Lava Coil deals no damage to it (due to a prevention effect)." + } + ], + "scryfallId":"f6135cf4-50c2-4e44-b9b0-96e1187a2200", + "scryfallIllustrationId":"8ccebed1-8cd9-4468-80ac-37a35cf24933", + "scryfallOracleId":"fa71db44-5181-4c51-8b24-7fbedf36e3ca", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":176562, + "text":"Lava Coil deals 4 damage to target creature. If that creature would die this turn, exile it instead.", + "type":"Sorcery", + "types":[ + "Sorcery" + ], + "uuid":"ebbc4b2b-df49-53fb-98cf-2a6b71af728d" + }, + { + "artist":"Titus Lunter", + "borderColor":"black", + "colorIdentity":[ + "R", + "U" + ], + "colors":[ + "R", + "U" + ], + "convertedManaCost":3.0, + "count":1, + "edhrecRank":4282, + "foreignData":[ + { + "language":"German", + "multiverseId":453163, + "name":"Signalturm-Blitz", + "text":"Der Signalturm-Blitz fügt einer Kreatur deiner Wahl Schaden in Höhe der Gesamtanzahl an Spontanzauber- und Hexerei-Karten zu, die du besitzt und die sich im Exil oder in deinem Friedhof befinden.\nKatalyse (Du kannst diese Karte aus deinem Friedhof wirken, indem du zusätzlich zu ihren anderen Kosten eine Karte abwirfst. Schicke sie danach ins Exil.)", + "type":"Hexerei" + }, + { + "language":"Spanish", + "multiverseId":453422, + "name":"Rayo de la atalaya", + "text":"El Rayo de la atalaya hace una cantidad de daño a la criatura objetivo igual a la cantidad total de cartas de instantáneo y de conjuro en tu cementerio y en el exilio de las cuales eres propietario.\nRecargar. (Puedes lanzar esta carta desde tu cementerio descartando una carta además de pagar sus otros costes. Luego, exilia esta carta.)", + "type":"Conjuro" + }, + { + "language":"French", + "multiverseId":453681, + "name":"Éclair du flambeau", + "text":"L'Éclair du flambeau inflige à une créature ciblée un nombre de blessures égal au nombre total de cartes d'éphémère et de rituel que vous possédez en exil et dans votre cimetière.\nRelancez (Vous pouvez lancer cette carte depuis votre cimetière en vous défaussant d'une carte en plus de payer ses autres coûts. Puis exilez cette carte.)", + "type":"Rituel" + }, + { + "language":"Italian", + "multiverseId":453940, + "name":"Saetta del Faro", + "text":"La Saetta del Faro infligge a una creatura bersaglio danno pari al numero totale di carte istantaneo e stregoneria che possiedi in esilio e nel tuo cimitero.\nCarica d'avvio (Puoi lanciare questa carta dal tuo cimitero scartando una carta oltre a pagare i suoi altri costi. Poi esilia questa carta.)", + "type":"Stregoneria" + }, + { + "language":"Japanese", + "multiverseId":454199, + "name":"標の稲妻", + "text":"クリーチャー1体を対象とする。標の稲妻はそれに、追放領域かあなたの墓地にあり、あなたがオーナーであり、インスタントかソーサリーであるカードの枚数に等しい点数のダメージを与える。\n再活(あなたはあなたの墓地から、このカードを、これの他のコストの支払いに加えてカード1枚を捨てることで唱えてもよい。その後、このカードを追放する。)", + "type":"ソーサリー" + }, + { + "language":"Korean", + "multiverseId":454458, + "name":"봉화의 번개", + "text":"생물을 목표로 정한다. 봉화의 번개는 그 생물에게 추방 영역과 당신의 무덤에 있는 당신이 소유한 순간마법과 집중마법 카드 수의 합만큼 피해를 입힌다.\n강제 시동 (당신은 이 카드의 다른 비용들을 지불하는 것에 추가로 카드를 한 장 버리는 것으로 이 카드를 당신의 무덤에서 발동할 수 있다. 그 후 이 카드를 추방한다.)", + "type":"집중마법" + }, + { + "language":"Portuguese (Brazil)", + "multiverseId":454717, + "name":"Raio do Farol", + "text":"Raio do Farol causa à criatura alvo dano igual ao número total de cards de mágica instantânea ou feitiço que você possui no exílio e em seu cemitério.\nRecarregar (Você pode conjurar este card de seu cemitério descartando um card além de pagar seus outros custos. Depois, exile este card.)", + "type":"Feitiço" + }, + { + "language":"Russian", + "multiverseId":454976, + "name":"Сигнальный Разряд", + "text":"Сигнальный Разряд наносит целевому существу повреждения, равные суммарному количеству принадлежащих вам карт мгновенных заклинаний и волшебства в изгнании и на вашем кладбище.\nИмпульс (Вы можете разыграть эту карту из вашего кладбища, сбросив карту в дополнение к оплате других ее стоимостей. Затем изгоните эту карту.)", + "type":"Волшебство" + }, + { + "language":"Chinese Simplified", + "multiverseId":455235, + "name":"信标电击", + "text":"信标电击对目标生物造成伤害,其数量等同于放逐区中由你拥有之瞬间与法术牌数量和你坟墓场中这两类牌数量的加总。\n再起(你可以从你的坟墓场施放此牌,但必须支付其所需费用并额外弃一张牌。然后放逐此牌。)", + "type":"法术" + }, + { + "language":"Chinese Traditional", + "multiverseId":455494, + "name":"信標電擊", + "text":"信標電擊對目標生物造成傷害,其數量等同於放逐區中由你擁有之瞬間與巫術牌數量和你墳墓場中這兩類牌數量的加總。\n再起(你可以從你的墳墓場施放此牌,但必須支付其所需費用並額外棄一張牌。然後放逐此牌。)", + "type":"巫術" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{1}{U}{R}", + "mcmId":364198, + "mcmMetaId":265406, + "mtgArenaId":68615, + "mtgoId":69681, + "multiverseId":452904, + "name":"Beacon Bolt", + "number":"154", + "originalText":"Beacon Bolt deals damage to target creature equal to the total number of instant and sorcery cards you own in exile and in your graveyard.\nJump-start (You may cast this card from your graveyard by discarding a card in addition to paying its other costs. Then exile this card.)", + "originalType":"Sorcery", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.26 + }, + "paper":{ + "2020-04-26":0.18 + }, + "paperFoil":{ + "2020-04-26":0.44 + } + }, + "printings":[ + "GRN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/12cf454f26e64b85", + "tcgplayer":"https://mtgjson.com/links/6990423345ff57a8" + }, + "rarity":"uncommon", + "rulings":[ + { + "date":"2018-10-05", + "text":"Beacon Bolt is still on the stack while you count your instant and sorcery cards in your graveyard and in exile. It doesn’t count itself." + }, + { + "date":"2018-10-05", + "text":"If any exiled cards you own are face down, they have no characteristics. If they’re normally instants or sorceries, they won’t be counted." + }, + { + "date":"2018-10-05", + "text":"You must still follow any timing restrictions and permissions, including those based on the card’s type. For instance, you can cast a sorcery using jump-start only when you could normally cast a sorcery." + }, + { + "date":"2018-10-05", + "text":"A spell cast using jump-start will always be exiled afterward, whether it resolves, it’s countered, or it leaves the stack in some other way." + }, + { + "date":"2018-10-05", + "text":"If an effect allows you to pay an alternative cost rather than a spell’s mana cost, you may pay that alternative cost when you jump-start a spell. You’ll still discard a card as an additional cost to cast it." + }, + { + "date":"2018-10-05", + "text":"If a card with jump-start is put into your graveyard during your turn, you’ll be able to cast it right away if it’s legal to do so, before an opponent can take any actions." + } + ], + "scryfallId":"39c9e4b5-364b-4c0b-bb47-266563a6abf2", + "scryfallIllustrationId":"c2ce83a1-ddba-429c-9f7b-7e02585ea99d", + "scryfallOracleId":"884686dd-515c-4484-a324-e7d126903a42", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":176857, + "text":"Beacon Bolt deals damage to target creature equal to the total number of instant and sorcery cards you own in exile and in your graveyard.\nJump-start (You may cast this card from your graveyard by discarding a card in addition to paying its other costs. Then exile this card.)", + "type":"Sorcery", + "types":[ + "Sorcery" + ], + "uuid":"34b50961-dd25-54b3-b0e2-1c8071189342", + "watermark":"izzet" + }, + { + "artist":"Tyler Jacobson", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":1.0, + "count":4, + "edhrecRank":284, + "flavorText":"The crystal pulsed with the power of Teferi's planeswalker spark. Had Jhoira given him a blessing or a curse?", + "foreignData":[ + { + "flavorText":"Der Kristall pulsierte mit der Macht von Teferis Planeswalker-Funken. War Jhoiras Geschenk ein Segen oder ein Fluch?", + "language":"German", + "multiverseId":443217, + "name":"Optionen", + "text":"Hellsicht 1. (Schaue dir die oberste Karte deiner Bibliothek an. Du kannst sie unter deine Bibliothek legen.)\nZiehe eine Karte.", + "type":"Spontanzauber" + }, + { + "flavorText":"El cristal latía con el poder de la chispa de planeswalker de Teferi. ¿Lo que le había dado Jhoira era una bendición o una maldición?", + "language":"Spanish", + "multiverseId":443486, + "name":"Optar", + "text":"Adivina 1. (Mira la primera carta de tu biblioteca. Puedes poner esa carta en el fondo de tu biblioteca.)\nRoba una carta.", + "type":"Instantáneo" + }, + { + "flavorText":"Le cristal brillait sous l'effet de la puissance de l'étincelle de Planeswalker de Téfeiri. Djoïra l'avait-elle béni ou maudit ?", + "language":"French", + "multiverseId":443755, + "name":"Option", + "text":"Regard 1. (Regardez la carte du dessus de votre bibliothèque. Vous pouvez la mettre au-dessous de votre bibliothèque.)\nPiochez une carte.", + "type":"Éphémère" + }, + { + "flavorText":"Nel cristallo pulsava il potere della scintilla di Planeswalker di Teferi. Jhoira l'aveva benedetto o maledetto?", + "language":"Italian", + "multiverseId":444024, + "name":"Opzione", + "text":"Profetizza 1. (Guarda la prima carta del tuo grimorio. Puoi mettere quella carta in fondo al tuo grimorio.)\nPesca una carta.", + "type":"Istantaneo" + }, + { + "flavorText":"テフェリーのプレインズウォーカーの灯を受けて水晶が明滅していた。ジョイラが与えたのは恩恵なのか、それとも呪いなのだろうか?", + "language":"Japanese", + "multiverseId":444293, + "name":"選択", + "text":"占術1を行う。(あなたのライブラリーの一番上のカードを見る。あなたはそのカードをあなたのライブラリーの一番下に置いてもよい。)\nカードを1枚引く。", + "type":"インスタント" + }, + { + "flavorText":"수정은 테페리의 플레인즈워커 불꽃으로 고동치고 있다. 조이라는 그를 축복한 것인가, 저주한 것인가?", + "language":"Korean", + "multiverseId":444562, + "name":"선택", + "text":"점술 1을 한다. (당신의 서고 맨 위 카드 한 장을 본다. 당신은 그 카드를 당신의 서고 맨 밑에 놓을 수 있다.)\n카드 한 장을 뽑는다.", + "type":"순간마법" + }, + { + "flavorText":"O cristal pulsou com o poder da centelha de planeswalker de Teferi. O que Jhoira lhe dera era uma bênção ou uma maldição?", + "language":"Portuguese (Brazil)", + "multiverseId":444831, + "name":"Optar", + "text":"Vidência 1. (Olhe o card do topo de seu grimório. Você pode colocar aquele card no fundo de seu grimório.)\nCompre um card.", + "type":"Mágica Instantânea" + }, + { + "flavorText":"Кристалл пульсировал, до краев переполненный силой искры Тефери. Что дала ему Джойра — благословение или проклятье?", + "language":"Russian", + "multiverseId":445100, + "name":"Выбор", + "text":"Предскажите 1. (Посмотрите верхнюю карту вашей библиотеки. Вы можете положить ту карту в низ вашей библиотеки.)\nВозьмите карту.", + "type":"Мгновенное заклинание" + }, + { + "flavorText":"水晶球涌动着泰菲力鹏洛客火花的力量。尤依拉带给他的是祝福,还是诅咒?", + "language":"Chinese Simplified", + "multiverseId":445369, + "name":"抉择", + "text":"占卜1。(检视你牌库顶的牌。你可以将该牌置于你的牌库底。)\n抓一张牌。", + "type":"瞬间" + }, + { + "flavorText":"水晶球湧動著泰菲力鵬洛客火花的力量。尤依菈帶給他的是祝福,還是詛咒?", + "language":"Chinese Traditional", + "multiverseId":445638, + "name":"抉擇", + "text":"占卜1。(檢視你牌庫頂的牌。你可以將該牌置於你的牌庫底。)\n抽一張牌。", + "type":"瞬間" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "isReprint":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{U}", + "mcmId":320844, + "mcmMetaId":3999, + "mtgArenaId":67224, + "mtgoId":67585, + "multiverseId":442948, + "name":"Opt", + "number":"60", + "originalText":"Scry 1. (Look at the top card of your library. You may put that card on the bottom of your library.)\nDraw a card.", + "originalType":"Instant", + "prices":{ + "mtgo":{ + "2020-04-27":0.03 + }, + "mtgoFoil":{ + "2020-04-27":0.02 + }, + "paper":{ + "2020-04-26":0.2 + }, + "paperFoil":{ + "2020-04-26":1.16 + } + }, + "printings":[ + "DOM", + "ELD", + "INV", + "MB1", + "PDOM", + "PRM", + "WC01", + "XLN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/4fa5af45246dd841", + "tcgplayer":"https://mtgjson.com/links/037c9d4de2e27013" + }, + "rarity":"common", + "rulings":[ + + ], + "scryfallId":"25f2e4d0-effd-4e83-b7aa-1a0d8f120951", + "scryfallIllustrationId":"8be8d8ae-97c1-4da1-bc21-0aeb6c7082e9", + "scryfallOracleId":"713332c1-5bd8-400f-bfff-c1ca0697a043", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":162901, + "text":"Scry 1. (Look at the top card of your library. You may put that card on the bottom of your library.)\nDraw a card.", + "type":"Instant", + "types":[ + "Instant" + ], + "uuid":"71b0445a-8dfa-5f43-97ec-bebe63523f86" + }, + { + "artist":"Izzy", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":2.0, + "count":4, + "edhrecRank":3367, + "flavorText":"Genius is finding the edge of what's possible, then jumping over it.", + "foreignData":[ + { + "flavorText":"Genialität bedeutet, die Grenzen des Möglichen auszuloten und diese dann zu überwinden.", + "language":"German", + "multiverseId":453061, + "name":"Radikale Idee", + "text":"Ziehe eine Karte.\nKatalyse (Du kannst diese Karte aus deinem Friedhof wirken, indem du zusätzlich zu ihren anderen Kosten eine Karte abwirfst. Schicke sie danach ins Exil.)", + "type":"Spontanzauber" + }, + { + "flavorText":"La genialidad es hallar el límite de lo posible y después saltárselo.", + "language":"Spanish", + "multiverseId":453320, + "name":"Idea radical", + "text":"Roba una carta.\nRecargar. (Puedes lanzar esta carta desde tu cementerio descartando una carta además de pagar sus otros costes. Luego, exilia esta carta.)", + "type":"Instantáneo" + }, + { + "flavorText":"Le génie revient à aller aux frontières du possible et à les dépasser.", + "language":"French", + "multiverseId":453579, + "name":"Idée radicale", + "text":"Piochez une carte.\nRelancez (Vous pouvez lancer cette carte depuis votre cimetière en vous défaussant d'une carte en plus de payer ses autres coûts. Puis exilez cette carte.)", + "type":"Éphémère" + }, + { + "flavorText":"Il genio consiste nel cercare il limite di ciò che è possibile per poi oltrepassarlo.", + "language":"Italian", + "multiverseId":453838, + "name":"Idea Rivoluzionaria", + "text":"Pesca una carta.\nCarica d'avvio (Puoi lanciare questa carta dal tuo cimitero scartando una carta oltre a pagare i suoi altri costi. Poi esilia questa carta.)", + "type":"Istantaneo" + }, + { + "flavorText":"天才とは可能性の限界を見つけ、それを飛び越えることだ。", + "language":"Japanese", + "multiverseId":454097, + "name":"急進思想", + "text":"カードを1枚引く。\n再活(あなたはあなたの墓地から、このカードを、これの他のコストの支払いに加えてカード1枚を捨てることで唱えてもよい。その後、このカードを追放する。)", + "type":"インスタント" + }, + { + "flavorText":"천재는 가능한 것의 한계를 찾아낸 다음, 그것을 뛰어넘는 것이다.", + "language":"Korean", + "multiverseId":454356, + "name":"과격한 생각", + "text":"카드 한 장을 뽑는다.\n강제 시동 (당신은 이 카드의 다른 비용들을 지불하는 것에 추가로 카드를 한 장 버리는 것으로 이 카드를 당신의 무덤에서 발동할 수 있다. 그 후 이 카드를 추방한다.)", + "type":"순간마법" + }, + { + "flavorText":"Genialidade é achar o limite do possível e saltar para além dele.", + "language":"Portuguese (Brazil)", + "multiverseId":454615, + "name":"Ideia Radical", + "text":"Compre um card.\nRecarregar (Você pode conjurar este card de seu cemitério descartando um card além de pagar seus outros custos. Depois, exile este card.)", + "type":"Mágica Instantânea" + }, + { + "flavorText":"Гениальность — это способность найти предел возможного, а потом перескочить через него.", + "language":"Russian", + "multiverseId":454874, + "name":"Радикальная Идея", + "text":"Возьмите карту.\nИмпульс (Вы можете разыграть эту карту из вашего кладбища, сбросив карту в дополнение к оплате других ее стоимостей. Затем изгоните эту карту.)", + "type":"Мгновенное заклинание" + }, + { + "flavorText":"所谓天才之举,便是发现可能的边界,然后一跃而过。", + "language":"Chinese Simplified", + "multiverseId":455133, + "name":"激进创想", + "text":"抓一张牌。\n再起(你可以从你的坟墓场施放此牌,但必须支付其所需费用并额外弃一张牌。然后放逐此牌。)", + "type":"瞬间" + }, + { + "flavorText":"所謂天才之舉,便是發現可能的邊界,然後一躍而過。", + "language":"Chinese Traditional", + "multiverseId":455392, + "name":"激進創想", + "text":"抽一張牌。\n再起(你可以從你的墳墓場施放此牌,但必須支付其所需費用並額外棄一張牌。)然後放逐此牌。)", + "type":"瞬間" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{1}{U}", + "mcmId":363618, + "mcmMetaId":265100, + "mtgArenaId":68513, + "mtgoId":69477, + "multiverseId":452802, + "name":"Radical Idea", + "number":"52", + "originalText":"Draw a card.\nJump-start (You may cast this card from your graveyard by discarding a card in addition to paying its other costs. Then exile this card.)", + "originalType":"Instant", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.01 + }, + "paper":{ + "2020-04-26":0.12 + }, + "paperFoil":{ + "2020-04-26":0.86 + } + }, + "printings":[ + "GRN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/82baf449d4ebff3f", + "tcgplayer":"https://mtgjson.com/links/630d644bf9313203" + }, + "rarity":"common", + "rulings":[ + { + "date":"2018-10-05", + "text":"You must still follow any timing restrictions and permissions, including those based on the card’s type. For instance, you can cast a sorcery using jump-start only when you could normally cast a sorcery." + }, + { + "date":"2018-10-05", + "text":"A spell cast using jump-start will always be exiled afterward, whether it resolves, it’s countered, or it leaves the stack in some other way." + }, + { + "date":"2018-10-05", + "text":"If an effect allows you to pay an alternative cost rather than a spell’s mana cost, you may pay that alternative cost when you jump-start a spell. You’ll still discard a card as an additional cost to cast it." + }, + { + "date":"2018-10-05", + "text":"If a card with jump-start is put into your graveyard during your turn, you’ll be able to cast it right away if it’s legal to do so, before an opponent can take any actions." + } + ], + "scryfallId":"c9570734-5e9b-46ff-b606-9759b5195756", + "scryfallIllustrationId":"3e1c9cdc-581e-4660-ab73-29dbd92843e3", + "scryfallOracleId":"fd51dce2-8e8a-4686-910e-1bbc2825d190", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":175198, + "text":"Draw a card.\nJump-start (You may cast this card from your graveyard by discarding a card in addition to paying its other costs. Then exile this card.)", + "type":"Instant", + "types":[ + "Instant" + ], + "uuid":"6abf8b05-1513-5c21-bb5b-0e46d765fd7d", + "watermark":"izzet" + }, + { + "artist":"Jason Rainville", + "borderColor":"black", + "colorIdentity":[ + "R" + ], + "colors":[ + "R" + ], + "convertedManaCost":1.0, + "count":4, + "edhrecRank":3167, + "flavorText":"The tools of invention became the weapons of revolution.", + "foreignData":[ + { + "flavorText":"Die Werkzeuge der Erfinder wurden zu Waffen der Revolution.", + "language":"German", + "multiverseId":447573, + "name":"Schock", + "text":"Der Schock fügt einem Ziel deiner Wahl 2 Schadenspunkte zu.", + "type":"Spontanzauber" + }, + { + "flavorText":"Las herramientas de invención se convirtieron en las armas de la revolución.", + "language":"Spanish", + "multiverseId":447854, + "name":"Choque", + "text":"El Choque hace 2 puntos de daño a cualquier objetivo.", + "type":"Instantáneo" + }, + { + "flavorText":"Les outils de l'invention devinrent les armes de la révolution.", + "language":"French", + "multiverseId":448135, + "name":"Choc", + "text":"Le Choc inflige 2 blessures à n'importe quelle cible.", + "type":"Éphémère" + }, + { + "flavorText":"Gli strumenti dell'invenzione divennero le armi della rivoluzione.", + "language":"Italian", + "multiverseId":448416, + "name":"Shock", + "text":"Lo Shock infligge 2 danni a un qualsiasi bersaglio.", + "type":"Istantaneo" + }, + { + "flavorText":"発明の賜物が革命の業物になった。", + "language":"Japanese", + "multiverseId":448697, + "name":"ショック", + "text":"クリーチャー1体かプレインズウォーカー1体かプレイヤー1人を対象とする。ショックはそれに2点のダメージを与える。", + "type":"インスタント" + }, + { + "flavorText":"발명의 도구가 혁명의 무기가 되었다.", + "language":"Korean", + "multiverseId":448978, + "name":"충격", + "text":"원하는 목표를 정한다. 충격은 그 목표에게 피해 2점을 입힌다.", + "type":"순간마법" + }, + { + "flavorText":"As ferramentas da invenção tornaram-se as armas da revolução.", + "language":"Portuguese (Brazil)", + "multiverseId":449259, + "name":"Choque", + "text":"Choque causa 2 pontos de dano a qualquer alvo.", + "type":"Mágica Instantânea" + }, + { + "flavorText":"Инструменты изобретателей стали оружием революции.", + "language":"Russian", + "multiverseId":449540, + "name":"Шок", + "text":"Шок наносит 2 повреждения любой цели.", + "type":"Мгновенное заклинание" + }, + { + "flavorText":"发明的工具成为了革命的武器。", + "language":"Chinese Simplified", + "multiverseId":449821, + "name":"电震", + "text":"电震对任意一个目标造成2点伤害。", + "type":"瞬间" + }, + { + "flavorText":"發明的工具成為了革命的武器。", + "language":"Chinese Traditional", + "multiverseId":450102, + "name":"電震", + "text":"電震對任意一個目標造成2點傷害。", + "type":"瞬間" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "isReprint":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{R}", + "mcmId":359623, + "mcmMetaId":5160, + "mtgArenaId":67992, + "mtgoId":68473, + "multiverseId":447292, + "name":"Shock", + "number":"156", + "originalText":"Shock deals 2 damage to any target.", + "originalType":"Instant", + "prices":{ + "mtgo":{ + "2020-04-27":0.04 + }, + "mtgoFoil":{ + "2020-04-27":0.02 + }, + "paper":{ + "2020-04-26":0.1 + }, + "paperFoil":{ + "2020-04-26":0.58 + } + }, + "printings":[ + "10E", + "6ED", + "7ED", + "8ED", + "9ED", + "AER", + "BBD", + "BTD", + "DDN", + "DPA", + "FNM", + "M12", + "M14", + "M19", + "M20", + "MB1", + "ONS", + "PMEI", + "PRM", + "PS11", + "PSAL", + "STH", + "WC98", + "WC99" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/b94930d2530bc268", + "tcgplayer":"https://mtgjson.com/links/22ef10ffb652e2a3" + }, + "rarity":"common", + "rulings":[ + + ], + "scryfallId":"bf5a0e1e-5239-41f3-a63f-d9303b1b01fc", + "scryfallIllustrationId":"167dac35-21f6-4174-b496-4b66ffd980b1", + "scryfallOracleId":"a9d288b8-cdc1-4e55-a0c9-d6edfc95e65d", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":168475, + "text":"Shock deals 2 damage to any target.", + "type":"Instant", + "types":[ + "Instant" + ], + "uuid":"a2513497-ca4c-5732-a322-b9a8a391c468" + }, + { + "artist":"Magali Villeneuve", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":1.0, + "count":2, + "edhrecRank":5884, + "flavorText":"\"Seeing the river is not the same as seeing the fish.\"", + "foreignData":[ + { + "flavorText":"„Die Reflexion der Wasseroberfläche verbirgt alles, was sich darunter versteckt.\"", + "language":"German", + "multiverseId":435504, + "name":"Abtauchen", + "text":"Eine Kreatur deiner Wahl, die du kontrollierst, erhält +0/+3 und Fluchsicherheit bis zum Ende des Zuges. (Sie kann nicht das Ziel von Zaubersprüchen oder Fähigkeiten sein, die deine Gegner kontrollieren.)", + "type":"Spontanzauber" + }, + { + "flavorText":"\"Ver el río no es lo mismo que ver los peces\".", + "language":"Spanish", + "multiverseId":437597, + "name":"Sumergirse", + "text":"La criatura objetivo que controlas obtiene +0/+3 y gana la habilidad de antimaleficio hasta el final del turno. (No puede ser objetivo de hechizos o habilidades que controlen tus oponentes.)", + "type":"Instantáneo" + }, + { + "flavorText":"« Il y a une différence entre voir la rivière et voir le poisson. »", + "language":"French", + "multiverseId":435803, + "name":"Plongée", + "text":"La créature ciblée que vous contrôlez gagne +0/+3 et acquiert la défense talismanique jusqu'à la fin du tour. (Elle ne peut pas être la cible de sorts ou de capacités que vos adversaires contrôlent.)", + "type":"Éphémère" + }, + { + "flavorText":"\"Non sempre guardando il fiume si riescono a vedere i pesci.\"", + "language":"Italian", + "multiverseId":436102, + "name":"Immergersi", + "text":"Una creatura bersaglio che controlli prende +0/+3 e ha anti-malocchio fino alla fine del turno. (Non può essere bersaglio di magie o abilità controllate dai tuoi avversari.)", + "type":"Istantaneo" + }, + { + "flavorText":"「川を見ても魚を見たことにはならないわ。」", + "language":"Japanese", + "multiverseId":436401, + "name":"潜水", + "text":"あなたがコントロールしているクリーチャー1体を対象とする。ターン終了時まで、それは+0/+3の修整を受けるとともに呪禁を得る。(それは対戦相手がコントロールしている呪文や能力の対象にならない。)", + "type":"インスタント" + }, + { + "flavorText":"\"강을 보는 것과 물고기를 보는 것은 다르다.\"", + "language":"Korean", + "multiverseId":436700, + "name":"잠수", + "text":"당신이 조종하는 생물을 목표로 정한다. 그 생물은 턴종료까지 +0/+3을 받고 방호를 얻는다. (해당 생물은 상대가 조종하는 주문이나 능력의 목표로 정해질 수 없다.)", + "type":"순간마법" + }, + { + "flavorText":"\"Ver o rio não é o mesmo que ver os peixes.\"", + "language":"Portuguese (Brazil)", + "multiverseId":436999, + "name":"Mergulhar", + "text":"A criatura alvo que você controla recebe +0/+3 e ganha resistência a magia até o final do turno. (Ela não pode ser alvo de mágicas nem de habilidades que seus oponentes controlam.)", + "type":"Mágica Instantânea" + }, + { + "flavorText":"«Увидеть реку — совсем не то же самое, что увидеть рыб».", + "language":"Russian", + "multiverseId":437298, + "name":"Погружение Вглубь", + "text":"Целевое существо под вашим контролем получает +0/+3 и Порчеустойчивость до конца хода. (Оно не может быть целью заклинаний или способностей под контролем ваших оппонентов.)", + "type":"Мгновенное заклинание" + }, + { + "flavorText":"「看得到河不代表能看见鱼。」", + "language":"Chinese Simplified", + "multiverseId":437896, + "name":"深潜", + "text":"直到回合结束,目标由你操控的生物得+0/+3且获得辟邪异能。(它不能成为由对手操控之咒语或异能的目标。)", + "type":"瞬间" + }, + { + "flavorText":"「看得到河不代表能看見魚。」", + "language":"Chinese Traditional", + "multiverseId":438195, + "name":"深潛", + "text":"直到回合結束,目標由你操控的生物得+0/+3且獲得辟邪異能。(它不能成為由對手操控之咒語或異能的目標。)", + "type":"瞬間" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "commander":"Legal", + "duel":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "vintage":"Legal" + }, + "manaCost":"{U}", + "mcmId":301756, + "mcmMetaId":226987, + "mtgArenaId":66067, + "mtgoFoilId":65119, + "mtgoId":65118, + "multiverseId":435205, + "name":"Dive Down", + "number":"53", + "originalText":"Target creature you control gets +0/+3 and gains hexproof until end of turn. (It can't be the target of spells or abilities your opponents control.)", + "originalType":"Instant", + "prices":{ + "mtgo":{ + "2020-04-27":0.03 + }, + "mtgoFoil":{ + "2020-04-27":0.02 + }, + "paper":{ + "2020-04-26":0.15 + }, + "paperFoil":{ + "2020-04-26":1.29 + } + }, + "printings":[ + "XLN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/e1c6937706e5e3c5", + "tcgplayer":"https://mtgjson.com/links/fd1bef4a42a09314" + }, + "rarity":"common", + "rulings":[ + + ], + "scryfallId":"b33e493e-1aef-43b3-9716-52158b002430", + "scryfallIllustrationId":"8c6e66cb-f420-4cc3-8c3a-67db605bee1f", + "scryfallOracleId":"2f990b54-fbf3-4949-85bb-9ba39710e72a", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":145743, + "text":"Target creature you control gets +0/+3 and gains hexproof until end of turn. (It can't be the target of spells or abilities your opponents control.)", + "type":"Instant", + "types":[ + "Instant" + ], + "uuid":"08af01a2-a2ee-5ff3-a71a-bb878ef7ef43" + }, + { + "artist":"Igor Kieryluk", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":2.0, + "count":1, + "edhrecRank":1713, + "foreignData":[ + { + "language":"German", + "multiverseId":443203, + "name":"Von Augenblick zu Augenblick", + "text":"Bonus {1}{U} (Du kannst zusätzlich {1}{U} bezahlen, sowie du diesen Zauberspruch wirkst.)\nBringe eine bleibende Karte deiner Wahl, die kein Land ist, auf die Hand ihres Besitzers zurück. Falls die Bonuskosten dieses Zauberspruchs bezahlt wurden, ziehe eine Karte.", + "type":"Spontanzauber" + }, + { + "language":"Spanish", + "multiverseId":443472, + "name":"Abrir y cerrar de ojos", + "text":"Estímulo {1}{U}. (Puedes pagar {1}{U} adicionales al lanzar este hechizo.)\nRegresa el permanente objetivo que no sea tierra a la mano de su propietario. Si este hechizo fue estimulado, roba una carta.", + "type":"Instantáneo" + }, + { + "language":"French", + "multiverseId":443741, + "name":"Clin d'œil", + "text":"Kick {1}{U} (Vous pouvez payer {1}{U} supplémentaires au moment où vous lancez ce sort.)\nRenvoyez le permanent non-terrain ciblé dans la main de son propriétaire. Si ce sort a été kické, piochez une carte.", + "type":"Éphémère" + }, + { + "language":"Italian", + "multiverseId":444010, + "name":"Batter d'Occhio", + "text":"Potenziamento {1}{U} (Puoi pagare {1}{U} addizionale mentre lanci questa magia.)\nFai tornare un permanente non terra bersaglio in mano al suo proprietario. Se questa magia è stata potenziata, pesca una carta.", + "type":"Istantaneo" + }, + { + "language":"Japanese", + "multiverseId":444279, + "name":"一瞬", + "text":"キッカー{1}{U}(あなたはこの呪文を唱えるに際し、追加で{1}{U}を支払ってもよい。)\n土地でないパーマネント1つを対象とし、それをオーナーの手札に戻す。この呪文がキッカーされていたなら、カードを1枚引く。", + "type":"インスタント" + }, + { + "language":"Korean", + "multiverseId":444548, + "name":"눈 깜빡이기", + "text":"키커 {1}{U} (당신은 이 주문을 발동하면서 추가로 {1}{U}를 지불할 수 있다.)\n대지가 아닌 지속물을 목표로 정한다. 그 지속물을 소유자의 손으로 되돌린다. 이 주문의 키커 비용이 지불되었다면, 카드 한 장을 뽑는다.", + "type":"순간마법" + }, + { + "language":"Portuguese (Brazil)", + "multiverseId":444817, + "name":"Piscar de Olhos", + "text":"Reforçar {1}{U} (Você pode pagar um custo adicional de {1}{U} ao conjurar esta mágica.)\nDevolva a permanente alvo que não seja um terreno para a mão de seu dono. Se esta mágica foi reforçada, compre um card.", + "type":"Mágica Instantânea" + }, + { + "language":"Russian", + "multiverseId":445086, + "name":"Мгновение Ока", + "text":"Усилитель {1}{U} (При разыгрывании этого заклинания вы можете дополнительно заплатить {1}{U}.)\nВерните целевой не являющийся землей перманент в руку его владельца. Если это заклинание получило Усилитель, возьмите карту.", + "type":"Мгновенное заклинание" + }, + { + "language":"Chinese Simplified", + "multiverseId":445355, + "name":"眨眼即逝", + "text":"增幅{1}{U}(你施放此咒语时可以额外支付{1}{U}。)\n将目标非地永久物移回其拥有者手上。如果此咒语已增幅,则抓一张牌。", + "type":"瞬间" + }, + { + "language":"Chinese Traditional", + "multiverseId":445624, + "name":"眨眼即逝", + "text":"增幅{1}{U}(你施放此咒語時可以額外支付{1}{U}。)\n將目標非地永久物移回其擁有者手上。如果此咒語已增幅,則抽一張牌。", + "type":"瞬間" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "commander":"Legal", + "duel":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "pioneer":"Legal", + "vintage":"Legal" + }, + "manaCost":"{1}{U}", + "mcmId":319758, + "mcmMetaId":239397, + "mtgArenaId":67196, + "mtgoId":67557, + "multiverseId":442934, + "name":"Blink of an Eye", + "number":"46", + "originalText":"Kicker {1}{U} (You may pay an additional {1}{U} as you cast this spell.)\nReturn target nonland permanent to its owner's hand. If this spell was kicked, draw a card.", + "originalType":"Instant", + "prices":{ + "mtgo":{ + "2020-04-27":0.03 + }, + "mtgoFoil":{ + "2020-04-27":0.03 + }, + "paper":{ + "2020-04-26":0.14 + }, + "paperFoil":{ + "2020-04-26":1.28 + } + }, + "printings":[ + "DOM" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/00681774143ca838", + "tcgplayer":"https://mtgjson.com/links/c972d524131d292d" + }, + "rarity":"common", + "rulings":[ + { + "date":"2018-04-27", + "text":"If the target nonland permanent is an illegal target by the time Blink of an Eye tries to resolve, the spell doesn’t resolve. You won’t draw a card if it was kicked." + } + ], + "scryfallId":"ab830392-4d7e-4b45-93cf-35ed1e935228", + "scryfallIllustrationId":"4f1c67e5-b02d-4b1c-b263-e23cb9315f8b", + "scryfallOracleId":"51acd176-e84d-42c2-acb2-cceeb28a9fec", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":162214, + "text":"Kicker {1}{U} (You may pay an additional {1}{U} as you cast this spell.)\nReturn target nonland permanent to its owner's hand. If this spell was kicked, draw a card.", + "type":"Instant", + "types":[ + "Instant" + ], + "uuid":"6252cc36-4723-5774-8709-82ababa5b0bb" + }, + { + "artist":"James Arnold", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":5.0, + "count":1, + "edhrecRank":1700, + "foreignData":[ + { + "language":"German", + "multiverseId":443214, + "name":"Die Mirari-Hypothese", + "text":"(Je eine Sagenmarke beim Ins-Spiel-Kommen und nach deinem Ziehsegment. Opfern nach III.)\nI — Bringe eine Spontanzauberkarte deiner Wahl aus deinem Friedhof auf deine Hand zurück.\nII — Bringe eine Hexereikarte deiner Wahl aus deinem Friedhof auf deine Hand zurück.\nIII — Immer wenn du bis zum Ende des Zuges einen Spontanzauber oder eine Hexerei wirkst, kopiere ihn bzw. sie. Du kannst neue Ziele für die Kopie bestimmen.", + "type":"Verzauberung — Sage" + }, + { + "language":"Spanish", + "multiverseId":443483, + "name":"La conjetura del Mirari", + "text":"(En cuanto esta Saga entre y después de tu paso de robar, agrega un contador de sabiduría. Sacrifícala después de III.)\nI — Regresa la carta de instantáneo objetivo de tu cementerio a tu mano.\nII — Regresa la carta de conjuro objetivo de tu cementerio a tu mano.\nIII — Hasta el final del turno, siempre que lances un hechizo de instantáneo o de conjuro, cópialo. Puedes elegir nuevos objetivos para la copia.", + "type":"Encantamiento — Saga" + }, + { + "language":"French", + "multiverseId":443752, + "name":"La conjecture du Mirari", + "text":"(Au moment où cette saga arrive sur le champ de bataille et après votre étape de pioche, ajoutez un marqueur « sapience ». Sacrifiez après III.)\nI — Renvoyez une carte d'éphémère ciblée depuis votre cimetière dans votre main.\nII — Renvoyez une carte de rituel ciblée depuis votre cimetière dans votre main.\nIII — Jusqu'à la fin du tour, à chaque fois que vous lancez un sort d'éphémère ou de rituel, copiez-le. Vous pouvez choisir de nouvelles cibles pour cette copie.", + "type":"Enchantement : saga" + }, + { + "language":"Italian", + "multiverseId":444021, + "name":"Congettura sul Mirari", + "text":"(Mentre questa Saga entra e dopo la tua sottofase di acquisizione, aggiungi un segnalino sapere. Sacrifica dopo III.)\nI — Riprendi in mano una carta istantaneo bersaglio dal tuo cimitero.\nII — Riprendi in mano una carta stregoneria bersaglio dal tuo cimitero.\nIII — Fino alla fine del turno, ogniqualvolta lanci una magia istantaneo o stregoneria, copiala. Puoi scegliere nuovi bersagli per la copia.", + "type":"Incantesimo — Saga" + }, + { + "language":"Japanese", + "multiverseId":444290, + "name":"ミラーリ予想", + "text":"(この英雄譚が出た際とあなたのドロー・ステップの後に、伝承カウンターを1個加える。IIIの後に、生け贄に捧げる。)\nI ― あなたの墓地からインスタント・カード1枚を対象とし、それをあなたの手札に戻す。\nII ― あなたの墓地からソーサリー・カード1枚を対象とし、それをあなたの手札に戻す。\nIII ― ターン終了時まで、あなたがインスタントかソーサリーである呪文を唱えるたび、それをコピーする。あなたはそのコピーの新しい対象を選んでもよい。", + "type":"エンチャント — 英雄譚" + }, + { + "language":"Korean", + "multiverseId":444559, + "name":"미라리의 예측", + "text":"(이 서사시가 들어오면서 그리고 당신의 뽑기단 후에, 전승 카운터 한 개를 추가한다. III 이후에 희생한다.)\nI — 당신의 무덤에 있는 순간마법 카드를 목표로 정한다. 그 카드를 당신의 손으로 되돌린다.\nII — 당신의 무덤에 있는 집중마법 카드를 목표로 정한다. 그 카드를 당신의 손으로 되돌린다.\nIII — 턴종료까지, 당신이 순간마법 또는 집중마법 주문을 발동할 때마다, 그 주문을 복사한다. 당신은 그 복사본의 목표를 새로 정할 수 있다.", + "type":"부여마법 — 서사시" + }, + { + "language":"Portuguese (Brazil)", + "multiverseId":444828, + "name":"A Conjectura do Mirari", + "text":"(Conforme esta Saga entra e após sua etapa de compra, adicione um marcador de conhecimento. Sacrifique-a após III.)\nI — Devolva o card de mágica instantânea alvo de seu cemitério para sua mão.\nII — Devolva o card de feitiço alvo de seu cemitério para sua mão.\nIII — Até o final do turno, toda vez que você conjurar uma mágica instantânea ou um feitiço, copie-o. Você pode escolher novos alvos para a cópia.", + "type":"Encantamento — Saga" + }, + { + "language":"Russian", + "multiverseId":445097, + "name":"Гипотеза о Мирари", + "text":"(При выходе этой Саги и после вашего шага взятия карты добавьте один жетон знаний. Пожертвуйте после III.)\nI — Верните целевую карту мгновенного заклинания из вашего кладбища в вашу руку.\nII — Верните целевую карту волшебства из вашего кладбища в вашу руку.\nIII — До конца хода, каждый раз, когда вы разыгрываете мгновенное заклинание или заклинание волшебства, скопируйте его. Вы можете выбрать новые цели для той копии.", + "type":"Чары — Сага" + }, + { + "language":"Chinese Simplified", + "multiverseId":445366, + "name":"映奇宝珠探究", + "text":"(于此传纪进战场时及于你抓牌步骤后,加一个学问指示物。到III后牺牲之。)\nI — 将目标瞬间牌从你的坟墓场移回你手上。\nII — 将目标法术牌从你的坟墓场移回你手上。\nIII — 直到回合结束,每当你施放瞬间或法术咒语时,将其复制。你可以为该复制品选择新的目标。", + "type":"结界 ~传纪" + }, + { + "language":"Chinese Traditional", + "multiverseId":445635, + "name":"映奇寶珠探究", + "text":"(於此傳紀進戰場時及於你抽牌步驟後,加一個學問指示物。到III後犧牲之。)\nI — 將目標瞬間牌從你的墳墓場移回你手上。\nII — 將目標巫術牌從你的墳墓場移回你手上。\nIII — 直到回合結束,每當你施放瞬間或巫術咒語時,將其複製。你可以為該複製品選擇新的目標。", + "type":"結界 ~傳紀" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"saga", + "legalities":{ + "commander":"Legal", + "duel":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "vintage":"Legal" + }, + "manaCost":"{4}{U}", + "mcmId":319825, + "mcmMetaId":239456, + "mtgArenaId":67218, + "mtgoId":67579, + "multiverseId":442945, + "name":"The Mirari Conjecture", + "number":"57", + "originalText":"(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI — Return target instant card from your graveyard to your hand.\nII — Return target sorcery card from your graveyard to your hand.\nIII — Until end of turn, whenever you cast an instant or sorcery spell, copy it. You may choose new targets for the copy.", + "originalType":"Enchantment — Saga", + "prices":{ + "mtgo":{ + "2020-04-27":0.02 + }, + "mtgoFoil":{ + "2020-04-27":0.24 + }, + "paper":{ + "2020-04-26":0.25 + }, + "paperFoil":{ + "2020-04-26":1.02 + } + }, + "printings":[ + "DOM", + "MB1", + "PDOM" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/b810138b43bd610a", + "tcgplayer":"https://mtgjson.com/links/59dac989507e4177" + }, + "rarity":"rare", + "rulings":[ + { + "date":"2018-04-27", + "text":"The Mirari Conjecture’s final chapter ability copies any instant or sorcery spell you cast, not just those with targets." + }, + { + "date":"2018-04-27", + "text":"The copy is created on the stack, so it’s not “cast.” Abilities that trigger when a player casts a spell won’t trigger." + }, + { + "date":"2018-04-27", + "text":"The copy will have the same targets as the spell it’s copying unless you choose new ones. You may change any number of the targets, including all of them or none of them. If, for one of the targets, you can’t choose a new legal target, then it remains unchanged (even if the current target is illegal)." + }, + { + "date":"2018-04-27", + "text":"If the spell that’s copied is modal (that is, it says “Choose one —” or the like), the copy will have the same mode. A different mode can’t be chosen." + }, + { + "date":"2018-04-27", + "text":"If the spell that’s copied has an X whose value was determined as it was cast (like Jaya’s Immolating Inferno does), the copy will have the same value of X." + }, + { + "date":"2018-04-27", + "text":"If the spell has damage divided as it was cast (like Fight with Fire does when kicked), the division can’t be changed (although the targets receiving that damage still can)." + }, + { + "date":"2018-04-27", + "text":"The controller of a copy can’t choose to pay any alternative or additional costs for the copy. However, effects based on any alternative or additional costs that were paid for the original spell are copied as though those same costs were paid for the copy." + }, + { + "date":"2018-04-27", + "text":"As a Saga enters the battlefield, its controller puts a lore counter on it. As your precombat main phase begins (immediately after your draw step), you put another lore counter on each Saga you control. Putting a lore counter on a Saga in either of these ways doesn’t use the stack." + }, + { + "date":"2018-04-27", + "text":"Each symbol on the left of a Saga’s text box represents a chapter ability. A chapter ability is a triggered ability that triggers when a lore counter that is put on the Saga causes the number of lore counters on the Saga to become equal to or greater than the ability’s chapter number. Chapter abilities are put onto the stack and may be responded to." + }, + { + "date":"2018-04-27", + "text":"A chapter ability doesn’t trigger if a lore counter is put on a Saga that already had a number of lore counters greater than or equal to that chapter’s number. For example, the third lore counter put on a Saga causes the III chapter ability to trigger, but I and II won’t trigger again." + }, + { + "date":"2018-04-27", + "text":"Once a chapter ability has triggered, the ability on the stack won’t be affected if the Saga gains or loses counters, or if it leaves the battlefield." + }, + { + "date":"2018-04-27", + "text":"If multiple chapter abilities trigger at the same time, their controller puts them on the stack in any order. If any of them require targets, those targets are chosen as you put the abilities on the stack, before any of those abilities resolve." + }, + { + "date":"2018-04-27", + "text":"If counters are removed from a Saga, the appropriate chapter abilities will trigger again when the Saga receives lore counters. Removing lore counters won’t cause a previous chapter ability to trigger." + }, + { + "date":"2018-04-27", + "text":"Once the number of lore counters on a Saga is greater than or equal to the greatest number among its chapter abilities—in the Dominaria set, this is always three—the Saga’s controller sacrifices it as soon as its chapter ability has left the stack, most likely by resolving or being countered. This state-based action doesn’t use the stack." + } + ], + "scryfallId":"1c68954c-4bab-4973-9819-ecd084438303", + "scryfallIllustrationId":"be099eea-cb0f-4223-89be-f10f683adaf9", + "scryfallOracleId":"56528234-46c0-4fba-972a-0c8011996f8d", + "subtypes":[ + "Saga" + ], + "supertypes":[ + + ], + "tcgplayerProductId":162241, + "text":"(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI — Return target instant card from your graveyard to your hand.\nII — Return target sorcery card from your graveyard to your hand.\nIII — Until end of turn, whenever you cast an instant or sorcery spell, copy it. You may choose new targets for the copy.", + "type":"Enchantment — Saga", + "types":[ + "Enchantment" + ], + "uuid":"3533640b-39e0-54e7-ae60-b194fc9dddb0" + }, + { + "artist":"Cliff Childs", + "borderColor":"black", + "colorIdentity":[ + "R", + "U" + ], + "colors":[ + + ], + "convertedManaCost":0.0, + "count":3, + "edhrecRank":125, + "flavorText":"\"We have inherited the mysteries of the Thran, but few of the answers.\" —Jhoira", + "foreignData":[ + { + "flavorText":"„Uns wurden viele der Mysterien von Thran überliefert, doch nur wenige Antworten.\" —Jhoira", + "language":"German", + "multiverseId":443404, + "name":"Schwefelfälle", + "text":"Die Schwefelfälle kommen getappt ins Spiel, es sei denn, du kontrollierst eine Insel oder ein Gebirge.\n{T}: Erzeuge {U} oder {R}.", + "type":"Land" + }, + { + "flavorText":"\"Hemos heredado los misterios de los thran, pero pocas de sus respuestas\". —Jhoira", + "language":"Spanish", + "multiverseId":443673, + "name":"Cascada de azufre", + "text":"La Cascada de azufre entra al campo de batalla girada a menos que controles una Isla o una Montaña.\n{T}: Agrega {U} o {R}.", + "type":"Tierra" + }, + { + "flavorText":"« Nous avons hérité de tous les mystères des Thrans, mais de peu de réponses. » —Djoïra", + "language":"French", + "multiverseId":443942, + "name":"Chutes de soufre", + "text":"Les Chutes de soufre arrivent sur le champ de bataille engagées à moins que vous ne contrôliez une île ou une montagne.\n{T} : Ajoutez {U} ou {R}.", + "type":"Terrain" + }, + { + "flavorText":"\"Abbiamo ereditato molti misteri dai Thran, ma ben poche risposte.\" —Jhoira", + "language":"Italian", + "multiverseId":444211, + "name":"Cascata Sulfurea", + "text":"La Cascata Sulfurea entra nel campo di battaglia TAPpata a meno che tu non controlli un'Isola o una Montagna.\n{T}: Aggiungi {U} o {R}.", + "type":"Terra" + }, + { + "flavorText":"「私たちはスランの謎を受け継いだけれど、答えはほとんど受け継いでいない。」 ――ジョイラ", + "language":"Japanese", + "multiverseId":444480, + "name":"硫黄の滝", + "text":"硫黄の滝は、あなたが島か山をコントロールしていないかぎり、タップ状態で戦場に出る。\n{T}:{U}か{R}を加える。", + "type":"土地" + }, + { + "flavorText":"\"우리는 트란의 신비들을 계승했지만, 그 대답은 거의 물려받지 못했지.\" —조이라", + "language":"Korean", + "multiverseId":444749, + "name":"유황 폭포", + "text":"유황 폭포는 당신이 섬이나 산을 조종하지 않으면 탭된 채로 전장에 들어온다.\n{T}: {U} 또는 {R}를 추가한다.", + "type":"대지" + }, + { + "flavorText":"\"Herdamos os mistérios dos Thran, mas poucas das respostas.\" — Jhoira", + "language":"Portuguese (Brazil)", + "multiverseId":445018, + "name":"Cascata de Enxofre", + "text":"Cascata de Enxofre entra no campo de batalha virado, a menos que você controle uma Ilha ou uma Montanha.\n{T}: Adicione {U} ou {R}.", + "type":"Terreno" + }, + { + "flavorText":"«Мы унаследовали множество загадок транов, но совсем немного ответов». — Джойра", + "language":"Russian", + "multiverseId":445287, + "name":"Серный Водопад", + "text":"Серный Водопад выходит на поле битвы повернутым, если под вашим контролем нет Острова или Горы.\n{T}: добавьте {U} или {R}.", + "type":"Земля" + }, + { + "flavorText":"「索蓝帝国给我们留下了诸多谜团,却未留下任何答案。」 ~尤依拉", + "language":"Chinese Simplified", + "multiverseId":445556, + "name":"硫磺瀑布", + "text":"除非你操控海岛或山脉,否则硫磺瀑布须横置进战场。\n{T}:加{U}或{R}。", + "type":"地" + }, + { + "flavorText":"「索藍帝國給我們留下了諸多謎團,卻未留下任何答案。」 ~尤依菈", + "language":"Chinese Traditional", + "multiverseId":445825, + "name":"硫磺瀑布", + "text":"除非你操控海島或山脈,否則硫磺瀑布須橫置進戰場。\n{T}:加{U}或{R}。", + "type":"地" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "isReprint":true, + "layout":"normal", + "legalities":{ + "commander":"Legal", + "duel":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pioneer":"Legal", + "vintage":"Legal" + }, + "mcmId":319894, + "mcmMetaId":203988, + "mtgArenaId":67598, + "mtgoId":67959, + "multiverseId":443135, + "name":"Sulfur Falls", + "number":"247", + "originalText":"Sulfur Falls enters the battlefield tapped unless you control an Island or a Mountain.\n{T}: Add {U} or {R}.", + "originalType":"Land", + "prices":{ + "mtgo":{ + "2020-04-27":0.31 + }, + "mtgoFoil":{ + "2020-04-27":0.8 + }, + "paper":{ + "2020-04-26":3.2 + }, + "paperFoil":{ + "2020-04-26":5.43 + } + }, + "printings":[ + "DOM", + "ISD", + "PDOM" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/633e5e0b2be8e7c9", + "tcgplayer":"https://mtgjson.com/links/eb5bc6d706ccf000" + }, + "rarity":"rare", + "rulings":[ + + ], + "scryfallId":"6bc412fc-e9d4-4283-bd4a-811384e79b5c", + "scryfallIllustrationId":"8106243d-d8b4-4fe1-ad6a-38f4d27a023f", + "scryfallOracleId":"6a6c5e17-6465-4a1f-9d63-8a3ce2edc522", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":162122, + "text":"Sulfur Falls enters the battlefield tapped unless you control an Island or a Mountain.\n{T}: Add {U} or {R}.", + "type":"Land", + "types":[ + "Land" + ], + "uuid":"71f9fdad-ba40-5771-81d9-6b4099a04a1c" + }, + { + "artist":"Kirsten Zirngibl", + "borderColor":"black", + "colorIdentity":[ + "R", + "U" + ], + "colors":[ + + ], + "convertedManaCost":0.0, + "count":4, + "edhrecRank":271, + "flavorText":"Every laboratory buzzes with new experiments, each a piece of Ral's ambitious project.", + "foreignData":[ + + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "isReprint":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "mcmId":363964, + "mcmMetaId":206450, + "mtgArenaId":68732, + "mtgoId":69915, + "multiverseId":453001, + "name":"Izzet Guildgate", + "number":"251", + "originalText":"Izzet Guildgate enters the battlefield tapped.\n{T}: Add {U} or {R}.", + "originalType":"Land — Gate", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.01 + }, + "paper":{ + "2020-04-26":0.09 + }, + "paperFoil":{ + "2020-04-26":0.74 + } + }, + "printings":[ + "C13", + "C15", + "C18", + "C19", + "DGM", + "GRN", + "MM3", + "RTR" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/a0cf70e3083f004d", + "tcgplayer":"https://mtgjson.com/links/203579dfee442b43" + }, + "rarity":"common", + "rulings":[ + { + "date":"2013-04-15", + "text":"The subtype Gate has no special rules significance, but other spells and abilities may refer to it." + }, + { + "date":"2013-04-15", + "text":"Gate is not a basic land type." + } + ], + "scryfallId":"2055a83a-99c8-4808-90b9-1c2fdcda79b4", + "scryfallIllustrationId":"8be1c514-41b7-4188-9cb5-dfbb1d00b2e0", + "scryfallOracleId":"bf75a3d1-f184-4b48-a913-21caee1db084", + "subtypes":[ + "Gate" + ], + "supertypes":[ + + ], + "tcgplayerProductId":176409, + "text":"Izzet Guildgate enters the battlefield tapped.\n{T}: Add {U} or {R}.", + "type":"Land — Gate", + "types":[ + "Land" + ], + "uuid":"c6aa8c6f-537c-5bbc-841f-49c95133b0e0", + "variations":[ + "719725f4-eb9c-5fae-be2a-06d2003b1f9c" + ], + "watermark":"izzet" + }, + { + "artist":"Richard Wright", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + + ], + "convertedManaCost":0.0, + "count":8, + "foreignData":[ + + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "isReprint":true, + "isStarter":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "mtgArenaId":68742, + "mtgoId":69349, + "multiverseId":455601, + "name":"Island", + "number":"261", + "originalText":"U", + "originalType":"Basic Land — Island", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.02 + }, + "paper":{ + "2020-04-26":0.43 + }, + "paperFoil":{ + "2020-04-26":0.43 + } + }, + "printings":[ + "10E", + "2ED", + "3ED", + "4BB", + "4ED", + "5ED", + "6ED", + "7ED", + "8ED", + "9ED", + "AKH", + "ALA", + "ANA", + "ARC", + "AVR", + "BBD", + "BFZ", + "BRB", + "BTD", + "C13", + "C14", + "C15", + "C16", + "C17", + "C18", + "C19", + "CED", + "CEI", + "CHK", + "CM2", + "CMA", + "CMD", + "CST", + "DD2", + "DDE", + "DDF", + "DDH", + "DDI", + "DDJ", + "DDM", + "DDN", + "DDO", + "DDQ", + "DDS", + "DDT", + "DDU", + "DOM", + "DPA", + "DTK", + "E01", + "ELD", + "FBB", + "FRF", + "G17", + "GK1", + "GK2", + "GN2", + "GNT", + "GRN", + "GS1", + "H09", + "HOP", + "HOU", + "ICE", + "IKO", + "INV", + "ISD", + "ITP", + "J14", + "JVC", + "KLD", + "KTK", + "LEA", + "LEB", + "LRW", + "M10", + "M11", + "M12", + "M13", + "M14", + "M15", + "M19", + "M20", + "MBS", + "ME1", + "ME3", + "MIR", + "MMQ", + "MRD", + "NPH", + "ODY", + "ONS", + "ORI", + "P02", + "PAL00", + "PAL01", + "PAL02", + "PAL03", + "PAL04", + "PAL05", + "PAL06", + "PAL99", + "PALP", + "PANA", + "PARL", + "PC2", + "PCA", + "PELP", + "PF19", + "PF20", + "PGPX", + "PGRU", + "PMPS", + "PMPS06", + "PMPS07", + "PMPS08", + "PMPS09", + "PMPS10", + "PMPS11", + "POR", + "PPP1", + "PRM", + "PRW2", + "PRWK", + "PS11", + "PSAL", + "PSS2", + "PSS3", + "PTC", + "PTK", + "PZ2", + "RAV", + "RIX", + "RNA", + "ROE", + "RQS", + "RTR", + "S99", + "SHM", + "SLD", + "SOI", + "SOM", + "SUM", + "TD0", + "TD2", + "THB", + "THS", + "TMP", + "TPR", + "TSP", + "UGL", + "UND", + "UNH", + "USG", + "UST", + "WAR", + "WC00", + "WC01", + "WC02", + "WC03", + "WC04", + "WC97", + "WC98", + "XLN", + "ZEN" + ], + "purchaseUrls":{ + "tcgplayer":"https://mtgjson.com/links/04dc6694f8d33792" + }, + "rarity":"common", + "rulings":[ + + ], + "scryfallId":"29bfbf3e-3a6c-40d4-8e1b-255f429de6cc", + "scryfallIllustrationId":"145071b4-5241-4f52-ada0-7cbe1d19d11e", + "scryfallOracleId":"b2c6aa39-2d2a-459c-a555-fb48ba993373", + "subtypes":[ + "Island" + ], + "supertypes":[ + "Basic" + ], + "tcgplayerProductId":176616, + "text":"({T}: Add {U}.)", + "type":"Basic Land — Island", + "types":[ + "Land" + ], + "uuid":"3be0ff7d-e3f2-5443-a1d1-707a27f8c4db" + }, + { + "artist":"Richard Wright", + "borderColor":"black", + "colorIdentity":[ + "R" + ], + "colors":[ + + ], + "convertedManaCost":0.0, + "count":7, + "foreignData":[ + + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "isReprint":true, + "isStarter":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "mtgArenaId":68744, + "mtgoId":69353, + "multiverseId":455603, + "name":"Mountain", + "number":"263", + "originalText":"R", + "originalType":"Basic Land — Mountain", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.02 + }, + "paper":{ + "2020-04-26":0.27 + }, + "paperFoil":{ + "2020-04-26":0.29 + } + }, + "printings":[ + "10E", + "2ED", + "3ED", + "4BB", + "4ED", + "5ED", + "6ED", + "7ED", + "8ED", + "9ED", + "AKH", + "ALA", + "ANA", + "ARC", + "ARN", + "ATH", + "AVR", + "BBD", + "BFZ", + "BRB", + "BTD", + "C13", + "C14", + "C15", + "C16", + "C17", + "C18", + "C19", + "CED", + "CEI", + "CHK", + "CM2", + "CMA", + "CMD", + "CST", + "DD1", + "DD2", + "DDE", + "DDG", + "DDH", + "DDI", + "DDJ", + "DDK", + "DDL", + "DDN", + "DDP", + "DDS", + "DDT", + "DDU", + "DKM", + "DOM", + "DPA", + "DTK", + "E01", + "ELD", + "EVG", + "FBB", + "FRF", + "G17", + "GK1", + "GK2", + "GN2", + "GNT", + "GRN", + "GS1", + "H09", + "HOP", + "HOU", + "ICE", + "IKO", + "INV", + "ISD", + "ITP", + "J14", + "JVC", + "KLD", + "KTK", + "LEA", + "LEB", + "LRW", + "M10", + "M11", + "M12", + "M13", + "M14", + "M15", + "M19", + "M20", + "MBS", + "ME1", + "ME3", + "MIR", + "MMQ", + "MRD", + "NPH", + "ODY", + "ONS", + "ORI", + "P02", + "PAL00", + "PAL01", + "PAL03", + "PAL04", + "PAL05", + "PAL06", + "PAL99", + "PALP", + "PANA", + "PARL", + "PC2", + "PCA", + "PD2", + "PELP", + "PF19", + "PF20", + "PGPX", + "PGRU", + "PMPS", + "PMPS06", + "PMPS07", + "PMPS08", + "PMPS09", + "PMPS10", + "PMPS11", + "POR", + "PPP1", + "PRM", + "PRW2", + "PRWK", + "PS11", + "PSAL", + "PSS2", + "PSS3", + "PTC", + "PTK", + "PZ2", + "RAV", + "RIX", + "RNA", + "ROE", + "RQS", + "RTR", + "S99", + "SHM", + "SLD", + "SOI", + "SOM", + "SUM", + "TD0", + "THB", + "THS", + "TMP", + "TPR", + "TSP", + "UGL", + "UND", + "UNH", + "USG", + "UST", + "WAR", + "WC00", + "WC01", + "WC02", + "WC03", + "WC97", + "WC98", + "WC99", + "XLN", + "ZEN" + ], + "purchaseUrls":{ + "tcgplayer":"https://mtgjson.com/links/e8d9c3a3092cf897" + }, + "rarity":"common", + "rulings":[ + + ], + "scryfallId":"7f918a49-a046-4115-80b8-13490ed5cd0a", + "scryfallIllustrationId":"3b6d5669-f95b-4aba-9f9b-8eaa93b5e9e7", + "scryfallOracleId":"a3fb7228-e76b-4e96-a40e-20b5fed75685", + "subtypes":[ + "Mountain" + ], + "supertypes":[ + "Basic" + ], + "tcgplayerProductId":176618, + "text":"({T}: Add {R}.)", + "type":"Basic Land — Mountain", + "types":[ + "Land" + ], + "uuid":"e061ca4d-4882-51e2-8400-96bf29c1c463" + } + ], + "meta":{ + "date":"2020-04-27", + "pricesDate":"2020-04-27", + "version":"4.6.3+20200427" + }, + "name":"Arcane Tempo", + "releaseDate":"2019-04-12", + "sideBoard":[ + { + "artist":"James Arnold", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":5.0, + "count":1, + "edhrecRank":1700, + "foreignData":[ + { + "language":"German", + "multiverseId":443214, + "name":"Die Mirari-Hypothese", + "text":"(Je eine Sagenmarke beim Ins-Spiel-Kommen und nach deinem Ziehsegment. Opfern nach III.)\nI — Bringe eine Spontanzauberkarte deiner Wahl aus deinem Friedhof auf deine Hand zurück.\nII — Bringe eine Hexereikarte deiner Wahl aus deinem Friedhof auf deine Hand zurück.\nIII — Immer wenn du bis zum Ende des Zuges einen Spontanzauber oder eine Hexerei wirkst, kopiere ihn bzw. sie. Du kannst neue Ziele für die Kopie bestimmen.", + "type":"Verzauberung — Sage" + }, + { + "language":"Spanish", + "multiverseId":443483, + "name":"La conjetura del Mirari", + "text":"(En cuanto esta Saga entre y después de tu paso de robar, agrega un contador de sabiduría. Sacrifícala después de III.)\nI — Regresa la carta de instantáneo objetivo de tu cementerio a tu mano.\nII — Regresa la carta de conjuro objetivo de tu cementerio a tu mano.\nIII — Hasta el final del turno, siempre que lances un hechizo de instantáneo o de conjuro, cópialo. Puedes elegir nuevos objetivos para la copia.", + "type":"Encantamiento — Saga" + }, + { + "language":"French", + "multiverseId":443752, + "name":"La conjecture du Mirari", + "text":"(Au moment où cette saga arrive sur le champ de bataille et après votre étape de pioche, ajoutez un marqueur « sapience ». Sacrifiez après III.)\nI — Renvoyez une carte d'éphémère ciblée depuis votre cimetière dans votre main.\nII — Renvoyez une carte de rituel ciblée depuis votre cimetière dans votre main.\nIII — Jusqu'à la fin du tour, à chaque fois que vous lancez un sort d'éphémère ou de rituel, copiez-le. Vous pouvez choisir de nouvelles cibles pour cette copie.", + "type":"Enchantement : saga" + }, + { + "language":"Italian", + "multiverseId":444021, + "name":"Congettura sul Mirari", + "text":"(Mentre questa Saga entra e dopo la tua sottofase di acquisizione, aggiungi un segnalino sapere. Sacrifica dopo III.)\nI — Riprendi in mano una carta istantaneo bersaglio dal tuo cimitero.\nII — Riprendi in mano una carta stregoneria bersaglio dal tuo cimitero.\nIII — Fino alla fine del turno, ogniqualvolta lanci una magia istantaneo o stregoneria, copiala. Puoi scegliere nuovi bersagli per la copia.", + "type":"Incantesimo — Saga" + }, + { + "language":"Japanese", + "multiverseId":444290, + "name":"ミラーリ予想", + "text":"(この英雄譚が出た際とあなたのドロー・ステップの後に、伝承カウンターを1個加える。IIIの後に、生け贄に捧げる。)\nI ― あなたの墓地からインスタント・カード1枚を対象とし、それをあなたの手札に戻す。\nII ― あなたの墓地からソーサリー・カード1枚を対象とし、それをあなたの手札に戻す。\nIII ― ターン終了時まで、あなたがインスタントかソーサリーである呪文を唱えるたび、それをコピーする。あなたはそのコピーの新しい対象を選んでもよい。", + "type":"エンチャント — 英雄譚" + }, + { + "language":"Korean", + "multiverseId":444559, + "name":"미라리의 예측", + "text":"(이 서사시가 들어오면서 그리고 당신의 뽑기단 후에, 전승 카운터 한 개를 추가한다. III 이후에 희생한다.)\nI — 당신의 무덤에 있는 순간마법 카드를 목표로 정한다. 그 카드를 당신의 손으로 되돌린다.\nII — 당신의 무덤에 있는 집중마법 카드를 목표로 정한다. 그 카드를 당신의 손으로 되돌린다.\nIII — 턴종료까지, 당신이 순간마법 또는 집중마법 주문을 발동할 때마다, 그 주문을 복사한다. 당신은 그 복사본의 목표를 새로 정할 수 있다.", + "type":"부여마법 — 서사시" + }, + { + "language":"Portuguese (Brazil)", + "multiverseId":444828, + "name":"A Conjectura do Mirari", + "text":"(Conforme esta Saga entra e após sua etapa de compra, adicione um marcador de conhecimento. Sacrifique-a após III.)\nI — Devolva o card de mágica instantânea alvo de seu cemitério para sua mão.\nII — Devolva o card de feitiço alvo de seu cemitério para sua mão.\nIII — Até o final do turno, toda vez que você conjurar uma mágica instantânea ou um feitiço, copie-o. Você pode escolher novos alvos para a cópia.", + "type":"Encantamento — Saga" + }, + { + "language":"Russian", + "multiverseId":445097, + "name":"Гипотеза о Мирари", + "text":"(При выходе этой Саги и после вашего шага взятия карты добавьте один жетон знаний. Пожертвуйте после III.)\nI — Верните целевую карту мгновенного заклинания из вашего кладбища в вашу руку.\nII — Верните целевую карту волшебства из вашего кладбища в вашу руку.\nIII — До конца хода, каждый раз, когда вы разыгрываете мгновенное заклинание или заклинание волшебства, скопируйте его. Вы можете выбрать новые цели для той копии.", + "type":"Чары — Сага" + }, + { + "language":"Chinese Simplified", + "multiverseId":445366, + "name":"映奇宝珠探究", + "text":"(于此传纪进战场时及于你抓牌步骤后,加一个学问指示物。到III后牺牲之。)\nI — 将目标瞬间牌从你的坟墓场移回你手上。\nII — 将目标法术牌从你的坟墓场移回你手上。\nIII — 直到回合结束,每当你施放瞬间或法术咒语时,将其复制。你可以为该复制品选择新的目标。", + "type":"结界 ~传纪" + }, + { + "language":"Chinese Traditional", + "multiverseId":445635, + "name":"映奇寶珠探究", + "text":"(於此傳紀進戰場時及於你抽牌步驟後,加一個學問指示物。到III後犧牲之。)\nI — 將目標瞬間牌從你的墳墓場移回你手上。\nII — 將目標巫術牌從你的墳墓場移回你手上。\nIII — 直到回合結束,每當你施放瞬間或巫術咒語時,將其複製。你可以為該複製品選擇新的目標。", + "type":"結界 ~傳紀" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"saga", + "legalities":{ + "commander":"Legal", + "duel":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "vintage":"Legal" + }, + "manaCost":"{4}{U}", + "mcmId":319825, + "mcmMetaId":239456, + "mtgArenaId":67218, + "mtgoId":67579, + "multiverseId":442945, + "name":"The Mirari Conjecture", + "number":"57", + "originalText":"(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI — Return target instant card from your graveyard to your hand.\nII — Return target sorcery card from your graveyard to your hand.\nIII — Until end of turn, whenever you cast an instant or sorcery spell, copy it. You may choose new targets for the copy.", + "originalType":"Enchantment — Saga", + "prices":{ + "mtgo":{ + "2020-04-27":0.02 + }, + "mtgoFoil":{ + "2020-04-27":0.24 + }, + "paper":{ + "2020-04-26":0.25 + }, + "paperFoil":{ + "2020-04-26":1.02 + } + }, + "printings":[ + "DOM", + "MB1", + "PDOM" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/b810138b43bd610a", + "tcgplayer":"https://mtgjson.com/links/59dac989507e4177" + }, + "rarity":"rare", + "rulings":[ + { + "date":"2018-04-27", + "text":"The Mirari Conjecture’s final chapter ability copies any instant or sorcery spell you cast, not just those with targets." + }, + { + "date":"2018-04-27", + "text":"The copy is created on the stack, so it’s not “cast.” Abilities that trigger when a player casts a spell won’t trigger." + }, + { + "date":"2018-04-27", + "text":"The copy will have the same targets as the spell it’s copying unless you choose new ones. You may change any number of the targets, including all of them or none of them. If, for one of the targets, you can’t choose a new legal target, then it remains unchanged (even if the current target is illegal)." + }, + { + "date":"2018-04-27", + "text":"If the spell that’s copied is modal (that is, it says “Choose one —” or the like), the copy will have the same mode. A different mode can’t be chosen." + }, + { + "date":"2018-04-27", + "text":"If the spell that’s copied has an X whose value was determined as it was cast (like Jaya’s Immolating Inferno does), the copy will have the same value of X." + }, + { + "date":"2018-04-27", + "text":"If the spell has damage divided as it was cast (like Fight with Fire does when kicked), the division can’t be changed (although the targets receiving that damage still can)." + }, + { + "date":"2018-04-27", + "text":"The controller of a copy can’t choose to pay any alternative or additional costs for the copy. However, effects based on any alternative or additional costs that were paid for the original spell are copied as though those same costs were paid for the copy." + }, + { + "date":"2018-04-27", + "text":"As a Saga enters the battlefield, its controller puts a lore counter on it. As your precombat main phase begins (immediately after your draw step), you put another lore counter on each Saga you control. Putting a lore counter on a Saga in either of these ways doesn’t use the stack." + }, + { + "date":"2018-04-27", + "text":"Each symbol on the left of a Saga’s text box represents a chapter ability. A chapter ability is a triggered ability that triggers when a lore counter that is put on the Saga causes the number of lore counters on the Saga to become equal to or greater than the ability’s chapter number. Chapter abilities are put onto the stack and may be responded to." + }, + { + "date":"2018-04-27", + "text":"A chapter ability doesn’t trigger if a lore counter is put on a Saga that already had a number of lore counters greater than or equal to that chapter’s number. For example, the third lore counter put on a Saga causes the III chapter ability to trigger, but I and II won’t trigger again." + }, + { + "date":"2018-04-27", + "text":"Once a chapter ability has triggered, the ability on the stack won’t be affected if the Saga gains or loses counters, or if it leaves the battlefield." + }, + { + "date":"2018-04-27", + "text":"If multiple chapter abilities trigger at the same time, their controller puts them on the stack in any order. If any of them require targets, those targets are chosen as you put the abilities on the stack, before any of those abilities resolve." + }, + { + "date":"2018-04-27", + "text":"If counters are removed from a Saga, the appropriate chapter abilities will trigger again when the Saga receives lore counters. Removing lore counters won’t cause a previous chapter ability to trigger." + }, + { + "date":"2018-04-27", + "text":"Once the number of lore counters on a Saga is greater than or equal to the greatest number among its chapter abilities—in the Dominaria set, this is always three—the Saga’s controller sacrifices it as soon as its chapter ability has left the stack, most likely by resolving or being countered. This state-based action doesn’t use the stack." + } + ], + "scryfallId":"1c68954c-4bab-4973-9819-ecd084438303", + "scryfallIllustrationId":"be099eea-cb0f-4223-89be-f10f683adaf9", + "scryfallOracleId":"56528234-46c0-4fba-972a-0c8011996f8d", + "subtypes":[ + "Saga" + ], + "supertypes":[ + + ], + "tcgplayerProductId":162241, + "text":"(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI — Return target instant card from your graveyard to your hand.\nII — Return target sorcery card from your graveyard to your hand.\nIII — Until end of turn, whenever you cast an instant or sorcery spell, copy it. You may choose new targets for the copy.", + "type":"Enchantment — Saga", + "types":[ + "Enchantment" + ], + "uuid":"3533640b-39e0-54e7-ae60-b194fc9dddb0" + }, + { + "artist":"Titus Lunter", + "borderColor":"black", + "colorIdentity":[ + "R", + "U" + ], + "colors":[ + "R", + "U" + ], + "convertedManaCost":3.0, + "count":1, + "edhrecRank":4282, + "foreignData":[ + { + "language":"German", + "multiverseId":453163, + "name":"Signalturm-Blitz", + "text":"Der Signalturm-Blitz fügt einer Kreatur deiner Wahl Schaden in Höhe der Gesamtanzahl an Spontanzauber- und Hexerei-Karten zu, die du besitzt und die sich im Exil oder in deinem Friedhof befinden.\nKatalyse (Du kannst diese Karte aus deinem Friedhof wirken, indem du zusätzlich zu ihren anderen Kosten eine Karte abwirfst. Schicke sie danach ins Exil.)", + "type":"Hexerei" + }, + { + "language":"Spanish", + "multiverseId":453422, + "name":"Rayo de la atalaya", + "text":"El Rayo de la atalaya hace una cantidad de daño a la criatura objetivo igual a la cantidad total de cartas de instantáneo y de conjuro en tu cementerio y en el exilio de las cuales eres propietario.\nRecargar. (Puedes lanzar esta carta desde tu cementerio descartando una carta además de pagar sus otros costes. Luego, exilia esta carta.)", + "type":"Conjuro" + }, + { + "language":"French", + "multiverseId":453681, + "name":"Éclair du flambeau", + "text":"L'Éclair du flambeau inflige à une créature ciblée un nombre de blessures égal au nombre total de cartes d'éphémère et de rituel que vous possédez en exil et dans votre cimetière.\nRelancez (Vous pouvez lancer cette carte depuis votre cimetière en vous défaussant d'une carte en plus de payer ses autres coûts. Puis exilez cette carte.)", + "type":"Rituel" + }, + { + "language":"Italian", + "multiverseId":453940, + "name":"Saetta del Faro", + "text":"La Saetta del Faro infligge a una creatura bersaglio danno pari al numero totale di carte istantaneo e stregoneria che possiedi in esilio e nel tuo cimitero.\nCarica d'avvio (Puoi lanciare questa carta dal tuo cimitero scartando una carta oltre a pagare i suoi altri costi. Poi esilia questa carta.)", + "type":"Stregoneria" + }, + { + "language":"Japanese", + "multiverseId":454199, + "name":"標の稲妻", + "text":"クリーチャー1体を対象とする。標の稲妻はそれに、追放領域かあなたの墓地にあり、あなたがオーナーであり、インスタントかソーサリーであるカードの枚数に等しい点数のダメージを与える。\n再活(あなたはあなたの墓地から、このカードを、これの他のコストの支払いに加えてカード1枚を捨てることで唱えてもよい。その後、このカードを追放する。)", + "type":"ソーサリー" + }, + { + "language":"Korean", + "multiverseId":454458, + "name":"봉화의 번개", + "text":"생물을 목표로 정한다. 봉화의 번개는 그 생물에게 추방 영역과 당신의 무덤에 있는 당신이 소유한 순간마법과 집중마법 카드 수의 합만큼 피해를 입힌다.\n강제 시동 (당신은 이 카드의 다른 비용들을 지불하는 것에 추가로 카드를 한 장 버리는 것으로 이 카드를 당신의 무덤에서 발동할 수 있다. 그 후 이 카드를 추방한다.)", + "type":"집중마법" + }, + { + "language":"Portuguese (Brazil)", + "multiverseId":454717, + "name":"Raio do Farol", + "text":"Raio do Farol causa à criatura alvo dano igual ao número total de cards de mágica instantânea ou feitiço que você possui no exílio e em seu cemitério.\nRecarregar (Você pode conjurar este card de seu cemitério descartando um card além de pagar seus outros custos. Depois, exile este card.)", + "type":"Feitiço" + }, + { + "language":"Russian", + "multiverseId":454976, + "name":"Сигнальный Разряд", + "text":"Сигнальный Разряд наносит целевому существу повреждения, равные суммарному количеству принадлежащих вам карт мгновенных заклинаний и волшебства в изгнании и на вашем кладбище.\nИмпульс (Вы можете разыграть эту карту из вашего кладбища, сбросив карту в дополнение к оплате других ее стоимостей. Затем изгоните эту карту.)", + "type":"Волшебство" + }, + { + "language":"Chinese Simplified", + "multiverseId":455235, + "name":"信标电击", + "text":"信标电击对目标生物造成伤害,其数量等同于放逐区中由你拥有之瞬间与法术牌数量和你坟墓场中这两类牌数量的加总。\n再起(你可以从你的坟墓场施放此牌,但必须支付其所需费用并额外弃一张牌。然后放逐此牌。)", + "type":"法术" + }, + { + "language":"Chinese Traditional", + "multiverseId":455494, + "name":"信標電擊", + "text":"信標電擊對目標生物造成傷害,其數量等同於放逐區中由你擁有之瞬間與巫術牌數量和你墳墓場中這兩類牌數量的加總。\n再起(你可以從你的墳墓場施放此牌,但必須支付其所需費用並額外棄一張牌。然後放逐此牌。)", + "type":"巫術" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{1}{U}{R}", + "mcmId":364198, + "mcmMetaId":265406, + "mtgArenaId":68615, + "mtgoId":69681, + "multiverseId":452904, + "name":"Beacon Bolt", + "number":"154", + "originalText":"Beacon Bolt deals damage to target creature equal to the total number of instant and sorcery cards you own in exile and in your graveyard.\nJump-start (You may cast this card from your graveyard by discarding a card in addition to paying its other costs. Then exile this card.)", + "originalType":"Sorcery", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.26 + }, + "paper":{ + "2020-04-26":0.18 + }, + "paperFoil":{ + "2020-04-26":0.44 + } + }, + "printings":[ + "GRN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/12cf454f26e64b85", + "tcgplayer":"https://mtgjson.com/links/6990423345ff57a8" + }, + "rarity":"uncommon", + "rulings":[ + { + "date":"2018-10-05", + "text":"Beacon Bolt is still on the stack while you count your instant and sorcery cards in your graveyard and in exile. It doesn’t count itself." + }, + { + "date":"2018-10-05", + "text":"If any exiled cards you own are face down, they have no characteristics. If they’re normally instants or sorceries, they won’t be counted." + }, + { + "date":"2018-10-05", + "text":"You must still follow any timing restrictions and permissions, including those based on the card’s type. For instance, you can cast a sorcery using jump-start only when you could normally cast a sorcery." + }, + { + "date":"2018-10-05", + "text":"A spell cast using jump-start will always be exiled afterward, whether it resolves, it’s countered, or it leaves the stack in some other way." + }, + { + "date":"2018-10-05", + "text":"If an effect allows you to pay an alternative cost rather than a spell’s mana cost, you may pay that alternative cost when you jump-start a spell. You’ll still discard a card as an additional cost to cast it." + }, + { + "date":"2018-10-05", + "text":"If a card with jump-start is put into your graveyard during your turn, you’ll be able to cast it right away if it’s legal to do so, before an opponent can take any actions." + } + ], + "scryfallId":"39c9e4b5-364b-4c0b-bb47-266563a6abf2", + "scryfallIllustrationId":"c2ce83a1-ddba-429c-9f7b-7e02585ea99d", + "scryfallOracleId":"884686dd-515c-4484-a324-e7d126903a42", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":176857, + "text":"Beacon Bolt deals damage to target creature equal to the total number of instant and sorcery cards you own in exile and in your graveyard.\nJump-start (You may cast this card from your graveyard by discarding a card in addition to paying its other costs. Then exile this card.)", + "type":"Sorcery", + "types":[ + "Sorcery" + ], + "uuid":"34b50961-dd25-54b3-b0e2-1c8071189342", + "watermark":"izzet" + }, + { + "artist":"Magali Villeneuve", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":2.0, + "count":3, + "edhrecRank":51, + "flavorText":"\"As one, nature lifts its voice to tell you this: 'No.'\"", + "foreignData":[ + { + "flavorText":"„Mit vereinter Kraft erhebt die Natur ihre Stimme und sagt: ‚Nein'.\"", + "language":"German", + "multiverseId":439904, + "name":"Negieren", + "text":"Neutralisiere einen Nichtkreatur-Zauberspruch deiner Wahl.", + "type":"Spontanzauber" + }, + { + "flavorText":"\"La naturaleza entera alza la voz para decirte con sus muchas bocas: 'No'\".", + "language":"Spanish", + "multiverseId":440107, + "name":"Negar", + "text":"Contrarresta el hechizo objetivo que no sea de criatura.", + "type":"Instantáneo" + }, + { + "flavorText":"« Unie, la nature lève la voix pour vous dire ceci : \"Non\". »", + "language":"French", + "multiverseId":440310, + "name":"Négation", + "text":"Contrecarrez le sort non-créature ciblé.", + "type":"Éphémère" + }, + { + "flavorText":"\"Unita, la natura leva la sua voce per dirvi: 'No'.\"", + "language":"Italian", + "multiverseId":440513, + "name":"Negare", + "text":"Neutralizza una magia non creatura bersaglio.", + "type":"Istantaneo" + }, + { + "flavorText":"「自然がね、あなたに向かって口をそろえて言うのよ。『だめだ。』ってね。」", + "language":"Japanese", + "multiverseId":440716, + "name":"否認", + "text":"クリーチャーでない呪文1つを対象とし、それを打ち消す。", + "type":"インスタント" + }, + { + "flavorText":"\"자연이 한 마음으로 네게 이 말을 전한다: '안돼.'\"", + "language":"Korean", + "multiverseId":440919, + "name":"부인", + "text":"생물 주문이 아닌 주문을 목표로 정한다. 그 주문을 무효화한다.", + "type":"순간마법" + }, + { + "flavorText":"\"Em uníssono, a natureza ergue a voz para lhes dizer: 'Não'.\"", + "language":"Portuguese (Brazil)", + "multiverseId":441122, + "name":"Negar", + "text":"Anule a mágica alvo que não seja de criatura.", + "type":"Mágica Instantânea" + }, + { + "flavorText":"«Вся природа поднимается, чтобы в унисон сказать вам: \"Нет\"».", + "language":"Russian", + "multiverseId":441325, + "name":"Отвергнуть", + "text":"Отмените целевое заклинание, не являющееся заклинанием существа.", + "type":"Мгновенное заклинание" + }, + { + "flavorText":"「自然万物齐声喝止:『不许。』」", + "language":"Chinese Simplified", + "multiverseId":441528, + "name":"失效", + "text":"反击目标非生物咒语。", + "type":"瞬间" + }, + { + "flavorText":"「自然萬物齊聲喝止:『不許。』」", + "language":"Chinese Traditional", + "multiverseId":441731, + "name":"失效", + "text":"反擊目標非生物咒語。", + "type":"瞬間" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "isReprint":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{1}{U}", + "mcmId":315384, + "mcmMetaId":9460, + "mtgArenaId":66705, + "mtgoId":66541, + "multiverseId":439701, + "name":"Negate", + "number":"44", + "originalText":"Counter target noncreature spell.", + "originalType":"Instant", + "prices":{ + "mtgo":{ + "2020-04-27":0.03 + }, + "mtgoFoil":{ + "2020-04-27":0.03 + }, + "paper":{ + "2020-04-26":0.13 + }, + "paperFoil":{ + "2020-04-26":1.29 + } + }, + "printings":[ + "AER", + "BBD", + "CN2", + "DPA", + "DTK", + "M10", + "M11", + "M12", + "M13", + "M14", + "M15", + "M20", + "MB1", + "MOR", + "OGW", + "ORI", + "P09", + "PM20", + "PRM", + "PS11", + "RIX", + "SS1" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/caa2177363f219b8", + "tcgplayer":"https://mtgjson.com/links/1b1e530eee2d6966" + }, + "rarity":"common", + "rulings":[ + { + "date":"2009-10-01", + "text":"A “creature spell” is any spell with the type creature, even if it has other types such as artifact or enchantment. Older cards of type summon are also creature spells." + } + ], + "scryfallId":"31534f45-43e6-4103-bf58-ad8fa688e4b0", + "scryfallIllustrationId":"5ebee670-4898-4816-a90a-3168fd54df0e", + "scryfallOracleId":"3407fe41-fdd3-4119-8f70-4bc4590a379f", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":155801, + "text":"Counter target noncreature spell.", + "type":"Instant", + "types":[ + "Instant" + ], + "uuid":"79ac58c7-0a82-55c9-a586-f9ce03facbc0" + }, + { + "artist":"Winona Nelson", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":2.0, + "count":3, + "edhrecRank":4919, + "flavorText":"The stronger the will, the more bewitching the song.", + "foreignData":[ + { + "flavorText":"Je stärker der Wille, desto berauschender die Melodie.", + "language":"German", + "multiverseId":435506, + "name":"Berauschende Melodie", + "text":"Übernimm die Kontrolle über eine Kreatur deiner Wahl mit umgewandelten Manakosten von X.", + "type":"Hexerei" + }, + { + "flavorText":"Cuanto más fuerte es la voluntad, más cautivadora resulta la canción.", + "language":"Spanish", + "multiverseId":437599, + "name":"Melodía de ensueño", + "text":"Gana el control de la criatura objetivo con coste de maná convertido de X.", + "type":"Conjuro" + }, + { + "flavorText":"Plus la volonté est forte, plus le chant est envoûtant.", + "language":"French", + "multiverseId":435805, + "name":"Mélodie enchanteresse", + "text":"Acquérez le contrôle d'une créature ciblée avec un coût converti de mana de X.", + "type":"Rituel" + }, + { + "flavorText":"Più forte è la volontà con cui si scontra, più affascinante diventa la melodia.", + "language":"Italian", + "multiverseId":436104, + "name":"Melodia Ammaliante", + "text":"Prendi il controllo di una creatura bersaglio con costo di mana convertito pari a X.", + "type":"Stregoneria" + }, + { + "flavorText":"意志が強ければ、歌が一層魅力的になる。", + "language":"Japanese", + "multiverseId":436403, + "name":"幻惑の旋律", + "text":"点数で見たマナ・コストがXのクリーチャー1体を対象とし、それのコントロールを得る。", + "type":"ソーサリー" + }, + { + "flavorText":"의지가 강할수록, 노래는 더 매혹적으로 들린다.", + "language":"Korean", + "multiverseId":436702, + "name":"넋을 빼앗는 선율", + "text":"전환마나비용이 X인 생물을 목표로 정한다. 그 생물의 조종권을 얻는다.", + "type":"집중마법" + }, + { + "flavorText":"Quanto maior a força de vontade, mais sedutora a canção.", + "language":"Portuguese (Brazil)", + "multiverseId":437001, + "name":"Melodia Arrebatadora", + "text":"Ganhe o controle da criatura alvo com custo de mana convertido igual a X.", + "type":"Feitiço" + }, + { + "flavorText":"Чем крепче воля, тем обворожительнее песня.", + "language":"Russian", + "multiverseId":437300, + "name":"Чарующая Мелодия", + "text":"Получите контроль над целевым существом с конвертированной мана-стоимостью X.", + "type":"Волшебство" + }, + { + "flavorText":"意志越坚定,旋律越诱人。", + "language":"Chinese Simplified", + "multiverseId":437898, + "name":"摄魂旋律", + "text":"获得目标总法术力费用为X的生物之操控权。", + "type":"法术" + }, + { + "flavorText":"意志越堅定,旋律越誘人。", + "language":"Chinese Traditional", + "multiverseId":438197, + "name":"攝魂旋律", + "text":"獲得目標總魔法力費用為X的生物之操控權。", + "type":"巫術" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "commander":"Legal", + "duel":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "vintage":"Legal" + }, + "manaCost":"{X}{U}{U}", + "mcmId":301201, + "mcmMetaId":226756, + "mtgArenaId":66071, + "mtgoFoilId":65123, + "mtgoId":65122, + "multiverseId":435207, + "name":"Entrancing Melody", + "number":"55", + "originalText":"Gain control of target creature with converted mana cost X.", + "originalType":"Sorcery", + "prices":{ + "mtgo":{ + "2020-04-27":0.02 + }, + "mtgoFoil":{ + "2020-04-27":0.32 + }, + "paper":{ + "2020-04-26":0.36 + }, + "paperFoil":{ + "2020-04-26":1.57 + } + }, + "printings":[ + "PXLN", + "XLN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/5288b79c558dfe33", + "tcgplayer":"https://mtgjson.com/links/9f15b660c9c26784" + }, + "rarity":"rare", + "rulings":[ + { + "date":"2017-09-29", + "text":"If a permanent has {X} in its mana cost, X is considered to be 0." + }, + { + "date":"2017-09-29", + "text":"The control-change effect of Entrancing Melody lasts indefinitely. It doesn’t wear off during the cleanup step." + }, + { + "date":"2017-09-29", + "text":"In a multiplayer game, if a player leaves the game, all cards that player owns leave as well, and any effects that give the player control of permanents immediately end." + } + ], + "scryfallId":"15732049-7e56-432f-881f-215e45b7a70e", + "scryfallIllustrationId":"e7a10776-f604-4dc6-be90-f0527d9b73e1", + "scryfallOracleId":"f9f82958-a7c6-4161-b0d8-fdbf7a3dd911", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":142004, + "text":"Gain control of target creature with converted mana cost X.", + "type":"Sorcery", + "types":[ + "Sorcery" + ], + "uuid":"b733efaa-e718-5f55-9eef-cd82286154d0" + }, + { + "artist":"Ben Wootten", + "borderColor":"black", + "colorIdentity":[ + "R" + ], + "colors":[ + "R" + ], + "convertedManaCost":3.0, + "count":3, + "edhrecRank":5742, + "flavorText":"Wary of the ferocious dinosaurs, the Legion of Dusk built up the walls of their fort—just in time for the pirates to burn them down.", + "foreignData":[ + { + "flavorText":"Aus Angst vor den wilden Dinosauriern hat die Legion des Zwielichts ihr Fort verstärkt, was jedoch die Piraten nicht abschreckt.", + "language":"German", + "multiverseId":435596, + "name":"Feurige Kanonade", + "text":"Die Feurige Kanonade fügt jeder Nicht-Pirat-Kreatur 2 Schadenspunkte zu.", + "type":"Spontanzauber" + }, + { + "flavorText":"Por temor a los feroces dinosaurios, la Legión del Crepúsculo rodeó su fortaleza de murallas... que fueron inmediatamente calcinadas por los piratas.", + "language":"Spanish", + "multiverseId":437689, + "name":"Cañonazo ardiente", + "text":"El Cañonazo ardiente hace 2 puntos de daño a cada criatura que no sea Pirata.", + "type":"Instantáneo" + }, + { + "flavorText":"Connaissant la férocité des dinosaures, la Légion du crépuscule entoura son fort de palissades — qui furent aussitôt réduites en cendres par les pirates.", + "language":"French", + "multiverseId":435895, + "name":"Canonnade ardente", + "text":"La Canonnade ardente inflige 2 blessures à chaque créature non-Pirate.", + "type":"Éphémère" + }, + { + "flavorText":"Temendo i feroci dinosauri, la Legione del Vespro rialzò le difese del suo forte, giusto in tempo per vederle ridotte in cenere dai pirati.", + "language":"Italian", + "multiverseId":436194, + "name":"Cannonata Fiammante", + "text":"La Cannonata Fiammante infligge 2 danni a ogni creatura non Pirata.", + "type":"Istantaneo" + }, + { + "flavorText":"獰猛な恐竜の対策として、薄暮の軍団は城砦の周りに壁を築いた。ちょうどそのとき、海賊船が現れてそれを焼き払った。", + "language":"Japanese", + "multiverseId":436493, + "name":"焦熱の連続砲撃", + "text":"焦熱の連続砲撃は、海賊でない各クリーチャーにそれぞれ2点のダメージを与える。", + "type":"インスタント" + }, + { + "flavorText":"황혼의 군단은 사나운 공룡들을 경계하여 요새 방책을 세웠으나, 때마침 나타난 해적들이 이를 모두 불태워버렸다.", + "language":"Korean", + "multiverseId":436792, + "name":"맹렬한 연속포격", + "text":"맹렬한 연속포격은 해적이 아닌 각 생물에게 피해 2점을 입힌다.", + "type":"순간마법" + }, + { + "flavorText":"Receosa dos ferozes dinossauros, a Legião do Crepúsculo ergueu as muralhas de seu forte, que foram prontamente incendiadas pelos piratas.", + "language":"Portuguese (Brazil)", + "multiverseId":437091, + "name":"Canhonada Ardente", + "text":"Canhonada Ardente causa 2 pontos de dano a cada criatura que não seja um Pirata.", + "type":"Mágica Instantânea" + }, + { + "flavorText":"Опасаясь свирепых динозавров, Легион Заката окружил свой форт частоколом — как раз к прибытию обожающих устраивать пожары пиратов.", + "language":"Russian", + "multiverseId":437390, + "name":"Огненная Канонада", + "text":"Огненная Канонада наносит 2 повреждения каждому не являющемуся Пиратом существу.", + "type":"Мгновенное заклинание" + }, + { + "flavorText":"暮影军团为了抵御恐龙侵袭,架高了要塞的壁垒~但完工时正好赶上海盗炮火,全部付诸一炬。", + "language":"Chinese Simplified", + "multiverseId":437988, + "name":"激烈炮击", + "text":"激烈炮击对每个非海盗的生物各造成2点伤害。", + "type":"瞬间" + }, + { + "flavorText":"暮影軍團為了抵禦恐龍侵襲,架高了要塞的壁壘~但完工時正好趕上海盜炮火,全部付諸一炬。", + "language":"Chinese Traditional", + "multiverseId":438287, + "name":"激烈炮擊", + "text":"激烈炮擊對每個非海盜的生物各造成2點傷害。", + "type":"瞬間" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "commander":"Legal", + "duel":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pioneer":"Legal", + "vintage":"Legal" + }, + "manaCost":"{2}{R}", + "mcmId":301792, + "mcmMetaId":227020, + "mtgArenaId":66251, + "mtgoFoilId":65303, + "mtgoId":65302, + "multiverseId":435297, + "name":"Fiery Cannonade", + "number":"143", + "originalText":"Fiery Cannonade deals 2 damage to each non-Pirate creature.", + "originalType":"Instant", + "prices":{ + "mtgo":{ + "2020-04-27":0.03 + }, + "mtgoFoil":{ + "2020-04-27":0.02 + }, + "paper":{ + "2020-04-26":0.09 + }, + "paperFoil":{ + "2020-04-26":0.5 + } + }, + "printings":[ + "XLN" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/d017f19b7a0510c4", + "tcgplayer":"https://mtgjson.com/links/b9eb0af51ad72aa1" + }, + "rarity":"uncommon", + "rulings":[ + + ], + "scryfallId":"664d21c9-4b6c-4797-845f-7bca79c2b76b", + "scryfallIllustrationId":"bbef4f87-7598-43d4-ae85-3bedcac5c9d8", + "scryfallOracleId":"7e108b31-2ea3-4129-b324-a2d31c78bbe6", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":145808, + "text":"Fiery Cannonade deals 2 damage to each non-Pirate creature.", + "type":"Instant", + "types":[ + "Instant" + ], + "uuid":"f265087e-e26e-536d-ae9a-91a29bd587df" + }, + { + "artist":"Grzegorz Rutkowski", + "borderColor":"black", + "colorIdentity":[ + "R" + ], + "colors":[ + "R" + ], + "convertedManaCost":1.0, + "count":2, + "edhrecRank":8170, + "flavorText":"Sometimes Shivan dragons toy with their food. Other times they just cook it.", + "foreignData":[ + { + "flavorText":"Manchmal spielen shivanische Drachen mit ihrem Essen. Aber manchmal soll es einfach nur gut durch sein.", + "language":"German", + "multiverseId":443299, + "name":"Shivanisches Feuer", + "text":"Bonus {4} (Du kannst zusätzlich {4} bezahlen, sowie du diesen Zauberspruch wirkst.)\nDas Shivanische Feuer fügt einer Kreatur deiner Wahl 2 Schadenspunkte zu. Falls die Bonuskosten dieses Zauberspruchs bezahlt wurden, fügt er der Kreatur stattdessen 4 Schadenspunkte zu.", + "type":"Spontanzauber" + }, + { + "flavorText":"Los dragones shivanos juegan de vez en cuando con la comida. Otras veces, la asan y ya está.", + "language":"Spanish", + "multiverseId":443568, + "name":"Fuego shivano", + "text":"Estímulo {4}. (Puedes pagar {4} adicionales al lanzar este hechizo.)\nEl Fuego shivano hace 2 puntos de daño a la criatura objetivo. Si este hechizo fue estimulado, en vez de eso, hace 4 puntos de daño a esa criatura.", + "type":"Instantáneo" + }, + { + "flavorText":"Parfois, les dragons shivâns jouent avec leur nourriture. Parfois, ils se contentent de la cuire.", + "language":"French", + "multiverseId":443837, + "name":"Feu shivân", + "text":"Kick {4} (Vous pouvez payer {4} supplémentaires au moment où vous lancez ce sort.)\nLe Feu shivân inflige 2 blessures à la créature ciblée. Si ce sort a été kické, il inflige 4 blessures à cette créature à la place.", + "type":"Éphémère" + }, + { + "flavorText":"A volte i draghi di Shiv si divertono con il loro cibo. Altre, invece, si limitano a cucinarlo.", + "language":"Italian", + "multiverseId":444106, + "name":"Fuoco di Shiv", + "text":"Potenziamento {4} (Puoi pagare {4} addizionale mentre lanci questa magia.)\nIl Fuoco di Shiv infligge 2 danni a una creatura bersaglio. Se questa magia è stata potenziata, infligge invece 4 danni a quella creatura.", + "type":"Istantaneo" + }, + { + "flavorText":"シヴのドラゴンは餌を弄ぶことがある。すぐに焼いて食べることもある。", + "language":"Japanese", + "multiverseId":444375, + "name":"シヴの火", + "text":"キッカー{4}(あなたはこの呪文を唱えるに際し、追加で{4}を支払ってもよい。)\nクリーチャー1体を対象とする。シヴの火はそれに2点のダメージを与える。この呪文がキッカーされていたなら、代わりに、これはそのクリーチャーに4点のダメージを与える。", + "type":"インスタント" + }, + { + "flavorText":"시브의 용들은 가끔 먹이를 장난감처럼 가지고 논다. 또 어떤 때는 그저 요리해버리기도 한다.", + "language":"Korean", + "multiverseId":444644, + "name":"시브의 불", + "text":"키커 {4} (당신은 이 주문을 발동하면서 추가로 {4}를 지불할 수 있다.)\n생물을 목표로 정한다. 시브의 불은 그 생물에게 피해 2점을 입힌다. 이 주문의 키커 비용이 지불되었다면, 시브의 불은 대신 그 생물에게 피해 4점을 입힌다.", + "type":"순간마법" + }, + { + "flavorText":"Às vezes, os dragões de Shiv brincam com a comida. Outras vezes eles só cozinham.", + "language":"Portuguese (Brazil)", + "multiverseId":444913, + "name":"Fogo de Shiv", + "text":"Reforçar {4} (Você pode pagar um custo adicional de {4} ao conjurar esta mágica.)\nFogo de Shiv causa 2 pontos de dano à criatura alvo. Se esta mágica foi reforçada, ela causa, em vez disso, 4 pontos de dano àquela criatura.", + "type":"Mágica Instantânea" + }, + { + "flavorText":"Иногда драконы Шива играют со своей едой. Обычно же они ее просто готовят.", + "language":"Russian", + "multiverseId":445182, + "name":"Шиванский Огонь", + "text":"Усилитель {4} (При разыгрывании этого заклинания вы можете дополнительно заплатить {4}.)\nШиванский Огонь наносит 2 повреждения целевому существу. Если это заклинание получило Усилитель, вместо этого оно наносит 4 повреждения тому существу.", + "type":"Мгновенное заклинание" + }, + { + "flavorText":"西瓦的巨龙偶尔会戏耍食物;大多时候是直接烧烤了事。", + "language":"Chinese Simplified", + "multiverseId":445451, + "name":"西瓦烈焰", + "text":"增幅{4}(你施放此咒语时可以额外支付{4}。)\n西瓦烈焰对目标生物造成2点伤害。如果此咒语已增幅,则改为它对该生物造成4点伤害。", + "type":"瞬间" + }, + { + "flavorText":"西瓦的巨龍偶爾會戲耍食物;大多時候是直接燒烤了事。", + "language":"Chinese Traditional", + "multiverseId":445720, + "name":"西瓦烈焰", + "text":"增幅{4}(你施放此咒語時可以額外支付{4})。\n西瓦烈焰對目標生物造成2點傷害。如果此咒語已增幅,則改為它對該生物造成4點傷害。", + "type":"瞬間" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "layout":"normal", + "legalities":{ + "commander":"Legal", + "duel":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "pioneer":"Legal", + "vintage":"Legal" + }, + "manaCost":"{R}", + "mcmId":355425, + "mcmMetaId":261133, + "mtgArenaId":67388, + "mtgoId":67749, + "multiverseId":443030, + "name":"Shivan Fire", + "number":"142", + "originalText":"Kicker {4} (You may pay an additional {4} as you cast this spell.)\nShivan Fire deals 2 damage to target creature. If this spell was kicked, it deals 4 damage to that creature instead.", + "originalType":"Instant", + "prices":{ + "mtgo":{ + "2020-04-27":0.05 + }, + "mtgoFoil":{ + "2020-04-27":0.05 + }, + "paper":{ + "2020-04-26":0.04 + }, + "paperFoil":{ + "2020-04-26":0.38 + } + }, + "printings":[ + "DOM" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/573af701fb9c1631", + "tcgplayer":"https://mtgjson.com/links/35e16668e16c30b8" + }, + "rarity":"common", + "rulings":[ + + ], + "scryfallId":"21b9d339-99ed-4923-8f56-be37f29a0bfa", + "scryfallIllustrationId":"419314d1-8bf2-4209-9bde-28a9a80772c2", + "scryfallOracleId":"854fd120-9a51-4d37-9922-7e4b0464e0f5", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":164777, + "text":"Kicker {4} (You may pay an additional {4} as you cast this spell.)\nShivan Fire deals 2 damage to target creature. If this spell was kicked, it deals 4 damage instead.", + "type":"Instant", + "types":[ + "Instant" + ], + "uuid":"198f534b-0dc1-543f-b5e3-35e7d40be1e5" + }, + { + "artist":"Deruchenko Alexander", + "borderColor":"black", + "colorIdentity":[ + "U" + ], + "colors":[ + "U" + ], + "convertedManaCost":2.0, + "count":2, + "edhrecRank":815, + "flavorText":"\"Laws apply only to those who lack the vision to see past them.\"", + "foreignData":[ + { + "flavorText":"„Gesetze gelten nur für jene, denen der Wagemut fehlt, über sie hinwegzugehen.\"", + "language":"German", + "multiverseId":453046, + "name":"Verächtlicher Hieb", + "text":"Neutralisiere einen Zauberspruch deiner Wahl mit umgewandelten Manakosten von 4 oder mehr.", + "type":"Spontanzauber" + }, + { + "flavorText":"\"Las leyes se aplican solo a los que son incapaces de ver más allá de ellas\".", + "language":"Spanish", + "multiverseId":453305, + "name":"Golpe altanero", + "text":"Contrarresta el hechizo objetivo con coste de maná convertido de 4 o más.", + "type":"Instantáneo" + }, + { + "flavorText":"« Les lois ne s'appliquent qu'à ceux qui manquent d'imagination pour les contourner. »", + "language":"French", + "multiverseId":453564, + "name":"Botte dédaigneuse", + "text":"Contrecarrez le sort avec un coût converti de mana supérieur ou égal à 4 ciblé.", + "type":"Éphémère" + }, + { + "flavorText":"\"Le leggi si applicano solo a chi non riesce a vedere oltre esse.\"", + "language":"Italian", + "multiverseId":453823, + "name":"Fendente Sdegnoso", + "text":"Neutralizza una magia bersaglio con costo di mana convertito pari o superiore a 4.", + "type":"Istantaneo" + }, + { + "flavorText":"「法が適用されるのは、その裏を見通す目を持たない者のみだ。」", + "language":"Japanese", + "multiverseId":454082, + "name":"軽蔑的な一撃", + "text":"点数で見たマナ・コストが4以上の呪文1つを対象とし、それを打ち消す。", + "type":"インスタント" + }, + { + "flavorText":"\"법은 그것들 너머를 보는 시각이 없는 자들에게만 적용되지.\"", + "language":"Korean", + "multiverseId":454341, + "name":"경멸의 일격", + "text":"전환마나비용이 4 이상인 주문을 목표로 정한다. 그 주문을 무효화한다.", + "type":"순간마법" + }, + { + "flavorText":"\"As leis só se aplicam aos que carecem da capacidade de ver além delas.\"", + "language":"Portuguese (Brazil)", + "multiverseId":454600, + "name":"Golpe Desdenhoso", + "text":"Anule a mágica alvo com custo de mana convertido igual ou superior a 4.", + "type":"Mágica Instantânea" + }, + { + "flavorText":"«Законы применяются только к тем, у кого не хватает воображения взглянуть сквозь них».", + "language":"Russian", + "multiverseId":454859, + "name":"Презрительный Удар", + "text":"Отмените целевое заклинание с конвертированной мана-стоимостью 4 или больше.", + "type":"Мгновенное заклинание" + }, + { + "flavorText":"「律法只能约束那些无法看穿它的人。」", + "language":"Chinese Simplified", + "multiverseId":455118, + "name":"倨傲击", + "text":"反击目标总法术力费用等于或大于4的咒语。", + "type":"瞬间" + }, + { + "flavorText":"「律法只能約束那些無法看穿它的人。」", + "language":"Chinese Traditional", + "multiverseId":455377, + "name":"倨傲擊", + "text":"反擊目標總魔法力費用等於或大於4的咒語。", + "type":"瞬間" + } + ], + "frameVersion":"2015", + "hasFoil":true, + "hasNonFoil":true, + "isArena":true, + "isFoil":false, + "isMtgo":true, + "isPaper":true, + "isReprint":true, + "layout":"normal", + "legalities":{ + "brawl":"Legal", + "commander":"Legal", + "duel":"Legal", + "future":"Legal", + "historic":"Legal", + "legacy":"Legal", + "modern":"Legal", + "pauper":"Legal", + "penny":"Legal", + "pioneer":"Legal", + "standard":"Legal", + "vintage":"Legal" + }, + "manaCost":"{1}{U}", + "mcmId":364114, + "mcmMetaId":210285, + "mtgArenaId":68498, + "mtgoId":69447, + "multiverseId":452787, + "name":"Disdainful Stroke", + "number":"37", + "originalText":"Counter target spell with converted mana cost 4 or greater.", + "originalType":"Instant", + "prices":{ + "mtgo":{ + "2020-04-27":0.01 + }, + "mtgoFoil":{ + "2020-04-27":0.02 + }, + "paper":{ + "2020-04-26":0.13 + }, + "paperFoil":{ + "2020-04-26":0.91 + } + }, + "printings":[ + "C16", + "CM2", + "F15", + "GRN", + "KTK", + "PRM" + ], + "purchaseUrls":{ + "cardmarket":"https://mtgjson.com/links/417381eeeb3bf8bd", + "tcgplayer":"https://mtgjson.com/links/b6a3cf5023003789" + }, + "rarity":"common", + "rulings":[ + { + "date":"2014-09-20", + "text":"A face-down spell has converted mana cost 0 and can’t be targeted by Disdainful Stroke." + }, + { + "date":"2018-10-05", + "text":"If a spell has {X} in its mana cost, include the value chosen for that X when determining the converted mana cost of that spell." + } + ], + "scryfallId":"0193dfa3-8409-44be-b4be-6c3cad42d4a4", + "scryfallIllustrationId":"3201547b-1aaf-4c5c-a186-b52b91831971", + "scryfallOracleId":"11e02134-7b1a-46a4-a89e-7539dd1efada", + "subtypes":[ + + ], + "supertypes":[ + + ], + "tcgplayerProductId":176441, + "text":"Counter target spell with converted mana cost 4 or greater.", + "type":"Instant", + "types":[ + "Instant" + ], + "uuid":"0a1a353e-d368-59c0-9c26-e16460cf3784" + } + ], + "type":"Challenger Deck" +} \ No newline at end of file diff --git a/Utils/keywords.txt b/Utils/keywords.txt index efa2cfe60c..2d66132a34 100644 --- a/Utils/keywords.txt +++ b/Utils/keywords.txt @@ -64,6 +64,7 @@ Miracle|cost| Mountaincycling|cost| Mountainwalk|new| Morph|card, cost| +Mutate|card, manaString| Myriad|new| Ninjutsu|cost| Outlast|cost| diff --git a/Utils/known-sets.txt b/Utils/known-sets.txt index e38a5014d0..d2726adaee 100644 --- a/Utils/known-sets.txt +++ b/Utils/known-sets.txt @@ -27,6 +27,7 @@ Commander 2016 Edition|Commander2016Edition| Commander 2017 Edition|Commander2017Edition| Commander 2018|Commander2018Edition| Commander 2019|Commander2019Edition| +Commander 2020|Commander2020Edition| Commander Anthology|CommanderAnthology| Commander Anthology 2018|CommanderAnthology2018| Commander's Arsenal|CommandersArsenal| @@ -34,6 +35,7 @@ Conflux|Conflux| Conspiracy: Take the Crown|ConspiracyTakeTheCrown| Core Set 2019|CoreSet2019| Core Set 2020|CoreSet2020| +Core Set 2021|CoreSet2021| Dark Ascension|DarkAscension| Darksteel|Darksteel| Dissension|Dissension| @@ -105,12 +107,14 @@ Homelands|Homelands| Hour of Devastation|HourOfDevastation| Ice Age|IceAge| IconicMasters|IconicMasters| +Ikoria: Lair of Behemoths|IkoriaLairOfBehemoths| Innistrad|Innistrad| Invasion|Invasion| Ixalan|Ixalan| Journey into Nyx|JourneyIntoNyx| Judge Promo|JudgePromo| Judgment|Judgment| +Jumpstart|Jumpstart| Kaladesh|Kaladesh| Khans of Tarkir|KhansOfTarkir| Launch Party|LaunchParty| diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 31af267772..50a1a042a1 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -36728,3 +36728,1605 @@ Slaying Fire|Throne of Eldraine|394|U|{2}{R}|Instant|||Slaying Fire deals 3 dama Kenrith's Transformation|Throne of Eldraine|395|U|{1}{G}|Enchantment - Aura|||Enchant creature$When Kenrith's Transformation enters the battlefield, draw a card.$Enchanted creature loses all abilities and is a green Elk creature with base power and toughness 3/3.| Improbable Alliance|Throne of Eldraine|396|U|{U}{R}|Enchantment|||Whenever you draw your second card each turn, create a 1/1 blue Faerie creature token with flying.${4}{U}{R}: Draw a card, then discard a card.| Inspiring Veteran|Throne of Eldraine|397|U|{R}{W}|Creature - Human Knight|2|2|Other Knights you control get +1/+1.| +Trynn, Champion of Freedom|Commander 2020|1|M|{3}{W}|Legendary Creature - Human Soldier|3|3|Partner with Silvar, Devourer of the Free$At the beginning of your end step, if you attacked this turn, create a 1/1 white Human Soldier creature token.| +Haldan, Avid Arcanist|Commander 2020|2|M|{2}{U}|Legendary Creature - Human Wizard|1|4|Partner with Pako, Arcane Retriever$You may play noncreature cards from exile with fetch counters on them if you exiled them, and you may spend mana as though it were mana of any color to cast those spells.| +Nikara, Lair Scavenger|Commander 2020|3|M|{2}{B}|Legendary Creature - Human Cleric|2|2|Partner with Yannik, Scavenging Sentinel$Menace$Whenever another creature you control leaves the battlefield, if it had one or more counters on it, you draw a card and lose 1 life.| +Brallin, Skyshark Rider|Commander 2020|4|M|{3}{R}|Legendary Creature - Human Shaman|3|3|Partner with Shabraz, the Skyshark$Whenever you discard a card, put a +1/+1 counter on Brallin, Skyshark Rider and it deals 1 damage to each opponent.${R}: Target Shark gains trample until end of turn.| +Cazur, Ruthless Stalker|Commander 2020|5|M|{3}{G}|Legendary Creature - Human Warrior|3|3|Partner with Ukkima, Stalking Shadow$Whenever a creature you control deals combat damage to a player, put a +1/+1 counter on that creature.| +Akim, the Soaring Wind|Commander 2020|6|M|{2}{U}{R}{W}|Legendary Creature - Bird Dinosaur|3|4|Flying$Whenever you create one or more tokens for the first time each turn, create a 1/1 white Bird creature token with flying.${3}{U}{R}{W}: Creature tokens you control gain double strike until end of turn.| +Gavi, Nest Warden|Commander 2020|7|M|{2}{U}{R}{W}|Legendary Creature - Human Shaman|2|5|You may pay {0} rather than pay the cycling cost of the first card you cycle each turn.$Whenever you draw your second card each turn, create a 2/2 red and white Dinosaur Cat creature token.| +Jirina Kudro|Commander 2020|8|M|{1}{R}{W}{B}|Legendary Creature - Human Soldier|3|3|When Jirina Kudro enters the battlefield, create a 1/1 white Human Soldier creature token for each time you've cast a commander from the command zone this game.$Other Humans you control get +2/+0.| +Kalamax, the Stormsire|Commander 2020|9|M|{1}{G}{U}{R}|Legendary Creature - Elemental Dinosaur|4|4|Whenever you cast your first instant spell each turn, if Kalamax, the Stormsire is tapped, copy that spell. You may choose new targets for the copy.$Whenever you copy an instant spell, put a +1/+1 counter on Kalamax.| +Kathril, Aspect Warper|Commander 2020|10|M|{2}{W}{B}{G}|Legendary Creature - Nightmare Insect|3|3|When Kathril, Aspect Warper enters the battlefield, put a flying counter on any creature you control if a creature card in your graveyard has flying. Repeat this process for first strike, double strike, deathtouch, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance. Then put a +1/+1 counter on Kathril for each counter put on a creature this way.| +Kelsien, the Plague|Commander 2020|11|M|{R}{W}{B}|Legendary Creature - Human Assassin|2|2|Vigilance, haste$Kelsien, the Plague gets +1/+1 for each experience counter you have.${T}: Kelsien deals 1 damage to target creature you don't control. When that creature dies this turn, you get an experience counter.| +Otrimi, the Ever-Playful|Commander 2020|12|M|{3}{B}{G}{U}|Legendary Creature - Nightmare Beast|6|6|Mutate {1}{B}{G}{U}$Trample$Whenever this creature deals combat damage to a player, return target creature card with mutate from your graveyard to your hand.| +Pako, Arcane Retriever|Commander 2020|13|M|{3}{R}{G}|Legendary Creature - Elemental Hound|3|3|Partner with Haldan, Avid Arcanist$Haste$Whenever Pako, Arcane Retriever attacks, exile the top card of each player's library and put a fetch counter on each of them. Put a +1/+1 counter on Pako for each noncreature card exiled this way.| +Shabraz, the Skyshark|Commander 2020|14|M|{3}{W}{U}|Legendary Creature - Shark Bird|3|3|Partner with Brallin, Skyshark Rider$Flying$Whenever you draw a card, put a +1/+1 counter on Shabraz, the Skyshark and you gain 1 life.${W/U}: Target Human gains flying until end of turn.| +Silvar, Devourer of the Free|Commander 2020|15|M|{3}{B}{R}|Legendary Creature - Cat Nightmare|4|2|Partner with Trynn, Champion of Freedom$Menace$Sacrifice a Human: Put a +1/+1 counter on Silvar, Devourer of the Free. It gains indestructible until end of turn.| +Tayam, Luminous Enigma|Commander 2020|16|M|{1}{W}{B}{G}|Legendary Creature - Nightmare Beast|3|3|Each other creature you control enters the battlefield with an additional vigilance counter on it.${3}, Remove three counters from among creatures you control: Put the top three cards of your library into your graveyard, then return a permanent card with converted mana cost 3 or less from your graveyard to the battlefield.| +Ukkima, Stalking Shadow|Commander 2020|17|M|{1}{U}{B}|Legendary Creature - Whale Wolf|2|2|Partner with Cazur, Ruthless Stalker$Ukkima, Stalking Shadow can't be blocked.$When Ukkima leaves the battlefield, it deals X damage to target player and you gain X life, where X is its power.| +Xyris, the Writhing Storm|Commander 2020|18|M|{2}{G}{U}{R}|Legendary Creature - Snake Leviathan|3|5|Flying$Whenever an opponent draws a card except the first one they draw in each of their draw steps, create a 1/1 green Snake creature token.$Whenever Xyris, the Writhing Storm deals combat damage to a player, you and that player each draw that many cards.| +Yannik, Scavenging Sentinel|Commander 2020|19|M|{2}{G}{W}|Legendary Creature - Hyena Beast|3|3|Partner with Nikara, Lair Scavenger$Vigilance$When Yannik, Scavenging Sentinel enters the battlefield, exile another creature you control until Yannik leaves the battlefield. When you do, distribute X +1/+1 counters among any number of target creatures, where X is the exiled creature's power.| +Zaxara, the Exemplary|Commander 2020|20|M|{1}{B}{G}{U}|Legendary Creature - Nightmare Hydra|2|3|Deathtouch${T}: Add two mana of any one color.$Whenever you cast a spell with {X} in its mana cost, create a 0/0 green Hydra creature token, then put X +1/+1 counters on it.| +Cryptic Trilobite|Commander 2020|21|R|{X}{X}|Creature - Trilobite|0|0|Cryptic Trilobite enters the battlefield with X +1/+1 counters on it.$Remove a +1/+1 counter from Cryptic Trilobite: Add {C}{C}. Spend this mana only to activate abilities.${1}, {T}: Put a +1/+1 counter on Cryptic Trilobite.| +Avenging Huntbonder|Commander 2020|22|R|{3}{W}{W}|Creature - Human Warrior|3|3|Double strike$Whenever Avenging Huntbonder attacks, put a double strike counter on another target attacking creature.| +Call the Coppercoats|Commander 2020|23|R|{2}{W}|Instant|||Strive — This spell costs {1}{W} more to cast for each target beyond the first.$Choose any number of target opponents. Create X 1/1 white Human soldier creature tokens, where X is the number of creatures those opponents control.| +Cartographer's Hawk|Commander 2020|24|R|{1}{W}|Creature - Bird|2|1|Flying$When Cartographer's Hawk deals combat damage to a player who controls more lands than you, return it to its owner's hand. If you do, you may search your library for a Plains card, put it onto the battlefield tapped, then shuffle your library.| +Dismantling Wave|Commander 2020|25|R|{2}{W}|Sorcery|||For each opponent, destroy up to one target artifact or enchantment that player controls.$Cycling {6}{W}{W}$When you cycle Dismantling Wave, destroy all artifacts and enchantments.| +Flawless Maneuver|Commander 2020|26|R|{2}{W}|Instant|||If you control a commander, you may cast this spell without paying its mana cost.$Creatures you control gain indestructible until end of turn.| +Herald of the Forgotten|Commander 2020|27|R|{6}{W}{W}|Creature - Cat Beast|6|6|Flying$When Herald of the Forgotten enters the battlefield, if you cast it, return any number of target permanent cards with cycling abilities from your graveyard to the battlefield.| +Martial Impetus|Commander 2020|28|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and is goaded.$Whenever enchanted creature attacks, each other creature that's attacking one of your opponents gets +1/+1 until end of turn.| +Verge Rangers|Commander 2020|29|R|{2}{W}|Creature - Human Scout|3|3|First strike$You may look at the top card of your library any time.$As long as an opponent controls more lands than you, you may play lands from the top of your library.| +Vitality Hunter|Commander 2020|30|R|{3}{W}|Creature - Nightmare|3|4|Lifelink${X}{W}{W}: Monstrosity X.$When Vitality Hunter becomes monstrous, put a lifelink counter on each of up to X target creatures.| +Crystalline Resonance|Commander 2020|31|R|{2}{U}|Enchantment|||Whenever you cycle a card, you may have Crystalline Resonance become a copy of another target permanent until your next turn, except it has this ability.| +Decoy Gambit|Commander 2020|32|R|{2}{U}|Instant|||For each opponent, choose up to one target creature that player controls, then return that creature to its owner's hand unless its controller has you draw a card.| +Eon Frolicker|Commander 2020|33|R|{2}{U}{U}|Creature - Elemental Otter|5|5|Flying$When Eon Frolicker enters the battlefield, if you cast it, target opponent takes an extra turn after this one. Until your next turn, you and planeswalkers you control gain protection from that player.| +Ethereal Forager|Commander 2020|34|R|{4}{U}{U}|Creature - Elemental Whale|3|3|Delve$Flying$Whenever Ethereal Forager attacks, you may return an instant or sorcery card exiled with Ethereal Forager to its owner's hand.| +Fierce Guardianship|Commander 2020|35|R|{2}{U}|Instant|||If you control a commander, you may cast this spell without paying its mana cost.$Counter target noncreature spell.| +Nascent Metamorph|Commander 2020|36|R|{1}{U}|Creature - Shapeshifter|1|1|Whenever Nascent Metamorph attacks or blocks, target opponent reveals cards from the top of their library until they reveal a creature card. Nascent Metamorph becomes a copy of that card until end of turn. Then that player puts all cards revealed this way on the bottom of their library in a random order.| +Psychic Impetus|Commander 2020|37|U|{2}{U}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and is goaded.$Whenever enchanted creature attacks, you scry 2.| +Souvenir Snatcher|Commander 2020|38|R|{4}{U}|Creature - Bird|4|4|Mutate {5}{U}$Flying$Whenever this creature mutates, gain control of target noncreature artifact.| +Tidal Barracuda|Commander 2020|39|R|{3}{U}|Creature - Fish|3|4|Any player may cast spells as though they had flash.$Your opponents can't cast spells during your turn.| +Boneyard Mycodrax|Commander 2020|40|R|{2}{B}|Creature - Fungus|*|*|Boneyard Mycodrax's power and toughness are each equal to the number of other creature cards in your graveyard.$Scavenge {4}{B}| +Daring Fiendbonder|Commander 2020|41|R|{3}{B}|Creature - Human Warlock|5|1|Haste$Daring Fiendbonder attacks each combat if able.${1}{B}, Exile Daring Fiendbonder from your graveyard: Put an indestructible counter on target creature. Activate this ability only any time you could cast a sorcery.| +Deadly Rollick|Commander 2020|42|R|{3}{B}|Instant|||If you control a commander, you may cast this spell without paying its mana cost.$Exile target creature.| +Dredge the Mire|Commander 2020|43|R|{3}{B}|Sorcery|||Each opponent chooses a creature card in their graveyard. Put those cards onto the battlefield under your control.| +Mindleecher|Commander 2020|44|R|{4}{B}{B}|Creature - Nightmare|5|5|Mutate {4}{B}$Flying$Whenever this creature mutates, exile the top card of each opponent's library face down. You may look at and play those cards for as long as they remain exiled.| +Netherborn Altar|Commander 2020|45|R|{1}{B}|Artifact|||{T}, Put a soul counter on Netherborn Altar: Put your commander into your hand from the command zone. Then you lose 3 life for each soul counter on Netherborn Altar.| +Parasitic Impetus|Commander 2020|46|U|{2}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and is goaded.$Whenever enchanted creature attacks, its controller loses 2 life and you gain 2 life.| +Species Specialist|Commander 2020|47|R|{2}{B}{B}|Creature - Human Warrior|2|3|As Species Specialist enters the battlefield, choose a creature type.$Whenever a creature of the chosen type dies, you may draw a card.| +Titan Hunter|Commander 2020|48|R|{4}{B}|Creature - Human Warrior|4|5|At the beginning of each player's end step, if no creatures died this turn, Titan Hunter deals 4 damage to that player.${1}{B}, Sacrifice a creature: You gain 4 life.| +Agitator Ant|Commander 2020|49|R|{2}{R}|Creature - Insect|2|2|At the beginning of your end step, each player may put two +1/+1 counters on a creature they control. Goad each creature that had counters put on it this way.| +Deflecting Swat|Commander 2020|50|R|{2}{R}|Instant|||If you control a commander, you may cast this spell without paying its mana cost.$You may choose new targets for target spell or ability.| +Fireflux Squad|Commander 2020|51|R|{3}{R}|Creature - Human Soldier|4|3|Haste$Whenever Fireflux Squad attacks, you may exile another target attacking creature you control. If you do, reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield tapped and attacking and the rest on the bottom of your library in a random order.| +Frontier Warmonger|Commander 2020|52|R|{3}{R}|Creature - Human Warrior|4|4|Whenever one or more creatures attack an opponent or a planeswalker an opponent controls, those creatures gain menace until end of turn.| +Lavabrink Floodgates|Commander 2020|53|R|{3}{R}|Artifact|||{T}: Add {R}{R}.$At the beginning of each player's upkeep, that player may put a doom counter on Lavabrink Floodgates or remove a doom counter from it. Then if it has three or more doom counters on it, sacrifice it. When you do, it deals 6 damage to each creature.| +Molten Echoes|Commander 2020|54|R|{2}{R}{R}|Enchantment|||As Molten Echoes enters the battlefield, choose a creature type.$Whenever a nontoken creature of the chosen type enters the battlefield under your control, create a token that's a copy of that creature. That token gains haste. Exile it at the beginning of the next end step.| +Shiny Impetus|Commander 2020|55|U|{2}{R}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and is goaded.$Whenever enchanted creature attacks, you create a Treasure token.| +Spellpyre Phoenix|Commander 2020|56|R|{3}{R}{R}|Creature - Phoenix|4|2|Flying$When Spellpyre Phoenix enters the battlefield, you may return target instant or sorcery card with a cycling ability from your graveyard to your hand.$At the beginning of each end step, if you cycled two or more cards this turn, return Spellpyre Phoenix from your graveyard to your hand.| +Surly Badgersaur|Commander 2020|57|R|{3}{R}|Creature - Badger Dinosaur|3|3|Whenever you discard a creature card, put a +1/+1 counter on Surly Badgersaur.$Whenever you discard a land card, create a treasure token.$Whenever you discard a noncreature, nonland card, Surly Badgersaur fights up to one target creature you don't control.| +Capricopian|Commander 2020|58|R|{X}{G}|Creature - Goat Hydra|0|0|Capricopian enters the battlefield with X +1/+1 counters on it.${2}: Put a +1/+1 counter on Capricopian, then you may reselect which player Capricopian is attacking. Only the player Capricopian is attacking may activate this ability and only during the declare attackers step.| +Curious Herd|Commander 2020|59|R|{3}{G}|Instant|||Choose target opponent. You create X 3/3 green Beast creature tokens, where X is the number of artifacts that player controls.| +Glademuse|Commander 2020|60|R|{2}{G}|Creature - Beast|2|4|Whenever a player casts a spell, if it's not their turn, that player draws a card.| +Obscuring Haze|Commander 2020|61|R|{2}{G}|Instant|||If you control a commander, you may cast this spell without paying its mana cost.$Prevent all damage that would be dealt this turn by creatures your opponents control.| +Predatory Impetus|Commander 2020|62|U|{4}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3, must be blocked if able, and is goaded.| +Ravenous Gigantotherium|Commander 2020|63|R|{5}{G}{G}|Creature - Beast|3|3|Devour 3$When Ravenous Gigantotherium enters the battlefield, it deals X damage divided as you choose among up to X target creatures, where X is its power. Each of those creatures deals damage equal to its power to Ravenous Gigantotherium.| +Sawtusk Demolisher|Commander 2020|64|R|{4}{G}{G}|Creature - Beast|6|6|Mutate {3}{G}$Trample$Whenever this creature mutates, destroy target noncreature permanent. Its controller creates a 3/3 green Beast creature token.| +Selective Adaptation|Commander 2020|65|R|{4}{G}{G}|Sorcery|||Reveal the top seven cards of your library. Choose from among them a card with flying, a card with first strike, and so on for double strike, deathtouch, haste, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance. Put one of the chosen cards onto the battlefield, the other chosen cards into your hand, and the rest into your graveyard.| +Slippery Bogbonder|Commander 2020|66|R|{3}{G}|Creature - Human Druid|3|3|Flash$Hexproof$When Slippery Bogbonder enters the battlefield, put a hexproof counter on target creature. Then move any number of counters from among creatures you control onto that creature.| +Bonder's Ornament|Commander 2020|67|C|{3}|Artifact|||{T}: Add one mana of any color.${4}, {T}: Each player who controls a permanent named Bonder's Ornament draws a card.| +Manascape Refractor|Commander 2020|68|R|{3}|Artifact|||Manascape Refractor enters the battlefield tapped.$Manascape Refractor has all activated abilities of all lands on the battlefield.$You may spend mana as though it were mana of any color to pay the activation costs of Manascape Refractor's abilities.| +Sanctuary Blade|Commander 2020|69|R|{2}|Artifact - Equipment|||As Sanctuary Blade becomes attached to a creature, choose a color.$Equipped creature gets +2/+0 and has protection from the last chosen color.$Equip {3}| +Twinning Staff|Commander 2020|70|R|{3}|Artifact|||If you would copy a spell one or more times, instead copy it that many times plus an additional time. You may choose new targets for the additional copy.${7}, {T}: Copy target instant or sorcery spell you control. You may choose new targets for the copy.| +Nesting Grounds|Commander 2020|71|R||Land|||{T}: Add {C}.${1}, {T}: Move a counter from target permanent you control onto another target permanent. Activate this ability only any time you could cast a sorcery.| +Aerial Responder|Commander 2020|72|U|{1}{W}{W}|Creature - Dwarf Soldier|2|3|Flying, vigilance, lifelink| +Akroma, Angel of Wrath|Commander 2020|73|M|{5}{W}{W}{W}|Legendary Creature - Angel|6|6|Flying, first strike, vigilance, trample, haste, protection from black and from red| +Akroma's Vengeance|Commander 2020|74|R|{4}{W}{W}|Sorcery|||Destroy all artifacts, creatures, and enchantments.$Cycling {3}| +Angel of Finality|Commander 2020|75|R|{3}{W}|Creature - Angel|3|4|Flying$When Angel of Finality enters the battlefield, exile all cards from target player's graveyard.| +Astral Drift|Commander 2020|76|R|{2}{W}|Enchantment|||Whenever you cycle Astral Drift or cycle another card while Astral Drift is on the battlefield, you may exile target creature. If you do, return that card to the battlefield under its owner's control at the beginning of the next end step.$Cycling {2}{W}| +Banisher Priest|Commander 2020|77|U|{1}{W}{W}|Creature - Human Cleric|2|2|When Banisher Priest enters the battlefield, exile target creature an opponent controls until Banisher Priest leaves the battlefield.| +Bounty Agent|Commander 2020|78|R|{1}{W}|Creature - Human Soldier|2|2|Vigilance${T}, Sacrifice Bounty Agent: Destroy target legendary permanent that's an artifact, creature, or enchantment.| +Cast Out|Commander 2020|79|U|{3}{W}|Enchantment|||Flash$When Cast Out enters the battlefield, exile target nonland permanent an opponent controls until Cast Out leaves the battlefield.$Cycling {W}| +Cataclysmic Gearhulk|Commander 2020|80|M|{3}{W}{W}|Artifact Creature - Construct|4|5|Vigilance$When Cataclysmic Gearhulk enters the battlefield, each player chooses an artifact, a creature, an enchantment, and a planeswalker from among the nonland permanents they control, then sacrifices the rest.| +Cavalry Pegasus|Commander 2020|81|C|{1}{W}|Creature - Pegasus|1|1|Flying$Whenever Cavalry Pegasus attacks, each attacking Human gains flying until end of turn.| +Citywide Bust|Commander 2020|82|R|{1}{W}{W}|Sorcery|||Destroy all creatures with toughness 4 or greater.| +Cleansing Nova|Commander 2020|83|R|{3}{W}{W}|Sorcery|||Choose one —$• Destroy all creatures.$• Destroy all artifacts and enchantments.| +Dearly Departed|Commander 2020|84|R|{4}{W}{W}|Creature - Spirit|5|5|Flying$As long as Dearly Departed is in your graveyard, each Human creature you control enters the battlefield with an additional +1/+1 counter on it.| +Decree of Justice|Commander 2020|85|R|{X}{X}{2}{W}{W}|Sorcery|||Create X 4/4 white Angel creature tokens with flying.$Cycling {2}{W}$When you cycle Decree of Justice, you may pay {X}. If you do, create X 1/1 white Soldier creature tokens.| +Descend upon the Sinful|Commander 2020|86|M|{4}{W}{W}|Sorcery|||Exile all creatures.$Delirium — Create a 4/4 white Angel creature token with flying if there are four or more card types among cards in your graveyard.| +Devout Chaplain|Commander 2020|87|U|{2}{W}|Creature - Human Cleric|2|2|{T}, Tap two untapped Humans you control: Exile target artifact or enchantment.| +Eternal Dragon|Commander 2020|88|R|{5}{W}{W}|Creature - Dragon Spirit|5|5|Flying${3}{W}{W}: Return Eternal Dragon from your graveyard to your hand. Activate this ability only during your upkeep.$Plainscycling {2}| +Frontline Medic|Commander 2020|89|R|{2}{W}|Creature - Human Cleric|3|3|Battalion — Whenever Frontline Medic and at least two other creatures attack, creatures you control gain indestructible until end of turn.$Sacrifice Frontline Medic: Counter target spell with {X} in its mana cost unless its controller pays {3}.| +Hoofprints of the Stag|Commander 2020|90|R|{1}{W}|Tribal Enchantment - Elemental|||Whenever you draw a card, you may put a hoofprint counter on Hoofprints of the Stag.${2}{W}, Remove four hoofprint counters from Hoofprints of the Stag: Create a 4/4 white Elemental creature token with flying. Activate this ability only during your turn.| +Increasing Devotion|Commander 2020|91|R|{3}{W}{W}|Sorcery|||Create five 1/1 white Human creature tokens. If this spell was cast from a graveyard, create ten of those tokens instead.$Flashback {7}{W}{W}| +Kalemne's Captain|Commander 2020|92|R|{3}{W}{W}|Creature - Giant Soldier|5|5|Vigilance${5}{W}{W}: Monstrosity 3.$When Kalemne's Captain becomes monstrous, exile all artifacts and enchantments.| +Knight of the White Orchid|Commander 2020|93|R|{W}{W}|Creature - Human Knight|2|2|First strike$When Knight of the White Orchid enters the battlefield, if an opponent controls more lands than you, you may search your library for a Plains card, put it onto the battlefield, then shuffle your library.| +Magus of the Disk|Commander 2020|94|R|{2}{W}{W}|Creature - Human Wizard|2|4|Magus of the Disk enters the battlefield tapped.${1}, {T}: Destroy all artifacts, creatures, and enchantments.| +Odric, Lunarch Marshal|Commander 2020|95|R|{3}{W}|Legendary Creature - Human Soldier|3|3|At the beginning of each combat, creatures you control gain first strike until end of turn if a creature you control has first strike. The same is true for flying, deathtouch, double strike, haste, hexproof, indestructible, lifelink, menace, reach, skulk, trample, and vigilance.| +Odric, Master Tactician|Commander 2020|96|R|{2}{W}{W}|Legendary Creature - Human Soldier|3|4|First strike$Whenever Odric, Master Tactician and at least three other creatures attack, you choose which creatures block this combat and how those creatures block.| +Reveillark|Commander 2020|97|R|{4}{W}|Creature - Elemental|4|3|Flying$When Reveillark leaves the battlefield, return up to two target creature cards with power 2 or less from your graveyard to the battlefield.$Evoke {5}{W}| +Riders of Gavony|Commander 2020|98|R|{2}{W}{W}|Creature - Human Knight|3|3|Vigilance$As Riders of Gavony enters the battlefield, choose a creature type.$Human creatures you control have protection from creatures of the chosen type.| +Solemn Recruit|Commander 2020|99|R|{1}{W}{W}|Creature - Dwarf Warrior|2|2|Double strike$Revolt — At the beginning of your end step, if a permanent you controlled left the battlefield this turn, put a +1/+1 counter on Solemn Recruit.| +Spirit Cairn|Commander 2020|100|U|{2}{W}|Enchantment|||Whenever a player discards a card, you may pay {W}. If you do, create a 1/1 white Spirit creature token with flying.| +Sun Titan|Commander 2020|101|M|{4}{W}{W}|Creature - Giant|6|6|Vigilance$Whenever Sun Titan enters the battlefield or attacks, you may return target permanent card with converted mana cost 3 or less from your graveyard to the battlefield.| +Sunblast Angel|Commander 2020|102|R|{4}{W}{W}|Creature - Angel|4|5|Flying$When Sunblast Angel enters the battlefield, destroy all tapped creatures.| +Thalia's Lieutenant|Commander 2020|103|R|{1}{W}|Creature - Human Soldier|1|1|When Thalia's Lieutenant enters the battlefield, put a +1/+1 counter on each other Human you control.$Whenever another Human enters the battlefield under your control, put a +1/+1 counter on Thalia's Lieutenant.| +Thraben Doomsayer|Commander 2020|104|R|{1}{W}{W}|Creature - Human Cleric|2|2|{T}: Create a 1/1 white Human creature token.$Fateful hour — As long as you have 5 or less life, other creatures you control get +2/+2.| +Together Forever|Commander 2020|105|R|{W}{W}|Enchantment|||When Together Forever enters the battlefield, support 2.${1}: Choose target creature with a counter on it. When that creature dies this turn, return that card to its owner's hand.| +Unexpectedly Absent|Commander 2020|106|R|{X}{W}{W}|Instant|||Put target nonland permanent into its owner's library just beneath the top X cards of that library.| +Zetalpa, Primal Dawn|Commander 2020|107|R|{6}{W}{W}|Legendary Creature - Elder Dinosaur|4|8|Flying, double strike, vigilance, trample, indestructible| +Chemister's Insight|Commander 2020|108|U|{3}{U}|Instant|||Draw two cards.$Jump-start| +Curator of Mysteries|Commander 2020|109|R|{2}{U}{U}|Creature - Sphinx|4|4|Flying$Whenever you cycle or discard another card, scry 1.$Cycling {U}| +Drake Haven|Commander 2020|110|R|{2}{U}|Enchantment|||Whenever you cycle or discard a card, you may pay {1}. If you do, create a 2/2 blue Drake creature token with flying.| +Frantic Search|Commander 2020|111|C|{2}{U}|Instant|||Draw two cards, then discard two cards. Untap up to three lands.| +Hieroglyphic Illumination|Commander 2020|112|C|{3}{U}|Instant|||Draw two cards.$Cycling {U}| +Illusory Ambusher|Commander 2020|113|U|{4}{U}|Creature - Cat Illusion|4|1|Flash$Whenever Illusory Ambusher is dealt damage, draw that many cards.| +Jace, Architect of Thought|Commander 2020|114|M|{2}{U}{U}|Legendary Planeswalker - Jace|4|+1: Until your next turn, whenever a creature an opponent controls attacks, it gets -1/-0 until end of turn.$−2: Reveal the top three cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other on the bottom of your library in any order.$−8: For each player, search that player's library for a nonland card and exile it, then that player shuffles their library. You may cast those cards without paying their mana costs.| +Lunar Mystic|Commander 2020|115|R|{2}{U}{U}|Creature - Human Wizard|2|2|Whenever you cast an instant spell, you may pay {1}. If you do, draw a card.| +Mind Spring|Commander 2020|116|R|{X}{U}{U}|Sorcery|||Draw X cards.| +Mulldrifter|Commander 2020|117|U|{4}{U}|Creature - Elemental|2|2|Flying$When Mulldrifter enters the battlefield, draw two cards.$Evoke {2}{U}| +Murmuring Mystic|Commander 2020|118|U|{3}{U}|Creature - Human Wizard|1|5|Whenever you cast an instant or sorcery spell, create a 1/1 blue Bird Illusion creature token with flying.| +New Perspectives|Commander 2020|119|R|{5}{U}|Enchantment|||When New Perspectives enters the battlefield, draw three cards.$As long as you have seven or more cards in hand, you may pay {0} rather than pay cycling costs.| +Niblis of Frost|Commander 2020|120|R|{2}{U}{U}|Creature - Spirit|3|3|Flying$Prowess$Whenever you cast an instant or sorcery spell, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.| +Nimble Obstructionist|Commander 2020|121|R|{2}{U}|Creature - Bird Wizard|3|1|Flash$Flying$Cycling {2}{U}$When you cycle Nimble Obstructionist, counter target activated or triggered ability you don't control.| +Portal Mage|Commander 2020|122|R|{2}{U}|Creature - Human Wizard|2|2|Flash$When Portal Mage enters the battlefield during the declare attackers step, you may reselect which player or planeswalker target attacking creature is attacking.| +Propaganda|Commander 2020|123|U|{2}{U}|Enchantment|||Creatures can't attack you unless their controller pays {2} for each creature they control that's attacking you.| +Swarm Intelligence|Commander 2020|124|R|{6}{U}|Enchantment|||Whenever you cast an instant or sorcery spell, you may copy that spell. You may choose new targets for the copy.| +Talrand, Sky Summoner|Commander 2020|125|R|{2}{U}{U}|Legendary Creature - Merfolk Wizard|2|2|Whenever you cast an instant or sorcery spell, create a 2/2 blue Drake creature token with flying.| +Vizier of Tumbling Sands|Commander 2020|126|U|{2}{U}|Creature - Human Cleric|1|3|{T}: Untap another target permanent.$Cycling {1}{U}$When you cycle Vizier of Tumbling Sands, untap target permanent.| +Whiplash Trap|Commander 2020|127|C|{3}{U}{U}|Instant - Trap|||If an opponent had two or more creatures enter the battlefield under their control this turn, you may pay {U} rather than pay this spell's mana cost.$Return two target creatures to their owners' hands.| +Windfall|Commander 2020|128|U|{2}{U}|Sorcery|||Each player discards their hand, then draws cards equal to the greatest number of cards a player discarded this way.| +Ambition's Cost|Commander 2020|129|U|{3}{B}|Sorcery|||You draw three cards and you lose 3 life.| +Cairn Wanderer|Commander 2020|130|R|{4}{B}|Creature - Shapeshifter|4|4|Changeling$As long as a creature card with flying is in a graveyard, Cairn Wanderer has flying. The same is true for fear, first strike, double strike, deathtouch, haste, landwalk, lifelink, protection, reach, trample, shroud, and vigilance.| +Deadly Tempest|Commander 2020|131|R|{4}{B}{B}|Sorcery|||Destroy all creatures. Each player loses life equal to the number of creatures they controlled that were destroyed this way.| +Disciple of Bolas|Commander 2020|132|R|{3}{B}|Creature - Human Wizard|2|1|When Disciple of Bolas enters the battlefield, sacrifice another creature. You gain X life and draw X cards, where X is that creature's power.| +Ever After|Commander 2020|133|R|{4}{B}{B}|Sorcery|||Return up to two target creature cards from your graveyard to the battlefield. Each of those creatures is a black Zombie in addition to its other colors and types. Put Ever After on the bottom of its owner's library.| +Painful Truths|Commander 2020|134|R|{2}{B}|Sorcery|||Converge — You draw X cards and you lose X life, where X is the number of colors of mana spent to cast Painful Truths.| +Profane Command|Commander 2020|135|R|{X}{B}{B}|Sorcery|||Choose two —$• Target player loses X life.$• Return target creature card with converted mana cost X or less from your graveyard to the battlefield.$• Target creature gets -X/-X until end of turn.$• Up to X target creatures gain fear until end of turn.| +Shriekmaw|Commander 2020|136|U|{4}{B}|Creature - Elemental|3|2|Fear$When Shriekmaw enters the battlefield, destroy target nonartifact, nonblack creature.$Evoke {1}{B}| +Soul of Innistrad|Commander 2020|137|M|{4}{B}{B}|Creature - Avatar|6|6|Deathtouch${3}{B}{B}: Return up to three target creature cards from your graveyard to your hand.${3}{B}{B}, Exile Soul of Innistrad from your graveyard: Return up to three target creature cards from your graveyard to your hand.| +Soulflayer|Commander 2020|138|R|{4}{B}{B}|Creature - Demon|4|4|Delve$If a creature card with flying was exiled with Soulflayer's delve ability, Soulflayer has flying. The same is true for first strike, double strike, deathtouch, haste, hexproof, indestructible, lifelink, reach, trample, and vigilance.| +Unburial Rites|Commander 2020|139|U|{4}{B}|Sorcery|||Return target creature card from your graveyard to the battlefield.$Flashback {3}{W}| +Vampire Nighthawk|Commander 2020|140|U|{1}{B}{B}|Creature - Vampire Shaman|2|3|Flying, deathtouch, lifelink| +Xathrid Necromancer|Commander 2020|141|R|{2}{B}|Creature - Human Wizard|2|2|Whenever Xathrid Necromancer or another Human creature you control dies, create a tapped 2/2 black Zombie creature token.| +Zulaport Cutthroat|Commander 2020|142|U|{1}{B}|Creature - Human Rogue Ally|1|1|Whenever Zulaport Cutthroat or another creature you control dies, each opponent loses 1 life and you gain 1 life.| +Alesha, Who Smiles at Death|Commander 2020|143|R|{2}{R}|Legendary Creature - Human Warrior|3|2|First strike$Whenever Alesha, Who Smiles at Death attacks, you may pay {W/B}{W/B}. If you do, return target creature card with power 2 or less from your graveyard to the battlefield tapped and attacking.| +Captivating Crew|Commander 2020|144|R|{3}{R}|Creature - Human Pirate|4|3|{3}{R}: Gain control of target creature an opponent controls until end of turn. Untap that creature. It gains haste until end of turn. Activate this ability only any time you could cast a sorcery.| +Chandra, Flamecaller|Commander 2020|145|M|{4}{R}{R}|Legendary Planeswalker - Chandra|4|+1: Create two 3/1 red Elemental creature tokens with haste. Exile them at the beginning of the next end step.$0: Discard all the cards in your hand, then draw that many cards plus one.$−X: Chandra, Flamecaller deals X damage to each creature.| +Chaos Warp|Commander 2020|146|R|{2}{R}|Instant|||The owner of target permanent shuffles it into their library, then reveals the top card of their library. If it's a permanent card, they put it onto the battlefield.| +Charmbreaker Devils|Commander 2020|147|R|{5}{R}|Creature - Devil|4|4|At the beginning of your upkeep, return an instant or sorcery card at random from your graveyard to your hand.$Whenever you cast an instant or sorcery spell, Charmbreaker Devils gets +4/+0 until end of turn.| +Comet Storm|Commander 2020|148|M|{X}{R}{R}|Instant|||Multikicker {1}$Choose any target, then choose another target for each time this spell was kicked. Comet Storm deals X damage to each of them.| +Commune with Lava|Commander 2020|149|R|{X}{R}{R}|Instant|||Exile the top X cards of your library. Until the end of your next turn, you may play those cards.| +Dualcaster Mage|Commander 2020|150|R|{1}{R}{R}|Creature - Human Wizard|2|2|Flash$When Dualcaster Mage enters the battlefield, copy target instant or sorcery spell. You may choose new targets for the copy.| +Etali, Primal Storm|Commander 2020|151|R|{4}{R}{R}|Legendary Creature - Elder Dinosaur|6|6|Whenever Etali, Primal Storm attacks, exile the top card of each player's library, then you may cast any number of spells from among those cards without paying their mana costs.| +Fumiko the Lowblood|Commander 2020|152|R|{2}{R}{R}|Legendary Creature - Human Samurai|3|2|Fumiko the Lowblood has bushido X, where X is the number of attacking creatures.$Creatures your opponents control attack each combat if able.| +Goblin Dark-Dwellers|Commander 2020|153|R|{3}{R}{R}|Creature - Goblin|4|4|Menace$When Goblin Dark-Dwellers enters the battlefield, you may cast target instant or sorcery card with converted mana cost 3 or less from your graveyard without paying its mana cost. If that card would be put into your graveyard this turn, exile it instead.| +Humble Defector|Commander 2020|154|U|{1}{R}|Creature - Human Rogue|2|1|{T}: Draw two cards. Target opponent gains control of Humble Defector. Activate this ability only during your turn.| +Lightning Rift|Commander 2020|155|U|{1}{R}|Enchantment|||Whenever a player cycles a card, you may pay {1}. If you do, Lightning Rift deals 2 damage to any target.| +Magus of the Wheel|Commander 2020|156|R|{2}{R}|Creature - Human Wizard|3|3|{1}{R}, {T}, Sacrifice Magus of the Wheel: Each player discards their hand, then draws seven cards.| +Outpost Siege|Commander 2020|157|R|{3}{R}|Enchantment|||As Outpost Siege enters the battlefield, choose Khans or Dragons.$• Khans — At the beginning of your upkeep, exile the top card of your library. Until end of turn, you may play that card.$• Dragons — Whenever a creature you control leaves the battlefield, Outpost Siege deals 1 damage to any target.| +Shared Animosity|Commander 2020|158|R|{2}{R}|Enchantment|||Whenever a creature you control attacks, it gets +1/+0 until end of turn for each other attacking creature that shares a creature type with it.| +Slice and Dice|Commander 2020|159|U|{4}{R}{R}|Sorcery|||Slice and Dice deals 4 damage to each creature.$Cycling {2}{R}$When you cycle Slice and Dice, you may have it deal 1 damage to each creature.| +Starstorm|Commander 2020|160|R|{X}{R}{R}|Instant|||Starstorm deals X damage to each creature.$Cycling {3}| +Surreal Memoir|Commander 2020|161|U|{3}{R}|Sorcery|||Return an instant card at random from your graveyard to your hand.$Rebound| +Tectonic Reformation|Commander 2020|162|R|{1}{R}|Enchantment|||Each land card in your hand has cycling {R}.$Cycling {2}| +Titan of Eternal Fire|Commander 2020|163|R|{5}{R}|Creature - Giant|5|6|Each Human creature you control has "{R}, {T}: This creature deals 1 damage to any target."| +Vigilante Justice|Commander 2020|164|U|{3}{R}|Enchantment|||Whenever a Human enters the battlefield under your control, Vigilante Justice deals 1 damage to any target.| +Acidic Slime|Commander 2020|165|U|{3}{G}{G}|Creature - Ooze|2|2|Deathtouch$When Acidic Slime enters the battlefield, destroy target artifact, enchantment, or land.| +Animist's Awakening|Commander 2020|166|R|{X}{G}|Sorcery|||Reveal the top X cards of your library. Put all land cards from among them onto the battlefield tapped and the rest on the bottom of your library in a random order.$Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, untap those lands.| +Beast Whisperer|Commander 2020|167|R|{2}{G}{G}|Creature - Elf Druid|2|3|Whenever you cast a creature spell, draw a card.| +Beast Within|Commander 2020|168|U|{2}{G}|Instant|||Destroy target permanent. Its controller creates a 3/3 green Beast creature token.| +Crop Rotation|Commander 2020|169|C|{G}|Instant|||As an additional cost to cast this spell, sacrifice a land.$Search your library for a land card and put that card onto the battlefield. Then shuffle your library.| +Cultivate|Commander 2020|170|C|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.| +Evolution Charm|Commander 2020|171|C|{1}{G}|Instant|||Choose one —$• Search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.$• Return target creature card from your graveyard to your hand.$• Target creature gains flying until end of turn.| +Genesis Hydra|Commander 2020|172|R|{X}{G}{G}|Creature - Plant Hydra|0|0|When you cast this spell, reveal the top X cards of your library. You may put a nonland permanent card with converted mana cost X or less from among them onto the battlefield. Then shuffle the rest into your library.$Genesis Hydra enters the battlefield with X +1/+1 counters on it.| +Harmonize|Commander 2020|173|U|{2}{G}{G}|Sorcery|||Draw three cards.| +Harrow|Commander 2020|174|C|{2}{G}|Instant|||As an additional cost to cast this spell, sacrifice a land.$Search your library for up to two basic land cards, put them onto the battlefield, then shuffle your library.| +Heroes' Bane|Commander 2020|175|U|{3}{G}{G}|Creature - Hydra|0|0|Heroes' Bane enters the battlefield with four +1/+1 counters on it.${2}{G}{G}: Put X +1/+1 counters on Heroes' Bane, where X is its power.| +Hornet Queen|Commander 2020|176|R|{4}{G}{G}{G}|Creature - Insect|2|2|Flying$Deathtouch$When Hornet Queen enters the battlefield, create four 1/1 green Insect creature tokens with flying and deathtouch.| +Hungering Hydra|Commander 2020|177|R|{X}{G}|Creature - Hydra|0|0|Hungering Hydra enters the battlefield with X +1/+1 counters on it.$Hungering Hydra can't be blocked by more than one creature.$Whenever Hungering Hydra is dealt damage, put that many +1/+1 counters on it.| +Hunter's Insight|Commander 2020|178|U|{2}{G}|Instant|||Choose target creature you control. Whenever that creature deals combat damage to a player or planeswalker this turn, draw that many cards.| +Hunting Pack|Commander 2020|179|U|{5}{G}{G}|Instant|||Create a 4/4 green Beast creature token.$Storm| +Kodama's Reach|Commander 2020|180|C|{2}{G}|Sorcery - Arcane|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.| +Krosan Grip|Commander 2020|181|U|{2}{G}|Instant|||Split second$Destroy target artifact or enchantment.| +Majestic Myriarch|Commander 2020|182|M|{4}{G}|Creature - Chimera|*|*|Majestic Myriarch's power and toughness are each equal to twice the number of creatures you control.$At the beginning of each combat, Majestic Myriarch gains flying until end of turn if you control a creature with flying. The same is true for first strike, double strike, deathtouch, haste, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance.| +Masked Admirers|Commander 2020|183|R|{2}{G}{G}|Creature - Elf Shaman|3|2|When Masked Admirers enters the battlefield, draw a card.$Whenever you cast a creature spell, you may pay {G}{G}. If you do, return Masked Admirers from your graveyard to your hand.| +Natural Connection|Commander 2020|184|C|{2}{G}|Instant|||Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.| +Predator Ooze|Commander 2020|185|R|{G}{G}{G}|Creature - Ooze|1|1|Indestructible$Whenever Predator Ooze attacks, put a +1/+1 counter on it.$Whenever a creature dealt damage by Predator Ooze this turn dies, put a +1/+1 counter on Predator Ooze.| +Reclamation Sage|Commander 2020|186|U|{2}{G}|Creature - Elf Shaman|2|1|When Reclamation Sage enters the battlefield, you may destroy target artifact or enchantment.| +Sakura-Tribe Elder|Commander 2020|187|C|{1}{G}|Creature - Snake Shaman|1|1|Sacrifice Sakura-Tribe Elder: Search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.| +Satyr Wayfinder|Commander 2020|188|C|{1}{G}|Creature - Satyr|1|1|When Satyr Wayfinder enters the battlefield, reveal the top four cards of your library. You may put a land card from among them into your hand. Put the rest into your graveyard.| +Skullwinder|Commander 2020|189|U|{2}{G}|Creature - Snake|1|3|Deathtouch$When Skullwinder enters the battlefield, return target card from your graveyard to your hand, then choose an opponent. That player returns a card from their graveyard to their hand.| +Slice in Twain|Commander 2020|190|U|{2}{G}{G}|Instant|||Destroy target artifact or enchantment.$Draw a card.| +Splinterfright|Commander 2020|191|R|{2}{G}|Creature - Elemental|*|*|Trample$Splinterfright's power and toughness are each equal to the number of creature cards in your graveyard.$At the beginning of your upkeep, put the top two cards of your library into your graveyard.| +Strength of the Tajuru|Commander 2020|192|R|{X}{G}{G}|Instant|||Multikicker {1}$Choose target creature, then choose another target creature for each time this spell was kicked. Put X +1/+1 counters on each of them.| +Tribute to the Wild|Commander 2020|193|U|{1}{G}|Instant|||Each opponent sacrifices an artifact or enchantment.| +Vastwood Hydra|Commander 2020|194|R|{X}{G}{G}|Creature - Hydra|0|0|Vastwood Hydra enters the battlefield with X +1/+1 counters on it.$When Vastwood Hydra dies, you may distribute a number of +1/+1 counters equal to the number of +1/+1 counters on Vastwood Hydra among any number of creatures you control.| +Vorapede|Commander 2020|195|M|{2}{G}{G}{G}|Creature - Insect|5|4|Vigilance, trample$Undying| +Wilderness Reclamation|Commander 2020|196|U|{3}{G}|Enchantment|||At the beginning of your end step, untap all lands you control.| +Yavimaya Dryad|Commander 2020|197|U|{1}{G}{G}|Creature - Dryad|2|1|Forestwalk$When Yavimaya Dryad enters the battlefield, you may search your library for a Forest card and put it onto the battlefield tapped under target player's control. If you do, shuffle your library.| +Abzan Ascendancy|Commander 2020|198|R|{W}{B}{G}|Enchantment|||When Abzan Ascendancy enters the battlefield, put a +1/+1 counter on each creature you control.$Whenever a nontoken creature you control dies, create a 1/1 white Spirit creature token with flying.| +Abzan Charm|Commander 2020|199|U|{W}{B}{G}|Instant|||Choose one —$• Exile target creature with power 3 or greater.$• You draw two cards and you lose 2 life.$• Distribute two +1/+1 counters among one or two target creatures.| +Adriana, Captain of the Guard|Commander 2020|200|R|{3}{R}{W}|Legendary Creature - Human Knight|4|4|Melee$Other creatures you control have melee.| +Ajani Unyielding|Commander 2020|201|M|{4}{G}{W}|Legendary Planeswalker - Ajani|4|+2: Reveal the top three cards of your library. Put all nonland permanent cards revealed this way into your hand and the rest on the bottom of your library in any order.$−2: Exile target creature. Its controller gains life equal to its power.$−9: Put five +1/+1 counters on each creature you control and five loyalty counters on each other planeswalker you control.| +Archon of Valor's Reach|Commander 2020|202|R|{4}{G}{W}|Creature - Archon|5|6|Flying, vigilance, trample$As Archon of Valor's Reach enters the battlefield, choose artifact, enchantment, instant, sorcery, or planeswalker.$Players can't cast spells of the chosen type.| +Artifact Mutation|Commander 2020|203|R|{R}{G}|Instant|||Destroy target artifact. It can't be regenerated. Create X 1/1 green Saproling creature tokens, where X is that artifact's converted mana cost.| +Cold-Eyed Selkie|Commander 2020|204|R|{1}{G/U}{G/U}|Creature - Merfolk Rogue|1|1|Islandwalk$Whenever Cold-Eyed Selkie deals combat damage to a player, you may draw that many cards.| +Crackling Doom|Commander 2020|205|R|{R}{W}{B}|Instant|||Crackling Doom deals 2 damage to each opponent. Each opponent sacrifices a creature with the greatest power among creatures that player controls.| +Crackling Drake|Commander 2020|206|U|{U}{U}{R}{R}|Creature - Drake|*|4|Flying$Crackling Drake's power is equal to the total number of instant and sorcery cards you own in exile and in your graveyard.$When Crackling Drake enters the battlefield, draw a card.| +Deadbridge Chant|Commander 2020|207|M|{4}{B}{G}|Enchantment|||When Deadbridge Chant enters the battlefield, put the top ten cards of your library into your graveyard.$At the beginning of your upkeep, choose a card at random in your graveyard. If it's a creature card, put it onto the battlefield. Otherwise, put it into your hand.| +Deathsprout|Commander 2020|208|U|{1}{B}{B}{G}|Instant|||Destroy target creature. Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.| +Despark|Commander 2020|209|U|{W}{B}|Instant|||Exile target permanent with converted mana cost 4 or greater.| +Djinn Illuminatus|Commander 2020|210|R|{5}{U/R}{U/R}|Creature - Djinn|3|5|({U/R} can be paid with either {U} or {R}.)$Flying$Each instant and sorcery spell you cast has replicate. The replicate cost is equal to its mana cost.| +Duneblast|Commander 2020|211|R|{4}{W}{B}{G}|Sorcery|||Choose up to one creature. Destroy the rest.| +Finality|Commander 2020|212|R|{4}{B}{G}|Sorcery|||You may put two +1/+1 counters on a creature you control. Then all creatures get -4/-4 until end of turn.| +Find|Commander 2020|212|R|{B/G}{B/G}|Sorcery|||Return up to two target creature cards from your graveyard to your hand.| +Garna, the Bloodflame|Commander 2020|213|U|{3}{B}{R}|Legendary Creature - Human Warrior|3|3|Flash$When Garna, the Bloodflame enters the battlefield, return to your hand all creature cards in your graveyard that were put there from anywhere this turn.$Other creatures you control have haste.| +Gaze of Granite|Commander 2020|214|R|{X}{B}{B}{G}|Sorcery|||Destroy each nonland permanent with converted mana cost X or less.| +Grisly Salvage|Commander 2020|215|C|{B}{G}|Instant|||Reveal the top five cards of your library. You may put a creature or land card from among them into your hand. Put the rest into your graveyard.| +Growth Spiral|Commander 2020|216|C|{G}{U}|Instant|||Draw a card. You may put a land card from your hand onto the battlefield.| +Isperia, Supreme Judge|Commander 2020|217|M|{2}{W}{W}{U}{U}|Legendary Creature - Sphinx|6|4|Flying$Whenever a creature attacks you or a planeswalker you control, you may draw a card.| +Karametra, God of Harvests|Commander 2020|218|M|{3}{G}{W}|Legendary Enchantment Creature - God|6|7|Indestructible$As long as your devotion to green and white is less than seven, Karametra isn't a creature.$Whenever you cast a creature spell, you may search your library for a Forest or Plains card, put it onto the battlefield tapped, then shuffle your library.| +The Locust God|Commander 2020|219|M|{4}{U}{R}|Legendary Creature - God|4|4|Flying$Whenever you draw a card, create a 1/1 blue and red Insect creature token with flying and haste.${2}{U}{R}: Draw a card, then discard a card.$When The Locust God dies, return it to its owner's hand at the beginning of the next end step.| +Melek, Izzet Paragon|Commander 2020|220|R|{4}{U}{R}|Legendary Creature - Weird Wizard|2|4|Play with the top card of your library revealed.$You may cast instant and sorcery spells from the top of your library.$Whenever you cast an instant or sorcery spell from your library, copy it. You may choose new targets for the copy.| +Mercurial Chemister|Commander 2020|221|R|{3}{U}{R}|Creature - Human Wizard|2|3|{U}, {T}: Draw two cards.${R}, {T}, Discard a card: Mercurial Chemister deals damage to target creature equal to the discarded card's converted mana cost.| +Migratory Route|Commander 2020|222|U|{3}{W}{U}|Sorcery|||Create four 1/1 white Bird creature tokens with flying.$Basic landcycling {2}| +Nahiri, the Harbinger|Commander 2020|223|M|{2}{R}{W}|Legendary Planeswalker - Nahiri|4|+2: You may discard a card. If you do, draw a card.$−2: Exile target enchantment, tapped artifact, or tapped creature.$−8: Search your library for an artifact or creature card, put it onto the battlefield, then shuffle your library. It gains haste. Return it to your hand at the beginning of the next end step.| +Nissa, Steward of Elements|Commander 2020|224|M|{X}{G}{U}|Legendary Planeswalker - Nissa|X|+2: Scry 2.$0: Look at the top card of your library. If it's a land card or a creature card with converted mana cost less than or equal to the number of loyalty counters on Nissa, Steward of Elements, you may put that card onto the battlefield.$−6: Untap up to two target lands you control. They become 5/5 Elemental creatures with flying and haste until end of turn. They're still lands.| +Niv-Mizzet, the Firemind|Commander 2020|225|R|{2}{U}{U}{R}{R}|Legendary Creature - Dragon Wizard|4|4|Flying$Whenever you draw a card, Niv-Mizzet, the Firemind deals 1 damage to any target.${T}: Draw a card.| +Nyx Weaver|Commander 2020|226|U|{1}{B}{G}|Enchantment Creature - Spider|2|3|Reach$At the beginning of your upkeep, put the top two cards of your library into your graveyard.${1}{B}{G}, Exile Nyx Weaver: Return target card from your graveyard to your hand.| +Prophetic Bolt|Commander 2020|227|R|{3}{U}{R}|Instant|||Prophetic Bolt deals 4 damage to any target. Look at the top four cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order.| +Putrefy|Commander 2020|228|U|{1}{B}{G}|Instant|||Destroy target artifact or creature. It can't be regenerated.| +Rashmi, Eternities Crafter|Commander 2020|229|M|{2}{G}{U}|Legendary Creature - Elf Druid|2|3|Whenever you cast your first spell each turn, reveal the top card of your library. You may cast it without paying its mana cost if it's a spell with lesser converted mana cost. If you don't cast it, put it into your hand.| +Temur Charm|Commander 2020|230|U|{G}{U}{R}|Instant|||Choose one —$• Target creature you control gets +1/+1 until end of turn. It fights target creature you don't control.$• Counter target spell unless its controller pays {3}.$• Creatures with power 3 or less can't block this turn.| +Terminate|Commander 2020|231|U|{B}{R}|Instant|||Destroy target creature. It can't be regenerated.| +Trygon Predator|Commander 2020|232|U|{1}{G}{U}|Creature - Beast|2|3|Flying$Whenever Trygon Predator deals combat damage to a player, you may destroy target artifact or enchantment that player controls.| +Villainous Wealth|Commander 2020|233|R|{X}{B}{G}{U}|Sorcery|||Target opponent exiles the top X cards of their library. You may cast any number of spells with converted mana cost X or less from among them without paying their mana costs.| +Wort, the Raidmother|Commander 2020|234|R|{4}{R/G}{R/G}|Legendary Creature - Goblin Shaman|3|3|When Wort, the Raidmother enters the battlefield, create two 1/1 red and green Goblin Warrior creature tokens.$Each red or green instant or sorcery spell you cast has conspire.| +Wydwen, the Biting Gale|Commander 2020|235|R|{2}{U}{B}|Legendary Creature - Faerie Wizard|3|3|Flash$Flying${U}{B}, Pay 1 life: Return Wydwen, the Biting Gale to its owner's hand.| +Abandoned Sarcophagus|Commander 2020|236|R|{3}|Artifact|||You may cast nonland cards with cycling from your graveyard.$If a card with cycling would be put into your graveyard from anywhere and it wasn't cycled, exile it instead.| +Arcane Signet|Commander 2020|237|C|{2}|Artifact|||{T}: Add one mana of any color in your commander's color identity.| +Azorius Signet|Commander 2020|238|U|{2}|Artifact|||{1}, {T}: Add {W}{U}.| +Boros Signet|Commander 2020|239|U|{2}|Artifact|||{1}, {T}: Add {R}{W}.| +Commander's Sphere|Commander 2020|240|C|{3}|Artifact|||{T}: Add one mana of any color in your commander's color identity.$Sacrifice Commander's Sphere: Draw a card.| +Fluctuator|Commander 2020|241|R|{2}|Artifact|||Cycling abilities you activate cost up to {2} less to activate.| +Heirloom Blade|Commander 2020|242|U|{3}|Artifact - Equipment|||Equipped creature gets +3/+1.$Whenever equipped creature dies, you may reveal cards from the top of your library until you reveal a creature card that shares a creature type with it. Put that card into your hand and the rest on the bottom of your library in a random order.$Equip {1}| +Izzet Signet|Commander 2020|243|U|{2}|Artifact|||{1}, {T}: Add {U}{R}.| +Lifecrafter's Bestiary|Commander 2020|244|R|{3}|Artifact|||At the beginning of your upkeep, scry 1.$Whenever you cast a creature spell, you may pay {G}. If you do, draw a card.| +Lightning Greaves|Commander 2020|245|U|{2}|Artifact - Equipment|||Equipped creature has haste and shroud.$Equip {0}| +Mimic Vat|Commander 2020|246|R|{3}|Artifact|||Imprint — Whenever a nontoken creature dies, you may exile that card. If you do, return each other card exiled with Mimic Vat to its owner's graveyard.${3}, {T}: Create a token that's a copy of a card exiled with Mimic Vat. It gains haste. Exile it at the beginning of the next end step.| +Orzhov Signet|Commander 2020|247|U|{2}|Artifact|||{1}, {T}: Add {W}{B}.| +Psychosis Crawler|Commander 2020|248|R|{5}|Artifact Creature - Horror|*|*|Psychosis Crawler's power and toughness are each equal to the number of cards in your hand.$Whenever you draw a card, each opponent loses 1 life.| +Rakdos Signet|Commander 2020|249|U|{2}|Artifact|||{1}, {T}: Add {B}{R}.| +Silent Arbiter|Commander 2020|250|R|{4}|Artifact Creature - Construct|1|5|No more than one creature can attack each combat.$No more than one creature can block each combat.| +Skullclamp|Commander 2020|251|U|{1}|Artifact - Equipment|||Equipped creature gets +1/-1.$Whenever equipped creature dies, draw two cards.$Equip {1}| +Sol Ring|Commander 2020|252|U|{1}|Artifact|||{T}: Add {C}{C}.| +Solemn Simulacrum|Commander 2020|253|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.| +Swiftfoot Boots|Commander 2020|254|U|{2}|Artifact - Equipment|||Equipped creature has hexproof and haste.$Equip {1}| +Ash Barrens|Commander 2020|255|C||Land|||{T}: Add {C}.$Basic landcycling {1}| +Azorius Chancery|Commander 2020|256|U||Land|||Azorius Chancery enters the battlefield tapped.$When Azorius Chancery enters the battlefield, return a land you control to its owner's hand.${T}: Add {W}{U}.| +Battlefield Forge|Commander 2020|257|R||Land|||{T}: Add {C}.${T}: Add {R} or {W}. Battlefield Forge deals 1 damage to you.| +Blighted Woodland|Commander 2020|258|U||Land|||{T}: Add {C}.${3}{G}, {T}, Sacrifice Blighted Woodland: Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library.| +Bojuka Bog|Commander 2020|259|C||Land|||Bojuka Bog enters the battlefield tapped.$When Bojuka Bog enters the battlefield, exile all cards from target player's graveyard.${T}: Add {B}.| +Boros Garrison|Commander 2020|260|C||Land|||Boros Garrison enters the battlefield tapped.$When Boros Garrison enters the battlefield, return a land you control to its owner's hand.${T}: Add {R}{W}.| +Canopy Vista|Commander 2020|261|R||Land - Forest Plains|||({T}: Add {G} or {W}.)$Canopy Vista enters the battlefield tapped unless you control two or more basic lands.| +Caves of Koilos|Commander 2020|262|R||Land|||{T}: Add {C}.${T}: Add {W} or {B}. Caves of Koilos deals 1 damage to you.| +Cinder Glade|Commander 2020|263|R||Land - Mountain Forest|||({T}: Add {R} or {G}.)$Cinder Glade enters the battlefield tapped unless you control two or more basic lands.| +Command Tower|Commander 2020|264|C||Land|||{T}: Add one mana of any color in your commander's color identity.| +Darkwater Catacombs|Commander 2020|265|R||Land|||{1}, {T}: Add {U}{B}.| +Desert of the Fervent|Commander 2020|266|C||Land - Desert|||Desert of the Fervent enters the battlefield tapped.${T}: Add {R}.$Cycling {1}{R}| +Desert of the Mindful|Commander 2020|267|C||Land - Desert|||Desert of the Mindful enters the battlefield tapped.${T}: Add {U}.$Cycling {1}{U}| +Desert of the True|Commander 2020|268|C||Land - Desert|||Desert of the True enters the battlefield tapped.${T}: Add {W}.$Cycling {1}{W}| +Desolate Lighthouse|Commander 2020|269|R||Land|||{T}: Add {C}.${1}{U}{R}, {T}: Draw a card, then discard a card.| +Dimir Aqueduct|Commander 2020|270|U||Land|||Dimir Aqueduct enters the battlefield tapped.$When Dimir Aqueduct enters the battlefield, return a land you control to its owner's hand.${T}: Add {U}{B}.| +Drifting Meadow|Commander 2020|271|C||Land|||Drifting Meadow enters the battlefield tapped.${T}: Add {W}.$Cycling {2}| +Endless Sands|Commander 2020|272|R||Land - Desert|||{T}: Add {C}.${2}, {T}: Exile target creature you control.${4}, {T}, Sacrifice Endless Sands: Return each creature card exiled with Endless Sands to the battlefield under its owner's control.| +Exotic Orchard|Commander 2020|273|R||Land|||{T}: Add one mana of any color that a land an opponent controls could produce.| +Forgotten Cave|Commander 2020|274|U||Land|||Forgotten Cave enters the battlefield tapped.${T}: Add {R}.$Cycling {R}| +Frontier Bivouac|Commander 2020|275|U||Land|||Frontier Bivouac enters the battlefield tapped.${T}: Add {G}, {U}, or {R}.| +Gavony Township|Commander 2020|276|R||Land|||{T}: Add {C}.${2}{G}{W}, {T}: Put a +1/+1 counter on each creature you control.| +Golgari Rot Farm|Commander 2020|277|U||Land|||Golgari Rot Farm enters the battlefield tapped.$When Golgari Rot Farm enters the battlefield, return a land you control to its owner's hand.${T}: Add {B}{G}.| +Grim Backwoods|Commander 2020|278|R||Land|||{T}: Add {C}.${2}{B}{G}, {T}, Sacrifice a creature: Draw a card.| +Gruul Turf|Commander 2020|279|U||Land|||Gruul Turf enters the battlefield tapped.$When Gruul Turf enters the battlefield, return a land you control to its owner's hand.${T}: Add {R}{G}.| +Halimar Depths|Commander 2020|280|C||Land|||Halimar Depths enters the battlefield tapped.$When Halimar Depths enters the battlefield, look at the top three cards of your library, then put them back in any order.${T}: Add {U}.| +Hostile Desert|Commander 2020|281|R||Land - Desert|||{T}: Add {C}.${2}, Exile a land card from your graveyard: Hostile Desert becomes a 3/4 Elemental creature until end of turn. It's still a land.| +Irrigated Farmland|Commander 2020|282|R||Land - Plains Island|||({T}: Add {W} or {U}.)$Irrigated Farmland enters the battlefield tapped.$Cycling {2}| +Izzet Boilerworks|Commander 2020|283|U||Land|||Izzet Boilerworks enters the battlefield tapped.$When Izzet Boilerworks enters the battlefield, return a land you control to its owner's hand.${T}: Add {U}{R}.| +Kessig Wolf Run|Commander 2020|284|R||Land|||{T}: Add {C}.${X}{R}{G}, {T}: Target creature gets +X/+0 and gains trample until end of turn.| +Krosan Verge|Commander 2020|285|U||Land|||Krosan Verge enters the battlefield tapped.${T}: Add {C}.${2}, {T}, Sacrifice Krosan Verge: Search your library for a Forest card and a Plains card, put them onto the battlefield tapped, then shuffle your library.| +Llanowar Wastes|Commander 2020|286|R||Land|||{T}: Add {C}.${T}: Add {B} or {G}. Llanowar Wastes deals 1 damage to you.| +Lonely Sandbar|Commander 2020|287|U||Land|||Lonely Sandbar enters the battlefield tapped.${T}: Add {U}.$Cycling {U}| +Memorial to Folly|Commander 2020|288|U||Land|||Memorial to Folly enters the battlefield tapped.${T}: Add {B}.${2}{B}, {T}, Sacrifice Memorial to Folly: Return target creature card from your graveyard to your hand.| +Mortuary Mire|Commander 2020|289|C||Land|||Mortuary Mire enters the battlefield tapped.$When Mortuary Mire enters the battlefield, you may put target creature card from your graveyard on top of your library.${T}: Add {B}.| +Mossfire Valley|Commander 2020|290|R||Land|||{1}, {T}: Add {R}{G}.| +Mosswort Bridge|Commander 2020|291|R||Land|||Hideaway${T}: Add {G}.${G}, {T}: You may play the exiled card without paying its mana cost if creatures you control have total power 10 or greater.| +Myriad Landscape|Commander 2020|292|U||Land|||Myriad Landscape enters the battlefield tapped.${T}: Add {C}.${2}, {T}, Sacrifice Myriad Landscape: Search your library for up to two basic land cards that share a land type, put them onto the battlefield tapped, then shuffle your library.| +Mystic Monastery|Commander 2020|293|U||Land|||Mystic Monastery enters the battlefield tapped.${T}: Add {U}, {R}, or {W}.| +Nomad Outpost|Commander 2020|294|U||Land|||Nomad Outpost enters the battlefield tapped.${T}: Add {R}, {W}, or {B}.| +Opulent Palace|Commander 2020|295|U||Land|||Opulent Palace enters the battlefield tapped.${T}: Add {B}, {G}, or {U}.| +Oran-Rief, the Vastwood|Commander 2020|296|R||Land|||Oran-Rief, the Vastwood enters the battlefield tapped.${T}: Add {G}.${T}: Put a +1/+1 counter on each green creature that entered the battlefield this turn.| +Orzhov Basilica|Commander 2020|297|C||Land|||Orzhov Basilica enters the battlefield tapped.$When Orzhov Basilica enters the battlefield, return a land you control to its owner's hand.${T}: Add {W}{B}.| +Path of Ancestry|Commander 2020|298|C||Land|||Path of Ancestry enters the battlefield tapped.${T}: Add one mana of any color in your commander's color identity. When that mana is spent to cast a creature spell that shares a creature type with your commander, scry 1.| +Prairie Stream|Commander 2020|299|R||Land - Plains Island|||({T}: Add {W} or {U}.)$Prairie Stream enters the battlefield tapped unless you control two or more basic lands.| +Rakdos Carnarium|Commander 2020|300|C||Land|||Rakdos Carnarium enters the battlefield tapped.$When Rakdos Carnarium enters the battlefield, return a land you control to its owner's hand.${T}: Add {B}{R}.| +Reliquary Tower|Commander 2020|301|U||Land|||You have no maximum hand size.${T}: Add {C}.| +Remote Isle|Commander 2020|302|C||Land|||Remote Isle enters the battlefield tapped.${T}: Add {U}.$Cycling {2}| +Rogue's Passage|Commander 2020|303|U||Land|||{T}: Add {C}.${4}, {T}: Target creature can't be blocked this turn.| +Rupture Spire|Commander 2020|304|C||Land|||Rupture Spire enters the battlefield tapped.$When Rupture Spire enters the battlefield, sacrifice it unless you pay {1}.${T}: Add one mana of any color.| +Sandsteppe Citadel|Commander 2020|305|U||Land|||Sandsteppe Citadel enters the battlefield tapped.${T}: Add {W}, {B}, or {G}.| +Scavenger Grounds|Commander 2020|306|R||Land - Desert|||{T}: Add {C}.${2}, {T}, Sacrifice a Desert: Exile all cards from all graveyards.| +Secluded Steppe|Commander 2020|307|U||Land|||Secluded Steppe enters the battlefield tapped.${T}: Add {W}.$Cycling {W}| +Selesnya Sanctuary|Commander 2020|308|C||Land|||Selesnya Sanctuary enters the battlefield tapped.$When Selesnya Sanctuary enters the battlefield, return a land you control to its owner's hand.${T}: Add {G}{W}.| +Shadowblood Ridge|Commander 2020|309|R||Land|||{1}, {T}: Add {B}{R}.| +Shivan Reef|Commander 2020|310|R||Land|||{T}: Add {C}.${T}: Add {U} or {R}. Shivan Reef deals 1 damage to you.| +Simic Growth Chamber|Commander 2020|311|U||Land|||Simic Growth Chamber enters the battlefield tapped.$When Simic Growth Chamber enters the battlefield, return a land you control to its owner's hand.${T}: Add {G}{U}.| +Skycloud Expanse|Commander 2020|312|R||Land|||{1}, {T}: Add {W}{U}.| +Smoldering Crater|Commander 2020|313|C||Land|||Smoldering Crater enters the battlefield tapped.${T}: Add {R}.$Cycling {2}| +Smoldering Marsh|Commander 2020|314|R||Land - Swamp Mountain|||({T}: Add {B} or {R}.)$Smoldering Marsh enters the battlefield tapped unless you control two or more basic lands.| +Soaring Seacliff|Commander 2020|315|C||Land|||Soaring Seacliff enters the battlefield tapped.$When Soaring Seacliff enters the battlefield, target creature gains flying until end of turn.${T}: Add {U}.| +Spinerock Knoll|Commander 2020|316|R||Land|||Hideaway${T}: Add {R}.${R}, {T}: You may play the exiled card without paying its mana cost if an opponent was dealt 7 or more damage this turn.| +Sungrass Prairie|Commander 2020|317|R||Land|||{1}, {T}: Add {G}{W}.| +Sunken Hollow|Commander 2020|318|R||Land - Island Swamp|||({T}: Add {U} or {B}.)$Sunken Hollow enters the battlefield tapped unless you control two or more basic lands.| +Temple of the False God|Commander 2020|319|U||Land|||{T}: Add {C}{C}. Activate this ability only if you control five or more lands.| +Unclaimed Territory|Commander 2020|320|U||Land|||As Unclaimed Territory enters the battlefield, choose a creature type.${T}: Add {C}.${T}: Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type.| +Windbrisk Heights|Commander 2020|321|R||Land|||Hideaway${T}: Add {W}.${W}, {T}: You may play the exiled card without paying its mana cost if you attacked with three or more creatures this turn.| +Yavimaya Coast|Commander 2020|322|R||Land|||{T}: Add {C}.${T}: Add {G} or {U}. Yavimaya Coast deals 1 damage to you.| +Adaptive Shimmerer|Ikoria: Lair of Behemoths|1|C|{5}|Creature - Insect|0|0|Flash$Adaptive Shimmerer enters the battlefield with three +1/+1 counters on it.| +Farfinder|Ikoria: Lair of Behemoths|2|C|{3}|Creature - Fox|1|1|Vigilance$When Farfinder enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.| +Mysterious Egg|Ikoria: Lair of Behemoths|3|C|{1}|Creature - Egg|0|2|Whenever this creature mutates, put a +1/+1 counter on it.| +Blade Banish|Ikoria: Lair of Behemoths|4|C|{3}{W}|Instant|||Exile target creature with power 4 or greater.| +Checkpoint Officer|Ikoria: Lair of Behemoths|5|C|{1}{W}|Creature - Human Soldier|1|2|{1}{W}, {T}: Tap target creature.| +Coordinated Charge|Ikoria: Lair of Behemoths|6|C|{4}{W}|Instant|||Creatures you control get +2/+1 until end of turn.$Cycling {2}| +Cubwarden|Ikoria: Lair of Behemoths|7|R|{3}{W}|Creature - Cat|3|5|Mutate {2}{W}{W}$Lifelink$Whenever this creature mutates, create two 1/1 white Cat creature tokens with lifelink.| +Daysquad Marshal|Ikoria: Lair of Behemoths|8|C|{3}{W}|Creature - Human Soldier|3|3|When Daysquad Marshal enters the battlefield, create a 1/1 white Human Soldier creature token.| +Divine Arrow|Ikoria: Lair of Behemoths|9|C|{1}{W}|Instant|||Divine Arrow deals 4 damage to target attacking or blocking creature.| +Drannith Healer|Ikoria: Lair of Behemoths|10|C|{1}{W}|Creature - Human Cleric|2|2|Whenever you cycle another card, you gain 1 life.$Cycling {1}| +Drannith Magistrate|Ikoria: Lair of Behemoths|11|R|{1}{W}|Creature - Human Wizard|1|3|Your opponents can't cast spells from anywhere other than their hands.| +Fight as One|Ikoria: Lair of Behemoths|12|U|{W}|Instant|||Choose one or both—$• Target Human creature you control gets +1/+1 and gains indestructible until end of turn.$• Target non-Human creature you control gets +1/+1 and gains indestructible until end of turn.| +Flourishing Fox|Ikoria: Lair of Behemoths|13|U|{W}|Creature - Fox|1|1|Whenever you cycle another card, put a +1/+1 counter on Flourishing Fox.$Cycling {1}| +Garrison Cat|Ikoria: Lair of Behemoths|14|C|{W}|Creature - Cat|1|1|When Garrison Cat dies, create a 1/1 white Human Soldier creature token.| +Helica Glider|Ikoria: Lair of Behemoths|15|C|{2}{W}|Creature - Nightmare Squirrel|2|2|Helica Glider enters the battlefield with your choice of a flying counter or a first strike counter on it.| +Huntmaster Liger|Ikoria: Lair of Behemoths|16|U|{3}{W}|Creature - Cat|3|4|Mutate {2}{W}$Whenever this creature mutates, other creatures you control get +X/+X until end of turn, where X is the number of times this creature has mutated.| +Imposing Vantasaur|Ikoria: Lair of Behemoths|17|C|{5}{W}|Creature - Dinosaur|3|6|Vigilance$Cycling {1}| +Keensight Mentor|Ikoria: Lair of Behemoths|18|U|{2}{W}|Creature - Human Cleric|1|4|When Keensight Mentor enters the battlefield, put a vigilance counter on target non-Human creature you control.${1}{W}, {T}: Put a +1/+1 counter on each creature you control with vigilance.| +Lavabrink Venturer|Ikoria: Lair of Behemoths|19|R|{2}{W}|Creature - Human Soldier|3|3|As Lavabrink Venturer enters the battlefield, choose odd or even.$Lavabrink Venturer has protection from each converted mana cost of the chosen value.| +Light of Hope|Ikoria: Lair of Behemoths|20|C|{W}|Instant|||Choose one —$• You gain 4 life.$• Destroy target enchantment.$• Put a +1/+1 counter on target creature.| +Luminous Broodmoth|Ikoria: Lair of Behemoths|21|M|{2}{W}{W}|Creature - Insect|3|4|Flying$Whenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it.| +Majestic Auricorn|Ikoria: Lair of Behemoths|22|U|{4}{W}|Creature - Unicorn|4|4|Mutate {3}{W}$Vigilance$Whenever this creature mutates, you gain 4 life.| +Maned Serval|Ikoria: Lair of Behemoths|23|C|{1}{W}|Creature - Cat|1|4|Vigilance| +Mythos of Snapdax|Ikoria: Lair of Behemoths|24|R|{2}{W}{W}|Sorcery|||Each player chooses an artifact, a creature, an enchantment, and a planeswalker from among the nonland permanents they control, then sacrifices the rest. If {B}{R} was spent to cast this spell, you choose the permanents for each player instead.| +Pacifism|Ikoria: Lair of Behemoths|25|C|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature can't attack or block.| +Patagia Tiger|Ikoria: Lair of Behemoths|26|C|{4}{W}|Creature - Cat|3|4|Flying$When Patagia Tiger enters the battlefield, target Human you control gets +2/+2 until end of turn.| +Perimeter Sergeant|Ikoria: Lair of Behemoths|27|C|{2}{W}|Creature - Human Soldier|3|2|Whenever Perimeter Sergeant attacks, other Humans you control get +1/+0 until end of turn.| +Sanctuary Lockdown|Ikoria: Lair of Behemoths|28|U|{2}{W}|Enchantment|||Humans you control get +1/+1.${2}, Tap two untapped Humans you control: Tap target creature an opponent controls.| +Savai Sabertooth|Ikoria: Lair of Behemoths|29|C|{1}{W}|Creature - Cat|3|1|| +Snare Tactician|Ikoria: Lair of Behemoths|30|C|{2}{W}|Creature - Human Soldier|2|3|Whenever you cycle a card, tap target creature an opponent controls.| +Solid Footing|Ikoria: Lair of Behemoths|31|C|{W}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature gets +1/+1.$As long as enchanted creature has vigilance, it assigns combat damage equal to its toughness rather than its power.| +Splendor Mare|Ikoria: Lair of Behemoths|32|U|{2}{W}|Creature - Elk Unicorn|3|3|Lifelink$Cycling {1}{W}$When you cycle Splendor Mare, put a lifelink counter on target creature you control.| +Spontaneous Flight|Ikoria: Lair of Behemoths|33|C|{2}{W}|Instant|||Target creature gets +2/+2 until end of turn. Put a flying counter on it.| +Stormwild Capridor|Ikoria: Lair of Behemoths|34|U|{2}{W}|Creature - Bird Goat|1|3|Flying$If noncombat damage would be dealt to Stormwild Capridor, prevent that damage. Put a +1/+1 counter on Stormwild Capridor for each 1 damage prevented this way.| +Swallow Whole|Ikoria: Lair of Behemoths|35|U|{W}|Sorcery|||As an additional cost to cast this spell, tap an untapped creature you control.$Exile target tapped creature. Put a +1/+1 counter on the creature tapped to pay this spell's additional cost.| +Valiant Rescuer|Ikoria: Lair of Behemoths|36|U|{1}{W}|Creature - Human Soldier|3|1|Whenever you cycle another card for the first time each turn, create a 1/1 white Human Soldier creature token.$Cycling {2}| +Vulpikeet|Ikoria: Lair of Behemoths|37|C|{3}{W}|Creature - Fox Bird|2|3|Mutate {2}{W}$Flying$Whenever this creature mutates, put a +1/+1 counter on it.| +Will of the All-Hunter|Ikoria: Lair of Behemoths|38|U|{1}{W}|Instant|||Target creature gets +2/+2 until end of turn. If it's blocking, instead put two +1/+1 counters on it.$Cycling {2}| +Aegis Turtle|Ikoria: Lair of Behemoths|39|C|{U}|Creature - Turtle|0|5|| +Anticipate|Ikoria: Lair of Behemoths|40|C|{1}{U}|Instant|||Look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order.| +Archipelagore|Ikoria: Lair of Behemoths|41|U|{5}{U}{U}|Creature - Leviathan|7|7|Mutate {5}{U}$Whenever this creature mutates, tap up to X target creatures, where X is the number of times this creature has mutated. Those creatures don't untap during their controller's next untap step.| +Avian Oddity|Ikoria: Lair of Behemoths|42|U|{3}{U}|Creature - Bird|2|4|Flying$Cycling {2}{U}$When you cycle Avian Oddity, put a flying counter on target creature you control.| +Boon of the Wish-Giver|Ikoria: Lair of Behemoths|43|U|{4}{U}{U}|Sorcery|||Draw four cards.$Cycling {1}| +Capture Sphere|Ikoria: Lair of Behemoths|44|C|{3}{U}|Enchantment - Aura|||Flash$Enchant creature$When Capture Sphere enters the battlefield, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.| +Convolute|Ikoria: Lair of Behemoths|45|C|{2}{U}|Instant|||Counter target spell unless its controller pays {4}.| +Crystacean|Ikoria: Lair of Behemoths|46|C|{3}{U}|Creature - Crab|1|6|Flash| +Dreamtail Heron|Ikoria: Lair of Behemoths|47|C|{4}{U}|Creature - Elemental Bird|3|4|Mutate {3}{U}$Flying$Whenever this creature mutates, draw a card.| +Escape Protocol|Ikoria: Lair of Behemoths|48|U|{1}{U}|Enchantment|||Whenever you cycle a card, you may pay {1}. When you do, exile target artifact or creature you control, then return it to the battlefield under its owner's control.| +Essence Scatter|Ikoria: Lair of Behemoths|49|C|{1}{U}|Instant|||Counter target creature spell.| +Facet Reader|Ikoria: Lair of Behemoths|50|C|{1}{U}|Creature - Human Wizard|1|2|{1}, {T}: Draw a card, then discard a card.| +Frost Lynx|Ikoria: Lair of Behemoths|51|C|{2}{U}|Creature - Elemental Cat|2|2|When Frost Lynx enters the battlefield, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.| +Frostveil Ambush|Ikoria: Lair of Behemoths|52|C|{3}{U}{U}|Instant|||Tap up to two target creatures. Those creatures don't untap during their controller's next untap step.$Cycling {1}| +Glimmerbell|Ikoria: Lair of Behemoths|53|C|{1}{U}|Creature - Elemental Jellyfish|1|3|Flying${1}{U}: Untap Glimmerbell.| +Gust of Wind|Ikoria: Lair of Behemoths|54|C|{3}{U}|Sorcery|||This spell costs {2} less to cast if you control a creature with flying.$Return target nonland permanent you don't control to its owner's hand.$Draw a card.| +Hampering Snare|Ikoria: Lair of Behemoths|55|C|{1}{U}|Instant|||Creatures your opponents control get -2/-0 until end of turn.$Cycling {2}| +Keep Safe|Ikoria: Lair of Behemoths|56|C|{1}{U}|Instant|||Counter target spell that targets a permanent you control.$Draw a card.| +Mystic Subdual|Ikoria: Lair of Behemoths|57|U|{1}{U}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature gets -2/-0 and loses all abilities.| +Mythos of Illuna|Ikoria: Lair of Behemoths|58|R|{2}{U}{U}|Sorcery|||Create a token that's a copy of target permanent. If {R}{G} was spent to cast this spell, instead create a token that's a copy of that permanent, except the token has "When this permanent enters the battlefield, if it's a creature, it fights up to one target creature you don't control."| +Neutralize|Ikoria: Lair of Behemoths|59|U|{1}{U}{U}|Instant|||Counter target spell.$Cycling {2}| +Of One Mind|Ikoria: Lair of Behemoths|60|C|{2}{U}|Sorcery|||This spell costs {2} less to cast if you control a Human creature and a non-Human creature.$Draw two cards.| +Ominous Seas|Ikoria: Lair of Behemoths|61|U|{1}{U}|Enchantment|||Whenever you draw a card, put a foreshadow counter on Ominous Seas.$Remove eight foreshadow counters from Ominous Seas: Create an 8/8 blue Kraken creature token.$Cycling {2}| +Phase Dolphin|Ikoria: Lair of Behemoths|62|C|{2}{U}|Creature - Elemental Whale|1|4|Whenever Phase Dolphin attacks, another target attacking creature can't be blocked this turn.| +Pollywog Symbiote|Ikoria: Lair of Behemoths|63|U|{1}{U}|Creature - Frog|1|3|Each creature spell you cast costs {1} less to cast if it has mutate.$Whenever you cast a creature spell, if it has mutate, draw a card, then discard a card.| +Pouncing Shoreshark|Ikoria: Lair of Behemoths|64|U|{4}{U}|Creature - Shark Beast|4|3|Mutate {3}{U}$Flash$Whenever this creature mutates, you may return target creature an opponent controls to its owner's hand.| +Reconnaissance Mission|Ikoria: Lair of Behemoths|65|U|{2}{U}{U}|Enchantment|||Whenever a creature you control deals combat damage to a player, you may draw a card.$Cycling {2}| +Sea-Dasher Octopus|Ikoria: Lair of Behemoths|66|R|{1}{U}{U}|Creature - Octopus|2|2|Mutate {1}{U}$Flash$Whenever this creature deals combat damage to a player, draw a card.| +Shark Typhoon|Ikoria: Lair of Behemoths|67|R|{5}{U}|Enchantment|||Whenever you cast a noncreature spell, create an X/X blue Shark creature token with flying, where X is that spell's converted mana cost.$Cycling {X}{1}{U}$When you cycle Shark Typhoon, create an X/X blue Shark creature token with flying.| +Startling Development|Ikoria: Lair of Behemoths|68|C|{1}{U}|Instant|||Until end of turn, target creature becomes a blue Serpent with base power and toughness 4/4.$Cycling {1}| +Thieving Otter|Ikoria: Lair of Behemoths|69|C|{2}{U}|Creature - Otter|2|2|Whenever Thieving Otter deals damage to an opponent, draw a card.| +Voracious Greatshark|Ikoria: Lair of Behemoths|70|R|{3}{U}{U}|Creature - Shark|5|4|Flash$When Voracious Greatshark enters the battlefield, counter target artifact or creature spell.| +Wingfold Pteron|Ikoria: Lair of Behemoths|71|C|{5}{U}|Creature - Dinosaur|3|6|Wingfold Pteron enters the battlefield with your choice of a flying counter or a hexproof counter on it.| +Wingspan Mentor|Ikoria: Lair of Behemoths|72|U|{2}{U}|Creature - Human Wizard|1|3|When Wingspan Mentor enters the battlefield, put a flying counter on target non-Human creature you control.${2}{U}, {T}: Put a +1/+1 counter on each creature you control with flying.| +Bastion of Remembrance|Ikoria: Lair of Behemoths|73|U|{2}{B}|Enchantment|||When Bastion of Remembrance enters the battlefield, create a 1/1 white Human Soldier creature token.$Whenever a creature you control dies, each opponent loses 1 life and you gain 1 life.| +Blitz Leech|Ikoria: Lair of Behemoths|74|C|{5}{B}|Creature - Leech|5|2|Flash$When Blitz Leech enters the battlefield, target creature an opponent controls gets -2/-2 until end of turn. Remove all counters from that creature.| +Blood Curdle|Ikoria: Lair of Behemoths|75|C|{3}{B}|Instant|||Destroy target creature. Put a menace counter on a creature you control.| +Boot Nipper|Ikoria: Lair of Behemoths|76|C|{1}{B}|Creature - Beast|2|1|Boot Nipper enters the battlefield with your choice of a deathtouch counter or a lifelink counter on it.| +Bushmeat Poacher|Ikoria: Lair of Behemoths|77|C|{3}{B}|Creature - Human Soldier|2|4|{1}, {T}, Sacrifice another creature: You gain life equal to that creature's toughness. Draw a card.| +Call of the Death-Dweller|Ikoria: Lair of Behemoths|78|U|{2}{B}|Sorcery|||Return up to two target creature cards with total converted mana cost 3 or less from your graveyard to the battlefield. Put a deathtouch counter on either of them. Then put a menace counter on either of them.| +Cavern Whisperer|Ikoria: Lair of Behemoths|79|C|{4}{B}|Creature - Nightmare|4|4|Mutate {3}{B}$Menace$Whenever this creature mutates, each opponent discards a card.| +Chittering Harvester|Ikoria: Lair of Behemoths|80|U|{5}{B}|Creature - Nightmare|4|6|Mutate {4}{B}$Whenever this creature mutates, each opponent sacrifices a creature.| +Corpse Churn|Ikoria: Lair of Behemoths|81|C|{1}{B}|Instant|||Put the top three cards of your library into your graveyard, then you may return a creature card from your graveyard to your hand.| +Dark Bargain|Ikoria: Lair of Behemoths|82|C|{3}{B}|Instant|||Look at the top three cards of your library. Put two of them into your hand and the other into your graveyard. Dark Bargain deals 2 damage to you.| +Dead Weight|Ikoria: Lair of Behemoths|83|C|{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets -2/-2.| +Dirge Bat|Ikoria: Lair of Behemoths|84|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4}{B}{B}$Flash$Flying$Whenever this creature mutates, destroy target creature or planeswalker an opponent controls.| +Durable Coilbug|Ikoria: Lair of Behemoths|85|C|{1}{B}|Creature - Insect|2|2|{4}{B}: Return Durable Coilbug from your graveyard to your hand.| +Duskfang Mentor|Ikoria: Lair of Behemoths|86|U|{2}{B}|Creature - Human Cleric|1|3|When Duskfang Mentor enters the battlefield, put a lifelink counter on target non-Human creature you control.${1}{B}, {T}: Put a +1/+1 counter on each creature you control with lifelink.| +Easy Prey|Ikoria: Lair of Behemoths|87|U|{1}{B}|Instant|||Destroy target creature with converted mana cost 2 or less.$Cycling {2}| +Extinction Event|Ikoria: Lair of Behemoths|88|R|{3}{B}|Sorcery|||Choose odd or even. Exile each creature with converted mana cost of the chosen value.| +Gloom Pangolin|Ikoria: Lair of Behemoths|89|C|{2}{B}|Creature - Nightmare Pangolin|1|5|| +Grimdancer|Ikoria: Lair of Behemoths|90|U|{1}{B}{B}|Creature - Nightmare|3|3|Grimdancer enters the battlefield with your choice of two different counters on it from among menace, deathtouch, and lifelink.| +Heartless Act|Ikoria: Lair of Behemoths|91|U|{1}{B}|Instant|||Choose one —$• Destroy target creature with no counters on it.$• Remove up to three counters from target creature.| +Hunted Nightmare|Ikoria: Lair of Behemoths|92|R|{1}{B}{B}|Creature - Nightmare|4|5|Menace$When Hunted Nightmare enters the battlefield, target opponent puts a deathtouch counter on a creature they control.| +Insatiable Hemophage|Ikoria: Lair of Behemoths|93|U|{3}{B}|Creature - Nightmare|3|3|Mutate {2}{B}$Deathtouch$Whenever this creature mutates, each opponent loses X life and you gain X life, where X is the number of times this creature has mutated.| +Lurking Deadeye|Ikoria: Lair of Behemoths|94|C|{3}{B}|Creature - Human Assassin|4|2|Flash$When Lurking Deadeye enters the battlefield, destroy target creature that was dealt damage this turn.| +Memory Leak|Ikoria: Lair of Behemoths|95|C|{2}{B}|Sorcery|||Target opponent reveals their hand. You choose a nonland card from that player's graveyard or hand and exile it.$Cycling {1}| +Mutual Destruction|Ikoria: Lair of Behemoths|96|C|{B}|Sorcery|||This spell has flash as long as you control a permanent with flash.$As an additional cost to cast this spell, sacrifice a creature.$Destroy target creature.| +Mythos of Nethroi|Ikoria: Lair of Behemoths|97|R|{2}{B}|Instant|||Destroy target nonland permanent if it's a creature or if {G}{W} was spent to cast this spell.| +Nightsquad Commando|Ikoria: Lair of Behemoths|98|C|{2}{B}|Creature - Human Soldier|2|3|When Nightsquad Commando enters the battlefield, if you attacked this turn, create a 1/1 white Human Soldier creature token.| +Serrated Scorpion|Ikoria: Lair of Behemoths|99|C|{B}|Creature - Scorpion|1|2|When Serrated Scorpion dies, it deals 2 damage to each opponent and you gain 2 life.| +Suffocating Fumes|Ikoria: Lair of Behemoths|100|C|{2}{B}|Instant|||Creatures your opponents control get -1/-1 until end of turn.$Cycling {2}| +Unbreakable Bond|Ikoria: Lair of Behemoths|101|U|{4}{B}|Sorcery|||Return target creature card from your graveyard to the battlefield with a lifelink counter on it.| +Unexpected Fangs|Ikoria: Lair of Behemoths|102|C|{1}{B}|Instant|||Put a +1/+1 counter and a lifelink counter on target creature.| +Unlikely Aid|Ikoria: Lair of Behemoths|103|C|{1}{B}|Instant|||Target creature gets +2/+0 and gains indestructible until end of turn.| +Void Beckoner|Ikoria: Lair of Behemoths|104|U|{6}{B}{B}|Creature - Nightmare Horror|8|8|Deathtouch$Cycling {2}{B}$When you cycle Void Beckoner, put a deathtouch counter on target creature you control.| +Whisper Squad|Ikoria: Lair of Behemoths|105|C|{B}|Creature - Human Soldier|1|1|{1}{B}: Search your library for a card named Whisper Squad, put it onto the battlefield tapped, then shuffle your library.| +Zagoth Mamba|Ikoria: Lair of Behemoths|106|U|{B}|Creature - Nightmare Snake|1|1|Whenever this creature mutates, target creature an opponent controls gets -2/-2 until end of turn.| +Blazing Volley|Ikoria: Lair of Behemoths|107|C|{R}|Sorcery|||Blazing Volley deals 1 damage to each creature your opponents control.| +Blisterspit Gremlin|Ikoria: Lair of Behemoths|108|C|{R}|Creature - Gremlin|1|1|{1}, {T}: Blisterspit Gremlin deals 1 damage to each opponent.$Whenever you cast a noncreature spell, untap Blisterspit Gremlin.| +Blitz of the Thunder-Raptor|Ikoria: Lair of Behemoths|109|U|{1}{R}|Instant|||Blitz of the Thunder-Raptor deals damage to target creature or planeswalker equal to the number of instant and sorcery cards in your graveyard. If that creature or planeswalker would die this turn, exile it instead.| +Cathartic Reunion|Ikoria: Lair of Behemoths|110|C|{1}{R}|Sorcery|||As an additional cost to cast this spell, discard two cards.$Draw three cards.| +Clash of Titans|Ikoria: Lair of Behemoths|111|U|{3}{R}{R}|Instant|||Target creature fights another target creature.| +Cloudpiercer|Ikoria: Lair of Behemoths|112|C|{4}{R}|Creature - Dinosaur|5|4|Mutate {3}{R}$Reach$Whenever this creature mutates, you may discard a card. If you do, draw a card.| +Drannith Stinger|Ikoria: Lair of Behemoths|113|C|{1}{R}|Creature - Human Wizard|2|2|Whenever you cycle another card, Drannith Stinger deals 1 damage to each opponent.$Cycling {1}| +Everquill Phoenix|Ikoria: Lair of Behemoths|114|R|{2}{R}{R}|Creature - Phoenix|4|4|Mutate {3}{R}$Flying$Whenever this creature mutates, create a red artifact token named Feather with "{1}, Sacrifice Feather: Return target Phoenix card from your graveyard to the battlefield tapped."| +Ferocious Tigorilla|Ikoria: Lair of Behemoths|115|C|{3}{R}|Creature - Cat Ape|4|3|Ferocious Tigorilla enters the battlefield with your choice of a trample counter or a menace counter on it.| +Fire Prophecy|Ikoria: Lair of Behemoths|116|C|{1}{R}|Instant|||Fire Prophecy deals 3 damage to target creature. You may put a card from your hand on the bottom of your library. If you do, draw a card.| +Flame Spill|Ikoria: Lair of Behemoths|117|U|{2}{R}|Instant|||Flame Spill deals 4 damage to target creature. Excess damage is dealt to that creature's controller instead.| +Footfall Crater|Ikoria: Lair of Behemoths|118|U|{R}|Enchantment - Aura|||Enchant land$Enchanted land has "{T}: Target creature gains trample and haste until end of turn."$Cycling {1}| +Forbidden Friendship|Ikoria: Lair of Behemoths|119|C|{1}{R}|Sorcery|||Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token.| +Frenzied Raptor|Ikoria: Lair of Behemoths|120|C|{2}{R}|Creature - Dinosaur|4|2|| +Frillscare Mentor|Ikoria: Lair of Behemoths|121|U|{2}{R}|Creature - Human Warrior|3|2|When Frillscare Mentor enters the battlefield, put a menace counter on target non-Human creature you control.${2}{R}, {T}: Put a +1/+1 counter on each creature you control with menace.| +Go for Blood|Ikoria: Lair of Behemoths|122|C|{1}{R}|Sorcery|||Target creature you control fights target creature you don't control.$Cycling {1}| +Heightened Reflexes|Ikoria: Lair of Behemoths|123|C|{R}|Instant|||Target creature gets +1/+0 until end of turn. Put a first strike counter on it.| +Lava Serpent|Ikoria: Lair of Behemoths|124|C|{5}{R}|Creature - Elemental Serpent|5|5|Haste$Cycling {2}| +Lukka, Coppercoat Outcast|Ikoria: Lair of Behemoths|125|M|{3}{R}{R}|Legendary Planeswalker - Lukka|5|+1: Exile the top three cards of your library. Creature cards exiled this way gain "You may cast this card from exile as long as you control a Lukka planeswalker."$−2: Exile target creature you control, then reveal cards from the top of your library until you reveal a creature card with higher converted mana cost. Put that card onto the battlefield and the rest on the bottom of your library in a random order.$−7: Each creature you control deals damage equal to its power to each opponent.| +Momentum Rumbler|Ikoria: Lair of Behemoths|126|U|{3}{R}|Creature - Dinosaur|3|3|Whenever Momentum Rumbler attacks, if it doesn't have first strike, put a first strike counter on it.$Whenever Momentum Rumbler attacks, if it has first strike, it gains double strike until end of turn.| +Mythos of Vadrok|Ikoria: Lair of Behemoths|127|R|{2}{R}{R}|Sorcery|||Mythos of Vadrok deals 5 damage divided as you choose among any number of target creatures and/or planeswalkers. If {W}{U} was spent to cast this spell, until your next turn, those permanents can't attack or block and their activated abilities can't be activated.| +Porcuparrot|Ikoria: Lair of Behemoths|128|U|{3}{R}|Creature - Bird Beast|3|4|Mutate {2}{R}${T}: This creature deals X damage to any target, where X is the number of times this creature has mutated.| +Prickly Marmoset|Ikoria: Lair of Behemoths|129|C|{2}{R}|Creature - Monkey|2|3|First strike$Whenever you cycle a card, Prickly Marmoset gets +2/+0 until end of turn.| +Pyroceratops|Ikoria: Lair of Behemoths|130|C|{3}{R}|Creature - Elemental Dinosaur|2|3|Trample$Whenever you cast a noncreature spell, put a +1/+1 counter on Pyroceratops.| +Raking Claws|Ikoria: Lair of Behemoths|131|C|{1}{R}|Instant|||Target creature gains double strike until end of turn.$Cycling {2}| +Reptilian Reflection|Ikoria: Lair of Behemoths|132|U|{2}{R}|Enchantment|||Whenever you cycle a card, you may have Reptilian Reflection become a 5/4 Dinosaur creature with trample and haste in addition to its other types until end of turn.| +Rooting Moloch|Ikoria: Lair of Behemoths|133|U|{4}{R}|Creature - Lizard|4|4|When Rooting Moloch enters the battlefield, exile target card with a cycling ability from your graveyard. Until the end of your next turn, you may play that card.$Cycling {2}| +Rumbling Rockslide|Ikoria: Lair of Behemoths|134|C|{3}{R}|Sorcery|||Rumbling Rockslide deals damage to target creature equal to the number of lands you control.| +Sanctuary Smasher|Ikoria: Lair of Behemoths|135|U|{4}{R}{R}|Creature - Rhino Beast|6|4|First strike$Cycling {2}{R}$When you cycle Sanctuary Smasher, put a first strike counter on target creature you control.| +Shredded Sails|Ikoria: Lair of Behemoths|136|C|{1}{R}|Instant|||Choose one —$• Destroy target artifact.$• Shredded Sails deals 4 damage to target creature with flying.$Cycling {2}| +Spelleater Wolverine|Ikoria: Lair of Behemoths|137|C|{2}{R}|Creature - Wolverine|3|2|Spelleater Wolverine has double strike as long as there are three or more instant and/or sorcery cards in your graveyard.| +Tentative Connection|Ikoria: Lair of Behemoths|138|C|{3}{R}|Sorcery|||This spell costs {3} less to cast if you control a creature with menace.$Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.| +Unpredictable Cyclone|Ikoria: Lair of Behemoths|139|R|{3}{R}{R}|Enchantment|||If a cycling ability of another nonland card would cause you to draw a card, instead exile cards from the top of your library until you exile a card that shares a card type with the cycled card. You may cast that card without paying its mana cost. Then put the exiled cards that weren't cast this way on the bottom of your library in a random order.$Cycling {2}| +Weaponize the Monsters|Ikoria: Lair of Behemoths|140|U|{R}|Enchantment|||{2}, Sacrifice a creature: Weaponize the Monsters deals 2 damage to any target.| +Yidaro, Wandering Monster|Ikoria: Lair of Behemoths|141|R|{5}{R}{R}|Legendary Creature - Dinosaur Turtle|8|8|Trample, haste$Cycling {1}{R}$When you cycle Yidaro, Wandering Monster, shuffle it into your library from your graveyard. If you've cycled a card named Yidaro, Wandering Monster four or more times this game, put it onto the battlefield from your graveyard instead.| +Adventurous Impulse|Ikoria: Lair of Behemoths|142|C|{G}|Sorcery|||Look at the top three cards of your library. You may reveal a creature or land card from among them and put it into your hand. Put the rest on the bottom of your library in any order.| +Almighty Brushwagg|Ikoria: Lair of Behemoths|143|C|{G}|Creature - Brushwagg|1|1|Trample${3}{G}: Almighty Brushwagg gets +3/+3 until end of turn.| +Auspicious Starrix|Ikoria: Lair of Behemoths|144|U|{4}{G}|Creature - Elk Beast|6|6|Mutate {5}{G}$Whenever this creature mutates, exile cards from the top of your library until you exile X permanent cards, where X is the number of times this creature has mutated. Put those permanent cards onto the battlefield.| +Barrier Breach|Ikoria: Lair of Behemoths|145|U|{2}{G}|Instant|||Exile up to three target enchantments.$Cycling {2}| +Bristling Boar|Ikoria: Lair of Behemoths|146|C|{3}{G}|Creature - Boar|4|3|Bristling Boar can't be blocked by more than one creature.| +Charge of the Forever-Beast|Ikoria: Lair of Behemoths|147|U|{2}{G}|Sorcery|||As an additional cost to cast this spell, reveal a creature card from your hand.$Charge of the Forever-Beast deals damage to target creature or planeswalker equal to the revealed card's power.| +Colossification|Ikoria: Lair of Behemoths|148|R|{5}{G}{G}|Enchantment - Aura|||Enchant creature$When Colossification enters the battlefield, tap enchanted creature.$Enchanted creature gets +20/+20.| +Essence Symbiote|Ikoria: Lair of Behemoths|149|C|{1}{G}|Creature - Beast|2|2|Whenever a creature you control mutates, put a +1/+1 counter on that creature and you gain 2 life.| +Excavation Mole|Ikoria: Lair of Behemoths|150|C|{2}{G}|Creature - Mole|3|3|Trample$When Excavation Mole enters the battlefield, put the top three cards of your library into your graveyard.| +Exuberant Wolfbear|Ikoria: Lair of Behemoths|151|U|{3}{G}|Creature - Wolf Bear|4|4|Whenever Exuberant Wolfbear attacks, you may change the base power and toughness of target Human you control to Exuberant Wolfbear's power and toughness until end of turn.| +Fertilid|Ikoria: Lair of Behemoths|152|C|{2}{G}|Creature - Elemental|0|0|Fertilid enters the battlefield with two +1/+1 counters on it.${1}{G}, Remove a +1/+1 counter from Fertilid: Target player searches their library for a basic land card, puts it onto the battlefield tapped, then shuffles their library.| +Flycatcher Giraffid|Ikoria: Lair of Behemoths|153|C|{4}{G}|Creature - Antelope Lizard|3|5|Flycatcher Giraffid enters the battlefield with your choice of a vigilance counter or a reach counter on it.| +Fully Grown|Ikoria: Lair of Behemoths|154|C|{2}{G}|Instant|||Target creature gets +3/+3 until end of turn. Put a trample counter on it.| +Gemrazer|Ikoria: Lair of Behemoths|155|R|{3}{G}|Creature - Beast|4|4|Mutate {1}{G}{G}$Reach, trample$Whenever this creature mutates, destroy target artifact or enchantment an opponent controls.| +Glowstone Recluse|Ikoria: Lair of Behemoths|156|U|{2}{G}|Creature - Spider|2|3|Mutate {3}{G}$Reach$Whenever this creature mutates, put two +1/+1 counters on it.| +Greater Sandwurm|Ikoria: Lair of Behemoths|157|C|{5}{G}{G}|Creature - Wurm|7|7|Greater Sandwurm can't be blocked by creatures with power 2 or less.$Cycling {2}| +Honey Mammoth|Ikoria: Lair of Behemoths|158|C|{4}{G}{G}|Creature - Elephant|6|6|When Honey Mammoth enters the battlefield, you gain 4 life.| +Hornbash Mentor|Ikoria: Lair of Behemoths|159|U|{2}{G}|Creature - Human Warrior|3|3|When Hornbash Mentor enters the battlefield, put a trample counter on target non-Human creature you control.${2}{G}, {T}: Put a +1/+1 counter on each creature you control with trample.| +Humble Naturalist|Ikoria: Lair of Behemoths|160|C|{1}{G}|Creature - Human Druid|1|3|{T}: Add one mana of any color. Spend this mana only to cast a creature spell.| +Ivy Elemental|Ikoria: Lair of Behemoths|161|U|{X}{G}|Creature - Elemental|0|0|Ivy Elemental enters the battlefield with X +1/+1 counters on it.| +Kogla, the Titan Ape|Ikoria: Lair of Behemoths|162|R|{3}{G}{G}{G}|Legendary Creature - Ape|7|6|When Kogla, the Titan Ape enters the battlefield, it fights up to one target creature you don't control.$Whenever Kogla attacks, destroy target artifact or enchantment defending player controls.${1}{G}: Return target Human you control to its owner's hand. Kogla gains indestructible until end of turn.| +Lead the Stampede|Ikoria: Lair of Behemoths|163|U|{2}{G}|Sorcery|||Look at the top five cards of your library. You may reveal any number of creature cards from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in any order.| +Migration Path|Ikoria: Lair of Behemoths|164|U|{3}{G}|Sorcery|||Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library.$Cycling {2}| +Migratory Greathorn|Ikoria: Lair of Behemoths|165|C|{3}{G}|Creature - Beast|3|4|Mutate {2}{G}$Whenever this creature mutates, search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.| +Monstrous Step|Ikoria: Lair of Behemoths|166|U|{4}{G}|Sorcery|||Target creature gets +7/+7 until end of turn. Up to one other target creature blocks it this turn if able.$Cycling {2}| +Mosscoat Goriak|Ikoria: Lair of Behemoths|167|C|{2}{G}|Creature - Beast|2|4|Vigilance| +Mythos of Brokkos|Ikoria: Lair of Behemoths|168|R|{2}{G}{G}|Sorcery|||If {U}{B} was spent to cast Mythos of Brokkos, search your library for a card, put that card into your graveyard, then shuffle your library.$Return up to two permanent cards from your graveyard to your hand.| +Plummet|Ikoria: Lair of Behemoths|169|C|{1}{G}|Instant|||Destroy target creature with flying.| +Ram Through|Ikoria: Lair of Behemoths|170|C|{1}{G}|Instant|||Target creature you control deals damage equal to its power to target creature you don't control. If the creature you control has trample, excess damage is dealt to that creature's controller instead.| +Sudden Spinnerets|Ikoria: Lair of Behemoths|171|C|{G}|Instant|||Target creature gets +1/+3 until end of turn. Put a reach counter on it. Untap it.| +Survivors' Bond|Ikoria: Lair of Behemoths|172|C|{1}{G}|Sorcery|||Choose one or both —$• Return target Human creature card from your graveyard to your hand.$• Return target non-Human creature card from your graveyard to your hand.| +Thwart the Enemy|Ikoria: Lair of Behemoths|173|C|{2}{G}|Instant|||Prevent all damage that would be dealt this turn by creatures your opponents control.| +Titanoth Rex|Ikoria: Lair of Behemoths|174|U|{7}{G}{G}|Creature - Dinosaur Beast|11|11|Trample$Cycling {1}{G}$When you cycle Titanoth Rex, put a trample counter on target creature you control.| +Vivien, Monsters' Advocate|Ikoria: Lair of Behemoths|175|M|{3}{G}{G}|Legendary Planeswalker - Vivien|3|You may look at the top card of your library any time.$You may cast creature spells from the top of your library.$+1: Create a 3/3 green Beast creature token. Put your choice of a vigilance counter, a reach counter, or a trample counter on it.$−2: When you cast your next creature spell this turn, search your library for a creature card with lesser converted mana cost, put it onto the battlefield, then shuffle your library.| +Wilt|Ikoria: Lair of Behemoths|176|C|{1}{G}|Instant|||Destroy target artifact or enchantment.$Cycling {2}| +Back for More|Ikoria: Lair of Behemoths|177|U|{4}{B}{G}|Instant|||Return target creature card from your graveyard to the battlefield. When you do, it fights up to one target creature you don't control.| +Boneyard Lurker|Ikoria: Lair of Behemoths|178|U|{2}{B}{G}|Creature - Nightmare Beast|4|4|Mutate {2}{B/G}{B/G}$Whenever this creature mutates, return target permanent card from your graveyard to your hand.| +Brokkos, Apex of Forever|Ikoria: Lair of Behemoths|179|M|{2}{B}{G}{U}|Legendary Creature - Nightmare Beast Elemental|6|6|Mutate {2}{U/B}{G}{G}$Trample$You may cast Brokkos, Apex of Forever from your graveyard using its mutate ability.| +Channeled Force|Ikoria: Lair of Behemoths|180|U|{2}{U}{R}|Instant|||As an additional cost to cast this spell, discard X cards.$Target player draws X cards. Channeled Force deals X damage to up to one target creature or planeswalker.| +Chevill, Bane of Monsters|Ikoria: Lair of Behemoths|181|M|{B}{G}|Legendary Creature - Human Rogue|1|3|Deathtouch$At the beginning of your upkeep, if your opponents control no permanents with bounty counters on them, put a bounty counter on target creature or planeswalker an opponent controls.$Whenever a permanent an opponent controls with a bounty counter on it dies, you gain 3 life and draw a card.| +Death's Oasis|Ikoria: Lair of Behemoths|182|R|{W}{B}{G}|Enchantment|||Whenever a nontoken creature you control dies, put the top two cards of your library into your graveyard. Then return a creature card with lesser converted mana cost than the creature that died from your graveyard to your hand.${1}, Sacrifice Death's Oasis: You gain life equal to the greatest converted mana cost among creatures you control.| +Dire Tactics|Ikoria: Lair of Behemoths|183|U|{W}{B}|Instant|||Exile target creature. If you don't control a Human, you lose life equal to that creature's toughness.| +Eerie Ultimatum|Ikoria: Lair of Behemoths|184|R|{W}{W}{B}{B}{B}{G}{G}|Sorcery|||Return any number of permanent cards with different names from your graveyard to the battlefield.| +Emergent Ultimatum|Ikoria: Lair of Behemoths|185|R|{B}{B}{G}{G}{G}{U}{U}|Sorcery|||Search your library for up to three monocolored cards with different names and exile them. An opponent chooses one of those cards. Shuffle that card into your library. You may cast the other cards without paying their mana costs. Exile Emergent Ultimatum.| +Frondland Felidar|Ikoria: Lair of Behemoths|186|R|{2}{G}{W}|Creature - Cat Beast|3|5|Vigilance$Creatures you control with vigilance have "{1}, {T}: Tap target creature."| +General Kudro of Drannith|Ikoria: Lair of Behemoths|187|M|{1}{W}{B}|Legendary Creature - Human Soldier|3|3|Other Humans you control get +1/+1.$Whenever General Kudro of Drannith or another Human enters the battlefield under your control, exile target card from an opponent's graveyard.${2}, Sacrifice two Humans: Destroy target creature with power 4 or greater.| +General's Enforcer|Ikoria: Lair of Behemoths|188|U|{W}{B}|Creature - Human Soldier|2|3|Legendary Humans you control have indestructible.${2}{W}{B}: Exile target card from a graveyard. If it was a creature card, create a 1/1 white Human Soldier creature token.| +Genesis Ultimatum|Ikoria: Lair of Behemoths|189|R|{G}{G}{U}{U}{U}{R}{R}|Sorcery|||Look at the top five cards of your library. Put any number of permanent cards from among them onto the battlefield and the rest into your hand. Exile Genesis Ultimatum.| +Illuna, Apex of Wishes|Ikoria: Lair of Behemoths|190|M|{2}{G}{U}{R}|Legendary Creature - Beast Elemental Dinosaur|6|6|Mutate {3}{R/G}{U}{U}$Flying, trample$Whenever this creature mutates, exile cards from the top of your library until you exile a nonland permanent card. Put that card onto the battlefield or into your hand.| +Inspired Ultimatum|Ikoria: Lair of Behemoths|191|R|{U}{U}{R}{R}{R}{W}{W}|Sorcery|||Target player gains 5 life, Inspired Ultimatum deals 5 damage to any target, then you draw five cards.| +Kinnan, Bonder Prodigy|Ikoria: Lair of Behemoths|192|M|{G}{U}|Legendary Creature - Human Druid|2|2|Whenever you tap a nonland permanent for mana, add one mana of any type that permanent produced.${5}{G}{U}: Look at the top five cards of your library. You may put a non-Human creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order.| +Labyrinth Raptor|Ikoria: Lair of Behemoths|193|R|{B}{R}|Creature - Nightmare Dinosaur|2|2|Menace$Whenever a creature you control with menace becomes blocked, defending player sacrifices a creature blocking it.${B}{R}: Creatures you control with menace get +1/+0 until end of turn.| +Lore Drakkis|Ikoria: Lair of Behemoths|194|U|{1}{U}{R}|Creature - Lizard Beast|2|3|Mutate {U/R}{U/R}$Whenever this creature mutates, return target instant or sorcery card from your graveyard to your hand.| +Narset of the Ancient Way|Ikoria: Lair of Behemoths|195|M|{1}{U}{R}{W}|Legendary Planeswalker - Narset|4|+1: You gain 2 life. Add {U}, {R}, or {W}. Spend this mana only to cast a noncreature spell.$−2: Draw a card, then you may discard a card. When you discard a nonland card this way, Narset of the Ancient Way deals damage equal to that card's converted mana cost to target creature or planeswalker.$−6: You get an emblem with "Whenever you cast a noncreature spell, this emblem deals 2 damage to any target."| +Necropanther|Ikoria: Lair of Behemoths|196|U|{1}{W}{B}|Creature - Cat Nightmare|3|3|Mutate {2}{W/B}{W/B}$Whenever this creature mutates, return target creature card with converted mana cost 3 or less from your graveyard to the battlefield.| +Nethroi, Apex of Death|Ikoria: Lair of Behemoths|197|M|{2}{W}{B}{G}|Legendary Creature - Cat Nightmare Beast|5|5|Mutate {4}{G/W}{B}{B}$Deathtouch, lifelink$Whenever this creature mutates, return any number of target creature cards with total power 10 or less from your graveyard to the battlefield.| +Offspring's Revenge|Ikoria: Lair of Behemoths|198|R|{2}{R}{W}{B}|Enchantment|||At the beginning of combat on your turn, exile target red, white, or black creature card from your graveyard. Create a token that's a copy of that card, except it's 1/1. It gains haste until your next turn.| +Parcelbeast|Ikoria: Lair of Behemoths|199|U|{2}{G}{U}|Creature - Elemental Beast|2|4|Mutate {G}{U}${1}, {T}: Look at the top card of your library. If it's a land card, you may put it onto the battlefield. If you don't put the card onto the battlefield, put it into your hand.| +Primal Empathy|Ikoria: Lair of Behemoths|200|U|{1}{G}{U}|Enchantment|||At the beginning of your upkeep, draw a card if you control a creature with the greatest power among creatures on the battlefield. Otherwise, put a +1/+1 counter on a creature you control.| +Quartzwood Crasher|Ikoria: Lair of Behemoths|201|R|{2}{R}{R}{G}|Creature - Dinosaur Beast|6|6|Trample$Whenever one or more creatures you control with trample deal combat damage to a player, create an X/X green Dinosaur Beast creature token with trample, where X is the amount of damage those creatures dealt to that player.| +Regal Leosaur|Ikoria: Lair of Behemoths|202|U|{R}{W}|Creature - Dinosaur Cat|2|2|Mutate {1}{R/W}{R/W}$Whenever this creature mutates, other creatures you control get +2/+1 until end of turn.| +Rielle, the Everwise|Ikoria: Lair of Behemoths|203|M|{1}{U}{R}|Legendary Creature - Human Wizard|0|3|Rielle, the Everwise gets +1/+0 for each instant and sorcery card in your graveyard.$Whenever you discard one or more cards for the first time each turn, draw that many cards.| +Ruinous Ultimatum|Ikoria: Lair of Behemoths|204|R|{R}{R}{W}{W}{W}{B}{B}|Sorcery|||Destroy all nonland permanents your opponents control.| +Savai Thundermane|Ikoria: Lair of Behemoths|205|U|{R}{W}|Creature - Elemental Cat|3|2|Whenever you cycle a card, you may pay {2}. When you do, Savai Thundermane deals 2 damage to target creature and you gain 2 life.| +Skull Prophet|Ikoria: Lair of Behemoths|206|U|{B}{G}|Creature - Human Druid|3|1|{T}: Add {B} or {G}.${T}: Put the top two cards of your library into your graveyard.| +Skycat Sovereign|Ikoria: Lair of Behemoths|207|R|{W}{U}|Creature - Elemental Cat|1|1|Flying$Skycat Sovereign gets +1/+1 for each other creature you control with flying.${2}{W}{U}: Create a 1/1 white Cat Bird creature token with flying.| +Slitherwisp|Ikoria: Lair of Behemoths|208|R|{U}{B}{B}|Creature - Elemental Nightmare|3|2|Flash$Whenever you cast another spell that has flash, you draw a card and each opponent loses 1 life.| +Snapdax, Apex of the Hunt|Ikoria: Lair of Behemoths|209|M|{1}{R}{W}{B}|Legendary Creature - Dinosaur Cat Nightmare|3|5|Mutate {2}{B/R}{W}{W}$Double Strike$Whenever this creature mutates, it deals 4 damage to target creature or planeswalker an opponent controls and you gain 4 life.| +Song of Creation|Ikoria: Lair of Behemoths|210|R|{1}{G}{U}{R}|Enchantment|||You may play an additional land on each of your turns.$Whenever you cast a spell, draw two cards.$At the beginning of your end step, discard your hand.| +Sprite Dragon|Ikoria: Lair of Behemoths|211|U|{U}{R}|Creature - Faerie Dragon|1|1|Flying, haste$Whenever you cast a noncreature spell, put a +1/+1 counter on Sprite Dragon.| +Titans' Nest|Ikoria: Lair of Behemoths|212|R|{1}{B}{G}{U}|Enchantment|||At the beginning of your upkeep, look at the top card of your library. You may put that card into your graveyard.$Exile a card from your graveyard: Add {C}. Spend this mana only to cast a colored spell without {X} in its mana cost.| +Trumpeting Gnarr|Ikoria: Lair of Behemoths|213|U|{1}{G}{U}|Creature - Beast|3|3|Mutate {3}{G/U}{G/U}$Whenever this creature mutates, create a 3/3 green Beast creature token.| +Vadrok, Apex of Thunder|Ikoria: Lair of Behemoths|214|M|{U}{R}{W}|Legendary Creature - Elemental Dinosaur Cat|3|3|Mutate {1}{W/U}{R}{R}$Flying, first strike$Whenever this creature mutates, you may cast target noncreature card with converted mana cost 3 or less from your graveyard without paying its mana cost.| +Whirlwind of Thought|Ikoria: Lair of Behemoths|215|R|{1}{U}{R}{W}|Enchantment|||Whenever you cast a noncreature spell, draw a card.| +Winota, Joiner of Forces|Ikoria: Lair of Behemoths|216|M|{2}{R}{W}|Legendary Creature - Human Warrior|4|4|Whenever a non-Human creature you control attacks, look at the top six cards of your library. You may put a Human creature card from among them onto the battlefield tapped and attacking. It gains indestructible until end of turn. Put the rest of the cards on the bottom of your library in a random order.| +Zenith Flare|Ikoria: Lair of Behemoths|217|U|{2}{R}{W}|Instant|||Zenith Flare deals X damage to any target and you gain X life, where X is the number of cards with a cycling ability in your graveyard.| +Alert Heedbonder|Ikoria: Lair of Behemoths|218|U|{1}{G/W}{G/W}|Creature - Human Scout|2|4|Vigilance$At the beginning of your end step, you gain 1 life for each creature you control with vigilance.| +Cunning Nightbonder|Ikoria: Lair of Behemoths|219|U|{U/B}{U/B}|Creature - Human Rogue|2|2|Flash$Spells with flash you cast cost {1} less to cast and can't be countered.| +Fiend Artisan|Ikoria: Lair of Behemoths|220|M|{B/G}{B/G}|Creature - Nightmare|1|1|Fiend Artisan gets +1/+1 for each creature card in your graveyard.${X}{B/G}, {T}, Sacrifice another creature: Search your library for a creature card with converted mana cost X or less, put it onto the battlefield, then shuffle your library. Activate this ability only any time you could cast a sorcery.| +Gyruda, Doom of Depths|Ikoria: Lair of Behemoths|221|R|{4}{U/B}{U/B}|Legendary Creature - Demon Kraken|6|6|Companion — Your starting deck contains only cards with even converted mana costs.$When Gyruda, Doom of Depths enters the battlefield, each player puts the top four cards of the library into their graveyard. Put a creature card with an even converted mana cost from among those cards onto the battlefield under your control.| +Jegantha, the Wellspring|Ikoria: Lair of Behemoths|222|R|{4}{R/G}|Legendary Creature - Elemental Elk|5|5|Companion — No card in your starting deck has more than one of the same mana symbol in its mana cost.${T}: Add {W}{U}{B}{R}{G}. This mana can't be spent to pay generic mana costs.| +Jubilant Skybonder|Ikoria: Lair of Behemoths|223|U|{1}{W/U}{W/U}|Creature - Human Wizard|2|2|Flying$Creatures you control with flying have "Spells your opponents cast that target this creature cost {2} more to cast."| +Kaheera, the Orphanguard|Ikoria: Lair of Behemoths|224|R|{1}{G/W}{G/W}|Legendary Creature - Cat Beast|3|2|Companion — Each creature card in your starting deck is a Cat, Elemental, Nightmare, Dinosaur, or Beast card.$Vigilance$Each other creature you control that's a Cat, Elemental, Nightmare, Dinosaur, or Beast gets +1/+1 and has vigilance.| +Keruga, the Macrosage|Ikoria: Lair of Behemoths|225|R|{3}{G/U}{G/U}|Legendary Creature - Dinosaur Hippo|5|4|Companion — Your starting deck contains only cards with converted mana cost 3 or greater and land cards.$When Keruga, the Macrosage enters the battlefield, draw a card for each other permanent you control with converted mana cost 3 or greater.| +Lurrus of the Dream-Den|Ikoria: Lair of Behemoths|226|R|{1}{W/B}{W/B}|Legendary Creature - Cat Nightmare|3|2|Companion — Each permanent card in your starting deck has converted mana cost 2 or less.$Lifelink$During each of your turns, you may cast one permanent spell with converted mana cost 2 or less from your graveyard.| +Lutri, the Spellchaser|Ikoria: Lair of Behemoths|227|R|{1}{U/R}{U/R}|Legendary Creature - Elemental Otter|3|2|Companion — Each nonland card in your starting deck has a different name.$Flash$When Lutri, the Spellchaser enters the battlefield, if you cast it, copy target instant or sorcery spell you control. You may choose new targets for the copy.| +Obosh, the Preypiercer|Ikoria: Lair of Behemoths|228|R|{3}{B/R}{B/R}|Legendary Creature - Hellion Horror|3|5|Companion — Your starting deck contains only cards with odd converted mana costs and land cards.$If a source you control with an odd converted mana cost would deal damage to a permanent or player, it deals double that damage to that permanent or player instead.| +Proud Wildbonder|Ikoria: Lair of Behemoths|229|U|{2}{R/G}{R/G}|Creature - Human Warrior|4|3|Trample$Creatures you control with trample have "You may have this creature assign its combat damage as though it weren't blocked."| +Sonorous Howlbonder|Ikoria: Lair of Behemoths|230|U|{1}{B/R}{B/R}|Creature - Human Warrior|2|2|Menace$Each creature you control with menace can't be blocked except by three or more creatures.| +Umori, the Collector|Ikoria: Lair of Behemoths|231|R|{2}{B/G}{B/G}|Legendary Creature - Ooze|4|5|Companion — Each nonland card in your starting deck shares a card type.$As Umori, the Collector enters the battlefield, choose a card type.$Spells you cast of the chosen type cost {1} less to cast.| +Yorion, Sky Nomad|Ikoria: Lair of Behemoths|232|R|{3}{W/U}{W/U}|Legendary Creature - Bird Serpent|4|5|Companion — Your starting deck contains at least twenty cards more than the minimum deck size.$Flying$When Yorion enters the battlefield, exile any number of other nonland permanents you own and control. Return those cards to the battlefield at the beginning of the next end step.| +Zirda, the Dawnwaker|Ikoria: Lair of Behemoths|233|R|{1}{R/W}{R/W}|Legendary Creature - Elemental Fox|3|3|Companion — Each permanent card in your starting deck has an activated ability.$Abilities you activate that aren't mana abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.${1}, {T}: Target creature can't block this turn.| +Crystalline Giant|Ikoria: Lair of Behemoths|234|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.| +Indatha Crystal|Ikoria: Lair of Behemoths|235|U|{3}|Artifact|||{T}: Add {W}, {B}, or {G}.$Cycling {2}| +Ketria Crystal|Ikoria: Lair of Behemoths|236|U|{3}|Artifact|||{T}: Add {G}, {U}, or {R}.$Cycling {2}| +The Ozolith|Ikoria: Lair of Behemoths|237|R|{1}|Legendary Artifact|||Whenever a creature you control leaves the battlefield, if it had counters on it, put those counters on The Ozolith.$At the beginning of combat on your turn, if The Ozolith has counters on it, you may move all counters from The Ozolith onto target creature.| +Raugrin Crystal|Ikoria: Lair of Behemoths|238|U|{3}|Artifact|||{T}: Add {U}, {R}, or {W}.$Cycling {2}| +Savai Crystal|Ikoria: Lair of Behemoths|239|U|{3}|Artifact|||{T}: Add {R}, {W}, or {B}.$Cycling {2}| +Sleeper Dart|Ikoria: Lair of Behemoths|240|C|{2}|Artifact|||When Sleeper Dart enters the battlefield, draw a card.${T}, Sacrifice Sleeper Dart: Target creature doesn't untap during its controller's next untap step.| +Springjaw Trap|Ikoria: Lair of Behemoths|241|C|{1}|Artifact|||Flash${4}, {T}, Sacrifice Springjaw Trap: It deals 3 damage to any target.| +Zagoth Crystal|Ikoria: Lair of Behemoths|242|U|{3}|Artifact|||{T}: Add {B}, {G}, or {U}.$Cycling {2}| +Bloodfell Caves|Ikoria: Lair of Behemoths|243|C||Land|||Bloodfell Caves enters the battlefield tapped.$When Bloodfell Caves enters the battlefield, you gain 1 life.${T}: Add {B} or {R}.| +Blossoming Sands|Ikoria: Lair of Behemoths|244|C||Land|||Blossoming Sands enters the battlefield tapped.$When Blossoming Sands enters the battlefield, you gain 1 life.${T}: Add {G} or {W}.| +Bonders' Enclave|Ikoria: Lair of Behemoths|245|R||Land|||{T}: Add {C}.${3}, {T}: Draw a card. Activate this ability only if you control a creature with power 4 or greater.| +Dismal Backwater|Ikoria: Lair of Behemoths|246|C||Land|||Dismal Backwater enters the battlefield tapped.$When Dismal Backwater enters the battlefield, you gain 1 life.${T}: Add {U} or {B}.| +Evolving Wilds|Ikoria: Lair of Behemoths|247|C||Land|||{T}, Sacrifice Evolving Wilds: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.| +Indatha Triome|Ikoria: Lair of Behemoths|248|R||Land - Plains Swamp Forest|||({T}: Add {W}, {B}, or {G}.)$Indatha Triome enters the battlefield tapped.$Cycling {3}| +Jungle Hollow|Ikoria: Lair of Behemoths|249|C||Land|||Jungle Hollow enters the battlefield tapped.$When Jungle Hollow enters the battlefield, you gain 1 life.${T}: Add {B} or {G}.| +Ketria Triome|Ikoria: Lair of Behemoths|250|R||Land - Forest Island Mountain|||({T}: Add {G}, {U}, or {R}.)$Ketria Triome enters the battlefield tapped.$Cycling {3}| +Raugrin Triome|Ikoria: Lair of Behemoths|251|R||Land - Island Mountain Plains|||({T}: Add {U}, {R}, or {W}.)$Raugrin Triome enters the battlefield tapped.$Cycling {3}| +Rugged Highlands|Ikoria: Lair of Behemoths|252|C||Land|||Rugged Highlands enters the battlefield tapped.$When Rugged Highlands enters the battlefield, you gain 1 life.${T}: Add {R} or {G}.| +Savai Triome|Ikoria: Lair of Behemoths|253|R||Land - Mountain Plains Swamp|||({T}: Add {R}, {W}, or {B}.)$Savai Triome enters the battlefield tapped.$Cycling {3}| +Scoured Barrens|Ikoria: Lair of Behemoths|254|C||Land|||Scoured Barrens enters the battlefield tapped.$When Scoured Barrens enters the battlefield, you gain 1 life.${T}: Add {W} or {B}.| +Swiftwater Cliffs|Ikoria: Lair of Behemoths|255|C||Land|||Swiftwater Cliffs enters the battlefield tapped.$When Swiftwater Cliffs enters the battlefield, you gain 1 life.${T}: Add {U} or {R}.| +Thornwood Falls|Ikoria: Lair of Behemoths|256|C||Land|||Thornwood Falls enters the battlefield tapped.$When Thornwood Falls enters the battlefield, you gain 1 life.${T}: Add {G} or {U}.| +Tranquil Cove|Ikoria: Lair of Behemoths|257|C||Land|||Tranquil Cove enters the battlefield tapped.$When Tranquil Cove enters the battlefield, you gain 1 life.${T}: Add {W} or {U}.| +Wind-Scarred Crag|Ikoria: Lair of Behemoths|258|C||Land|||Wind-Scarred Crag enters the battlefield tapped.$When Wind-Scarred Crag enters the battlefield, you gain 1 life.${T}: Add {R} or {W}.| +Zagoth Triome|Ikoria: Lair of Behemoths|259|R||Land - Swamp Forest Island|||({T}: Add {B}, {G}, or {U}.)$Zagoth Triome enters the battlefield tapped.$Cycling {3}| +Plains|Ikoria: Lair of Behemoths|260|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Ikoria: Lair of Behemoths|261|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Ikoria: Lair of Behemoths|262|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Ikoria: Lair of Behemoths|263|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Ikoria: Lair of Behemoths|264|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Ikoria: Lair of Behemoths|265|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Ikoria: Lair of Behemoths|266|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Ikoria: Lair of Behemoths|267|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Ikoria: Lair of Behemoths|268|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Ikoria: Lair of Behemoths|269|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Ikoria: Lair of Behemoths|270|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Ikoria: Lair of Behemoths|271|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Ikoria: Lair of Behemoths|272|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Ikoria: Lair of Behemoths|273|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Ikoria: Lair of Behemoths|274|C||Basic Land - Forest|||({T}: Add {G}.)| +Zilortha, Strength Incarnate|Ikoria: Lair of Behemoths|275|M|{3}{R}{G}|Legendary Creature - Dinosaur|7|3|Trample$Lethal damage dealt to creatures you control is determined by their power rather than their toughness.| +Lukka, Coppercoat Outcast|Ikoria: Lair of Behemoths|276|M|{3}{R}{R}|Legendary Planeswalker - Lukka|5|+1: Exile the top three cards of your library. Creature cards exiled this way gain "You may cast this card from exile as long as you control a Lukka planeswalker."$−2: Exile target creature you control, then reveal cards from the top of your library until you reveal a creature card with higher converted mana cost. Put that card onto the battlefield and the rest on the bottom of your library in a random order.$−7: Each creature you control deals damage equal to its power to each opponent.| +Vivien, Monsters' Advocate|Ikoria: Lair of Behemoths|277|M|{3}{G}{G}|Legendary Planeswalker - Vivien|3|You may look at the top card of your library any time.$You may cast creature spells from the top of your library.$+1: Create a 3/3 green Beast creature token. Put your choice of a vigilance counter, a reach counter, or a trample counter on it.$−2: When you cast your next creature spell this turn, search your library for a creature card with lesser converted mana cost, put it onto the battlefield, then shuffle your library.| +Narset of the Ancient Way|Ikoria: Lair of Behemoths|278|M|{1}{U}{R}{W}|Legendary Planeswalker - Narset|4|+1: You gain 2 life. Add {U}, {R}, or {W}. Spend this mana only to cast a noncreature spell.$−2: Draw a card, then you may discard a card. When you discard a nonland card this way, Narset of the Ancient Way deals damage equal to that card's converted mana cost to target creature or planeswalker.$−6: You get an emblem with "Whenever you cast a noncreature spell, this emblem deals 2 damage to any target."| +Cubwarden|Ikoria: Lair of Behemoths|279|R|{3}{W}|Creature - Cat|3|5|Mutate {2}{W}{W}$Lifelink$Whenever this creature mutates, create two 1/1 white Cat creature tokens with lifelink.| +Huntmaster Liger|Ikoria: Lair of Behemoths|280|U|{3}{W}|Creature - Cat|3|4|Mutate {2}{W}$Whenever this creature mutates, other creatures you control get +X/+X until end of turn, where X is the number of times this creature has mutated.| +Majestic Auricorn|Ikoria: Lair of Behemoths|281|U|{4}{W}|Creature - Unicorn|4|4|Mutate {3}{W}$Vigilance$Whenever this creature mutates, you gain 4 life.| +Vulpikeet|Ikoria: Lair of Behemoths|282|C|{3}{W}|Creature - Fox Bird|2|3|Mutate {2}{W}$Flying$Whenever this creature mutates, put a +1/+1 counter on it.| +Archipelagore|Ikoria: Lair of Behemoths|283|U|{5}{U}{U}|Creature - Leviathan|7|7|Mutate {5}{U}$Whenever this creature mutates, tap up to X target creatures, where X is the number of times this creature has mutated. Those creatures don't untap during their controller's next untap step.| +Dreamtail Heron|Ikoria: Lair of Behemoths|284|C|{4}{U}|Creature - Elemental Bird|3|4|Mutate {3}{U}$Flying$Whenever this creature mutates, draw a card.| +Pouncing Shoreshark|Ikoria: Lair of Behemoths|285|U|{4}{U}|Creature - Shark Beast|4|3|Mutate {3}{U}$Flash$Whenever this creature mutates, you may return target creature an opponent controls to its owner's hand.| +Sea-Dasher Octopus|Ikoria: Lair of Behemoths|286|R|{1}{U}{U}|Creature - Octopus|2|2|Mutate {1}{U}$Flash$Whenever this creature deals combat damage to a player, draw a card.| +Cavern Whisperer|Ikoria: Lair of Behemoths|287|C|{4}{B}|Creature - Nightmare|4|4|Mutate {3}{B}$Menace$Whenever this creature mutates, each opponent discards a card.| +Chittering Harvester|Ikoria: Lair of Behemoths|288|U|{5}{B}|Creature - Nightmare|4|6|Mutate {4}{B}$Whenever this creature mutates, each opponent sacrifices a creature.| +Dirge Bat|Ikoria: Lair of Behemoths|289|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4}{B}{B}$Flash$Flying$Whenever this creature mutates, destroy target creature or planeswalker an opponent controls.| +Insatiable Hemophage|Ikoria: Lair of Behemoths|290|U|{3}{B}|Creature - Nightmare|3|3|Mutate {2}{B}$Deathtouch$Whenever this creature mutates, each opponent loses X life and you gain X life, where X is the number of times this creature has mutated.| +Cloudpiercer|Ikoria: Lair of Behemoths|291|C|{4}{R}|Creature - Dinosaur|5|4|Mutate {3}{R}$Reach$Whenever this creature mutates, you may discard a card. If you do, draw a card.| +Everquill Phoenix|Ikoria: Lair of Behemoths|292|R|{2}{R}{R}|Creature - Phoenix|4|4|Mutate {3}{R}$Flying$Whenever this creature mutates, create a red artifact token named Feather with "{1}, Sacrifice Feather: Return target Phoenix card from your graveyard to the battlefield tapped."| +Porcuparrot|Ikoria: Lair of Behemoths|293|U|{3}{R}|Creature - Bird Beast|3|4|Mutate {2}{R}${T}: This creature deals X damage to any target, where X is the number of times this creature has mutated.| +Auspicious Starrix|Ikoria: Lair of Behemoths|294|U|{4}{G}|Creature - Elk Beast|6|6|Mutate {5}{G}$Whenever this creature mutates, exile cards from the top of your library until you exile X permanent cards, where X is the number of times this creature has mutated. Put those permanent cards onto the battlefield.| +Gemrazer|Ikoria: Lair of Behemoths|295|R|{3}{G}|Creature - Beast|4|4|Mutate {1}{G}{G}$Reach, trample$Whenever this creature mutates, destroy target artifact or enchantment an opponent controls.| +Glowstone Recluse|Ikoria: Lair of Behemoths|296|U|{2}{G}|Creature - Spider|2|3|Mutate {3}{G}$Reach$Whenever this creature mutates, put two +1/+1 counters on it.| +Migratory Greathorn|Ikoria: Lair of Behemoths|297|C|{3}{G}|Creature - Beast|3|4|Mutate {2}{G}$Whenever this creature mutates, search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.| +Boneyard Lurker|Ikoria: Lair of Behemoths|298|U|{2}{B}{G}|Creature - Nightmare Beast|4|4|Mutate {2}{B/G}{B/G}$Whenever this creature mutates, return target permanent card from your graveyard to your hand.| +Brokkos, Apex of Forever|Ikoria: Lair of Behemoths|299|M|{2}{B}{G}{U}|Legendary Creature - Nightmare Beast Elemental|6|6|Mutate {2}{U/B}{G}{G}$Trample$You may cast Brokkos, Apex of Forever from your graveyard using its mutate ability.| +Illuna, Apex of Wishes|Ikoria: Lair of Behemoths|300|M|{2}{G}{U}{R}|Legendary Creature - Beast Elemental Dinosaur|6|6|Mutate {3}{R/G}{U}{U}$Flying, trample$Whenever this creature mutates, exile cards from the top of your library until you exile a nonland permanent card. Put that card onto the battlefield or into your hand.| +Lore Drakkis|Ikoria: Lair of Behemoths|301|U|{1}{U}{R}|Creature - Lizard Beast|2|3|Mutate {U/R}{U/R}$Whenever this creature mutates, return target instant or sorcery card from your graveyard to your hand.| +Necropanther|Ikoria: Lair of Behemoths|302|U|{1}{W}{B}|Creature - Cat Nightmare|3|3|Mutate {2}{W/B}{W/B}$Whenever this creature mutates, return target creature card with converted mana cost 3 or less from your graveyard to the battlefield.| +Nethroi, Apex of Death|Ikoria: Lair of Behemoths|303|M|{2}{W}{B}{G}|Legendary Creature - Cat Nightmare Beast|5|5|Mutate {4}{G/W}{B}{B}$Deathtouch, lifelink$Whenever this creature mutates, return any number of target creature cards with total power 10 or less from your graveyard to the battlefield.| +Parcelbeast|Ikoria: Lair of Behemoths|304|U|{2}{G}{U}|Creature - Elemental Beast|2|4|Mutate {G}{U}${1}, {T}: Look at the top card of your library. If it's a land card, you may put it onto the battlefield. If you don't put the card onto the battlefield, put it into your hand.| +Regal Leosaur|Ikoria: Lair of Behemoths|305|U|{R}{W}|Creature - Dinosaur Cat|2|2|Mutate {1}{R/W}{R/W}$Whenever this creature mutates, other creatures you control get +2/+1 until end of turn.| +Snapdax, Apex of the Hunt|Ikoria: Lair of Behemoths|306|M|{1}{R}{W}{B}|Legendary Creature - Dinosaur Cat Nightmare|3|5|Mutate {2}{B/R}{W}{W}$Double Strike$Whenever this creature mutates, it deals 4 damage to target creature or planeswalker an opponent controls and you gain 4 life.| +Trumpeting Gnarr|Ikoria: Lair of Behemoths|307|U|{1}{G}{U}|Creature - Beast|3|3|Mutate {3}{G/U}{G/U}$Whenever this creature mutates, create a 3/3 green Beast creature token.| +Vadrok, Apex of Thunder|Ikoria: Lair of Behemoths|308|M|{U}{R}{W}|Legendary Creature - Elemental Dinosaur Cat|3|3|Mutate {1}{W/U}{R}{R}$Flying, first strike$Whenever this creature mutates, you may cast target noncreature card with converted mana cost 3 or less from your graveyard without paying its mana cost.| +Indatha Triome|Ikoria: Lair of Behemoths|309|R||Land - Plains Swamp Forest|||({T}: Add {W}, {B}, or {G}.)$Indatha Triome enters the battlefield tapped.$Cycling {3}| +Ketria Triome|Ikoria: Lair of Behemoths|310|R||Land - Forest Island Mountain|||({T}: Add {G}, {U}, or {R}.)$Ketria Triome enters the battlefield tapped.$Cycling {3}| +Raugrin Triome|Ikoria: Lair of Behemoths|311|R||Land - Island Mountain Plains|||({T}: Add {U}, {R}, or {W}.)$Raugrin Triome enters the battlefield tapped.$Cycling {3}| +Savai Triome|Ikoria: Lair of Behemoths|312|R||Land - Mountain Plains Swamp|||({T}: Add {R}, {W}, or {B}.)$Savai Triome enters the battlefield tapped.$Cycling {3}| +Zagoth Triome|Ikoria: Lair of Behemoths|313|R||Land - Swamp Forest Island|||({T}: Add {B}, {G}, or {U}.)$Zagoth Triome enters the battlefield tapped.$Cycling {3}| +Drannith Magistrate|Ikoria: Lair of Behemoths|314|R|{1}{W}|Creature - Human Wizard|1|3|Your opponents can't cast spells from anywhere other than their hands.| +Lavabrink Venturer|Ikoria: Lair of Behemoths|315|R|{2}{W}|Creature - Human Soldier|3|3|As Lavabrink Venturer enters the battlefield, choose odd or even.$Lavabrink Venturer has protection from each converted mana cost of the chosen value.| +Luminous Broodmoth|Ikoria: Lair of Behemoths|316|M|{2}{W}{W}|Creature - Insect|3|4|Flying$Whenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it.| +Mythos of Snapdax|Ikoria: Lair of Behemoths|317|R|{2}{W}{W}|Sorcery|||Each player chooses an artifact, a creature, an enchantment, and a planeswalker from among the nonland permanents they control, then sacrifices the rest. If {B}{R} was spent to cast this spell, you choose the permanents for each player instead.| +Mythos of Illuna|Ikoria: Lair of Behemoths|318|R|{2}{U}{U}|Sorcery|||Create a token that's a copy of target permanent. If {R}{G} was spent to cast this spell, instead create a token that's a copy of that permanent, except the token has "When this permanent enters the battlefield, if it's a creature, it fights up to one target creature you don't control."| +Shark Typhoon|Ikoria: Lair of Behemoths|319|R|{5}{U}|Enchantment|||Whenever you cast a noncreature spell, create an X/X blue Shark creature token with flying, where X is that spell's converted mana cost.$Cycling {X}{1}{U}$When you cycle Shark Typhoon, create an X/X blue Shark creature token with flying.| +Voracious Greatshark|Ikoria: Lair of Behemoths|320|R|{3}{U}{U}|Creature - Shark|5|4|Flash$When Voracious Greatshark enters the battlefield, counter target artifact or creature spell.| +Extinction Event|Ikoria: Lair of Behemoths|321|R|{3}{B}|Sorcery|||Choose odd or even. Exile each creature with converted mana cost of the chosen value.| +Hunted Nightmare|Ikoria: Lair of Behemoths|322|R|{1}{B}{B}|Creature - Nightmare|4|5|Menace$When Hunted Nightmare enters the battlefield, target opponent puts a deathtouch counter on a creature they control.| +Mythos of Nethroi|Ikoria: Lair of Behemoths|323|R|{2}{B}|Instant|||Destroy target nonland permanent if it's a creature or if {G}{W} was spent to cast this spell.| +Mythos of Vadrok|Ikoria: Lair of Behemoths|324|R|{2}{R}{R}|Sorcery|||Mythos of Vadrok deals 5 damage divided as you choose among any number of target creatures and/or planeswalkers. If {W}{U} was spent to cast this spell, until your next turn, those permanents can't attack or block and their activated abilities can't be activated.| +Unpredictable Cyclone|Ikoria: Lair of Behemoths|325|R|{3}{R}{R}|Enchantment|||If a cycling ability of another nonland card would cause you to draw a card, instead exile cards from the top of your library until you exile a card that shares a card type with the cycled card. You may cast that card without paying its mana cost. Then put the exiled cards that weren't cast this way on the bottom of your library in a random order.$Cycling {2}| +Yidaro, Wandering Monster|Ikoria: Lair of Behemoths|326|R|{5}{R}{R}|Legendary Creature - Dinosaur Turtle|8|8|Trample, haste$Cycling {1}{R}$When you cycle Yidaro, Wandering Monster, shuffle it into your library from your graveyard. If you've cycled a card named Yidaro, Wandering Monster four or more times this game, put it onto the battlefield from your graveyard instead.| +Colossification|Ikoria: Lair of Behemoths|327|R|{5}{G}{G}|Enchantment - Aura|||Enchant creature$When Colossification enters the battlefield, tap enchanted creature.$Enchanted creature gets +20/+20.| +Kogla, the Titan Ape|Ikoria: Lair of Behemoths|328|R|{3}{G}{G}{G}|Legendary Creature - Ape|7|6|When Kogla, the Titan Ape enters the battlefield, it fights up to one target creature you don't control.$Whenever Kogla attacks, destroy target artifact or enchantment defending player controls.${1}{G}: Return target Human you control to its owner's hand. Kogla gains indestructible until end of turn.| +Mythos of Brokkos|Ikoria: Lair of Behemoths|329|R|{2}{G}{G}|Sorcery|||If {U}{B} was spent to cast Mythos of Brokkos, search your library for a card, put that card into your graveyard, then shuffle your library.$Return up to two permanent cards from your graveyard to your hand.| +Chevill, Bane of Monsters|Ikoria: Lair of Behemoths|330|M|{B}{G}|Legendary Creature - Human Rogue|1|3|Deathtouch$At the beginning of your upkeep, if your opponents control no permanents with bounty counters on them, put a bounty counter on target creature or planeswalker an opponent controls.$Whenever a permanent an opponent controls with a bounty counter on it dies, you gain 3 life and draw a card.| +Death's Oasis|Ikoria: Lair of Behemoths|331|R|{W}{B}{G}|Enchantment|||Whenever a nontoken creature you control dies, put the top two cards of your library into your graveyard. Then return a creature card with lesser converted mana cost than the creature that died from your graveyard to your hand.${1}, Sacrifice Death's Oasis: You gain life equal to the greatest converted mana cost among creatures you control.| +Eerie Ultimatum|Ikoria: Lair of Behemoths|332|R|{W}{W}{B}{B}{B}{G}{G}|Sorcery|||Return any number of permanent cards with different names from your graveyard to the battlefield.| +Emergent Ultimatum|Ikoria: Lair of Behemoths|333|R|{B}{B}{G}{G}{G}{U}{U}|Sorcery|||Search your library for up to three monocolored cards with different names and exile them. An opponent chooses one of those cards. Shuffle that card into your library. You may cast the other cards without paying their mana costs. Exile Emergent Ultimatum.| +Frondland Felidar|Ikoria: Lair of Behemoths|334|R|{2}{G}{W}|Creature - Cat Beast|3|5|Vigilance$Creatures you control with vigilance have "{1}, {T}: Tap target creature."| +General Kudro of Drannith|Ikoria: Lair of Behemoths|335|M|{1}{W}{B}|Legendary Creature - Human Soldier|3|3|Other Humans you control get +1/+1.$Whenever General Kudro of Drannith or another Human enters the battlefield under your control, exile target card from an opponent's graveyard.${2}, Sacrifice two Humans: Destroy target creature with power 4 or greater.| +Genesis Ultimatum|Ikoria: Lair of Behemoths|336|R|{G}{G}{U}{U}{U}{R}{R}|Sorcery|||Look at the top five cards of your library. Put any number of permanent cards from among them onto the battlefield and the rest into your hand. Exile Genesis Ultimatum.| +Inspired Ultimatum|Ikoria: Lair of Behemoths|337|R|{U}{U}{R}{R}{R}{W}{W}|Sorcery|||Target player gains 5 life, Inspired Ultimatum deals 5 damage to any target, then you draw five cards.| +Kinnan, Bonder Prodigy|Ikoria: Lair of Behemoths|338|M|{G}{U}|Legendary Creature - Human Druid|2|2|Whenever you tap a nonland permanent for mana, add one mana of any type that permanent produced.${5}{G}{U}: Look at the top five cards of your library. You may put a non-Human creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order.| +Labyrinth Raptor|Ikoria: Lair of Behemoths|339|R|{B}{R}|Creature - Nightmare Dinosaur|2|2|Menace$Whenever a creature you control with menace becomes blocked, defending player sacrifices a creature blocking it.${B}{R}: Creatures you control with menace get +1/+0 until end of turn.| +Offspring's Revenge|Ikoria: Lair of Behemoths|340|R|{2}{R}{W}{B}|Enchantment|||At the beginning of combat on your turn, exile target red, white, or black creature card from your graveyard. Create a token that's a copy of that card, except it's 1/1. It gains haste until your next turn.| +Quartzwood Crasher|Ikoria: Lair of Behemoths|341|R|{2}{R}{R}{G}|Creature - Dinosaur Beast|6|6|Trample$Whenever one or more creatures you control with trample deal combat damage to a player, create an X/X green Dinosaur Beast creature token with trample, where X is the amount of damage those creatures dealt to that player.| +Rielle, the Everwise|Ikoria: Lair of Behemoths|342|M|{1}{U}{R}|Legendary Creature - Human Wizard|0|3|Rielle, the Everwise gets +1/+0 for each instant and sorcery card in your graveyard.$Whenever you discard one or more cards for the first time each turn, draw that many cards.| +Ruinous Ultimatum|Ikoria: Lair of Behemoths|343|R|{R}{R}{W}{W}{W}{B}{B}|Sorcery|||Destroy all nonland permanents your opponents control.| +Skycat Sovereign|Ikoria: Lair of Behemoths|344|R|{W}{U}|Creature - Elemental Cat|1|1|Flying$Skycat Sovereign gets +1/+1 for each other creature you control with flying.${2}{W}{U}: Create a 1/1 white Cat Bird creature token with flying.| +Slitherwisp|Ikoria: Lair of Behemoths|345|R|{U}{B}{B}|Creature - Elemental Nightmare|3|2|Flash$Whenever you cast another spell that has flash, you draw a card and each opponent loses 1 life.| +Song of Creation|Ikoria: Lair of Behemoths|346|R|{1}{G}{U}{R}|Enchantment|||You may play an additional land on each of your turns.$Whenever you cast a spell, draw two cards.$At the beginning of your end step, discard your hand.| +Titans' Nest|Ikoria: Lair of Behemoths|347|R|{1}{B}{G}{U}|Enchantment|||At the beginning of your upkeep, look at the top card of your library. You may put that card into your graveyard.$Exile a card from your graveyard: Add {C}. Spend this mana only to cast a colored spell without {X} in its mana cost.| +Whirlwind of Thought|Ikoria: Lair of Behemoths|348|R|{1}{U}{R}{W}|Enchantment|||Whenever you cast a noncreature spell, draw a card.| +Winota, Joiner of Forces|Ikoria: Lair of Behemoths|349|M|{2}{R}{W}|Legendary Creature - Human Warrior|4|4|Whenever a non-Human creature you control attacks, look at the top six cards of your library. You may put a Human creature card from among them onto the battlefield tapped and attacking. It gains indestructible until end of turn. Put the rest of the cards on the bottom of your library in a random order.| +Fiend Artisan|Ikoria: Lair of Behemoths|350|M|{B/G}{B/G}|Creature - Nightmare|1|1|Fiend Artisan gets +1/+1 for each creature card in your graveyard.${X}{B/G}, {T}, Sacrifice another creature: Search your library for a creature card with converted mana cost X or less, put it onto the battlefield, then shuffle your library. Activate this ability only any time you could cast a sorcery.| +Gyruda, Doom of Depths|Ikoria: Lair of Behemoths|351|R|{4}{U/B}{U/B}|Legendary Creature - Demon Kraken|6|6|Companion — Your starting deck contains only cards with even converted mana costs.$When Gyruda, Doom of Depths enters the battlefield, each player puts the top four cards of the library into their graveyard. Put a creature card with an even converted mana cost from among those cards onto the battlefield under your control.| +Jegantha, the Wellspring|Ikoria: Lair of Behemoths|352|R|{4}{R/G}|Legendary Creature - Elemental Elk|5|5|Companion — No card in your starting deck has more than one of the same mana symbol in its mana cost.${T}: Add {W}{U}{B}{R}{G}. This mana can't be spent to pay generic mana costs.| +Kaheera, the Orphanguard|Ikoria: Lair of Behemoths|353|R|{1}{G/W}{G/W}|Legendary Creature - Cat Beast|3|2|Companion — Each creature card in your starting deck is a Cat, Elemental, Nightmare, Dinosaur, or Beast card.$Vigilance$Each other creature you control that's a Cat, Elemental, Nightmare, Dinosaur, or Beast gets +1/+1 and has vigilance.| +Keruga, the Macrosage|Ikoria: Lair of Behemoths|354|R|{3}{G/U}{G/U}|Legendary Creature - Dinosaur Hippo|5|4|Companion — Your starting deck contains only cards with converted mana cost 3 or greater and land cards.$When Keruga, the Macrosage enters the battlefield, draw a card for each other permanent you control with converted mana cost 3 or greater.| +Lurrus of the Dream-Den|Ikoria: Lair of Behemoths|355|R|{1}{W/B}{W/B}|Legendary Creature - Cat Nightmare|3|2|Companion — Each permanent card in your starting deck has converted mana cost 2 or less.$Lifelink$During each of your turns, you may cast one permanent spell with converted mana cost 2 or less from your graveyard.| +Lutri, the Spellchaser|Ikoria: Lair of Behemoths|356|R|{1}{U/R}{U/R}|Legendary Creature - Elemental Otter|3|2|Companion — Each nonland card in your starting deck has a different name.$Flash$When Lutri, the Spellchaser enters the battlefield, if you cast it, copy target instant or sorcery spell you control. You may choose new targets for the copy.| +Obosh, the Preypiercer|Ikoria: Lair of Behemoths|357|R|{3}{B/R}{B/R}|Legendary Creature - Hellion Horror|3|5|Companion — Your starting deck contains only cards with odd converted mana costs and land cards.$If a source you control with an odd converted mana cost would deal damage to a permanent or player, it deals double that damage to that permanent or player instead.| +Umori, the Collector|Ikoria: Lair of Behemoths|358|R|{2}{B/G}{B/G}|Legendary Creature - Ooze|4|5|Companion — Each nonland card in your starting deck shares a card type.$As Umori, the Collector enters the battlefield, choose a card type.$Spells you cast of the chosen type cost {1} less to cast.| +Yorion, Sky Nomad|Ikoria: Lair of Behemoths|359|R|{3}{W/U}{W/U}|Legendary Creature - Bird Serpent|4|5|Companion — Your starting deck contains at least twenty cards more than the minimum deck size.$Flying$When Yorion enters the battlefield, exile any number of other nonland permanents you own and control. Return those cards to the battlefield at the beginning of the next end step.| +Zirda, the Dawnwaker|Ikoria: Lair of Behemoths|360|R|{1}{R/W}{R/W}|Legendary Creature - Elemental Fox|3|3|Companion — Each permanent card in your starting deck has an activated ability.$Abilities you activate that aren't mana abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.${1}, {T}: Target creature can't block this turn.| +Crystalline Giant|Ikoria: Lair of Behemoths|361|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.| +The Ozolith|Ikoria: Lair of Behemoths|362|R|{1}|Legendary Artifact|||Whenever a creature you control leaves the battlefield, if it had counters on it, put those counters on The Ozolith.$At the beginning of combat on your turn, if The Ozolith has counters on it, you may move all counters from The Ozolith onto target creature.| +Bonders' Enclave|Ikoria: Lair of Behemoths|363|R||Land|||{T}: Add {C}.${3}, {T}: Draw a card. Activate this ability only if you control a creature with power 4 or greater.| +Colossification|Ikoria: Lair of Behemoths|364|R|{5}{G}{G}|Enchantment - Aura|||Enchant creature$When Colossification enters the battlefield, tap enchanted creature.$Enchanted creature gets +20/+20.| +Flourishing Fox|Ikoria: Lair of Behemoths|365|U|{W}|Creature - Fox|1|1|Whenever you cycle another card, put a +1/+1 counter on Flourishing Fox.$Cycling {1}| +Heartless Act|Ikoria: Lair of Behemoths|366|U|{1}{B}|Instant|||Choose one —$• Destroy target creature with no counters on it.$• Remove up to three counters from target creature.| +Forbidden Friendship|Ikoria: Lair of Behemoths|367|C|{1}{R}|Sorcery|||Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token.| +Migration Path|Ikoria: Lair of Behemoths|368|U|{3}{G}|Sorcery|||Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library.$Cycling {2}| +Sprite Dragon|Ikoria: Lair of Behemoths|369|U|{U}{R}|Creature - Faerie Dragon|1|1|Flying, haste$Whenever you cast a noncreature spell, put a +1/+1 counter on Sprite Dragon.| +Huntmaster Liger|Ikoria: Lair of Behemoths|370|U|{3}{W}|Creature - Cat|3|4|Mutate {2}{W}$Whenever this creature mutates, other creatures you control get +X/+X until end of turn, where X is the number of times this creature has mutated.| +Luminous Broodmoth|Ikoria: Lair of Behemoths|371|M|{2}{W}{W}|Creature - Insect|3|4|Flying$Whenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it.| +Pollywog Symbiote|Ikoria: Lair of Behemoths|372|U|{1}{U}|Creature - Frog|1|3|Each creature spell you cast costs {1} less to cast if it has mutate.$Whenever you cast a creature spell, if it has mutate, draw a card, then discard a card.| +Void Beckoner|Ikoria: Lair of Behemoths|373|U|{6}{B}{B}|Creature - Nightmare Horror|8|8|Deathtouch$Cycling {2}{B}$When you cycle Void Beckoner, put a deathtouch counter on target creature you control.| +Everquill Phoenix|Ikoria: Lair of Behemoths|374|R|{2}{R}{R}|Creature - Phoenix|4|4|Mutate {3}{R}$Flying$Whenever this creature mutates, create a red artifact token named Feather with "{1}, Sacrifice Feather: Return target Phoenix card from your graveyard to the battlefield tapped."| +Yidaro, Wandering Monster|Ikoria: Lair of Behemoths|375|R|{5}{R}{R}|Legendary Creature - Dinosaur Turtle|8|8|Trample, haste$Cycling {1}{R}$When you cycle Yidaro, Wandering Monster, shuffle it into your library from your graveyard. If you've cycled a card named Yidaro, Wandering Monster four or more times this game, put it onto the battlefield from your graveyard instead.| +Gemrazer|Ikoria: Lair of Behemoths|376|R|{3}{G}|Creature - Beast|4|4|Mutate {1}{G}{G}$Reach, trample$Whenever this creature mutates, destroy target artifact or enchantment an opponent controls.| +Titanoth Rex|Ikoria: Lair of Behemoths|377|U|{7}{G}{G}|Creature - Dinosaur Beast|11|11|Trample$Cycling {1}{G}$When you cycle Titanoth Rex, put a trample counter on target creature you control.| +Brokkos, Apex of Forever|Ikoria: Lair of Behemoths|378|M|{2}{B}{G}{U}|Legendary Creature - Nightmare Beast Elemental|6|6|Mutate {2}{U/B}{G}{G}$Trample$You may cast Brokkos, Apex of Forever from your graveyard using its mutate ability.| +Illuna, Apex of Wishes|Ikoria: Lair of Behemoths|379|M|{2}{G}{U}{R}|Legendary Creature - Beast Elemental Dinosaur|6|6|Mutate {3}{R/G}{U}{U}$Flying, trample$Whenever this creature mutates, exile cards from the top of your library until you exile a nonland permanent card. Put that card onto the battlefield or into your hand.| +Nethroi, Apex of Death|Ikoria: Lair of Behemoths|380|M|{2}{W}{B}{G}|Legendary Creature - Cat Nightmare Beast|5|5|Mutate {4}{G/W}{B}{B}$Deathtouch, lifelink$Whenever this creature mutates, return any number of target creature cards with total power 10 or less from your graveyard to the battlefield.| +Snapdax, Apex of the Hunt|Ikoria: Lair of Behemoths|381|M|{1}{R}{W}{B}|Legendary Creature - Dinosaur Cat Nightmare|3|5|Mutate {2}{B/R}{W}{W}$Double Strike$Whenever this creature mutates, it deals 4 damage to target creature or planeswalker an opponent controls and you gain 4 life.| +Sprite Dragon|Ikoria: Lair of Behemoths|382|U|{U}{R}|Creature - Faerie Dragon|1|1|Flying, haste$Whenever you cast a noncreature spell, put a +1/+1 counter on Sprite Dragon.| +Vadrok, Apex of Thunder|Ikoria: Lair of Behemoths|383|M|{U}{R}{W}|Legendary Creature - Elemental Dinosaur Cat|3|3|Mutate {1}{W/U}{R}{R}$Flying, first strike$Whenever this creature mutates, you may cast target noncreature card with converted mana cost 3 or less from your graveyard without paying its mana cost.| +Gyruda, Doom of Depths|Ikoria: Lair of Behemoths|384|R|{4}{U/B}{U/B}|Legendary Creature - Demon Kraken|6|6|Companion — Your starting deck contains only cards with even converted mana costs.$When Gyruda, Doom of Depths enters the battlefield, each player puts the top four cards of the library into their graveyard. Put a creature card with an even converted mana cost from among those cards onto the battlefield under your control.| +Mysterious Egg|Ikoria: Lair of Behemoths|385|C|{1}|Creature - Egg|0|2|Whenever this creature mutates, put a +1/+1 counter on it.| +Dirge Bat|Ikoria: Lair of Behemoths|386|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4}{B}{B}$Flash$Flying$Whenever this creature mutates, destroy target creature or planeswalker an opponent controls.| +Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.| +Ugin, the Spirit Dragon|Core Set 2021|1|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.| +Alpine Watchdog|Core Set 2021|2|C|{1}{W}|Creature - Dog|2|2|Vigilance| +Angelic Ascension|Core Set 2021|3|U|{1}{W}|Instant|||Exile target creature or planeswalker. Its controller creates a 4/4 white Angel creature token with flying.| +Anointed Chorister|Core Set 2021|4|C|{W}|Creature - Human Cleric|1|1|Lifelink${4}{W}: Anointed Chorister gets +3/+3 until end of turn.| +Aven Gagglemaster|Core Set 2021|5|U|{3}{W}{W}|Creature - Bird Warrior|4|3|Flying$When Aven Gagglemaster enters the battlefield, you gain 2 life for each creature you control with flying.| +Baneslayer Angel|Core Set 2021|6|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons| +Basri Ket|Core Set 2021|7|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.$−2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.$−6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."| +Basri's Acolyte|Core Set 2021|8|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.| +Basri's Lieutenant|Core Set 2021|9|R|{3}{W}|Creature - Human Knight|3|4|Vigilance, protection from multicolored$When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.$Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.| +Basri's Solidarity|Core Set 2021|10|U|{1}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.| +Celestial Enforcer|Core Set 2021|11|C|{2}{W}|Creature - Human Cleric|2|3|{1}{W}, {T}: Tap target creature. Activate this ability only if you control a creature with flying.| +Concordia Pegasus|Core Set 2021|12|C|{1}{W}|Creature - Pegasus|1|3|Flying| +Containment Priest|Core Set 2021|13|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.| +Daybreak Charger|Core Set 2021|14|C|{1}{W}|Creature - Unicorn|3|1|When Daybreak Charger enters the battlefield, target creature gets +2/+0 until end of turn.| +Defiant Strike|Core Set 2021|15|C|{W}|Instant|||Target creature gets +1/+0 until end of turn.$Draw a card.| +Dub|Core Set 2021|16|C|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2, has first strike, and is a Knight in addition to its other types.| +Faith's Fetters|Core Set 2021|17|U|{3}{W}|Enchantment - Aura|||Enchant permanent$When Faith's Fetters enters the battlefield, you gain 4 life.$Enchanted permanent can't attack or block, and its activated abilities can't be activated unless they're mana abilities.| +Falconer Adept|Core Set 2021|18|U|{3}{W}|Creature - Human Soldier|2|3|Whenever Falconer Adept attacks, create a 1/1 white Bird creature token with flying that's tapped and attacking.| +Feat of Resistance|Core Set 2021|19|C|{1}{W}|Instant|||Put a +1/+1 counter on target creature you control. It gains protection from the color of your choice until end of turn.| +Gale Swooper|Core Set 2021|20|C|{3}{W}|Creature - Griffin|3|2|Flying$When Gale Swooper enters the battlefield, target creature gains flying until end of turn.| +Glorious Anthem|Core Set 2021|21|R|{1}{W}{W}|Enchantment|||Creatures you control get +1/+1.| +Griffin Aerie|Core Set 2021|22|U|{1}{W}|Enchantment|||At the beginning of your end step, if you gained 3 or more life this turn, create a 2/2 white Griffin creature token with flying.| +Idol of Endurance|Core Set 2021|23|R|{2}{W}|Artifact|||When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.${1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without paying its mana cost.| +Legion's Judgment|Core Set 2021|24|C|{2}{W}|Sorcery|||Destroy target creature with power 4 or greater.| +Light of Promise|Core Set 2021|25|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."| +Makeshift Battalion|Core Set 2021|26|C|{2}{W}|Creature - Human Soldier|3|2|Whenever Makeshift Battalion and at least two other creatures attack, put a +1/+1 counter on Makeshift Battalion.| +Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.| +Nine Lives|Core Set 2021|28|R|{1}{W}{W}|Enchantment|||Hexproof$If a source would deal damage to you, prevent that damage and put an incarnation counter on Nine Lives.$When there are nine or more incarnation counters on Nine Lives, exile it.$When Nine Lives leaves the battlefield, you lose the game.| +Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.| +Rambunctious Mutt|Core Set 2021|30|C|{3}{W}{W}|Creature - Dog|3|4|When Rambunctious Mutt enters the battlefield, destroy target artifact or enchantment an opponent controls.| +Revitalize|Core Set 2021|31|C|{1}{W}|Instant|||You gain 3 life.$Draw a card.| +Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen card name.| +Sanctum of Tranquil Light|Core Set 2021|33|U|{W}|Legendary Enchantment - Shrine|||{5}{W}: Tap target creature. This ability costs {1} less to activate for each Shrine you control.| +Seasoned Hallowblade|Core Set 2021|34|U|{1}{W}|Creature - Human Warrior|3|1|Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn.| +Secure the Scene|Core Set 2021|35|C|{4}{W}|Sorcery|||Exile target nonland permanent. Its controller creates a 1/1 white Soldier creature token.| +Selfless Savior|Core Set 2021|36|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Savior: Another target creature you control gains indestructible until end of turn.| +Siege Striker|Core Set 2021|37|U|{2}{W}|Creature - Human Soldier|1|1|Double strike$Whenever Siege Striker attacks, you may tap any number of untapped creatures you control. Siege Striker gets +1/+1 until end of turn for each creature tapped this way.| +Speaker of the Heavens|Core Set 2021|38|R|{W}|Creature - Human Cleric|1|1|Vigilance, lifelink${T}: Create a 4/4 white Angel creature token with flying. Activate this ability only if you have at least 7 more life than your starting life total and only any time you could cast a sorcery.| +Staunch Shieldmate|Core Set 2021|39|C|{W}|Creature - Dwarf Soldier|1|3|| +Swift Response|Core Set 2021|40|C|{1}{W}|Instant|||Destroy target tapped creature.| +Tempered Veteran|Core Set 2021|41|U|{1}{W}|Creature - Human Knight|1|2|{W}, {T}: Put a +1/+1 counter on target creature with a +1/+1 counter on it.${4}{W}{W}, {T}: Put a +1/+1 counter on target creature.| +Valorous Steed|Core Set 2021|42|C|{4}{W}|Creature - Unicorn|3|3|Vigilance$When Valorous Steed enters the battlefield, create a 2/2 white Knight creature token with vigilance.| +Vryn Wingmare|Core Set 2021|43|U|{2}{W}|Creature - Pegasus|2|1|Flying$Noncreature spells cost {1} more to cast.| +Warded Battlements|Core Set 2021|44|C|{2}{W}|Creature - Wall|0|3|Defender$Attacking creatures you control get +1/+0.| +Barrin, Tolarian Archmage|Core Set 2021|45|R|{1}{U}{U}|Legendary Creature - Human Wizard|2|2|When Barrin, Tolarian Archmage enters the battlefield, return up to one other target creature or planeswalker to its owner's hand.$At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card.| +Cancel|Core Set 2021|46|C|{1}{U}{U}|Instant|||Counter target spell.| +Capture Sphere|Core Set 2021|47|C|{3}{U}|Enchantment - Aura|||Flash$Enchant creature$When Capture Sphere enters the battlefield, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.| +Discontinuity|Core Set 2021|48|M|{3}{U}{U}{U}|Instant|||As long as it's your turn, this spell costs {2}{U}{U} less to cast.$End the turn.| +Enthralling Hold|Core Set 2021|49|U|{3}{U}{U}|Enchantment - Aura|||Enchant creature$You can't choose an untapped creature as this spell's target as you cast it.$You control enchanted creature.| +Frantic Inventory|Core Set 2021|50|C|{1}{U}|Instant|||Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard.| +Frost Breath|Core Set 2021|51|C|{2}{U}|Instant|||Tap up to two target creatures. Those creatures don't untap during their controller's next untap step.| +Ghostly Pilferer|Core Set 2021|52|R|{1}{U}|Creature - Spirit Rogue|2|1|Whenever Ghostly Pilferer becomes untapped, you may pay {2}. If you do, draw a card.$Whenever an opponent casts a spell from anywhere other than their hand, draw a card.$Discard a card: Ghostly Pilferer can't be blocked this turn.| +Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.| +Keen Glidemaster|Core Set 2021|54|C|{1}{U}|Creature - Human Soldier|2|1|{2}{U}: Target creature gains flying until end of turn.| +Library Larcenist|Core Set 2021|55|C|{2}{U}|Creature - Merfolk Rogue|1|2|Whenever Library Larcenist attacks, draw a card.| +Lofty Denial|Core Set 2021|56|C|{1}{U}|Instant|||Counter target spell unless its controller pays {1}. If you control a creature with flying, counter that spell unless its controller pays {4} instead.| +Miscast|Core Set 2021|57|U|{U}|Instant|||Counter target instant or sorcery spell unless its controller pays {3}.| +Mistral Singer|Core Set 2021|58|C|{2}{U}|Creature - Siren|2|2|Flying$Prowess| +Opt|Core Set 2021|59|C|{U}|Instant|||Scry 1.$Draw a card.| +Pursued Whale|Core Set 2021|60|R|{5}{U}{U}|Creature - Whale|8|8|When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able."$Spells your opponents cast that target Pursued Whale cost {3} more to cast.| +Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.| +Read the Tides|Core Set 2021|62|C|{5}{U}|Sorcery|||Choose one —$• Draw three cards.$• Return up to two target creatures to their owners' hands.| +Rewind|Core Set 2021|63|U|{2}{U}{U}|Instant|||Counter target spell. Untap up to four lands.| +Riddleform|Core Set 2021|64|U|{1}{U}|Enchantment|||Whenever you cast a noncreature spell, you may have Riddleform become a 3/3 Sphinx creature with flying in addition to its other types until end of turn.${2}{U}: Scry 1.| +Roaming Ghostlight|Core Set 2021|65|C|{3}{U}{U}|Creature - Spirit|3|2|Flying$When Roaming Ghostlight enters the battlefield, return up to one target non-Spirit creature to its owner's hand.| +Rookie Mistake|Core Set 2021|66|C|{U}|Instant|||Until end of turn, target creature gets +0/+2 and another target creature gets -2/-0.| +Rousing Read|Core Set 2021|67|C|{2}{U}|Enchantment - Aura|||Enchant creature$When Rousing Read enters the battlefield, draw two cards, then discard a card.$Enchanted creature gets +1/+1 and has flying.| +Sanctum of Calm Waters|Core Set 2021|68|U|{3}{U}|Legendary Enchantment - Shrine|||At the beginning of your precombat main phase, you may draw X cards, where X is the number of Shrines you control. If you do, discard a card.| +See the Truth|Core Set 2021|69|R|{1}{U}|Sorcery|||Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.| +Shacklegeist|Core Set 2021|70|R|{1}{U}|Creature - Spirit|2|2|Flying$Shacklegeist can block only creatures with flying.$Tap two untapped Spirits you control: Tap target creature you don't control.| +Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.| +Spined Megalodon|Core Set 2021|72|C|{5}{U}{U}|Creature - Shark|5|7|Hexproof$Whenever Spined Megalodon attacks, scry 1.| +Stormwing Entity|Core Set 2021|73|R|{3}{U}{U}|Creature - Elemental|3|3|This spell costs {2}{U} less to cast if you've cast an instant or sorcery spell this turn.$Flying$Prowess$When Stormwing Entity enters the battlefield, scry 2.| +Sublime Epiphany|Core Set 2021|74|R|{4}{U}{U}|Instant|||Choose one or more —$• Counter target spell.$• Counter target activated or triggered ability.$• Return target nonland permanent to its owner's hand.$• Create a token that's a copy of target creature you control.$• Target player draws a card.| +Teferi, Master of Time|Core Set 2021|75|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.| +Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.| +Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.| +Teferi's Tutelage|Core Set 2021|78|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.| +Tide Skimmer|Core Set 2021|79|U|{3}{U}|Creature - Drake|2|3|Flying$Whenever you attack with two or more creatures with flying, draw a card.| +Tolarian Kraken|Core Set 2021|80|U|{4}{U}{U}|Creature - Kraken|4|6|Whenever you draw a card, you may pay {1}. When you do, you may tap or untap target creature.| +Tome Anima|Core Set 2021|81|C|{3}{U}|Creature - Spirit|3|3|Tome Anima can't be blocked as long as you've drawn two or more cards this turn.| +Unsubstantiate|Core Set 2021|82|U|{1}{U}|Instant|||Return target spell or creature to its owner's hand.| +Vodalian Arcanist|Core Set 2021|83|C|{1}{U}|Creature - Merfolk Wizard|1|3|{T}: Add {C}. Spend this mana only to cast an instant or sorcery spell.| +Waker of Waves|Core Set 2021|84|U|{5}{U}{U}|Creature - Whale|7|7|Creatures your opponents control get -1/-0.${1}{U}, Discard Waker of Waves: Look at the top two cards of your library. Put one of them into your hand and the other into your graveyard.| +Wall of Runes|Core Set 2021|85|C|{U}|Creature - Wall|0|4|Defender$When Wall of Runes enters the battlefield, scry 1.| +Wishcoin Crab|Core Set 2021|86|C|{3}{U}|Creature - Crab|2|5|| +Alchemist's Gift|Core Set 2021|87|C|{B}|Instant|||Target creature gets +1/+1 and gains your choice of deathtouch or lifelink until end of turn.| +Archfiend's Vessel|Core Set 2021|88|U|{B}|Creature - Human Cleric|1|1|Lifelink$When Archfiend's Vessel enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying.| +Bad Deal|Core Set 2021|89|U|{4}{B}{B}|Sorcery|||You draw two cards and each opponent discards two cards. Each player loses 2 life.| +Blood Glutton|Core Set 2021|90|C|{4}{B}|Creature - Vampire|4|3|Lifelink| +Caged Zombie|Core Set 2021|91|C|{2}{B}|Creature - Zombie|2|3|{1}{B}, {T}: Each opponent loses 2 life. Activate this ability only if a creature died this turn.| +Carrion Grub|Core Set 2021|92|U|{3}{B}|Creature - Insect|0|5|Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard.$When Carrion Grub enters the battlefield, mill four cards.| +Crypt Lurker|Core Set 2021|93|C|{3}{B}|Creature - Horror|3|4|When Crypt Lurker enters the battlefield, you may sacrifice a creature or discard a creature card. If you do, draw a card.| +Deathbloom Thallid|Core Set 2021|94|C|{2}{B}|Creature - Fungus|3|2|When Deathbloom Thallid dies, create a 1/1 green Saproling creature token.| +Demonic Embrace|Core Set 2021|95|R|{1}{B}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+1, has flying, and is a Demon in addition to its other types.$You may cast Demonic Embrace from your graveyard by paying 3 life and discarding a card in addition to paying its other costs.| +Duress|Core Set 2021|96|C|{B}|Sorcery|||Target opponent reveals their hand. You choose a noncreature, nonland card from it. That player discards that card.| +Eliminate|Core Set 2021|97|U|{1}{B}|Instant|||Destroy target creature or planeswalker with converted mana cost 3 or less.| +Fetid Imp|Core Set 2021|98|C|{1}{B}|Creature - Imp|1|2|Flying${B}: Fetid Imp gains deathtouch until end of turn.| +Finishing Blow|Core Set 2021|99|C|{4}{B}|Instant|||Destroy target creature or planeswalker.| +Gloom Sower|Core Set 2021|100|C|{5}{B}{B}|Creature - Horror|8|6|Whenever Gloom Sower becomes blocked by a creature, that creature's controller loses 2 life and you gain 2 life.| +Goremand|Core Set 2021|101|U|{4}{B}{B}|Creature - Demon|5|5|As an additional cost to cast this spell, sacrifice a creature.$Flying$Trample$When Goremand enters the battlefield, each opponent sacrifices a creature.| +Grasp of Darkness|Core Set 2021|102|C|{B}{B}|Instant|||Target creature gets -4/-4 until end of turn.| +Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card, put that card into your hand, then shuffle your library. You lose 3 life.| +Hooded Blightfang|Core Set 2021|104|R|{2}{B}|Creature - Snake|1|4|Deathtouch$Whenever a creature you control with deathtouch attacks, each opponent loses 1 life and you gain 1 life.$Whenever a creature you control with deathtouch deals damage to a planeswalker, destroy that planeswalker.| +Infernal Scarring|Core Set 2021|105|C|{1}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+0 and has "When this creature dies, draw a card."| +Kaervek, the Spiteful|Core Set 2021|106|R|{2}{B}{B}|Legendary Creature - Human Warlock|3|2|Other creatures get -1/-1.| +Kitesail Freebooter|Core Set 2021|107|U|{1}{B}|Creature - Human Pirate|1|2|Flying$When Kitesail Freebooter enters the battlefield, target opponent reveals their hand. You choose a noncreature, nonland card from it. Exile that card until Kitesail Freebooter leaves the battlefield.| +Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."| +Liliana's Devotee|Core Set 2021|109|U|{2}{B}|Creature - Human Warlock|2|3|Zombies you control get +1/+0.$At the beginning of your end step, if a creature died this turn, you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token.| +Liliana's Standard Bearer|Core Set 2021|110|R|{2}{B}|Creature - Zombie Knight|3|1|Flash$When Liliana's Standard Bearer enters the battlefield, draw X cards, where X is the number of creatures that died under your control this turn.| +Liliana's Steward|Core Set 2021|111|C|{B}|Creature - Zombie|1|2|{T}, Sacrifice Liliana's Steward: Target opponent discards a card. Activate this ability only any time you could cast a sorcery.| +Malefic Scythe|Core Set 2021|112|U|{1}{B}|Artifact - Equipment|||Malefic Scythe enters the battlefield with a soul counter on it.$Equipped creature gets +1/+1 for each soul counter on Malefic Scythe.$Whenever equipped creature dies, put a soul counter on Malefic Scythe.$Equip {1}| +Masked Blackguard|Core Set 2021|113|C|{1}{B}|Creature - Human Rogue|2|1|Flash${2}{B}: Masked Blackguard gets +1/+1 until end of turn.| +Massacre Wurm|Core Set 2021|114|M|{3}{B}{B}{B}|Creature - Wurm|6|5|When Massacre Wurm enters the battlefield, creatures your opponents control get -2/-2 until end of turn.$Whenever a creature an opponent controls dies, that player loses 2 life.| +Mind Rot|Core Set 2021|115|C|{2}{B}|Sorcery|||Target player discards two cards.| +Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.| +Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.| +Pestilent Haze|Core Set 2021|118|U|{1}{B}{B}|Sorcery|||Choose one —$• All creatures get -2/-2 until end of turn.$• Remove two loyalty counters from each planeswalker.| +Rise Again|Core Set 2021|119|C|{4}{B}|Sorcery|||Return target creature card from your graveyard to the battlefield.| +Sanctum of Stone Fangs|Core Set 2021|120|U|{1}{B}|Legendary Enchantment - Shrine|||At the beginning of your precombat main phase, each opponent loses X life and you gain X life, where X is the number of Shrines you control.| +Sanguine Indulgence|Core Set 2021|121|C|{3}{B}|Sorcery|||This spell costs {3} less to cast if you've gained 3 or more life this turn.$Return up to two target creature cards from your graveyard to your hand.| +Silversmote Ghoul|Core Set 2021|122|U|{2}{B}|Creature - Zombie Vampire|3|1|At the beginning of your end step, if you gained 3 or more life this turn, return Silversmote Ghoul from your graveyard to the battlefield tapped.${1}{B}, Sacrifice Silversmote Ghoul: Draw a card.| +Skeleton Archer|Core Set 2021|123|C|{3}{B}|Creature - Skeleton Archer|3|3|When Skeleton Archer enters the battlefield, it deals 1 damage to any target.| +Tavern Swindler|Core Set 2021|124|U|{1}{B}|Creature - Human Rogue|2|2|{T}, Pay 3 life: Flip a coin. If you win the flip, you gain 6 life.| +Thieves' Guild Enforcer|Core Set 2021|125|R|{B}|Creature - Human Rogue|1|1|Flash$Whenever Thieves' Guild Enforcer or another Rogue enters the battlefield under your control, each opponent mills two cards.$As long as an opponent has eight or more cards in their graveyard, Thieves' Guild Enforcer gets +2/+1 and has deathtouch.| +Village Rites|Core Set 2021|126|C|{B}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Draw two cards.| +Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.| +Walking Corpse|Core Set 2021|128|C|{1}{B}|Creature - Zombie|2|2|| +Witch's Cauldron|Core Set 2021|129|U|{B}|Artifact|||{1}{B}, {T}, Sacrifice a creature: You gain 1 life and draw a card.| +Battle-Rattle Shaman|Core Set 2021|130|U|{3}{R}|Creature - Goblin Shaman|2|2|At the beginning of combat on your turn, you may have target creature get +2/+0 until end of turn.| +Bolt Hound|Core Set 2021|131|U|{2}{R}|Creature - Elemental Dog|2|2|Haste$Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn.| +Bone Pit Brute|Core Set 2021|132|C|{4}{R}{R}|Creature - Cyclops|4|5|Menace$When Bone Pit Brute enters the battlefield, target creature gets +4/+0 until end of turn.| +Brash Taunter|Core Set 2021|133|R|{4}{R}|Creature - Goblin|1|1|Indestructible$Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent.${2}{R}, {T}: Brash Taunter fights another target creature.| +Burn Bright|Core Set 2021|134|C|{2}{R}|Instant|||Creatures you control get +2/+0 until end of turn.| +Chandra, Heart of Fire|Core Set 2021|135|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.| +Chandra's Incinerator|Core Set 2021|136|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.| +Chandra's Magmutt|Core Set 2021|137|C|{1}{R}|Creature - Elemental Dog|2|2|{T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.| +Chandra's Pyreling|Core Set 2021|138|U|{1}{R}|Creature - Elemental Lizard|1|3|Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn.| +Conspicuous Snoop|Core Set 2021|139|R|{R}{R}|Creature - Goblin Rogue|2|2|Play with the top card of your library revealed.$You may cast Goblin spells from the top of your library.$As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card.| +Crash Through|Core Set 2021|140|C|{R}|Sorcery|||Creatures you control gain trample until end of turn.$Draw a card.| +Destructive Tampering|Core Set 2021|141|C|{2}{R}|Sorcery|||Choose one —$• Destroy target artifact.$• Creatures without flying can't block this turn.| +Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.| +Fiery Emancipation|Core Set 2021|143|M|{3}{R}{R}{R}|Enchantment|||If a source you control would deal damage to a permanent or player, it deals triple that damage to that permanent or player instead.| +Furious Rise|Core Set 2021|144|U|{2}{R}|Enchantment|||At the beginning of your end step, if you control a creature with power 4 or greater, exile the top card of your library. You may play that card until you exile another card with Furious Rise.| +Furor of the Bitten|Core Set 2021|145|C|{R}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and attacks each combat if able.| +Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.| +Goblin Arsonist|Core Set 2021|147|C|{R}|Creature - Goblin Shaman|1|1|When Goblin Arsonist dies, you may have it deal 1 damage to any target.| +Goblin Wizardry|Core Set 2021|148|C|{3}{R}|Instant|||Create two 1/1 red Goblin Wizard creature tokens with prowess.| +Havoc Jester|Core Set 2021|149|U|{4}{R}|Creature - Devil|5|5|Whenever you sacrifice a permanent, Havoc Jester deals 1 damage to any target.| +Heartfire Immolator|Core Set 2021|150|U|{1}{R}|Creature - Human Wizard|2|2|Prowess${R}, Sacrifice Heartfire Immolator: It deals damage equal to its power to target creature or planeswalker.| +Hellkite Punisher|Core Set 2021|151|U|{5}{R}{R}|Creature - Dragon|6|6|Flying${R}: Hellkite Punisher gets +1/+0 until end of turn.| +Hobblefiend|Core Set 2021|152|C|{1}{R}|Creature - Devil|2|1|Trample${1}, Sacrifice another creature: Put a +1/+1 counter on Hobblefiend.| +Igneous Cur|Core Set 2021|153|C|{1}{R}|Creature - Elemental Dog|1|2|{1}{R}: Igneous Cur gets +2/+0 until end of turn.| +Kinetic Augur|Core Set 2021|154|U|{3}{R}|Creature - Human Shaman|*|4|Trample$Kinetic Augur's power is equal to the number of instant and sorcery cards in your graveyard.$When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards.| +Onakke Ogre|Core Set 2021|155|C|{2}{R}|Creature - Ogre Warrior|4|2|| +Pitchburn Devils|Core Set 2021|156|C|{4}{R}|Creature - Devil|3|3|When Pitchburn Devils dies, it deals 3 damage to any target.| +Sanctum of Shattered Heights|Core Set 2021|157|U|{2}{R}|Legendary Enchantment - Shrine|||{1}, Discard a land card or Shrine card: Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control.| +Scorching Dragonfire|Core Set 2021|158|C|{1}{R}|Instant|||Scorching Dragonfire deals 3 damage to target creature or planeswalker. If that creature or planeswalker would die this turn, exile it instead.| +Shock|Core Set 2021|159|C|{R}|Instant|||Shock deals 2 damage to any target.| +Soul Sear|Core Set 2021|160|U|{2}{R}|Instant|||Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn.| +Spellgorger Weird|Core Set 2021|161|C|{2}{R}|Creature - Weird|2|2|Whenever you cast a noncreature spell, put a +1/+1 counter on Spellgorger Weird.| +Subira, Tulzidi Caravanner|Core Set 2021|162|R|{2}{R}|Legendary Creature - Human Shaman|2|3|Haste${1}: Another target creature with power 2 or less can't be blocked this turn.${1}{R}, {T}, Discard your hand: Until end of turn, whenever a creature you control with power 2 or less deals combat damage to a player, draw a card.| +Sure Strike|Core Set 2021|163|C|{1}{R}|Instant|||Target creature gets +3/+0 and gains first strike until end of turn.| +Terror of the Peaks|Core Set 2021|164|M|{3}{R}{R}|Creature - Dragon|5|4|Flying$Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast.$Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any target.| +Thrill of Possibility|Core Set 2021|165|C|{1}{R}|Instant|||As an additional cost to cast this spell, discard a card.$Draw two cards.| +Traitorous Greed|Core Set 2021|166|U|{3}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Add two mana of any one color.| +Transmogrify|Core Set 2021|167|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.| +Turn to Slag|Core Set 2021|168|C|{3}{R}{R}|Sorcery|||Turn to Slag deals 5 damage to target creature. Destroy all Equipment attached to that creature.| +Turret Ogre|Core Set 2021|169|C|{3}{R}|Creature - Ogre Warrior|4|3|Reach$When Turret Ogre enters the battlefield, if you control another creature with power 4 or greater, Turret Ogre deals 2 damage to each opponent.| +Unleash Fury|Core Set 2021|170|U|{1}{R}|Instant|||Double the power of target creature until end of turn.| +Volcanic Geyser|Core Set 2021|171|U|{X}{R}{R}|Instant|||Volcanic Geyser deals X damage to any target.| +Volcanic Salvo|Core Set 2021|172|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.| +Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.| +Burlfist Oak|Core Set 2021|174|U|{2}{G}{G}|Creature - Treefolk|2|3|Whenever you draw a card, Burlfist Oak gets +2/+2 until end of turn.| +Canopy Stalker|Core Set 2021|175|U|{3}{G}|Creature - Cat|4|2|Canopy Stalker must be blocked if able.$When Canopy Stalker dies, you gain 1 life for each creature that died this turn.| +Colossal Dreadmaw|Core Set 2021|176|C|{4}{G}{G}|Creature - Dinosaur|6|6|Trample| +Cultivate|Core Set 2021|177|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.| +Drowsing Tyrannodon|Core Set 2021|178|C|{1}{G}|Creature - Dinosaur|3|3|Defender$As long as you control a creature with power 4 or greater, Drowsing Tyrannodon can attack as though it didn't have defender.| +Elder Gargaroth|Core Set 2021|179|M|{3}{G}{G}|Creature - Beast|6|6|Vigilance, reach, trample$Whenever Elder Gargaroth attacks or blocks, choose one —$• Create a 3/3 green Beast creature token.$• You gain 3 life.$• Draw a card.| +Feline Sovereign|Core Set 2021|180|R|{2}{G}|Creature - Cat|2|3|Other Cats you control get +1/+1 and have protection from Dogs.$Whenever one or more Cats you control deal combat damage to a player, destroy up to one target artifact or enchantment that player controls.| +Fierce Empath|Core Set 2021|181|U|{2}{G}|Creature - Elf|1|1|When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library.| +Fungal Rebirth|Core Set 2021|182|U|{2}{G}|Instant|||Return target permanent card from your graveyard to your hand. If a creature died this turn, create two 1/1 green Saproling creature tokens.| +Garruk, Unleashed|Core Set 2021|183|M|{2}{G}{G}|Legendary Planeswalker - Garruk|4|+1: Up to one target creature gets +3/+3 and gains trample until end of turn.$−2: Create a 3/3 green Beast creature token. Then if an opponent controls more creatures than you, put a loyalty counter on Garruk, Unleashed.$−7: Create an emblem with "At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library."| +Garruk's Gorehorn|Core Set 2021|184|C|{4}{G}|Creature - Beast|7|3|| +Garruk's Harbinger|Core Set 2021|185|R|{1}{G}{G}|Creature - Beast|4|3|Hexproof from Black$Whenever Garruk's Harbinger deals combat damage to a player or planeswalker, look at that many cards from the top of your library. You may reveal a creature card or Garruk planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| +Garruk's Uprising|Core Set 2021|186|U|{2}{G}|Enchantment|||When Garruk's Uprising enters the battlefield, if you control a creature with power 4 or greater, draw a card.$Creatures you control have trample.$Whenever a creature with power 4 or greater enters the battlefield under your control, draw a card.| +Gnarled Sage|Core Set 2021|187|C|{3}{G}{G}|Creature - Treefolk Druid|4|4|Reach$As long as you've drawn two or more cards this turn, Gnarled Sage gets +0/+2 and has vigilance.| +Heroic Intervention|Core Set 2021|188|R|{1}{G}|Instant|||Permanents you control gain hexproof and indestructible until end of turn.| +Hunter's Edge|Core Set 2021|189|C|{3}{G}|Sorcery|||Put a +1/+1 counter on target creature you control. Then that creature deals damage equal to its power to target creature you don't control.| +Invigorating Surge|Core Set 2021|190|U|{2}{G}|Instant|||Put a +1/+1 counter on target creature you control, then double the number of +1/+1 counters on that creature.| +Jolrael, Mwonvuli Recluse|Core Set 2021|191|R|{1}{G}|Legendary Creature - Human Druid|1|2|Whenever you draw your second card each turn, create a 2/2 green Cat creature token.${4}{G}{G}: Until end of turn, creatures you control have base power and toughness X/X, where X is the number of cards in your hand.| +Life Goes On|Core Set 2021|192|C|{G}|Instant|||You gain 4 life. If a creature died this turn, you gain 8 life instead.| +Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.| +Ornery Dilophosaur|Core Set 2021|194|C|{3}{G}|Creature - Dinosaur|2|2|Deathtouch$Whenever Ornery Dilophosaur attacks, if you control a creature with power 4 or greater, Ornery Dilophosaur gets +2/+2 until end of turn.| +Portcullis Vine|Core Set 2021|195|C|{G}|Creature - Plant Wall|0|3|Defender${2}, {T}, Sacrifice a creature with defender: Draw a card.| +Pridemalkin|Core Set 2021|196|C|{2}{G}|Creature - Cat|2|1|When Pridemalkin enters the battlefield, put a +1/+1 counter on target creature you control.$Each creature you control with a +1/+1 counter on it has trample.| +Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.| +Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a spell that's white, blue, black, or red, put a +1/+1 counter on Quirion Dryad.| +Ranger's Guile|Core Set 2021|199|C|{G}|Instant|||Target creature you control gets +1/+1 and gains hexproof until end of turn.| +Return to Nature|Core Set 2021|200|C|{1}{G}|Instant|||Choose one —$• Destroy target artifact.$• Destroy target enchantment.$• Exile target card from a graveyard.| +Run Afoul|Core Set 2021|201|C|{G}|Instant|||Target opponent sacrifices a creature with flying.| +Sabertooth Mauler|Core Set 2021|202|C|{3}{G}|Creature - Cat|3|3|At the beginning of your end step, if a creature died this turn, put a +1/+1 counter on Sabertooth Mauler and untap it.| +Sanctum of Fruitful Harvest|Core Set 2021|203|U|{2}{G}|Legendary Enchantment - Shrine|||At the beginning of your precombat main phase, add X mana of any one color, where X is the number of Shrines you control.| +Scavenging Ooze|Core Set 2021|204|R|{1}{G}|Creature - Ooze|2|2|{G}: Exile target card from a graveyard. If it was a creature card, put a +1/+1 counter on Scavenging Ooze and you gain 1 life.| +Setessan Training|Core Set 2021|205|C|{1}{G}|Enchantment - Aura|||Enchant creature you control$When Setessan Training enters the battlefield, draw a card.$Enchanted creature gets +1/+0 and has trample.| +Skyway Sniper|Core Set 2021|206|U|{G}|Creature - Elf Archer|1|2|Reach${2}{G}: Skyway Sniper deals 1 damage to target creature with flying.| +Snarespinner|Core Set 2021|207|C|{1}{G}|Creature - Spider|1|3|Reach$Whenever Snarespinner blocks a creature with flying, Snarespinner gets +2/+0 until end of turn.| +Sporeweb Weaver|Core Set 2021|208|R|{2}{G}|Creature - Spider|1|4|Reach, hexproof from blue$Whenever Sporeweb Weaver is dealt damage, you gain 1 life and create a 1/1 green Saproling creature token.| +Thrashing Brontodon|Core Set 2021|209|U|{1}{G}{G}|Creature - Dinosaur|3|4|{1}, Sacrifice Thrashing Brontodon: Destroy target artifact or enchantment.| +Titanic Growth|Core Set 2021|210|C|{1}{G}|Instant|||Target creature gets +4/+4 until end of turn.| +Track Down|Core Set 2021|211|C|{1}{G}|Sorcery|||Scry 3, then reveal the top card of your library. If it's a creature or land card, draw a card.| +Trufflesnout|Core Set 2021|212|C|{2}{G}|Creature - Boar|2|2|When Trufflesnout enters the battlefield, choose one —$• Put a +1/+1 counter on Trufflesnout.$• You gain 4 life.| +Warden of the Woods|Core Set 2021|213|U|{4}{G}{G}|Creature - Treefolk|5|7|Vigilance$Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards.| +Wildwood Scourge|Core Set 2021|214|U|{X}{G}|Creature - Hydra|0|0|Wildwood Scourge enters the battlefield with X +1/+1 counters on it.$Whenever one or more +1/+1 counters are put on another non-Hydra creature you control, put a +1/+1 counter on Wildwood Scourge.| +Alpine Houndmaster|Core Set 2021|215|U|{R}{W}|Creature - Human Warrior|2|2|When Alpine Houndmaster enters the battlefield, you may search your library for a card named Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library.$Whenever Alpine Houndmaster attacks, it gets +X/+0 until end of turn, where X is the number of other attacking creatures.| +Conclave Mentor|Core Set 2021|216|U|{G}{W}|Creature - Centaur Cleric|2|2|If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on that creature instead.$When Conclave Mentor dies, you gain life equal to its power.| +Dire Fleet Warmonger|Core Set 2021|217|U|{1}{B}{R}|Creature - Orc Pirate|3|3|At the beginning of combat on your turn, you may sacrifice another creature. If you do, Dire Fleet Warmonger gets +2/+2 and gains trample until end of turn.| +Experimental Overload|Core Set 2021|218|U|{2}{U}{R}|Sorcery|||Create an X/X blue and red Weird creature token, where X is the number of instant and sorcery cards in your graveyard. Then you may return an instant or sorcery card from your graveyard to your hand. Exile Experimental Overload.| +Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.| +Leafkin Avenger|Core Set 2021|220|U|{2}{R}{G}|Creature - Elemental Druid|4|3|{T}: Add {G} for each creature you control with power 4 or greater.${7}{R}: Leafkin Avenger deals damage equal to its power to target player or planeswalker.| +Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, put a +1/+1 counter on Lorescale Coatl.| +Niambi, Esteemed Speaker|Core Set 2021|222|R|{W}{U}|Legendary Creature - Human Cleric|2|1|Flash$When Niambi, Esteemed Speaker enters the battlefield, you may return another target creature you control to its owner's hand. If you do, you gain life equal to that creature's converted mana cost.${1}{W}{U}, {T}, Discard a legendary card: Draw two cards.| +Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.| +Radha, Heart of Keld|Core Set 2021|224|R|{1}{R}{G}|Legendary Creature - Elf Warrior|3|3|As long as it's your turn, Radha, Heart of Keld has first strike.$You may look at the top card of your library any time, and you may play lands from the top of your library.${4}{R}{G}: Radha gets +X/+X until end of turn, where X is the number of lands you control.| +Sanctum of All|Core Set 2021|225|R|{W}{U}{B}{R}{G}|Legendary Enchantment - Shrine|||At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it.$If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.| +Twinblade Assassins|Core Set 2021|226|U|{3}{B}{G}|Creature - Elf Assassin|5|4|At the beginning of your end step, if a creature died this turn, draw a card.| +Watcher of the Spheres|Core Set 2021|227|U|{W}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.$Whenever another creature with flying enters the battlefield under your control, Watcher of the Spheres gets +1/+1 until end of turn.| +Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.| +Chrome Replicator|Core Set 2021|229|U|{5}|Artifact Creature - Construct|4|4|When Chrome Replicator enters the battlefield, if you control two or more nonland, nontoken permanents with the same name as one another, create a 4/4 colorless Construct artifact creature token.| +Epitaph Golem|Core Set 2021|230|U|{5}|Artifact Creature - Golem|3|5|{2}: Put target card from your graveyard on the bottom of your library.| +Forgotten Sentinel|Core Set 2021|231|C|{4}|Artifact Creature - Golem|4|3|Forgotten Sentinel enters the battlefield tapped.| +Mazemind Tome|Core Set 2021|232|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.| +Meteorite|Core Set 2021|233|U|{5}|Artifact|||When Meteorite enters the battlefield, it deals 2 damage to any target.${T}: Add one mana of any color.| +Palladium Myr|Core Set 2021|234|U|{3}|Artifact Creature - Myr|2|2|{T}: Add {C}{C}.| +Prismite|Core Set 2021|235|C|{2}|Artifact Creature - Golem|2|1|{2}: Add one mana of any color.| +Short Sword|Core Set 2021|236|C|{1}|Artifact - Equipment|||Equipped creature gets +1/+1.$Equip {1}| +Silent Dart|Core Set 2021|237|C|{1}|Artifact|||{4}, {T}, Sacrifice Silent Dart: It deals 3 damage to target creature.| +Skyscanner|Core Set 2021|238|C|{3}|Artifact Creature - Thopter|1|1|Flying$When Skyscanner enters the battlefield, draw a card.| +Solemn Simulacrum|Core Set 2021|239|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.| +Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.| +Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.| +Animal Sanctuary|Core Set 2021|242|R||Land|||{T}: Add {C}.${2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake.| +Bloodfell Caves|Core Set 2021|243|C||Land|||Bloodfell Caves enters the battlefield tapped.$When Bloodfell Caves enters the battlefield, you gain 1 life.${T}: Add {B} or {R}.| +Blossoming Sands|Core Set 2021|244|C||Land|||Blossoming Sands enters the battlefield tapped.$When Blossoming Sands enters the battlefield, you gain 1 life.${T}: Add {G} or {W}.| +Dismal Backwater|Core Set 2021|245|C||Land|||Dismal Backwater enters the battlefield tapped.$When Dismal Backwater enters the battlefield, you gain 1 life.${T}: Add {U} or {B}.| +Fabled Passage|Core Set 2021|246|R||Land|||{T}, Sacrifice Fabled Passage: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. Then if you control four or more lands, untap that land.| +Jungle Hollow|Core Set 2021|247|C||Land|||Jungle Hollow enters the battlefield tapped.$When Jungle Hollow enters the battlefield, you gain 1 life.${T}: Add {B} or {G}.| +Radiant Fountain|Core Set 2021|248|C||Land|||When Radiant Fountain enters the battlefield, you gain 2 life.${T}: Add {C}.| +Rugged Highlands|Core Set 2021|249|C||Land|||Rugged Highlands enters the battlefield tapped.$When Rugged Highlands enters the battlefield, you gain 1 life.${T}: Add {R} or {G}.| +Scoured Barrens|Core Set 2021|250|C||Land|||Scoured Barrens enters the battlefield tapped.$When Scoured Barrens enters the battlefield, you gain 1 life.${T}: Add {W} or {B}.| +Swiftwater Cliffs|Core Set 2021|251|C||Land|||Swiftwater Cliffs enters the battlefield tapped.$When Swiftwater Cliffs enters the battlefield, you gain 1 life.${T}: Add {U} or {R}.| +Temple of Epiphany|Core Set 2021|252|R||Land|||Temple of Epiphany enters the battlefield tapped.$When Temple of Epiphany enters the battlefield, scry 1.${T}: Add {U} or {R}.| +Temple of Malady|Core Set 2021|253|R||Land|||Temple of Malady enters the battlefield tapped.$When Temple of Malady enters the battlefield, scry 1.${T}: Add {B} or {G}.| +Temple of Mystery|Core Set 2021|254|R||Land|||Temple of Mystery enters the battlefield tapped.$When Temple of Mystery enters the battlefield, scry 1.${T}: Add {G} or {U}.| +Temple of Silence|Core Set 2021|255|R||Land|||Temple of Silence enters the battlefield tapped.$When Temple of Silence enters the battlefield, scry 1.${T}: Add {W} or {B}.| +Temple of Triumph|Core Set 2021|256|R||Land|||Temple of Triumph enters the battlefield tapped.$When Temple of Triumph enters the battlefield, scry 1.${T}: Add {R} or {W}.| +Thornwood Falls|Core Set 2021|257|C||Land|||Thornwood Falls enters the battlefield tapped.$When Thornwood Falls enters the battlefield, you gain 1 life.${T}: Add {G} or {U}.| +Tranquil Cove|Core Set 2021|258|C||Land|||Tranquil Cove enters the battlefield tapped.$When Tranquil Cove enters the battlefield, you gain 1 life.${T}: Add {W} or {U}.| +Wind-Scarred Crag|Core Set 2021|259|C||Land|||Wind-Scarred Crag enters the battlefield tapped.$When Wind-Scarred Crag enters the battlefield, you gain 1 life.${T}: Add {R} or {W}.| +Plains|Core Set 2021|260|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Core Set 2021|261|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Core Set 2021|262|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Core Set 2021|263|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Core Set 2021|264|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Core Set 2021|265|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Core Set 2021|266|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Core Set 2021|267|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Core Set 2021|268|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Core Set 2021|269|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Core Set 2021|270|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Core Set 2021|271|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Core Set 2021|272|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Core Set 2021|273|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Core Set 2021|274|C||Basic Land - Forest|||({T}: Add {G}.)| +Teferi, Master of Time|Core Set 2021|275|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.| +Teferi, Master of Time|Core Set 2021|276|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.| +Teferi, Master of Time|Core Set 2021|277|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.| +Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.| +Ugin, the Spirit Dragon|Core Set 2021|279|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.| +Basri Ket|Core Set 2021|280|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.$−2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.$−6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."| +Teferi, Master of Time|Core Set 2021|281|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.| +Liliana, Waker of the Dead|Core Set 2021|282|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."| +Chandra, Heart of Fire|Core Set 2021|283|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.| +Garruk, Unleashed|Core Set 2021|284|M|{2}{G}{G}|Legendary Planeswalker - Garruk|4|+1: Up to one target creature gets +3/+3 and gains trample until end of turn.$−2: Create a 3/3 green Beast creature token. Then if an opponent controls more creatures than you, put a loyalty counter on Garruk, Unleashed.$−7: Create an emblem with "At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library."| +Ugin, the Spirit Dragon|Core Set 2021|285|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.| +Basri Ket|Core Set 2021|286|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.$−2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.$−6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."| +Basri's Acolyte|Core Set 2021|287|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.| +Basri's Lieutenant|Core Set 2021|288|R|{3}{W}|Creature - Human Knight|3|4|Vigilance, protection from multicolored$When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.$Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.| +Basri's Solidarity|Core Set 2021|289|U|{1}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.| +Teferi, Master of Time|Core Set 2021|290|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.| +Teferi, Master of Time|Core Set 2021|291|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.| +Teferi, Master of Time|Core Set 2021|292|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.| +Teferi, Master of Time|Core Set 2021|293|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.| +Teferi's Ageless Insight|Core Set 2021|294|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.| +Teferi's Protege|Core Set 2021|295|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.| +Teferi's Tutelage|Core Set 2021|296|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.| +Liliana, Waker of the Dead|Core Set 2021|297|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."| +Liliana's Devotee|Core Set 2021|298|U|{2}{B}|Creature - Human Warlock|2|3|Zombies you control get +1/+0.$At the beginning of your end step, if a creature died this turn, you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token.| +Liliana's Standard Bearer|Core Set 2021|299|R|{2}{B}|Creature - Zombie Knight|3|1|Flash$When Liliana's Standard Bearer enters the battlefield, draw X cards, where X is the number of creatures that died under your control this turn.| +Liliana's Steward|Core Set 2021|300|C|{B}|Creature - Zombie|1|2|{T}, Sacrifice Liliana's Steward: Target opponent discards a card. Activate this ability only any time you could cast a sorcery.| +Chandra, Heart of Fire|Core Set 2021|301|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.| +Chandra's Incinerator|Core Set 2021|302|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.| +Chandra's Magmutt|Core Set 2021|303|C|{1}{R}|Creature - Elemental Dog|2|2|{T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.| +Chandra's Pyreling|Core Set 2021|304|U|{1}{R}|Creature - Elemental Lizard|1|3|Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn.| +Garruk, Unleashed|Core Set 2021|305|M|{2}{G}{G}|Legendary Planeswalker - Garruk|4|+1: Up to one target creature gets +3/+3 and gains trample until end of turn.$−2: Create a 3/3 green Beast creature token. Then if an opponent controls more creatures than you, put a loyalty counter on Garruk, Unleashed.$−7: Create an emblem with "At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library."| +Garruk's Gorehorn|Core Set 2021|306|C|{4}{G}|Creature - Beast|7|3|| +Garruk's Harbinger|Core Set 2021|307|R|{1}{G}{G}|Creature - Beast|4|3|Hexproof from Black$Whenever Garruk's Harbinger deals combat damage to a player or planeswalker, look at that many cards from the top of your library. You may reveal a creature card or Garruk planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| +Garruk's Uprising|Core Set 2021|308|U|{2}{G}|Enchantment|||When Garruk's Uprising enters the battlefield, if you control a creature with power 4 or greater, draw a card.$Creatures you control have trample.$Whenever a creature with power 4 or greater enters the battlefield under your control, draw a card.| +Plains|Core Set 2021|309|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Core Set 2021|310|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Core Set 2021|311|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Core Set 2021|312|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Core Set 2021|313|C||Basic Land - Forest|||({T}: Add {G}.)| +Containment Priest|Core Set 2021|314|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.| +Grim Tutor|Core Set 2021|315|M|{1}{B}{B}|Sorcery|||Search your library for a card, put that card into your hand, then shuffle your library. You lose 3 life.| +Massacre Wurm|Core Set 2021|316|M|{3}{B}{B}{B}|Creature - Wurm|6|5|When Massacre Wurm enters the battlefield, creatures your opponents control get -2/-2 until end of turn.$Whenever a creature an opponent controls dies, that player loses 2 life.| +Cultivate|Core Set 2021|317|R|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.| +Scavenging Ooze|Core Set 2021|318|R|{1}{G}|Creature - Ooze|2|2|{G}: Exile target card from a graveyard. If it was a creature card, put a +1/+1 counter on Scavenging Ooze and you gain 1 life.| +Solemn Simulacrum|Core Set 2021|319|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.| +Basri, Devoted Paladin|Core Set 2021|320|M|{4}{W}{W}|Legendary Planeswalker - Basri|4|+1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn.$−1: Whenever a creature attacks this turn, put a +1/+1 counter on it.$−6: Creatures you control get +2/+2 and gain flying until end of turn.| +Adherent of Hope|Core Set 2021|321|C|{1}{W}|Creature - Human Soldier|2|1|At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope.| +Basri's Aegis|Core Set 2021|322|R|{2}{W}{W}|Sorcery|||Put a +1/+1 counter on each of up to two target creatures. You may search your library and/or graveyard for a card named Basri, Devoted Paladin, reveal it, and put it into your hand. If you search your library this way, shuffle it.| +Sigiled Contender|Core Set 2021|323|U|{3}{W}|Creature - Human Warrior|3|3|Sigiled Contender has lifelink as long as it has a +1/+1 counter on it.| +Teferi, Timeless Voyager|Core Set 2021|324|M|{4}{U}{U}|Legendary Planeswalker - Teferi|4|+1: Draw a card.$−3: Put target creature on top of its owner's library.$−8: Each creature target opponent controls phases out. Until the end of your next turn, they can't phase in.| +Historian of Zhalfir|Core Set 2021|325|U|{2}{U}{U}|Creature - Human Wizard|3|3|Whenever Historian of Zhalfir attacks, if you control a Teferi planeswalker, draw a card.| +Mystic Skyfish|Core Set 2021|326|C|{2}{U}|Creature - Fish|3|1|Whenever you draw your second card each turn, Mystic Skyfish gains flying until end of turn.| +Teferi's Wavecaster|Core Set 2021|327|R|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Flash$When Teferi's Wavecaster enters the battlefield, you may search your library and/or graveyard for a card named Teferi, Timeless Voyager, reveal it, and put it into your hand. If you search your library this way, shuffle it.| +Liliana, Death Mage|Core Set 2021|328|M|{4}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Return up to one target creature card from your graveyard to your hand.$−3: Destroy target creature. Its controller loses 2 life.$−7: Target opponent loses 2 life for each creature card in their graveyard.| +Liliana's Scorn|Core Set 2021|329|R|{3}{B}{B}|Sorcery|||Destroy target creature. You may search your library and/or graveyard for a card named Liliana, Death Mage, reveal it, and put it into your hand. If you search your library this way, shuffle it.| +Liliana's Scrounger|Core Set 2021|330|U|{2}{B}|Creature - Human Wizard|3|2|At the beginning of each end step, if a creature died this turn, you may put a loyalty counter on a Liliana planeswalker you control.| +Spirit of Malevolence|Core Set 2021|331|C|{1}{B}|Creature - Spirit|2|1|When Spirit of Malevolence dies, each opponent loses 1 life and you gain 1 life.| +Chandra, Flame's Catalyst|Core Set 2021|332|M|{4}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Chandra, Flame's Catalyst deals 3 damage to each opponent.$−2: You may cast target red instant or sorcery card from your graveyard. If that spell would be put into your graveyard this turn, exile it instead.$−8: Discard your hand, then draw seven cards. Until end of turn, you may cast spells from your hand without paying their mana costs.| +Chandra's Firemaw|Core Set 2021|333|R|{3}{R}{R}|Creature - Hellion|4|2|Haste$When Chandra's Firemaw enters the battlefield, you may search your library and/or graveyard for a card named Chandra, Flame's Catalyst, reveal it, and put it into your hand. If you search your library this way, shuffle it.| +Keral Keep Disciples|Core Set 2021|334|U|{2}{R}{R}|Creature - Human Monk|4|3|Whenever you activate a loyalty ability of a Chandra planeswalker, Keral Keep Disciples deals 1 damage to each opponent.| +Storm Caller|Core Set 2021|335|C|{2}{R}|Creature - Ogre Shaman|3|2|When Storm Caller enters the battlefield, it deals 2 damage to each opponent.| +Garruk, Savage Herald|Core Set 2021|336|M|{4}{G}{G}|Legendary Planeswalker - Garruk|5|+1: Reveal the top card of your library. If it's a creature card, put it into your hand. Otherwise, put it on the bottom of your library.$−2: Target creature you control deals damage equal to its power to another target creature.$−7: Until end of turn, creatures you control gain "You may have this creature assign its combat damage as though it weren't blocked."| +Garruk's Warsteed|Core Set 2021|337|R|{3}{G}{G}|Creature - Rhino|3|5|Vigilance$When Garruk's Warsteed enters the battlefield, you may search your library and/or graveyard for a card named Garruk, Savage Herald, reveal it, and put it into your hand. If you search your library this way, shuffle it.| +Predatory Wurm|Core Set 2021|338|U|{3}{G}|Creature - Wurm|4|4|Vigilance$Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker.| +Wildwood Patrol|Core Set 2021|339|C|{2}{G}|Creature - Centaur Scout|4|2|Trample| +Baneslayer Angel|Core Set 2021|340|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons| +Glorious Anthem|Core Set 2021|341|R|{1}{W}{W}|Enchantment|||Creatures you control get +1/+1.| +Idol of Endurance|Core Set 2021|342|R|{2}{W}|Artifact|||When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.${1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without paying its mana cost.| +Mangara, the Diplomat|Core Set 2021|343|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.| +Nine Lives|Core Set 2021|344|R|{1}{W}{W}|Enchantment|||Hexproof$If a source would deal damage to you, prevent that damage and put an incarnation counter on Nine Lives.$When there are nine or more incarnation counters on Nine Lives, exile it.$When Nine Lives leaves the battlefield, you lose the game.| +Pack Leader|Core Set 2021|345|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.| +Runed Halo|Core Set 2021|346|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen card name.| +Speaker of the Heavens|Core Set 2021|347|R|{W}|Creature - Human Cleric|1|1|Vigilance, lifelink${T}: Create a 4/4 white Angel creature token with flying. Activate this ability only if you have at least 7 more life than your starting life total and only any time you could cast a sorcery.| +Barrin, Tolarian Archmage|Core Set 2021|348|R|{1}{U}{U}|Legendary Creature - Human Wizard|2|2|When Barrin, Tolarian Archmage enters the battlefield, return up to one other target creature or planeswalker to its owner's hand.$At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card.| +Discontinuity|Core Set 2021|349|M|{3}{U}{U}{U}|Instant|||As long as it's your turn, this spell costs {2}{U}{U} less to cast.$End the turn.| +Ghostly Pilferer|Core Set 2021|350|R|{1}{U}|Creature - Spirit Rogue|2|1|Whenever Ghostly Pilferer becomes untapped, you may pay {2}. If you do, draw a card.$Whenever an opponent casts a spell from anywhere other than their hand, draw a card.$Discard a card: Ghostly Pilferer can't be blocked this turn.| +Pursued Whale|Core Set 2021|351|R|{5}{U}{U}|Creature - Whale|8|8|When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able."$Spells your opponents cast that target Pursued Whale cost {3} more to cast.| +See the Truth|Core Set 2021|352|R|{1}{U}|Sorcery|||Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.| +Shacklegeist|Core Set 2021|353|R|{1}{U}|Creature - Spirit|2|2|Flying$Shacklegeist can block only creatures with flying.$Tap two untapped Spirits you control: Tap target creature you don't control.| +Stormwing Entity|Core Set 2021|354|R|{3}{U}{U}|Creature - Elemental|3|3|This spell costs {2}{U} less to cast if you've cast an instant or sorcery spell this turn.$Flying$Prowess$When Stormwing Entity enters the battlefield, scry 2.| +Sublime Epiphany|Core Set 2021|355|R|{4}{U}{U}|Instant|||Choose one or more —$• Counter target spell.$• Counter target activated or triggered ability.$• Return target nonland permanent to its owner's hand.$• Create a token that's a copy of target creature you control.$• Target player draws a card.| +Demonic Embrace|Core Set 2021|356|R|{1}{B}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+1, has flying, and is a Demon in addition to its other types.$You may cast Demonic Embrace from your graveyard by paying 3 life and discarding a card in addition to paying its other costs.| +Hooded Blightfang|Core Set 2021|357|R|{2}{B}|Creature - Snake|1|4|Deathtouch$Whenever a creature you control with deathtouch attacks, each opponent loses 1 life and you gain 1 life.$Whenever a creature you control with deathtouch deals damage to a planeswalker, destroy that planeswalker.| +Kaervek, the Spiteful|Core Set 2021|358|R|{2}{B}{B}|Legendary Creature - Human Warlock|3|2|Other creatures get -1/-1.| +Necromentia|Core Set 2021|359|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.| +Peer into the Abyss|Core Set 2021|360|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.| +Thieves' Guild Enforcer|Core Set 2021|361|R|{B}|Creature - Human Rogue|1|1|Flash$Whenever Thieves' Guild Enforcer or another Rogue enters the battlefield under your control, each opponent mills two cards.$As long as an opponent has eight or more cards in their graveyard, Thieves' Guild Enforcer gets +2/+1 and has deathtouch.| +Vito, Thorn of the Dusk Rose|Core Set 2021|362|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.| +Brash Taunter|Core Set 2021|363|R|{4}{R}|Creature - Goblin|1|1|Indestructible$Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent.${2}{R}, {T}: Brash Taunter fights another target creature.| +Conspicuous Snoop|Core Set 2021|364|R|{R}{R}|Creature - Goblin Rogue|2|2|Play with the top card of your library revealed.$You may cast Goblin spells from the top of your library.$As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card.| +Double Vision|Core Set 2021|365|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.| +Fiery Emancipation|Core Set 2021|366|M|{3}{R}{R}{R}|Enchantment|||If a source you control would deal damage to a permanent or player, it deals triple that damage to that permanent or player instead.| +Gadrak, the Crown-Scourge|Core Set 2021|367|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.| +Subira, Tulzidi Caravanner|Core Set 2021|368|R|{2}{R}|Legendary Creature - Human Shaman|2|3|Haste${1}: Another target creature with power 2 or less can't be blocked this turn.${1}{R}, {T}, Discard your hand: Until end of turn, whenever a creature you control with power 2 or less deals combat damage to a player, draw a card.| +Terror of the Peaks|Core Set 2021|369|M|{3}{R}{R}|Creature - Dragon|5|4|Flying$Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast.$Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any target.| +Transmogrify|Core Set 2021|370|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.| +Volcanic Salvo|Core Set 2021|371|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.| +Azusa, Lost but Seeking|Core Set 2021|372|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.| +Elder Gargaroth|Core Set 2021|373|M|{3}{G}{G}|Creature - Beast|6|6|Vigilance, reach, trample$Whenever Elder Gargaroth attacks or blocks, choose one —$• Create a 3/3 green Beast creature token.$• You gain 3 life.$• Draw a card.| +Feline Sovereign|Core Set 2021|374|R|{2}{G}|Creature - Cat|2|3|Other Cats you control get +1/+1 and have protection from Dogs.$Whenever one or more Cats you control deal combat damage to a player, destroy up to one target artifact or enchantment that player controls.| +Heroic Intervention|Core Set 2021|375|R|{1}{G}|Instant|||Permanents you control gain hexproof and indestructible until end of turn.| +Jolrael, Mwonvuli Recluse|Core Set 2021|376|R|{1}{G}|Legendary Creature - Human Druid|1|2|Whenever you draw your second card each turn, create a 2/2 green Cat creature token.${4}{G}{G}: Until end of turn, creatures you control have base power and toughness X/X, where X is the number of cards in your hand.| +Primal Might|Core Set 2021|377|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.| +Sporeweb Weaver|Core Set 2021|378|R|{2}{G}|Creature - Spider|1|4|Reach, hexproof from blue$Whenever Sporeweb Weaver is dealt damage, you gain 1 life and create a 1/1 green Saproling creature token.| +Niambi, Esteemed Speaker|Core Set 2021|379|R|{W}{U}|Legendary Creature - Human Cleric|2|1|Flash$When Niambi, Esteemed Speaker enters the battlefield, you may return another target creature you control to its owner's hand. If you do, you gain life equal to that creature's converted mana cost.${1}{W}{U}, {T}, Discard a legendary card: Draw two cards.| +Radha, Heart of Keld|Core Set 2021|380|R|{1}{R}{G}|Legendary Creature - Elf Warrior|3|3|As long as it's your turn, Radha, Heart of Keld has first strike.$You may look at the top card of your library any time, and you may play lands from the top of your library.${4}{R}{G}: Radha gets +X/+X until end of turn, where X is the number of lands you control.| +Sanctum of All|Core Set 2021|381|R|{W}{U}{B}{R}{G}|Legendary Enchantment - Shrine|||At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it.$If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.| +Chromatic Orrery|Core Set 2021|382|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.| +Mazemind Tome|Core Set 2021|383|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.| +Sparkhunter Masticore|Core Set 2021|384|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.| +Animal Sanctuary|Core Set 2021|385|R||Land|||{T}: Add {C}.${2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake.| +Fabled Passage|Core Set 2021|386|R||Land|||{T}, Sacrifice Fabled Passage: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. Then if you control four or more lands, untap that land.| +Temple of Epiphany|Core Set 2021|387|R||Land|||Temple of Epiphany enters the battlefield tapped.$When Temple of Epiphany enters the battlefield, scry 1.${T}: Add {U} or {R}.| +Temple of Malady|Core Set 2021|388|R||Land|||Temple of Malady enters the battlefield tapped.$When Temple of Malady enters the battlefield, scry 1.${T}: Add {B} or {G}.| +Temple of Mystery|Core Set 2021|389|R||Land|||Temple of Mystery enters the battlefield tapped.$When Temple of Mystery enters the battlefield, scry 1.${T}: Add {G} or {U}.| +Temple of Silence|Core Set 2021|390|R||Land|||Temple of Silence enters the battlefield tapped.$When Temple of Silence enters the battlefield, scry 1.${T}: Add {W} or {B}.| +Temple of Triumph|Core Set 2021|391|R||Land|||Temple of Triumph enters the battlefield tapped.$When Temple of Triumph enters the battlefield, scry 1.${T}: Add {R} or {W}.| +Pack Leader|Core Set 2021|392|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.| +Selfless Savior|Core Set 2021|393|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Savior: Another target creature you control gains indestructible until end of turn.| +Frantic Inventory|Core Set 2021|394|C|{1}{U}|Instant|||Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard.| +Eliminate|Core Set 2021|395|U|{1}{B}|Instant|||Destroy target creature or planeswalker with converted mana cost 3 or less.| +Heartfire Immolator|Core Set 2021|396|U|{1}{R}|Creature - Human Wizard|2|2|Prowess${R}, Sacrifice Heartfire Immolator: It deals damage equal to its power to target creature or planeswalker.| +Llanowar Visionary|Core Set 2021|397|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.| +Blessed Sanctuary|Jumpstart|1|R|{3}{W}{W}|Enchantment|||Prevent all noncombat damage that would be dealt to you and creatures you control.$Whenever a nontoken creature enters the battlefield under your control, create a 2/2 white Unicorn creature token.| +Brightmare|Jumpstart|2|U|{2}{W}|Creature - Unicorn|2|3|When Brightmare enters the battlefield, tap up to one target creature. You gain life equal to that creature's power.| +Emiel the Blessed|Jumpstart|3|M|{2}{W}{W}|Legendary Creature - Unicorn|4|4|{3}: Exile another target creature you control, then return it to the battlefield under its owner's control.$Whenever another creature enters the battlefield under your control, you may pay {G/W}. If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead.| +Release the Dogs|Jumpstart|4|U|{3}{W}|Sorcery|||Create four 1/1 white Dog creature tokens.| +Steel-Plume Marshal|Jumpstart|5|R|{3}{W}{W}|Creature - Bird Soldier|3|3|Flying$Whenever Steel-Plume Marshal attacks, other attacking creatures you control with flying get +2/+2 until end of turn.| +Stone Haven Pilgrim|Jumpstart|6|U|{1}{W}|Creature - Kor Cleric|2|2|Whenever Stone Haven Pilgrim attacks, if you control an artifact or enchantment, Stone Haven Pilgrim gets +1/+1 and gains lifelink until end of turn.| +Supply Runners|Jumpstart|7|U|{4}{W}|Creature - Dog|2|2|When Supply Runners enters the battlefield, put a +1/+1 counter on each other creature you control.| +Trusty Retriever|Jumpstart|8|C|{3}{W}|Creature - Dog|2|3|When Trusty Retriever enters the battlefield, choose one —$• Put a +1/+1 counter on Trusty Retriever.$• Return target artifact or enchantment card from your graveyard to your hand.| +Archaeomender|Jumpstart|9|C|{2}{U}|Creature - Human Wizard|2|3|When Archaeomender enters the battlefield, return target artifact card from your graveyard to your hand.| +Bruvac the Grandiloquent|Jumpstart|10|M|{2}{U}|Legendary Creature - Human Advisor|1|4|If an opponent would mill one or more cards, they mill twice that many cards instead.| +Corsair Captain|Jumpstart|11|R|{2}{U}|Creature - Human Pirate|2|2|When Corsair Captain enters the battlefield, create a treasure token.$Other Pirates you control get +1/+1.| +Inniaz, the Gale Force|Jumpstart|12|R|{3}{U}{U}|Legendary Creature - Djinn|4|4|Flying${2}{W/U}: Attacking creatures with flying get +1/+1 until end of turn.$Whenever three or more creatures you control with flying attack, each player gains control of a nonland permanent of your choice controlled by the player to their right.| +Ormos, Archive Keeper|Jumpstart|13|R|{4}{U}{U}|Legendary Creature - Sphinx|5|5|Flying$If you would draw a card while your library has no cards in it, instead put five +1/+1 counters on Ormos, Archive Keeper.${1}{U}{U}, Discard three cards with different names: Draw five cards.| +Scholar of the Lost Trove|Jumpstart|14|R|{5}{U}{U}|Creature - Sphinx|5|5|Flying$When Scholar of the Lost Trove enters the battlefield, you may cast target instant, sorcery, or artifact card from your graveyard without paying its mana cost. If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead.| +Kels, Fight Fixer|Jumpstart|15|R|{2}{B}{B}|Legendary Creature - Azra Warlock|4|3|Menace$Whenever you sacrifice a creature, you may pay {U/B}. If you do, draw a card.${1}, Sacrifice a creature: Kels, Fight Fixer gains indestructible until end of turn.| +Nocturnal Feeder|Jumpstart|16|C|{2}{B}|Creature - Vampire Rogue|2|1|Flying$When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life.| +Tinybones, Trinket Thief|Jumpstart|17|M|{1}{B}|Legendary Creature - Skeleton Rogue|1|2|At the beginning of each end step, if an opponent discarded a card this turn, you draw a card and you lose 1 life.${4}{B}{B}: Each opponent with no cards in hand loses 10 life.| +Witch of the Moors|Jumpstart|18|R|{3}{B}{B}|Creature - Human Warlock|4|4|Deathtouch$At the beginning of your end step, if you gained life this turn, each opponent sacrifices a creature and you return up to one target creature card from your graveyard to your hand.| +Chained Brute|Jumpstart|19|U|{1}{R}|Creature - Devil|4|3|Chained Brute doesn't untap during your untap step.${1}, Sacrifice another creature: Untap Chained Brute. Activate this ability only during your turn.| +Immolating Gyre|Jumpstart|20|M|{4}{R}{R}|Sorcery|||Immolating Gyre deals X damage to each creature and planeswalker you don't control, where X is the number of instant and sorcery cards in your graveyard.| +Lightning Phoenix|Jumpstart|21|R|{2}{R}|Creature - Phoenix|2|2|Flying, haste$Lightning Phoenix can't block.$At the beginning of your end step, if an opponent was dealt 3 or more damage this turn, you may pay {R}. If you do, return Lightning Phoenix from your graveyard to the battlefield.| +Lightning Visionary|Jumpstart|22|C|{1}{R}|Creature - Minotaur Shaman|2|1|Prowess| +Living Lightning|Jumpstart|23|U|{3}{R}|Creature - Elemental Shaman|3|2|When Living Lightning dies, return target instant or sorcery card from your graveyard to your hand.| +Muxus, Goblin Grandee|Jumpstart|24|R|{4}{R}{R}|Legendary Creature - Goblin Noble|4|4|When Muxus, Goblin Grandee enters the battlefield, reveal the top six cards of your library. Put all Goblin creature cards with converted mana cost 5 or less from among them onto the battlefield and the rest on the bottom of your library in a random order.$Whenever Muxus attacks, it gets +1/+1 until end of turn for each other Goblin you control.| +Sethron, Hurloon General|Jumpstart|25|R|{3}{R}{R}|Legendary Creature - Minotaur Warrior|4|4|Whenever Sethron, Hurloon General or another nontoken Minotaur enters the battlefield under your control, create a 2/3 red Minotaur creature token.${2}{B/R}: Minotaurs you control get +1/+0 and gain menace and haste until end of turn.| +Spiteful Prankster|Jumpstart|26|U|{2}{R}|Creature - Devil|3|2|As long as it's your turn, Spiteful Prankster has first strike.$Whenever another creature dies, Spiteful Prankster deals 1 damage to target player or planeswalker.| +Zurzoth, Chaos Rider|Jumpstart|27|R|{2}{R}|Legendary Creature - Devil|2|3|Whenever an opponent draws their first card each turn, if it's not their turn, you create a 1/1 red Devil creature token with "When this creature dies, it deals 1 damage to any target."$Whenever one or more Devils you control attack one or more players, you and those players each draw a card, then discard a card at random.| +Allosaurus Shepherd|Jumpstart|28|M|{G}|Creature - Elf Shaman|1|1|Allosaurus Shepherd can't be countered.$Green spells you control can't be countered.${4}{G}{G}: Until end of turn, each Elf creature you control has base power and toughness 5/5 and becomes a Dinosaur in addition to its other creature types.| +Branching Evolution|Jumpstart|29|R|{2}{G}|Enchantment|||If one or more +1/+1 counters would be put a on a creature you control, twice that many +1/+1 counters are put on that creature instead.| +Neyith of the Dire Hunt|Jumpstart|30|R|{2}{G}{G}|Legendary Creature - Human Warrior|3|3|Whenever one or more creatures you control fight or become blocked, draw a card.$At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature's power until end of turn. That creature must be blocked this combat if able.| +Towering Titan|Jumpstart|31|M|{4}{G}{G}|Creature - Giant|0|0|Towering Titan enters the battlefield with X +1/+1 counters on it, where X is the total toughness of other creatures you control.$Sacrifice a creature with defender: All creatures gain trample until end of turn.| +Lightning-Core Excavator|Jumpstart|32|C|{1}|Artifact Creature - Golem|0|3|{5}, {T}, Sacrifice Lightning-Core Excavator: It deals 3 damage to any target.| +Thriving Bluff|Jumpstart|33|C||Land|||Thriving Bluff enters the battlefield tapped.$As Thriving Bluff enters the battlefield, choose a color other than red.${T}: Add {R} or one mana of the chosen color.| +Thriving Grove|Jumpstart|34|C||Land|||Thriving Grove enters the battlefield tapped.$As Thriving Grove enters the battlefield, choose a color other than green.${T}: Add {G} or one mana of the chosen color.| +Thriving Heath|Jumpstart|35|C||Land|||Thriving Heath enters the battlefield tapped.$As Thriving Heath enters the battlefield, choose a color other than white.${T}: Add {W} or one mana of the chosen color.| +Thriving Isle|Jumpstart|36|C||Land|||Thriving Isle enters the battlefield tapped.$As Thriving Isle enters the battlefield, choose a color other than blue.${T}: Add {U} or one mana of the chosen color.| +Thriving Moor|Jumpstart|37|C||Land|||Thriving Moor enters the battlefield tapped.$As Thriving Moor enters the battlefield, choose a color other than black.${T}: Add {B} or one mana of the chosen color.| +Plains|Jumpstart|38|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Jumpstart|39|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Jumpstart|40|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Jumpstart|41|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Jumpstart|42|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Jumpstart|43|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Jumpstart|44|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Jumpstart|45|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Jumpstart|46|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Jumpstart|47|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Jumpstart|48|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Jumpstart|49|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Jumpstart|50|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Jumpstart|51|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Jumpstart|52|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Jumpstart|53|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Jumpstart|54|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Jumpstart|55|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Jumpstart|56|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Jumpstart|57|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Jumpstart|58|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Jumpstart|59|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Jumpstart|60|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Jumpstart|61|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Jumpstart|62|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Jumpstart|63|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Jumpstart|64|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Jumpstart|65|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Jumpstart|66|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Jumpstart|67|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Jumpstart|68|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Jumpstart|69|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Jumpstart|70|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Jumpstart|71|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Jumpstart|72|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Jumpstart|73|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Jumpstart|74|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Jumpstart|75|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Jumpstart|76|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Jumpstart|77|C||Basic Land - Forest|||({T}: Add {G}.)| +Terramorphic Expanse|Jumpstart|78|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.| +Aegis of the Heavens|Jumpstart|79|U|{1}{W}|Instant|||Target creature gets +1/+7 until end of turn.| +Aerial Assault|Jumpstart|80|C|{2}{W}|Sorcery|||Destroy target tapped creature. You gain 1 life for each creature you control with flying.| +Affa Guard Hound|Jumpstart|81|U|{2}{W}|Creature - Dog|2|2|Flash$When Affa Guard Hound enters the battlefield, target creature gets +0/+3 until end of turn.| +Ajani's Chosen|Jumpstart|82|R|{2}{W}{W}|Creature - Cat Soldier|3|3|Whenever an enchantment enters the battlefield under your control, create a 2/2 white Cat creature token. If that enchantment is an Aura, you may attach it to the token.| +Alabaster Mage|Jumpstart|83|U|{1}{W}|Creature - Human Wizard|2|1|{1}{W}: Target creature you control gains lifelink until end of turn.| +Angel of Mercy|Jumpstart|84|C|{4}{W}|Creature - Angel|3|3|Flying$When Angel of Mercy enters the battlefield, you gain 3 life.| +Angel of the Dire Hour|Jumpstart|85|R|{5}{W}{W}|Creature - Angel|5|4|Flash$Flying$When Angel of the Dire Hour enters the battlefield, if you cast it from your hand, exile all attacking creatures.| +Angelic Arbiter|Jumpstart|86|R|{5}{W}{W}|Creature - Angel|5|6|Flying$Each opponent who cast a spell this turn can't attack with creatures.$Each opponent who attacked with a creature this turn can't cast spells.| +Angelic Edict|Jumpstart|87|C|{4}{W}|Sorcery|||Exile target creature or enchantment.| +Angelic Page|Jumpstart|88|C|{1}{W}|Creature - Angel Spirit|1|1|Flying${T}: Target attacking or blocking creature gets +1/+1 until end of turn.| +Archon of Justice|Jumpstart|89|R|{3}{W}{W}|Creature - Archon|4|4|Flying$When Archon of Justice dies, exile target permanent.| +Archon of Redemption|Jumpstart|90|R|{3}{W}{W}|Creature - Archon|3|4|Flying$Whenever Archon of Redemption or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power.| +Battlefield Promotion|Jumpstart|91|C|{1}{W}|Instant|||Put a +1/+1 counter on target creature. That creature gains first strike until end of turn. You gain 2 life.| +Blessed Spirits|Jumpstart|92|U|{2}{W}|Creature - Spirit|2|2|Flying$Whenever you cast an enchantment spell, put a +1/+1 counter on Blessed Spirits.| +Bulwark Giant|Jumpstart|93|C|{5}{W}|Creature - Giant Soldier|3|6|When Bulwark Giant enters the battlefield, you gain 5 life.| +Cathar's Companion|Jumpstart|94|C|{2}{W}|Creature - Dog|3|1|Whenever you cast a noncreature spell, Cathar's Companion gains indestructible until end of turn.| +Cathars' Crusade|Jumpstart|95|R|{3}{W}{W}|Enchantment|||Whenever a creature enters the battlefield under your control, put a +1/+1 counter on each creature you control.| +Celestial Mantle|Jumpstart|96|R|{3}{W}{W}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3.$Whenever enchanted creature deals combat damage to a player, double its controller's life total.| +Cloudshift|Jumpstart|97|C|{W}|Instant|||Exile target creature you control, then return that card to the battlefield under your control.| +Cradle of Vitality|Jumpstart|98|R|{3}{W}|Enchantment|||Whenever you gain life, you may pay {1}{W}. If you do, put a +1/+1 counter on target creature for each 1 life you gained.| +Dauntless Onslaught|Jumpstart|99|U|{2}{W}|Instant|||Up to two target creatures each get +2/+2 until end of turn.| +Divine Arrow|Jumpstart|100|C|{1}{W}|Instant|||Divine Arrow deals 4 damage to target attacking or blocking creature.| +Duelist's Heritage|Jumpstart|101|R|{2}{W}|Enchantment|||Whenever one or more creatures attack, you may have target attacking creature gain double strike until end of turn.| +Emancipation Angel|Jumpstart|102|U|{1}{W}{W}|Creature - Angel|3|3|Flying$When Emancipation Angel enters the battlefield, return a permanent you control to its owner's hand.| +Face of Divinity|Jumpstart|103|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2.$As long as another Aura is attached to enchanted creature, it has first strike and lifelink.| +Forced Worship|Jumpstart|104|C|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature can't attack.${2}{W}: Return Forced Worship to its owner's hand.| +Fortify|Jumpstart|105|C|{2}{W}|Instant|||Choose one —$• Creatures you control get +2/+0 until end of turn.$• Creatures you control get +0/+2 until end of turn.| +Gird for Battle|Jumpstart|106|U|{W}|Sorcery|||Put a +1/+1 counter on each of up to two target creatures.| +Healer's Hawk|Jumpstart|107|C|{W}|Creature - Bird|1|1|Flying, lifelink| +High Sentinels of Arashin|Jumpstart|108|R|{3}{W}|Creature - Bird Soldier|3|4|Flying$High Sentinels of Arashin gets +1/+1 for each other creature you control with a +1/+1 counter on it.${3}{W}: Put a +1/+1 counter on target creature.| +Indomitable Will|Jumpstart|109|C|{1}{W}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature gets +1/+2.| +Inspired Charge|Jumpstart|110|C|{2}{W}{W}|Instant|||Creatures you control get +2/+1 until end of turn.| +Inspiring Captain|Jumpstart|111|C|{3}{W}|Creature - Human Knight|3|3|When Inspiring Captain enters the battlefield, creatures you control get +1/+1 until end of turn.| +Inspiring Unicorn|Jumpstart|112|U|{2}{W}{W}|Creature - Unicorn|2|2|Whenever Inspiring Unicorn attacks, creatures you control get +1/+1 until end of turn.| +Isamaru, Hound of Konda|Jumpstart|113|R|{W}|Legendary Creature - Dog|2|2|| +Knight of the Tusk|Jumpstart|114|C|{4}{W}{W}|Creature - Human Knight|3|7|Vigilance| +Knightly Valor|Jumpstart|115|C|{4}{W}|Enchantment - Aura|||Enchant creature$When Knightly Valor enters the battlefield, create a 2/2 white Knight creature token with vigilance.$Enchanted creature gets +2/+2 and has vigilance.| +Kor Spiritdancer|Jumpstart|116|R|{1}{W}|Creature - Kor Wizard|0|2|Kor Spiritdancer gets +2/+2 for each Aura attached to it.$Whenever you cast an Aura spell, you may draw a card.| +Lena, Selfless Champion|Jumpstart|117|R|{4}{W}{W}|Legendary Creature - Human Knight|3|3|When Lena, Selfless Champion enters the battlefield, create a 1/1 white Soldier creature token for each nontoken creature you control.$Sacrifice Lena: Creatures you control with power less than Lena's power gain indestructible until end of turn.| +Lightwalker|Jumpstart|118|C|{1}{W}|Creature - Human Warrior|2|1|Lightwalker has flying as long as it has a +1/+1 counter on it.| +Linvala, Keeper of Silence|Jumpstart|119|M|{2}{W}{W}|Legendary Creature - Angel|3|4|Flying$Activated abilities of creatures your opponents control can't be activated.| +Long Road Home|Jumpstart|120|U|{1}{W}|Instant|||Exile target creature. At the beginning of the next end step, return that card to the battlefield under its owner's control with a +1/+1 counter on it.| +Mentor of the Meek|Jumpstart|121|R|{2}{W}|Creature - Human Soldier|2|2|Whenever another creature with power 2 or less enters the battlefield under your control, you may pay {1}. If you do, draw a card.| +Mesa Unicorn|Jumpstart|122|C|{1}{W}|Creature - Unicorn|2|2|Lifelink| +Mikaeus, the Lunarch|Jumpstart|123|M|{X}{W}|Legendary Creature - Human Cleric|0|0|Mikaeus, the Lunarch enters the battlefield with X +1/+1 counters on it.${T}: Put a +1/+1 counter on Mikaeus.${T}, Remove a +1/+1 counter from Mikaeus: Put a +1/+1 counter on each other creature you control.| +Moment of Heroism|Jumpstart|124|C|{1}{W}|Instant|||Target creature gets +2/+2 and gains lifelink until end of turn.| +Pacifism|Jumpstart|125|C|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature can't attack or block.| +Path of Bravery|Jumpstart|126|R|{2}{W}|Enchantment|||As long as your life total is greater than or equal to your starting life total, creatures you control get +1/+1.$Whenever one or more creatures you control attack, you gain life equal to the number of attacking creatures.| +Path to Exile|Jumpstart|127|U|{W}|Instant|||Exile target creature. Its controller may search their library for a basic land card, put that card onto the battlefield tapped, then shuffle their library.| +Patron of the Valiant|Jumpstart|128|U|{3}{W}{W}|Creature - Angel|4|4|Flying$When Patron of the Valiant enters the battlefield, put a +1/+1 counter on each creature you control with a +1/+1 counter on it.| +Raise the Alarm|Jumpstart|129|C|{1}{W}|Instant|||Create two 1/1 white Soldier creature tokens.| +Rhox Faithmender|Jumpstart|130|R|{3}{W}|Creature - Rhino Monk|1|5|Lifelink$If you would gain life, you gain twice that much life instead.| +Ronom Unicorn|Jumpstart|131|C|{1}{W}|Creature - Unicorn|2|2|Sacrifice Ronom Unicorn: Destroy target enchantment.| +Serra Angel|Jumpstart|132|U|{3}{W}{W}|Creature - Angel|4|4|Flying, vigilance| +Sky Tether|Jumpstart|133|U|{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has defender and loses flying.| +Take Heart|Jumpstart|134|C|{W}|Instant|||Target creature gets +2/+2 until end of turn. You gain 1 life for each attacking creature you control.| +Tandem Tactics|Jumpstart|135|C|{1}{W}|Instant|||Up to two target creatures each get +1/+2 until end of turn. You gain 2 life.| +Valorous Stance|Jumpstart|136|U|{1}{W}|Instant|||Choose one —$• Target creature gains indestructible until end of turn.$• Destroy target creature with toughness 4 or greater.| +Voice of the Provinces|Jumpstart|137|C|{4}{W}{W}|Creature - Angel|3|3|Flying$When Voice of the Provinces enters the battlefield, create a 1/1 white Human creature token.| +Aegis Turtle|Jumpstart|138|C|{U}|Creature - Turtle|0|5|| +Battleground Geist|Jumpstart|139|U|{4}{U}|Creature - Spirit|3|3|Flying$Other Spirit creatures you control get +1/+0.| +Befuddle|Jumpstart|140|C|{2}{U}|Instant|||Target creature gets -4/-0 until end of turn.$Draw a card.| +Belltower Sphinx|Jumpstart|141|U|{4}{U}|Creature - Sphinx|2|5|Flying$Whenever a source deals damage to Belltower Sphinx, that source's controller mills that many cards.| +Chart a Course|Jumpstart|142|U|{1}{U}|Sorcery|||Draw two cards. Then discard a card unless you attacked this turn.| +Cloudreader Sphinx|Jumpstart|143|C|{4}{U}|Creature - Sphinx|3|4|Flying$When Cloudreader Sphinx enters the battlefield, scry 2.| +Coastal Piracy|Jumpstart|144|U|{2}{U}{U}|Enchantment|||Whenever a creature you control deals combat damage to an opponent, you may draw a card.| +Crookclaw Transmuter|Jumpstart|145|C|{3}{U}|Creature - Bird Wizard|3|1|Flash$Flying$When Crookclaw Transmuter enters the battlefield, switch target creature's power and toughness until end of turn.| +Cryptic Serpent|Jumpstart|146|U|{5}{U}{U}|Creature - Serpent|6|5|This spell costs {1} less to cast for each instant and sorcery card in your graveyard.| +Curiosity|Jumpstart|147|U|{U}|Enchantment - Aura|||Enchant creature$Whenever enchanted creature deals damage to an opponent, you may draw a card.| +Curious Obsession|Jumpstart|148|U|{U}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and has "Whenever this creature deals combat damage to a player, you may draw a card."$At the beginning of your end step, if you didn't attack with a creature this turn, sacrifice Curious Obsession.| +Departed Deckhand|Jumpstart|149|U|{1}{U}|Creature - Spirit Pirate|2|2|When Departed Deckhand becomes the target of a spell, sacrifice it.$Departed Deckhand can't be blocked except by Spirits.${3}{U}: Another target creature you control can't be blocked this turn except by Spirits.| +Erratic Visionary|Jumpstart|150|C|{1}{U}|Creature - Human Wizard|1|3|{1}{U}, {T}: Draw a card, then discard a card.| +Essence Flux|Jumpstart|151|U|{U}|Instant|||Exile target creature you control, then return that card to the battlefield under its owner's control. If it's a Spirit, put a +1/+1 counter on it.| +Exclude|Jumpstart|152|U|{2}{U}|Instant|||Counter target creature spell.$Draw a card.| +Exclusion Mage|Jumpstart|153|U|{2}{U}|Creature - Human Wizard|2|2|When Exclusion Mage enters the battlefield, return target creature an opponent controls to its owner's hand.| +Kira, Great Glass-Spinner|Jumpstart|154|R|{1}{U}{U}|Legendary Creature - Spirit|2|2|Flying$Creatures you control have "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability."| +Kitesail Corsair|Jumpstart|155|C|{1}{U}|Creature - Human Pirate|2|1|Kitesail Corsair has flying as long as it's attacking.| +Leave in the Dust|Jumpstart|156|C|{3}{U}|Instant|||Return target nonland permanent to its owner's hand.$Draw a card.| +Murmuring Phantasm|Jumpstart|157|C|{1}{U}|Creature - Spirit|0|5|Defender| +Mystic Archaeologist|Jumpstart|158|R|{1}{U}|Creature - Human Wizard|2|1|{3}{U}{U}: Draw two cards.| +Narcolepsy|Jumpstart|159|C|{1}{U}|Enchantment - Aura|||Enchant creature$At the beginning of each upkeep, if enchanted creature is untapped, tap it.| +Nebelgast Herald|Jumpstart|160|U|{2}{U}|Creature - Spirit|2|1|Flash$Flying$Whenever Nebelgast Herald or another Spirit enters the battlefield under your control, tap target creature an opponent controls.| +Octoprophet|Jumpstart|161|C|{3}{U}|Creature - Octopus|3|3|When Octoprophet enters the battlefield, scry 2.| +Oneirophage|Jumpstart|162|U|{3}{U}|Creature - Squid Illusion|1|2|Flying$Whenever you draw a card, put a +1/+1 counter on Oneirophage.| +Peel from Reality|Jumpstart|163|C|{1}{U}|Instant|||Return target creature you control and target creature you don't control to their owners' hands.| +Prescient Chimera|Jumpstart|164|C|{3}{U}{U}|Creature - Chimera|3|4|Flying$Whenever you cast an instant or sorcery spell, scry 1.| +Prosperous Pirates|Jumpstart|165|C|{4}{U}|Creature - Human Pirate|3|4|When Prosperous Pirates enters the battlefield, create two Treasure tokens.| +Rattlechains|Jumpstart|166|R|{1}{U}|Creature - Spirit|2|1|Flash$Flying$When Rattlechains enters the battlefield, target Spirit gains hexproof until end of turn.$You may cast Spirit spells as though they had flash.| +Read the Runes|Jumpstart|167|R|{X}{U}|Instant|||Draw X cards. For each card drawn this way, discard a card unless you sacrifice a permanent.| +Reckless Scholar|Jumpstart|168|U|{2}{U}|Creature - Human Wizard|2|1|{T}: Target player draws a card, then discards a card.| +Rhystic Study|Jumpstart|169|R|{2}{U}|Enchantment|||Whenever an opponent casts a spell, you may draw a card unless that player pays {1}.| +Rishadan Airship|Jumpstart|170|C|{2}{U}|Creature - Human Pirate|3|1|Flying$Rishadan Airship can block only creatures with flying.| +Sage's Row Savant|Jumpstart|171|C|{1}{U}|Creature - Vedalken Wizard|2|1|When Sage's Row Savant enters the battlefield, scry 2.| +Sailor of Means|Jumpstart|172|C|{2}{U}|Creature - Human Pirate|1|4|When Sailor of Means enters the battlefield, create a Treasure token.| +Sea Gate Oracle|Jumpstart|173|C|{2}{U}|Creature - Human Wizard|1|3|When Sea Gate Oracle enters the battlefield, look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library.| +Selhoff Occultist|Jumpstart|174|C|{2}{U}|Creature - Human Rogue|2|3|Whenever Selhoff Occultist or another creature dies, target player mills a card.| +Serendib Efreet|Jumpstart|175|R|{2}{U}|Creature - Efreet|3|4|Flying$At the beginning of your upkeep, Serendib Efreet deals 1 damage to you.| +Sharding Sphinx|Jumpstart|176|R|{4}{U}{U}|Artifact Creature - Sphinx|4|4|Flying$Whenever an artifact creature you control deals combat damage to a player, you may create a 1/1 blue Thopter artifact creature token with flying.| +Sigiled Starfish|Jumpstart|177|U|{1}{U}|Creature - Starfish|0|3|{T}: Scry 1.| +Spectral Sailor|Jumpstart|178|U|{U}|Creature - Spirit Pirate|1|1|Flash$Flying${3}{U}: Draw a card.| +Storm Sculptor|Jumpstart|179|C|{3}{U}|Creature - Merfolk Wizard|3|2|Storm Sculptor can't be blocked.$When Storm Sculptor enters the battlefield, return a creature you control to its owner's hand.| +Sweep Away|Jumpstart|180|C|{2}{U}|Instant|||Return target creature to its owner's hand. If that creature is attacking, you may put it on top of its owner's library instead.| +Talrand, Sky Summoner|Jumpstart|181|R|{2}{U}{U}|Legendary Creature - Merfolk Wizard|2|2|Whenever you cast an instant or sorcery spell, create a 2/2 blue Drake creature token with flying.| +Talrand's Invocation|Jumpstart|182|U|{2}{U}{U}|Sorcery|||Create two 2/2 blue Drake creature tokens with flying.| +Thirst for Knowledge|Jumpstart|183|U|{2}{U}|Instant|||Draw three cards. Then discard two cards unless you discard an artifact card.| +Thought Collapse|Jumpstart|184|C|{1}{U}{U}|Instant|||Counter target spell. Its controller mills three cards.| +Thought Scour|Jumpstart|185|C|{U}|Instant|||Target player mills two cards.$Draw a card.| +Towering-Wave Mystic|Jumpstart|186|C|{1}{U}|Creature - Merfolk Wizard|2|1|Whenever Towering-Wave Mystic deals damage, target player mills that many cards.| +Vedalken Archmage|Jumpstart|187|R|{2}{U}{U}|Creature - Vedalken Wizard|0|2|Whenever you cast an artifact spell, draw a card.| +Vedalken Entrancer|Jumpstart|188|C|{3}{U}|Creature - Vedalken Wizard|1|4|{U}, {T}: Target player mills two cards.| +Voyage's End|Jumpstart|189|C|{1}{U}|Instant|||Return target creature to its owner's hand. Scry 1.| +Wall of Lost Thoughts|Jumpstart|190|U|{1}{U}|Creature - Wall|0|4|Defender$When Wall of Lost Thoughts enters the battlefield, target player mills four cards.| +Warden of Evos Isle|Jumpstart|191|U|{2}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.| +Waterknot|Jumpstart|192|C|{1}{U}{U}|Enchantment - Aura|||Enchant creature$When Waterknot enters the battlefield, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.| +Whelming Wave|Jumpstart|193|R|{2}{U}{U}|Sorcery|||Return all creatures to their owners' hands except for Krakens, Leviathans, Octopuses, and Serpents.| +Windreader Sphinx|Jumpstart|194|R|{5}{U}{U}|Creature - Sphinx|3|7|Flying$Whenever a creature with flying attacks, you may draw a card.| +Windstorm Drake|Jumpstart|195|U|{4}{U}|Creature - Drake|3|3|Flying$Other creatures you control with flying get +1/+0.| +Winged Words|Jumpstart|196|C|{2}{U}|Sorcery|||This spell costs {1} less to cast if you control a creature with flying.$Draw two cards.| +Wishful Merfolk|Jumpstart|197|C|{1}{U}|Creature - Merfolk|3|2|Defender${1}{U}: Wishful Merfolk loses defender and becomes a Human until end of turn.| +Wizard's Retort|Jumpstart|198|U|{1}{U}{U}|Instant|||This spell costs {1} less to cast if you control a Wizard.$Counter target spell.| +Agonizing Syphon|Jumpstart|199|C|{3}{B}|Sorcery|||Agonizing Syphon deals 3 damage to any target and you gain 3 life.| +Assassin's Strike|Jumpstart|200|U|{4}{B}{B}|Sorcery|||Destroy target creature. Its controller discards a card.| +Bake into a Pie|Jumpstart|201|C|{2}{B}{B}|Instant|||Destroy target creature. Create a Food token.| +Barter in Blood|Jumpstart|202|U|{2}{B}{B}|Sorcery|||Each player sacrifices two creatures.| +Black Cat|Jumpstart|203|C|{1}{B}|Creature - Zombie Cat|1|1|When Black Cat dies, target opponent discards a card at random.| +Black Market|Jumpstart|204|R|{3}{B}{B}|Enchantment|||Whenever a creature dies, put a charge counter on Black Market.$At the beginning of your precombat main phase, add {B} for each charge counter on Black Market.| +Blighted Bat|Jumpstart|205|C|{2}{B}|Creature - Zombie Bat|2|1|Flying${1}: Blighted Bat gains haste until end of turn.| +Blood Artist|Jumpstart|206|U|{1}{B}|Creature - Vampire|0|1|Whenever Blood Artist or another creature dies, target player loses 1 life and you gain 1 life.| +Blood Divination|Jumpstart|207|U|{3}{B}|Sorcery|||As an additional cost to cast this spell, sacrifice a creature.$Draw three cards.| +Blood Host|Jumpstart|208|U|{3}{B}{B}|Creature - Vampire|3|3|{1}{B}, Sacrifice another creature: Put a +1/+1 counter on Blood Host and you gain 2 life.| +Bloodbond Vampire|Jumpstart|209|U|{2}{B}{B}|Creature - Vampire Shaman Ally|3|3|Whenever you gain life, put a +1/+1 counter on Bloodbond Vampire.| +Bloodhunter Bat|Jumpstart|210|C|{3}{B}|Creature - Bat|2|2|Flying$When Bloodhunter Bat enters the battlefield, target player loses 2 life and you gain 2 life.| +Bogbrew Witch|Jumpstart|211|R|{3}{B}|Creature - Human Wizard|1|3|{2}, {T}: Search your library for a card named Festering Newt or Bubbling Cauldron, put it onto the battlefield tapped, then shuffle your library.| +Bone Picker|Jumpstart|212|U|{3}{B}|Creature - Bird|3|2|This spell costs {3} less to cast if a creature died this turn.$Flying, deathtouch| +Bone Splinters|Jumpstart|213|C|{B}|Sorcery|||As an additional cost to cast this spell, sacrifice a creature.$Destroy target creature.| +Burglar Rat|Jumpstart|214|C|{1}{B}|Creature - Rat|1|1|When Burglar Rat enters the battlefield, each opponent discards a card.| +Cadaver Imp|Jumpstart|215|C|{1}{B}{B}|Creature - Imp|1|1|Flying$When Cadaver Imp enters the battlefield, you may return target creature card from your graveyard to your hand.| +Cauldron Familiar|Jumpstart|216|C|{B}|Creature - Cat|1|1|When Cauldron Familiar enters the battlefield, each opponent loses 1 life and you gain 1 life.$Sacrifice a Food: Return Cauldron Familiar from your graveyard to the battlefield.| +Cemetery Recruitment|Jumpstart|217|C|{1}{B}|Sorcery|||Return target creature card from your graveyard to your hand. If it's a Zombie card, draw a card.| +Child of Night|Jumpstart|218|C|{1}{B}|Creature - Vampire|2|1|Lifelink| +Corpse Hauler|Jumpstart|219|C|{1}{B}|Creature - Human Rogue|2|1|{2}{B}, Sacrifice Corpse Hauler: Return another target creature card from your graveyard to your hand.| +Corpse Traders|Jumpstart|220|U|{3}{B}|Creature - Human Rogue|3|3|{2}{B}, Sacrifice a creature: Target opponent reveals their hand. You choose a card from it. That player discards that card. Activate this ability only any time you could cast a sorcery.| +Crow of Dark Tidings|Jumpstart|221|C|{2}{B}|Creature - Zombie Bird|2|1|Flying$When Crow of Dark Tidings enters the battlefield or dies, mill two cards.| +Death's Approach|Jumpstart|222|C|{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets -X/-X, where X is the number of creature cards in its controller's graveyard.| +Douse in Gloom|Jumpstart|223|C|{2}{B}|Instant|||Douse in Gloom deals 2 damage to target creature and you gain 2 life.| +Drainpipe Vermin|Jumpstart|224|C|{B}|Creature - Rat|1|1|When Drainpipe Vermin dies, you may pay {B}. If you do, target player discards a card.| +Drana, Liberator of Malakir|Jumpstart|225|M|{1}{B}{B}|Legendary Creature - Vampire Ally|2|3|Flying, first strike$Whenever Drana, Liberator of Malakir deals combat damage to a player, put a +1/+1 counter on each attacking creature you control.| +Dutiful Attendant|Jumpstart|226|C|{2}{B}|Creature - Human Warrior|1|2|When Dutiful Attendant dies, return another target creature card from your graveyard to your hand.| +Entomber Exarch|Jumpstart|227|U|{2}{B}{B}|Creature - Cleric|2|2|When Entomber Exarch enters the battlefield, choose one —$• Return target creature card from your graveyard to your hand.$• Target opponent reveals their hand. You choose a noncreature card from it. That player discards that card.| +Eternal Taskmaster|Jumpstart|228|U|{1}{B}|Creature - Zombie|2|3|Eternal Taskmaster enters the battlefield tapped.$Whenever Eternal Taskmaster attacks, you may pay {2}{B}. If you do, return target creature card from your graveyard to your hand.| +Eternal Thirst|Jumpstart|229|C|{1}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature has lifelink and "Whenever a creature an opponent controls dies, put a +1/+1 counter on this creature."| +Exhume|Jumpstart|230|U|{1}{B}|Sorcery|||Each player puts a creature card from their graveyard onto the battlefield.| +Exquisite Blood|Jumpstart|231|R|{4}{B}|Enchantment|||Whenever an opponent loses life, you gain that much life.| +Falkenrath Noble|Jumpstart|232|U|{3}{B}|Creature - Vampire Noble|2|2|Flying$Whenever Falkenrath Noble or another creature dies, target player loses 1 life and you gain 1 life.| +Fell Specter|Jumpstart|233|U|{3}{B}|Creature - Specter|1|3|Flying$When Fell Specter enters the battlefield, target opponent discards a card.$Whenever an opponent discards a card, that player loses 2 life.| +Festering Newt|Jumpstart|234|C|{B}|Creature - Salamander|1|1|When Festering Newt dies, target creature an opponent controls gets -1/-1 until end of turn. That creature gets -4/-4 instead if you control a creature named Bogbrew Witch.| +Funeral Rites|Jumpstart|235|C|{2}{B}|Sorcery|||You draw two cards, lose 2 life, then mill two cards.| +Ghoulcaller Gisa|Jumpstart|236|M|{3}{B}{B}|Legendary Creature - Human Wizard|3|4|{B}, {T}, Sacrifice another creature: Create X 2/2 black Zombie creature tokens, where X is the sacrificed creature's power.| +Ghoulcaller's Accomplice|Jumpstart|237|C|{1}{B}|Creature - Human Rogue|2|2|{3}{B}, Exile Ghoulcaller's Accomplice from your graveyard: Create a 2/2 black Zombie creature token. Activate this ability only any time you could cast a sorcery.| +Ghoulraiser|Jumpstart|238|C|{1}{B}{B}|Creature - Zombie|2|2|When Ghoulraiser enters the battlefield, return a Zombie card at random from your graveyard to your hand.| +Gifted Aetherborn|Jumpstart|239|U|{B}{B}|Creature - Aetherborn Vampire|2|3|Deathtouch, lifelink| +Gonti, Lord of Luxury|Jumpstart|240|R|{2}{B}{B}|Legendary Creature - Aetherborn Rogue|2|3|Deathtouch$When Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, then put the rest on the bottom of that library in a random order. You may look at and cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any type to cast that spell.| +Gravewaker|Jumpstart|241|R|{4}{B}{B}|Creature - Bird Spirit|5|5|Flying${5}{B}{B}: Return target creature card from your graveyard to the battlefield tapped.| +Gristle Grinner|Jumpstart|242|U|{4}{B}|Creature - Zombie|3|3|Whenever a creature dies, Gristle Grinner gets +2/+2 until end of turn.| +Harvester of Souls|Jumpstart|243|R|{4}{B}{B}|Creature - Demon|5|5|Deathtouch$Whenever another nontoken creature dies, you may draw a card.| +Innocent Blood|Jumpstart|244|C|{B}|Sorcery|||Each player sacrifices a creature.| +Kalastria Nightwatch|Jumpstart|245|C|{4}{B}|Creature - Vampire Warrior Ally|4|5|Whenever you gain life, Kalastria Nightwatch gains flying until end of turn.| +Languish|Jumpstart|246|R|{2}{B}{B}|Sorcery|||All creatures get -4/-4 until end of turn.| +Last Gasp|Jumpstart|247|C|{1}{B}|Instant|||Target creature gets -3/-3 until end of turn.| +Launch Party|Jumpstart|248|C|{3}{B}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Destroy target creature. Its controller loses 2 life.| +Lawless Broker|Jumpstart|249|C|{2}{B}|Creature - Aetherborn Rogue|3|2|When Lawless Broker dies, put a +1/+1 counter on target creature you control.| +Liliana's Elite|Jumpstart|250|U|{2}{B}|Creature - Zombie|1|1|Liliana's Elite gets +1/+1 for each creature card in your graveyard.| +Liliana's Reaver|Jumpstart|251|R|{2}{B}{B}|Creature - Zombie|4|3|Deathtouch$Whenever Liliana's Reaver deals combat damage to a player, that player discards a card and you create a tapped 2/2 black Zombie creature token.| +Macabre Waltz|Jumpstart|252|C|{1}{B}|Sorcery|||Return up to two target creature cards from your graveyard to your hand, then discard a card.| +Malakir Familiar|Jumpstart|253|U|{2}{B}|Creature - Bat|2|1|Flying, deathtouch$Whenever you gain life, Malakir Familiar gets +1/+1 until end of turn.| +Mark of the Vampire|Jumpstart|254|C|{3}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and has lifelink.| +Mausoleum Turnkey|Jumpstart|255|U|{3}{B}|Creature - Ogre Rogue|3|2|When Mausoleum Turnkey enters the battlefield, return target creature card of an opponent's choice from your graveyard to your hand.| +Miasmic Mummy|Jumpstart|256|C|{1}{B}|Creature - Zombie Jackal|2|2|When Miasmic Mummy enters the battlefield, each player discards a card.| +Mire Triton|Jumpstart|257|U|{1}{B}|Creature - Zombie Merfolk|2|1|Deathtouch$When Mire Triton enters the battlefield, mill two cards and you gain 2 life.| +Nightshade Stinger|Jumpstart|258|C|{B}|Creature - Faerie Rogue|1|1|Flying$Nightshade Stinger can't block.| +Nyxathid|Jumpstart|259|R|{1}{B}{B}|Creature - Elemental|7|7|As Nyxathid enters the battlefield, choose an opponent.$Nyxathid gets -1/-1 for each card in the chosen player's hand.| +Ogre Slumlord|Jumpstart|260|R|{3}{B}{B}|Creature - Ogre Rogue|3|3|Whenever another nontoken creature dies, you may create a 1/1 black Rat creature token.$Rats you control have deathtouch.| +Oona's Blackguard|Jumpstart|261|U|{1}{B}|Creature - Faerie Rogue|1|1|Flying$Each other Rogue creature you control enters the battlefield with an additional +1/+1 counter on it.$Whenever a creature you control with a +1/+1 counter on it deals combat damage to a player, that player discards a card.| +Parasitic Implant|Jumpstart|262|C|{3}{B}|Enchantment - Aura|||Enchant creature$At the beginning of your upkeep, enchanted creature's controller sacrifices it and you create a 1/1 colorless Myr artifact creature token.| +Phyrexian Broodlings|Jumpstart|263|C|{1}{B}{B}|Creature - Minion|2|2|{1}, Sacrifice a creature: Put a +1/+1 counter on Phyrexian Broodlings.| +Phyrexian Debaser|Jumpstart|264|C|{3}{B}|Creature - Carrier|2|2|Flying${T}, Sacrifice Phyrexian Debaser: Target creature gets -2/-2 until end of turn.| +Phyrexian Gargantua|Jumpstart|265|U|{4}{B}{B}|Creature - Horror|4|4|When Phyrexian Gargantua enters the battlefield, you draw two cards and you lose 2 life.| +Phyrexian Rager|Jumpstart|266|C|{2}{B}|Creature - Horror|2|2|When Phyrexian Rager enters the battlefield, you draw a card and you lose 1 life.| +Phyrexian Reclamation|Jumpstart|267|U|{B}|Enchantment|||{1}{B}, Pay 2 life: Return target creature card from your graveyard to your hand.| +Plagued Rusalka|Jumpstart|268|C|{B}|Creature - Spirit|1|1|{B}, Sacrifice a creature: Target creature gets -1/-1 until end of turn.| +Ravenous Chupacabra|Jumpstart|269|U|{2}{B}{B}|Creature - Beast Horror|2|2|When Ravenous Chupacabra enters the battlefield, destroy target creature an opponent controls.| +Reanimate|Jumpstart|270|R|{B}|Sorcery|||Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost.| +Rise of the Dark Realms|Jumpstart|271|M|{7}{B}{B}|Sorcery|||Put all creature cards from all graveyards onto the battlefield under your control.| +Sangromancer|Jumpstart|272|R|{2}{B}{B}|Creature - Vampire Shaman|3|3|Flying$Whenever a creature an opponent controls dies, you may gain 3 life.$Whenever an opponent discards a card, you may gain 3 life.| +Sanitarium Skeleton|Jumpstart|273|C|{B}|Creature - Skeleton|1|2|{2}{B}: Return Sanitarium Skeleton from your graveyard to your hand.| +Scourge of Nel Toth|Jumpstart|274|R|{5}{B}{B}|Creature - Zombie Dragon|6|6|Flying$You may cast Scourge of Nel Toth from your graveyard by paying {B}{B} and sacrificing two creatures rather than paying its mana cost.| +Sengir Vampire|Jumpstart|275|U|{3}{B}{B}|Creature - Vampire|4|4|Flying$Whenever a creature dealt damage by Sengir Vampire this turn dies, put a +1/+1 counter on Sengir Vampire.| +Settle the Score|Jumpstart|276|U|{2}{B}{B}|Sorcery|||Exile target creature. Put two loyalty counters on a planeswalker you control.| +Shambling Goblin|Jumpstart|277|C|{B}|Creature - Zombie Goblin|1|1|When Shambling Goblin dies, target creature an opponent controls gets -1/-1 until end of turn.| +Sheoldred, Whispering One|Jumpstart|278|M|{5}{B}{B}|Legendary Creature - Praetor|6|6|Swampwalk$At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.$At the beginning of each opponent's upkeep, that player sacrifices a creature.| +Slate Street Ruffian|Jumpstart|279|C|{2}{B}|Creature - Human Warrior|2|2|Whenever Slate Street Ruffian becomes blocked, defending player discards a card.| +Soul Salvage|Jumpstart|280|C|{2}{B}|Sorcery|||Return up to two target creature cards from your graveyard to your hand.| +Stab Wound|Jumpstart|281|U|{2}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets -2/-2.$At the beginning of the upkeep of enchanted creature's controller, that player loses 2 life.| +Swarm of Bloodflies|Jumpstart|282|U|{4}{B}|Creature - Insect|0|0|Flying$Swarm of Bloodflies enters the battlefield with two +1/+1 counters on it.$Whenever another creature dies, put a +1/+1 counter on Swarm of Bloodflies.| +Tempting Witch|Jumpstart|283|U|{2}{B}|Creature - Human Warlock|1|3|When Tempting Witch enters the battlefield, create a Food token.${2}, {T}, Sacrifice a Food: Target player loses 3 life.| +Tithebearer Giant|Jumpstart|284|C|{5}{B}|Creature - Giant Warrior|4|5|When Tithebearer Giant enters the battlefield, you draw a card and you lose 1 life.| +Vampire Neonate|Jumpstart|285|C|{B}|Creature - Vampire|0|3|{2}, {T}: Each opponent loses 1 life and you gain 1 life.| +Wailing Ghoul|Jumpstart|286|C|{1}{B}|Creature - Zombie|1|3|When Wailing Ghoul enters the battlefield, mill two cards.| +Wight of Precinct Six|Jumpstart|287|C|{1}{B}|Creature - Zombie|1|1|Wight of Precinct Six gets +1/+1 for each creature card in your opponents' graveyards.| +Zombie Infestation|Jumpstart|288|U|{1}{B}|Enchantment|||Discard two cards: Create a 2/2 black Zombie creature token.| +Act of Treason|Jumpstart|289|C|{2}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.| +Ashmouth Hound|Jumpstart|290|C|{1}{R}|Creature - Elemental Dog|2|1|Whenever Ashmouth Hound blocks or becomes blocked by a creature, Ashmouth Hound deals 1 damage to that creature.| +Ball Lightning|Jumpstart|291|R|{R}{R}{R}|Creature - Elemental|6|1|Trample$Haste$At the beginning of the end step, sacrifice Ball Lightning.| +Barrage of Expendables|Jumpstart|292|U|{R}|Enchantment|||{R}, Sacrifice a creature: Barrage of Expendables deals 1 damage to any target.| +Bathe in Dragonfire|Jumpstart|293|C|{2}{R}|Sorcery|||Bathe in Dragonfire deals 4 damage to target creature.| +Beetleback Chief|Jumpstart|294|U|{2}{R}{R}|Creature - Goblin Warrior|2|2|When Beetleback Chief enters the battlefield, create two 1/1 red Goblin creature tokens.| +Blindblast|Jumpstart|295|C|{2}{R}|Instant|||Blindblast deals 1 damage to target creature. That creature can't block this turn.$Draw a card.| +Bloodrage Brawler|Jumpstart|296|U|{1}{R}|Creature - Minotaur Warrior|4|3|When Bloodrage Brawler enters the battlefield, discard a card.| +Bloodrock Cyclops|Jumpstart|297|C|{2}{R}|Creature - Cyclops|3|3|Bloodrock Cyclops attacks each combat if able.| +Bloodshot Trainee|Jumpstart|298|U|{3}{R}|Creature - Goblin Warrior|2|3|{T}: Bloodshot Trainee deals 4 damage to target creature. Activate this ability only if Bloodshot Trainee's power is 4 or greater.| +Boggart Brute|Jumpstart|299|C|{2}{R}|Creature - Goblin Warrior|3|2|Menace| +Borderland Marauder|Jumpstart|300|C|{1}{R}|Creature - Human Warrior|1|2|Whenever Borderland Marauder attacks, it gets +2/+0 until end of turn.| +Borderland Minotaur|Jumpstart|301|C|{2}{R}{R}|Creature - Minotaur Warrior|4|3|| +Chain Lightning|Jumpstart|302|U|{R}|Sorcery|||Chain Lightning deals 3 damage to any target. Then that player or that permanent's controller may pay {R}{R}. If the player does, they may copy this spell and may choose a new target for that copy.| +Charmbreaker Devils|Jumpstart|303|R|{5}{R}|Creature - Devil|4|4|At the beginning of your upkeep, return an instant or sorcery card at random from your graveyard to your hand.$Whenever you cast an instant or sorcery spell, Charmbreaker Devils gets +4/+0 until end of turn.| +Cinder Elemental|Jumpstart|304|U|{3}{R}|Creature - Elemental|2|2|{X}{R}, {T}, Sacrifice Cinder Elemental: It deals X damage to any target.| +Collateral Damage|Jumpstart|305|C|{R}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Collateral Damage deals 3 damage to any target.| +Dance with Devils|Jumpstart|306|U|{3}{R}|Instant|||Create two 1/1 red Devil creature tokens. They have "When this creature dies, it deals 1 damage to any target."| +Doublecast|Jumpstart|307|U|{R}{R}|Sorcery|||When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.| +Draconic Roar|Jumpstart|308|U|{1}{R}|Instant|||As an additional cost to cast this spell, you may reveal a Dragon card from your hand.$Draconic Roar deals 3 damage to target creature. If you revealed a Dragon card or controlled a Dragon as you cast this spell, Draconic Roar deals 3 damage to that creature's controller.| +Dragon Fodder|Jumpstart|309|C|{1}{R}|Sorcery|||Create two 1/1 red Goblin creature tokens.| +Dragon Hatchling|Jumpstart|310|C|{1}{R}|Creature - Dragon|0|1|Flying${R}: Dragon Hatchling gets +1/+0 until end of turn.| +Dragonlord's Servant|Jumpstart|311|U|{1}{R}|Creature - Goblin Shaman|1|3|Dragon spells you cast cost {1} less to cast.| +Dragonspeaker Shaman|Jumpstart|312|U|{1}{R}{R}|Creature - Human Barbarian Shaman|2|2|Dragon spells you cast cost {2} less to cast.| +Dualcaster Mage|Jumpstart|313|R|{1}{R}{R}|Creature - Human Wizard|2|2|Flash$When Dualcaster Mage enters the battlefield, copy target instant or sorcery spell. You may choose new targets for the copy.| +Etali, Primal Storm|Jumpstart|314|R|{4}{R}{R}|Legendary Creature - Elder Dinosaur|6|6|Whenever Etali, Primal Storm attacks, exile the top card of each player's library, then you may cast any number of spells from among those cards without paying their mana costs.| +Fanatical Firebrand|Jumpstart|315|C|{R}|Creature - Goblin Pirate|1|1|Haste${T}, Sacrifice Fanatical Firebrand: It deals 1 damage to any target.| +Flame Lash|Jumpstart|316|C|{3}{R}|Instant|||Flame Lash deals 4 damage to any target.| +Flames of the Firebrand|Jumpstart|317|U|{2}{R}|Sorcery|||Flames of the Firebrand deals 3 damage divided as you choose among one, two, or three targets.| +Flames of the Raze-Boar|Jumpstart|318|U|{5}{R}|Instant|||Flames of the Raze-Boar deals 4 damage to target creature an opponent controls. Then Flames of the Raze-Boar deals 2 damage to each other creature that player controls if you control a creature with power 4 or greater.| +Flametongue Kavu|Jumpstart|319|U|{3}{R}|Creature - Kavu|4|2|When Flametongue Kavu enters the battlefield, it deals 4 damage to target creature.| +Fling|Jumpstart|320|C|{1}{R}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Fling deals damage equal to the sacrificed creature's power to any target.| +Flurry of Horns|Jumpstart|321|C|{4}{R}|Sorcery|||Create two 2/3 red Minotaur creature tokens with haste.| +Forge Devil|Jumpstart|322|C|{R}|Creature - Devil|1|1|When Forge Devil enters the battlefield, it deals 1 damage to target creature and 1 damage to you.| +Furnace Whelp|Jumpstart|323|U|{2}{R}{R}|Creature - Dragon|2|2|Flying${R}: Furnace Whelp gets +1/+0 until end of turn.| +Goblin Chieftain|Jumpstart|324|R|{1}{R}{R}|Creature - Goblin|2|2|Haste$Other Goblin creatures you control get +1/+1 and have haste.| +Goblin Commando|Jumpstart|325|U|{4}{R}|Creature - Goblin|2|2|When Goblin Commando enters the battlefield, it deals 2 damage to target creature.| +Goblin Goon|Jumpstart|326|R|{3}{R}|Creature - Goblin Mutant|6|6|Goblin Goon can't attack unless you control more creatures than defending player.$Goblin Goon can't block unless you control more creatures than attacking player.| +Goblin Instigator|Jumpstart|327|C|{1}{R}|Creature - Goblin Rogue|1|1|When Goblin Instigator enters the battlefield, create a 1/1 red Goblin creature token.| +Goblin Lore|Jumpstart|328|U|{1}{R}|Sorcery|||Draw four cards, then discard three cards at random.| +Goblin Rally|Jumpstart|329|U|{3}{R}{R}|Sorcery|||Create four 1/1 red Goblin creature tokens.| +Goblin Shortcutter|Jumpstart|330|C|{1}{R}|Creature - Goblin Scout|2|1|When Goblin Shortcutter enters the battlefield, target creature can't block this turn.| +Grim Lavamancer|Jumpstart|331|R|{R}|Creature - Human Wizard|1|1|{R}, {T}, Exile two cards from your graveyard: Grim Lavamancer deals 2 damage to any target.| +Hamletback Goliath|Jumpstart|332|R|{6}{R}|Creature - Giant Warrior|6|6|Whenever another creature enters the battlefield, you may put X +1/+1 counters on Hamletback Goliath, where X is that creature's power.| +Heartfire|Jumpstart|333|C|{1}{R}|Instant|||As an additional cost to cast this spell, sacrifice a creature or planeswalker.$Heartfire deals 4 damage to any target.| +Hellrider|Jumpstart|334|R|{2}{R}{R}|Creature - Devil|3|3|Haste$Whenever a creature you control attacks, Hellrider deals 1 damage to the player or planeswalker it's attacking.| +Homing Lightning|Jumpstart|335|U|{2}{R}{R}|Instant|||Homing Lightning deals 4 damage to target creature and each other creature with the same name as that creature.| +Hungry Flames|Jumpstart|336|C|{2}{R}|Instant|||Hungry Flames deals 3 damage to target creature and 2 damage to target player or planeswalker.| +Inferno Hellion|Jumpstart|337|U|{3}{R}|Creature - Hellion|7|3|Trample$At the beginning of each end step, if Inferno Hellion attacked or blocked this turn, its owner shuffles it into their library.| +Kiln Fiend|Jumpstart|338|C|{1}{R}|Creature - Elemental Beast|1|2|Whenever you cast an instant or sorcery spell, Kiln Fiend gets +3/+0 until end of turn.| +Krenko, Mob Boss|Jumpstart|339|R|{2}{R}{R}|Legendary Creature - Goblin Warrior|3|3|{T}: Create X 1/1 red Goblin creature tokens, where X is the number of Goblins you control.| +Lathliss, Dragon Queen|Jumpstart|340|R|{4}{R}{R}|Legendary Creature - Dragon|6|6|Flying$Whenever another nontoken Dragon enters the battlefield under your control, create a 5/5 red Dragon creature token with flying.${1}{R}: Dragons you control get +1/+0 until end of turn.| +Lightning Axe|Jumpstart|341|U|{R}|Instant|||As an additional cost to cast this spell, discard a card or pay {5}.$Lightning Axe deals 5 damage to target creature.| +Lightning Bolt|Jumpstart|342|U|{R}|Instant|||Lightning Bolt deals 3 damage to any target.| +Lightning Diadem|Jumpstart|343|C|{5}{R}|Enchantment - Aura|||Enchant creature$When Lightning Diadem enters the battlefield, it deals 2 damage to any target.$Enchanted creature gets +2/+2.| +Lightning Elemental|Jumpstart|344|C|{3}{R}|Creature - Elemental|4|1|Haste| +Lightning Shrieker|Jumpstart|345|C|{4}{R}|Creature - Dragon|5|5|Flying, trample, haste$At the beginning of the end step, Lightning Shrieker's owner shuffles it into their library.| +Magma Jet|Jumpstart|346|U|{1}{R}|Instant|||Magma Jet deals 2 damage to any target. Scry 2.| +Magmaquake|Jumpstart|347|R|{X}{R}{R}|Instant|||Magmaquake deals X damage to each creature without flying and each planeswalker.| +Makeshift Munitions|Jumpstart|348|U|{1}{R}|Enchantment|||{1}, Sacrifice an artifact or creature: Makeshift Munitions deals 1 damage to any target.| +Minotaur Skullcleaver|Jumpstart|349|C|{2}{R}|Creature - Minotaur Berserker|2|2|Haste$When Minotaur Skullcleaver enters the battlefield, it gets +2/+0 until end of turn.| +Minotaur Sureshot|Jumpstart|350|C|{2}{R}|Creature - Minotaur Archer|2|3|Reach${1}{R}: Minotaur Sureshot gets +1/+0 until end of turn.| +Molten Ravager|Jumpstart|351|C|{2}{R}|Creature - Elemental|0|4|{R}: Molten Ravager gets +1/+0 until end of turn.| +Mugging|Jumpstart|352|C|{R}|Sorcery|||Mugging deals 2 damage to target creature. That creature can't block this turn.| +Ornery Goblin|Jumpstart|353|C|{1}{R}|Creature - Goblin Warrior|2|1|Whenever Ornery Goblin blocks or becomes blocked by a creature, Ornery Goblin deals 1 damage to that creature.| +Outnumber|Jumpstart|354|C|{R}|Instant|||Outnumber deals damage to target creature equal to the number of creatures you control.| +Pillar of Flame|Jumpstart|355|C|{R}|Sorcery|||Pillar of Flame deals 2 damage to any target. If a creature dealt damage this way would die this turn, exile it instead.| +Pyroclastic Elemental|Jumpstart|356|U|{3}{R}{R}|Creature - Elemental|5|4|{1}{R}{R}: Pyroclastic Elemental deals 1 damage to target player.| +Rageblood Shaman|Jumpstart|357|R|{1}{R}{R}|Creature - Minotaur Shaman|2|3|Trample$Other Minotaur creatures you control get +1/+1 and have trample.| +Rapacious Dragon|Jumpstart|358|U|{4}{R}|Creature - Dragon|3|3|Flying$When Rapacious Dragon enters the battlefield, create two Treasure tokens.| +Riddle of Lightning|Jumpstart|359|U|{3}{R}{R}|Instant|||Choose any target. Scry 3, then reveal the top card of your library. Riddle of Lightning deals damage equal to that card's converted mana cost to that permanent or player.| +Sarkhan's Rage|Jumpstart|360|C|{4}{R}|Instant|||Sarkhan's Rage deals 5 damage to any target. If you control no Dragons, Sarkhan's Rage deals 2 damage to you.| +Sarkhan's Unsealing|Jumpstart|361|R|{3}{R}|Enchantment|||Whenever you cast a creature spell with power 4, 5, or 6, Sarkhan's Unsealing deals 4 damage to any target.$Whenever you cast a creature spell with power 7 or greater, Sarkhan's Unsealing deals 4 damage to each opponent and each creature and planeswalker they control.| +Seismic Elemental|Jumpstart|362|U|{3}{R}{R}|Creature - Elemental|4|4|When Seismic Elemental enters the battlefield, creatures without flying can't block this turn.| +Sin Prodder|Jumpstart|363|R|{2}{R}|Creature - Devil|3|2|Menace$At the beginning of your upkeep, reveal the top card of your library. Any opponent may have you put that card into your graveyard. If a player does, Sin Prodder deals damage to that player equal to that card's converted mana cost. Otherwise, put that card into your hand.| +Spitting Earth|Jumpstart|364|C|{1}{R}|Sorcery|||Spitting Earth deals damage to target creature equal to the number of Mountains you control.| +Thermo-Alchemist|Jumpstart|365|C|{1}{R}|Creature - Human Shaman|0|3|Defender${T}: Thermo-Alchemist deals 1 damage to each opponent.$Whenever you cast an instant or sorcery spell, untap Thermo-Alchemist.| +Tibalt's Rager|Jumpstart|366|U|{1}{R}|Creature - Devil|1|2|When Tibalt's Rager dies, it deals 1 damage to any target.${1}{R}: Tibalt's Rager gets +2/+0 until end of turn.| +Torch Fiend|Jumpstart|367|C|{1}{R}|Creature - Devil|2|1|{R}, Sacrifice Torch Fiend: Destroy target artifact.| +Volcanic Fallout|Jumpstart|368|U|{1}{R}{R}|Instant|||This spell can't be countered.$Volcanic Fallout deals 2 damage to each creature and each player.| +Volley Veteran|Jumpstart|369|U|{3}{R}|Creature - Goblin Warrior|4|2|When Volley Veteran enters the battlefield, it deals damage to target creature an opponent controls equal to the number of Goblins you control.| +Warfire Javelineer|Jumpstart|370|U|{3}{R}|Creature - Minotaur Warrior|2|3|When Warfire Javelineer enters the battlefield, it deals X damage to target creature an opponent controls, where X is the number of instant and sorcery cards in your graveyard.| +Weaver of Lightning|Jumpstart|371|U|{2}{R}|Creature - Human Shaman|1|4|Reach$Whenever you cast an instant or sorcery spell, Weaver of Lightning deals 1 damage to target creature an opponent controls.| +Young Pyromancer|Jumpstart|372|U|{1}{R}|Creature - Human Shaman|2|1|Whenever you cast an instant or sorcery spell, create a 1/1 red Elemental creature token.| +Affectionate Indrik|Jumpstart|373|U|{5}{G}|Creature - Beast|4|4|When Affectionate Indrik enters the battlefield, you may have it fight target creature you don't control.| +Aggressive Urge|Jumpstart|374|C|{1}{G}|Instant|||Target creature gets +1/+1 until end of turn.$Draw a card.| +Ambassador Oak|Jumpstart|375|C|{3}{G}|Creature - Treefolk Warrior|3|3|When Ambassador Oak enters the battlefield, create a 1/1 green Elf Warrior creature token.| +Arbor Armament|Jumpstart|376|C|{G}|Instant|||Put a +1/+1 counter on target creature. That creature gains reach until end of turn.| +Armorcraft Judge|Jumpstart|377|U|{3}{G}|Creature - Elf Artificer|3|3|When Armorcraft Judge enters the battlefield, draw a card for each creature you control with a +1/+1 counter on it.| +Assault Formation|Jumpstart|378|R|{1}{G}|Enchantment|||Each creature you control assigns combat damage equal to its toughness rather than its power.${G}: Target creature with defender can attack this turn as though it didn't have defender.${2}{G}: Creatures you control get +0/+1 until end of turn.| +Awakener Druid|Jumpstart|379|U|{2}{G}|Creature - Human Druid|1|1|When Awakener Druid enters the battlefield, target Forest becomes a 4/5 green Treefolk creature for as long as Awakener Druid remains on the battlefield. It's still a land.| +Brindle Shoat|Jumpstart|380|U|{1}{G}|Creature - Boar|1|1|When Brindle Shoat dies, create a 3/3 green Boar creature token.| +Brushstrider|Jumpstart|381|U|{1}{G}|Creature - Beast|3|1|Vigilance| +Carven Caryatid|Jumpstart|382|U|{1}{G}{G}|Creature - Spirit|2|5|Defender$When Carven Caryatid enters the battlefield, draw a card.| +Champion of Lambholt|Jumpstart|383|R|{1}{G}{G}|Creature - Human Warrior|1|1|Creatures with power less than Champion of Lambholt's power can't block creatures you control.$Whenever another creature enters the battlefield under your control, put a +1/+1 counter on Champion of Lambholt.| +Commune with Dinosaurs|Jumpstart|384|C|{G}|Sorcery|||Look at the top five cards of your library. You may reveal a Dinosaur or land card from among them and put it into your hand. Put the rest on the bottom of your library in any order.| +Craterhoof Behemoth|Jumpstart|385|M|{5}{G}{G}{G}|Creature - Beast|5|5|Haste$When Craterhoof Behemoth enters the battlefield, creatures you control gain trample and get +X/+X until end of turn, where X is the number of creatures you control.| +Crushing Canopy|Jumpstart|386|C|{2}{G}|Instant|||Choose one —$• Destroy target creature with flying.$• Destroy target enchantment.| +Dawntreader Elk|Jumpstart|387|C|{1}{G}|Creature - Elk|2|2|{G}, Sacrifice Dawntreader Elk: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.| +Drover of the Mighty|Jumpstart|388|U|{1}{G}|Creature - Human Druid|1|1|Drover of the Mighty gets +2/+2 as long as you control a Dinosaur.${T}: Add one mana of any color.| +Dwynen's Elite|Jumpstart|389|U|{1}{G}|Creature - Elf Warrior|2|2|When Dwynen's Elite enters the battlefield, if you control another Elf, create a 1/1 green Elf Warrior creature token.| +Elemental Uprising|Jumpstart|390|C|{1}{G}|Instant|||Target land you control becomes a 4/4 Elemental creature with haste until end of turn. It's still a land. It must be blocked this turn if able.| +Elvish Archdruid|Jumpstart|391|R|{1}{G}{G}|Creature - Elf Druid|2|2|Other Elf creatures you control get +1/+1.${T}: Add {G} for each Elf you control.| +Enlarge|Jumpstart|392|U|{3}{G}{G}|Sorcery|||Target creature gets +7/+7 and gains trample until end of turn. It must be blocked this turn if able.| +Explore|Jumpstart|393|C|{1}{G}|Sorcery|||You may play an additional land this turn.$Draw a card.| +Fa'adiyah Seer|Jumpstart|394|C|{1}{G}|Creature - Human Shaman|1|1|{T}: Draw a card and reveal it. If it isn't a land card, discard it.| +Feral Hydra|Jumpstart|395|U|{X}{G}|Creature - Hydra Beast|0|0|Feral Hydra enters the battlefield with X +1/+1 counters on it.${3}: Put a +1/+1 counter on Feral Hydra. Any player may activate this ability.| +Feral Invocation|Jumpstart|396|C|{2}{G}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature gets +2/+2.| +Feral Prowler|Jumpstart|397|C|{1}{G}|Creature - Cat|1|3|When Feral Prowler dies, draw a card.| +Fertilid|Jumpstart|398|C|{2}{G}|Creature - Elemental|0|0|Fertilid enters the battlefield with two +1/+1 counters on it.${1}{G}, Remove a +1/+1 counter from Fertilid: Target player searches their library for a basic land card, puts it onto the battlefield tapped, then shuffles their library.| +Ghalta, Primal Hunger|Jumpstart|399|R|{10}{G}{G}|Legendary Creature - Elder Dinosaur|12|12|This spell costs {X} less to cast, where X is the total power of creatures you control.$Trample| +Ghirapur Guide|Jumpstart|400|U|{2}{G}|Creature - Elf Scout|3|2|{2}{G}: Target creature you control can't be blocked by creatures with power 2 or less this turn.| +Grave Bramble|Jumpstart|401|C|{1}{G}{G}|Creature - Plant|3|4|Defender, protection from Zombies| +Hunter's Insight|Jumpstart|402|U|{2}{G}|Instant|||Choose target creature you control. Whenever that creature deals combat damage to a player or planeswalker this turn, draw that many cards.| +Initiate's Companion|Jumpstart|403|C|{1}{G}|Creature - Cat|3|1|Whenever Initiate's Companion deals combat damage to a player, untap target creature or land.| +Inspiring Call|Jumpstart|404|U|{2}{G}|Instant|||Draw a card for each creature you control with a +1/+1 counter on it. Those creatures gain indestructible until end of turn.| +Ironshell Beetle|Jumpstart|405|C|{1}{G}|Creature - Insect|1|1|When Ironshell Beetle enters the battlefield, put a +1/+1 counter on target creature.| +Irresistible Prey|Jumpstart|406|U|{G}|Sorcery|||Target creature must be blocked this turn if able.$Draw a card.| +Keeper of Fables|Jumpstart|407|U|{3}{G}{G}|Creature - Cat|4|5|Whenever one or more non-Human creatures you control deal combat damage to a player, draw a card.| +Leaf Gilder|Jumpstart|408|C|{1}{G}|Creature - Elf Druid|2|1|{T}: Add {G}.| +Lifecrafter's Gift|Jumpstart|409|U|{3}{G}|Instant|||Put a +1/+1 counter on target creature, then put a +1/+1 counter on each creature you control with a +1/+1 counter on it.| +Lurking Predators|Jumpstart|410|R|{4}{G}{G}|Enchantment|||Whenever an opponent casts a spell, reveal the top card of your library. If it's a creature card, put it onto the battlefield. Otherwise, you may put that card on the bottom of your library.| +Momentous Fall|Jumpstart|411|R|{2}{G}{G}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$You draw cards equal to the sacrificed creature's power, then you gain life equal to its toughness.| +Nature's Way|Jumpstart|412|U|{1}{G}|Sorcery|||Target creature you control gains vigilance and trample until end of turn. It deals damage equal to its power to target creature you don't control.| +Nessian Hornbeetle|Jumpstart|413|U|{1}{G}|Creature - Insect|2|2|At the beginning of combat on your turn, if you control another creature with power 4 or greater, put a +1/+1 counter on Nessian Hornbeetle.| +New Horizons|Jumpstart|414|C|{2}{G}|Enchantment - Aura|||Enchant land$When New Horizons enters the battlefield, put a +1/+1 counter on target creature you control.$Enchanted land has "{T}: Add two mana of any one color."| +Oracle of Mul Daya|Jumpstart|415|R|{3}{G}|Creature - Elf Shaman|2|2|You may play an additional land on each of your turns.$Play with the top card of your library revealed.$You may play lands from the top of your library.| +Orazca Frillback|Jumpstart|416|C|{2}{G}|Creature - Dinosaur|4|2|| +Overgrown Battlement|Jumpstart|417|U|{1}{G}|Creature - Wall|0|4|Defender${T}: Add {G} for each creature with defender you control.| +Penumbra Bobcat|Jumpstart|418|C|{2}{G}|Creature - Cat|2|1|When Penumbra Bobcat dies, create a 2/1 black Cat creature token.| +Pouncing Cheetah|Jumpstart|419|C|{2}{G}|Creature - Cat|3|2|Flash| +Presence of Gond|Jumpstart|420|C|{2}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature has "{T}: Create a 1/1 green Elf Warrior creature token."| +Primeval Bounty|Jumpstart|421|M|{5}{G}|Enchantment|||Whenever you cast a creature spell, create a 3/3 green Beast creature token.$Whenever you cast a noncreature spell, put three +1/+1 counters on target creature you control.$Whenever a land enters the battlefield under your control, you gain 3 life.| +Primordial Sage|Jumpstart|422|R|{4}{G}{G}|Creature - Spirit|4|5|Whenever you cast a creature spell, you may draw a card.| +Rampaging Brontodon|Jumpstart|423|R|{5}{G}{G}|Creature - Dinosaur|7|7|Trample$Whenever Rampaging Brontodon attacks, it gets +1/+1 until end of turn for each land you control.| +Ravenous Baloth|Jumpstart|424|R|{2}{G}{G}|Creature - Beast|4|4|Sacrifice a Beast: You gain 4 life.| +Rishkar, Peema Renegade|Jumpstart|425|R|{2}{G}|Legendary Creature - Elf Druid|2|2|When Rishkar, Peema Renegade enters the battlefield, put a +1/+1 counter on each of up to two target creatures.$Each creature you control with a counter on it has "{T}: Add {G}."| +Rumbling Baloth|Jumpstart|426|C|{2}{G}{G}|Creature - Beast|4|4|| +Savage Stomp|Jumpstart|427|U|{2}{G}|Sorcery|||This spell costs {2} less to cast if it targets a Dinosaur you control.$Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control.| +Scrounging Bandar|Jumpstart|428|C|{1}{G}|Creature - Cat Monkey|0|0|Scrounging Bandar enters the battlefield with two +1/+1 counters on it.$At the beginning of your upkeep, you may move any number of +1/+1 counters from Scrounging Bandar onto another target creature.| +Selvala, Heart of the Wilds|Jumpstart|429|M|{1}{G}{G}|Legendary Creature - Elf Scout|2|3|Whenever another creature enters the battlefield, its controller may draw a card if its power is greater than each other creature's power.${G}, {T}: Add X mana in any combination of colors, where X is the greatest power among creatures you control.| +Silhana Wayfinder|Jumpstart|430|U|{1}{G}|Creature - Elf Scout|2|1|When Silhana Wayfinder enters the battlefield, look at the top four cards of your library. You may reveal a creature or land card from among them and put it on top of your library. Put the rest on the bottom of your library in a random order.| +Somberwald Stag|Jumpstart|431|U|{3}{G}{G}|Creature - Elk|4|3|When Somberwald Stag enters the battlefield, you may have it fight target creature you don't control.| +Soul of the Harvest|Jumpstart|432|R|{4}{G}{G}|Creature - Elemental|6|6|Trample$Whenever another nontoken creature enters the battlefield under your control, you may draw a card.| +Sporemound|Jumpstart|433|C|{3}{G}{G}|Creature - Fungus|3|3|Whenever a land enters the battlefield under your control, create a 1/1 green Saproling creature token.| +Sylvan Brushstrider|Jumpstart|434|C|{2}{G}|Creature - Beast|3|2|When Sylvan Brushstrider enters the battlefield, you gain 2 life.| +Sylvan Ranger|Jumpstart|435|C|{1}{G}|Creature - Elf Scout|1|1|When Sylvan Ranger enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.| +Thragtusk|Jumpstart|436|R|{4}{G}|Creature - Beast|5|3|When Thragtusk enters the battlefield, you gain 5 life.$When Thragtusk leaves the battlefield, create a 3/3 green Beast creature token.| +Thundering Spineback|Jumpstart|437|U|{5}{G}{G}|Creature - Dinosaur|5|5|Other Dinosaurs you control get +1/+1.${5}{G}: Create a 3/3 green Dinosaur creature token with trample.| +Time to Feed|Jumpstart|438|C|{2}{G}|Sorcery|||Choose target creature an opponent controls. When that creature dies this turn, you gain 3 life. Target creature you control fights that creature.| +Ulvenwald Hydra|Jumpstart|439|M|{4}{G}{G}|Creature - Hydra|*|*|Reach$Ulvenwald Hydra's power and toughness are each equal to the number of lands you control.$When Ulvenwald Hydra enters the battlefield, you may search your library for a land card, put it onto the battlefield tapped, then shuffle your library.| +Vastwood Zendikon|Jumpstart|440|C|{4}{G}|Enchantment - Aura|||Enchant land$Enchanted land is a 6/4 green Elemental creature. It's still a land.$When enchanted land dies, return that card to its owner's hand.| +Verdant Embrace|Jumpstart|441|R|{3}{G}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3 and has "At the beginning of each upkeep, create a 1/1 green Saproling creature token."| +Wall of Blossoms|Jumpstart|442|U|{1}{G}|Creature - Plant Wall|0|4|Defender$When Wall of Blossoms enters the battlefield, draw a card.| +Wall of Vines|Jumpstart|443|C|{G}|Creature - Plant Wall|0|3|Defender$Reach| +Wildheart Invoker|Jumpstart|444|C|{2}{G}{G}|Creature - Elf Shaman|4|3|{8}: Target creature gets +5/+5 and gains trample until end of turn.| +Wildsize|Jumpstart|445|C|{2}{G}|Instant|||Target creature gets +2/+2 and gains trample until end of turn.$Draw a card.| +Woodborn Behemoth|Jumpstart|446|U|{3}{G}{G}|Creature - Elemental|4|4|As long as you control eight or more lands, Woodborn Behemoth gets +4/+4 and has trample.| +Wren's Run Vanquisher|Jumpstart|447|U|{1}{G}|Creature - Elf Warrior|3|3|As an additional cost to cast this spell, reveal an Elf card from your hand or pay {3}.$Deathtouch| +Zendikar's Roil|Jumpstart|448|U|{3}{G}{G}|Enchantment|||Whenever a land enters the battlefield under your control, create a 2/2 green Elemental creature token.| +Auger Spree|Jumpstart|449|C|{1}{B}{R}|Instant|||Target creature gets +4/-4 until end of turn.| +Dinrova Horror|Jumpstart|450|U|{4}{U}{B}|Creature - Horror|4|4|When Dinrova Horror enters the battlefield, return target permanent to its owner's hand, then that player discards a card.| +Fusion Elemental|Jumpstart|451|U|{W}{U}{B}{R}{G}|Creature - Elemental|8|8|| +Ironroot Warlord|Jumpstart|452|U|{1}{G}{W}|Creature - Treefolk Soldier|*|5|Ironroot Warlord's power is equal to the number of creatures you control.${3}{G}{W}: Create a 1/1 white Soldier creature token.| +Lawmage's Binding|Jumpstart|453|C|{1}{W}{U}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature can't attack or block, and its activated abilities can't be activated.| +Maelstrom Archangel|Jumpstart|454|M|{W}{U}{B}{R}{G}|Creature - Angel|5|5|Flying$Whenever Maelstrom Archangel deals combat damage to a player, you may cast a spell from your hand without paying its mana cost.| +Raging Regisaur|Jumpstart|455|U|{2}{R}{G}|Creature - Dinosaur|4|4|Whenever Raging Regisaur attacks, it deals 1 damage to any target.| +Aether Spellbomb|Jumpstart|456|C|{1}|Artifact|||{U}, Sacrifice Aether Spellbomb: Return target creature to its owner's hand.${1}, Sacrifice Aether Spellbomb: Draw a card.| +Alloy Myr|Jumpstart|457|C|{3}|Artifact Creature - Myr|2|2|{T}: Add one mana of any color.| +Ancestral Statue|Jumpstart|458|C|{4}|Artifact Creature - Golem|3|4|When Ancestral Statue enters the battlefield, return a nonland permanent you control to its owner's hand.| +Arcane Encyclopedia|Jumpstart|459|U|{3}|Artifact|||{3}, {T}: Draw a card.| +Bubbling Cauldron|Jumpstart|460|U|{2}|Artifact|||{1}, {T}, Sacrifice a creature: You gain 4 life.${1}, {T}, Sacrifice a creature named Festering Newt: Each opponent loses 4 life. You gain life equal to the life lost this way.| +Chamber Sentry|Jumpstart|461|R|{X}|Artifact Creature - Construct|0|0|Chamber Sentry enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.${X}, {T}, Remove X +1/+1 counters from Chamber Sentry: It deals X damage to any target.${W}{U}{B}{R}{G}: Return Chamber Sentry from your graveyard to your hand.| +Chromatic Sphere|Jumpstart|462|C|{1}|Artifact|||{1}, {T}, Sacrifice Chromatic Sphere: Add one mana of any color. Draw a card.| +Dragonloft Idol|Jumpstart|463|U|{4}|Artifact Creature - Gargoyle|3|3|As long as you control a Dragon, Dragonloft Idol gets +1/+1 and has flying and trample.| +Dreamstone Hedron|Jumpstart|464|U|{6}|Artifact|||{T}: Add {C}{C}{C}.${3}, {T}, Sacrifice Dreamstone Hedron: Draw three cards.| +Gargoyle Sentinel|Jumpstart|465|U|{3}|Artifact Creature - Gargoyle|3|3|Defender${3}: Until end of turn, Gargoyle Sentinel loses defender and gains flying.| +Gingerbrute|Jumpstart|466|C|{1}|Artifact Creature - Food Golem|1|1|Haste${1}: Gingerbrute can't be blocked this turn except by creatures with haste.${2}, {T}, Sacrifice Gingerbrute: You gain 3 life.| +Guardian Idol|Jumpstart|467|U|{2}|Artifact|||Guardian Idol enters the battlefield tapped.${T}: Add {C}.${2}: Guardian Idol becomes a 2/2 Golem artifact creature until end of turn.| +Hedron Archive|Jumpstart|468|U|{4}|Artifact|||{T}: Add {C}{C}.${2}, {T}, Sacrifice Hedron Archive: Draw two cards.| +Herald's Horn|Jumpstart|469|U|{3}|Artifact|||As Herald's Horn enters the battlefield, choose a creature type.$Creature spells you cast of the chosen type cost {1} less to cast.$At the beginning of your upkeep, look at the top card of your library. If it's a creature card of the chosen type, you may reveal it and put it into your hand.| +Jousting Dummy|Jumpstart|470|C|{2}|Artifact Creature - Scarecrow Knight|2|1|{3}: Jousting Dummy gets +1/+0 until end of turn.| +Juggernaut|Jumpstart|471|U|{4}|Artifact Creature - Juggernaut|5|3|Juggernaut attacks each combat if able.$Juggernaut can't be blocked by Walls.| +Mana Geode|Jumpstart|472|C|{3}|Artifact|||When Mana Geode enters the battlefield, scry 1.${T}: Add one mana of any color.| +Marauder's Axe|Jumpstart|473|C|{2}|Artifact - Equipment|||Equipped creature gets +2/+0.$Equip {2}| +Meteor Golem|Jumpstart|474|U|{7}|Artifact Creature - Golem|3|3|When Meteor Golem enters the battlefield, destroy target nonland permanent an opponent controls.| +Myr Sire|Jumpstart|475|C|{2}|Artifact Creature - Myr|1|1|When Myr Sire dies, create a 1/1 colorless Myr artifact creature token.| +Perilous Myr|Jumpstart|476|U|{2}|Artifact Creature - Myr|1|1|When Perilous Myr dies, it deals 2 damage to any target.| +Pirate's Cutlass|Jumpstart|477|C|{3}|Artifact - Equipment|||When Pirate's Cutlass enters the battlefield, attach it to target Pirate you control.$Equipped creature gets +2/+1.$Equip {2}| +Prophetic Prism|Jumpstart|478|C|{2}|Artifact|||When Prophetic Prism enters the battlefield, draw a card.${1}, {T}: Add one mana of any color.| +Rogue's Gloves|Jumpstart|479|U|{2}|Artifact - Equipment|||Whenever equipped creature deals combat damage to a player, you may draw a card.$Equip {2}| +Roving Keep|Jumpstart|480|C|{7}|Artifact Creature - Wall|5|7|Defender${7}: Roving Keep gets +2/+0 and gains trample until end of turn. It can attack this turn as though it didn't have defender.| +Runed Servitor|Jumpstart|481|C|{2}|Artifact Creature - Construct|2|2|When Runed Servitor dies, each player draws a card.| +Scarecrone|Jumpstart|482|R|{3}|Artifact Creature - Scarecrow|1|2|{1}, Sacrifice a Scarecrow: Draw a card.${4}, {T}: Return target artifact creature card from your graveyard to the battlefield.| +Scroll of Avacyn|Jumpstart|483|C|{1}|Artifact|||{1}, Sacrifice Scroll of Avacyn: Draw a card. If you control an Angel, you gain 5 life.| +Scuttlemutt|Jumpstart|484|U|{3}|Artifact Creature - Scarecrow|2|2|{T}: Add one mana of any color.${T}: Target creature becomes the color or colors of your choice until end of turn.| +Signpost Scarecrow|Jumpstart|485|C|{4}|Artifact Creature - Scarecrow|2|4|Vigilance${2}: Add one mana of any color.| +Skittering Surveyor|Jumpstart|486|C|{3}|Artifact Creature - Construct|1|2|When Skittering Surveyor enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.| +Suspicious Bookcase|Jumpstart|487|U|{2}|Artifact Creature - Wall|0|4|Defender${3}, {T}: Target creature can't be blocked this turn.| +Terrarion|Jumpstart|488|C|{1}|Artifact|||Terrarion enters the battlefield tapped.${2}, {T}, Sacrifice Terrarion: Add two mana in any combination of colors.$When Terrarion is put into a graveyard from the battlefield, draw a card.| +Unstable Obelisk|Jumpstart|489|U|{3}|Artifact|||{T}: Add {C}.${7}, {T}, Sacrifice Unstable Obelisk: Destroy target permanent.| +Warmonger's Chariot|Jumpstart|490|U|{2}|Artifact - Equipment|||Equipped creature gets +2/+2.$As long as equipped creature has defender, it can attack as though it didn't have defender.$Equip {3}| +Buried Ruin|Jumpstart|491|U||Land|||{T}: Add {C}.${2}, {T}, Sacrifice Buried Ruin: Return target artifact card from your graveyard to your hand.| +Mirrodin's Core|Jumpstart|492|U||Land|||{T}: Add {C}.${T}: Put a charge counter on Mirrodin's Core.${T}, Remove a charge counter from Mirrodin's Core: Add one mana of any color.| +Phyrexian Tower|Jumpstart|493|R||Legendary Land|||{T}: Add {C}.${T}, Sacrifice a creature: Add {B}{B}.| +Riptide Laboratory|Jumpstart|494|R||Land|||{T}: Add {C}.${1}{U}, {T}: Return target Wizard you control to its owner's hand.| +Rupture Spire|Jumpstart|495|C||Land|||Rupture Spire enters the battlefield tapped.$When Rupture Spire enters the battlefield, sacrifice it unless you pay {1}.${T}: Add one mana of any color.| diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt index a6dd8bb0fa..f331a07819 100644 --- a/Utils/mtg-sets-data.txt +++ b/Utils/mtg-sets-data.txt @@ -34,6 +34,7 @@ Commander 2016 Edition|C16| Commander 2017 Edition|C17| Commander 2018 Edition|C18| Commander 2019 Edition|C19| +Commander 2020 Edition|C20| Champions of Kamigawa|CHK| Chronicles|CHR| Clash Pack|CLASH| @@ -109,11 +110,13 @@ Planechase|HOP| Hour of Devastation|HOU| Ice Age|ICE| Iconic Masters|IMA| +Ikoria: Lair of Behemoths|IKO| Invasion|INV| Innistrad|ISD| Journey into Nyx|JOU| Judge Promo|JR| Judgment|JUD| +Jumpstart|JMP| Kaladesh|KLD| Khans of Tarkir|KTK| Limited Edition Alpha|LEA| @@ -129,6 +132,7 @@ Magic 2014|M14| Magic 2015|M15| Core Set 2019|M19| Core Set 2020|M20| +Core Set 2021|M21| Masters 25|A25| Magic: The Gathering-Commander|CMD| Magic: The Gathering-Conspiracy|CNS| diff --git a/Utils/release/startMage.sh b/Utils/release/startMage.sh index a737503b04..ddc86bd71e 100644 --- a/Utils/release/startMage.sh +++ b/Utils/release/startMage.sh @@ -1,5 +1,5 @@ -#!/bin/sh -cd ./mage-client -./startClient.sh -cd ../mage-server -./startServer.sh +#!/bin/sh +cd ./mage-client +./startClient.sh +cd ../mage-server +./startServer.sh diff --git a/pom.xml b/pom.xml index 09cfb75e74..b411403e37 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.42 + 1.4.44 pom Mage Root Mage Root POM @@ -86,7 +86,7 @@ - 1.4.42 + 1.4.44 UTF-8 yyyy-MM-dd'T'HH:mm:ss'Z' @@ -111,7 +111,7 @@ com.google.guava guava - 20.0 + 29.0-jre